diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts
index 08ec720..324612b 100644
--- a/src/app/app.routes.ts
+++ b/src/app/app.routes.ts
@@ -1,12 +1,17 @@
import { Routes } from '@angular/router';
import { RecipePage } from './recipe-page/recipe-page';
import { RecipesPage } from './recipes-page/recipes-page';
+import { RecipesSearchPage } from './recipes-search-page/recipes-search-page';
export const routes: Routes = [
{
path: '',
component: RecipesPage,
},
+ {
+ path: 'recipes-search',
+ component: RecipesSearchPage,
+ },
{
path: 'recipes/:username/:slug',
component: RecipePage,
diff --git a/src/app/nav/nav.html b/src/app/nav/nav.html
index b2f5a6f..11bc2f4 100644
--- a/src/app/nav/nav.html
+++ b/src/app/nav/nav.html
@@ -2,5 +2,6 @@
Nav
diff --git a/src/app/pipe/dateTimeFormat.pipe.ts b/src/app/pipe/dateTimeFormat.pipe.ts
index aae77b2..c05f698 100644
--- a/src/app/pipe/dateTimeFormat.pipe.ts
+++ b/src/app/pipe/dateTimeFormat.pipe.ts
@@ -2,14 +2,12 @@ import { inject, Pipe, PipeTransform } from '@angular/core';
import { DateService } from '../service/date.service';
@Pipe({
- name: 'dateTimeFormat'
+ name: 'dateTimeFormat',
})
export class DateTimeFormatPipe implements PipeTransform {
-
private readonly dateService = inject(DateService);
public transform(raw: string): string {
return this.dateService.format(raw);
}
-
}
diff --git a/src/app/recipe-card-grid/recipe-card-grid.css b/src/app/recipe-card-grid/recipe-card-grid.css
new file mode 100644
index 0000000..42e694b
--- /dev/null
+++ b/src/app/recipe-card-grid/recipe-card-grid.css
@@ -0,0 +1,17 @@
+.recipe-card-grid {
+ display: grid;
+ gap: 20px;
+ grid-template-columns: repeat(2, 1fr);
+}
+
+@media screen and (min-width: 1300px) {
+ .recipe-card-grid {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+
+@media screen and (min-width: 1700px) {
+ .recipe-card-grid {
+ grid-template-columns: repeat(4, 1fr);
+ }
+}
diff --git a/src/app/recipe-card-grid/recipe-card-grid.html b/src/app/recipe-card-grid/recipe-card-grid.html
new file mode 100644
index 0000000..e9a31ca
--- /dev/null
+++ b/src/app/recipe-card-grid/recipe-card-grid.html
@@ -0,0 +1,5 @@
+
+ @for (recipe of recipes(); track recipe.id) {
+
+ }
+
diff --git a/src/app/recipe-card-grid/recipe-card-grid.spec.ts b/src/app/recipe-card-grid/recipe-card-grid.spec.ts
new file mode 100644
index 0000000..e9769d9
--- /dev/null
+++ b/src/app/recipe-card-grid/recipe-card-grid.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RecipeCardGrid } from './recipe-card-grid';
+
+describe('RecipeCardGrid', () => {
+ let component: RecipeCardGrid;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [RecipeCardGrid],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(RecipeCardGrid);
+ component = fixture.componentInstance;
+ await fixture.whenStable();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/recipe-card-grid/recipe-card-grid.ts b/src/app/recipe-card-grid/recipe-card-grid.ts
new file mode 100644
index 0000000..046baf9
--- /dev/null
+++ b/src/app/recipe-card-grid/recipe-card-grid.ts
@@ -0,0 +1,13 @@
+import { Component, input } from '@angular/core';
+import { RecipeCard } from './recipe-card/recipe-card';
+import { Recipe } from '../model/Recipe.model';
+
+@Component({
+ selector: 'app-recipe-card-grid',
+ imports: [RecipeCard],
+ templateUrl: './recipe-card-grid.html',
+ styleUrl: './recipe-card-grid.css',
+})
+export class RecipeCardGrid {
+ public readonly recipes = input.required();
+}
diff --git a/src/app/recipes-page/recipe-card/recipe-card.css b/src/app/recipe-card-grid/recipe-card/recipe-card.css
similarity index 100%
rename from src/app/recipes-page/recipe-card/recipe-card.css
rename to src/app/recipe-card-grid/recipe-card/recipe-card.css
diff --git a/src/app/recipes-page/recipe-card/recipe-card.html b/src/app/recipe-card-grid/recipe-card/recipe-card.html
similarity index 100%
rename from src/app/recipes-page/recipe-card/recipe-card.html
rename to src/app/recipe-card-grid/recipe-card/recipe-card.html
diff --git a/src/app/recipes-page/recipe-card/recipe-card.spec.ts b/src/app/recipe-card-grid/recipe-card/recipe-card.spec.ts
similarity index 100%
rename from src/app/recipes-page/recipe-card/recipe-card.spec.ts
rename to src/app/recipe-card-grid/recipe-card/recipe-card.spec.ts
diff --git a/src/app/recipes-page/recipe-card/recipe-card.ts b/src/app/recipe-card-grid/recipe-card/recipe-card.ts
similarity index 100%
rename from src/app/recipes-page/recipe-card/recipe-card.ts
rename to src/app/recipe-card-grid/recipe-card/recipe-card.ts
diff --git a/src/app/recipe-comments-list/recipe-comments-list.component.html b/src/app/recipe-comments-list/recipe-comments-list.component.html
index 7b79ca5..7d59a85 100644
--- a/src/app/recipe-comments-list/recipe-comments-list.component.html
+++ b/src/app/recipe-comments-list/recipe-comments-list.component.html
@@ -31,7 +31,9 @@
diff --git a/src/app/recipe-comments-list/recipe-comments-list.component.ts b/src/app/recipe-comments-list/recipe-comments-list.component.ts
index 6713366..fc160a8 100644
--- a/src/app/recipe-comments-list/recipe-comments-list.component.ts
+++ b/src/app/recipe-comments-list/recipe-comments-list.component.ts
@@ -47,7 +47,7 @@ export class RecipeCommentsList {
protected readonly addCommentMutation = injectMutation(() => ({
mutationFn: (commentText: string) =>
- this.recipeService.addComment(this.recipeUsername(), this.recipeSlug(), commentText)
+ this.recipeService.addComment(this.recipeUsername(), this.recipeSlug(), commentText),
}));
protected onCommentSubmit() {
diff --git a/src/app/recipes-page/recipes-page.css b/src/app/recipes-page/recipes-page.css
index 42e694b..e69de29 100644
--- a/src/app/recipes-page/recipes-page.css
+++ b/src/app/recipes-page/recipes-page.css
@@ -1,17 +0,0 @@
-.recipe-card-grid {
- display: grid;
- gap: 20px;
- grid-template-columns: repeat(2, 1fr);
-}
-
-@media screen and (min-width: 1300px) {
- .recipe-card-grid {
- grid-template-columns: repeat(3, 1fr);
- }
-}
-
-@media screen and (min-width: 1700px) {
- .recipe-card-grid {
- grid-template-columns: repeat(4, 1fr);
- }
-}
diff --git a/src/app/recipes-page/recipes-page.html b/src/app/recipes-page/recipes-page.html
index 79cd5db..935f60d 100644
--- a/src/app/recipes-page/recipes-page.html
+++ b/src/app/recipes-page/recipes-page.html
@@ -1,10 +1,6 @@
Recipes
@if (recipes.isSuccess()) {
-
- @for (recipe of recipes.data(); track recipe.id) {
-
- }
-
+
} @else if (recipes.isLoading()) {
Loading...
} @else if (recipes.isError()) {
diff --git a/src/app/recipes-page/recipes-page.ts b/src/app/recipes-page/recipes-page.ts
index e6cc3d5..f48faf8 100644
--- a/src/app/recipes-page/recipes-page.ts
+++ b/src/app/recipes-page/recipes-page.ts
@@ -1,11 +1,11 @@
import { Component, inject } from '@angular/core';
import { RecipeService } from '../service/recipe.service';
import { injectQuery } from '@tanstack/angular-query-experimental';
-import { RecipeCard } from './recipe-card/recipe-card';
+import { RecipeCardGrid } from '../recipe-card-grid/recipe-card-grid';
@Component({
selector: 'app-recipes-page',
- imports: [RecipeCard],
+ imports: [RecipeCardGrid],
templateUrl: './recipes-page.html',
styleUrl: './recipes-page.css',
})
diff --git a/src/app/recipes-search-page/recipes-search-page.css b/src/app/recipes-search-page/recipes-search-page.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/recipes-search-page/recipes-search-page.html b/src/app/recipes-search-page/recipes-search-page.html
new file mode 100644
index 0000000..9ecb80a
--- /dev/null
+++ b/src/app/recipes-search-page/recipes-search-page.html
@@ -0,0 +1,16 @@
+Search Recipes
+
+@if (givenPrompt() !== null) {
+ @if (resultsQuery.isLoading()) {
+ Loading search results...
+ } @else if (resultsQuery.isSuccess()) {
+ Showing results for {{ givenPrompt() }}
+
+ } @else if (resultsQuery.isError()) {
+ There was an error during search.
+ }
+}
diff --git a/src/app/recipes-search-page/recipes-search-page.spec.ts b/src/app/recipes-search-page/recipes-search-page.spec.ts
new file mode 100644
index 0000000..5e3539f
--- /dev/null
+++ b/src/app/recipes-search-page/recipes-search-page.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RecipesSearchPage } from './recipes-search-page';
+
+describe('RecipesSearchPage', () => {
+ let component: RecipesSearchPage;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [RecipesSearchPage],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(RecipesSearchPage);
+ component = fixture.componentInstance;
+ await fixture.whenStable();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/recipes-search-page/recipes-search-page.ts b/src/app/recipes-search-page/recipes-search-page.ts
new file mode 100644
index 0000000..7715ffa
--- /dev/null
+++ b/src/app/recipes-search-page/recipes-search-page.ts
@@ -0,0 +1,33 @@
+import { Component, inject, signal } from '@angular/core';
+import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { injectQuery } from '@tanstack/angular-query-experimental';
+import { RecipeService } from '../service/recipe.service';
+import { RecipeCardGrid } from '../recipe-card-grid/recipe-card-grid';
+
+@Component({
+ selector: 'app-recipes-search-page',
+ imports: [ReactiveFormsModule, RecipeCardGrid],
+ templateUrl: './recipes-search-page.html',
+ styleUrl: './recipes-search-page.css',
+})
+export class RecipesSearchPage {
+ private readonly recipeService = inject(RecipeService);
+
+ protected readonly searchRecipesForm = new FormGroup({
+ prompt: new FormControl('', [Validators.required]),
+ });
+
+ protected readonly givenPrompt = signal(null);
+
+ protected readonly resultsQuery = injectQuery(() => ({
+ queryFn: () => this.recipeService.aiSearch(this.givenPrompt()!),
+ queryKey: ['recipes-search', this.givenPrompt()],
+ enabled: () => !!this.givenPrompt(),
+ }));
+
+ protected onPromptSubmit() {
+ if (this.searchRecipesForm.value.prompt) {
+ this.givenPrompt.set(this.searchRecipesForm.value.prompt);
+ }
+ }
+}
diff --git a/src/app/service/date.service.ts b/src/app/service/date.service.ts
index c6e29d1..45c6765 100644
--- a/src/app/service/date.service.ts
+++ b/src/app/service/date.service.ts
@@ -1,17 +1,15 @@
import { Injectable } from '@angular/core';
@Injectable({
- providedIn: 'root'
+ providedIn: 'root',
})
export class DateService {
-
private readonly dateTimeFormat = Intl.DateTimeFormat('en-US', {
dateStyle: 'long',
- timeStyle: 'short'
+ timeStyle: 'short',
});
public format(raw: string): string {
return this.dateTimeFormat.format(new Date(raw));
}
-
}
diff --git a/src/app/service/endpoint.service.ts b/src/app/service/endpoint.service.ts
index a8be3d5..25327ff 100644
--- a/src/app/service/endpoint.service.ts
+++ b/src/app/service/endpoint.service.ts
@@ -33,7 +33,7 @@ export class EndpointService {
}
});
- let pathString = pathParams?.join('/');
+ let pathString = pathParams?.join('/') || '';
if (pathString?.length) {
pathString = '/' + pathString;
}
diff --git a/src/app/service/recipe.service.ts b/src/app/service/recipe.service.ts
index 0e1a1e1..2aa285d 100644
--- a/src/app/service/recipe.service.ts
+++ b/src/app/service/recipe.service.ts
@@ -80,4 +80,16 @@ export class RecipeService {
});
return comment;
}
+
+ public async aiSearch(prompt: string): Promise {
+ const recipeInfoViews = await firstValueFrom(
+ this.http.post<{ results: Recipe[] }>(this.endpointService.getUrl('recipes'), {
+ type: 'AI_PROMPT',
+ data: {
+ prompt,
+ },
+ }),
+ );
+ return recipeInfoViews.results;
+ }
}