Prepare upload package and DB sample config
11
Db+Conf/README.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Db+Conf Inhalt
|
||||
|
||||
- schema.sql: in MySQL/MariaDB importieren
|
||||
- config.sample.php: Beispiel fuer api/config.php mit Platzhaltern
|
||||
|
||||
Empfohlener Ablauf:
|
||||
1. Datenbank und User beim Hoster anlegen
|
||||
2. schema.sql importieren
|
||||
3. config.sample.php nach api/config.php auf dem Zielhost kopieren
|
||||
4. Zugangsdaten und Kalender-Passwoerter anpassen
|
||||
5. Anwendung testen
|
||||
10
Db+Conf/config.sample.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
const DB_HOST = 'localhost';
|
||||
const DB_PORT = 3306;
|
||||
const DB_NAME = 'deine_datenbank';
|
||||
const DB_USER = 'dein_user';
|
||||
const DB_PASSWORD = 'dein_passwort';
|
||||
const SESSION_COOKIE_NAME = 'esv_bludenz_calendar_session';
|
||||
24
Db+Conf/schema.sql
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
CREATE TABLE IF NOT EXISTS calendars (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
slug VARCHAR(100) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
calendar_id INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT NULL,
|
||||
location VARCHAR(255) NULL,
|
||||
start_at DATETIME NOT NULL,
|
||||
end_at DATETIME NOT NULL,
|
||||
color VARCHAR(20) DEFAULT '#1a73e8',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_events_calendar FOREIGN KEY (calendar_id) REFERENCES calendars(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
INSERT IGNORE INTO calendars (slug, name, description) VALUES
|
||||
('dittes', 'Buchungskalender Dittes Hütte', ''),
|
||||
('kegeln', 'Buchungskalender ESV-Bludenz Sektion Kegeln', '');
|
||||
11
Upload/README_UPLOAD.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Upload-Inhalt für den Zielhost
|
||||
|
||||
Hochladen:
|
||||
- gesamter Inhalt dieses Upload-Ordners in das Webroot / Zielverzeichnis
|
||||
- api/config.sample.php nach api/config.php kopieren und echte Zugangsdaten eintragen
|
||||
- schema.sql einmal manuell in MySQL/MariaDB importieren
|
||||
|
||||
Wichtig:
|
||||
- Die Datenbank wird aktuell NICHT automatisch beim ersten Start befüllt.
|
||||
- Voraussetzung ist, dass die Tabellen aus api/schema.sql bereits existieren.
|
||||
- Danach funktioniert die Anwendung, sofern PHP, PDO MySQL und Sessions beim Hoster verfügbar sind.
|
||||
10
Upload/api/config.example.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
const DB_HOST = 'localhost';
|
||||
const DB_PORT = 3306;
|
||||
const DB_NAME = 'deine_datenbank';
|
||||
const DB_USER = 'dein_user';
|
||||
const DB_PASSWORD = 'dein_passwort';
|
||||
const SESSION_COOKIE_NAME = 'esv_bludenz_calendar_session';
|
||||
10
Upload/api/config.sample.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
const DB_HOST = 'localhost';
|
||||
const DB_PORT = 3306;
|
||||
const DB_NAME = 'deine_datenbank';
|
||||
const DB_USER = 'dein_user';
|
||||
const DB_PASSWORD = 'dein_passwort';
|
||||
const SESSION_COOKIE_NAME = 'esv_bludenz_calendar_session';
|
||||
68
Upload/api/db.php
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
function calendar_db(): PDO
|
||||
{
|
||||
static $pdo = null;
|
||||
|
||||
if ($pdo instanceof PDO) {
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
assert_db_config_present();
|
||||
|
||||
$dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4', db_host(), db_port(), db_name());
|
||||
$pdo = new PDO($dsn, db_user(), db_password(), [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]);
|
||||
|
||||
initialize_calendar_db($pdo);
|
||||
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
function initialize_calendar_db(PDO $pdo): void
|
||||
{
|
||||
$pdo->exec('CREATE TABLE IF NOT EXISTS calendars (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
slug VARCHAR(100) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
|
||||
|
||||
$pdo->exec('CREATE TABLE IF NOT EXISTS events (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
calendar_id INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT NULL,
|
||||
location VARCHAR(255) NULL,
|
||||
start_at DATETIME NOT NULL,
|
||||
end_at DATETIME NOT NULL,
|
||||
color VARCHAR(20) DEFAULT "#1a73e8",
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_events_calendar FOREIGN KEY (calendar_id) REFERENCES calendars(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
|
||||
|
||||
$seedCalendars = [
|
||||
['slug' => 'dittes', 'name' => 'Buchungskalender Dittes Hütte', 'description' => ''],
|
||||
['slug' => 'kegeln', 'name' => 'Buchungskalender ESV-Bludenz Sektion Kegeln', 'description' => ''],
|
||||
];
|
||||
|
||||
$insertCalendar = $pdo->prepare('INSERT IGNORE INTO calendars (slug, name, description) VALUES (:slug, :name, :description)');
|
||||
foreach ($seedCalendars as $calendar) {
|
||||
$insertCalendar->execute($calendar);
|
||||
}
|
||||
}
|
||||
|
||||
function find_calendar_by_slug(PDO $pdo, string $slug): ?array
|
||||
{
|
||||
$stmt = $pdo->prepare('SELECT * FROM calendars WHERE slug = :slug LIMIT 1');
|
||||
$stmt->execute(['slug' => $slug]);
|
||||
$row = $stmt->fetch();
|
||||
return $row ?: null;
|
||||
}
|
||||
24
Upload/api/delete-event.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/helpers.php';
|
||||
|
||||
$body = read_json_body();
|
||||
$id = (int)($body['id'] ?? ($_GET['id'] ?? 0));
|
||||
if ($id <= 0) {
|
||||
json_response(['error' => 'Termin fehlt'], 400);
|
||||
}
|
||||
|
||||
$pdo = calendar_db();
|
||||
$existing = find_event_by_id($pdo, $id);
|
||||
if (!$existing) {
|
||||
json_response(['error' => 'Termin nicht gefunden'], 404);
|
||||
}
|
||||
|
||||
require_admin_for_slug($existing['calendar_slug']);
|
||||
|
||||
$stmt = $pdo->prepare('DELETE FROM events WHERE id = :id');
|
||||
$stmt->execute(['id' => $id]);
|
||||
|
||||
json_response(['ok' => true]);
|
||||
34
Upload/api/events.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
$pdo = calendar_db();
|
||||
$slug = $_GET['calendar'] ?? '';
|
||||
|
||||
if ($slug === '') {
|
||||
json_response(['error' => 'Kalender fehlt'], 400);
|
||||
}
|
||||
|
||||
$calendar = find_calendar_by_slug($pdo, $slug);
|
||||
if (!$calendar) {
|
||||
json_response(['error' => 'Kalender nicht gefunden'], 404);
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('SELECT id, title, description, location, start_at AS start, end_at AS end, color
|
||||
FROM events
|
||||
WHERE calendar_id = :calendar_id
|
||||
ORDER BY start_at ASC');
|
||||
$stmt->execute(['calendar_id' => $calendar['id']]);
|
||||
$events = $stmt->fetchAll();
|
||||
|
||||
json_response([
|
||||
'calendar' => [
|
||||
'slug' => $calendar['slug'],
|
||||
'name' => $calendar['name'],
|
||||
'description' => $calendar['description'],
|
||||
],
|
||||
'events' => $events,
|
||||
'isAdmin' => current_admin_calendar_slug() === $calendar['slug'],
|
||||
]);
|
||||
37
Upload/api/helpers.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
function validate_event_payload(array $body): ?string
|
||||
{
|
||||
$title = trim((string)($body['title'] ?? ''));
|
||||
$start = (string)($body['start'] ?? '');
|
||||
$end = (string)($body['end'] ?? '');
|
||||
|
||||
if ($title === '') return 'Titel fehlt';
|
||||
if ($start === '' || $end === '') return 'Start oder Ende fehlt';
|
||||
|
||||
$startTs = strtotime($start);
|
||||
$endTs = strtotime($end);
|
||||
if ($startTs === false || $endTs === false) return 'Ungültiges Datum';
|
||||
if ($endTs < $startTs) return 'Ende liegt vor dem Start';
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function require_admin_for_slug(string $slug): void
|
||||
{
|
||||
if (current_admin_calendar_slug() !== $slug) {
|
||||
json_response(['error' => 'Nicht eingeloggt'], 401);
|
||||
}
|
||||
}
|
||||
|
||||
function find_event_by_id(PDO $pdo, int $id): ?array
|
||||
{
|
||||
$stmt = $pdo->prepare('SELECT events.*, calendars.slug AS calendar_slug FROM events JOIN calendars ON calendars.id = events.calendar_id WHERE events.id = :id LIMIT 1');
|
||||
$stmt->execute(['id' => $id]);
|
||||
$row = $stmt->fetch();
|
||||
return $row ?: null;
|
||||
}
|
||||
23
Upload/api/login.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/db.php';
|
||||
|
||||
$body = read_json_body();
|
||||
$calendarSlug = (string)($body['calendarSlug'] ?? '');
|
||||
$password = (string)($body['password'] ?? '');
|
||||
|
||||
$passwords = calendar_passwords();
|
||||
if (!isset($passwords[$calendarSlug])) {
|
||||
json_response(['error' => 'Unbekannter Kalender'], 400);
|
||||
}
|
||||
|
||||
if ($passwords[$calendarSlug] !== $password) {
|
||||
json_response(['error' => 'Passwort falsch'], 401);
|
||||
}
|
||||
|
||||
start_calendar_session();
|
||||
$_SESSION['admin_calendar_slug'] = $calendarSlug;
|
||||
|
||||
json_response(['ok' => true]);
|
||||
10
Upload/api/logout.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/config.php';
|
||||
|
||||
start_calendar_session();
|
||||
session_destroy();
|
||||
|
||||
json_response(['ok' => true]);
|
||||
48
Upload/api/save-event.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/helpers.php';
|
||||
|
||||
$body = read_json_body();
|
||||
$calendarSlug = (string)($body['calendarSlug'] ?? '');
|
||||
|
||||
if ($calendarSlug === '') {
|
||||
json_response(['error' => 'Kalender fehlt'], 400);
|
||||
}
|
||||
|
||||
require_admin_for_slug($calendarSlug);
|
||||
|
||||
$error = validate_event_payload($body);
|
||||
if ($error) {
|
||||
json_response(['error' => $error], 400);
|
||||
}
|
||||
|
||||
$pdo = calendar_db();
|
||||
$calendar = find_calendar_by_slug($pdo, $calendarSlug);
|
||||
if (!$calendar) {
|
||||
json_response(['error' => 'Kalender nicht gefunden'], 404);
|
||||
}
|
||||
|
||||
$stmt = $pdo->prepare('INSERT INTO events (calendar_id, title, description, location, start_at, end_at, color) VALUES (:calendar_id, :title, :description, :location, :start_at, :end_at, :color)');
|
||||
$stmt->execute([
|
||||
'calendar_id' => $calendar['id'],
|
||||
'title' => trim((string)$body['title']),
|
||||
'description' => (string)($body['description'] ?? ''),
|
||||
'location' => (string)($body['location'] ?? ''),
|
||||
'start_at' => (string)$body['start'],
|
||||
'end_at' => (string)$body['end'],
|
||||
'color' => (string)($body['color'] ?? '#1a73e8'),
|
||||
]);
|
||||
|
||||
$id = (int)$pdo->lastInsertId();
|
||||
$created = find_event_by_id($pdo, $id);
|
||||
json_response([
|
||||
'id' => $created['id'],
|
||||
'title' => $created['title'],
|
||||
'description' => $created['description'],
|
||||
'location' => $created['location'],
|
||||
'start' => $created['start_at'],
|
||||
'end' => $created['end_at'],
|
||||
'color' => $created['color'],
|
||||
], 201);
|
||||
24
Upload/api/schema.sql
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
CREATE TABLE IF NOT EXISTS calendars (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
slug VARCHAR(100) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
calendar_id INT NOT NULL,
|
||||
title VARCHAR(255) NOT NULL,
|
||||
description TEXT NULL,
|
||||
location VARCHAR(255) NULL,
|
||||
start_at DATETIME NOT NULL,
|
||||
end_at DATETIME NOT NULL,
|
||||
color VARCHAR(20) DEFAULT '#1a73e8',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_events_calendar FOREIGN KEY (calendar_id) REFERENCES calendars(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
INSERT IGNORE INTO calendars (slug, name, description) VALUES
|
||||
('dittes', 'Buchungskalender Dittes Hütte', ''),
|
||||
('kegeln', 'Buchungskalender ESV-Bludenz Sektion Kegeln', '');
|
||||
46
Upload/api/update-event.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/helpers.php';
|
||||
|
||||
$body = read_json_body();
|
||||
$id = (int)($body['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
json_response(['error' => 'Termin fehlt'], 400);
|
||||
}
|
||||
|
||||
$error = validate_event_payload($body);
|
||||
if ($error) {
|
||||
json_response(['error' => $error], 400);
|
||||
}
|
||||
|
||||
$pdo = calendar_db();
|
||||
$existing = find_event_by_id($pdo, $id);
|
||||
if (!$existing) {
|
||||
json_response(['error' => 'Termin nicht gefunden'], 404);
|
||||
}
|
||||
|
||||
require_admin_for_slug($existing['calendar_slug']);
|
||||
|
||||
$stmt = $pdo->prepare('UPDATE events SET title = :title, description = :description, location = :location, start_at = :start_at, end_at = :end_at, color = :color, updated_at = CURRENT_TIMESTAMP WHERE id = :id');
|
||||
$stmt->execute([
|
||||
'id' => $id,
|
||||
'title' => trim((string)$body['title']),
|
||||
'description' => (string)($body['description'] ?? ''),
|
||||
'location' => (string)($body['location'] ?? ''),
|
||||
'start_at' => (string)$body['start'],
|
||||
'end_at' => (string)$body['end'],
|
||||
'color' => (string)($body['color'] ?? '#1a73e8'),
|
||||
]);
|
||||
|
||||
$updated = find_event_by_id($pdo, $id);
|
||||
json_response([
|
||||
'id' => $updated['id'],
|
||||
'title' => $updated['title'],
|
||||
'description' => $updated['description'],
|
||||
'location' => $updated['location'],
|
||||
'start' => $updated['start_at'],
|
||||
'end' => $updated['end_at'],
|
||||
'color' => $updated['color'],
|
||||
]);
|
||||
13
Upload/asset-manifest.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.229d58bf.css",
|
||||
"main.js": "./static/js/main.f77b3dd0.js",
|
||||
"index.html": "./index.html",
|
||||
"main.229d58bf.css.map": "./static/css/main.229d58bf.css.map",
|
||||
"main.f77b3dd0.js.map": "./static/js/main.f77b3dd0.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.229d58bf.css",
|
||||
"static/js/main.f77b3dd0.js"
|
||||
]
|
||||
}
|
||||
BIN
Upload/favicon.ico
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
Upload/images/askoe.jpg
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
Upload/images/logo_dittes.png
Normal file
|
After Width: | Height: | Size: 737 KiB |
BIN
Upload/images/oees.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
Upload/images/sektion_bergwandern.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
Upload/images/sektion_foto.png
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
Upload/images/sektion_kegeln.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
Upload/images/sektion_schuetzen.png
Normal file
|
After Width: | Height: | Size: 4 KiB |
1
Upload/images/sektion_schuetzen.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' width='27' height='32' viewBox='0 0 27 32' fill='rgb(255, 255, 255)'><title>screenshot</title><path d='M21.376 18.272h-1.952q-0.448 0-0.8-0.32t-0.352-0.8v-2.304q0-0.448 0.352-0.8t0.8-0.32h1.952q-0.576-1.952-2.016-3.392t-3.36-1.984v1.92q0 0.48-0.352 0.832t-0.8 0.32h-2.272q-0.48 0-0.8-0.32t-0.352-0.832v-1.92q-1.92 0.544-3.36 1.984t-2.016 3.392h1.952q0.48 0 0.8 0.32t0.352 0.8v2.304q0 0.448-0.352 0.8t-0.8 0.352h-1.952q0.576 1.92 2.016 3.36t3.36 1.984v-1.92q0-0.48 0.352-0.8t0.8-0.352h2.272q0.48 0 0.8 0.352t0.352 0.8v1.92q1.92-0.544 3.36-1.984t2.016-3.36zM27.424 14.848v2.304q0 0.448-0.32 0.8t-0.832 0.32h-2.528q-0.672 2.88-2.784 4.992t-4.96 2.752v2.56q0 0.448-0.352 0.8t-0.8 0.352h-2.272q-0.48 0-0.8-0.352t-0.352-0.8v-2.56q-2.88-0.672-4.96-2.752t-2.752-4.992h-2.56q-0.48 0-0.8-0.32t-0.352-0.8v-2.304q0-0.448 0.352-0.8t0.8-0.32h2.56q0.64-2.88 2.752-4.992t4.96-2.752v-2.56q0-0.448 0.352-0.8t0.8-0.352h2.272q0.48 0 0.8 0.352t0.352 0.8v2.56q2.88 0.672 4.96 2.752t2.784 4.992h2.528q0.48 0 0.832 0.32t0.32 0.8z'/></svg>
|
||||
|
After Width: | Height: | Size: 1 KiB |
1
Upload/index.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="./logo192.png"/><link rel="manifest" href="./manifest.json"/><title>ESV-Bludenz</title><script defer="defer" src="./static/js/main.f77b3dd0.js"></script><link href="./static/css/main.229d58bf.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
25
Upload/manifest.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
Upload/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
6
Upload/static/css/main.229d58bf.css
Normal file
1
Upload/static/css/main.229d58bf.css.map
Normal file
3
Upload/static/js/main.f77b3dd0.js
Normal file
55
Upload/static/js/main.f77b3dd0.js.LICENSE.txt
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*!
|
||||
Copyright (c) 2018 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/classnames
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom-client.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-jsx-runtime.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* scheduler.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
1
Upload/static/js/main.f77b3dd0.js.map
Normal file
8
build/.htaccess
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.html$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.html [L]
|
||||
</IfModule>
|
||||
13
build/asset-manifest.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.229d58bf.css",
|
||||
"main.js": "./static/js/main.f77b3dd0.js",
|
||||
"index.html": "./index.html",
|
||||
"main.229d58bf.css.map": "./static/css/main.229d58bf.css.map",
|
||||
"main.f77b3dd0.js.map": "./static/js/main.f77b3dd0.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.229d58bf.css",
|
||||
"static/js/main.f77b3dd0.js"
|
||||
]
|
||||
}
|
||||
BIN
build/favicon.ico
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
build/images/askoe.jpg
Normal file
|
After Width: | Height: | Size: 113 KiB |
BIN
build/images/logo_dittes.png
Normal file
|
After Width: | Height: | Size: 737 KiB |
BIN
build/images/oees.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
build/images/sektion_bergwandern.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
build/images/sektion_foto.png
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
build/images/sektion_kegeln.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
build/images/sektion_schuetzen.png
Normal file
|
After Width: | Height: | Size: 4 KiB |
1
build/images/sektion_schuetzen.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' width='27' height='32' viewBox='0 0 27 32' fill='rgb(255, 255, 255)'><title>screenshot</title><path d='M21.376 18.272h-1.952q-0.448 0-0.8-0.32t-0.352-0.8v-2.304q0-0.448 0.352-0.8t0.8-0.32h1.952q-0.576-1.952-2.016-3.392t-3.36-1.984v1.92q0 0.48-0.352 0.832t-0.8 0.32h-2.272q-0.48 0-0.8-0.32t-0.352-0.832v-1.92q-1.92 0.544-3.36 1.984t-2.016 3.392h1.952q0.48 0 0.8 0.32t0.352 0.8v2.304q0 0.448-0.352 0.8t-0.8 0.352h-1.952q0.576 1.92 2.016 3.36t3.36 1.984v-1.92q0-0.48 0.352-0.8t0.8-0.352h2.272q0.48 0 0.8 0.352t0.352 0.8v1.92q1.92-0.544 3.36-1.984t2.016-3.36zM27.424 14.848v2.304q0 0.448-0.32 0.8t-0.832 0.32h-2.528q-0.672 2.88-2.784 4.992t-4.96 2.752v2.56q0 0.448-0.352 0.8t-0.8 0.352h-2.272q-0.48 0-0.8-0.352t-0.352-0.8v-2.56q-2.88-0.672-4.96-2.752t-2.752-4.992h-2.56q-0.48 0-0.8-0.32t-0.352-0.8v-2.304q0-0.448 0.352-0.8t0.8-0.32h2.56q0.64-2.88 2.752-4.992t4.96-2.752v-2.56q0-0.448 0.352-0.8t0.8-0.352h2.272q0.48 0 0.8 0.352t0.352 0.8v2.56q2.88 0.672 4.96 2.752t2.784 4.992h2.528q0.48 0 0.832 0.32t0.32 0.8z'/></svg>
|
||||
|
After Width: | Height: | Size: 1 KiB |
1
build/index.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="./logo192.png"/><link rel="manifest" href="./manifest.json"/><title>ESV-Bludenz</title><script defer="defer" src="./static/js/main.f77b3dd0.js"></script><link href="./static/css/main.229d58bf.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
25
build/manifest.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
build/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
6
build/static/css/main.229d58bf.css
Normal file
1
build/static/css/main.229d58bf.css.map
Normal file
3
build/static/js/main.f77b3dd0.js
Normal file
55
build/static/js/main.f77b3dd0.js.LICENSE.txt
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*!
|
||||
Copyright (c) 2018 Jed Watson.
|
||||
Licensed under the MIT License (MIT), see
|
||||
http://jedwatson.github.io/classnames
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom-client.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-jsx-runtime.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* scheduler.production.js
|
||||
*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
1
build/static/js/main.f77b3dd0.js.map
Normal file
27
router.php
|
|
@ -2,7 +2,32 @@
|
|||
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
$root = __DIR__;
|
||||
$public = $root . '/public';
|
||||
$build = $root . '/build';
|
||||
|
||||
$staticBase = $build;
|
||||
if ($path !== '/' && file_exists($staticBase . $path) && !is_dir($staticBase . $path)) {
|
||||
$file = $staticBase . $path;
|
||||
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
||||
$types = [
|
||||
'js' => 'application/javascript',
|
||||
'css' => 'text/css',
|
||||
'map' => 'application/json',
|
||||
'json' => 'application/json',
|
||||
'png' => 'image/png',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'svg' => 'image/svg+xml',
|
||||
'ico' => 'image/x-icon',
|
||||
'txt' => 'text/plain',
|
||||
'webmanifest' => 'application/manifest+json',
|
||||
];
|
||||
if (isset($types[$ext])) {
|
||||
header('Content-Type: ' . $types[$ext]);
|
||||
}
|
||||
readfile($file);
|
||||
return true;
|
||||
}
|
||||
if ($path !== '/' && file_exists($public . $path) && !is_dir($public . $path)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -18,4 +43,4 @@ if (str_starts_with($path, '/api/')) {
|
|||
return true;
|
||||
}
|
||||
|
||||
require $public . '/index.html';
|
||||
require $build . '/index.html';
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ import { Modal } from "react-bootstrap";
|
|||
import { Link } from "react-router-dom";
|
||||
import "./Dittes.css";
|
||||
|
||||
const configuredApiBase = process.env.REACT_APP_API_BASE;
|
||||
const API_BASE =
|
||||
process.env.REACT_APP_API_BASE ||
|
||||
`${window.location.origin}/api`;
|
||||
configuredApiBase && !configuredApiBase.includes("localhost:3002")
|
||||
? configuredApiBase
|
||||
: `${window.location.origin}/api`;
|
||||
|
||||
function formatDateTime(value) {
|
||||
if (!value) return "";
|
||||
|
|
@ -141,15 +143,31 @@ function Dittes({
|
|||
const response = await fetch(`${API_BASE}/events.php?calendar=${encodeURIComponent(calendarSlug)}`, {
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`API Fehler ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setEvents(data.events || []);
|
||||
const normalizedEvents = Array.isArray(data.events)
|
||||
? data.events.map((entry) => ({
|
||||
...entry,
|
||||
start: entry.start ? entry.start.replace(" ", "T") : entry.start,
|
||||
end: entry.end ? entry.end.replace(" ", "T") : entry.end,
|
||||
}))
|
||||
: [];
|
||||
|
||||
setEvents(normalizedEvents);
|
||||
setCalendarInfo(data.calendar || { name: fallbackName, description: "" });
|
||||
setAdminMode(Boolean(data.isAdmin));
|
||||
setStatus("");
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadEvents().catch(() => setStatus("Kalender konnte nicht geladen werden. Läuft das Backend schon?"));
|
||||
loadEvents().catch((error) => {
|
||||
console.error("Kalender laden fehlgeschlagen", error);
|
||||
setStatus(`Kalender konnte nicht geladen werden: ${error.message}`);
|
||||
});
|
||||
}, [calendarSlug, fallbackName]);
|
||||
|
||||
function changeMonth(direction) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
export const esvSections = [
|
||||
{
|
||||
name: "ESV Bludenz - Sektion Kegeln",
|
||||
link: "/kegeln",
|
||||
link: "https://esv-bludenz-kegeln.at/",
|
||||
icon: "images/sektion_kegeln.png",
|
||||
},
|
||||
{
|
||||
|
|
|
|||