blob: 6651ff9fe03671d619ab0e485bc704e393cff92e (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
import { Link } from "react-router";
import type { Event } from "~/lib/db.server";
interface Props {
event: Event;
}
export default function EventCard({ event }: Props) {
const formattedDate = formatDate(event.date);
const timeLabel = buildTimeLabel(event.open_time, event.start_time);
return (
<Link
to={`/events/${event.id}`}
className="group flex flex-col rounded-xl bg-gray-800/60 border border-gray-700/40 overflow-hidden hover:border-indigo-500/60 hover:bg-gray-800 transition-all"
>
{event.image_url ? (
<img
src={event.image_url}
alt={event.title}
className="h-36 w-full object-cover"
/>
) : (
<div className="h-36 w-full bg-gradient-to-br from-gray-800 to-gray-900 flex items-center justify-center">
<span className="text-4xl opacity-20">🎸</span>
</div>
)}
<div className="flex-1 p-4 flex flex-col gap-2">
{/* Title */}
<h2 className="font-semibold text-sm leading-snug group-hover:text-indigo-300 transition-colors line-clamp-2">
{event.title}
</h2>
{/* Artist — required */}
<p className="text-xs text-indigo-300 font-medium line-clamp-1">
{event.artist ?? "出演者未定"}
</p>
{/* Date + time */}
<div className="flex items-center gap-2 text-xs text-gray-300">
<span>📅 {formattedDate}</span>
{timeLabel && <span className="text-gray-500">| {timeLabel}</span>}
</div>
{/* Venue */}
<div className="flex items-center gap-1 text-xs text-gray-400">
<span>📍</span>
<span className="rounded-full bg-gray-700/60 px-2 py-0.5">
{event.venue_name}
{event.venue_area ? `(${event.venue_area})` : ""}
</span>
</div>
{/* Fee */}
{event.price && (
<p className="text-xs text-emerald-400">¥ {event.price}</p>
)}
{/* Ticket URL */}
{event.ticket_url && (
<a
href={event.ticket_url}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
className="mt-auto inline-flex items-center gap-1 text-xs text-indigo-400 hover:underline"
>
🎟 チケット
</a>
)}
</div>
</Link>
);
}
function formatDate(iso: string): string {
const [y, m, d] = iso.split("-");
const days = ["日", "月", "火", "水", "木", "金", "土"];
const dayIdx = new Date(`${iso}T00:00:00`).getDay();
return `${y}/${m}/${d}(${days[dayIdx]})`;
}
function buildTimeLabel(open: string | null, start: string | null): string {
const parts: string[] = [];
if (open) parts.push(`OPEN ${open}`);
if (start) parts.push(`START ${start}`);
return parts.join(" / ");
}
|