MME-34 Remove tanstack from recipes search page.

This commit is contained in:
Jesse Brault 2026-02-20 09:22:20 -06:00
parent fabb8bd517
commit cd532ef092
3 changed files with 45 additions and 30 deletions

View File

@ -6,13 +6,13 @@
</mat-form-field>
<button matButton="filled" type="submit" [disabled]="!searchRecipesForm.valid">Search</button>
</form>
@if (givenPrompt() !== null) {
@if (resultsQuery.isLoading()) {
<p>Loading search results...</p>
} @else if (resultsQuery.isSuccess()) {
<p>Showing results for {{ givenPrompt() }}</p>
<app-recipe-card-grid [recipes]="resultsQuery.data()" />
} @else if (resultsQuery.isError()) {
<p>There was an error during search.</p>
}
@if (loadingResults()) {
<app-spinner></app-spinner>
} @else if (loadResultsError()) {
<p>There was an error during search. Try again.</p>
} @else if (results()?.length) {
<p>Showing results for '{{ submittedPrompt() }}'</p>
<app-recipe-card-grid [recipes]="results()!" />
} @else if (results()?.length === 0) {
<p>There were no results for '{{ submittedPrompt() }}'</p>
}

View File

@ -1,28 +1,30 @@
import { Component, inject, signal } from '@angular/core';
import { Component, inject, OnInit, signal } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { injectQuery } from '@tanstack/angular-query-experimental';
import { RecipeService } from '../../shared/services/RecipeService';
import { RecipeCardGrid } from '../../shared/components/recipe-card-grid/recipe-card-grid';
import { ActivatedRoute, Router } from '@angular/router';
import { MatButton } from '@angular/material/button';
import { MatFormField, MatInput, MatLabel } from '@angular/material/input';
import { RecipeInfoView } from '../../shared/models/Recipe.model';
import { Spinner } from '../../shared/components/spinner/spinner';
@Component({
selector: 'app-recipes-search-page',
imports: [ReactiveFormsModule, RecipeCardGrid, MatButton, MatFormField, MatInput, MatLabel],
imports: [RecipeCardGrid, MatButton, MatFormField, MatInput, MatLabel, ReactiveFormsModule, Spinner],
templateUrl: './recipes-search-page.html',
styleUrl: './recipes-search-page.css',
})
export class RecipesSearchPage {
export class RecipesSearchPage implements OnInit {
private readonly recipeService = inject(RecipeService);
private readonly router = inject(Router);
private readonly activatedRoute = inject(ActivatedRoute);
public constructor() {
public ngOnInit(): void {
this.activatedRoute.queryParams.subscribe((queryParams) => {
if (queryParams['prompt']) {
this.givenPrompt.set(queryParams['prompt']);
this.searchRecipesForm.controls.prompt.setValue(queryParams['prompt']);
const prompt = queryParams['prompt'] as string;
this.searchRecipesForm.controls.prompt.setValue(prompt);
this.loadResults(prompt);
}
});
}
@ -31,20 +33,34 @@ export class RecipesSearchPage {
prompt: new FormControl('', [Validators.required]),
});
protected readonly givenPrompt = signal<null | string>(null);
protected readonly submittedPrompt = signal<string | null>(null);
protected readonly loadingResults = signal(false);
protected readonly loadResultsError = signal<Error | null>(null);
protected readonly results = signal<RecipeInfoView[] | null>(null);
protected readonly resultsQuery = injectQuery(() => ({
queryFn: () => this.recipeService.aiSearch(this.givenPrompt()!),
queryKey: ['recipes-search', this.givenPrompt()],
enabled: () => !!this.givenPrompt(),
}));
private loadResults(prompt: string): void {
this.submittedPrompt.set(prompt);
this.loadingResults.set(true);
this.recipeService.aiSearch(prompt).subscribe({
next: (results) => {
this.loadingResults.set(false);
this.results.set(results);
},
error: (e) => {
this.loadingResults.set(false);
this.loadResultsError.set(e);
console.error(e);
},
});
}
protected async onPromptSubmit() {
if (this.searchRecipesForm.value.prompt) {
const prompt = this.searchRecipesForm.value.prompt;
await this.router.navigate(['/recipes-search'], {
queryParams: { prompt: this.searchRecipesForm.value.prompt },
queryParams: { prompt },
});
this.givenPrompt.set(this.searchRecipesForm.value.prompt);
this.loadResults(prompt);
}
}
}

View File

@ -143,16 +143,15 @@ export class RecipeService {
return comment;
}
public async aiSearch(prompt: string): Promise<FullRecipeView[]> {
const recipeInfoViews = await firstValueFrom(
this.http.post<{ results: FullRecipeView[] }>(this.endpointService.getUrl('recipes'), {
public aiSearch(prompt: string): Observable<FullRecipeView[]> {
return this.http
.post<{ results: FullRecipeView[] }>(this.endpointService.getUrl('recipes'), {
type: 'AI_PROMPT',
data: {
prompt,
},
}),
);
return recipeInfoViews.results;
})
.pipe(map((res) => res.results));
}
public deleteRecipe(username: string, slug: string): Observable<void> {