mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
1a34366a3f
commit
5a837a9482
2
frontend/.vscode/tasks.json
vendored
2
frontend/.vscode/tasks.json
vendored
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
|
@ -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 [],
|
||||||
}) {
|
}) {
|
||||||
|
@ -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();
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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 "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.read<SelectOptionFilterListBloc>().add(
|
||||||
|
SelectOptionFilterListEvent.selectOption(
|
||||||
|
widget.option,
|
||||||
context
|
context
|
||||||
.read<SelectOptionFilterListBloc>()
|
.read<SelectOptionFilterEditorBloc>()
|
||||||
.add(SelectOptionFilterListEvent.selectOption(widget.option));
|
.state
|
||||||
|
.filter
|
||||||
|
.condition,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
|
@ -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(
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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");
|
||||||
});
|
});
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 [
|
||||||
{
|
{
|
||||||
|
@ -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,
|
||||||
|
@ -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 '';
|
||||||
|
@ -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')}`;
|
||||||
|
@ -422,13 +422,9 @@
|
|||||||
"isComplete": "አይደለም",
|
"isComplete": "አይደለም",
|
||||||
"isIncomplted": "ባዶ ነው"
|
"isIncomplted": "ባዶ ነው"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "ነው",
|
"is": "ነው",
|
||||||
"isNot": "አይደለም",
|
"isNot": "አይደለም",
|
||||||
"isEmpty": "ባዶ ነው",
|
|
||||||
"isNotEmpty": "ባዶ አይደለም"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "ይይዛል",
|
"contains": "ይይዛል",
|
||||||
"doesNotContain": "አይይዝም",
|
"doesNotContain": "አይይዝም",
|
||||||
"isEmpty": "ባዶ ነው",
|
"isEmpty": "ባዶ ነው",
|
||||||
|
@ -486,13 +486,9 @@
|
|||||||
"isComplete": "كاملة",
|
"isComplete": "كاملة",
|
||||||
"isIncomplted": "غير مكتمل"
|
"isIncomplted": "غير مكتمل"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "يكون",
|
"is": "يكون",
|
||||||
"isNot": "ليس",
|
"isNot": "ليس",
|
||||||
"isEmpty": "فارغ",
|
|
||||||
"isNotEmpty": "ليس فارغا"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "يتضمن",
|
"contains": "يتضمن",
|
||||||
"doesNotContain": "لا يحتوي",
|
"doesNotContain": "لا يحتوي",
|
||||||
"isEmpty": "فارغ",
|
"isEmpty": "فارغ",
|
||||||
|
@ -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",
|
||||||
|
@ -356,13 +356,9 @@
|
|||||||
"isComplete": "تەواوە",
|
"isComplete": "تەواوە",
|
||||||
"isIncomplted": "ناتەواوە"
|
"isIncomplted": "ناتەواوە"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "هەیە",
|
"is": "هەیە",
|
||||||
"isNot": "نییە",
|
"isNot": "نییە",
|
||||||
"isEmpty": "بهتاڵه",
|
|
||||||
"isNotEmpty": "بەتاڵ نییە"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "لەخۆ دەگرێت",
|
"contains": "لەخۆ دەگرێت",
|
||||||
"doesNotContain": "لەخۆناگرێت",
|
"doesNotContain": "لەخۆناگرێت",
|
||||||
"isEmpty": "بهتاڵه",
|
"isEmpty": "بهتاڵه",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -356,13 +356,9 @@
|
|||||||
"isComplete": "کامل است",
|
"isComplete": "کامل است",
|
||||||
"isIncomplted": "کامل نیست"
|
"isIncomplted": "کامل نیست"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "است",
|
"is": "است",
|
||||||
"isNot": "نیست",
|
"isNot": "نیست",
|
||||||
"isEmpty": "خالی است",
|
|
||||||
"isNotEmpty": "خالی نیست"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "شامل",
|
"contains": "شامل",
|
||||||
"doesNotContain": "شامل نیست",
|
"doesNotContain": "شامل نیست",
|
||||||
"isEmpty": "خالی است",
|
"isEmpty": "خالی است",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -396,13 +396,9 @@
|
|||||||
"isComplete": "पूर्ण है",
|
"isComplete": "पूर्ण है",
|
||||||
"isIncomplted": "अपूर्ण है"
|
"isIncomplted": "अपूर्ण है"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "है",
|
"is": "है",
|
||||||
"isNot": "नहीं है",
|
"isNot": "नहीं है",
|
||||||
"isEmpty": "खाली है",
|
|
||||||
"isNotEmpty": "खाली नहीं है"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "शामिल है",
|
"contains": "शामिल है",
|
||||||
"doesNotContain": "इसमें शामिल नहीं है",
|
"doesNotContain": "इसमें शामिल नहीं है",
|
||||||
"isEmpty": "खाली है",
|
"isEmpty": "खाली है",
|
||||||
@ -740,4 +736,4 @@
|
|||||||
"frequentlyUsed": "अक्सर उपयोग किया जाता है"
|
"frequentlyUsed": "अक्सर उपयोग किया जाता है"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -412,13 +412,9 @@
|
|||||||
"isComplete": "完了",
|
"isComplete": "完了",
|
||||||
"isIncomplted": "未完了"
|
"isIncomplted": "未完了"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "等しい",
|
"is": "等しい",
|
||||||
"isNot": "等しくない",
|
"isNot": "等しくない",
|
||||||
"isEmpty": "空である",
|
|
||||||
"isNotEmpty": "空ではない"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "を含む",
|
"contains": "を含む",
|
||||||
"doesNotContain": "を含まない",
|
"doesNotContain": "を含まない",
|
||||||
"isEmpty": "空である",
|
"isEmpty": "空である",
|
||||||
|
@ -324,13 +324,9 @@
|
|||||||
"isComplete": "완료되었습니다",
|
"isComplete": "완료되었습니다",
|
||||||
"isIncomplted": "불완전하다"
|
"isIncomplted": "불완전하다"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "~이다",
|
"is": "~이다",
|
||||||
"isNot": "아니다",
|
"isNot": "아니다",
|
||||||
"isEmpty": "비었다",
|
|
||||||
"isNotEmpty": "비어 있지 않음"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "포함",
|
"contains": "포함",
|
||||||
"doesNotContain": "포함되어 있지 않다",
|
"doesNotContain": "포함되어 있지 않다",
|
||||||
"isEmpty": "비었다",
|
"isEmpty": "비었다",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -528,13 +528,9 @@
|
|||||||
"isComplete": "завершено",
|
"isComplete": "завершено",
|
||||||
"isIncomplted": "не завершено"
|
"isIncomplted": "не завершено"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "Является",
|
"is": "Является",
|
||||||
"isNot": "Не является",
|
"isNot": "Не является",
|
||||||
"isEmpty": "Пусто",
|
|
||||||
"isNotEmpty": "Не пусто"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "Содержит",
|
"contains": "Содержит",
|
||||||
"doesNotContain": "Не содержит",
|
"doesNotContain": "Не содержит",
|
||||||
"isEmpty": "Пусто",
|
"isEmpty": "Пусто",
|
||||||
|
@ -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",
|
||||||
|
@ -478,13 +478,9 @@
|
|||||||
"isComplete": "เสร็จสมบูรณ์",
|
"isComplete": "เสร็จสมบูรณ์",
|
||||||
"isIncomplted": "ไม่เสร็จสมบูรณ์"
|
"isIncomplted": "ไม่เสร็จสมบูรณ์"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "เป็น",
|
"is": "เป็น",
|
||||||
"isNot": "ไม่เป็น",
|
"isNot": "ไม่เป็น",
|
||||||
"isEmpty": "ว่างเปล่า",
|
|
||||||
"isNotEmpty": "ไม่ว่างเปล่า"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "ประกอบด้วย",
|
"contains": "ประกอบด้วย",
|
||||||
"doesNotContain": "ไม่ประกอบด้วย",
|
"doesNotContain": "ไม่ประกอบด้วย",
|
||||||
"isEmpty": "ว่างเปล่า",
|
"isEmpty": "ว่างเปล่า",
|
||||||
|
@ -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",
|
||||||
|
@ -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": "порожнє",
|
||||||
|
@ -391,7 +391,7 @@
|
|||||||
"isComplete": "مکمل ہے",
|
"isComplete": "مکمل ہے",
|
||||||
"isIncomplted": "نامکمل ہے"
|
"isIncomplted": "نامکمل ہے"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "ہے",
|
"is": "ہے",
|
||||||
"isNot": "نہیں ہے",
|
"isNot": "نہیں ہے",
|
||||||
"isEmpty": "خالی ہے",
|
"isEmpty": "خالی ہے",
|
||||||
|
@ -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",
|
||||||
|
@ -531,13 +531,9 @@
|
|||||||
"isComplete": "已完成",
|
"isComplete": "已完成",
|
||||||
"isIncomplted": "未完成"
|
"isIncomplted": "未完成"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "是",
|
"is": "是",
|
||||||
"isNot": "不是",
|
"isNot": "不是",
|
||||||
"isEmpty": "为空",
|
|
||||||
"isNotEmpty": "不为空"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "包含",
|
"contains": "包含",
|
||||||
"doesNotContain": "不包含",
|
"doesNotContain": "不包含",
|
||||||
"isEmpty": "为空",
|
"isEmpty": "为空",
|
||||||
|
@ -513,13 +513,9 @@
|
|||||||
"isComplete": "已完成",
|
"isComplete": "已完成",
|
||||||
"isIncomplted": "未完成"
|
"isIncomplted": "未完成"
|
||||||
},
|
},
|
||||||
"singleSelectOptionFilter": {
|
"selectOptionFilter": {
|
||||||
"is": "是",
|
"is": "是",
|
||||||
"isNot": "不是",
|
"isNot": "不是",
|
||||||
"isEmpty": "為空",
|
|
||||||
"isNotEmpty": "不為空"
|
|
||||||
},
|
|
||||||
"multiSelectOptionFilter": {
|
|
||||||
"contains": "包含",
|
"contains": "包含",
|
||||||
"doesNotContain": "不包含",
|
"doesNotContain": "不包含",
|
||||||
"isEmpty": "為空",
|
"isEmpty": "為空",
|
||||||
|
@ -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(),
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 =
|
||||||
|
|| (!self.option_ids.is_empty()).then(|| self.option_ids.clone());
|
||||||
|
|
||||||
|
let strategy = match self.condition {
|
||||||
|
SelectOptionFilterConditionPB::OptionIs => {
|
||||||
|
SelectOptionFilterStrategy::Is(get_non_empty_expected_options()?)
|
||||||
|
},
|
||||||
|
SelectOptionFilterConditionPB::OptionIsNot => {
|
||||||
|
SelectOptionFilterStrategy::IsNot(get_non_empty_expected_options()?)
|
||||||
|
},
|
||||||
|
SelectOptionFilterConditionPB::OptionContains => {
|
||||||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let required_options = self
|
selected_option_ids.len() == option_ids.len()
|
||||||
.option_ids
|
&& selected_option_ids.iter().all(|id| option_ids.contains(id))
|
||||||
.iter()
|
},
|
||||||
.filter(|id| selected_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<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
!required_options.is_empty()
|
!required_options.is_empty()
|
||||||
},
|
},
|
||||||
FieldType::MultiSelect => {
|
SelectOptionFilterStrategy::DoesNotContain(option_ids) => {
|
||||||
if self.option_ids.is_empty() {
|
if selected_option_ids.is_empty() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let required_options = self
|
let required_options = option_ids
|
||||||
.option_ids
|
.into_iter()
|
||||||
.iter()
|
.filter(|id| selected_option_ids.contains(&id))
|
||||||
.filter(|id| selected_option_ids.contains(id))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
!required_options.is_empty()
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
SelectOptionConditionPB::OptionIsNot => match field_type {
|
|
||||||
FieldType::SingleSelect => {
|
|
||||||
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<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
required_options.is_empty()
|
required_options.is_empty()
|
||||||
},
|
},
|
||||||
FieldType::MultiSelect => {
|
SelectOptionFilterStrategy::IsEmpty => selected_option_ids.is_empty(),
|
||||||
let required_options = self
|
SelectOptionFilterStrategy::IsNotEmpty => !selected_option_ids.is_empty(),
|
||||||
.option_ids
|
|
||||||
.iter()
|
|
||||||
.filter(|id| selected_option_ids.contains(id))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
required_options.is_empty()
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
SelectOptionConditionPB::OptionIsEmpty => selected_option_ids.is_empty(),
|
|
||||||
SelectOptionConditionPB::OptionIsNotEmpty => !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]
|
// one expected option
|
||||||
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");
|
|
||||||
|
|
||||||
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]
|
// multiple expected options
|
||||||
fn single_select_option_filter_is_test2() {
|
|
||||||
let option_1 = SelectOption::new("A");
|
|
||||||
let option_2 = SelectOption::new("B");
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user