Skip to content

Commit

Permalink
Search for characters (#59)
Browse files Browse the repository at this point in the history
* search for characters

* mobile styles
  • Loading branch information
KMontag42 authored Nov 21, 2024
1 parent ba584c2 commit 496bb5b
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 58 deletions.
5 changes: 3 additions & 2 deletions app/api/search/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const query = searchParams.get("q") || undefined;
const tags = searchParams.get("t") || undefined;
const characters = searchParams.get("c") || undefined;

if (!query && !tags) {
if (!query && !tags && !characters) {
return new Response(JSON.stringify({ formations: [] }), {
status: 200,
});
}

const formations = await searchFormations(query, tags);
const formations = await searchFormations(query, tags, characters);

return new Response(JSON.stringify({ formations }), { status: 200 });
}
34 changes: 20 additions & 14 deletions components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,13 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";

import FormationCard from "@/components/FormationCard";

import { FormationData } from "@/lib/formations";
import { type CmsData } from "@/lib/cms-types";
import MultiSelect from "./ui/multi-select";
import { Character } from "@/lib/characters";

type Props = {
cmsData: CmsData;
Expand All @@ -39,7 +33,9 @@ export default function Search({
const [results, setResults] = useState<FormationData[]>([]);
const [loading, setLoading] = useState(true);
const [tags, setTags] = useState<string[]>([]);
const [characters, setCharacters] = useState<string[]>([]);
const allTags = cmsData.tags;
const allCharacters = Object.values(cmsData.characters).map((x) => x.name);

const handleSearch = async (e: FormEvent) => {
e.preventDefault();
Expand All @@ -51,6 +47,9 @@ export default function Search({
if (tags.length > 0) {
params.append("t", tags.join(","));
}
if (characters.length > 0) {
params.append("c", characters.join(","));
}
const response = await fetch(`/api/search?${params.toString()}`);
const data = await response.json();

Expand All @@ -73,10 +72,10 @@ export default function Search({
}, [results]);

return (
<div className="flex flex-col items-center md:px-0 md:container mx-auto">
<div className="flex flex-col items-center md:px-0 md:container mx-auto mt-4">
<form
onSubmit={handleSearch}
className="flex md:w-[40vw] flex-col gap-2 px-2"
className="flex w-full md:w-[40vw] flex-col gap-2 md:px-2"
>
<Label
htmlFor="name"
Expand Down Expand Up @@ -106,11 +105,18 @@ export default function Search({
onChange={(e) => setSearch(e.target.value)}
placeholder="Formation name"
/>
<MultiSelect
itemDescription={"tag"}
items={allTags.map((x) => ({ name: x, value: x }))}
callback={setTags}
/>
<div className="flex flex-col md:flex-row gap-2 md:gap-4">
<MultiSelect
itemDescription={"tag"}
items={allTags.map((x) => ({ name: x, value: x }))}
callback={setTags}
/>
<MultiSelect
itemDescription={"characters"}
items={allCharacters.map((x) => ({ name: x, value: x }))}
callback={setCharacters}
/>
</div>
<Button type="submit">Search</Button>
</form>

Expand Down
2 changes: 1 addition & 1 deletion components/ui/multi-select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default function MultiSelect({
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="md:w-[40vw] p-0">
<PopoverContent className="w-full p-0">
<Command>
<CommandInput placeholder={`Search ${itemDescription}...`} />
<CommandList>
Expand Down
68 changes: 27 additions & 41 deletions lib/server/formations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,51 +66,37 @@ export async function getFormationsForUserId(
export async function searchFormations(
query?: string,
rawTags?: string,
rawCharacters?: string,
): Promise<FormationData[]> {
const heroMap = await heroNameToId();
// split query into words
const words = query?.split(" ").map((x) => x.toLowerCase());
// build our query arrays
const queryWords: string[] = [];
const heroIds: string[] = [];
words?.forEach((word) => {
if (word in heroMap) {
heroIds.push(heroMap[word]);
return;
}
queryWords.push(word);
});
const tags = rawTags?.split(",");

let queryResponse;

if (queryWords.length > 0 || heroIds.length > 0) {
queryResponse = await drizzle.query.formations.findMany({
where: (formations, { like, and }) =>
and(
...[
...queryWords.map((word) => like(formations.name, `%${word}%`)),
...heroIds.map((id) => like(formations.formation, `%${id}%`)),
],
),
with: {
votes: true,
},
});
} else {
// we only care about looking for tags
queryResponse = await drizzle.query.formations.findMany({
with: {
votes: true,
},
});
}
const heroIds = rawCharacters
?.split(",")
.map((x) => heroMap[x.toLowerCase()]);
const tags = rawTags?.split(",");

if (tags && tags.length > 0) {
queryResponse = queryResponse.filter((x) =>
tags.every((t) => x.tags.includes(t)),
);
}
console.log(query, tags, heroIds);
const queryResponse = await drizzle.query.formations.findMany({
where: (formations, { like, and, or }) =>
and(
...[
query ? like(formations.name, `%${query}%`) : undefined,
...(tags ? tags.map((tag) => like(formations.tags, `%${tag}%`)) : []),
...(heroIds
? heroIds.map((id) =>
or(
like(formations.formation, `%,${id},%`),
like(formations.formation, `${id},%`),
like(formations.formation, `%,${id}`),
),
)
: []),
],
),
with: {
votes: true,
},
});

const searchFormations = await Promise.all(
queryResponse.map(async (formation) => {
Expand Down

0 comments on commit 496bb5b

Please sign in to comment.