Switch termine loading from static CSV to dynamic API for instant updates
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"date": "2025-10-21T13:58:04.795Z",
|
"date": "2025-10-21T14:19:03.866Z",
|
||||||
"preset": "node-server",
|
"preset": "node-server",
|
||||||
"framework": {
|
"framework": {
|
||||||
"name": "nuxt",
|
"name": "nuxt",
|
||||||
|
|||||||
69
.output/public/_nuxt/B5GgiMm1.js
Normal file
69
.output/public/_nuxt/B5GgiMm1.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
{"id":"e3471523-9d80-4085-b77f-94b960db296e","timestamp":1761055077384}
|
{"id":"8ea2eaf9-1082-435b-8f11-acd12e0664d5","timestamp":1761056336789}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"id":"8ea2eaf9-1082-435b-8f11-acd12e0664d5","timestamp":1761056336789,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"id":"e3471523-9d80-4085-b77f-94b960db296e","timestamp":1761055077384,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
|
|
||||||
1
.output/public/_nuxt/entry.BFpxHKmh.css
Normal file
1
.output/public/_nuxt/entry.BFpxHKmh.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,11 +1,2 @@
|
|||||||
"datum","titel","beschreibung","kategorie"
|
"datum","titel","beschreibung","kategorie"
|
||||||
"2025-10-25","Herbstturnier","Offenes Turnier für alle Leistungsklassen","Turnier"
|
"2025-12-18","Weihnachtsfeier 2025 im Gasthaus Zum Einhorn in Frankfurt - Bonames","","Veranstaltung"
|
||||||
"2025-11-02","Halloween-Special","Spooky Training mit Kostümen und Süßigkeiten","Event"
|
|
||||||
"2025-11-15","Vereinsmeisterschaft","Das Highlight der Saison - Vereinsmeisterschaft in allen Kategorien","Turnier"
|
|
||||||
"2025-12-06","Nikolaus-Turnier","Weihnachtliches Turnier mit kleinen Geschenken","Turnier"
|
|
||||||
"2025-12-20","Weihnachtsfeier","Gemütlicher Jahresabschluss mit Siegerehrung","Event"
|
|
||||||
"2026-01-10","Neujahrstraining","Erstes Training im neuen Jahr","Event"
|
|
||||||
"2026-02-14","Valentinstag-Special","Paar-Turnier für Verliebte","Turnier"
|
|
||||||
"2026-03-15","Frühlingsturnier","Saisoneröffnung mit großem Turnier","Turnier"
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
@@ -340,7 +340,7 @@ const client_manifest = {
|
|||||||
"module": true,
|
"module": true,
|
||||||
"prefetch": true,
|
"prefetch": true,
|
||||||
"preload": true,
|
"preload": true,
|
||||||
"file": "BCCaC8ND.js",
|
"file": "B5GgiMm1.js",
|
||||||
"name": "entry",
|
"name": "entry",
|
||||||
"src": "node_modules/nuxt/dist/app/entry.js",
|
"src": "node_modules/nuxt/dist/app/entry.js",
|
||||||
"isEntry": true,
|
"isEntry": true,
|
||||||
@@ -350,14 +350,14 @@ const client_manifest = {
|
|||||||
"node_modules/nuxt/dist/app/components/error-500.vue"
|
"node_modules/nuxt/dist/app/components/error-500.vue"
|
||||||
],
|
],
|
||||||
"css": [
|
"css": [
|
||||||
"entry.BiMUV0e6.css"
|
"entry.BFpxHKmh.css"
|
||||||
],
|
],
|
||||||
"assets": [
|
"assets": [
|
||||||
"Harheimer TC.CKfYAfp1.svg"
|
"Harheimer TC.CKfYAfp1.svg"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"entry.BiMUV0e6.css": {
|
"entry.BFpxHKmh.css": {
|
||||||
"file": "entry.BiMUV0e6.css",
|
"file": "entry.BFpxHKmh.css",
|
||||||
"resourceType": "style",
|
"resourceType": "style",
|
||||||
"prefetch": true,
|
"prefetch": true,
|
||||||
"preload": true
|
"preload": true
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import process from 'node:process';globalThis._importMeta_=globalThis._importMeta_||{url:"file:///_entry.js",env:process.env};import { defineComponent, shallowRef, h, resolveComponent, hasInjectionContext, inject, computed, getCurrentInstance, createElementBlock, provide, cloneVNode, ref, Suspense, Fragment, createApp, shallowReactive, mergeProps, unref, withCtx, createTextVNode, toRef, onErrorCaptured, onServerPrefetch, createVNode, resolveDynamicComponent, reactive, effectScope, isReadonly, isRef, isShallow, isReactive, toRaw, defineAsyncComponent, getCurrentScope, toDisplayString, useSSRContext } from 'vue';
|
import process from 'node:process';globalThis._importMeta_=globalThis._importMeta_||{url:"file:///_entry.js",env:process.env};import { defineComponent, shallowRef, h, resolveComponent, hasInjectionContext, inject, computed, getCurrentInstance, createElementBlock, provide, cloneVNode, ref, Suspense, Fragment, createApp, shallowReactive, mergeProps, withCtx, createVNode, createTextVNode, unref, toDisplayString, toRef, onErrorCaptured, onServerPrefetch, resolveDynamicComponent, reactive, effectScope, isReadonly, isRef, isShallow, isReactive, toRaw, defineAsyncComponent, getCurrentScope, useSSRContext } from 'vue';
|
||||||
import { p as parseQuery, c as createError$1, o as hasProtocol, q as isScriptProtocol, m as joinURL, w as withQuery, t as sanitizeStatusCode, v as withTrailingSlash, x as withoutTrailingSlash, y as getContext, $ as $fetch$1, z as createHooks, A as executeAsync, B as toRouteMatcher, C as createRouter$1, D as defu } from '../nitro/nitro.mjs';
|
import { p as parseQuery, c as createError$1, o as hasProtocol, q as isScriptProtocol, m as joinURL, w as withQuery, t as sanitizeStatusCode, v as withTrailingSlash, x as withoutTrailingSlash, y as getContext, $ as $fetch$1, z as createHooks, A as executeAsync, B as toRouteMatcher, C as createRouter$1, D as defu } from '../nitro/nitro.mjs';
|
||||||
import { b as baseURL } from '../routes/renderer.mjs';
|
import { b as baseURL } from '../routes/renderer.mjs';
|
||||||
import { defineStore, createPinia, setActivePinia, shouldHydrate } from 'pinia';
|
import { defineStore, createPinia, setActivePinia, shouldHydrate } from 'pinia';
|
||||||
import { RouterView, useRouter as useRouter$1, createMemoryHistory, createRouter, START_LOCATION, useRoute as useRoute$1 } from 'vue-router';
|
import { RouterView, useRoute as useRoute$1, useRouter as useRouter$1, createMemoryHistory, createRouter, START_LOCATION } from 'vue-router';
|
||||||
import { ssrRenderAttrs, ssrInterpolate, ssrRenderComponent, ssrRenderSuspense, ssrRenderVNode, ssrRenderAttr, ssrRenderStyle, ssrRenderClass, ssrRenderList } from 'vue/server-renderer';
|
import { ssrRenderAttrs, ssrRenderComponent, ssrRenderAttr, ssrRenderStyle, ssrRenderClass, ssrRenderList, ssrInterpolate, ssrRenderSuspense, ssrRenderVNode } from 'vue/server-renderer';
|
||||||
import { User, ChevronUp, X, Menu, ChevronDown } from 'lucide-vue-next';
|
import { ChevronDown, X, Menu, User, ChevronUp } from 'lucide-vue-next';
|
||||||
import 'node:http';
|
import 'node:http';
|
||||||
import 'node:https';
|
import 'node:https';
|
||||||
import 'node:events';
|
import 'node:events';
|
||||||
@@ -1414,6 +1414,7 @@ const _sfc_main$4 = {
|
|||||||
const mobileSubmenu = ref(null);
|
const mobileSubmenu = ref(null);
|
||||||
const mannschaften = ref([]);
|
const mannschaften = ref([]);
|
||||||
const hasGalleryImages = ref(false);
|
const hasGalleryImages = ref(false);
|
||||||
|
const showCmsDropdown = ref(false);
|
||||||
const isLoggedIn = computed(() => authStore.isLoggedIn);
|
const isLoggedIn = computed(() => authStore.isLoggedIn);
|
||||||
const isAdmin = computed(() => authStore.isAdmin);
|
const isAdmin = computed(() => authStore.isAdmin);
|
||||||
const currentSubmenu = computed(() => {
|
const currentSubmenu = computed(() => {
|
||||||
@@ -1855,18 +1856,73 @@ const _sfc_main$4 = {
|
|||||||
_: 1
|
_: 1
|
||||||
}, _parent));
|
}, _parent));
|
||||||
if (isAdmin.value) {
|
if (isAdmin.value) {
|
||||||
_push(`<!--[--><div class="h-3 w-px bg-primary-700"></div>`);
|
_push(`<!--[--><div class="h-3 w-px bg-primary-700"></div><div class="relative inline-block"><button class="${ssrRenderClass([unref(route).path.startsWith("/cms") ? "text-white bg-primary-600" : "", "px-2.5 py-1 text-xs text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded transition-all flex items-center"])}"> CMS `);
|
||||||
|
_push(ssrRenderComponent(unref(ChevronDown), {
|
||||||
|
size: 12,
|
||||||
|
class: ["ml-1", ["transition-transform", showCmsDropdown.value ? "rotate-180" : ""]]
|
||||||
|
}, null, _parent));
|
||||||
|
_push(`</button>`);
|
||||||
|
if (showCmsDropdown.value) {
|
||||||
|
_push(`<div class="absolute left-0 top-full mt-1 w-48 bg-gray-800 border border-gray-700 rounded-lg shadow-xl overflow-hidden z-50">`);
|
||||||
_push(ssrRenderComponent(_component_NuxtLink, {
|
_push(ssrRenderComponent(_component_NuxtLink, {
|
||||||
to: "/cms",
|
to: "/cms",
|
||||||
class: "px-2.5 py-1 text-xs text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded transition-all",
|
onClick: ($event) => showCmsDropdown.value = false,
|
||||||
"active-class": "text-white bg-primary-600"
|
class: "block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
|
||||||
}, {
|
}, {
|
||||||
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
||||||
if (_push2) {
|
if (_push2) {
|
||||||
_push2(` CMS `);
|
_push2(` Übersicht `);
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
createTextVNode(" CMS ")
|
createTextVNode(" Übersicht ")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_: 1
|
||||||
|
}, _parent));
|
||||||
|
_push(ssrRenderComponent(_component_NuxtLink, {
|
||||||
|
to: "/mitgliederbereich/news",
|
||||||
|
onClick: ($event) => showCmsDropdown.value = false,
|
||||||
|
class: "block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
|
||||||
|
}, {
|
||||||
|
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
||||||
|
if (_push2) {
|
||||||
|
_push2(` Interne News `);
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
createTextVNode(" Interne News ")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_: 1
|
||||||
|
}, _parent));
|
||||||
|
_push(ssrRenderComponent(_component_NuxtLink, {
|
||||||
|
to: "/cms/termine",
|
||||||
|
onClick: ($event) => showCmsDropdown.value = false,
|
||||||
|
class: "block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
|
||||||
|
}, {
|
||||||
|
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
||||||
|
if (_push2) {
|
||||||
|
_push2(` Termine `);
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
createTextVNode(" Termine ")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_: 1
|
||||||
|
}, _parent));
|
||||||
|
_push(ssrRenderComponent(_component_NuxtLink, {
|
||||||
|
to: "/mitgliederbereich/mitglieder",
|
||||||
|
onClick: ($event) => showCmsDropdown.value = false,
|
||||||
|
class: "block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
|
||||||
|
}, {
|
||||||
|
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
||||||
|
if (_push2) {
|
||||||
|
_push2(` Mitglieder `);
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
createTextVNode(" Mitglieder ")
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -1874,8 +1930,8 @@ const _sfc_main$4 = {
|
|||||||
}, _parent));
|
}, _parent));
|
||||||
_push(ssrRenderComponent(_component_NuxtLink, {
|
_push(ssrRenderComponent(_component_NuxtLink, {
|
||||||
to: "/cms/benutzer",
|
to: "/cms/benutzer",
|
||||||
class: "px-2.5 py-1 text-xs text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded transition-all",
|
onClick: ($event) => showCmsDropdown.value = false,
|
||||||
"active-class": "text-white bg-primary-600"
|
class: "block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors"
|
||||||
}, {
|
}, {
|
||||||
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
||||||
if (_push2) {
|
if (_push2) {
|
||||||
@@ -1888,7 +1944,11 @@ const _sfc_main$4 = {
|
|||||||
}),
|
}),
|
||||||
_: 1
|
_: 1
|
||||||
}, _parent));
|
}, _parent));
|
||||||
_push(`<!--]-->`);
|
_push(`</div>`);
|
||||||
|
} else {
|
||||||
|
_push(`<!---->`);
|
||||||
|
}
|
||||||
|
_push(`</div><!--]-->`);
|
||||||
} else {
|
} else {
|
||||||
_push(`<!---->`);
|
_push(`<!---->`);
|
||||||
}
|
}
|
||||||
@@ -2310,14 +2370,62 @@ const _sfc_main$4 = {
|
|||||||
_push(ssrRenderComponent(_component_NuxtLink, {
|
_push(ssrRenderComponent(_component_NuxtLink, {
|
||||||
to: "/cms",
|
to: "/cms",
|
||||||
onClick: ($event) => isMobileMenuOpen.value = false,
|
onClick: ($event) => isMobileMenuOpen.value = false,
|
||||||
|
class: "block px-4 py-2 text-sm font-semibold text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
|
||||||
|
}, {
|
||||||
|
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
||||||
|
if (_push2) {
|
||||||
|
_push2(` CMS Übersicht `);
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
createTextVNode(" CMS Übersicht ")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_: 1
|
||||||
|
}, _parent));
|
||||||
|
_push(ssrRenderComponent(_component_NuxtLink, {
|
||||||
|
to: "/mitgliederbereich/news",
|
||||||
|
onClick: ($event) => isMobileMenuOpen.value = false,
|
||||||
class: "block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
|
class: "block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
|
||||||
}, {
|
}, {
|
||||||
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
||||||
if (_push2) {
|
if (_push2) {
|
||||||
_push2(` CMS `);
|
_push2(` Interne News `);
|
||||||
} else {
|
} else {
|
||||||
return [
|
return [
|
||||||
createTextVNode(" CMS ")
|
createTextVNode(" Interne News ")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_: 1
|
||||||
|
}, _parent));
|
||||||
|
_push(ssrRenderComponent(_component_NuxtLink, {
|
||||||
|
to: "/cms/termine",
|
||||||
|
onClick: ($event) => isMobileMenuOpen.value = false,
|
||||||
|
class: "block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
|
||||||
|
}, {
|
||||||
|
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
||||||
|
if (_push2) {
|
||||||
|
_push2(` Termine `);
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
createTextVNode(" Termine ")
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
_: 1
|
||||||
|
}, _parent));
|
||||||
|
_push(ssrRenderComponent(_component_NuxtLink, {
|
||||||
|
to: "/mitgliederbereich/mitglieder",
|
||||||
|
onClick: ($event) => isMobileMenuOpen.value = false,
|
||||||
|
class: "block px-4 py-2 text-sm text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors"
|
||||||
|
}, {
|
||||||
|
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
||||||
|
if (_push2) {
|
||||||
|
_push2(` Mitglieder `);
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
createTextVNode(" Mitglieder ")
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -2381,6 +2489,7 @@ _sfc_main$4.setup = (props, ctx) => {
|
|||||||
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/Navigation.vue");
|
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("components/Navigation.vue");
|
||||||
return _sfc_setup$4 ? _sfc_setup$4(props, ctx) : void 0;
|
return _sfc_setup$4 ? _sfc_setup$4(props, ctx) : void 0;
|
||||||
};
|
};
|
||||||
|
const Navigation = Object.assign(_sfc_main$4, { __name: "Navigation" });
|
||||||
const _sfc_main$3 = {
|
const _sfc_main$3 = {
|
||||||
__name: "Footer",
|
__name: "Footer",
|
||||||
__ssrInlineRender: true,
|
__ssrInlineRender: true,
|
||||||
@@ -2511,7 +2620,7 @@ const _sfc_main$2 = {
|
|||||||
return (_ctx, _push, _parent, _attrs) => {
|
return (_ctx, _push, _parent, _attrs) => {
|
||||||
const _component_NuxtPage = __nuxt_component_0;
|
const _component_NuxtPage = __nuxt_component_0;
|
||||||
_push(`<div${ssrRenderAttrs(mergeProps({ class: "h-screen flex flex-col overflow-hidden" }, _attrs))}>`);
|
_push(`<div${ssrRenderAttrs(mergeProps({ class: "h-screen flex flex-col overflow-hidden" }, _attrs))}>`);
|
||||||
_push(ssrRenderComponent(_sfc_main$4, null, null, _parent));
|
_push(ssrRenderComponent(Navigation, null, null, _parent));
|
||||||
_push(`<main class="flex-1 overflow-y-auto pt-20">`);
|
_push(`<main class="flex-1 overflow-y-auto pt-20">`);
|
||||||
_push(ssrRenderComponent(_component_NuxtPage, null, null, _parent));
|
_push(ssrRenderComponent(_component_NuxtPage, null, null, _parent));
|
||||||
_push(`</main>`);
|
_push(`</main>`);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -272,7 +272,7 @@ async function renderInlineStyles(usedModules) {
|
|||||||
|
|
||||||
const renderSSRHeadOptions = {"omitLineBreaks":true};
|
const renderSSRHeadOptions = {"omitLineBreaks":true};
|
||||||
|
|
||||||
const entryFileName = "BCCaC8ND.js";
|
const entryFileName = "B5GgiMm1.js";
|
||||||
|
|
||||||
globalThis.__buildAssetsURL = buildAssetsURL;
|
globalThis.__buildAssetsURL = buildAssetsURL;
|
||||||
globalThis.__publicAssetsURL = publicAssetsURL;
|
globalThis.__publicAssetsURL = publicAssetsURL;
|
||||||
|
|||||||
@@ -87,50 +87,11 @@ const formatMonth = (dateString) => {
|
|||||||
|
|
||||||
const loadTermine = async () => {
|
const loadTermine = async () => {
|
||||||
try {
|
try {
|
||||||
console.log('Lade Termine...')
|
const response = await $fetch('/api/termine')
|
||||||
const response = await fetch('/data/termine.csv')
|
termine.value = response.termine || []
|
||||||
console.log('Response:', response)
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`HTTP error! status: ${response.status}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const csv = await response.text()
|
|
||||||
console.log('CSV Text:', csv)
|
|
||||||
|
|
||||||
// Vereinfachter CSV-Parser
|
|
||||||
const lines = csv.split('\n').filter(line => line.trim() !== '')
|
|
||||||
console.log('CSV Lines:', lines)
|
|
||||||
|
|
||||||
if (lines.length < 2) {
|
|
||||||
console.log('Keine Datenzeilen gefunden')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
termine.value = lines.slice(1).map((line, index) => {
|
|
||||||
// Entferne Anführungszeichen und teile bei Kommas
|
|
||||||
const cleanLine = line.replace(/"/g, '')
|
|
||||||
const values = cleanLine.split(',')
|
|
||||||
|
|
||||||
if (values.length < 4) {
|
|
||||||
console.log(`Zeile ${index + 2} hat zu wenige Werte:`, values)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const termin = {
|
|
||||||
datum: values[0].trim(),
|
|
||||||
titel: values[1].trim(),
|
|
||||||
beschreibung: values[2].trim(),
|
|
||||||
kategorie: values[3].trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Termin ${index + 1}:`, termin)
|
|
||||||
return termin
|
|
||||||
}).filter(termin => termin !== null)
|
|
||||||
|
|
||||||
console.log('Alle geparsten Termine:', termine.value)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden der Termine:', error)
|
console.error('Fehler beim Laden der Termine:', error)
|
||||||
|
termine.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,51 +101,11 @@ const formatFullDate = (dateString) => {
|
|||||||
|
|
||||||
const loadTermine = async () => {
|
const loadTermine = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/data/termine.csv')
|
const response = await $fetch('/api/termine')
|
||||||
if (!response.ok) {
|
termine.value = response.termine || []
|
||||||
throw new Error(`HTTP error! status: ${response.status}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const csv = await response.text()
|
|
||||||
const lines = csv.split('\n').filter(line => line.trim() !== '')
|
|
||||||
|
|
||||||
if (lines.length < 2) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
termine.value = lines.slice(1).map((line, index) => {
|
|
||||||
// Besserer CSV-Parser: Respektiert Anführungszeichen
|
|
||||||
const values = []
|
|
||||||
let current = ''
|
|
||||||
let inQuotes = false
|
|
||||||
|
|
||||||
for (let i = 0; i < line.length; i++) {
|
|
||||||
const char = line[i]
|
|
||||||
|
|
||||||
if (char === '"') {
|
|
||||||
inQuotes = !inQuotes
|
|
||||||
} else if (char === ',' && !inQuotes) {
|
|
||||||
values.push(current.trim())
|
|
||||||
current = ''
|
|
||||||
} else {
|
|
||||||
current += char
|
|
||||||
}
|
|
||||||
}
|
|
||||||
values.push(current.trim())
|
|
||||||
|
|
||||||
if (values.length < 4) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
datum: values[0].trim(),
|
|
||||||
titel: values[1].trim(),
|
|
||||||
beschreibung: values[2].trim(),
|
|
||||||
kategorie: values[3].trim()
|
|
||||||
}
|
|
||||||
}).filter(termin => termin !== null)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Fehler beim Laden der Termine:', error)
|
console.error('Fehler beim Laden der Termine:', error)
|
||||||
|
termine.value = []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,2 @@
|
|||||||
"datum","titel","beschreibung","kategorie"
|
"datum","titel","beschreibung","kategorie"
|
||||||
"2025-10-25","Herbstturnier","Offenes Turnier für alle Leistungsklassen","Turnier"
|
"2025-12-18","Weihnachtsfeier 2025 im Gasthaus Zum Einhorn in Frankfurt - Bonames","","Veranstaltung"
|
||||||
"2025-11-02","Halloween-Special","Spooky Training mit Kostümen und Süßigkeiten","Event"
|
|
||||||
"2025-11-15","Vereinsmeisterschaft","Das Highlight der Saison - Vereinsmeisterschaft in allen Kategorien","Turnier"
|
|
||||||
"2025-12-06","Nikolaus-Turnier","Weihnachtliches Turnier mit kleinen Geschenken","Turnier"
|
|
||||||
"2025-12-20","Weihnachtsfeier","Gemütlicher Jahresabschluss mit Siegerehrung","Event"
|
|
||||||
"2026-01-10","Neujahrstraining","Erstes Training im neuen Jahr","Event"
|
|
||||||
"2026-02-14","Valentinstag-Special","Paar-Turnier für Verliebte","Turnier"
|
|
||||||
"2026-03-15","Frühlingsturnier","Saisoneröffnung mit großem Turnier","Turnier"
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
62
server/api/termine.get.js
Normal file
62
server/api/termine.get.js
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import { promises as fs } from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
try {
|
||||||
|
const cwd = process.cwd()
|
||||||
|
|
||||||
|
// In production (.output/server), working dir is .output
|
||||||
|
let csvPath
|
||||||
|
if (cwd.endsWith('.output')) {
|
||||||
|
csvPath = path.join(cwd, '../public/data/termine.csv')
|
||||||
|
} else {
|
||||||
|
csvPath = path.join(cwd, 'public/data/termine.csv')
|
||||||
|
}
|
||||||
|
|
||||||
|
const csv = await fs.readFile(csvPath, 'utf-8')
|
||||||
|
const lines = csv.split('\n').filter(line => line.trim() !== '')
|
||||||
|
|
||||||
|
if (lines.length < 2) {
|
||||||
|
return { success: true, termine: [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
const termine = []
|
||||||
|
for (let i = 1; i < lines.length; i++) {
|
||||||
|
const values = []
|
||||||
|
let current = ''
|
||||||
|
let inQuotes = false
|
||||||
|
|
||||||
|
for (let j = 0; j < lines[i].length; j++) {
|
||||||
|
const char = lines[i][j]
|
||||||
|
|
||||||
|
if (char === '"') {
|
||||||
|
inQuotes = !inQuotes
|
||||||
|
} else if (char === ',' && !inQuotes) {
|
||||||
|
values.push(current.trim())
|
||||||
|
current = ''
|
||||||
|
} else {
|
||||||
|
current += char
|
||||||
|
}
|
||||||
|
}
|
||||||
|
values.push(current.trim())
|
||||||
|
|
||||||
|
if (values.length >= 4) {
|
||||||
|
termine.push({
|
||||||
|
datum: values[0],
|
||||||
|
titel: values[1],
|
||||||
|
beschreibung: values[2],
|
||||||
|
kategorie: values[3]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
termine
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der Termine:', error)
|
||||||
|
return { success: true, termine: [] }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
Reference in New Issue
Block a user