From 1246c8382c8734dc705f96bf9fa6b5efdd3819bc Mon Sep 17 00:00:00 2001
From: yyamashita <yyamashita@mosquit.one>
Date: Fri, 8 May 2026 08:38:32 +0900
Subject: Fix all TODO bugs and implement feature additions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- SCRAPE_TARGETS.md: add 5 missing venues (nine-spices, nishieifuku-jam,
  fever-shindaita, moon-step-nakano, mod-shibasaki)
- Navigation: add 日付別 link to venues.tsx and events.$id.tsx headers
- venues.tsx: add official site external links per venue card
- ScrapeButton: new component with useFetcher-based trigger + 2s polling
  progress UI showing per-venue status and event count
- venues.tsx / events._index.tsx: wire in ScrapeButton
- FilterBar + db.server.ts: add area filter derived from venues, threaded
  through queryEvents

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
 app/routes/events._index.tsx | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

(limited to 'app/routes/events._index.tsx')

diff --git a/app/routes/events._index.tsx b/app/routes/events._index.tsx
index cb1a019..890e0fd 100644
--- a/app/routes/events._index.tsx
+++ b/app/routes/events._index.tsx
@@ -4,6 +4,7 @@ import { queryEvents, getVenues, type CapacityRange } from "~/lib/db.server";
 import EventCard from "~/components/EventCard";
 import EventListRow from "~/components/EventListRow";
 import FilterBar from "~/components/FilterBar";
+import ScrapeButton from "~/components/ScrapeButton";
 
 function defaultWindow() {
   const today = new Date();
@@ -24,18 +25,20 @@ export async function loader({ request }: Route.LoaderArgs) {
   const venue_id = url.searchParams.get("venue_id") ?? undefined;
   const keyword = url.searchParams.get("keyword") ?? undefined;
   const capacity_range = (url.searchParams.get("capacity_range") ?? undefined) as CapacityRange | undefined;
+  const area = url.searchParams.get("area") ?? undefined;
   const page = Math.max(1, parseInt(url.searchParams.get("page") ?? "1", 10));
   const limit = 30;
   const offset = (page - 1) * limit;
 
-  const events = queryEvents({ date_from, date_to, venue_id, keyword, capacity_range, limit, offset });
+  const events = queryEvents({ date_from, date_to, venue_id, keyword, capacity_range, area, limit, offset });
   const venues = getVenues();
+  const areas = [...new Set(venues.filter((v) => v.area).map((v) => v.area as string))].sort();
 
-  return { events, venues, page, hasMore: events.length === limit, date_from, date_to };
+  return { events, venues, areas, page, hasMore: events.length === limit, date_from, date_to };
 }
 
 export default function EventsIndex() {
-  const { events, venues, page, hasMore, date_from, date_to } = useLoaderData<typeof loader>();
+  const { events, venues, areas, page, hasMore, date_from, date_to } = useLoaderData<typeof loader>();
   const [searchParams, setSearchParams] = useSearchParams();
   const view = (searchParams.get("view") ?? "card") as "card" | "list";
 
@@ -62,6 +65,8 @@ export default function EventsIndex() {
       <main className="max-w-6xl mx-auto px-4 py-8">
         <div className="mb-6 flex items-center justify-between">
           <h1 className="text-2xl font-bold">イベント一覧</h1>
+          <div className="flex items-center gap-3">
+          <ScrapeButton />
           <div className="flex rounded-lg overflow-hidden border border-gray-700 text-sm">
             <button
               onClick={() => switchView("card")}
@@ -78,9 +83,10 @@ export default function EventsIndex() {
               ☰
             </button>
           </div>
+          </div>
         </div>
 
-        <FilterBar venues={venues} defaultDateFrom={date_from} defaultDateTo={date_to} />
+        <FilterBar venues={venues} areas={areas} defaultDateFrom={date_from} defaultDateTo={date_to} />
 
         {events.length === 0 ? (
           <div className="mt-16 text-center text-gray-500">
-- 
cgit v1.2.3

