fix: number and select filter logic (#4828)

* fix: number and select option filter bugs

* chore: rename filter condition enum and variants
This commit is contained in:
Richard Shiue 2024-03-14 14:27:57 +08:00 committed by GitHub
parent 1a34366a3f
commit 5a837a9482
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
57 changed files with 1338 additions and 1266 deletions

View File

@ -257,7 +257,7 @@
"label": "AF: Tauri UI Dev", "label": "AF: Tauri UI Dev",
"type": "shell", "type": "shell",
"isBackground": true, "isBackground": true,
"command": "pnpm run sync:i18n && pnpm run dev", "command": "pnpm sync:i18n && pnpm run dev",
"options": { "options": {
"cwd": "${workspaceFolder}/appflowy_tauri" "cwd": "${workspaceFolder}/appflowy_tauri"
} }

View File

@ -103,8 +103,8 @@ void main() {
// select the option 's4' // select the option 's4'
await tester.tapOptionFilterWithName('s4'); await tester.tapOptionFilterWithName('s4');
// The row with 's4' or 's5' should be shown. // The row with 's4' should be shown.
await tester.assertNumberOfRowsInGridPage(2); await tester.assertNumberOfRowsInGridPage(1);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
}); });

View File

@ -171,7 +171,7 @@ class FilterBackendService {
Future<FlowyResult<void, FlowyError>> insertSelectOptionFilter({ Future<FlowyResult<void, FlowyError>> insertSelectOptionFilter({
required String fieldId, required String fieldId,
required FieldType fieldType, required FieldType fieldType,
required SelectOptionConditionPB condition, required SelectOptionFilterConditionPB condition,
String? filterId, String? filterId,
List<String> optionIds = const [], List<String> optionIds = const [],
}) { }) {

View File

@ -114,7 +114,7 @@ class GridCreateFilterBloc
case FieldType.MultiSelect: case FieldType.MultiSelect:
return _filterBackendSvc.insertSelectOptionFilter( return _filterBackendSvc.insertSelectOptionFilter(
fieldId: fieldId, fieldId: fieldId,
condition: SelectOptionConditionPB.OptionIs, condition: SelectOptionFilterConditionPB.OptionContains,
fieldType: FieldType.MultiSelect, fieldType: FieldType.MultiSelect,
); );
case FieldType.Checklist: case FieldType.Checklist:
@ -130,19 +130,19 @@ class GridCreateFilterBloc
case FieldType.RichText: case FieldType.RichText:
return _filterBackendSvc.insertTextFilter( return _filterBackendSvc.insertTextFilter(
fieldId: fieldId, fieldId: fieldId,
condition: TextFilterConditionPB.Contains, condition: TextFilterConditionPB.TextContains,
content: '', content: '',
); );
case FieldType.SingleSelect: case FieldType.SingleSelect:
return _filterBackendSvc.insertSelectOptionFilter( return _filterBackendSvc.insertSelectOptionFilter(
fieldId: fieldId, fieldId: fieldId,
condition: SelectOptionConditionPB.OptionIs, condition: SelectOptionFilterConditionPB.OptionIs,
fieldType: FieldType.SingleSelect, fieldType: FieldType.SingleSelect,
); );
case FieldType.URL: case FieldType.URL:
return _filterBackendSvc.insertURLFilter( return _filterBackendSvc.insertURLFilter(
fieldId: fieldId, fieldId: fieldId,
condition: TextFilterConditionPB.Contains, condition: TextFilterConditionPB.TextContains,
); );
default: default:
throw UnimplementedError(); throw UnimplementedError();

View File

@ -38,7 +38,7 @@ class SelectOptionFilterEditorBloc
_startListening(); _startListening();
_loadOptions(); _loadOptions();
}, },
updateCondition: (SelectOptionConditionPB condition) { updateCondition: (SelectOptionFilterConditionPB condition) {
_filterBackendSvc.insertSelectOptionFilter( _filterBackendSvc.insertSelectOptionFilter(
filterId: filterInfo.filter.id, filterId: filterInfo.filter.id,
fieldId: filterInfo.fieldInfo.id, fieldId: filterInfo.fieldInfo.id,
@ -117,7 +117,7 @@ class SelectOptionFilterEditorEvent with _$SelectOptionFilterEditorEvent {
FilterPB filter, FilterPB filter,
) = _DidReceiveFilter; ) = _DidReceiveFilter;
const factory SelectOptionFilterEditorEvent.updateCondition( const factory SelectOptionFilterEditorEvent.updateCondition(
SelectOptionConditionPB condition, SelectOptionFilterConditionPB condition,
) = _UpdateCondition; ) = _UpdateCondition;
const factory SelectOptionFilterEditorEvent.updateContent( const factory SelectOptionFilterEditorEvent.updateContent(
List<String> optionIds, List<String> optionIds,

View File

@ -1,5 +1,5 @@
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/select_option/select_option_loader.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/choicechip/select_option/select_option_loader.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
@ -24,9 +24,12 @@ class SelectOptionFilterListBloc<T>
_startListening(); _startListening();
_loadOptions(); _loadOptions();
}, },
selectOption: (option) { selectOption: (option, condition) {
final selectedOptionIds = Set<String>.from(state.selectedOptionIds); final selectedOptionIds = delegate.selectOption(
selectedOptionIds.add(option.id); state.selectedOptionIds,
option.id,
condition,
);
_updateSelectOptions( _updateSelectOptions(
selectedOptionIds: selectedOptionIds, selectedOptionIds: selectedOptionIds,
@ -116,6 +119,7 @@ class SelectOptionFilterListEvent with _$SelectOptionFilterListEvent {
const factory SelectOptionFilterListEvent.initial() = _Initial; const factory SelectOptionFilterListEvent.initial() = _Initial;
const factory SelectOptionFilterListEvent.selectOption( const factory SelectOptionFilterListEvent.selectOption(
SelectOptionPB option, SelectOptionPB option,
SelectOptionFilterConditionPB condition,
) = _SelectOption; ) = _SelectOption;
const factory SelectOptionFilterListEvent.unselectOption( const factory SelectOptionFilterListEvent.unselectOption(
SelectOptionPB option, SelectOptionPB option,

View File

@ -1,15 +1,12 @@
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/condition_button.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/filter_info.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/widgets.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/field_entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_filter.pb.dart';
import 'package:flutter/material.dart';
import '../../condition_button.dart';
import '../../filter_info.dart';
class SelectOptionFilterConditionList extends StatelessWidget { class SelectOptionFilterConditionList extends StatelessWidget {
const SelectOptionFilterConditionList({ const SelectOptionFilterConditionList({
@ -21,7 +18,7 @@ class SelectOptionFilterConditionList extends StatelessWidget {
final FilterInfo filterInfo; final FilterInfo filterInfo;
final PopoverMutex popoverMutex; final PopoverMutex popoverMutex;
final Function(SelectOptionConditionPB) onCondition; final Function(SelectOptionFilterConditionPB) onCondition;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -30,18 +27,17 @@ class SelectOptionFilterConditionList extends StatelessWidget {
asBarrier: true, asBarrier: true,
mutex: popoverMutex, mutex: popoverMutex,
direction: PopoverDirection.bottomWithCenterAligned, direction: PopoverDirection.bottomWithCenterAligned,
actions: SelectOptionConditionPB.values actions: _conditionsForFieldType(filterInfo.fieldInfo.fieldType)
.map( .map(
(action) => ConditionWrapper( (action) => ConditionWrapper(
action, action,
selectOptionFilter.condition == action, selectOptionFilter.condition == action,
filterInfo.fieldInfo.fieldType,
), ),
) )
.toList(), .toList(),
buildChild: (controller) { buildChild: (controller) {
return ConditionButton( return ConditionButton(
conditionName: filterName(selectOptionFilter), conditionName: selectOptionFilter.condition.i18n,
onTap: () => controller.show(), onTap: () => controller.show(),
); );
}, },
@ -52,69 +48,62 @@ class SelectOptionFilterConditionList extends StatelessWidget {
); );
} }
String filterName(SelectOptionFilterPB filter) { List<SelectOptionFilterConditionPB> _conditionsForFieldType(
if (filterInfo.fieldInfo.fieldType == FieldType.SingleSelect) { FieldType fieldType,
return filter.condition.singleSelectFilterName; ) {
} else { // SelectOptionFilterConditionPB.values is not in order
return filter.condition.multiSelectFilterName; return switch (fieldType) {
} FieldType.SingleSelect => [
SelectOptionFilterConditionPB.OptionIs,
SelectOptionFilterConditionPB.OptionIsNot,
SelectOptionFilterConditionPB.OptionIsEmpty,
SelectOptionFilterConditionPB.OptionIsNotEmpty,
],
FieldType.MultiSelect => [
SelectOptionFilterConditionPB.OptionContains,
SelectOptionFilterConditionPB.OptionDoesNotContain,
SelectOptionFilterConditionPB.OptionIs,
SelectOptionFilterConditionPB.OptionIsNot,
SelectOptionFilterConditionPB.OptionIsEmpty,
SelectOptionFilterConditionPB.OptionIsNotEmpty,
],
_ => [],
};
} }
} }
class ConditionWrapper extends ActionCell { class ConditionWrapper extends ActionCell {
ConditionWrapper(this.inner, this.isSelected, this.fieldType); ConditionWrapper(this.inner, this.isSelected);
final SelectOptionConditionPB inner; final SelectOptionFilterConditionPB inner;
final bool isSelected; final bool isSelected;
final FieldType fieldType;
@override @override
Widget? rightIcon(Color iconColor) { Widget? rightIcon(Color iconColor) {
if (isSelected) { return isSelected ? const FlowySvg(FlowySvgs.check_s) : null;
return const FlowySvg(FlowySvgs.check_s);
} else {
return null;
}
} }
@override @override
String get name { String get name => inner.i18n;
if (fieldType == FieldType.SingleSelect) {
return inner.singleSelectFilterName;
} else {
return inner.multiSelectFilterName;
}
}
} }
extension SelectOptionConditionPBExtension on SelectOptionConditionPB { extension SelectOptionFilterConditionPBExtension
String get singleSelectFilterName { on SelectOptionFilterConditionPB {
switch (this) { String get i18n {
case SelectOptionConditionPB.OptionIs: return switch (this) {
return LocaleKeys.grid_singleSelectOptionFilter_is.tr(); SelectOptionFilterConditionPB.OptionIs =>
case SelectOptionConditionPB.OptionIsEmpty: LocaleKeys.grid_selectOptionFilter_is.tr(),
return LocaleKeys.grid_singleSelectOptionFilter_isEmpty.tr(); SelectOptionFilterConditionPB.OptionIsNot =>
case SelectOptionConditionPB.OptionIsNot: LocaleKeys.grid_selectOptionFilter_isNot.tr(),
return LocaleKeys.grid_singleSelectOptionFilter_isNot.tr(); SelectOptionFilterConditionPB.OptionContains =>
case SelectOptionConditionPB.OptionIsNotEmpty: LocaleKeys.grid_selectOptionFilter_isNot.tr(),
return LocaleKeys.grid_singleSelectOptionFilter_isNotEmpty.tr(); SelectOptionFilterConditionPB.OptionDoesNotContain =>
default: LocaleKeys.grid_selectOptionFilter_isNot.tr(),
return ""; SelectOptionFilterConditionPB.OptionIsEmpty =>
} LocaleKeys.grid_selectOptionFilter_isEmpty.tr(),
} SelectOptionFilterConditionPB.OptionIsNotEmpty =>
LocaleKeys.grid_selectOptionFilter_isNotEmpty.tr(),
String get multiSelectFilterName { _ => "",
switch (this) { };
case SelectOptionConditionPB.OptionIs:
return LocaleKeys.grid_multiSelectOptionFilter_contains.tr();
case SelectOptionConditionPB.OptionIsEmpty:
return LocaleKeys.grid_multiSelectOptionFilter_isEmpty.tr();
case SelectOptionConditionPB.OptionIsNot:
return LocaleKeys.grid_multiSelectOptionFilter_doesNotContain.tr();
case SelectOptionConditionPB.OptionIsNotEmpty:
return LocaleKeys.grid_multiSelectOptionFilter_isNotEmpty.tr();
default:
return "";
}
} }
} }

View File

@ -1,4 +1,5 @@
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/plugins/database/grid/application/filter/select_option_filter_bloc.dart';
import 'package:appflowy/plugins/database/grid/application/filter/select_option_filter_list_bloc.dart'; import 'package:appflowy/plugins/database/grid/application/filter/select_option_filter_list_bloc.dart';
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/filter_info.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/filter_info.dart';
@ -90,9 +91,16 @@ class _SelectOptionFilterCellState extends State<SelectOptionFilterCell> {
.read<SelectOptionFilterListBloc>() .read<SelectOptionFilterListBloc>()
.add(SelectOptionFilterListEvent.unselectOption(widget.option)); .add(SelectOptionFilterListEvent.unselectOption(widget.option));
} else { } else {
context context.read<SelectOptionFilterListBloc>().add(
.read<SelectOptionFilterListBloc>() SelectOptionFilterListEvent.selectOption(
.add(SelectOptionFilterListEvent.selectOption(widget.option)); widget.option,
context
.read<SelectOptionFilterEditorBloc>()
.state
.filter
.condition,
),
);
} }
}, },
children: [ children: [

View File

@ -101,9 +101,10 @@ class _SelectOptionFilterEditorState extends State<SelectOptionFilterEditor> {
SliverToBoxAdapter(child: _buildFilterPanel(context, state)), SliverToBoxAdapter(child: _buildFilterPanel(context, state)),
]; ];
if (state.filter.condition != SelectOptionConditionPB.OptionIsEmpty && if (state.filter.condition !=
SelectOptionFilterConditionPB.OptionIsEmpty &&
state.filter.condition != state.filter.condition !=
SelectOptionConditionPB.OptionIsNotEmpty) { SelectOptionFilterConditionPB.OptionIsNotEmpty) {
slivers.add(const SliverToBoxAdapter(child: VSpace(4))); slivers.add(const SliverToBoxAdapter(child: VSpace(4)));
slivers.add( slivers.add(
SliverToBoxAdapter( SliverToBoxAdapter(

View File

@ -1,9 +1,15 @@
import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart'; import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/filter_info.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/filter/filter_info.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/select_option_entities.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
abstract class SelectOptionFilterDelegate { abstract class SelectOptionFilterDelegate {
List<SelectOptionPB> loadOptions(); List<SelectOptionPB> loadOptions();
Set<String> selectOption(
Set<String> currentOptionIds,
String optionId,
SelectOptionFilterConditionPB condition,
);
} }
class SingleSelectOptionFilterDelegateImpl class SingleSelectOptionFilterDelegateImpl
@ -17,6 +23,22 @@ class SingleSelectOptionFilterDelegateImpl
final parser = SingleSelectTypeOptionDataParser(); final parser = SingleSelectTypeOptionDataParser();
return parser.fromBuffer(filterInfo.fieldInfo.field.typeOptionData).options; return parser.fromBuffer(filterInfo.fieldInfo.field.typeOptionData).options;
} }
@override
Set<String> selectOption(
Set<String> currentOptionIds,
String optionId,
SelectOptionFilterConditionPB condition,
) {
final selectOptionIds = Set<String>.from(currentOptionIds);
if (condition == SelectOptionFilterConditionPB.OptionIsNot ||
selectOptionIds.isEmpty) {
selectOptionIds.add(optionId);
}
return selectOptionIds;
}
} }
class MultiSelectOptionFilterDelegateImpl class MultiSelectOptionFilterDelegateImpl
@ -30,4 +52,12 @@ class MultiSelectOptionFilterDelegateImpl
final parser = MultiSelectTypeOptionDataParser(); final parser = MultiSelectTypeOptionDataParser();
return parser.fromBuffer(filterInfo.fieldInfo.field.typeOptionData).options; return parser.fromBuffer(filterInfo.fieldInfo.field.typeOptionData).options;
} }
@override
Set<String> selectOption(
Set<String> currentOptionIds,
String optionId,
SelectOptionFilterConditionPB condition,
) =>
Set<String>.from(currentOptionIds)..add(optionId);
} }

View File

@ -236,17 +236,17 @@ class ConditionWrapper extends ActionCell {
extension TextFilterConditionPBExtension on TextFilterConditionPB { extension TextFilterConditionPBExtension on TextFilterConditionPB {
String get filterName { String get filterName {
switch (this) { switch (this) {
case TextFilterConditionPB.Contains: case TextFilterConditionPB.TextContains:
return LocaleKeys.grid_textFilter_contains.tr(); return LocaleKeys.grid_textFilter_contains.tr();
case TextFilterConditionPB.DoesNotContain: case TextFilterConditionPB.TextDoesNotContain:
return LocaleKeys.grid_textFilter_doesNotContain.tr(); return LocaleKeys.grid_textFilter_doesNotContain.tr();
case TextFilterConditionPB.EndsWith: case TextFilterConditionPB.TextEndsWith:
return LocaleKeys.grid_textFilter_endsWith.tr(); return LocaleKeys.grid_textFilter_endsWith.tr();
case TextFilterConditionPB.Is: case TextFilterConditionPB.TextIs:
return LocaleKeys.grid_textFilter_is.tr(); return LocaleKeys.grid_textFilter_is.tr();
case TextFilterConditionPB.IsNot: case TextFilterConditionPB.TextIsNot:
return LocaleKeys.grid_textFilter_isNot.tr(); return LocaleKeys.grid_textFilter_isNot.tr();
case TextFilterConditionPB.StartsWith: case TextFilterConditionPB.TextStartsWith:
return LocaleKeys.grid_textFilter_startWith.tr(); return LocaleKeys.grid_textFilter_startWith.tr();
case TextFilterConditionPB.TextIsEmpty: case TextFilterConditionPB.TextIsEmpty:
return LocaleKeys.grid_textFilter_isEmpty.tr(); return LocaleKeys.grid_textFilter_isEmpty.tr();
@ -259,13 +259,13 @@ extension TextFilterConditionPBExtension on TextFilterConditionPB {
String get choicechipPrefix { String get choicechipPrefix {
switch (this) { switch (this) {
case TextFilterConditionPB.DoesNotContain: case TextFilterConditionPB.TextDoesNotContain:
return LocaleKeys.grid_textFilter_choicechipPrefix_isNot.tr(); return LocaleKeys.grid_textFilter_choicechipPrefix_isNot.tr();
case TextFilterConditionPB.EndsWith: case TextFilterConditionPB.TextEndsWith:
return LocaleKeys.grid_textFilter_choicechipPrefix_endWith.tr(); return LocaleKeys.grid_textFilter_choicechipPrefix_endWith.tr();
case TextFilterConditionPB.IsNot: case TextFilterConditionPB.TextIsNot:
return LocaleKeys.grid_textFilter_choicechipPrefix_isNot.tr(); return LocaleKeys.grid_textFilter_choicechipPrefix_isNot.tr();
case TextFilterConditionPB.StartsWith: case TextFilterConditionPB.TextStartsWith:
return LocaleKeys.grid_textFilter_choicechipPrefix_startWith.tr(); return LocaleKeys.grid_textFilter_choicechipPrefix_startWith.tr();
case TextFilterConditionPB.TextIsEmpty: case TextFilterConditionPB.TextIsEmpty:
return LocaleKeys.grid_textFilter_choicechipPrefix_isEmpty.tr(); return LocaleKeys.grid_textFilter_choicechipPrefix_isEmpty.tr();

View File

@ -55,13 +55,13 @@ void main() {
await service.insertTextFilter( await service.insertTextFilter(
fieldId: textField.id, fieldId: textField.id,
filterId: textFilter.filter.id, filterId: textFilter.filter.id,
condition: TextFilterConditionPB.Is, condition: TextFilterConditionPB.TextIs,
content: "ABC", content: "ABC",
); );
await gridResponseFuture(); await gridResponseFuture();
assert( assert(
menuBloc.state.filters.first.textFilter()!.condition == menuBloc.state.filters.first.textFilter()!.condition ==
TextFilterConditionPB.Is, TextFilterConditionPB.TextIs,
); );
assert(menuBloc.state.filters.first.textFilter()!.content == "ABC"); assert(menuBloc.state.filters.first.textFilter()!.content == "ABC");
}); });

View File

@ -118,7 +118,7 @@ void main() {
// create a new filter // create a new filter
await service.insertTextFilter( await service.insertTextFilter(
fieldId: textField.id, fieldId: textField.id,
condition: TextFilterConditionPB.Is, condition: TextFilterConditionPB.TextIs,
content: "A", content: "A",
); );
await gridResponseFuture(); await gridResponseFuture();
@ -132,7 +132,7 @@ void main() {
await service.insertTextFilter( await service.insertTextFilter(
fieldId: textField.id, fieldId: textField.id,
filterId: textFilter.filter.id, filterId: textFilter.filter.id,
condition: TextFilterConditionPB.Is, condition: TextFilterConditionPB.TextIs,
content: "B", content: "B",
); );
await gridResponseFuture(); await gridResponseFuture();
@ -142,7 +142,7 @@ void main() {
await service.insertTextFilter( await service.insertTextFilter(
fieldId: textField.id, fieldId: textField.id,
filterId: textFilter.filter.id, filterId: textFilter.filter.id,
condition: TextFilterConditionPB.Is, condition: TextFilterConditionPB.TextIs,
content: "b", content: "b",
); );
await gridResponseFuture(); await gridResponseFuture();
@ -152,7 +152,7 @@ void main() {
await service.insertTextFilter( await service.insertTextFilter(
fieldId: textField.id, fieldId: textField.id,
filterId: textFilter.filter.id, filterId: textFilter.filter.id,
condition: TextFilterConditionPB.Is, condition: TextFilterConditionPB.TextIs,
content: "C", content: "C",
); );
await gridResponseFuture(); await gridResponseFuture();

View File

@ -3,6 +3,7 @@ import {
ChecklistFilterConditionPB, ChecklistFilterConditionPB,
FieldType, FieldType,
NumberFilterConditionPB, NumberFilterConditionPB,
SelectOptionFilterConditionPB,
TextFilterConditionPB, TextFilterConditionPB,
} from '@/services/backend'; } from '@/services/backend';
import { UndeterminedFilter } from '$app/application/database'; import { UndeterminedFilter } from '$app/application/database';
@ -12,7 +13,7 @@ export function getDefaultFilter(fieldType: FieldType): UndeterminedFilter['data
case FieldType.RichText: case FieldType.RichText:
case FieldType.URL: case FieldType.URL:
return { return {
condition: TextFilterConditionPB.Contains, condition: TextFilterConditionPB.TextContains,
content: '', content: '',
}; };
case FieldType.Number: case FieldType.Number:
@ -27,6 +28,14 @@ export function getDefaultFilter(fieldType: FieldType): UndeterminedFilter['data
return { return {
condition: ChecklistFilterConditionPB.IsIncomplete, condition: ChecklistFilterConditionPB.IsIncomplete,
}; };
case FieldType.SingleSelect:
return {
condition: SelectOptionFilterConditionPB.OptionIs,
};
case FieldType.MultiSelect:
return {
condition: SelectOptionFilterConditionPB.OptionContains,
};
default: default:
return; return;
} }

View File

@ -5,7 +5,7 @@ import {
FilterPB, FilterPB,
NumberFilterConditionPB, NumberFilterConditionPB,
NumberFilterPB, NumberFilterPB,
SelectOptionConditionPB, SelectOptionFilterConditionPB,
SelectOptionFilterPB, SelectOptionFilterPB,
TextFilterConditionPB, TextFilterConditionPB,
TextFilterPB, TextFilterPB,
@ -66,7 +66,7 @@ export interface ChecklistFilterData {
} }
export interface SelectFilterData { export interface SelectFilterData {
condition?: SelectOptionConditionPB; condition?: SelectOptionFilterConditionPB;
optionIds?: string[]; optionIds?: string[];
} }

View File

@ -6,7 +6,7 @@ import {
DateFilterConditionPB, DateFilterConditionPB,
FieldType, FieldType,
NumberFilterConditionPB, NumberFilterConditionPB,
SelectOptionConditionPB, SelectOptionFilterConditionPB,
TextFilterConditionPB, TextFilterConditionPB,
} from '@/services/backend'; } from '@/services/backend';
@ -30,27 +30,27 @@ function FilterConditionSelect({
case FieldType.URL: case FieldType.URL:
return [ return [
{ {
value: TextFilterConditionPB.Contains, value: TextFilterConditionPB.TextContains,
text: t('grid.textFilter.contains'), text: t('grid.textFilter.contains'),
}, },
{ {
value: TextFilterConditionPB.DoesNotContain, value: TextFilterConditionPB.TextDoesNotContain,
text: t('grid.textFilter.doesNotContain'), text: t('grid.textFilter.doesNotContain'),
}, },
{ {
value: TextFilterConditionPB.StartsWith, value: TextFilterConditionPB.TextStartsWith,
text: t('grid.textFilter.startWith'), text: t('grid.textFilter.startWith'),
}, },
{ {
value: TextFilterConditionPB.EndsWith, value: TextFilterConditionPB.TextEndsWith,
text: t('grid.textFilter.endsWith'), text: t('grid.textFilter.endsWith'),
}, },
{ {
value: TextFilterConditionPB.Is, value: TextFilterConditionPB.TextIs,
text: t('grid.textFilter.is'), text: t('grid.textFilter.is'),
}, },
{ {
value: TextFilterConditionPB.IsNot, value: TextFilterConditionPB.TextIsNot,
text: t('grid.textFilter.isNot'), text: t('grid.textFilter.isNot'),
}, },
{ {
@ -63,26 +63,51 @@ function FilterConditionSelect({
}, },
]; ];
case FieldType.SingleSelect: case FieldType.SingleSelect:
return [
{
value: SelectOptionFilterConditionPB.OptionIs,
text: t('grid.selectOptionFilter.is'),
},
{
value: SelectOptionFilterConditionPB.OptionIsNot,
text: t('grid.selectOptionFilter.isNot'),
},
{
value: SelectOptionFilterConditionPB.OptionIsEmpty,
text: t('grid.selectOptionFilter.isEmpty'),
},
{
value: SelectOptionFilterConditionPB.OptionIsNotEmpty,
text: t('grid.selectOptionFilter.isNotEmpty'),
},
];
case FieldType.MultiSelect: case FieldType.MultiSelect:
return [ return [
{ {
value: SelectOptionConditionPB.OptionIs, value: SelectOptionFilterConditionPB.OptionIs,
text: t('grid.singleSelectOptionFilter.is'), text: t('grid.selectOptionFilter.is'),
}, },
{ {
value: SelectOptionConditionPB.OptionIsNot, value: SelectOptionFilterConditionPB.OptionIsNot,
text: t('grid.singleSelectOptionFilter.isNot'), text: t('grid.selectOptionFilter.isNot'),
}, },
{ {
value: SelectOptionConditionPB.OptionIsEmpty, value: SelectOptionFilterConditionPB.OptionContains,
text: t('grid.singleSelectOptionFilter.isEmpty'), text: t('grid.selectOptionFilter.contains'),
}, },
{ {
value: SelectOptionConditionPB.OptionIsNotEmpty, value: SelectOptionFilterConditionPB.OptionDoesNotContain,
text: t('grid.singleSelectOptionFilter.isNotEmpty'), text: t('grid.selectOptionFilter.doesNotContain'),
},
{
value: SelectOptionFilterConditionPB.OptionIsEmpty,
text: t('grid.selectOptionFilter.isEmpty'),
},
{
value: SelectOptionFilterConditionPB.OptionIsNotEmpty,
text: t('grid.selectOptionFilter.isNotEmpty'),
}, },
]; ];
case FieldType.Number: case FieldType.Number:
return [ return [
{ {

View File

@ -7,7 +7,7 @@ import {
} from '$app/application/database'; } from '$app/application/database';
import { Tag } from '$app/components/database/components/field_types/select/Tag'; import { Tag } from '$app/components/database/components/field_types/select/Tag';
import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg'; import { ReactComponent as SelectCheckSvg } from '$app/assets/select-check.svg';
import { SelectOptionConditionPB } from '@/services/backend'; import { SelectOptionFilterConditionPB } from '@/services/backend';
import { useTypeOption } from '$app/components/database'; import { useTypeOption } from '$app/components/database';
import KeyboardNavigation, { import KeyboardNavigation, {
KeyboardNavigationOption, KeyboardNavigationOption,
@ -42,8 +42,8 @@ function SelectFilter({ onClose, filter, field, onChange }: Props) {
const showOptions = const showOptions =
options.length > 0 && options.length > 0 &&
condition !== SelectOptionConditionPB.OptionIsEmpty && condition !== SelectOptionFilterConditionPB.OptionIsEmpty &&
condition !== SelectOptionConditionPB.OptionIsNotEmpty; condition !== SelectOptionFilterConditionPB.OptionIsNotEmpty;
const handleChange = ({ const handleChange = ({
condition, condition,

View File

@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
import { SelectFilterData, SelectTypeOption } from '$app/application/database'; import { SelectFilterData, SelectTypeOption } from '$app/application/database';
import { useStaticTypeOption } from '$app/components/database'; import { useStaticTypeOption } from '$app/components/database';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { SelectOptionConditionPB } from '@/services/backend'; import { SelectOptionFilterConditionPB } from '@/services/backend';
function SelectFilterValue({ data, fieldId }: { data: SelectFilterData; fieldId: string }) { function SelectFilterValue({ data, fieldId }: { data: SelectFilterData; fieldId: string }) {
const typeOption = useStaticTypeOption<SelectTypeOption>(fieldId); const typeOption = useStaticTypeOption<SelectTypeOption>(fieldId);
@ -19,13 +19,13 @@ function SelectFilterValue({ data, fieldId }: { data: SelectFilterData; fieldId:
.join(', '); .join(', ');
switch (data.condition) { switch (data.condition) {
case SelectOptionConditionPB.OptionIs: case SelectOptionFilterConditionPB.OptionIs:
return `: ${options}`; return `: ${options}`;
case SelectOptionConditionPB.OptionIsNot: case SelectOptionFilterConditionPB.OptionIsNot:
return `: ${t('grid.textFilter.choicechipPrefix.isNot')} ${options}`; return `: ${t('grid.textFilter.choicechipPrefix.isNot')} ${options}`;
case SelectOptionConditionPB.OptionIsEmpty: case SelectOptionFilterConditionPB.OptionIsEmpty:
return `: ${t('grid.textFilter.choicechipPrefix.isEmpty')}`; return `: ${t('grid.textFilter.choicechipPrefix.isEmpty')}`;
case SelectOptionConditionPB.OptionIsNotEmpty: case SelectOptionFilterConditionPB.OptionIsNotEmpty:
return `: ${t('grid.textFilter.choicechipPrefix.isNotEmpty')}`; return `: ${t('grid.textFilter.choicechipPrefix.isNotEmpty')}`;
default: default:
return ''; return '';

View File

@ -9,15 +9,15 @@ function TextFilterValue({ data }: { data: TextFilterData }) {
const value = useMemo(() => { const value = useMemo(() => {
if (!data.content) return ''; if (!data.content) return '';
switch (data.condition) { switch (data.condition) {
case TextFilterConditionPB.Contains: case TextFilterConditionPB.TextContains:
case TextFilterConditionPB.Is: case TextFilterConditionPB.TextIs:
return `: ${data.content}`; return `: ${data.content}`;
case TextFilterConditionPB.DoesNotContain: case TextFilterConditionPB.TextDoesNotContain:
case TextFilterConditionPB.IsNot: case TextFilterConditionPB.TextIsNot:
return `: ${t('grid.textFilter.choicechipPrefix.isNot')} ${data.content}`; return `: ${t('grid.textFilter.choicechipPrefix.isNot')} ${data.content}`;
case TextFilterConditionPB.StartsWith: case TextFilterConditionPB.TextStartsWith:
return `: ${t('grid.textFilter.choicechipPrefix.startWith')} ${data.content}`; return `: ${t('grid.textFilter.choicechipPrefix.startWith')} ${data.content}`;
case TextFilterConditionPB.EndsWith: case TextFilterConditionPB.TextEndsWith:
return `: ${t('grid.textFilter.choicechipPrefix.endWith')} ${data.content}`; return `: ${t('grid.textFilter.choicechipPrefix.endWith')} ${data.content}`;
case TextFilterConditionPB.TextIsEmpty: case TextFilterConditionPB.TextIsEmpty:
return `: ${t('grid.textFilter.choicechipPrefix.isEmpty')}`; return `: ${t('grid.textFilter.choicechipPrefix.isEmpty')}`;

View File

@ -422,13 +422,9 @@
"isComplete": "አይደለም", "isComplete": "አይደለም",
"isIncomplted": "ባዶ ነው" "isIncomplted": "ባዶ ነው"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "ነው", "is": "ነው",
"isNot": "አይደለም", "isNot": "አይደለም",
"isEmpty": "ባዶ ነው",
"isNotEmpty": "ባዶ አይደለም"
},
"multiSelectOptionFilter": {
"contains": "ይይዛል", "contains": "ይይዛል",
"doesNotContain": "አይይዝም", "doesNotContain": "አይይዝም",
"isEmpty": "ባዶ ነው", "isEmpty": "ባዶ ነው",

View File

@ -486,13 +486,9 @@
"isComplete": "كاملة", "isComplete": "كاملة",
"isIncomplted": "غير مكتمل" "isIncomplted": "غير مكتمل"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "يكون", "is": "يكون",
"isNot": "ليس", "isNot": "ليس",
"isEmpty": "فارغ",
"isNotEmpty": "ليس فارغا"
},
"multiSelectOptionFilter": {
"contains": "يتضمن", "contains": "يتضمن",
"doesNotContain": "لا يحتوي", "doesNotContain": "لا يحتوي",
"isEmpty": "فارغ", "isEmpty": "فارغ",
@ -1172,4 +1168,4 @@
"addField": "إضافة حقل", "addField": "إضافة حقل",
"userIcon": "رمز المستخدم" "userIcon": "رمز المستخدم"
} }
} }

View File

@ -459,13 +459,9 @@
"isComplete": "està completa", "isComplete": "està completa",
"isIncomplted": "és incompleta" "isIncomplted": "és incompleta"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "És", "is": "És",
"isNot": "No és", "isNot": "No és",
"isEmpty": "Està buit",
"isNotEmpty": "No està buit"
},
"multiSelectOptionFilter": {
"contains": "Conté", "contains": "Conté",
"doesNotContain": "No conté", "doesNotContain": "No conté",
"isEmpty": "Està buit", "isEmpty": "Està buit",
@ -811,4 +807,4 @@
"deleteContentTitle": "Esteu segur que voleu suprimir {pageType}?", "deleteContentTitle": "Esteu segur que voleu suprimir {pageType}?",
"deleteContentCaption": "si suprimiu aquest {pageType}, podeu restaurar-lo des de la paperera." "deleteContentCaption": "si suprimiu aquest {pageType}, podeu restaurar-lo des de la paperera."
} }
} }

View File

@ -356,13 +356,9 @@
"isComplete": "تەواوە", "isComplete": "تەواوە",
"isIncomplted": "ناتەواوە" "isIncomplted": "ناتەواوە"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "هەیە", "is": "هەیە",
"isNot": "نییە", "isNot": "نییە",
"isEmpty": "به‌تاڵه‌",
"isNotEmpty": "بەتاڵ نییە"
},
"multiSelectOptionFilter": {
"contains": "لەخۆ دەگرێت", "contains": "لەخۆ دەگرێت",
"doesNotContain": "لەخۆناگرێت", "doesNotContain": "لەخۆناگرێت",
"isEmpty": "به‌تاڵه‌", "isEmpty": "به‌تاڵه‌",
@ -673,4 +669,4 @@
"frequentlyUsed": "زۆرجار بەکارت هێناوە" "frequentlyUsed": "زۆرجار بەکارت هێناوە"
} }
} }
} }

View File

@ -503,13 +503,9 @@
"isComplete": "ist komplett", "isComplete": "ist komplett",
"isIncomplted": "ist unvollständig" "isIncomplted": "ist unvollständig"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Ist", "is": "Ist",
"isNot": "Ist nicht", "isNot": "Ist nicht",
"isEmpty": "Ist leer",
"isNotEmpty": "Ist nicht leer"
},
"multiSelectOptionFilter": {
"contains": "Enthält", "contains": "Enthält",
"doesNotContain": "Beinhaltet nicht", "doesNotContain": "Beinhaltet nicht",
"isEmpty": "Ist leer", "isEmpty": "Ist leer",
@ -1202,4 +1198,4 @@
"addField": "Ein Feld hinzufügen", "addField": "Ein Feld hinzufügen",
"userIcon": "Nutzerbild" "userIcon": "Nutzerbild"
} }
} }

View File

@ -570,13 +570,9 @@
"isComplete": "is complete", "isComplete": "is complete",
"isIncomplted": "is incomplete" "isIncomplted": "is incomplete"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Is", "is": "Is",
"isNot": "Is not", "isNot": "Is not",
"isEmpty": "Is empty",
"isNotEmpty": "Is not empty"
},
"multiSelectOptionFilter": {
"contains": "Contains", "contains": "Contains",
"doesNotContain": "Does not contain", "doesNotContain": "Does not contain",
"isEmpty": "Is empty", "isEmpty": "Is empty",

View File

@ -500,13 +500,9 @@
"isComplete": "Esta completo", "isComplete": "Esta completo",
"isIncomplted": "esta incompleto" "isIncomplted": "esta incompleto"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Es", "is": "Es",
"isNot": "No es", "isNot": "No es",
"isEmpty": "Esta vacio",
"isNotEmpty": "No está vacío"
},
"multiSelectOptionFilter": {
"contains": "Contiene", "contains": "Contiene",
"doesNotContain": "No contiene", "doesNotContain": "No contiene",
"isEmpty": "Esta vacio", "isEmpty": "Esta vacio",
@ -1063,4 +1059,4 @@
"backgroundColorPink": "fondo rosa", "backgroundColorPink": "fondo rosa",
"backgroundColorRed": "fondo rojo" "backgroundColorRed": "fondo rojo"
} }
} }

View File

@ -323,13 +323,9 @@
"isComplete": "osatu da", "isComplete": "osatu da",
"isIncomplted": "osatu gabe dago" "isIncomplted": "osatu gabe dago"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "da", "is": "da",
"isNot": "Ez da", "isNot": "Ez da",
"isEmpty": "Hutsa dago",
"isNotEmpty": "Ez dago hutsik"
},
"multiSelectOptionFilter": {
"contains": "Duen", "contains": "Duen",
"doesNotContain": "Ez dauka", "doesNotContain": "Ez dauka",
"isEmpty": "Hutsa dago", "isEmpty": "Hutsa dago",
@ -600,4 +596,4 @@
"deleteContentTitle": "Ziur {pageType} ezabatu nahi duzula?", "deleteContentTitle": "Ziur {pageType} ezabatu nahi duzula?",
"deleteContentCaption": "{pageType} hau ezabatzen baduzu, zaborrontzitik leheneratu dezakezu." "deleteContentCaption": "{pageType} hau ezabatzen baduzu, zaborrontzitik leheneratu dezakezu."
} }
} }

View File

@ -356,13 +356,9 @@
"isComplete": "کامل است", "isComplete": "کامل است",
"isIncomplted": "کامل نیست" "isIncomplted": "کامل نیست"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "است", "is": "است",
"isNot": "نیست", "isNot": "نیست",
"isEmpty": "خالی است",
"isNotEmpty": "خالی نیست"
},
"multiSelectOptionFilter": {
"contains": "شامل", "contains": "شامل",
"doesNotContain": "شامل نیست", "doesNotContain": "شامل نیست",
"isEmpty": "خالی است", "isEmpty": "خالی است",
@ -673,4 +669,4 @@
"frequentlyUsed": "استفاده‌شده" "frequentlyUsed": "استفاده‌شده"
} }
} }
} }

View File

@ -521,13 +521,9 @@
"isComplete": "fait", "isComplete": "fait",
"isIncomplted": "pas fait" "isIncomplted": "pas fait"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Est", "is": "Est",
"isNot": "N'est pas", "isNot": "N'est pas",
"isEmpty": "Est vide",
"isNotEmpty": "N'est pas vide"
},
"multiSelectOptionFilter": {
"contains": "Contient", "contains": "Contient",
"doesNotContain": "Ne contient pas", "doesNotContain": "Ne contient pas",
"isEmpty": "Est vide", "isEmpty": "Est vide",

View File

@ -542,13 +542,9 @@
"isComplete": "fait", "isComplete": "fait",
"isIncomplted": "pas fait" "isIncomplted": "pas fait"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Est", "is": "Est",
"isNot": "N'est pas", "isNot": "N'est pas",
"isEmpty": "Est vide",
"isNotEmpty": "N'est pas vide"
},
"multiSelectOptionFilter": {
"contains": "Contient", "contains": "Contient",
"doesNotContain": "Ne contient pas", "doesNotContain": "Ne contient pas",
"isEmpty": "Est vide", "isEmpty": "Est vide",

File diff suppressed because it is too large Load Diff

View File

@ -325,13 +325,9 @@
"isComplete": "teljes", "isComplete": "teljes",
"isIncomplted": "hiányos" "isIncomplted": "hiányos"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Is", "is": "Is",
"isNot": "Nem", "isNot": "Nem",
"isEmpty": "Üres",
"isNotEmpty": "Nem üres"
},
"multiSelectOptionFilter": {
"contains": "Tartalmaz", "contains": "Tartalmaz",
"doesNotContain": "Nem tartalmaz", "doesNotContain": "Nem tartalmaz",
"isEmpty": "Üres", "isEmpty": "Üres",
@ -598,4 +594,4 @@
"deleteContentTitle": "Biztosan törli a következőt: {pageType}?", "deleteContentTitle": "Biztosan törli a következőt: {pageType}?",
"deleteContentCaption": "ha törli ezt a {pageType} oldalt, visszaállíthatja a kukából." "deleteContentCaption": "ha törli ezt a {pageType} oldalt, visszaállíthatja a kukából."
} }
} }

View File

@ -452,13 +452,9 @@
"isComplete": "selesai", "isComplete": "selesai",
"isIncomplted": "tidak lengkap" "isIncomplted": "tidak lengkap"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Adalah", "is": "Adalah",
"isNot": "Tidak", "isNot": "Tidak",
"isEmpty": "Kosong",
"isNotEmpty": "Tidak kosong"
},
"multiSelectOptionFilter": {
"contains": "Mengandung", "contains": "Mengandung",
"doesNotContain": "Tidak mengandung", "doesNotContain": "Tidak mengandung",
"isEmpty": "Kosong", "isEmpty": "Kosong",
@ -1021,4 +1017,4 @@
"noFavorite": "Tidak ada halaman favorit", "noFavorite": "Tidak ada halaman favorit",
"noFavoriteHintText": "Geser halaman ke kiri untuk menambahkannya ke favorit Anda" "noFavoriteHintText": "Geser halaman ke kiri untuk menambahkannya ke favorit Anda"
} }
} }

View File

@ -516,13 +516,9 @@
"isComplete": "è completo", "isComplete": "è completo",
"isIncomplted": "è incompleto" "isIncomplted": "è incompleto"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "È", "is": "È",
"isNot": "Non è", "isNot": "Non è",
"isEmpty": "È vuoto",
"isNotEmpty": "Non è vuoto"
},
"multiSelectOptionFilter": {
"contains": "Contiene", "contains": "Contiene",
"doesNotContain": "Non contiene", "doesNotContain": "Non contiene",
"isEmpty": "È vuoto", "isEmpty": "È vuoto",

View File

@ -412,13 +412,9 @@
"isComplete": "完了", "isComplete": "完了",
"isIncomplted": "未完了" "isIncomplted": "未完了"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "等しい", "is": "等しい",
"isNot": "等しくない", "isNot": "等しくない",
"isEmpty": "空である",
"isNotEmpty": "空ではない"
},
"multiSelectOptionFilter": {
"contains": "を含む", "contains": "を含む",
"doesNotContain": "を含まない", "doesNotContain": "を含まない",
"isEmpty": "空である", "isEmpty": "空である",
@ -685,4 +681,4 @@
"deleteContentTitle": "{pageType} を削除してもよろしいですか?", "deleteContentTitle": "{pageType} を削除してもよろしいですか?",
"deleteContentCaption": "この {pageType} を削除しても、ゴミ箱から復元できます。" "deleteContentCaption": "この {pageType} を削除しても、ゴミ箱から復元できます。"
} }
} }

View File

@ -324,13 +324,9 @@
"isComplete": "완료되었습니다", "isComplete": "완료되었습니다",
"isIncomplted": "불완전하다" "isIncomplted": "불완전하다"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "~이다", "is": "~이다",
"isNot": "아니다", "isNot": "아니다",
"isEmpty": "비었다",
"isNotEmpty": "비어 있지 않음"
},
"multiSelectOptionFilter": {
"contains": "포함", "contains": "포함",
"doesNotContain": "포함되어 있지 않다", "doesNotContain": "포함되어 있지 않다",
"isEmpty": "비었다", "isEmpty": "비었다",
@ -597,4 +593,4 @@
"deleteContentTitle": "{pageType}을(를) 삭제하시겠습니까?", "deleteContentTitle": "{pageType}을(를) 삭제하시겠습니까?",
"deleteContentCaption": "이 {pageType}을(를) 삭제하면 휴지통에서 복원할 수 있습니다." "deleteContentCaption": "이 {pageType}을(를) 삭제하면 휴지통에서 복원할 수 있습니다."
} }
} }

View File

@ -454,13 +454,9 @@
"isComplete": "jest kompletna", "isComplete": "jest kompletna",
"isIncomplted": "jest niekompletna" "isIncomplted": "jest niekompletna"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Jest", "is": "Jest",
"isNot": "Nie jest", "isNot": "Nie jest",
"isEmpty": "Jest pusty",
"isNotEmpty": "Nie jest pusty"
},
"multiSelectOptionFilter": {
"contains": "Zawiera", "contains": "Zawiera",
"doesNotContain": "Nie zawiera", "doesNotContain": "Nie zawiera",
"isEmpty": "Jest pusty", "isEmpty": "Jest pusty",
@ -1076,4 +1072,4 @@
"language": "Język", "language": "Język",
"font": "Czcionka" "font": "Czcionka"
} }
} }

View File

@ -511,13 +511,9 @@
"isComplete": "está completo", "isComplete": "está completo",
"isIncomplted": "está imcompleto" "isIncomplted": "está imcompleto"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Está", "is": "Está",
"isNot": "Não está", "isNot": "Não está",
"isEmpty": "Está vazio",
"isNotEmpty": "Não está vazio"
},
"multiSelectOptionFilter": {
"contains": "Contém", "contains": "Contém",
"doesNotContain": "Não contém", "doesNotContain": "Não contém",
"isEmpty": "Está vazio", "isEmpty": "Está vazio",
@ -1211,4 +1207,4 @@
"addField": "Adicionar campo", "addField": "Adicionar campo",
"userIcon": "Ícone do usuário" "userIcon": "Ícone do usuário"
} }
} }

View File

@ -426,13 +426,9 @@
"isComplete": "está completo", "isComplete": "está completo",
"isIncomplted": "está incompleto" "isIncomplted": "está incompleto"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "É", "is": "É",
"isNot": "não é", "isNot": "não é",
"isEmpty": "Está vazia",
"isNotEmpty": "Não está vazio"
},
"multiSelectOptionFilter": {
"contains": "contém", "contains": "contém",
"doesNotContain": "Não contém", "doesNotContain": "Não contém",
"isEmpty": "Está vazia", "isEmpty": "Está vazia",
@ -856,4 +852,4 @@
"noResult": "Nenhum resultado", "noResult": "Nenhum resultado",
"caseSensitive": "Maiúsculas e minúsculas" "caseSensitive": "Maiúsculas e minúsculas"
} }
} }

View File

@ -528,13 +528,9 @@
"isComplete": "завершено", "isComplete": "завершено",
"isIncomplted": "не завершено" "isIncomplted": "не завершено"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Является", "is": "Является",
"isNot": "Не является", "isNot": "Не является",
"isEmpty": "Пусто",
"isNotEmpty": "Не пусто"
},
"multiSelectOptionFilter": {
"contains": "Содержит", "contains": "Содержит",
"doesNotContain": "Не содержит", "doesNotContain": "Не содержит",
"isEmpty": "Пусто", "isEmpty": "Пусто",

View File

@ -322,13 +322,9 @@
"isComplete": "är komplett", "isComplete": "är komplett",
"isIncomplted": "är ofullständig" "isIncomplted": "är ofullständig"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Är", "is": "Är",
"isNot": "Är inte", "isNot": "Är inte",
"isEmpty": "Är tom",
"isNotEmpty": "Är inte tom"
},
"multiSelectOptionFilter": {
"contains": "Innehåller", "contains": "Innehåller",
"doesNotContain": "Innehåller inte", "doesNotContain": "Innehåller inte",
"isEmpty": "Är tom", "isEmpty": "Är tom",
@ -595,4 +591,4 @@
"deleteContentTitle": "Är du säker på att du vill ta bort {pageType}?", "deleteContentTitle": "Är du säker på att du vill ta bort {pageType}?",
"deleteContentCaption": "om du tar bort denna {pageType} kan du återställa den från papperskorgen." "deleteContentCaption": "om du tar bort denna {pageType} kan du återställa den från papperskorgen."
} }
} }

View File

@ -478,13 +478,9 @@
"isComplete": "เสร็จสมบูรณ์", "isComplete": "เสร็จสมบูรณ์",
"isIncomplted": "ไม่เสร็จสมบูรณ์" "isIncomplted": "ไม่เสร็จสมบูรณ์"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "เป็น", "is": "เป็น",
"isNot": "ไม่เป็น", "isNot": "ไม่เป็น",
"isEmpty": "ว่างเปล่า",
"isNotEmpty": "ไม่ว่างเปล่า"
},
"multiSelectOptionFilter": {
"contains": "ประกอบด้วย", "contains": "ประกอบด้วย",
"doesNotContain": "ไม่ประกอบด้วย", "doesNotContain": "ไม่ประกอบด้วย",
"isEmpty": "ว่างเปล่า", "isEmpty": "ว่างเปล่า",

View File

@ -454,13 +454,9 @@
"isComplete": "Tamamlanmış", "isComplete": "Tamamlanmış",
"isIncomplted": "Tamamlanmamış" "isIncomplted": "Tamamlanmamış"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Şu olan", "is": "Şu olan",
"isNot": "Şu olmayan", "isNot": "Şu olmayan",
"isEmpty": "Boş olan",
"isNotEmpty": "Boş olmayan"
},
"multiSelectOptionFilter": {
"contains": "Şunu içeren", "contains": "Şunu içeren",
"doesNotContain": "Şunu içermeyen", "doesNotContain": "Şunu içermeyen",
"isEmpty": "Boş olan", "isEmpty": "Boş olan",
@ -1093,4 +1089,4 @@
"language": "Dil", "language": "Dil",
"font": "Yazı tipi" "font": "Yazı tipi"
} }
} }

View File

@ -281,7 +281,6 @@
"auto": "АВТО", "auto": "АВТО",
"fallback": "Такий же, як і напрямок макету" "fallback": "Такий же, як і напрямок макету"
}, },
"themeUpload": { "themeUpload": {
"button": "Завантажити", "button": "Завантажити",
"uploadTheme": "Завантажити тему", "uploadTheme": "Завантажити тему",
@ -347,7 +346,6 @@
"exportFileFail": "Помилка експорту файлу!", "exportFileFail": "Помилка експорту файлу!",
"export": "Експорт" "export": "Експорт"
}, },
"user": { "user": {
"name": "Ім'я", "name": "Ім'я",
"email": "Електронна пошта", "email": "Електронна пошта",
@ -416,13 +414,9 @@
"isComplete": "є завершено", "isComplete": "є завершено",
"isIncomplted": "є незавершено" "isIncomplted": "є незавершено"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "є", "is": "є",
"isNot": "не є", "isNot": "не є",
"isEmpty": "порожнє",
"isNotEmpty": "не порожнє"
},
"multiSelectOptionFilter": {
"contains": "Містить", "contains": "Містить",
"doesNotContain": "Не містить", "doesNotContain": "Не містить",
"isEmpty": "порожнє", "isEmpty": "порожнє",

View File

@ -391,7 +391,7 @@
"isComplete": "مکمل ہے", "isComplete": "مکمل ہے",
"isIncomplted": "نامکمل ہے" "isIncomplted": "نامکمل ہے"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "ہے", "is": "ہے",
"isNot": "نہیں ہے", "isNot": "نہیں ہے",
"isEmpty": "خالی ہے", "isEmpty": "خالی ہے",

View File

@ -491,13 +491,9 @@
"isComplete": "hoàn tất", "isComplete": "hoàn tất",
"isIncomplted": "chưa hoàn tất" "isIncomplted": "chưa hoàn tất"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "Là", "is": "Là",
"isNot": "Không phải", "isNot": "Không phải",
"isEmpty": "Rỗng",
"isNotEmpty": "Không rỗng"
},
"multiSelectOptionFilter": {
"contains": "Chứa", "contains": "Chứa",
"doesNotContain": "Không chứa", "doesNotContain": "Không chứa",
"isEmpty": "Rỗng", "isEmpty": "Rỗng",
@ -811,4 +807,4 @@
"font": "Phông chữ", "font": "Phông chữ",
"date": "Ngày" "date": "Ngày"
} }
} }

View File

@ -531,13 +531,9 @@
"isComplete": "已完成", "isComplete": "已完成",
"isIncomplted": "未完成" "isIncomplted": "未完成"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "是", "is": "是",
"isNot": "不是", "isNot": "不是",
"isEmpty": "为空",
"isNotEmpty": "不为空"
},
"multiSelectOptionFilter": {
"contains": "包含", "contains": "包含",
"doesNotContain": "不包含", "doesNotContain": "不包含",
"isEmpty": "为空", "isEmpty": "为空",

View File

@ -513,13 +513,9 @@
"isComplete": "已完成", "isComplete": "已完成",
"isIncomplted": "未完成" "isIncomplted": "未完成"
}, },
"singleSelectOptionFilter": { "selectOptionFilter": {
"is": "是", "is": "是",
"isNot": "不是", "isNot": "不是",
"isEmpty": "為空",
"isNotEmpty": "不為空"
},
"multiSelectOptionFilter": {
"contains": "包含", "contains": "包含",
"doesNotContain": "不包含", "doesNotContain": "不包含",
"isEmpty": "為空", "isEmpty": "為空",
@ -1261,4 +1257,4 @@
"userIcon": "使用者圖示" "userIcon": "使用者圖示"
}, },
"noLogFiles": "這裡沒有日誌記錄檔案" "noLogFiles": "這裡沒有日誌記錄檔案"
} }

View File

@ -8,38 +8,41 @@ use crate::services::{field::SelectOptionIds, filter::ParseFilterData};
#[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)] #[derive(Eq, PartialEq, ProtoBuf, Debug, Default, Clone)]
pub struct SelectOptionFilterPB { pub struct SelectOptionFilterPB {
#[pb(index = 1)] #[pb(index = 1)]
pub condition: SelectOptionConditionPB, pub condition: SelectOptionFilterConditionPB,
#[pb(index = 2)] #[pb(index = 2)]
pub option_ids: Vec<String>, pub option_ids: Vec<String>,
} }
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)] #[derive(Debug, Default, Clone, PartialEq, Eq, ProtoBuf_Enum)]
#[repr(u8)] #[repr(u8)]
#[derive(Default)] pub enum SelectOptionFilterConditionPB {
pub enum SelectOptionConditionPB {
#[default] #[default]
OptionIs = 0, OptionIs = 0,
OptionIsNot = 1, OptionIsNot = 1,
OptionIsEmpty = 2, OptionContains = 2,
OptionIsNotEmpty = 3, OptionDoesNotContain = 3,
OptionIsEmpty = 4,
OptionIsNotEmpty = 5,
} }
impl std::convert::From<SelectOptionConditionPB> for u32 { impl From<SelectOptionFilterConditionPB> for u32 {
fn from(value: SelectOptionConditionPB) -> Self { fn from(value: SelectOptionFilterConditionPB) -> Self {
value as u32 value as u32
} }
} }
impl std::convert::TryFrom<u8> for SelectOptionConditionPB { impl TryFrom<u8> for SelectOptionFilterConditionPB {
type Error = ErrorCode; type Error = ErrorCode;
fn try_from(value: u8) -> Result<Self, Self::Error> { fn try_from(value: u8) -> Result<Self, Self::Error> {
match value { match value {
0 => Ok(SelectOptionConditionPB::OptionIs), 0 => Ok(SelectOptionFilterConditionPB::OptionIs),
1 => Ok(SelectOptionConditionPB::OptionIsNot), 1 => Ok(SelectOptionFilterConditionPB::OptionIsNot),
2 => Ok(SelectOptionConditionPB::OptionIsEmpty), 2 => Ok(SelectOptionFilterConditionPB::OptionContains),
3 => Ok(SelectOptionConditionPB::OptionIsNotEmpty), 3 => Ok(SelectOptionFilterConditionPB::OptionDoesNotContain),
4 => Ok(SelectOptionFilterConditionPB::OptionIsEmpty),
5 => Ok(SelectOptionFilterConditionPB::OptionIsNotEmpty),
_ => Err(ErrorCode::InvalidParams), _ => Err(ErrorCode::InvalidParams),
} }
} }
@ -47,8 +50,8 @@ impl std::convert::TryFrom<u8> for SelectOptionConditionPB {
impl ParseFilterData for SelectOptionFilterPB { impl ParseFilterData for SelectOptionFilterPB {
fn parse(condition: u8, content: String) -> Self { fn parse(condition: u8, content: String) -> Self {
Self { Self {
condition: SelectOptionConditionPB::try_from(condition) condition: SelectOptionFilterConditionPB::try_from(condition)
.unwrap_or(SelectOptionConditionPB::OptionIs), .unwrap_or(SelectOptionFilterConditionPB::OptionIs),
option_ids: SelectOptionIds::from_str(&content) option_ids: SelectOptionIds::from_str(&content)
.unwrap_or_default() .unwrap_or_default()
.into_inner(), .into_inner(),

View File

@ -12,17 +12,16 @@ pub struct TextFilterPB {
pub content: String, pub content: String,
} }
#[derive(Debug, Clone, PartialEq, Eq, ProtoBuf_Enum)] #[derive(Debug, Clone, Default, PartialEq, Eq, ProtoBuf_Enum)]
#[repr(u8)] #[repr(u8)]
#[derive(Default)]
pub enum TextFilterConditionPB { pub enum TextFilterConditionPB {
#[default] #[default]
Is = 0, TextIs = 0,
IsNot = 1, TextIsNot = 1,
Contains = 2, TextContains = 2,
DoesNotContain = 3, TextDoesNotContain = 3,
StartsWith = 4, TextStartsWith = 4,
EndsWith = 5, TextEndsWith = 5,
TextIsEmpty = 6, TextIsEmpty = 6,
TextIsNotEmpty = 7, TextIsNotEmpty = 7,
} }
@ -38,12 +37,12 @@ impl std::convert::TryFrom<u8> for TextFilterConditionPB {
fn try_from(value: u8) -> Result<Self, Self::Error> { fn try_from(value: u8) -> Result<Self, Self::Error> {
match value { match value {
0 => Ok(TextFilterConditionPB::Is), 0 => Ok(TextFilterConditionPB::TextIs),
1 => Ok(TextFilterConditionPB::IsNot), 1 => Ok(TextFilterConditionPB::TextIsNot),
2 => Ok(TextFilterConditionPB::Contains), 2 => Ok(TextFilterConditionPB::TextContains),
3 => Ok(TextFilterConditionPB::DoesNotContain), 3 => Ok(TextFilterConditionPB::TextDoesNotContain),
4 => Ok(TextFilterConditionPB::StartsWith), 4 => Ok(TextFilterConditionPB::TextStartsWith),
5 => Ok(TextFilterConditionPB::EndsWith), 5 => Ok(TextFilterConditionPB::TextEndsWith),
6 => Ok(TextFilterConditionPB::TextIsEmpty), 6 => Ok(TextFilterConditionPB::TextIsEmpty),
7 => Ok(TextFilterConditionPB::TextIsNotEmpty), 7 => Ok(TextFilterConditionPB::TextIsNotEmpty),
_ => Err(ErrorCode::InvalidParams), _ => Err(ErrorCode::InvalidParams),
@ -54,7 +53,8 @@ impl std::convert::TryFrom<u8> for TextFilterConditionPB {
impl ParseFilterData for TextFilterPB { impl ParseFilterData for TextFilterPB {
fn parse(condition: u8, content: String) -> Self { fn parse(condition: u8, content: String) -> Self {
Self { Self {
condition: TextFilterConditionPB::try_from(condition).unwrap_or(TextFilterConditionPB::Is), condition: TextFilterConditionPB::try_from(condition)
.unwrap_or(TextFilterConditionPB::TextIs),
content, content,
} }
} }

View File

@ -1,23 +1,26 @@
use std::str::FromStr;
use rust_decimal::Decimal;
use crate::entities::{NumberFilterConditionPB, NumberFilterPB}; use crate::entities::{NumberFilterConditionPB, NumberFilterPB};
use crate::services::field::NumberCellFormat; use crate::services::field::NumberCellFormat;
use rust_decimal::prelude::Zero;
use rust_decimal::Decimal;
use std::str::FromStr;
impl NumberFilterPB { impl NumberFilterPB {
pub fn is_visible(&self, cell_data: &NumberCellFormat) -> Option<bool> { pub fn is_visible(&self, cell_data: &NumberCellFormat) -> Option<bool> {
let expected_decimal = Decimal::from_str(&self.content).unwrap_or_else(|_| Decimal::zero()); let expected_decimal = || Decimal::from_str(&self.content).ok();
let strategy = match self.condition { let strategy = match self.condition {
NumberFilterConditionPB::Equal => NumberFilterStrategy::Equal(expected_decimal), NumberFilterConditionPB::Equal => NumberFilterStrategy::Equal(expected_decimal()?),
NumberFilterConditionPB::NotEqual => NumberFilterStrategy::NotEqual(expected_decimal), NumberFilterConditionPB::NotEqual => NumberFilterStrategy::NotEqual(expected_decimal()?),
NumberFilterConditionPB::GreaterThan => NumberFilterStrategy::GreaterThan(expected_decimal), NumberFilterConditionPB::GreaterThan => {
NumberFilterConditionPB::LessThan => NumberFilterStrategy::LessThan(expected_decimal), NumberFilterStrategy::GreaterThan(expected_decimal()?)
},
NumberFilterConditionPB::LessThan => NumberFilterStrategy::LessThan(expected_decimal()?),
NumberFilterConditionPB::GreaterThanOrEqualTo => { NumberFilterConditionPB::GreaterThanOrEqualTo => {
NumberFilterStrategy::GreaterThanOrEqualTo(expected_decimal) NumberFilterStrategy::GreaterThanOrEqualTo(expected_decimal()?)
}, },
NumberFilterConditionPB::LessThanOrEqualTo => { NumberFilterConditionPB::LessThanOrEqualTo => {
NumberFilterStrategy::LessThanOrEqualTo(expected_decimal) NumberFilterStrategy::LessThanOrEqualTo(expected_decimal()?)
}, },
NumberFilterConditionPB::NumberIsEmpty => NumberFilterStrategy::Empty, NumberFilterConditionPB::NumberIsEmpty => NumberFilterStrategy::Empty,
NumberFilterConditionPB::NumberIsNotEmpty => NumberFilterStrategy::NotEmpty, NumberFilterConditionPB::NumberIsNotEmpty => NumberFilterStrategy::NotEmpty,

View File

@ -128,7 +128,7 @@ impl TypeOptionCellDataFilter for MultiSelectTypeOption {
cell_data: &<Self as TypeOption>::CellData, cell_data: &<Self as TypeOption>::CellData,
) -> bool { ) -> bool {
let selected_options = self.get_selected_options(cell_data.clone()).select_options; let selected_options = self.get_selected_options(cell_data.clone()).select_options;
filter.is_visible(&selected_options, FieldType::MultiSelect) filter.is_visible(&selected_options).unwrap_or(true)
} }
} }

View File

@ -1,106 +1,110 @@
#![allow(clippy::needless_collect)] use crate::entities::{SelectOptionFilterConditionPB, SelectOptionFilterPB};
use crate::entities::{FieldType, SelectOptionConditionPB, SelectOptionFilterPB};
use crate::services::field::SelectOption; use crate::services::field::SelectOption;
impl SelectOptionFilterPB { impl SelectOptionFilterPB {
pub fn is_visible(&self, selected_options: &[SelectOption], field_type: FieldType) -> bool { pub fn is_visible(&self, selected_options: &[SelectOption]) -> Option<bool> {
let selected_option_ids: Vec<&String> = let selected_option_ids = selected_options
selected_options.iter().map(|option| &option.id).collect(); .iter()
match self.condition { .map(|option| &option.id)
SelectOptionConditionPB::OptionIs => match field_type { .collect::<Vec<_>>();
FieldType::SingleSelect => {
if self.option_ids.is_empty() {
return true;
}
if selected_options.is_empty() { let get_non_empty_expected_options =
return false; || (!self.option_ids.is_empty()).then(|| self.option_ids.clone());
}
let required_options = self let strategy = match self.condition {
.option_ids SelectOptionFilterConditionPB::OptionIs => {
.iter() SelectOptionFilterStrategy::Is(get_non_empty_expected_options()?)
.filter(|id| selected_option_ids.contains(id))
.collect::<Vec<_>>();
!required_options.is_empty()
},
FieldType::MultiSelect => {
if self.option_ids.is_empty() {
return true;
}
let required_options = self
.option_ids
.iter()
.filter(|id| selected_option_ids.contains(id))
.collect::<Vec<_>>();
!required_options.is_empty()
},
_ => false,
}, },
SelectOptionConditionPB::OptionIsNot => match field_type { SelectOptionFilterConditionPB::OptionIsNot => {
FieldType::SingleSelect => { SelectOptionFilterStrategy::IsNot(get_non_empty_expected_options()?)
if self.option_ids.is_empty() {
return true;
}
if selected_options.is_empty() {
return false;
}
let required_options = self
.option_ids
.iter()
.filter(|id| selected_option_ids.contains(id))
.collect::<Vec<_>>();
required_options.is_empty()
},
FieldType::MultiSelect => {
let required_options = self
.option_ids
.iter()
.filter(|id| selected_option_ids.contains(id))
.collect::<Vec<_>>();
required_options.is_empty()
},
_ => false,
}, },
SelectOptionConditionPB::OptionIsEmpty => selected_option_ids.is_empty(), SelectOptionFilterConditionPB::OptionContains => {
SelectOptionConditionPB::OptionIsNotEmpty => !selected_option_ids.is_empty(), SelectOptionFilterStrategy::Contains(get_non_empty_expected_options()?)
},
SelectOptionFilterConditionPB::OptionDoesNotContain => {
SelectOptionFilterStrategy::DoesNotContain(get_non_empty_expected_options()?)
},
SelectOptionFilterConditionPB::OptionIsEmpty => SelectOptionFilterStrategy::IsEmpty,
SelectOptionFilterConditionPB::OptionIsNotEmpty => SelectOptionFilterStrategy::IsNotEmpty,
};
Some(strategy.filter(&selected_option_ids))
}
}
enum SelectOptionFilterStrategy {
Is(Vec<String>),
IsNot(Vec<String>),
Contains(Vec<String>),
DoesNotContain(Vec<String>),
IsEmpty,
IsNotEmpty,
}
impl SelectOptionFilterStrategy {
fn filter(self, selected_option_ids: &[&String]) -> bool {
match self {
SelectOptionFilterStrategy::Is(option_ids) => {
if selected_option_ids.is_empty() {
return false;
}
selected_option_ids.len() == option_ids.len()
&& selected_option_ids.iter().all(|id| option_ids.contains(id))
},
SelectOptionFilterStrategy::IsNot(option_ids) => {
if selected_option_ids.is_empty() {
return true;
}
selected_option_ids.len() != option_ids.len()
|| !selected_option_ids.iter().all(|id| option_ids.contains(id))
},
SelectOptionFilterStrategy::Contains(option_ids) => {
if selected_option_ids.is_empty() {
return false;
}
let required_options = option_ids
.into_iter()
.filter(|id| selected_option_ids.contains(&id))
.collect::<Vec<_>>();
!required_options.is_empty()
},
SelectOptionFilterStrategy::DoesNotContain(option_ids) => {
if selected_option_ids.is_empty() {
return true;
}
let required_options = option_ids
.into_iter()
.filter(|id| selected_option_ids.contains(&id))
.collect::<Vec<_>>();
required_options.is_empty()
},
SelectOptionFilterStrategy::IsEmpty => selected_option_ids.is_empty(),
SelectOptionFilterStrategy::IsNotEmpty => !selected_option_ids.is_empty(),
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::all)] use crate::entities::{SelectOptionFilterConditionPB, SelectOptionFilterPB};
use crate::entities::{FieldType, SelectOptionConditionPB, SelectOptionFilterPB};
use crate::services::field::SelectOption; use crate::services::field::SelectOption;
#[test] #[test]
fn select_option_filter_is_empty_test() { fn select_option_filter_is_empty_test() {
let option = SelectOption::new("A"); let option = SelectOption::new("A");
let filter = SelectOptionFilterPB { let filter = SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIsEmpty, condition: SelectOptionFilterConditionPB::OptionIsEmpty,
option_ids: vec![], option_ids: vec![],
}; };
assert_eq!(filter.is_visible(&vec![], FieldType::SingleSelect), true); assert_eq!(filter.is_visible(&[]), Some(true));
assert_eq!( assert_eq!(filter.is_visible(&[option.clone()]), Some(false));
filter.is_visible(&vec![option.clone()], FieldType::SingleSelect),
false,
);
assert_eq!(filter.is_visible(&vec![], FieldType::MultiSelect), true);
assert_eq!(
filter.is_visible(&vec![option], FieldType::MultiSelect),
false,
);
} }
#[test] #[test]
@ -108,157 +112,227 @@ mod tests {
let option_1 = SelectOption::new("A"); let option_1 = SelectOption::new("A");
let option_2 = SelectOption::new("B"); let option_2 = SelectOption::new("B");
let filter = SelectOptionFilterPB { let filter = SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIsNotEmpty, condition: SelectOptionFilterConditionPB::OptionIsNotEmpty,
option_ids: vec![option_1.id.clone(), option_2.id.clone()], option_ids: vec![option_1.id.clone(), option_2.id.clone()],
}; };
assert_eq!( assert_eq!(filter.is_visible(&[]), Some(false));
filter.is_visible(&vec![option_1.clone()], FieldType::SingleSelect), assert_eq!(filter.is_visible(&[option_1.clone()]), Some(true));
true
);
assert_eq!(filter.is_visible(&vec![], FieldType::SingleSelect), false,);
assert_eq!(
filter.is_visible(&vec![option_1.clone()], FieldType::MultiSelect),
true
);
assert_eq!(filter.is_visible(&vec![], FieldType::MultiSelect), false,);
} }
#[test] #[test]
fn single_select_option_filter_is_not_test() { fn select_option_filter_is_test() {
let option_1 = SelectOption::new("A"); let option_1 = SelectOption::new("A");
let option_2 = SelectOption::new("B"); let option_2 = SelectOption::new("B");
let option_3 = SelectOption::new("C"); let option_3 = SelectOption::new("C");
// no expected options
let filter = SelectOptionFilterPB { let filter = SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIsNot, condition: SelectOptionFilterConditionPB::OptionIs,
option_ids: vec![option_1.id.clone(), option_2.id.clone()], option_ids: vec![],
}; };
for (options, is_visible) in [
for (options, is_visible) in vec![ (vec![], None),
(vec![option_2.clone()], false), (vec![option_1.clone()], None),
(vec![option_1.clone()], false), (vec![option_1.clone(), option_2.clone()], None),
(vec![option_3.clone()], true),
(vec![option_1.clone(), option_2.clone()], false),
] { ] {
assert_eq!( assert_eq!(filter.is_visible(&options), is_visible);
filter.is_visible(&options, FieldType::SingleSelect),
is_visible
);
} }
}
#[test]
fn single_select_option_filter_is_test() {
let option_1 = SelectOption::new("A");
let option_2 = SelectOption::new("B");
let option_3 = SelectOption::new("c");
// one expected option
let filter = SelectOptionFilterPB { let filter = SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIs, condition: SelectOptionFilterConditionPB::OptionIs,
option_ids: vec![option_1.id.clone()], option_ids: vec![option_1.id.clone()],
}; };
for (options, is_visible) in vec![ for (options, is_visible) in [
(vec![option_1.clone()], true), (vec![], Some(false)),
(vec![option_2.clone()], false), (vec![option_1.clone()], Some(true)),
(vec![option_3.clone()], false), (vec![option_2.clone()], Some(false)),
(vec![option_1.clone(), option_2.clone()], true), (vec![option_3.clone()], Some(false)),
(vec![option_1.clone(), option_2.clone()], Some(false)),
] { ] {
assert_eq!( assert_eq!(filter.is_visible(&options), is_visible);
filter.is_visible(&options, FieldType::SingleSelect),
is_visible
);
} }
}
#[test]
fn single_select_option_filter_is_test2() {
let option_1 = SelectOption::new("A");
let option_2 = SelectOption::new("B");
// multiple expected options
let filter = SelectOptionFilterPB { let filter = SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIs, condition: SelectOptionFilterConditionPB::OptionIs,
option_ids: vec![],
};
for (options, is_visible) in vec![
(vec![option_1.clone()], true),
(vec![option_2.clone()], true),
(vec![option_1.clone(), option_2.clone()], true),
] {
assert_eq!(
filter.is_visible(&options, FieldType::SingleSelect),
is_visible
);
}
}
#[test]
fn multi_select_option_filter_not_contains_test() {
let option_1 = SelectOption::new("A");
let option_2 = SelectOption::new("B");
let option_3 = SelectOption::new("C");
let filter = SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIsNot,
option_ids: vec![option_1.id.clone(), option_2.id.clone()], option_ids: vec![option_1.id.clone(), option_2.id.clone()],
}; };
for (options, is_visible) in [
for (options, is_visible) in vec![ (vec![], Some(false)),
(vec![option_1.clone(), option_2.clone()], false), (vec![option_1.clone()], Some(false)),
(vec![option_1.clone()], false), (vec![option_1.clone(), option_2.clone()], Some(true)),
(vec![option_2.clone()], false),
(vec![option_3.clone()], true),
( (
vec![option_1.clone(), option_2.clone(), option_3.clone()], vec![option_1.clone(), option_2.clone(), option_3.clone()],
false, Some(false),
), ),
(vec![], true),
] { ] {
assert_eq!( assert_eq!(filter.is_visible(&options), is_visible);
filter.is_visible(&options, FieldType::MultiSelect),
is_visible
);
} }
} }
#[test] #[test]
fn multi_select_option_filter_contains_test() { fn select_option_filter_is_not_test() {
let option_1 = SelectOption::new("A"); let option_1 = SelectOption::new("A");
let option_2 = SelectOption::new("B"); let option_2 = SelectOption::new("B");
let option_3 = SelectOption::new("C"); let option_3 = SelectOption::new("C");
// no expected options
let filter = SelectOptionFilterPB { let filter = SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIs, condition: SelectOptionFilterConditionPB::OptionIsNot,
option_ids: vec![],
};
for (options, is_visible) in [
(vec![], None),
(vec![option_1.clone()], None),
(vec![option_1.clone(), option_2.clone()], None),
] {
assert_eq!(filter.is_visible(&options), is_visible);
}
// one expected option
let filter = SelectOptionFilterPB {
condition: SelectOptionFilterConditionPB::OptionIsNot,
option_ids: vec![option_1.id.clone()],
};
for (options, is_visible) in [
(vec![], Some(true)),
(vec![option_1.clone()], Some(false)),
(vec![option_2.clone()], Some(true)),
(vec![option_3.clone()], Some(true)),
(vec![option_1.clone(), option_2.clone()], Some(true)),
] {
assert_eq!(filter.is_visible(&options), is_visible);
}
// multiple expected options
let filter = SelectOptionFilterPB {
condition: SelectOptionFilterConditionPB::OptionIsNot,
option_ids: vec![option_1.id.clone(), option_2.id.clone()], option_ids: vec![option_1.id.clone(), option_2.id.clone()],
}; };
for (options, is_visible) in vec![ for (options, is_visible) in [
(vec![], Some(true)),
(vec![option_1.clone()], Some(true)),
(vec![option_1.clone(), option_2.clone()], Some(false)),
( (
vec![option_1.clone(), option_2.clone(), option_3.clone()], vec![option_1.clone(), option_2.clone(), option_3.clone()],
true, Some(true),
), ),
(vec![option_2.clone(), option_1.clone()], true),
(vec![option_2.clone()], true),
(vec![option_1.clone(), option_3.clone()], true),
(vec![option_3.clone()], false),
] { ] {
assert_eq!( assert_eq!(filter.is_visible(&options), is_visible);
filter.is_visible(&options, FieldType::MultiSelect),
is_visible
);
} }
} }
#[test] #[test]
fn multi_select_option_filter_contains_test2() { fn select_option_filter_contains_test() {
let option_1 = SelectOption::new("A"); let option_1 = SelectOption::new("A");
let option_2 = SelectOption::new("B");
let option_3 = SelectOption::new("C");
let option_4 = SelectOption::new("D");
// no expected options
let filter = SelectOptionFilterPB { let filter = SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIs, condition: SelectOptionFilterConditionPB::OptionContains,
option_ids: vec![], option_ids: vec![],
}; };
for (options, is_visible) in vec![(vec![option_1.clone()], true), (vec![], true)] { for (options, is_visible) in [
assert_eq!( (vec![], None),
filter.is_visible(&options, FieldType::MultiSelect), (vec![option_1.clone()], None),
is_visible (vec![option_1.clone(), option_2.clone()], None),
); ] {
assert_eq!(filter.is_visible(&options), is_visible);
}
// one expected option
let filter = SelectOptionFilterPB {
condition: SelectOptionFilterConditionPB::OptionContains,
option_ids: vec![option_1.id.clone()],
};
for (options, is_visible) in [
(vec![], Some(false)),
(vec![option_1.clone()], Some(true)),
(vec![option_2.clone()], Some(false)),
(vec![option_1.clone(), option_2.clone()], Some(true)),
(vec![option_3.clone(), option_4.clone()], Some(false)),
] {
assert_eq!(filter.is_visible(&options), is_visible);
}
// multiple expected options
let filter = SelectOptionFilterPB {
condition: SelectOptionFilterConditionPB::OptionContains,
option_ids: vec![option_1.id.clone(), option_2.id.clone()],
};
for (options, is_visible) in [
(vec![], Some(false)),
(vec![option_1.clone()], Some(true)),
(vec![option_3.clone()], Some(false)),
(vec![option_1.clone(), option_2.clone()], Some(true)),
(vec![option_1.clone(), option_3.clone()], Some(true)),
(vec![option_3.clone(), option_4.clone()], Some(false)),
(
vec![option_1.clone(), option_3.clone(), option_4.clone()],
Some(true),
),
] {
assert_eq!(filter.is_visible(&options), is_visible);
}
}
#[test]
fn select_option_filter_does_not_contain_test() {
let option_1 = SelectOption::new("A");
let option_2 = SelectOption::new("B");
let option_3 = SelectOption::new("C");
let option_4 = SelectOption::new("D");
// no expected options
let filter = SelectOptionFilterPB {
condition: SelectOptionFilterConditionPB::OptionDoesNotContain,
option_ids: vec![],
};
for (options, is_visible) in [
(vec![], None),
(vec![option_1.clone()], None),
(vec![option_1.clone(), option_2.clone()], None),
] {
assert_eq!(filter.is_visible(&options), is_visible);
}
// one expected option
let filter = SelectOptionFilterPB {
condition: SelectOptionFilterConditionPB::OptionDoesNotContain,
option_ids: vec![option_1.id.clone()],
};
for (options, is_visible) in [
(vec![], Some(true)),
(vec![option_1.clone()], Some(false)),
(vec![option_2.clone()], Some(true)),
(vec![option_1.clone(), option_2.clone()], Some(false)),
(vec![option_3.clone(), option_4.clone()], Some(true)),
] {
assert_eq!(filter.is_visible(&options), is_visible);
}
// multiple expected options
let filter = SelectOptionFilterPB {
condition: SelectOptionFilterConditionPB::OptionDoesNotContain,
option_ids: vec![option_1.id.clone(), option_2.id.clone()],
};
for (options, is_visible) in [
(vec![], Some(true)),
(vec![option_1.clone()], Some(false)),
(vec![option_3.clone()], Some(true)),
(vec![option_1.clone(), option_2.clone()], Some(false)),
(vec![option_1.clone(), option_3.clone()], Some(false)),
(vec![option_3.clone(), option_4.clone()], Some(true)),
(
vec![option_1.clone(), option_3.clone(), option_4.clone()],
Some(false),
),
] {
assert_eq!(filter.is_visible(&options), is_visible);
} }
} }
} }

View File

@ -119,7 +119,7 @@ impl TypeOptionCellDataFilter for SingleSelectTypeOption {
cell_data: &<Self as TypeOption>::CellData, cell_data: &<Self as TypeOption>::CellData,
) -> bool { ) -> bool {
let selected_options = self.get_selected_options(cell_data.clone()).select_options; let selected_options = self.get_selected_options(cell_data.clone()).select_options;
filter.is_visible(&selected_options, FieldType::SingleSelect) filter.is_visible(&selected_options).unwrap_or(true)
} }
} }

View File

@ -5,12 +5,12 @@ impl TextFilterPB {
let cell_data = cell_data.as_ref().to_lowercase(); let cell_data = cell_data.as_ref().to_lowercase();
let content = &self.content.to_lowercase(); let content = &self.content.to_lowercase();
match self.condition { match self.condition {
TextFilterConditionPB::Is => &cell_data == content, TextFilterConditionPB::TextIs => &cell_data == content,
TextFilterConditionPB::IsNot => &cell_data != content, TextFilterConditionPB::TextIsNot => &cell_data != content,
TextFilterConditionPB::Contains => cell_data.contains(content), TextFilterConditionPB::TextContains => cell_data.contains(content),
TextFilterConditionPB::DoesNotContain => !cell_data.contains(content), TextFilterConditionPB::TextDoesNotContain => !cell_data.contains(content),
TextFilterConditionPB::StartsWith => cell_data.starts_with(content), TextFilterConditionPB::TextStartsWith => cell_data.starts_with(content),
TextFilterConditionPB::EndsWith => cell_data.ends_with(content), TextFilterConditionPB::TextEndsWith => cell_data.ends_with(content),
TextFilterConditionPB::TextIsEmpty => cell_data.is_empty(), TextFilterConditionPB::TextIsEmpty => cell_data.is_empty(),
TextFilterConditionPB::TextIsNotEmpty => !cell_data.is_empty(), TextFilterConditionPB::TextIsNotEmpty => !cell_data.is_empty(),
} }
@ -25,7 +25,7 @@ mod tests {
#[test] #[test]
fn text_filter_equal_test() { fn text_filter_equal_test() {
let text_filter = TextFilterPB { let text_filter = TextFilterPB {
condition: TextFilterConditionPB::Is, condition: TextFilterConditionPB::TextIs,
content: "appflowy".to_owned(), content: "appflowy".to_owned(),
}; };
@ -37,7 +37,7 @@ mod tests {
#[test] #[test]
fn text_filter_start_with_test() { fn text_filter_start_with_test() {
let text_filter = TextFilterPB { let text_filter = TextFilterPB {
condition: TextFilterConditionPB::StartsWith, condition: TextFilterConditionPB::TextStartsWith,
content: "appflowy".to_owned(), content: "appflowy".to_owned(),
}; };
@ -49,7 +49,7 @@ mod tests {
#[test] #[test]
fn text_filter_end_with_test() { fn text_filter_end_with_test() {
let text_filter = TextFilterPB { let text_filter = TextFilterPB {
condition: TextFilterConditionPB::EndsWith, condition: TextFilterConditionPB::TextEndsWith,
content: "appflowy".to_owned(), content: "appflowy".to_owned(),
}; };
@ -70,7 +70,7 @@ mod tests {
#[test] #[test]
fn text_filter_contain_test() { fn text_filter_contain_test() {
let text_filter = TextFilterPB { let text_filter = TextFilterPB {
condition: TextFilterConditionPB::Contains, condition: TextFilterConditionPB::TextContains,
content: "appflowy".to_owned(), content: "appflowy".to_owned(),
}; };

View File

@ -1,4 +1,4 @@
use flowy_database2::entities::{FieldType, SelectOptionConditionPB, SelectOptionFilterPB}; use flowy_database2::entities::{FieldType, SelectOptionFilterConditionPB, SelectOptionFilterPB};
use lib_infra::box_any::BoxAny; use lib_infra::box_any::BoxAny;
use crate::database::filter_test::script::FilterScript::*; use crate::database::filter_test::script::FilterScript::*;
@ -12,7 +12,7 @@ async fn grid_filter_multi_select_is_empty_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::MultiSelect, field_type: FieldType::MultiSelect,
data: BoxAny::new(SelectOptionFilterPB { data: BoxAny::new(SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIsEmpty, condition: SelectOptionFilterConditionPB::OptionIsEmpty,
option_ids: vec![], option_ids: vec![],
}), }),
changed: None, changed: None,
@ -30,7 +30,7 @@ async fn grid_filter_multi_select_is_not_empty_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::MultiSelect, field_type: FieldType::MultiSelect,
data: BoxAny::new(SelectOptionFilterPB { data: BoxAny::new(SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIsNotEmpty, condition: SelectOptionFilterConditionPB::OptionIsNotEmpty,
option_ids: vec![], option_ids: vec![],
}), }),
changed: None, changed: None,
@ -50,12 +50,12 @@ async fn grid_filter_multi_select_is_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::MultiSelect, field_type: FieldType::MultiSelect,
data: BoxAny::new(SelectOptionFilterPB { data: BoxAny::new(SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIs, condition: SelectOptionFilterConditionPB::OptionIs,
option_ids: vec![options.remove(0).id, options.remove(0).id], option_ids: vec![options.remove(0).id, options.remove(0).id],
}), }),
changed: None, changed: None,
}, },
AssertNumberOfVisibleRows { expected: 5 }, AssertNumberOfVisibleRows { expected: 1 },
]; ];
test.run_scripts(scripts).await; test.run_scripts(scripts).await;
} }
@ -70,12 +70,12 @@ async fn grid_filter_multi_select_is_test2() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::MultiSelect, field_type: FieldType::MultiSelect,
data: BoxAny::new(SelectOptionFilterPB { data: BoxAny::new(SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIs, condition: SelectOptionFilterConditionPB::OptionIs,
option_ids: vec![options.remove(1).id], option_ids: vec![options.remove(1).id],
}), }),
changed: None, changed: None,
}, },
AssertNumberOfVisibleRows { expected: 4 }, AssertNumberOfVisibleRows { expected: 1 },
]; ];
test.run_scripts(scripts).await; test.run_scripts(scripts).await;
} }
@ -90,7 +90,7 @@ async fn grid_filter_single_select_is_empty_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::SingleSelect, field_type: FieldType::SingleSelect,
data: BoxAny::new(SelectOptionFilterPB { data: BoxAny::new(SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIsEmpty, condition: SelectOptionFilterConditionPB::OptionIsEmpty,
option_ids: vec![], option_ids: vec![],
}), }),
changed: Some(FilterRowChanged { changed: Some(FilterRowChanged {
@ -115,7 +115,7 @@ async fn grid_filter_single_select_is_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::SingleSelect, field_type: FieldType::SingleSelect,
data: BoxAny::new(SelectOptionFilterPB { data: BoxAny::new(SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIs, condition: SelectOptionFilterConditionPB::OptionIs,
option_ids: vec![options.remove(0).id], option_ids: vec![options.remove(0).id],
}), }),
changed: Some(FilterRowChanged { changed: Some(FilterRowChanged {
@ -142,7 +142,7 @@ async fn grid_filter_single_select_is_test2() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::SingleSelect, field_type: FieldType::SingleSelect,
data: BoxAny::new(SelectOptionFilterPB { data: BoxAny::new(SelectOptionFilterPB {
condition: SelectOptionConditionPB::OptionIs, condition: SelectOptionFilterConditionPB::OptionIs,
option_ids: vec![option.id.clone()], option_ids: vec![option.id.clone()],
}), }),
changed: Some(FilterRowChanged { changed: Some(FilterRowChanged {
@ -169,3 +169,43 @@ async fn grid_filter_single_select_is_test2() {
]; ];
test.run_scripts(scripts).await; test.run_scripts(scripts).await;
} }
#[tokio::test]
async fn grid_filter_multi_select_contains_test() {
let mut test = DatabaseFilterTest::new().await;
let field = test.get_first_field(FieldType::MultiSelect);
let mut options = test.get_multi_select_type_option(&field.id);
let scripts = vec![
CreateDataFilter {
parent_filter_id: None,
field_type: FieldType::MultiSelect,
data: BoxAny::new(SelectOptionFilterPB {
condition: SelectOptionFilterConditionPB::OptionContains,
option_ids: vec![options.remove(0).id, options.remove(0).id],
}),
changed: None,
},
AssertNumberOfVisibleRows { expected: 5 },
];
test.run_scripts(scripts).await;
}
#[tokio::test]
async fn grid_filter_multi_select_contains_test2() {
let mut test = DatabaseFilterTest::new().await;
let field = test.get_first_field(FieldType::MultiSelect);
let mut options = test.get_multi_select_type_option(&field.id);
let scripts = vec![
CreateDataFilter {
parent_filter_id: None,
field_type: FieldType::MultiSelect,
data: BoxAny::new(SelectOptionFilterPB {
condition: SelectOptionFilterConditionPB::OptionContains,
option_ids: vec![options.remove(1).id],
}),
changed: None,
},
AssertNumberOfVisibleRows { expected: 4 },
];
test.run_scripts(scripts).await;
}

View File

@ -70,7 +70,7 @@ async fn grid_filter_is_text_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::RichText, field_type: FieldType::RichText,
data: BoxAny::new(TextFilterPB { data: BoxAny::new(TextFilterPB {
condition: TextFilterConditionPB::Is, condition: TextFilterConditionPB::TextIs,
content: "A".to_string(), content: "A".to_string(),
}), }),
changed: Some(FilterRowChanged { changed: Some(FilterRowChanged {
@ -88,7 +88,7 @@ async fn grid_filter_contain_text_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::RichText, field_type: FieldType::RichText,
data: BoxAny::new(TextFilterPB { data: BoxAny::new(TextFilterPB {
condition: TextFilterConditionPB::Contains, condition: TextFilterConditionPB::TextContains,
content: "A".to_string(), content: "A".to_string(),
}), }),
changed: Some(FilterRowChanged { changed: Some(FilterRowChanged {
@ -109,7 +109,7 @@ async fn grid_filter_contain_text_test2() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::RichText, field_type: FieldType::RichText,
data: BoxAny::new(TextFilterPB { data: BoxAny::new(TextFilterPB {
condition: TextFilterConditionPB::Contains, condition: TextFilterConditionPB::TextContains,
content: "A".to_string(), content: "A".to_string(),
}), }),
changed: Some(FilterRowChanged { changed: Some(FilterRowChanged {
@ -137,7 +137,7 @@ async fn grid_filter_does_not_contain_text_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::RichText, field_type: FieldType::RichText,
data: BoxAny::new(TextFilterPB { data: BoxAny::new(TextFilterPB {
condition: TextFilterConditionPB::DoesNotContain, condition: TextFilterConditionPB::TextDoesNotContain,
content: "AB".to_string(), content: "AB".to_string(),
}), }),
changed: Some(FilterRowChanged { changed: Some(FilterRowChanged {
@ -155,7 +155,7 @@ async fn grid_filter_start_with_text_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::RichText, field_type: FieldType::RichText,
data: BoxAny::new(TextFilterPB { data: BoxAny::new(TextFilterPB {
condition: TextFilterConditionPB::StartsWith, condition: TextFilterConditionPB::TextStartsWith,
content: "A".to_string(), content: "A".to_string(),
}), }),
changed: Some(FilterRowChanged { changed: Some(FilterRowChanged {
@ -174,7 +174,7 @@ async fn grid_filter_ends_with_text_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::RichText, field_type: FieldType::RichText,
data: BoxAny::new(TextFilterPB { data: BoxAny::new(TextFilterPB {
condition: TextFilterConditionPB::EndsWith, condition: TextFilterConditionPB::TextEndsWith,
content: "A".to_string(), content: "A".to_string(),
}), }),
changed: None, changed: None,
@ -192,7 +192,7 @@ async fn grid_update_text_filter_test() {
parent_filter_id: None, parent_filter_id: None,
field_type: FieldType::RichText, field_type: FieldType::RichText,
data: BoxAny::new(TextFilterPB { data: BoxAny::new(TextFilterPB {
condition: TextFilterConditionPB::EndsWith, condition: TextFilterConditionPB::TextEndsWith,
content: "A".to_string(), content: "A".to_string(),
}), }),
changed: Some(FilterRowChanged { changed: Some(FilterRowChanged {
@ -210,7 +210,7 @@ async fn grid_update_text_filter_test() {
let scripts = vec![ let scripts = vec![
UpdateTextFilter { UpdateTextFilter {
filter, filter,
condition: TextFilterConditionPB::Is, condition: TextFilterConditionPB::TextIs,
content: "A".to_string(), content: "A".to_string(),
changed: Some(FilterRowChanged { changed: Some(FilterRowChanged {
showing_num_of_rows: 0, showing_num_of_rows: 0,