From 130be14c59da43ac6706d973c13e9728ae53b0fc Mon Sep 17 00:00:00 2001 From: Zsolt Tasnadi Date: Tue, 5 May 2026 19:49:24 +0200 Subject: [PATCH] events api --- apps/api/app/admin/events.rb | 35 +++++++++++ .../app/controllers/api/events_controller.rb | 8 +++ apps/api/app/models/event.rb | 9 +++ apps/api/config/routes.rb | 1 + .../migrate/20260505000005_create_events.rb | 15 +++++ apps/frontend/src/pages/index.astro | 60 ++++++------------- 6 files changed, 86 insertions(+), 42 deletions(-) create mode 100644 apps/api/app/admin/events.rb create mode 100644 apps/api/app/controllers/api/events_controller.rb create mode 100644 apps/api/app/models/event.rb create mode 100644 apps/api/db/migrate/20260505000005_create_events.rb diff --git a/apps/api/app/admin/events.rb b/apps/api/app/admin/events.rb new file mode 100644 index 0000000..2fb89c0 --- /dev/null +++ b/apps/api/app/admin/events.rb @@ -0,0 +1,35 @@ +ActiveAdmin.register Event do + permit_params :name, :date + + menu priority: 3 + + index do + selectable_column + id_column + column :name + column :date + column :created_at + actions + end + + filter :name + filter :date + + show do + attributes_table do + row :id + row :name + row :date + row :created_at + row :updated_at + end + end + + form do |f| + f.inputs do + f.input :name + f.input :date, as: :date_time_picker + end + f.actions + end +end diff --git a/apps/api/app/controllers/api/events_controller.rb b/apps/api/app/controllers/api/events_controller.rb new file mode 100644 index 0000000..703fe8a --- /dev/null +++ b/apps/api/app/controllers/api/events_controller.rb @@ -0,0 +1,8 @@ +class Api::EventsController < ApiController + def index + events = Event.upcoming.map do |e| + { name: e.name, date: e.date.utc.strftime("%Y-%m-%dT%H:%M:%SZ") } + end + render json: events + end +end diff --git a/apps/api/app/models/event.rb b/apps/api/app/models/event.rb new file mode 100644 index 0000000..4da5432 --- /dev/null +++ b/apps/api/app/models/event.rb @@ -0,0 +1,9 @@ +class Event < ApplicationRecord + default_scope { where(deleted_at: nil) } + + scope :upcoming, -> { where("date > ?", Time.current).order(:date) } + + def self.ransackable_attributes(auth_object = nil) + %w[id name date created_at updated_at] + end +end diff --git a/apps/api/config/routes.rb b/apps/api/config/routes.rb index 3496e5f..51dada1 100644 --- a/apps/api/config/routes.rb +++ b/apps/api/config/routes.rb @@ -5,6 +5,7 @@ Rails.application.routes.draw do namespace :api do get "software", to: "software#index" get "software/highlighted", to: "software_highlighted#index" + get "events", to: "events#index" end get "update", to: "update#update" diff --git a/apps/api/db/migrate/20260505000005_create_events.rb b/apps/api/db/migrate/20260505000005_create_events.rb new file mode 100644 index 0000000..852bdc8 --- /dev/null +++ b/apps/api/db/migrate/20260505000005_create_events.rb @@ -0,0 +1,15 @@ +class CreateEvents < ActiveRecord::Migration[8.1] + def change + create_table :events, id: { type: :bigint, unsigned: true }, + charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", + if_not_exists: true do |t| + t.string :name, null: false + t.datetime :date, null: false + t.datetime :deleted_at, precision: 3 + t.timestamps precision: 3 + + t.index :deleted_at, name: "idx_events_deleted_at" + t.index :date, name: "idx_events_date" + end + end +end diff --git a/apps/frontend/src/pages/index.astro b/apps/frontend/src/pages/index.astro index f008109..b36d0ba 100644 --- a/apps/frontend/src/pages/index.astro +++ b/apps/frontend/src/pages/index.astro @@ -1,58 +1,35 @@ --- import Layout from '../layouts/Layout.astro'; import { formatDateTime } from '../utils/dateFormat'; -import fs from 'node:fs'; -import path from 'node:path'; -import yaml from 'js-yaml'; interface Event { name: string; date: string; } -interface EventData { - events: Event[]; -} +const BASE = "https://games.teletype.hu"; let nextEvent: (Event & { dateISO: string; dateText: string }) | null = null; let followingEvents: (Event & { dateText: string })[] = []; try { - const rootDir = process.cwd(); - let eventsPath = path.join(rootDir, 'src', 'events.yaml'); - if (!fs.existsSync(eventsPath)) { - eventsPath = path.join(rootDir, 'src', 'events-example.yaml'); - } - - if (fs.existsSync(eventsPath)) { - const fileContents = fs.readFileSync(eventsPath, 'utf8'); - const data = yaml.load(fileContents) as EventData; - - if (data && data.events) { - const now = new Date(); - const futureEvents = data.events - .map(e => ({ - ...e, - dateObj: new Date(e.date) - })) - .filter(e => e.dateObj > now) - .sort((a, b) => a.dateObj.getTime() - b.dateObj.getTime()); - - if (futureEvents.length > 0) { - const first = futureEvents[0]; - nextEvent = { - name: first.name, - date: first.date, - dateISO: first.dateObj.toISOString(), - dateText: formatDateTime(first.dateObj) - }; - - followingEvents = futureEvents.slice(1).map(e => ({ - name: e.name, - date: e.date, - dateText: formatDateTime(e.dateObj) - })); - } + const res = await fetch(BASE + "/api/events"); + if (res.ok) { + const events: Event[] = await res.json(); + if (events.length > 0) { + const first = events[0]; + const firstDate = new Date(first.date); + nextEvent = { + name: first.name, + date: first.date, + dateISO: firstDate.toISOString(), + dateText: formatDateTime(firstDate) + }; + followingEvents = events.slice(1).map(e => ({ + name: e.name, + date: e.date, + dateText: formatDateTime(new Date(e.date)) + })); } } } catch (e) { @@ -60,7 +37,6 @@ try { } // Fetch highlighted software -const BASE = "https://games.teletype.hu"; const API_HIGHLIGHTED = BASE + "/api/software/highlighted"; let highlightedSoftware = null; let highlightedStableRelease = null;