diff --git a/app/api/projects.py b/app/api/projects.py index 00212c39..7284460e 100644 --- a/app/api/projects.py +++ b/app/api/projects.py @@ -49,10 +49,10 @@ class ProjectFilter(filters.FilterSet): tag_pattern = re.compile("#[^\s]+") tags = set(re.findall(tag_pattern, value)) - deep_tags = set([t for t in tags if t.startswith("##")]) - project_tags = tags - deep_tags + task_tags = set([t for t in tags if t.startswith("##")]) + project_tags = tags - task_tags - deep_tags = [t.replace("##", "") for t in deep_tags] + task_tags = [t.replace("##", "") for t in task_tags] project_tags = [t.replace("#", "") for t in project_tags] names = re.sub("\s+", " ", re.sub(tag_pattern, "", value)).strip() @@ -63,13 +63,12 @@ class ProjectFilter(filters.FilterSet): name_query = SearchQuery(names, search_type="plain") qs = qs.annotate(n_search=project_name_vec + task_name_vec).filter(n_search=name_query) - if len(deep_tags) > 0: - project_tags_vec = SearchVector("tags") - task_tags_vec = SearchVector(StringAgg("task__tags", delimiter=' ')) - tags_query = SearchQuery(deep_tags[0]) - for t in deep_tags[1:]: + if len(task_tags) > 0: + task_tags_vec = SearchVector("task__tags") + tags_query = SearchQuery(task_tags[0]) + for t in task_tags[1:]: tags_query = tags_query & SearchQuery(t) - qs = qs.annotate(dt_search=project_tags_vec + task_tags_vec).filter(dt_search=tags_query) + qs = qs.annotate(tt_search=task_tags_vec).filter(tt_search=tags_query) if len(project_tags) > 0: project_tags_vec = SearchVector("tags") diff --git a/app/static/app/js/components/Paginator.jsx b/app/static/app/js/components/Paginator.jsx index 9ab567b5..ec93da3c 100644 --- a/app/static/app/js/components/Paginator.jsx +++ b/app/static/app/js/components/Paginator.jsx @@ -122,6 +122,8 @@ class Paginator extends React.Component { ref={(domNode) => { this.searchInput = domNode}} className="form-control search theme-border-secondary-07" placeholder={_("Search names or #tags")} + spellCheck="false" + autoComplete="false" value={searchText} onKeyDown={this.handleSearchKeyDown} onChange={this.handleSearchChange} /> diff --git a/app/static/app/js/components/ProjectListItem.jsx b/app/static/app/js/components/ProjectListItem.jsx index f495376a..49a50a31 100644 --- a/app/static/app/js/components/ProjectListItem.jsx +++ b/app/static/app/js/components/ProjectListItem.jsx @@ -41,7 +41,9 @@ class ProjectListItem extends React.Component { importing: false, buttons: [], sortKey: "-created_at", - filterTags: [] + filterTags: [], + selectedTags: [], + filterText: "" }; this.sortItems = [{ @@ -90,6 +92,13 @@ class ProjectListItem extends React.Component { if (this.refreshRequest) this.refreshRequest.abort(); } + componentDidUpdate(prevProps, prevState){ + if (prevState.filterText !== this.state.filterText || + prevState.selectedTags.length !== this.state.selectedTags.length){ + if (this.taskList) this.taskList.applyFilter(this.state.filterText, this.state.selectedTags); + } + } + getDefaultUploadState(){ return { uploading: false, @@ -500,7 +509,42 @@ class ProjectListItem extends React.Component { } tagsChanged = (filterTags) => { - this.setState({filterTags}); + this.setState({filterTags, selectedTags: []}); + } + + handleFilterTextChange = e => { + this.setState({filterText: e.target.value}); + } + + toggleTag = t => { + return () => { + if (this.state.selectedTags.indexOf(t) === -1){ + this.setState(update(this.state, { selectedTags: {$push: [t]} })); + }else{ + this.setState({selectedTags: this.state.selectedTags.filter(tag => tag !== t)}); + } + } + } + + selectTag = t => { + if (this.state.selectedTags.indexOf(t) === -1){ + this.setState(update(this.state, { selectedTags: {$push: [t]} })); + } + } + + clearFilter = () => { + this.setState({ + filterText: "", + selectedTags: [] + }); + } + + onOpenFilter = () => { + if (this.state.filterTags.length === 0){ + setTimeout(() => { + this.filterTextInput.focus(); + }, 0); + } } render() { @@ -585,17 +629,35 @@ class ProjectListItem extends React.Component { {this.state.showTaskList && numTasks > 1 ?