diff options
| author | yyamashita <yyamashita@hetzner.yyamashita.com> | 2026-08-15 20:12:25 +0900 |
|---|---|---|
| committer | yyamashita <yyamashita@hetzner.yyamashita.com> | 2026-08-15 20:12:25 +0900 |
| commit | b9e06d9a139e6d9a6d509f500beee97d0d071602 (patch) | |
| tree | 1e131cac0c65471da6b17ca1abff258412f8816f /app/routes/search.tsx | |
| parent | 1777cdbf812959e39a2b2abc142afc2fe8932938 (diff) | |
Add site-wide search for bands and artists
Adds a nav search box and /search route backed by a LIKE-based
searchAll() query over band name/area/description and artist name.
Diffstat (limited to 'app/routes/search.tsx')
| -rw-r--r-- | app/routes/search.tsx | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/app/routes/search.tsx b/app/routes/search.tsx new file mode 100644 index 0000000..a4c03b7 --- /dev/null +++ b/app/routes/search.tsx @@ -0,0 +1,73 @@ +import { Form, Link, useLoaderData } from "react-router"; +import type { Route } from "./+types/search"; +import { searchAll } from "~/lib/db.server"; + +export function loader({ request }: Route.LoaderArgs) { + const q = new URL(request.url).searchParams.get("q")?.trim() ?? ""; + if (!q) return { q, bands: [], artists: [] }; + const { bands, artists } = searchAll(q); + return { q, bands, artists }; +} + +export default function Search() { + const { q, bands, artists } = useLoaderData<typeof loader>(); + const hasResults = bands.length > 0 || artists.length > 0; + + return ( + <main> + <div className="page-header"> + <h1>検索</h1> + </div> + + <Form method="get" action="/search" style={{ marginBottom: "1.5rem" }}> + <input + type="search" + name="q" + defaultValue={q} + placeholder="バンド名・アーティスト名で検索" + autoFocus + /> + </Form> + + {!q ? ( + <p className="muted">検索キーワードを入力してください。</p> + ) : !hasResults ? ( + <p className="muted">「{q}」に一致する結果が見つかりませんでした。</p> + ) : ( + <> + {bands.length > 0 && ( + <section> + <h2>Bands</h2> + <ul className="band-list"> + {bands.map((band) => ( + <li key={band.id}> + <Link to={`/bands/of/${band.id}`}>{band.name}</Link> + {band.area && ( + <span className="muted" style={{ fontSize: ".75rem" }}>{band.area}</span> + )} + {band.status === "hiatus" && ( + <span className="muted" style={{ fontSize: ".75rem" }}>活動休止</span> + )} + </li> + ))} + </ul> + </section> + )} + + {artists.length > 0 && ( + <section> + <h2>Artists</h2> + <ul className="band-list"> + {artists.map((artist) => ( + <li key={artist.id}> + <Link to={`/artists/of/${artist.id}`}>{artist.name}</Link> + </li> + ))} + </ul> + </section> + )} + </> + )} + </main> + ); +} |
