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 Oct 22, 2024
1 parent 94067ff commit c73f858
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 23 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_round is_inline is_outline text-style--alt text-style--size-1"
class="search-input--action action is_round is_inline is_outline text-style--alt text-style--size-1"
>
<strong>
{{ actionLabel || 'Search' }}
Expand Down
37 changes: 29 additions & 8 deletions src/app/components/search-input/search-input.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,33 @@
--search-input--focus: var(--hia_level_text, black);
}

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'] {
flex: 0 0 auto;
.search-input--action {
flex: 0 0 20%;
}
}

.input-field--clear,
.input-field--text {
border: 0;
border-radius: 0.25em;

&:focus {
outline-color: var(--search-input--focus);
outline-offset: 0.125rem;
outline-width: 0.125rem;
outline-style: solid;
}
}

Expand All @@ -30,11 +44,18 @@ form {
border-radius: 0.25em;
padding-block: 0.25em;
padding-inline: 0.5em;
width: 100%;
}

.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: var(--search-input--focus);
outline-offset: 2px;
outline-width: 2px;
outline-style: solid;
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
46 changes: 42 additions & 4 deletions src/app/pages/search/search.page.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
<app-parent-link [region]="region"></app-parent-link>

<h2 class="text-style--alt 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 @@ -17,12 +22,45 @@ <h2 class="text-style--alt text-style--size-2">
[tabindex]="-1"
class="focus--minimal focus--fade-out"
>
<p *ngIf="!!searchQuery">
{{ regionData?.labelSearchResultsCount }}
<strong>{{ searchResults?.length }}</strong>
<div
*ngIf="useSearchApi"
class="ion-padding"
style="border: 1px solid white; border-radius: 0.25em"
>
<div *ngIf="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="!!searchQuery && searchResultSummary && !loadingSearch"
markdown
[data]="searchResultSummary"
[inline]="false"
></div>
</div>

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

<app-q-a-set-list
*ngIf="!!searchQuery && !loadingSearch"
[baseUrl]="'../'"
[list]="searchResults"
[showDateUpdatedOutsideQuestion]="false"
Expand Down
83 changes: 81 additions & 2 deletions src/app/pages/search/search.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,31 @@ 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 { ParentLinkComponent } from 'src/app/components/parent-link/parent-link.component';
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';
import type { RegionData } from 'src/app/models/region-data';
import { ConfigService } from 'src/app/services/config.service';
import { OffersService } from 'src/app/services/offers.service';
import { PageMetaService } from 'src/app/services/page-meta.service';
import { RegionDataService } from 'src/app/services/region-data.service';
import { SearchService } from 'src/app/services/search.service';
import { environment } from 'src/environments/environment';
import { AppPath } from 'src/routes';

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

@Component({
selector: 'app-search-page',
templateUrl: './search.page.html',
Expand All @@ -25,6 +38,7 @@ import { AppPath } from 'src/routes';
RouterLink,
QASetListComponent,
SearchInputComponent,
MarkdownModule,
ParentLinkComponent,
],
})
Expand All @@ -37,6 +51,8 @@ export default class SearchPageComponent implements OnInit {

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

constructor(
private route: ActivatedRoute,
Expand All @@ -45,6 +61,7 @@ export default class SearchPageComponent implements OnInit {
private offersService: OffersService,
private searchService: SearchService,
private pageMeta: PageMetaService,
private configService: ConfigService,
) {}

async ngOnInit() {
Expand Down Expand Up @@ -94,7 +111,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 @@ -105,7 +124,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 @@ -119,4 +154,48 @@ 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.configService.getRegionByRegionSlug(this.region)
.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 c73f858

Please sign in to comment.