From 0cd5fb770ca9bd3f304d9556a4b33a4ad4f45e7e Mon Sep 17 00:00:00 2001
From: yyamashita <yyamashita@mosquit.one>
Date: Thu, 7 May 2026 10:16:43 +0900
Subject: Playwright scraping for FLAT/Pitbar; web UI display-only

- Install Playwright + Chromium; add shared browser singleton (playwright.server.ts)
- Rewrite flat-nishiogikubo scraper: Wix calendar via headless browser,
  month navigation via date picker, extracts .WPczEB/.ExCBIq selectors
- Rewrite pitbar-nishiogikubo scraper: freecalend.com via headless browser,
  parses cal-{member}-{year}-{month}-{day} cell IDs
- scraper-runner: close shared browser after each run with closeBrowser()
- Remove all scrape trigger buttons from web UI (events index, venues page);
  remove /api/scrape and /api/scrape-status from routes.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
 app/routes/venues.tsx | 41 ++++++++---------------------------------
 1 file changed, 8 insertions(+), 33 deletions(-)

(limited to 'app/routes/venues.tsx')

diff --git a/app/routes/venues.tsx b/app/routes/venues.tsx
index affa72a..fca90dd 100644
--- a/app/routes/venues.tsx
+++ b/app/routes/venues.tsx
@@ -1,18 +1,15 @@
-import { useLoaderData, Link, Form } from "react-router";
+import { useLoaderData, Link } from "react-router";
 import type { Route } from "./+types/venues";
 import { getVenues, getLastScrapePerVenue, type ScrapeLog } from "~/lib/db.server";
-import { getScraperIds } from "~/lib/venue-meta.server";
 
 export async function loader(_: Route.LoaderArgs) {
   const venues = getVenues();
-  const scraperIds = getScraperIds();
   const scrapeStatus = getLastScrapePerVenue();
-  return { venues, scraperIds, scrapeStatus };
+  return { venues, scrapeStatus };
 }
 
 export default function Venues() {
-  const { venues, scraperIds: scraperIdList, scrapeStatus } = useLoaderData<typeof loader>();
-  const scraperIds = new Set(scraperIdList);
+  const { venues, scrapeStatus } = useLoaderData<typeof loader>();
   const statusMap = new Map<string, ScrapeLog>(scrapeStatus.map((s) => [s.venue_id, s]));
 
   return (
@@ -28,21 +25,11 @@ export default function Venues() {
       </header>
 
       <main className="max-w-4xl mx-auto px-4 py-10">
-        <div className="mb-8 flex items-start justify-between gap-4 flex-wrap">
-          <div>
-            <h1 className="text-2xl font-bold">会場一覧</h1>
-            <p className="mt-1 text-sm text-gray-400">
-              現在 {scraperIdList.length} 会場のスクレイパーが登録されています。
-            </p>
-          </div>
-          <Form method="post" action="/api/scrape">
-            <button
-              type="submit"
-              className="rounded-md bg-indigo-600 px-4 py-2 text-sm font-medium hover:bg-indigo-500 transition-colors"
-            >
-              全会場を更新
-            </button>
-          </Form>
+        <div className="mb-8">
+          <h1 className="text-2xl font-bold">会場一覧</h1>
+          <p className="mt-1 text-sm text-gray-400">
+            現在 {venues.length} 会場が登録されています。
+          </p>
         </div>
 
         {venues.length === 0 ? (
@@ -79,18 +66,6 @@ export default function Venues() {
                     <span className="text-xs text-gray-600 whitespace-nowrap">未実行</span>
                   )}
 
-                  {/* 個別更新ボタン */}
-                  {scraperIds.has(v.id) && (
-                    <Form method="post" action="/api/scrape">
-                      <input type="hidden" name="venue_id" value={v.id} />
-                      <button
-                        type="submit"
-                        className="rounded bg-gray-700 px-3 py-1 text-xs hover:bg-gray-600 transition-colors whitespace-nowrap"
-                      >
-                        更新
-                      </button>
-                    </Form>
-                  )}
                 </div>
               );
             })}
-- 
cgit v1.2.3

