Add link for advanced search if helpful

This commit is contained in:
shamoon 2024-04-28 10:11:08 -07:00
parent b9dd89e222
commit 9972e26de8
6 changed files with 345 additions and 183 deletions

File diff suppressed because it is too large Load Diff

View File

@ -2,16 +2,25 @@
<div ngbDropdown #resultsDropdown="ngbDropdown" (openChange)="onDropdownOpenChange"> <div ngbDropdown #resultsDropdown="ngbDropdown" (openChange)="onDropdownOpenChange">
<form class="form-inline position-relative"> <form class="form-inline position-relative">
<i-bs width="1em" height="1em" name="search"></i-bs> <i-bs width="1em" height="1em" name="search"></i-bs>
<input #searchInput class="form-control form-control-sm" type="text" name="query" <div class="input-group">
placeholder="Search" aria-label="Search" i18n-placeholder <div class="form-control form-control-sm">
autocomplete="off" spellcheck="false" <input class="bg-transparent border-0 w-100 h-100" #searchInput type="text" name="query"
[(ngModel)]="query" (ngModelChange)="this.queryDebounce.next($event)" (keydown)="searchInputKeyDown($event)" ngbDropdownAnchor> placeholder="Search" aria-label="Search" i18n-placeholder
<div class="position-absolute top-50 end-0 translate-middle"> autocomplete="off" spellcheck="false"
@if (loading) { [(ngModel)]="query" (ngModelChange)="this.queryDebounce.next($event)" (keydown)="searchInputKeyDown($event)">
<div class="spinner-border spinner-border-sm text-muted mt-1"></div> <div class="position-absolute top-50 end-0 translate-middle">
@if (loading) {
<div class="spinner-border spinner-border-sm text-muted mt-1"></div>
}
</div>
</div>
@if (query && (searchResults?.documents.length === searchService.searchResultObjectLimit || searchService.searchDbOnly)) {
<button class="btn btn-sm btn-outline-secondary" type="button" (click)="runAdvanedSearch()">
<ng-container i18n>Advanced search</ng-container>
<i-bs width="1em" height="1em" name="arrow-right-short"></i-bs>
</button>
} }
</div> </div>
</form> </form>
<ng-template #resultItemTemplate let-item="item" let-nameProp="nameProp" let-type="type" let-icon="icon" let-date="date"> <ng-template #resultItemTemplate let-item="item" let-nameProp="nameProp" let-type="type" let-icon="icon" let-date="date">

View File

@ -1,7 +1,7 @@
form { form {
position: relative; position: relative;
> i-bs { > i-bs[name="search"] {
position: absolute; position: absolute;
left: 0.6rem; left: 0.6rem;
top: .35rem; top: .35rem;
@ -14,7 +14,7 @@ form {
} }
&:focus-within { &:focus-within {
i-bs, i-bs[name="search"],
.badge { .badge {
display: none !important; display: none !important;
} }
@ -27,25 +27,34 @@ form {
.badge { .badge {
font-size: 0.8rem; font-size: 0.8rem;
} }
}
.form-control { .input-group .btn {
color: rgba(255, 255, 255, 0.3); border-color: rgba(255, 255, 255, 0.2);
background-color: rgba(0, 0, 0, 0.15); color: var(--pngx-primary-text-contrast);
padding-left: 1.8rem;
border-color: rgba(255, 255, 255, 0.2);
transition: all .3s ease, padding-left 0s ease, background-color 0s ease; // Safari requires all
&::placeholder {
color: rgba(255, 255, 255, 0.4);
} }
&:focus { .form-control {
background-color: rgba(0, 0, 0, 0.3); color: rgba(255, 255, 255, 0.3);
color: var(--bs-light); background-color: rgba(0, 0, 0, 0.15);
flex-grow: 1; padding-left: 1.8rem;
padding-left: 0.5rem; border-color: rgba(255, 255, 255, 0.2);
transition: all .3s ease, padding-left 0s ease, background-color 0s ease; // Safari requires all
> input {
outline: none;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
}
&:focus-within {
background-color: rgba(0, 0, 0, 0.3);
color: var(--bs-light);
flex-grow: 1;
padding-left: 0.5rem;
}
} }
} }
* { * {

View File

@ -20,6 +20,7 @@ import { DocumentListViewService } from 'src/app/services/document-list-view.ser
import { HttpClientTestingModule } from '@angular/common/http/testing' import { HttpClientTestingModule } from '@angular/common/http/testing'
import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { import {
FILTER_FULLTEXT_QUERY,
FILTER_HAS_CORRESPONDENT_ANY, FILTER_HAS_CORRESPONDENT_ANY,
FILTER_HAS_DOCUMENT_TYPE_ANY, FILTER_HAS_DOCUMENT_TYPE_ANY,
FILTER_HAS_STORAGE_PATH_ANY, FILTER_HAS_STORAGE_PATH_ANY,
@ -440,4 +441,13 @@ describe('GlobalSearchComponent', () => {
component.onButtonHover(event as any) component.onButtonHover(event as any)
expect(focusSpy).toHaveBeenCalled() expect(focusSpy).toHaveBeenCalled()
}) })
it('should support explicit advanced search', () => {
const qfSpy = jest.spyOn(documentListViewService, 'quickFilter')
component.query = 'test'
component.runAdvanedSearch()
expect(qfSpy).toHaveBeenCalledWith([
{ rule_type: FILTER_FULLTEXT_QUERY, value: 'test' },
])
})
}) })

View File

@ -10,6 +10,7 @@ import { Router } from '@angular/router'
import { NgbDropdown, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' import { NgbDropdown, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'
import { Subject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs' import { Subject, debounceTime, distinctUntilChanged, filter, map } from 'rxjs'
import { import {
FILTER_FULLTEXT_QUERY,
FILTER_HAS_CORRESPONDENT_ANY, FILTER_HAS_CORRESPONDENT_ANY,
FILTER_HAS_DOCUMENT_TYPE_ANY, FILTER_HAS_DOCUMENT_TYPE_ANY,
FILTER_HAS_STORAGE_PATH_ANY, FILTER_HAS_STORAGE_PATH_ANY,
@ -62,7 +63,7 @@ export class GlobalSearchComponent implements OnInit {
@ViewChildren('secondaryButton') secondaryButtons: QueryList<ElementRef> @ViewChildren('secondaryButton') secondaryButtons: QueryList<ElementRef>
constructor( constructor(
private searchService: SearchService, public searchService: SearchService,
private router: Router, private router: Router,
private modalService: NgbModal, private modalService: NgbModal,
private documentService: DocumentService, private documentService: DocumentService,
@ -351,4 +352,11 @@ export class GlobalSearchComponent implements OnInit {
object object
) )
} }
runAdvanedSearch() {
this.documentListViewService.quickFilter([
{ rule_type: FILTER_FULLTEXT_QUERY, value: this.query },
])
this.reset(true)
}
} }

View File

@ -37,6 +37,8 @@ export interface GlobalSearchResult {
providedIn: 'root', providedIn: 'root',
}) })
export class SearchService { export class SearchService {
public readonly searchResultObjectLimit: number = 3 // documents/views.py GlobalSearchView > OBJECT_LIMIT
constructor( constructor(
private http: HttpClient, private http: HttpClient,
private settingsService: SettingsService private settingsService: SettingsService
@ -51,7 +53,7 @@ export class SearchService {
globalSearch(query: string): Observable<GlobalSearchResult> { globalSearch(query: string): Observable<GlobalSearchResult> {
let params = new HttpParams().set('query', query) let params = new HttpParams().set('query', query)
if (this.settingsService.get(SETTINGS_KEYS.SEARCH_DB_ONLY)) { if (this.searchDbOnly) {
params = params.set('db_only', true) params = params.set('db_only', true)
} }
return this.http.get<GlobalSearchResult>( return this.http.get<GlobalSearchResult>(
@ -59,4 +61,8 @@ export class SearchService {
{ params } { params }
) )
} }
public get searchDbOnly(): boolean {
return this.settingsService.get(SETTINGS_KEYS.SEARCH_DB_ONLY)
}
} }