Add CMS termine editor for admin and vorstand

This commit is contained in:
Torsten Schulz (local)
2025-10-21 15:57:42 +02:00
parent 201de0a278
commit 1cbfbaf754
15 changed files with 779 additions and 229 deletions

View File

@@ -1,5 +1,5 @@
{
"date": "2025-10-21T13:50:44.482Z",
"date": "2025-10-21T13:54:21.835Z",
"preset": "node-server",
"framework": {
"name": "nuxt",

View File

@@ -1 +1 @@
{"id":"88cab71a-6f5e-48fa-ae20-fc874da8cdac","timestamp":1761054637518}
{"id":"3a0403e7-f742-4a46-8731-aef781f88d6b","timestamp":1761054854878}

View File

@@ -0,0 +1 @@
{"id":"3a0403e7-f742-4a46-8731-aef781f88d6b","timestamp":1761054854878,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}

View File

@@ -1 +0,0 @@
{"id":"88cab71a-6f5e-48fa-ae20-fc874da8cdac","timestamp":1761054637518,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}

View File

@@ -1,13 +1,13 @@
const interopDefault = r => r.default || r || [];
const styles = {
"components/Hero.vue": () => import('./Hero-styles.03iOjY05.mjs').then(interopDefault),
"components/PublicNews.vue": () => import('./PublicNews-styles.nhqk16g5.mjs').then(interopDefault),
"node_modules/nuxt/dist/app/components/error-404.vue": () => import('./error-404-styles.BsF5Lbhq.mjs').then(interopDefault),
"node_modules/nuxt/dist/app/components/error-500.vue": () => import('./error-500-styles.Dccc6iq5.mjs').then(interopDefault),
"components/Hero.vue?vue&type=style&index=0&scoped=779d39d7&lang.css": () => import('./Hero-styles.03iOjY05.mjs').then(interopDefault),
"components/PublicNews.vue?vue&type=style&index=0&scoped=f894f108&lang.css": () => import('./PublicNews-styles.nhqk16g5.mjs').then(interopDefault),
"node_modules/nuxt/dist/app/components/error-404.vue?vue&type=style&index=0&scoped=b728498f&lang.css": () => import('./error-404-styles.BsF5Lbhq.mjs').then(interopDefault),
"node_modules/nuxt/dist/app/components/error-500.vue?vue&type=style&index=0&scoped=70d84538&lang.css": () => import('./error-500-styles.Dccc6iq5.mjs').then(interopDefault)
"node_modules/nuxt/dist/app/components/error-500.vue?vue&type=style&index=0&scoped=70d84538&lang.css": () => import('./error-500-styles.Dccc6iq5.mjs').then(interopDefault),
"components/Hero.vue": () => import('./Hero-styles.03iOjY05.mjs').then(interopDefault),
"components/PublicNews.vue": () => import('./PublicNews-styles.nhqk16g5.mjs').then(interopDefault),
"components/Hero.vue?vue&type=style&index=0&scoped=779d39d7&lang.css": () => import('./Hero-styles.03iOjY05.mjs').then(interopDefault),
"components/PublicNews.vue?vue&type=style&index=0&scoped=f894f108&lang.css": () => import('./PublicNews-styles.nhqk16g5.mjs').then(interopDefault)
};
export { styles as default };

View File

@@ -4293,7 +4293,7 @@ function _expandFromEnv(value) {
const _inlineRuntimeConfig = {
"app": {
"baseURL": "/",
"buildId": "88cab71a-6f5e-48fa-ae20-fc874da8cdac",
"buildId": "3a0403e7-f742-4a46-8731-aef781f88d6b",
"buildAssetsDir": "/_nuxt/",
"cdnURL": ""
},
@@ -4741,555 +4741,555 @@ const assets = {
"/data/mannschaften.csv": {
"type": "text/csv; charset=utf-8",
"etag": "\"858-l94GKn8Q0I5RQnhrM0ZPJsYUmcw\"",
"mtime": "2025-10-21T13:50:42.009Z",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 2136,
"path": "../public/data/mannschaften.csv"
},
"/data/spielsysteme.csv": {
"type": "text/csv; charset=utf-8",
"etag": "\"9bc-4npLrNHYClsD0TKV5vSifxitfV0\"",
"mtime": "2025-10-21T13:50:42.009Z",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 2492,
"path": "../public/data/spielsysteme.csv"
},
"/data/termine.csv": {
"type": "text/csv; charset=utf-8",
"etag": "\"2e8-sZtaHF6QRmOQHinTWOLAYRgo6xk\"",
"mtime": "2025-10-21T13:50:42.009Z",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 744,
"path": "../public/data/termine.csv"
},
"/data/vereinsmeisterschaften.csv": {
"type": "text/csv; charset=utf-8",
"etag": "\"989-X8AB+Zegy2xUbjDtbQcXhuuyBDQ\"",
"mtime": "2025-10-21T13:50:42.009Z",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 2441,
"path": "../public/data/vereinsmeisterschaften.csv"
},
"/documents/Tischtennisregeln light.pdf": {
"type": "application/pdf",
"etag": "\"5177b-y/88q2+Y3RRechJMqWhse21KRdQ\"",
"mtime": "2025-10-21T13:50:42.009Z",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 333691,
"path": "../public/documents/Tischtennisregeln light.pdf"
},
"/documents/satzung.pdf": {
"type": "application/pdf",
"etag": "\"5c7cf-L0A3nT8D24T9sD57FFbij3QRpzw\"",
"mtime": "2025-10-21T13:50:42.009Z",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 378831,
"path": "../public/documents/satzung.pdf"
},
"/spielplaene/1. Mannschaft 20252026.pdf": {
"type": "application/pdf",
"etag": "\"64c6-+477M+gD/spwpWR9NO/tMJ/inCc\"",
"mtime": "2025-10-21T13:50:42.009Z",
"size": 25798,
"path": "../public/spielplaene/1. Mannschaft 20252026.pdf"
},
"/spielplaene/2. Mannschaft 20252026.pdf": {
"type": "application/pdf",
"etag": "\"5bfa-DRJMHLV15iss67lEISoGqSYmZjE\"",
"mtime": "2025-10-21T13:50:42.009Z",
"size": 23546,
"path": "../public/spielplaene/2. Mannschaft 20252026.pdf"
},
"/spielplaene/3. Mannschaft 20252026.pdf": {
"type": "application/pdf",
"etag": "\"7447-w933CPQdXhkWJ2AZOVdY0UgJnPo\"",
"mtime": "2025-10-21T13:50:42.009Z",
"size": 29767,
"path": "../public/spielplaene/3. Mannschaft 20252026.pdf"
},
"/spielplaene/4. Mannschaft 20252026.pdf": {
"type": "application/pdf",
"etag": "\"6a9b-4TPGn1yQlFUMRj7oB43SN//Np9o\"",
"mtime": "2025-10-21T13:50:42.009Z",
"size": 27291,
"path": "../public/spielplaene/4. Mannschaft 20252026.pdf"
},
"/spielplaene/5. Mannschaft 20252026.pdf": {
"type": "application/pdf",
"etag": "\"6523-5VUfCMaoiNhcwHhptHHTVJ3lSwQ\"",
"mtime": "2025-10-21T13:50:42.009Z",
"size": 25891,
"path": "../public/spielplaene/5. Mannschaft 20252026.pdf"
},
"/spielplaene/Jugend 11 20252026.pdf": {
"type": "application/pdf",
"etag": "\"52e9-3Rrk9UKUxPh80pBJ0w9oLVbe5dA\"",
"mtime": "2025-10-21T13:50:42.009Z",
"size": 21225,
"path": "../public/spielplaene/Jugend 11 20252026.pdf"
},
"/images/club_about_us.png": {
"type": "image/png",
"etag": "\"202e56-s4fLsHEgoAgKJeBRuI1qxPmqHV0\"",
"mtime": "2025-10-21T13:50:42.009Z",
"size": 2109014,
"path": "../public/images/club_about_us.png"
},
"/_nuxt/B23trXK4.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1168-Ulrp7wDqGkKdNtiJFHTxmUmXldI\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.357Z",
"size": 4456,
"path": "../public/_nuxt/B23trXK4.js"
},
"/_nuxt/B3KXwwdt.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"81f-a2SnM+umqZ4dmwPE80AuhD/ofeY\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.357Z",
"size": 2079,
"path": "../public/_nuxt/B3KXwwdt.js"
},
"/_nuxt/B4mSF5Ac.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"185-hHs3mU4qOcQAkGQaPrUYGaG0yao\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.357Z",
"size": 389,
"path": "../public/_nuxt/B4mSF5Ac.js"
},
"/_nuxt/B6TEfPSg.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"faa-eKvxmyzZhwTvzR8CVB/YoBNhWPw\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.357Z",
"size": 4010,
"path": "../public/_nuxt/B6TEfPSg.js"
},
"/_nuxt/B94vUBDm.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"23f5-XOrzblQBbbg+gHCtcftlnEMApxI\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.357Z",
"size": 9205,
"path": "../public/_nuxt/B94vUBDm.js"
},
"/_nuxt/BASo1Rw1.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1a9f-AETC+kRrTxOyu6uck7TEQ709m7k\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.357Z",
"size": 6815,
"path": "../public/_nuxt/BASo1Rw1.js"
},
"/_nuxt/BC4PNGtJ.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"175-33lu59Ps/+kwbPv/hVeUdrq4wmI\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.357Z",
"size": 373,
"path": "../public/_nuxt/BC4PNGtJ.js"
},
"/_nuxt/BGsE9M8w.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1dc5-wDAFBA19AfN1chv6Fou4TR+Ocyk\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 7621,
"path": "../public/_nuxt/BGsE9M8w.js"
},
"/_nuxt/BRhWghMt.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"16e9-77KyMLdoERbk/9I/4xig919AOCQ\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 5865,
"path": "../public/_nuxt/BRhWghMt.js"
},
"/_nuxt/BU5mk92E.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"2029-KCj/1gSBJVISCuMvs9MA7PoIdnY\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 8233,
"path": "../public/_nuxt/BU5mk92E.js"
},
"/_nuxt/BZLaJF8o.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"685-ABktMZGm1wLg51VMeeUc8FwDm3U\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 1669,
"path": "../public/_nuxt/BZLaJF8o.js"
},
"/_nuxt/Bdk8d7qx.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"11e7-YYqkAkZMNacy1r/CaMWBfPJ94OU\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 4583,
"path": "../public/_nuxt/Bdk8d7qx.js"
},
"/_nuxt/BjiYAScN.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"13f6-Hw3IRFNgh5L2rLxP61MG/cT8NzA\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 5110,
"path": "../public/_nuxt/BjiYAScN.js"
},
"/_nuxt/Bn7G3VMx.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"16eb-6SRn6aWwajUUKphf99cu+aqdGpU\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 5867,
"path": "../public/_nuxt/Bn7G3VMx.js"
},
"/_nuxt/BteKZQ9T.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1ea-kmrGdt5SPmt15EiBI7kR9gXMQM0\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 490,
"path": "../public/_nuxt/BteKZQ9T.js"
},
"/_nuxt/BxR6w-Hd.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"5f0-Wm3M/7i81O9ViLRC6jPn0eQp24g\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 1520,
"path": "../public/_nuxt/BxR6w-Hd.js"
},
"/_nuxt/C-bqeYjt.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"3513-AcHmeXV6b+tvCtqgBG6EnnxPL8w\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 13587,
"path": "../public/_nuxt/C-bqeYjt.js"
},
"/_nuxt/C1eD6HzQ.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"abb-rmFamqyRm9KsgptRPeFJlFZ7AdI\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 2747,
"path": "../public/_nuxt/C1eD6HzQ.js"
},
"/_nuxt/C5SyyWEb.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"2a5-06iX+CL3i0ysaqW9nu7Eg2YzDhQ\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 677,
"path": "../public/_nuxt/C5SyyWEb.js"
},
"/_nuxt/C5WkR1xp.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"b10-7bTi/rXEsVv7CfCAujimlOWU3q0\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 2832,
"path": "../public/_nuxt/C5WkR1xp.js"
},
"/_nuxt/C8kQt0fa.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"197-7X99z1xphxry8OnMwU7Ofs/uE0Q\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 407,
"path": "../public/_nuxt/C8kQt0fa.js"
},
"/_nuxt/C9SglkVL.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"ee2-953PjDhBAep38tbBTU3/pMqFyww\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 3810,
"path": "../public/_nuxt/C9SglkVL.js"
},
"/_nuxt/C9UhLsiJ.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1744-UhJxrXDy4uAkLfPpSs6j/m/gx64\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 5956,
"path": "../public/_nuxt/C9UhLsiJ.js"
},
"/_nuxt/CGpRzXRB.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"37d-FnxQohNMb8l0n0XcPv+1bmf1WCE\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 893,
"path": "../public/_nuxt/CGpRzXRB.js"
},
"/_nuxt/CI8YYLGa.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"d8b-SdM/XDodrfUIexPUk7MhxL0ud70\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 3467,
"path": "../public/_nuxt/CI8YYLGa.js"
},
"/_nuxt/COK-PGSA.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1556-zTPwefaXANTxfUv6X27Ctd3j468\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 5462,
"path": "../public/_nuxt/COK-PGSA.js"
},
"/_nuxt/CT6VfTlB.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"10bb-m3q/ne67feCvdLmlKowXgEW18XE\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 4283,
"path": "../public/_nuxt/CT6VfTlB.js"
},
"/_nuxt/CTuRK0lH.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"177f-9AHJLxzT1QXHm05RkMKEx5CuaVU\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 6015,
"path": "../public/_nuxt/CTuRK0lH.js"
},
"/_nuxt/CUq_0rkE.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"12d-JV4KW1fgT85/V3Ap13X4q2h9U3g\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 301,
"path": "../public/_nuxt/CUq_0rkE.js"
},
"/_nuxt/CW9krljs.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"dbf-R7lqTpP/JoeELqrwiRklrTVdlP0\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 3519,
"path": "../public/_nuxt/CW9krljs.js"
},
"/_nuxt/CWEkTB1z.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"198-ej4DRqc3/5nSwWU3c6wbOD3Ib9w\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 408,
"path": "../public/_nuxt/CWEkTB1z.js"
},
"/_nuxt/C_U-NUAd.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"13f-zgvIssSMnG3JGf/eGC7PlZzbsiY\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 319,
"path": "../public/_nuxt/C_U-NUAd.js"
},
"/_nuxt/CkzaQq3X.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"17d-+xKrHjeww4bpFFkkjUNLD/ebn5A\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 381,
"path": "../public/_nuxt/CkzaQq3X.js"
},
"/_nuxt/CrCcIvVp.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"274-9U7hEMtgHqdnQopnKeJsBKqKyKw\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 628,
"path": "../public/_nuxt/CrCcIvVp.js"
},
"/_nuxt/CvtlfSR0.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1860-M3/zR8vfhVWmJ6erGqFzdHXQXuE\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 6240,
"path": "../public/_nuxt/CvtlfSR0.js"
},
"/_nuxt/Cx4UcKGu.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"19d-5AMD0EnFEjOkM3qKDpC/NZZzwDI\"",
"mtime": "2025-10-21T13:50:42.005Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 413,
"path": "../public/_nuxt/Cx4UcKGu.js"
},
"/_nuxt/Czdc6-TI.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"165-EMJ/yP2qajGIw0CL3y+L/hvMM/8\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 357,
"path": "../public/_nuxt/Czdc6-TI.js"
},
"/_nuxt/D11oUMHK.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1d9a-l6JEyKxxIcbq3Kkksqm5qE2ZWj4\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 7578,
"path": "../public/_nuxt/D11oUMHK.js"
},
"/_nuxt/D43Z0o-f.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"32c8e-LLbrtE0SvQkEnQ4ry/ZcUybqPmQ\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 208014,
"path": "../public/_nuxt/D43Z0o-f.js"
},
"/_nuxt/D99LnZYi.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1bdb-/r79rlmI33Ifam5FH18djsiUTQ8\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 7131,
"path": "../public/_nuxt/D99LnZYi.js"
},
"/_nuxt/DAACT36i.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1f9-dVOk5jAwb0VlMLJevIcT+s2NTgM\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 505,
"path": "../public/_nuxt/DAACT36i.js"
},
"/_nuxt/DHQur1V-.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1827-DA/YFWxSxxRm0zvcpiuZvwwfzNE\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 6183,
"path": "../public/_nuxt/DHQur1V-.js"
},
"/_nuxt/DaSgy0Cl.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"11f-soKnh1qfNJj5nvt+IcgQXYvg/z4\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 287,
"path": "../public/_nuxt/DaSgy0Cl.js"
},
"/_nuxt/DdHhmCne.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"22d-uKYfhsDcUsz2NrXOJmxptUGZdyE\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 557,
"path": "../public/_nuxt/DdHhmCne.js"
},
"/_nuxt/DjcJk1g8.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"201d-oQ9NMDE0anKxIZA105IRItTlM2w\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 8221,
"path": "../public/_nuxt/DjcJk1g8.js"
},
"/_nuxt/DkeYb0_S.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1ce-xiaAbRvqQ+zffTXF3Gc7rq14R0U\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.358Z",
"size": 462,
"path": "../public/_nuxt/DkeYb0_S.js"
},
"/_nuxt/DlAUqK2U.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"5b-eFCz/UrraTh721pgAl0VxBNR1es\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 91,
"path": "../public/_nuxt/DlAUqK2U.js"
},
"/_nuxt/DvrqIJw1.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"dfc-YqdcBHK+H9m+Gjgl+qCwmlBEIX0\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 3580,
"path": "../public/_nuxt/DvrqIJw1.js"
},
"/_nuxt/Dx1KRsmK.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1d86-kU610bWwsXBL249yP4W8lasEh3c\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 7558,
"path": "../public/_nuxt/Dx1KRsmK.js"
},
"/_nuxt/DxIIJuzj.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"bf0-Q7VHINZ8z6pKO1o0BhgbcvRgIr0\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 3056,
"path": "../public/_nuxt/DxIIJuzj.js"
},
"/_nuxt/Harheimer TC.CKfYAfp1.svg": {
"type": "image/svg+xml",
"etag": "\"1d2535-Tx2lTuuFn2hBqGZOnDan3/OdRU0\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 1910069,
"path": "../public/_nuxt/Harheimer TC.CKfYAfp1.svg"
},
"/_nuxt/KxVBmS-6.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"190-F0LVEAqwB2LwyGzW0v9yzLx0v/0\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 400,
"path": "../public/_nuxt/KxVBmS-6.js"
},
"/_nuxt/LPF2GIYR.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"557-80ya3yG6VaGTcTbyJrpOfIs1ttU\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 1367,
"path": "../public/_nuxt/LPF2GIYR.js"
},
"/_nuxt/XZ6RV9KH.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"175-xr1poEaGS4yjOp907AsRAr6XHLI\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 373,
"path": "../public/_nuxt/XZ6RV9KH.js"
},
"/_nuxt/YJHbYJtA.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"19f-nQw578pUen9o8yYaMA8Bwag6xho\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 415,
"path": "../public/_nuxt/YJHbYJtA.js"
},
"/_nuxt/entry.BbGskshJ.css": {
"type": "text/css; charset=utf-8",
"etag": "\"b49b-DhLpGn2VN/6EPp9+uW1fcsLCSBk\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 46235,
"path": "../public/_nuxt/entry.BbGskshJ.css"
},
"/_nuxt/error-404.CbXQcqJW.css": {
"type": "text/css; charset=utf-8",
"etag": "\"97e-Ty5bTTSEudJkO/DsGUoIf37xYxc\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 2430,
"path": "../public/_nuxt/error-404.CbXQcqJW.css"
},
"/_nuxt/error-500.L485xXhD.css": {
"type": "text/css; charset=utf-8",
"etag": "\"773-jNt1QdCa+iqaSZb1mv/IQWC5p6w\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 1907,
"path": "../public/_nuxt/error-500.L485xXhD.css"
},
"/_nuxt/index.ByttcLyP.css": {
"type": "text/css; charset=utf-8",
"etag": "\"1db-P3imbnjv59PWVm0HNpwpfdEtAK4\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 475,
"path": "../public/_nuxt/index.ByttcLyP.css"
},
"/_nuxt/jVj3QaoK.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"196-xWXv220Sy3kJeouwzrQ/gnXllWQ\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 406,
"path": "../public/_nuxt/jVj3QaoK.js"
},
"/_nuxt/nrzLFm_7.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"32e3-ybdqGbfFawjPOvyY2mCJu5CTjkg\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 13027,
"path": "../public/_nuxt/nrzLFm_7.js"
},
"/_nuxt/oN0_bS6A.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"1d3-DKhiaT2RUlSXk55jBttctUuTQQI\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 467,
"path": "../public/_nuxt/oN0_bS6A.js"
},
"/_nuxt/rgKTeSYE.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"e71-IzPUyj76F9mR9c9DaEV7x6UlmP0\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 3697,
"path": "../public/_nuxt/rgKTeSYE.js"
},
"/_nuxt/sVyj_WZX.js": {
"type": "text/javascript; charset=utf-8",
"etag": "\"4d4-T+i1jfWN+C61xV/Shhjh8+auLuI\"",
"mtime": "2025-10-21T13:50:42.006Z",
"mtime": "2025-10-21T13:54:19.359Z",
"size": 1236,
"path": "../public/_nuxt/sVyj_WZX.js"
},
"/spielplaene/1. Mannschaft 20252026.pdf": {
"type": "application/pdf",
"etag": "\"64c6-+477M+gD/spwpWR9NO/tMJ/inCc\"",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 25798,
"path": "../public/spielplaene/1. Mannschaft 20252026.pdf"
},
"/spielplaene/2. Mannschaft 20252026.pdf": {
"type": "application/pdf",
"etag": "\"5bfa-DRJMHLV15iss67lEISoGqSYmZjE\"",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 23546,
"path": "../public/spielplaene/2. Mannschaft 20252026.pdf"
},
"/spielplaene/3. Mannschaft 20252026.pdf": {
"type": "application/pdf",
"etag": "\"7447-w933CPQdXhkWJ2AZOVdY0UgJnPo\"",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 29767,
"path": "../public/spielplaene/3. Mannschaft 20252026.pdf"
},
"/spielplaene/4. Mannschaft 20252026.pdf": {
"type": "application/pdf",
"etag": "\"6a9b-4TPGn1yQlFUMRj7oB43SN//Np9o\"",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 27291,
"path": "../public/spielplaene/4. Mannschaft 20252026.pdf"
},
"/spielplaene/5. Mannschaft 20252026.pdf": {
"type": "application/pdf",
"etag": "\"6523-5VUfCMaoiNhcwHhptHHTVJ3lSwQ\"",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 25891,
"path": "../public/spielplaene/5. Mannschaft 20252026.pdf"
},
"/spielplaene/Jugend 11 20252026.pdf": {
"type": "application/pdf",
"etag": "\"52e9-3Rrk9UKUxPh80pBJ0w9oLVbe5dA\"",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 21225,
"path": "../public/spielplaene/Jugend 11 20252026.pdf"
},
"/images/club_about_us.png": {
"type": "image/png",
"etag": "\"202e56-s4fLsHEgoAgKJeBRuI1qxPmqHV0\"",
"mtime": "2025-10-21T13:54:19.362Z",
"size": 2109014,
"path": "../public/images/club_about_us.png"
},
"/_nuxt/builds/latest.json": {
"type": "application/json",
"etag": "\"47-oaS8zla0HqEH/6C0JmM9Ge/P27w\"",
"mtime": "2025-10-21T13:50:41.998Z",
"etag": "\"47-kVYTDCXn2ek6VAK9NgAHFay0zCg\"",
"mtime": "2025-10-21T13:54:19.351Z",
"size": 71,
"path": "../public/_nuxt/builds/latest.json"
},
"/_nuxt/builds/meta/88cab71a-6f5e-48fa-ae20-fc874da8cdac.json": {
"/_nuxt/builds/meta/3a0403e7-f742-4a46-8731-aef781f88d6b.json": {
"type": "application/json",
"etag": "\"8b-y0o/k4z8GsMRwtIxOumWzGX3oHk\"",
"mtime": "2025-10-21T13:50:41.996Z",
"etag": "\"8b-E/F64hsLBiBq9KU6aTwdVPD8Ynk\"",
"mtime": "2025-10-21T13:54:19.348Z",
"size": 139,
"path": "../public/_nuxt/builds/meta/88cab71a-6f5e-48fa-ae20-fc874da8cdac.json"
"path": "../public/_nuxt/builds/meta/3a0403e7-f742-4a46-8731-aef781f88d6b.json"
}
};

View File

@@ -39,7 +39,7 @@ const news_post = defineEventHandler(async (event) => {
});
}
const body = await readBody(event);
const { id, title, content } = body;
const { id, title, content, isPublic } = body;
if (!title || !content) {
throw createError({
statusCode: 400,
@@ -50,6 +50,7 @@ const news_post = defineEventHandler(async (event) => {
id: id || void 0,
title,
content,
isPublic: isPublic || false,
author: user.name
});
return {

View File

@@ -1 +1 @@
{"version":3,"file":"news.post.mjs","sources":["../../../../../server/api/news.post.js"],"sourcesContent":null,"names":[],"mappings":";;;;;;;;;;;;;;;;;AAGA,kBAAA,kBAAA,CAAA,OAAA,KAAA,KAAA;AACA,EAAA,IAAA;AACA,IAAA,MAAA,KAAA,GAAA,SAAA,CAAA,KAAA,EAAA,YAAA,CAAA;AAEA,IAAA,IAAA,CAAA,KAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,OAAA,GAAA,YAAA,KAAA,CAAA;AAEA,IAAA,IAAA,CAAA,OAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,IAAA,GAAA,MAAA,WAAA,CAAA,OAAA,CAAA,EAAA,CAAA;AAGA,IAAA,IAAA,CAAA,IAAA,IAAA,IAAA,CAAA,SAAA,OAAA,IAAA,IAAA,CAAA,SAAA,UAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,IAAA,GAAA,MAAA,QAAA,CAAA,KAAA,CAAA;AACA,IAAA,MAAA,EAAA,EAAA,EAAA,KAAA,EAAA,OAAA,EAAA,GAAA,IAAA;AAEA,IAAA,IAAA,CAAA,KAAA,IAAA,CAAA,OAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,QAAA,CAAA;AAAA,MACA,IAAA,EAAA,IAAA,KAAA,CAAA;AAAA,MACA,KAAA;AAAA,MACA,OAAA;AAAA,MACA,QAAA,IAAA,CAAA;AAAA,KACA,CAAA;AAEA,IAAA,OAAA;AAAA,MACA,OAAA,EAAA,IAAA;AAAA,MACA,OAAA,EAAA;AAAA,KACA;AAAA,EACA,SAAA,KAAA,EAAA;AACA,IAAA,OAAA,CAAA,KAAA,CAAA,mCAAA,KAAA,CAAA;AACA,IAAA,MAAA,KAAA;AAAA,EACA;AACA,CAAA,CAAA;;;;"}
{"version":3,"file":"news.post.mjs","sources":["../../../../../server/api/news.post.js"],"sourcesContent":null,"names":[],"mappings":";;;;;;;;;;;;;;;;;AAGA,kBAAA,kBAAA,CAAA,OAAA,KAAA,KAAA;AACA,EAAA,IAAA;AACA,IAAA,MAAA,KAAA,GAAA,SAAA,CAAA,KAAA,EAAA,YAAA,CAAA;AAEA,IAAA,IAAA,CAAA,KAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,OAAA,GAAA,YAAA,KAAA,CAAA;AAEA,IAAA,IAAA,CAAA,OAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,IAAA,GAAA,MAAA,WAAA,CAAA,OAAA,CAAA,EAAA,CAAA;AAGA,IAAA,IAAA,CAAA,IAAA,IAAA,IAAA,CAAA,SAAA,OAAA,IAAA,IAAA,CAAA,SAAA,UAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,IAAA,GAAA,MAAA,QAAA,CAAA,KAAA,CAAA;AACA,IAAA,MAAA,EAAA,EAAA,EAAA,KAAA,EAAA,OAAA,EAAA,UAAA,GAAA,IAAA;AAEA,IAAA,IAAA,CAAA,KAAA,IAAA,CAAA,OAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,QAAA,CAAA;AAAA,MACA,IAAA,EAAA,IAAA,KAAA,CAAA;AAAA,MACA,KAAA;AAAA,MACA,OAAA;AAAA,MACA,UAAA,QAAA,IAAA,KAAA;AAAA,MACA,QAAA,IAAA,CAAA;AAAA,KACA,CAAA;AAEA,IAAA,OAAA;AAAA,MACA,OAAA,EAAA,IAAA;AAAA,MACA,OAAA,EAAA;AAAA,KACA;AAAA,EACA,SAAA,KAAA,EAAA;AACA,IAAA,OAAA,CAAA,KAAA,CAAA,mCAAA,KAAA,CAAA;AACA,IAAA,MAAA,KAAA;AAAA,EACA;AACA,CAAA,CAAA;;;;"}

View File

@@ -6,122 +6,87 @@
</h1>
<div class="w-24 h-1 bg-primary-600 mb-8" />
<div class="bg-white rounded-xl shadow-lg p-8 mb-8">
<h2 class="text-2xl font-display font-bold text-gray-900 mb-4">
Willkommen im CMS, {{ user?.name }}!
</h2>
<p class="text-gray-600">
Hier können Sie Inhalte der Website verwalten.
</p>
</div>
<!-- CMS Modules -->
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center mr-4">
<Calendar :size="24" class="text-primary-600" />
</div>
<h3 class="text-lg font-semibold text-gray-900">Termine verwalten</h3>
</div>
<p class="text-gray-600 text-sm mb-4">
Termine hinzufügen, bearbeiten und löschen
</p>
<button class="text-sm text-primary-600 hover:text-primary-700 font-medium">
Öffnen
</button>
</div>
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center mr-4">
<Newspaper :size="24" class="text-primary-600" />
</div>
<h3 class="text-lg font-semibold text-gray-900">Interne News</h3>
</div>
<p class="text-gray-600 text-sm mb-4">
News für Mitglieder erstellen und verwalten
</p>
<button class="text-sm text-primary-600 hover:text-primary-700 font-medium">
Öffnen
</button>
</div>
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center mr-4">
<FileText :size="24" class="text-primary-600" />
</div>
<h3 class="text-lg font-semibold text-gray-900">Spielpläne</h3>
</div>
<p class="text-gray-600 text-sm mb-4">
Spielpläne hochladen und verwalten
</p>
<button class="text-sm text-primary-600 hover:text-primary-700 font-medium">
Öffnen
</button>
</div>
<!-- Interne News -->
<NuxtLink
to="/cms/benutzer"
class="bg-white p-6 rounded-xl shadow-lg border border-gray-100 hover:shadow-xl transition-shadow block"
to="/mitgliederbereich/news"
class="bg-white p-6 rounded-xl shadow-lg border border-gray-100 hover:shadow-xl transition-all group"
>
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center mr-4">
<Users :size="24" class="text-primary-600" />
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center group-hover:bg-blue-600 transition-colors">
<Newspaper :size="24" class="text-blue-600 group-hover:text-white" />
</div>
<h3 class="text-lg font-semibold text-gray-900">Benutzerverwaltung</h3>
<h2 class="ml-4 text-xl font-semibold text-gray-900">Interne News</h2>
</div>
<p class="text-gray-600 text-sm mb-4">
Registrierungen freischalten und Rollen verwalten
<p class="text-gray-600">
News für Mitglieder erstellen und verwalten
</p>
<span class="text-sm text-primary-600 hover:text-primary-700 font-medium">
Öffnen
</span>
</NuxtLink>
<div class="bg-white p-6 rounded-xl shadow-lg border border-gray-100">
<!-- Termine -->
<NuxtLink
to="/cms/termine"
class="bg-white p-6 rounded-xl shadow-lg border border-gray-100 hover:shadow-xl transition-all group"
>
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-primary-100 rounded-lg flex items-center justify-center mr-4">
<Image :size="24" class="text-primary-600" />
<div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center group-hover:bg-green-600 transition-colors">
<Calendar :size="24" class="text-green-600 group-hover:text-white" />
</div>
<h3 class="text-lg font-semibold text-gray-900">Galerie</h3>
<h2 class="ml-4 text-xl font-semibold text-gray-900">Termine</h2>
</div>
<p class="text-gray-600 text-sm mb-4">
Bilder hochladen und verwalten
<p class="text-gray-600">
Vereinstermine erstellen und verwalten
</p>
<button class="text-sm text-primary-600 hover:text-primary-700 font-medium">
Öffnen
</button>
</div>
</NuxtLink>
<!-- Mitglieder -->
<NuxtLink
to="/mitgliederbereich/mitglieder"
class="bg-white p-6 rounded-xl shadow-lg border border-gray-100 hover:shadow-xl transition-all group"
>
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-purple-100 rounded-lg flex items-center justify-center group-hover:bg-purple-600 transition-colors">
<Users :size="24" class="text-purple-600 group-hover:text-white" />
</div>
<h2 class="ml-4 text-xl font-semibold text-gray-900">Mitglieder</h2>
</div>
<p class="text-gray-600">
Mitgliederliste bearbeiten
</p>
</NuxtLink>
<!-- Benutzerverwaltung (nur für Admin) -->
<NuxtLink
v-if="authStore.role === 'admin'"
to="/cms/benutzer"
class="bg-white p-6 rounded-xl shadow-lg border border-gray-100 hover:shadow-xl transition-all group"
>
<div class="flex items-center mb-4">
<div class="w-12 h-12 bg-yellow-100 rounded-lg flex items-center justify-center group-hover:bg-yellow-600 transition-colors">
<UserCog :size="24" class="text-yellow-600 group-hover:text-white" />
</div>
<h2 class="ml-4 text-xl font-semibold text-gray-900">Benutzerverwaltung</h2>
</div>
<p class="text-gray-600">
Benutzer freischalten und verwalten
</p>
</NuxtLink>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { Calendar, Newspaper, FileText, Users, Image } from 'lucide-vue-next'
import { Newspaper, Calendar, Users, UserCog } from 'lucide-vue-next'
const user = ref(null)
onMounted(async () => {
try {
const response = await $fetch('/api/auth/status')
if (response.isLoggedIn) {
user.value = response.user
}
} catch (error) {
console.error('Fehler beim Laden der Benutzerdaten:', error)
}
})
const authStore = useAuthStore()
definePageMeta({
middleware: 'auth'
middleware: 'auth',
layout: 'default'
})
useHead({
title: 'CMS - Harheimer TC',
})
</script>

286
pages/cms/termine.vue Normal file
View File

@@ -0,0 +1,286 @@
<template>
<div class="min-h-full py-16 bg-gray-50">
<div class="max-w-6xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center mb-6">
<div>
<h1 class="text-4xl sm:text-5xl font-display font-bold text-gray-900 mb-2">
Termine verwalten
</h1>
<div class="w-24 h-1 bg-primary-600 mb-4" />
</div>
<button
@click="openAddModal"
class="flex items-center px-4 py-2 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors"
>
<Plus :size="20" class="mr-2" />
Termin hinzufügen
</button>
</div>
<!-- Loading State -->
<div v-if="isLoading" class="flex items-center justify-center py-12">
<Loader2 :size="40" class="animate-spin text-primary-600" />
</div>
<!-- Termine Table -->
<div v-else class="bg-white rounded-xl shadow-lg overflow-hidden">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Datum</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Titel</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Beschreibung</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Kategorie</th>
<th class="px-4 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Aktionen</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="termin in termine" :key="termin.id" class="hover:bg-gray-50">
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
{{ formatDate(termin.datum) }}
</td>
<td class="px-4 py-3 text-sm font-medium text-gray-900">
{{ termin.titel }}
</td>
<td class="px-4 py-3 text-sm text-gray-600">
{{ termin.beschreibung || '-' }}
</td>
<td class="px-4 py-3 whitespace-nowrap">
<span
:class="{
'bg-blue-100 text-blue-800': termin.kategorie === 'Training',
'bg-green-100 text-green-800': termin.kategorie === 'Punktspiel',
'bg-purple-100 text-purple-800': termin.kategorie === 'Turnier',
'bg-yellow-100 text-yellow-800': termin.kategorie === 'Veranstaltung',
'bg-gray-100 text-gray-800': termin.kategorie === 'Sonstiges'
}"
class="px-2 py-1 text-xs font-medium rounded-full"
>
{{ termin.kategorie }}
</span>
</td>
<td class="px-4 py-3 whitespace-nowrap text-right text-sm font-medium">
<button
@click="confirmDelete(termin)"
class="text-red-600 hover:text-red-900"
title="Löschen"
>
<Trash2 :size="18" />
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div v-if="termine.length === 0" class="text-center py-12 text-gray-500">
Keine Termine vorhanden.
</div>
</div>
<!-- Add Modal -->
<div
v-if="showModal"
class="fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4"
@click.self="closeModal"
>
<div class="bg-white rounded-xl shadow-2xl max-w-2xl w-full p-8">
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6">
Termin hinzufügen
</h2>
<form @submit.prevent="saveTermin" class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Datum *</label>
<input
v-model="formData.datum"
type="date"
required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
:disabled="isSaving"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Kategorie *</label>
<select
v-model="formData.kategorie"
required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
:disabled="isSaving"
>
<option value="Training">Training</option>
<option value="Punktspiel">Punktspiel</option>
<option value="Turnier">Turnier</option>
<option value="Veranstaltung">Veranstaltung</option>
<option value="Sonstiges">Sonstiges</option>
</select>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Titel *</label>
<input
v-model="formData.titel"
type="text"
required
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
:disabled="isSaving"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Beschreibung</label>
<textarea
v-model="formData.beschreibung"
rows="3"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500"
:disabled="isSaving"
/>
</div>
<div v-if="errorMessage" class="flex items-center p-3 rounded-md bg-red-50 text-red-700 text-sm">
<AlertCircle :size="20" class="mr-2" />
{{ errorMessage }}
</div>
<div class="flex justify-end space-x-4 pt-4">
<button
type="button"
@click="closeModal"
class="px-6 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
:disabled="isSaving"
>
Abbrechen
</button>
<button
type="submit"
class="px-6 py-2 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors flex items-center"
:disabled="isSaving"
>
<Loader2 v-if="isSaving" :size="20" class="animate-spin mr-2" />
<span>{{ isSaving ? 'Speichert...' : 'Speichern' }}</span>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { Calendar, Plus, Trash2, Loader2, AlertCircle } from 'lucide-vue-next'
const authStore = useAuthStore()
const isLoading = ref(true)
const isSaving = ref(false)
const termine = ref([])
const showModal = ref(false)
const errorMessage = ref('')
const formData = ref({
datum: '',
titel: '',
beschreibung: '',
kategorie: 'Sonstiges'
})
const loadTermine = async () => {
isLoading.value = true
try {
const response = await $fetch('/api/termine-manage')
termine.value = response.termine
} catch (error) {
console.error('Fehler beim Laden der Termine:', error)
} finally {
isLoading.value = false
}
}
const openAddModal = () => {
formData.value = {
datum: '',
titel: '',
beschreibung: '',
kategorie: 'Sonstiges'
}
showModal.value = true
errorMessage.value = ''
}
const closeModal = () => {
showModal.value = false
errorMessage.value = ''
}
const saveTermin = async () => {
isSaving.value = true
errorMessage.value = ''
try {
await $fetch('/api/termine-manage', {
method: 'POST',
body: formData.value
})
closeModal()
await loadTermine()
} catch (error) {
errorMessage.value = error.data?.message || 'Fehler beim Speichern des Termins.'
} finally {
isSaving.value = false
}
}
const confirmDelete = async (termin) => {
if (!confirm(`Möchten Sie den Termin "${termin.titel}" wirklich löschen?`)) {
return
}
try {
const params = new URLSearchParams({
datum: termin.datum,
titel: termin.titel,
beschreibung: termin.beschreibung || '',
kategorie: termin.kategorie || 'Sonstiges'
})
await $fetch(`/api/termine-manage?${params.toString()}`, {
method: 'DELETE'
})
await loadTermine()
} catch (error) {
alert('Fehler beim Löschen des Termins.')
}
}
const formatDate = (dateString) => {
if (!dateString) return ''
const date = new Date(dateString)
return date.toLocaleDateString('de-DE', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
})
}
onMounted(() => {
loadTermine()
})
definePageMeta({
middleware: 'auth',
layout: 'default'
})
useHead({
title: 'Termine verwalten - Harheimer TC',
})
</script>

View File

@@ -0,0 +1,60 @@
import { verifyToken, getUserById } from '../utils/auth.js'
import { deleteTermin } from '../utils/termine.js'
export default defineEventHandler(async (event) => {
try {
const token = getCookie(event, 'auth_token')
if (!token) {
throw createError({
statusCode: 401,
message: 'Nicht authentifiziert.'
})
}
const decoded = verifyToken(token)
if (!decoded) {
throw createError({
statusCode: 401,
message: 'Ungültiges Token.'
})
}
const user = await getUserById(decoded.id)
// Only admin and vorstand can delete termine
if (!user || (user.role !== 'admin' && user.role !== 'vorstand')) {
throw createError({
statusCode: 403,
message: 'Keine Berechtigung zum Löschen von Terminen.'
})
}
const query = getQuery(event)
const { datum, titel, beschreibung, kategorie } = query
if (!datum || !titel) {
throw createError({
statusCode: 400,
message: 'Datum und Titel sind erforderlich.'
})
}
await deleteTermin({
datum,
titel,
beschreibung: beschreibung || '',
kategorie: kategorie || 'Sonstiges'
})
return {
success: true,
message: 'Termin erfolgreich gelöscht.'
}
} catch (error) {
console.error('Fehler beim Löschen des Termins:', error)
throw error
}
})

View File

@@ -0,0 +1,45 @@
import { verifyToken, getUserById } from '../utils/auth.js'
import { readTermine } from '../utils/termine.js'
export default defineEventHandler(async (event) => {
try {
const token = getCookie(event, 'auth_token')
if (!token) {
throw createError({
statusCode: 401,
message: 'Nicht authentifiziert.'
})
}
const decoded = verifyToken(token)
if (!decoded) {
throw createError({
statusCode: 401,
message: 'Ungültiges Token.'
})
}
const user = await getUserById(decoded.id)
// Only admin and vorstand can manage termine
if (!user || (user.role !== 'admin' && user.role !== 'vorstand')) {
throw createError({
statusCode: 403,
message: 'Keine Berechtigung zum Verwalten von Terminen.'
})
}
const termine = await readTermine()
return {
success: true,
termine
}
} catch (error) {
console.error('Fehler beim Abrufen der Termine:', error)
throw error
}
})

View File

@@ -0,0 +1,60 @@
import { verifyToken, getUserById } from '../utils/auth.js'
import { saveTermin } from '../utils/termine.js'
export default defineEventHandler(async (event) => {
try {
const token = getCookie(event, 'auth_token')
if (!token) {
throw createError({
statusCode: 401,
message: 'Nicht authentifiziert.'
})
}
const decoded = verifyToken(token)
if (!decoded) {
throw createError({
statusCode: 401,
message: 'Ungültiges Token.'
})
}
const user = await getUserById(decoded.id)
// Only admin and vorstand can create termine
if (!user || (user.role !== 'admin' && user.role !== 'vorstand')) {
throw createError({
statusCode: 403,
message: 'Keine Berechtigung zum Erstellen von Terminen.'
})
}
const body = await readBody(event)
const { datum, titel, beschreibung, kategorie } = body
if (!datum || !titel) {
throw createError({
statusCode: 400,
message: 'Datum und Titel sind erforderlich.'
})
}
await saveTermin({
datum,
titel,
beschreibung: beschreibung || '',
kategorie: kategorie || 'Sonstiges'
})
return {
success: true,
message: 'Termin erfolgreich gespeichert.'
}
} catch (error) {
console.error('Fehler beim Speichern des Termins:', error)
throw error
}
})

View File

@@ -4,9 +4,9 @@
"title": "Wir starten durch!",
"content": "Endlich ist es so weit! Willkommen.",
"author": "Admin",
"isPublic": false,
"isPublic": true,
"created": "2025-10-21T13:34:28.915Z",
"updated": "2025-10-21T13:53:16.820Z"
"updated": "2025-10-21T13:54:57.247Z"
},
{
"id": "660e8400-e29b-41d4-a716-446655440002",

133
server/utils/termine.js Normal file
View File

@@ -0,0 +1,133 @@
import { promises as fs } from 'fs'
import path from 'path'
import { randomUUID } from 'crypto'
// Handle both dev and production paths
const getDataPath = (filename) => {
const cwd = process.cwd()
// In production (.output/server), working dir is .output
if (cwd.endsWith('.output')) {
return path.join(cwd, '../public/data', filename)
}
// In development, working dir is project root
return path.join(cwd, 'public/data', filename)
}
const TERMINE_FILE = getDataPath('termine.csv')
// Parse CSV to array of objects
export async function readTermine() {
try {
const data = await fs.readFile(TERMINE_FILE, 'utf-8')
const lines = data.split('\n').filter(line => line.trim() !== '')
if (lines.length < 2) return []
// Parse CSV with quote handling
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({
id: randomUUID(), // Generate ID on-the-fly for editing
datum: values[0],
titel: values[1],
beschreibung: values[2],
kategorie: values[3]
})
}
}
return termine
} catch (error) {
if (error.code === 'ENOENT') {
return []
}
console.error('Fehler beim Lesen der Termine:', error)
return []
}
}
// Write array of objects to CSV
export async function writeTermine(termine) {
try {
let csv = '"datum","titel","beschreibung","kategorie"\n'
for (const termin of termine) {
const datum = termin.datum || ''
const titel = termin.titel || ''
const beschreibung = termin.beschreibung || ''
const kategorie = termin.kategorie || ''
// Escape quotes in values
const escapedDatum = datum.replace(/"/g, '""')
const escapedTitel = titel.replace(/"/g, '""')
const escapedBeschreibung = beschreibung.replace(/"/g, '""')
const escapedKategorie = kategorie.replace(/"/g, '""')
csv += `"${escapedDatum}","${escapedTitel}","${escapedBeschreibung}","${escapedKategorie}"\n`
}
await fs.writeFile(TERMINE_FILE, csv, 'utf-8')
return true
} catch (error) {
console.error('Fehler beim Schreiben der Termine:', error)
return false
}
}
// Add or update termin
export async function saveTermin(terminData) {
const termine = await readTermine()
// Always add as new (CSV doesn't have persistent IDs)
const newTermin = {
datum: terminData.datum,
titel: terminData.titel,
beschreibung: terminData.beschreibung || '',
kategorie: terminData.kategorie || 'Sonstiges'
}
termine.push(newTermin)
// Sort by date
termine.sort((a, b) => new Date(a.datum) - new Date(b.datum))
await writeTermine(termine)
return true
}
// Delete termin by matching all fields (since we don't have persistent IDs)
export async function deleteTermin(terminData) {
let termine = await readTermine()
termine = termine.filter(t =>
!(t.datum === terminData.datum &&
t.titel === terminData.titel &&
t.beschreibung === terminData.beschreibung &&
t.kategorie === terminData.kategorie)
)
await writeTermine(termine)
return true
}