Skip to content

Commit

Permalink
WIP: Add server-side-search prototype to test with
Browse files Browse the repository at this point in the history
  • Loading branch information
elwinschmitz committed Mar 12, 2024
1 parent f2b51cb commit 617516c
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 24 deletions.
29 changes: 21 additions & 8 deletions src/app/components/search-input/search-input.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,31 @@
role="search"
#ngForm
(ngSubmit)="doSubmit()"
class="search-input"
>
<input
type="search"
name="q"
[(ngModel)]="searchQuery"
enterkeyhint="search"
class="input-field--text text-style--size-1"
/>
<span class="search-input--input">
<input
type="search"
name="q"
[(ngModel)]="searchQuery"
enterkeyhint="search"
class="input-field--text text-style--size-1"
/>

<button
*ngIf="!!searchQuery"
type="button"
class="input-field--clear"
[title]="clearLabel || 'Clear'"
(click)="searchQuery = ''; doSubmit(); ngForm.q.focus()"
>
<strong>&times;</strong>
</button>
</span>

<button
type="submit"
class="action is_attention text-style--header text-style--size-1"
class="search-input--action action is_attention text-style--header text-style--size-1"
>
<strong>
{{ actionLabel || 'Search' }}
Expand Down
38 changes: 30 additions & 8 deletions src/app/components/search-input/search-input.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,56 @@
--search-input--background: var(--ion-color-light, white);
}

form {
.search-input {
padding-block-start: 1em;
padding-block-end: 1em;

// Layout
display: flex;
gap: 0.5em;

input[type='search'] {
.search-input--input {
flex: 1 1 80%;
position: relative;
}
button[type='submit'] {
.search-input--action {
flex: 0 0 20%;
}
}

.input-field--clear,
.input-field--text {
color: var(--search-input--text);
background: var(--search-input--background);
border: 0;
border-radius: 0.25em;

&:focus {
outline-offset: 0.125rem;
outline-width: 0.25rem;
outline-style: solid;
}
}

.input-field--text {
color: var(--search-input--text);
background: var(--search-input--background);
padding-block: 0.25em;
padding-inline: 0.5em;
width: 100%;

&:focus {
outline-color: var(--search-input--background);
outline-offset: 2px;
outline-width: 4px;
outline-style: solid;
}
}

.input-field--clear {
position: absolute;
inset-block-start: 0.25rem;
inset-inline-end: 0.25rem;
display: inline-block;
padding: 0.25rem;
background: transparent;

&:focus {
outline-color: gold;
}
}
6 changes: 5 additions & 1 deletion src/app/components/search-input/search-input.component.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { NgIf } from '@angular/common';
import type { OnInit } from '@angular/core';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { FormsModule } from '@angular/forms';
Expand All @@ -7,7 +8,7 @@ import { FormsModule } from '@angular/forms';
templateUrl: './search-input.component.html',
styleUrls: ['./search-input.component.scss'],
standalone: true,
imports: [FormsModule],
imports: [FormsModule, NgIf],
})
export class SearchInputComponent implements OnInit {
@Input()
Expand All @@ -22,6 +23,9 @@ export class SearchInputComponent implements OnInit {
@Input()
public actionLabel: string;

@Input()
public clearLabel: string;

private previousQuery: string;

constructor() {}
Expand Down
43 changes: 39 additions & 4 deletions src/app/pages/search/search.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,16 @@
</div>

<h2 class="ion-no-margin text-style--header text-style--size-2">
{{ regionData?.labelSearchPageTitle }}
<ng-container *ngIf="!useSearchApi">
{{ regionData?.labelSearchPageTitle }}
</ng-container>
<ng-container *ngIf="!!useSearchApi">
What are you looking for?
</ng-container>
</h2>

<p *ngIf="!!useSearchApi">Search by typing a question or relevant keywords.</p>

<app-search-input
[(searchQuery)]="searchQuery"
(doSearch)="onSearchInput($event)"
Expand All @@ -28,12 +35,40 @@ <h2 class="ion-no-margin text-style--header text-style--size-2">
[tabindex]="-1"
class="focus--minimal focus--fade-out"
>
<p *ngIf="!!searchQuery">
{{ regionData?.labelSearchResultsCount }}
<strong>{{ searchResults?.length }}</strong>
<div
class="ion-padding"
style="border: 1px solid white; border-radius: 0.25em"
>
<div *ngIf="useSearchApi && loadingSearch">
<span
class="loading-text"
style="width: 95%"
></span>
<span class="loading-text"></span>
<span
class="loading-text"
style="width: 88%"
></span>
<span
class="loading-text"
style="width: 75%"
></span>
</div>

<div
*ngIf="useSearchApi && !!searchQuery && searchResultSummary && !loadingSearch"
markdown
[data]="searchResultSummary"
[inline]="false"
></div>
</div>

<p *ngIf="!!searchQuery && !loadingSearch">
For more detailed information, check out these FAQs:
</p>

<app-q-a-set-list
*ngIf="!!searchQuery && !loadingSearch"
[baseUrl]="'../'"
[list]="searchResults"
[showDateUpdatedOutsideQuestion]="false"
Expand Down
87 changes: 84 additions & 3 deletions src/app/pages/search/search.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { OnInit } from '@angular/core';
import { Component } from '@angular/core';
import type { Params } from '@angular/router';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { MarkdownModule } from 'ngx-markdown';
import { QASetListComponent } from 'src/app/components/q-a-set-list/q-a-set-list.component';
import { SearchInputComponent } from 'src/app/components/search-input/search-input.component';
import type { QASet } from 'src/app/models/qa-set.model';
Expand All @@ -13,12 +14,29 @@ import { RegionDataService } from 'src/app/services/region-data.service';
import { SearchService } from 'src/app/services/search.service';
import { environment } from 'src/environments/environment';

type SearchApiResponse = {
status?: number;
answer?: string;
references?: {
category: string;
subcategory: string;
slug?: string;
parent?: string;
}[];
};

@Component({
selector: 'app-search-page',
templateUrl: './search.page.html',
styleUrls: ['./search.page.css'],
standalone: true,
imports: [NgIf, RouterLink, QASetListComponent, SearchInputComponent],
imports: [
NgIf,
RouterLink,
QASetListComponent,
SearchInputComponent,
MarkdownModule,
],
})
export default class SearchPageComponent implements OnInit {
public useSearchApi = environment.useQandASearch && !!environment.searchApi;
Expand All @@ -28,6 +46,8 @@ export default class SearchPageComponent implements OnInit {

public searchQuery: string;
public searchResults: QASet[];
public searchResultSummary: string;
public loadingSearch: boolean;

constructor(
private route: ActivatedRoute,
Expand Down Expand Up @@ -84,7 +104,9 @@ export default class SearchPageComponent implements OnInit {
}

public onSearchInput(rawQuery: string) {
const safeQuery = this.searchService.sanitizeSearchQuery(rawQuery);
const safeQuery = this.useSearchApi
? rawQuery
: this.searchService.sanitizeSearchQuery(rawQuery);

this.router.navigate([], {
queryParams: { q: safeQuery },
Expand All @@ -95,7 +117,23 @@ export default class SearchPageComponent implements OnInit {
public async performSearch(query: string): Promise<void> {
const safeQuery = this.searchService.sanitizeSearchQuery(query);

this.searchResults = this.searchService.query(safeQuery);
if (this.useSearchApi) {
this.loadingSearch = true;
const apiResponse: SearchApiResponse =
await this.fetchApiResults(safeQuery);

if (apiResponse) {
this.loadingSearch = false;
}
if (apiResponse && apiResponse.answer) {
this.searchResultSummary = apiResponse.answer;
}
if (apiResponse && apiResponse.references) {
this.searchResults = this.createReferences(apiResponse.references);
}
} else {
this.searchResults = this.searchService.query(safeQuery);
}

if (this.searchResults.length > 1) {
this.pageMeta.setTitle({
Expand All @@ -108,4 +146,47 @@ export default class SearchPageComponent implements OnInit {
}
}
}

private createReferences(
references: SearchApiResponse['references'],
): QASet[] {
const results = references.map((reference) => {
const result = this.qaSets.find((qa) => {
return (
qa.categoryID === Number(reference.category) &&
qa.subCategoryID === Number(reference.subcategory)
);
});

return result;
});
return results;
}

private async fetchApiResults(query: string): Promise<SearchApiResponse> {
const response = await window.fetch(environment.searchApi, {
method: 'POST',
credentials: 'omit',
mode: 'cors',
headers: {
Authorization: environment.searchApiKey,
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
question: query,
googleSheetId: this.regionData.sheetId,
}),
});

if (!response || !response.ok) {
console.warn('Something went wrong:', response);
return {
answer: `Something went wrong.\nMaybe you can try again. \n\n ${response.status} ${response.statusText}`,
references: [],
};
}

return await response.json();
}
}

0 comments on commit 617516c

Please sign in to comment.