From b19f46ff929a5a9c0f03df3c54493ff4477f74de Mon Sep 17 00:00:00 2001 From: Stedoss <29103029+Stedoss@users.noreply.github.com> Date: Tue, 1 Nov 2022 19:58:15 +0000 Subject: [PATCH] Fix tag search, add text name search, add config around seeding --- README.md | 8 ++---- .../Controllers/VenueController.cs | 7 +++-- .../Data/Seed/LeedsBeerQuestSeeder.cs | 3 +-- .../Data/Services/Interfaces/IVenueService.cs | 2 +- .../Data/Services/VenueService.cs | 19 ++++++++++++-- .../LeedsBeerQuest.API/Program.cs | 2 +- .../appsettings.Development.json | 5 ++++ .../src/components/venue-grid.tsx | 2 ++ frontend/leeds-beer-quest/src/lib/api.ts | 10 ++++--- frontend/leeds-beer-quest/src/lib/types.ts | 1 + frontend/leeds-beer-quest/src/pages/index.tsx | 26 ++++++++++++------- 11 files changed, 58 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index a125187..418cb6b 100644 --- a/README.md +++ b/README.md @@ -133,10 +133,6 @@ More for the frontend, but backend doesn't have the best either. More checks mak Even if it's just a few, it will show off how I unit test for the frontend. -### A little better configuration for seeding ect +### Cors? -Just to say I have. - -### Make tags work - -The last big thing. \ No newline at end of file +Cors may be an issue here. \ No newline at end of file diff --git a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Controllers/VenueController.cs b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Controllers/VenueController.cs index 17fbf8c..bcb58ed 100644 --- a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Controllers/VenueController.cs +++ b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Controllers/VenueController.cs @@ -15,9 +15,12 @@ public class VenueController : ControllerBase } [HttpGet] - public async Task GetVenues([FromQuery] string[]? categories = null, [FromQuery] string[]? tags = null) + public async Task GetVenues( + [FromQuery] string? search = null, + [FromQuery] string[]? categories = null, + [FromQuery] string[]? tags = null) { - var venues = await _venueService.GetFilteredVenues(null, categories); + var venues = await _venueService.GetFilteredVenues(search, tags, categories); return Ok(venues); } diff --git a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Seed/LeedsBeerQuestSeeder.cs b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Seed/LeedsBeerQuestSeeder.cs index 0e71200..a17363d 100644 --- a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Seed/LeedsBeerQuestSeeder.cs +++ b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Seed/LeedsBeerQuestSeeder.cs @@ -2,7 +2,6 @@ using CsvHelper; using LeedsBeerQuest.API.Data.Contexts; using LeedsBeerQuest.API.Data.Models; -using Microsoft.EntityFrameworkCore; namespace LeedsBeerQuest.API.Data.Seed; @@ -28,7 +27,7 @@ public class LeedsBeerQuestSeeder IEnumerable? lbqCSV; - using (var reader = new StreamReader("../leedsbeerquest.csv")) + using (var reader = new StreamReader(_dataSourcePath)) using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) { lbqCSV = csv.GetRecords().ToList(); diff --git a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Services/Interfaces/IVenueService.cs b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Services/Interfaces/IVenueService.cs index 1488b68..d3362c1 100644 --- a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Services/Interfaces/IVenueService.cs +++ b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Services/Interfaces/IVenueService.cs @@ -6,5 +6,5 @@ public interface IVenueService { public Task> GetAllVenues(); public Task GetVenue(int venueId); - public Task> GetFilteredVenues(string[]? tags = null, string[]? categories = null); + public Task> GetFilteredVenues(string? nameSearch = null, string[]? tags = null, string[]? categories = null); } \ No newline at end of file diff --git a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Services/VenueService.cs b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Services/VenueService.cs index 3638bab..cfc69ee 100644 --- a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Services/VenueService.cs +++ b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Data/Services/VenueService.cs @@ -30,7 +30,10 @@ public class VenueService : IVenueService .SingleOrDefaultAsync(v => v.Id == venueId); } - public async Task> GetFilteredVenues(string[]? tags = null, string[]? categories = null) + public async Task> GetFilteredVenues( + string? nameSearch = null, + string[]? tags = null, + string[]? categories = null) { var venues = _context.Venues .Include(v => v.Category) @@ -39,9 +42,21 @@ public class VenueService : IVenueService IQueryable filteredVenues = venues; + if (nameSearch != null) + { + filteredVenues = filteredVenues.Where(v => v.Name.ToLower().Contains(nameSearch.ToLower())); + } + if (categories is { Length: > 0 }) { - filteredVenues = venues.Where(v => categories.Contains(v.Category.Name)); + filteredVenues = filteredVenues.Where(v => categories.Contains(v.Category.Name)); + } + + if (tags is { Length: > 0 }) + { + filteredVenues = tags.Aggregate(filteredVenues, (current, filteredTag) => + current.Where(v => + v.Tags.Contains(_context.Tags.FirstOrDefault(t => t.Name == filteredTag)!))); } return await filteredVenues.ToListAsync(); diff --git a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Program.cs b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Program.cs index cf81416..79e14dd 100644 --- a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Program.cs +++ b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/Program.cs @@ -19,7 +19,7 @@ var app = builder.Build(); #if LBQ_SEED_DATA var dbContext = app.Services.GetRequiredService(); -var seeder = new LeedsBeerQuestSeeder(dbContext!, ""); +var seeder = new LeedsBeerQuestSeeder(dbContext!, app.Configuration["Database:Seed:SeedDataLocation"]); seeder.Seed(); #endif diff --git a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/appsettings.Development.json b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/appsettings.Development.json index 0c208ae..8b1e135 100644 --- a/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/appsettings.Development.json +++ b/backend/LeedsBeerQuest.API/LeedsBeerQuest.API/appsettings.Development.json @@ -4,5 +4,10 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "Database": { + "Seed": { + "SeedDataLocation": "../leedsbeerquest.csv" + } } } diff --git a/frontend/leeds-beer-quest/src/components/venue-grid.tsx b/frontend/leeds-beer-quest/src/components/venue-grid.tsx index c9f7cfa..bee8603 100644 --- a/frontend/leeds-beer-quest/src/components/venue-grid.tsx +++ b/frontend/leeds-beer-quest/src/components/venue-grid.tsx @@ -17,6 +17,7 @@ export function VenueGrid(props: TVenueGridProps) { Category Date Visited Excerpt + Tags Phone @@ -32,6 +33,7 @@ export function VenueGrid(props: TVenueGridProps) { {venue.category.name} {venue.dateAttended.toString()} {venue.excerpt} + {venue.tags.map(t => `${t.name} `)} {venue.phone} ))} diff --git a/frontend/leeds-beer-quest/src/lib/api.ts b/frontend/leeds-beer-quest/src/lib/api.ts index 7fc4b74..104a023 100644 --- a/frontend/leeds-beer-quest/src/lib/api.ts +++ b/frontend/leeds-beer-quest/src/lib/api.ts @@ -5,9 +5,13 @@ export class API { constructor(private baseAPIUrl: string) {} getVenues = async (venueFilters: TVenueFilters | null): Promise => { - const categoriesParams = ArrayToArrayUrlParams("categories", venueFilters?.categories.map((c) => c.name) ?? []); - const response = await fetch(`${this.baseAPIUrl}/venue${categoriesParams}`); - console.log(`calling ${this.baseAPIUrl}/venue${categoriesParams}`); + let params = ""; + params += ArrayToArrayUrlParams("categories", venueFilters?.categories.map((c) => c.name) ?? []); + params += ArrayToArrayUrlParams("tags", venueFilters?.tags.map((v) => v.name) ?? [], params.length < 1); + params += venueFilters?.nameFilter ? `${(params.length < 1 ? "?" : "&")}search=${venueFilters.nameFilter}` : ""; + + const response = await fetch(`${this.baseAPIUrl}/venue${params}`); + console.log(`calling ${this.baseAPIUrl}/venue${params}`); return await response.json(); } diff --git a/frontend/leeds-beer-quest/src/lib/types.ts b/frontend/leeds-beer-quest/src/lib/types.ts index 0a43a81..dfac50b 100644 --- a/frontend/leeds-beer-quest/src/lib/types.ts +++ b/frontend/leeds-beer-quest/src/lib/types.ts @@ -30,6 +30,7 @@ export type TVenue = { } export type TVenueFilters = { + nameFilter: string | null; tags: TTag[]; categories: TCategory[]; } \ No newline at end of file diff --git a/frontend/leeds-beer-quest/src/pages/index.tsx b/frontend/leeds-beer-quest/src/pages/index.tsx index f2c47d3..b8a0e22 100644 --- a/frontend/leeds-beer-quest/src/pages/index.tsx +++ b/frontend/leeds-beer-quest/src/pages/index.tsx @@ -1,34 +1,40 @@ -import { useQuery, useQueryClient } from "@tanstack/react-query"; -import { useEffect, useState } from "react"; +import { TextField } from "@mui/material"; +import { useQuery } from "@tanstack/react-query"; +import { ChangeEventHandler, useState } from "react"; import { DropdownFilter } from "../components/dropdown-filter"; import { VenueGrid } from "../components/venue-grid"; import { API } from "../lib/api"; import { Environment } from "../lib/environment"; -import { TCategory, TTag, TVenue, TVenueFilters } from "../lib/types"; +import { TCategory, TTag, TVenue } from "../lib/types"; export function Index() { + const [nameTextFilter, setNameTextFilter] = useState(null); const [filteredTags, setFilteredTags] = useState([]); const [filteredCategories, setFilteredCategories] = useState([]); const api = new API(Environment.BaseAPIUrl); - const queryClient = useQueryClient(); - - const { data: venueData } = useQuery(["venues", filteredCategories], () => api.getVenues({categories: filteredCategories, tags: filteredTags})); + const { data: venueData } = useQuery(["venues", filteredCategories, filteredTags, nameTextFilter], () => api.getVenues({nameFilter: nameTextFilter, categories: filteredCategories, tags: filteredTags})); const { data: categoryData } = useQuery(["categories"], api.getCategories); const { data: tagData } = useQuery(["tags"], api.getTags); - // useEffect(() => { - // console.log("useeffect called!"); - // queryClient.invalidateQueries(["venues"]); - // }, [filteredCategories]); + const handleSearchChange = (event: any) => { + const { value } = event.target; + + if (value && value.length === 0) { + setNameTextFilter(null); + } + + setNameTextFilter(value as string); + } return( <>

Leeds Beer Quest

+