import { useLoaderData, Link } from "react-router"; import type { Route } from "./+types/events.$id"; import { getEvent } from "~/lib/db.server"; export async function loader({ params }: Route.LoaderArgs) { const id = parseInt(params.id, 10); if (isNaN(id)) throw new Response("Not Found", { status: 404 }); const event = getEvent(id); if (!event) throw new Response("Not Found", { status: 404 }); return { event }; } function buildGoogleCalendarUrl(event: Awaited>["event"]): string { const dates = (() => { if (event.start_time && /^\d{1,2}:\d{2}/.test(event.start_time)) { const [h, m] = event.start_time.split(":").map(Number); const [y, mo, d] = event.date.split("-").map(Number); const pad = (n: number) => String(n).padStart(2, "0"); const startStr = `${event.date.replace(/-/g, "")}T${pad(h)}${pad(m)}00`; const endH = h + 2; let endStr: string; if (endH < 24) { endStr = `${event.date.replace(/-/g, "")}T${pad(endH)}${pad(m)}00`; } else { const next = new Date(y, mo - 1, d + 1); endStr = `${next.getFullYear()}${pad(next.getMonth() + 1)}${pad(next.getDate())}` + `T${pad(endH - 24)}${pad(m)}00`; } return `${startStr}/${endStr}`; } const [y, mo, d] = event.date.split("-").map(Number); const next = new Date(y, mo - 1, d + 1); const pad = (n: number) => String(n).padStart(2, "0"); const dateStr = event.date.replace(/-/g, ""); const nextStr = `${next.getFullYear()}${pad(next.getMonth() + 1)}${pad(next.getDate())}`; return `${dateStr}/${nextStr}`; })(); const details: string[] = []; if (event.artist) details.push(`出演: ${event.artist}`); if (event.open_time) details.push(`OPEN: ${event.open_time}`); if (event.start_time) details.push(`START: ${event.start_time}`); if (event.price) details.push(`料金: ${event.price}`); if (event.source_url) details.push(`詳細: ${event.source_url}`); if (event.ticket_url) details.push(`チケット: ${event.ticket_url}`); const location = [event.venue_name, event.venue_area].filter(Boolean).join(" "); const params = new URLSearchParams({ action: "TEMPLATE", text: event.title, dates, location, ctz: "Asia/Tokyo", }); if (details.length > 0) params.set("details", details.join("\n")); return `https://calendar.google.com/calendar/render?${params.toString()}`; } export default function EventDetail() { const { event } = useLoaderData(); return (
🎸 ライブに行くしかない
← イベント一覧に戻る
{event.image_url && ( {event.title} )}

{event.title}

{event.artist && (

{event.artist}

)}
{event.venue_name}
{event.open_time && } {event.start_time && } {event.price && } {event.venue_area && }
{event.description && (

{event.description}

)}
{event.ticket_url && ( チケット購入 )} {event.source_url && ( 詳細ページ )} {event.venue_url && ( 会場サイト )}

最終取得: {event.fetched_at}

); } function CalendarIcon() { return ( ); } function Detail({ label, value }: { label: string; value: string }) { return (
{label}
{value}
); } function formatDate(iso: string): string { const [y, m, d] = iso.split("-"); const days = ["日", "月", "火", "水", "木", "金", "土"]; const day = days[new Date(iso).getDay()]; return `${y}年${m}月${d}日(${day})`; }