Skip to content

Commit

Permalink
feat(optionset) option search (#2688)
Browse files Browse the repository at this point in the history
* feat(optionset): add filtering for options

* fix: use token as filter operator

* fix: cleanup

* fix: conflict

* translation: add translation for disabled sort

* fix: keep filter after adding or deleting
  • Loading branch information
Birkbjo authored Mar 5, 2024
1 parent 85a21b9 commit 23e07b6
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class AddOptionDialog extends Component {
this.setState({ isSaving: false });
this.props.onRequestClose();
// After the save was successful we request the options from the server to get the updated list
actions.getOptionsFor(this.props.parentModel);
actions.getOptionsFor(this.props.parentModel, undefined);
}

onSaveError = ({ message, translate }) => {
Expand Down
50 changes: 39 additions & 11 deletions src/EditModel/option-set/OptionManagement.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import RaisedButton from 'material-ui/RaisedButton/RaisedButton';

import Pagination from 'd2-ui/lib/pagination/Pagination.component';
import LinearProgress from 'material-ui/LinearProgress/LinearProgress';
import AlertIcon from 'material-ui/svg-icons/alert/warning';
import TranslationDialog from 'd2-ui/lib/i18n/TranslationDialog.component';
import SharingDialog from '@dhis2/d2-ui-sharing-dialog';
import TextField from 'material-ui/TextField';

import OptionSorter from './OptionSorter/OptionSorter.component';
import OptionDialogForOptions from './OptionDialogForOptions/OptionDialogForOptions.component';
Expand All @@ -32,6 +32,12 @@ const styles = {
dataTableWrap: {
position: 'relative',
},
sortBarWrap: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'baseline',
marginTop: '-16px',
},
sortBarStyle: {
display: 'flex',
justifyContent: 'flex-end',
Expand Down Expand Up @@ -67,20 +73,21 @@ class OptionManagement extends Component {
isSorting: false,
modelToTranslate: null,
modelToShare: null,
filter: '',
};

this.i18n = context.d2.i18n;
}

componentDidMount() {
this.subscription = actions
.getOptionsFor(this.props.model)
.getOptionsFor(this.props.model, undefined)
.subscribe(() => this.forceUpdate());
}

componentWillReceiveProps(newProps) {
if (this.props.model !== newProps.model) {
actions.getOptionsFor(newProps.model);
actions.getOptionsFor(newProps.model, this.state.filter);
}
}

Expand All @@ -90,9 +97,16 @@ class OptionManagement extends Component {
}
}

handleFilter = ({ target: { value } }) => {
this.setState({ filter: value })
actions.getOptionsFor(this.props.model, value);
};

onAddOption = () => actions.setActiveModel();

onAddDialogClose = () => actions.closeOptionDialog();
onAddDialogClose = () => {
actions.closeOptionDialog();
}

onEditOption = model => actions.setActiveModel(model);

Expand Down Expand Up @@ -165,22 +179,36 @@ class OptionManagement extends Component {
modelToShare
})
},
delete: modelToDelete => actions.deleteOption(modelToDelete, this.props.model),
delete: modelToDelete => {
const deleteRef = actions.deleteOption(modelToDelete, this.props.model)
},
translate: (modelToTranslate) => {
this.setState({
modelToTranslate,
});
},
};

const isEmptyFilter = typeof this.state.filter === 'string' && this.state.filter.trim().length === 0

return (
<div style={styles.optionManagementWrap}>
{this.renderPagination()}
<OptionSorter
style={styles.sortBarStyle}
buttonStyle={styles.sortButtonStyle}
rows={this.props.rows}
/>
<div style={styles.sortBarWrap}>
<TextField
floatingLabelText={`${this.i18n.getTranslation(
'search_by_name_code_id'
)}`}
value={this.state.filter}
onChange={this.handleFilter}
/>

<OptionSorter
style={styles.sortBarStyle}
buttonStyle={styles.sortButtonStyle}
rows={this.props.rows}
disabled={!isEmptyFilter}
/>
</div>
<div style={styles.dataTableWrap}>
{this.props.isLoading && <LinearProgress />}
<DataTable
Expand Down
14 changes: 11 additions & 3 deletions src/EditModel/option-set/OptionSorter/OptionSorter.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,25 +93,31 @@ class OptionSorter extends Component {
}

render() {
const isDisabled = this.props.disabled || this.state.isSorting;
const disabledLabel = this.props.disabled ? this.getTranslation('sorting_is_disabled_when_filter_is_applied') : undefined;

return (
<div style={this.props.style}>
<RaisedButton
style={this.props.buttonStyle}
onClick={() => this.onSortBy('displayName')}
disabled={this.state.isSorting}
disabled={isDisabled}
label={this.getTranslation(this.state.isSorting ? 'sorting' : 'sort_by_name')}
title={disabledLabel}
/>
<RaisedButton
style={this.props.buttonStyle}
onClick={() => this.onSortBy('code')}
disabled={this.state.isSorting}
disabled={isDisabled}
label={this.getTranslation(this.state.isSorting ? 'sorting' : 'sort_by_code')}
title={disabledLabel}
/>
<RaisedButton
style={this.props.buttonStyle}
onClick={() => setSortDialogOpenTo(true)}
disabled={this.state.isSorting}
disabled={isDisabled}
label={this.getTranslation(this.state.isSorting ? 'sorting' : 'sort_manually')}
title={disabledLabel}
/>
<SortDialog />
</div>
Expand All @@ -121,11 +127,13 @@ class OptionSorter extends Component {

OptionSorter.propTypes = {
buttonStyle: PropTypes.object,
disabled: PropTypes.bool,
style: PropTypes.object,
};

OptionSorter.defaultProps = {
buttonStyle: {},
disabled: false,
style: {},
};

Expand Down
27 changes: 20 additions & 7 deletions src/EditModel/option-set/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@ const actions = Action.createActionsFromNames([
'updateModel',
], 'optionSet');

export async function loadOptionsForOptionSet(optionSetId, paging) {
export async function loadOptionsForOptionSet(optionSetId, { paging, filter }) {
const d2 = await getInstance();

return d2.models.option
let filteredOptions = d2.models.option
.filter().on('optionSet.id').equals(optionSetId)

if(filter) {
filteredOptions = filteredOptions.filter().on('identifiable').token(filter)
}

return filteredOptions
.list({ fields: ':all,attributeValues[:owner,value,attribute[id,name,displayName]]', paging, order: 'sortOrder:asc' });
}

Expand All @@ -40,6 +46,7 @@ function processResponse(options) {
onePage: true,
isLoading: false,
options: optionsInOrder,
filter: optionsForOptionSetStore.state.filter
});
});
}
Expand All @@ -54,6 +61,7 @@ function processResponse(options) {
options.pager.getPreviousPage()
.then(processResponse);
},
filter: optionsForOptionSetStore.state.filter,
pager: options.pager,
onePage: false,
isLoading: false,
Expand Down Expand Up @@ -138,14 +146,19 @@ actions.saveOption
});

actions.getOptionsFor
.subscribe(async ({ data: model, complete }) => {
.distinctUntilChanged()
.debounceTime(250)
.subscribe(async ({ data: [ model, newFilter ], complete }) => {
const filter = newFilter == undefined ? optionsForOptionSetStore.state.filter : newFilter;

optionsForOptionSetStore.setState({
...optionsForOptionSetStore.state,
isLoading: true,
options: [],
});
filter
})

if (model && model.id) {
loadOptionsForOptionSet(model.id, true)
loadOptionsForOptionSet(model.id, { paging: true, filter})
.then(processResponse)
.then(() => complete());
}
Expand Down Expand Up @@ -173,7 +186,7 @@ actions.deleteOption
return api.delete(`${modelParent.modelDefinition.apiEndpoint}/${modelParent.id}/options/${modelToDelete.id}`)
.then(() => modelToDelete.delete())
.then(() => snackActions.show({ message: deleteMessage }))
.then(() => actions.getOptionsFor(modelParent))
.then(() => actions.getOptionsFor(modelParent, undefined))
.then(() => modelParent.options.delete(modelToDelete.id))
.then(complete)
.catch(error);
Expand Down
1 change: 1 addition & 0 deletions src/i18n/i18n_module_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2343,3 +2343,4 @@ org_unit_label=Custom label for registering unit
relationship_label=Custom label for relationship
note_label=Custom label for note
tracked_entity_attribute_label=Custom label for tracked entity attribute
sorting_is_disabled_when_filter_is_applied=Sorting is diabled when a filter is applied

0 comments on commit 23e07b6

Please sign in to comment.