From b9e06d9a139e6d9a6d509f500beee97d0d071602 Mon Sep 17 00:00:00 2001 From: yyamashita Date: Sat, 15 Aug 2026 20:12:25 +0900 Subject: 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. --- app/app.css | 2 ++ app/lib/db.server.ts | 19 ++++++++++++++ app/root.tsx | 4 +++ app/routes.ts | 1 + app/routes/search.tsx | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 app/routes/search.tsx diff --git a/app/app.css b/app/app.css index 9125f90..0e301b5 100644 --- a/app/app.css +++ b/app/app.css @@ -23,6 +23,8 @@ button { cursor: pointer; } .nav-brand { font-weight: 700; color: #f9fafb; font-size: 1.1rem; } .nav a { font-size: .875rem; color: #9ca3af; } .nav a:hover { color: #f3f4f6; text-decoration: none; } +.nav-search { margin-left: auto; } +.nav-search input { font-size: .8rem; padding: .3rem .625rem; width: 12rem; } main { max-width: 760px; margin: 2rem auto; padding: 0 1.5rem; } diff --git a/app/lib/db.server.ts b/app/lib/db.server.ts index 37d819b..e554d75 100644 --- a/app/lib/db.server.ts +++ b/app/lib/db.server.ts @@ -481,6 +481,25 @@ function buildSnapshot(band: Band, links: BandLink[], members: BandMemberRow[]): }); } +// ── Search ──────────────────────────────────────────────────────────────────── + +export interface SearchResults { + bands: Band[]; + artists: Artist[]; +} + +export function searchAll(query: string): SearchResults { + const db = getDb(); + const q = `%${query}%`; + const bands = db + .prepare("SELECT * FROM bands WHERE name LIKE ? OR area LIKE ? OR description LIKE ? ORDER BY name LIMIT 50") + .all(q, q, q) as Band[]; + const artists = db + .prepare("SELECT * FROM artists WHERE name LIKE ? ORDER BY name LIMIT 50") + .all(q) as Artist[]; + return { bands, artists }; +} + // ── Artist queries ──────────────────────────────────────────────────────────── export function listArtists(): Artist[] { diff --git a/app/root.tsx b/app/root.tsx index 6c60350..f422f52 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -1,4 +1,5 @@ import { + Form, isRouteErrorResponse, Link, Links, @@ -55,6 +56,9 @@ export default function App() { whois.band Bands Artists +
+ +
diff --git a/app/routes.ts b/app/routes.ts index 6125a73..5660edb 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -2,6 +2,7 @@ import { type RouteConfig, index, route } from "@react-router/dev/routes"; export default [ index("routes/home.tsx"), + route("/search", "routes/search.tsx"), route("/api/bands", "routes/api-bands.tsx"), route("/api/artists", "routes/api-artists.tsx"), route("/api/export", "routes/api-export.tsx"), 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(); + const hasResults = bands.length > 0 || artists.length > 0; + + return ( +
+
+

検索

+
+ +
+ +
+ + {!q ? ( +

検索キーワードを入力してください。

+ ) : !hasResults ? ( +

「{q}」に一致する結果が見つかりませんでした。

+ ) : ( + <> + {bands.length > 0 && ( +
+

Bands

+
    + {bands.map((band) => ( +
  • + {band.name} + {band.area && ( + {band.area} + )} + {band.status === "hiatus" && ( + 活動休止 + )} +
  • + ))} +
+
+ )} + + {artists.length > 0 && ( +
+

Artists

+
    + {artists.map((artist) => ( +
  • + {artist.name} +
  • + ))} +
+
+ )} + + )} +
+ ); +} -- cgit v1.2.3