feat: show event location
This commit is contained in:
parent
472347a365
commit
f727ade539
8 changed files with 191 additions and 25 deletions
1
drizzle/0001_magenta_naoko.sql
Normal file
1
drizzle/0001_magenta_naoko.sql
Normal file
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE "eventsTable" ADD COLUMN "location" text;
|
||||
131
drizzle/meta/0001_snapshot.json
Normal file
131
drizzle/meta/0001_snapshot.json
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
{
|
||||
"id": "f3f30a9d-d72d-4e62-86b4-031282707007",
|
||||
"prevId": "c0102a7e-5d09-41ca-88d1-c66f2f299e65",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.eventsTable": {
|
||||
"name": "eventsTable",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"startTime": {
|
||||
"name": "startTime",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"endTime": {
|
||||
"name": "endTime",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"location": {
|
||||
"name": "location",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"lineId": {
|
||||
"name": "lineId",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"eventsTable_lineId_linesTable_id_fk": {
|
||||
"name": "eventsTable_lineId_linesTable_id_fk",
|
||||
"tableFrom": "eventsTable",
|
||||
"tableTo": "linesTable",
|
||||
"columnsFrom": [
|
||||
"lineId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "cascade"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.linesTable": {
|
||||
"name": "linesTable",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,13 @@
|
|||
"when": 1750786732013,
|
||||
"tag": "0000_noisy_omega_red",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1751371464551,
|
||||
"tag": "0001_magenta_naoko",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"push-db": "drizzle-kit push"
|
||||
"db-generate": "drizzle-kit generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electric-sql/pglite": "^0.3.3",
|
||||
|
|
|
|||
|
|
@ -6,14 +6,16 @@ export interface Line {
|
|||
description: string;
|
||||
}
|
||||
|
||||
const userAgentHeader = {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||
};
|
||||
|
||||
export async function getLines() {
|
||||
const annotation = await fetch(
|
||||
"https://amber.festivalfantazie.cz/porady.php",
|
||||
{
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||
},
|
||||
headers: userAgentHeader,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -68,16 +70,14 @@ export interface Event {
|
|||
description: string;
|
||||
type: string;
|
||||
lineId: number;
|
||||
location?: string; // Optional location field
|
||||
}
|
||||
|
||||
export async function getScheduleForLine(lineId: number) {
|
||||
const response = await fetch(
|
||||
`https://amber.festivalfantazie.cz/program_linie.php?linie=${lineId}`,
|
||||
{
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
|
||||
},
|
||||
headers: userAgentHeader,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -162,8 +162,7 @@ export async function getScheduleForLine(lineId: number) {
|
|||
"https://amber.festivalfantazie.cz/ajax/anotace.php",
|
||||
{
|
||||
headers: {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:139.0) Gecko/20100101 Firefox/139.0",
|
||||
...userAgentHeader,
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
referrer: `https://amber.festivalfantazie.cz/program_linie.php?linie=${lineId}`,
|
||||
|
|
@ -182,6 +181,25 @@ export async function getScheduleForLine(lineId: number) {
|
|||
const description$ = cheerio.load(descriptionHtml);
|
||||
const description = description$("description").text().trim();
|
||||
|
||||
const locationRes = await fetch(
|
||||
`https://app.festivalfantazie.cz/program-detail/${eventIdNumber}`,
|
||||
{
|
||||
headers: userAgentHeader,
|
||||
},
|
||||
);
|
||||
|
||||
if (!locationRes.ok) {
|
||||
throw new Error(
|
||||
`Failed to fetch location for event ID ${eventIdNumber}`,
|
||||
);
|
||||
}
|
||||
|
||||
const locationHtml = await locationRes.text();
|
||||
const location$ = cheerio.load(locationHtml);
|
||||
const location = location$("div.row.mb-5 > div.col-6.mb-2:nth-of-type(2)")
|
||||
.text()
|
||||
.trim();
|
||||
|
||||
events.push({
|
||||
id: eventIdNumber,
|
||||
startTime,
|
||||
|
|
@ -191,6 +209,7 @@ export async function getScheduleForLine(lineId: number) {
|
|||
description,
|
||||
type: $(td).find("td.program_typ").text().trim(),
|
||||
lineId,
|
||||
location: location || undefined, // Optional location field
|
||||
});
|
||||
|
||||
currentTime = new Date(endTime);
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@ import type { Event } from "@/common/parser";
|
|||
import { isEventFavorite } from "@/common/utils";
|
||||
import { Button } from "./ui/button";
|
||||
import { toggleFavoriteEvent } from "@/app/actions";
|
||||
import {
|
||||
HeartMinusIcon,
|
||||
HeartPlusIcon,
|
||||
} from "lucide-react";
|
||||
import { HeartMinusIcon, HeartPlusIcon } from "lucide-react";
|
||||
import { getLineById } from "@/db";
|
||||
import { Badge } from "./ui/badge";
|
||||
import Link from "next/link";
|
||||
|
|
@ -24,8 +21,15 @@ export async function EventCard({
|
|||
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-sm w-full">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<h3 className="text-lg font-semibold">{event.title}</h3>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold">{event.title}</h3>
|
||||
{line && (
|
||||
<Badge asChild variant="outline" className="my-2">
|
||||
<Link href={`/line/${line.id}`}>{line.name}</Link>
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<form action={toggleFavoriteEvent}>
|
||||
<input
|
||||
type="number"
|
||||
|
|
@ -34,23 +38,21 @@ export async function EventCard({
|
|||
hidden
|
||||
readOnly
|
||||
/>
|
||||
<Button variant={isFavorite ? "default" : "secondary"} className="cursor-pointer">
|
||||
<Button
|
||||
variant={isFavorite ? "default" : "secondary"}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
{isFavorite ? <HeartMinusIcon /> : <HeartPlusIcon />}
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
{line && (
|
||||
<Badge asChild variant="outline" className='my-2'>
|
||||
<Link href={`/line/${line.id}`}>{line.name}</Link>
|
||||
</Badge>
|
||||
)}
|
||||
<h6 className="text-xs text-gray-600 dark:text-gray-400">{event.name}</h6>
|
||||
<h5 className="text-md text-gray-500 dark:text-gray-300">
|
||||
{showDate
|
||||
? `${event.startTime.toLocaleDateString(["cs-CZ"], {
|
||||
month: "numeric",
|
||||
day: "numeric",
|
||||
weekday: 'short',
|
||||
weekday: "short",
|
||||
})} `
|
||||
: ""}
|
||||
{event.startTime.toLocaleTimeString(["cs-CZ"], {
|
||||
|
|
@ -65,6 +67,11 @@ export async function EventCard({
|
|||
hour12: false,
|
||||
})}
|
||||
</h5>
|
||||
{event.location && (
|
||||
<h6 className="text-xs text-gray-500 dark:text-gray-300">
|
||||
{event.location}
|
||||
</h6>
|
||||
)}
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
{event.description}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { Event } from "@/common/parser";
|
||||
import type { Event } from "@/common/parser";
|
||||
import { EventCard } from "./EventCard";
|
||||
|
||||
export function EventList({
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export const eventsTable = pgTable("eventsTable", {
|
|||
title: text().notNull(),
|
||||
description: text().notNull(),
|
||||
type: text().notNull(),
|
||||
location: text(), // Optional location field
|
||||
lineId: integer()
|
||||
.notNull()
|
||||
.references(() => linesTable.id, {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue