Implement member management enhancements; add bulk import functionality and duplicate checking based on geburtsdatum. Update API to support new fields and improve error handling for member data submissions. Refactor member-related components for better user experience and data validation.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"date": "2025-11-05T12:46:44.995Z",
|
||||
"date": "2025-11-05T13:24:50.100Z",
|
||||
"preset": "node-server",
|
||||
"framework": {
|
||||
"name": "nuxt",
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"id":"9438b35d-0d10-4203-a329-f7a1e287e2a1","timestamp":1762346796211}
|
||||
{"id":"8af76f9e-6e85-416c-9e2f-92c68e0dfd76","timestamp":1762349079882}
|
||||
@@ -72,16 +72,57 @@ async function writeMembers(members) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function normalizeDate(dateString) {
|
||||
if (!dateString) return "";
|
||||
try {
|
||||
const date = new Date(dateString);
|
||||
if (isNaN(date.getTime())) return dateString.trim();
|
||||
return date.toISOString().split("T")[0];
|
||||
} catch (e) {
|
||||
return dateString.trim();
|
||||
}
|
||||
}
|
||||
function findDuplicateMember(members, firstName, lastName, geburtsdatum) {
|
||||
const normalizedFirstName = (firstName || "").trim().toLowerCase();
|
||||
const normalizedLastName = (lastName || "").trim().toLowerCase();
|
||||
const normalizedDate = normalizeDate(geburtsdatum);
|
||||
return members.find((m) => {
|
||||
const mFirstName = (m.firstName || "").trim().toLowerCase();
|
||||
const mLastName = (m.lastName || "").trim().toLowerCase();
|
||||
const mDate = normalizeDate(m.geburtsdatum);
|
||||
return mFirstName === normalizedFirstName && mLastName === normalizedLastName && mDate === normalizedDate && mDate !== "";
|
||||
});
|
||||
}
|
||||
async function saveMember(memberData) {
|
||||
const members = await readMembers();
|
||||
if (memberData.id) {
|
||||
const index = members.findIndex((m) => m.id === memberData.id);
|
||||
if (index !== -1) {
|
||||
const duplicate = findDuplicateMember(
|
||||
members.filter((m) => m.id !== memberData.id),
|
||||
memberData.firstName,
|
||||
memberData.lastName,
|
||||
memberData.geburtsdatum
|
||||
);
|
||||
if (duplicate) {
|
||||
throw new Error("Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.");
|
||||
}
|
||||
members[index] = { ...members[index], ...memberData };
|
||||
} else {
|
||||
throw new Error("Mitglied nicht gefunden");
|
||||
}
|
||||
} else {
|
||||
if (memberData.firstName && memberData.lastName && memberData.geburtsdatum) {
|
||||
const duplicate = findDuplicateMember(
|
||||
members,
|
||||
memberData.firstName,
|
||||
memberData.lastName,
|
||||
memberData.geburtsdatum
|
||||
);
|
||||
if (duplicate) {
|
||||
throw new Error("Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.");
|
||||
}
|
||||
}
|
||||
const newMember = {
|
||||
...memberData,
|
||||
id: randomUUID()
|
||||
@@ -99,5 +140,5 @@ async function deleteMember(id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export { deleteMember as d, readMembers as r, saveMember as s };
|
||||
export { deleteMember as d, normalizeDate as n, readMembers as r, saveMember as s, writeMembers as w };
|
||||
//# sourceMappingURL=members.mjs.map
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"members.mjs","sources":["../../../../server/utils/members.js"],"sourcesContent":null,"names":["fs"],"mappings":"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"}
|
||||
{"version":3,"file":"members.mjs","sources":["../../../../server/utils/members.js"],"sourcesContent":null,"names":["fs"],"mappings":"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"}
|
||||
@@ -296,7 +296,7 @@ const client_manifest = {
|
||||
"module": true,
|
||||
"prefetch": true,
|
||||
"preload": true,
|
||||
"file": "oL_Xi1h-.js",
|
||||
"file": "CxKxaas7.js",
|
||||
"name": "entry",
|
||||
"src": "node_modules/nuxt/dist/app/entry.js",
|
||||
"isEntry": true,
|
||||
@@ -306,14 +306,14 @@ const client_manifest = {
|
||||
"node_modules/nuxt/dist/app/components/error-500.vue"
|
||||
],
|
||||
"css": [
|
||||
"entry.CYvTzE5H.css"
|
||||
"entry.wEUbgGlA.css"
|
||||
],
|
||||
"assets": [
|
||||
"Harheimer TC.CKfYAfp1.svg"
|
||||
]
|
||||
},
|
||||
"entry.CYvTzE5H.css": {
|
||||
"file": "entry.CYvTzE5H.css",
|
||||
"entry.wEUbgGlA.css": {
|
||||
"file": "entry.wEUbgGlA.css",
|
||||
"resourceType": "style",
|
||||
"prefetch": true,
|
||||
"preload": true
|
||||
@@ -699,6 +699,20 @@ const client_manifest = {
|
||||
"_Qy3ajxTk.js"
|
||||
]
|
||||
},
|
||||
"pages/mitgliederbereich/api.vue": {
|
||||
"resourceType": "script",
|
||||
"module": true,
|
||||
"prefetch": true,
|
||||
"preload": true,
|
||||
"file": "r4K7wPft.js",
|
||||
"name": "api",
|
||||
"src": "pages/mitgliederbereich/api.vue",
|
||||
"isDynamicEntry": true,
|
||||
"imports": [
|
||||
"_Qy3ajxTk.js",
|
||||
"node_modules/nuxt/dist/app/entry.js"
|
||||
]
|
||||
},
|
||||
"pages/mitgliederbereich/index.vue": {
|
||||
"resourceType": "script",
|
||||
"module": true,
|
||||
@@ -721,7 +735,7 @@ const client_manifest = {
|
||||
"module": true,
|
||||
"prefetch": true,
|
||||
"preload": true,
|
||||
"file": "CGzqx_GW.js",
|
||||
"file": "BKaEDYu3.js",
|
||||
"name": "mitglieder",
|
||||
"src": "pages/mitgliederbereich/mitglieder.vue",
|
||||
"isDynamicEntry": true,
|
||||
|
||||
@@ -397,6 +397,9 @@ const unhead_k2P3m_ZDyjlr2mMYnoDPwavjsDN8hBlk9cFai0bbopU = /* @__PURE__ */ defin
|
||||
function toArray(value) {
|
||||
return Array.isArray(value) ? value : [value];
|
||||
}
|
||||
const __nuxt_page_meta$8 = {
|
||||
layout: "default"
|
||||
};
|
||||
const __nuxt_page_meta$7 = {
|
||||
layout: "default"
|
||||
};
|
||||
@@ -430,7 +433,7 @@ const _routes = [
|
||||
{
|
||||
name: "login",
|
||||
path: "/login",
|
||||
meta: __nuxt_page_meta$7 || {},
|
||||
meta: __nuxt_page_meta$8 || {},
|
||||
component: () => import('./login-P5Yg6Pmv.mjs')
|
||||
},
|
||||
{
|
||||
@@ -466,7 +469,7 @@ const _routes = [
|
||||
{
|
||||
name: "cms",
|
||||
path: "/cms",
|
||||
meta: { ...__nuxt_page_meta$6 || {}, ...{ "middleware": "auth" } },
|
||||
meta: { ...__nuxt_page_meta$7 || {}, ...{ "middleware": "auth" } },
|
||||
component: () => import('./index-CQoOFH9O.mjs')
|
||||
},
|
||||
{
|
||||
@@ -503,7 +506,7 @@ const _routes = [
|
||||
{
|
||||
name: "cms-termine",
|
||||
path: "/cms/termine",
|
||||
meta: { ...__nuxt_page_meta$5 || {}, ...{ "middleware": "auth" } },
|
||||
meta: { ...__nuxt_page_meta$6 || {}, ...{ "middleware": "auth" } },
|
||||
component: () => import('./termine-D0MhYqRR.mjs')
|
||||
},
|
||||
{
|
||||
@@ -579,7 +582,7 @@ const _routes = [
|
||||
{
|
||||
name: "cms-einstellungen",
|
||||
path: "/cms/einstellungen",
|
||||
meta: { ...__nuxt_page_meta$4 || {}, ...{ "middleware": "auth" } },
|
||||
meta: { ...__nuxt_page_meta$5 || {}, ...{ "middleware": "auth" } },
|
||||
component: () => import('./einstellungen-D4Ua-Zgq.mjs')
|
||||
},
|
||||
{
|
||||
@@ -622,6 +625,12 @@ const _routes = [
|
||||
path: "/mannschaften/jugend",
|
||||
component: () => import('./jugend-D2PYrcrQ.mjs')
|
||||
},
|
||||
{
|
||||
name: "mitgliederbereich-api",
|
||||
path: "/mitgliederbereich/api",
|
||||
meta: { ...__nuxt_page_meta$4 || {}, ...{ "middleware": "auth" } },
|
||||
component: () => import('./api-2KvLR2u_.mjs')
|
||||
},
|
||||
{
|
||||
name: "mitgliederbereich-news",
|
||||
path: "/mitgliederbereich/news",
|
||||
@@ -665,7 +674,7 @@ const _routes = [
|
||||
name: "mitgliederbereich-mitglieder",
|
||||
path: "/mitgliederbereich/mitglieder",
|
||||
meta: { ...__nuxt_page_meta || {}, ...{ "middleware": "auth" } },
|
||||
component: () => import('./mitglieder-BUT03wq4.mjs')
|
||||
component: () => import('./mitglieder-yWh5DTR0.mjs')
|
||||
}
|
||||
];
|
||||
const ROUTE_KEY_PARENTHESES_RE = /(:\w+)\([^)]+\)/g;
|
||||
@@ -1930,6 +1939,23 @@ const _sfc_main$5 = {
|
||||
}),
|
||||
_: 1
|
||||
}, _parent));
|
||||
_push(`<div class="h-3 w-px bg-primary-700"></div>`);
|
||||
_push(ssrRenderComponent(_component_NuxtLink, {
|
||||
to: "/mitgliederbereich/api",
|
||||
class: "px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all",
|
||||
"active-class": "text-white bg-primary-600"
|
||||
}, {
|
||||
default: withCtx((_, _push2, _parent2, _scopeId) => {
|
||||
if (_push2) {
|
||||
_push2(` API-Dokumentation `);
|
||||
} else {
|
||||
return [
|
||||
createTextVNode(" API-Dokumentation ")
|
||||
];
|
||||
}
|
||||
}),
|
||||
_: 1
|
||||
}, _parent));
|
||||
if (isAdmin.value) {
|
||||
_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), {
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"server.mjs","sources":["../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Ffetch.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fglobal-polyfills.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fnuxt.config.mjs","../../../../node_modules/nuxt/dist/app/nuxt.js","../../../../node_modules/nuxt/dist/app/components/injections.js","../../../../node_modules/nuxt/dist/app/utils.js","../../../../node_modules/nuxt/dist/app/composables/router.js","../../../../node_modules/nuxt/dist/app/composables/error.js","../../../../node_modules/nuxt/dist/app/composables/manifest.js","../../../../node_modules/nuxt/dist/app/composables/payload.js","../../../../node_modules/@pinia/nuxt/dist/runtime/payload-plugin.js","../../../../node_modules/nuxt/dist/head/runtime/plugins/unhead.js","../../../../node_modules/nuxt/dist/pages/runtime/utils.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Froutes.mjs","../../../../node_modules/nuxt/dist/app/components/utils.js","../../../../node_modules/nuxt/dist/pages/runtime/router.options.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Frouter.options.mjs","../../../../node_modules/nuxt/dist/pages/runtime/validate.js","../../../../stores/auth.js","../../../../middleware/auth.global.js","../../../../node_modules/nuxt/dist/app/middleware/manifest-route-rule.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fmiddleware.mjs","../../../../node_modules/nuxt/dist/pages/runtime/plugins/router.js","../../../../node_modules/nuxt/dist/app/plugins/revive-payload.server.js","../../../../node_modules/nuxt/dist/app/components/server-placeholder.js","../../../../node_modules/nuxt/dist/app/components/client-only.js","../../../../node_modules/nuxt/dist/app/components/nuxt-link.js","../../../../node_modules/@pinia/nuxt/dist/runtime/plugin.vue3.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fcomponents.plugin.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fplugins.server.mjs","../../../../node_modules/nuxt/dist/app/components/route-provider.js","../../../../node_modules/nuxt/dist/pages/runtime/page.js","../../../../assets/images/logos/Harheimer TC.svg","../../../../components/Navigation.vue","../../../../components/Footer.vue","../../../../components/ModalDialog.vue","../../../../app.vue","../../../../node_modules/nuxt/dist/app/components/nuxt-error-page.vue","../../../../node_modules/nuxt/dist/app/components/nuxt-root.vue","../../../../node_modules/nuxt/dist/app/entry.js"],"sourcesContent":null,"names":["$fetch","plugin","provide","plugins","createH3Error","createRadixRouter","login1RYyYL8mxx17qR_nmdKvywxx7lKOLXMFu8pTLfvTLYwMeta","indexBAhz9QmEiLA6QGDd6cSKraFX9E0RhZLvBUZorkJgAVkMeta","termineaIqWCm431cOVikspmAA3_6UhrHiffuFLM_t95ydtEd4Meta","einstellungen3Fhl_VQPzY4eklctozY70Drhj8cWMt_FVlyEKQh9aAAMeta","newsPHTXuxdDl4fgPHQQbukE_XsTEemyPOWVSYs64f8qjC4Meta","indexqVbusfljIJ04j42RIA_KP4bSP7XytQIXdqcVfR3kPUAMeta","profilyVBy_UvZ8KvchY44_0SJbB0NHOad6MC_S9C8wfDdUWEMeta","mitglieder9TWncRC_sSS_zPWzxBDlRiNjjFojXSJny_uernMe_tAMeta","__executeAsync","createRouter","entry","payload_plugin_1_bEQpMjikuQhbV8UJ0PxUqmSvPdmV1jDa5DURnKW4M","router_GNCWhvtYfLTYRZZ135CdFAEjxdMexN0ixiUYCAN_tpw","plugin_vue3_CQ_pO3THrTGIeYc0dvC91V75hY8qpo9B_8yZzOW5SFs","useRoute","_ssrRenderAttrs","_mergeProps","_push","_parent","_ssrRenderAttr","_imports_0","_createVNode","_ssrRenderClass","_unref","_ssrRenderList","_ssrInterpolate","_createTextVNode","_toDisplayString","useRouter","_ssrRenderComponent","showError","ErrorComponent","RootComponent"],"mappings":"","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,20,21,22,23,24,25,26,27,28,29,30,31,37,38,39]}
|
||||
{"version":3,"file":"server.mjs","sources":["../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Ffetch.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fglobal-polyfills.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fnuxt.config.mjs","../../../../node_modules/nuxt/dist/app/nuxt.js","../../../../node_modules/nuxt/dist/app/components/injections.js","../../../../node_modules/nuxt/dist/app/utils.js","../../../../node_modules/nuxt/dist/app/composables/router.js","../../../../node_modules/nuxt/dist/app/composables/error.js","../../../../node_modules/nuxt/dist/app/composables/manifest.js","../../../../node_modules/nuxt/dist/app/composables/payload.js","../../../../node_modules/@pinia/nuxt/dist/runtime/payload-plugin.js","../../../../node_modules/nuxt/dist/head/runtime/plugins/unhead.js","../../../../node_modules/nuxt/dist/pages/runtime/utils.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Froutes.mjs","../../../../node_modules/nuxt/dist/app/components/utils.js","../../../../node_modules/nuxt/dist/pages/runtime/router.options.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Frouter.options.mjs","../../../../node_modules/nuxt/dist/pages/runtime/validate.js","../../../../stores/auth.js","../../../../middleware/auth.global.js","../../../../node_modules/nuxt/dist/app/middleware/manifest-route-rule.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fmiddleware.mjs","../../../../node_modules/nuxt/dist/pages/runtime/plugins/router.js","../../../../node_modules/nuxt/dist/app/plugins/revive-payload.server.js","../../../../node_modules/nuxt/dist/app/components/server-placeholder.js","../../../../node_modules/nuxt/dist/app/components/client-only.js","../../../../node_modules/nuxt/dist/app/components/nuxt-link.js","../../../../node_modules/@pinia/nuxt/dist/runtime/plugin.vue3.js","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fcomponents.plugin.mjs","../../../../virtual:nuxt:%2Fmnt%2Fshare%2Ftorsten%2FPrograms%2Fharheimertc%2Fnode_modules%2F.cache%2Fnuxt%2F.nuxt%2Fplugins.server.mjs","../../../../node_modules/nuxt/dist/app/components/route-provider.js","../../../../node_modules/nuxt/dist/pages/runtime/page.js","../../../../assets/images/logos/Harheimer TC.svg","../../../../components/Navigation.vue","../../../../components/Footer.vue","../../../../components/ModalDialog.vue","../../../../app.vue","../../../../node_modules/nuxt/dist/app/components/nuxt-error-page.vue","../../../../node_modules/nuxt/dist/app/components/nuxt-root.vue","../../../../node_modules/nuxt/dist/app/entry.js"],"sourcesContent":null,"names":["$fetch","plugin","provide","plugins","createH3Error","createRadixRouter","login1RYyYL8mxx17qR_nmdKvywxx7lKOLXMFu8pTLfvTLYwMeta","indexBAhz9QmEiLA6QGDd6cSKraFX9E0RhZLvBUZorkJgAVkMeta","termineaIqWCm431cOVikspmAA3_6UhrHiffuFLM_t95ydtEd4Meta","einstellungen3Fhl_VQPzY4eklctozY70Drhj8cWMt_FVlyEKQh9aAAMeta","apis3_Iku1IZkQQ3FilRpoRZFd20ZCr3RAgIorGyDHhuNIMeta","newsPHTXuxdDl4fgPHQQbukE_XsTEemyPOWVSYs64f8qjC4Meta","indexqVbusfljIJ04j42RIA_KP4bSP7XytQIXdqcVfR3kPUAMeta","profilyVBy_UvZ8KvchY44_0SJbB0NHOad6MC_S9C8wfDdUWEMeta","mitglieder9TWncRC_sSS_zPWzxBDlRiNjjFojXSJny_uernMe_tAMeta","__executeAsync","createRouter","entry","payload_plugin_1_bEQpMjikuQhbV8UJ0PxUqmSvPdmV1jDa5DURnKW4M","router_GNCWhvtYfLTYRZZ135CdFAEjxdMexN0ixiUYCAN_tpw","plugin_vue3_CQ_pO3THrTGIeYc0dvC91V75hY8qpo9B_8yZzOW5SFs","useRoute","_ssrRenderAttrs","_mergeProps","_push","_parent","_ssrRenderAttr","_imports_0","_createVNode","_ssrRenderClass","_unref","_ssrRenderList","_ssrInterpolate","_createTextVNode","_toDisplayString","useRouter","_ssrRenderComponent","showError","ErrorComponent","RootComponent"],"mappings":"","x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,20,21,22,23,24,25,26,27,28,29,30,31,37,38,39]}
|
||||
@@ -4,9 +4,9 @@ const styles = {
|
||||
"node_modules/nuxt/dist/app/components/error-500.vue": () => import('./error-500-styles.Dccc6iq5.mjs').then(interopDefault),
|
||||
"components/Hero.vue": () => import('./Hero-styles.CNXkzfUC.mjs').then(interopDefault),
|
||||
"components/PublicNews.vue": () => import('./PublicNews-styles.Muc48wAK.mjs').then(interopDefault),
|
||||
"components/Hero.vue?vue&type=style&index=0&scoped=28200092&lang.css": () => import('./Hero-styles.CNXkzfUC.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),
|
||||
"components/Hero.vue?vue&type=style&index=0&scoped=28200092&lang.css": () => import('./Hero-styles.CNXkzfUC.mjs').then(interopDefault),
|
||||
"components/PublicNews.vue?vue&type=style&index=0&scoped=c57f605c&lang.css": () => import('./PublicNews-styles.Muc48wAK.mjs').then(interopDefault)
|
||||
};
|
||||
|
||||
|
||||
@@ -4309,7 +4309,7 @@ function _expandFromEnv(value) {
|
||||
const _inlineRuntimeConfig = {
|
||||
"app": {
|
||||
"baseURL": "/",
|
||||
"buildId": "9438b35d-0d10-4203-a329-f7a1e287e2a1",
|
||||
"buildId": "8af76f9e-6e85-416c-9e2f-92c68e0dfd76",
|
||||
"buildAssetsDir": "/_nuxt/",
|
||||
"cdnURL": ""
|
||||
},
|
||||
@@ -4763,663 +4763,670 @@ const plugins = [
|
||||
];
|
||||
|
||||
const assets = {
|
||||
"/documents/Tischtennisregeln light.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"5177b-y/88q2+Y3RRechJMqWhse21KRdQ\"",
|
||||
"mtime": "2025-11-05T12:46:41.629Z",
|
||||
"size": 333691,
|
||||
"path": "../public/documents/Tischtennisregeln light.pdf"
|
||||
},
|
||||
"/documents/beitrittserklärung_template.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"6ac0-KgukOaJtKwdNaJOX7uoc6iYn7og\"",
|
||||
"mtime": "2025-11-05T12:46:41.630Z",
|
||||
"size": 27328,
|
||||
"path": "../public/documents/beitrittserklärung_template.pdf"
|
||||
},
|
||||
"/documents/satzung.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"5c7cf-L0A3nT8D24T9sD57FFbij3QRpzw\"",
|
||||
"mtime": "2025-11-05T12:46:41.630Z",
|
||||
"size": 378831,
|
||||
"path": "../public/documents/satzung.pdf"
|
||||
"/images/club_about_us.png": {
|
||||
"type": "image/png",
|
||||
"etag": "\"202e56-s4fLsHEgoAgKJeBRuI1qxPmqHV0\"",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 2109014,
|
||||
"path": "../public/images/club_about_us.png"
|
||||
},
|
||||
"/data/mannschaften.csv": {
|
||||
"type": "text/csv; charset=utf-8",
|
||||
"etag": "\"858-l94GKn8Q0I5RQnhrM0ZPJsYUmcw\"",
|
||||
"mtime": "2025-11-05T12:46:41.629Z",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 2136,
|
||||
"path": "../public/data/mannschaften.csv"
|
||||
},
|
||||
"/data/spielplan.csv": {
|
||||
"type": "text/csv; charset=utf-8",
|
||||
"etag": "\"8d38-QOShwHtHkSWtRusSJQlU5hXJcmk\"",
|
||||
"mtime": "2025-11-05T12:46:41.629Z",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 36152,
|
||||
"path": "../public/data/spielplan.csv"
|
||||
},
|
||||
"/data/spielsysteme.csv": {
|
||||
"type": "text/csv; charset=utf-8",
|
||||
"etag": "\"9bc-4npLrNHYClsD0TKV5vSifxitfV0\"",
|
||||
"mtime": "2025-11-05T12:46:41.629Z",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 2492,
|
||||
"path": "../public/data/spielsysteme.csv"
|
||||
},
|
||||
"/data/termine.csv": {
|
||||
"type": "text/csv; charset=utf-8",
|
||||
"etag": "\"a3-/pLg8n6Q4Adh8q/vZuoBiMsPHKY\"",
|
||||
"mtime": "2025-11-05T12:46:41.629Z",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 163,
|
||||
"path": "../public/data/termine.csv"
|
||||
},
|
||||
"/data/vereinsmeisterschaften.csv": {
|
||||
"type": "text/csv; charset=utf-8",
|
||||
"etag": "\"99b-ovCRxbAET3nNRs52LvINNjQLWPY\"",
|
||||
"mtime": "2025-11-05T12:46:41.629Z",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 2459,
|
||||
"path": "../public/data/vereinsmeisterschaften.csv"
|
||||
},
|
||||
"/images/club_about_us.png": {
|
||||
"type": "image/png",
|
||||
"etag": "\"202e56-s4fLsHEgoAgKJeBRuI1qxPmqHV0\"",
|
||||
"mtime": "2025-11-05T12:46:41.629Z",
|
||||
"size": 2109014,
|
||||
"path": "../public/images/club_about_us.png"
|
||||
},
|
||||
"/spielplaene/1. Mannschaft 2025⁄2026.pdf": {
|
||||
"/documents/Tischtennisregeln light.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"64c6-+477M+gD/spwpWR9NO/tMJ/inCc\"",
|
||||
"mtime": "2025-11-05T12:46:41.629Z",
|
||||
"size": 25798,
|
||||
"path": "../public/spielplaene/1. Mannschaft 2025⁄2026.pdf"
|
||||
"etag": "\"5177b-y/88q2+Y3RRechJMqWhse21KRdQ\"",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 333691,
|
||||
"path": "../public/documents/Tischtennisregeln light.pdf"
|
||||
},
|
||||
"/spielplaene/2. Mannschaft 2025⁄2026.pdf": {
|
||||
"/documents/beitrittserklärung_template.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"5bfa-DRJMHLV15iss67lEISoGqSYmZjE\"",
|
||||
"mtime": "2025-11-05T12:46:41.630Z",
|
||||
"size": 23546,
|
||||
"path": "../public/spielplaene/2. Mannschaft 2025⁄2026.pdf"
|
||||
"etag": "\"6ac0-KgukOaJtKwdNaJOX7uoc6iYn7og\"",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 27328,
|
||||
"path": "../public/documents/beitrittserklärung_template.pdf"
|
||||
},
|
||||
"/spielplaene/3. Mannschaft 2025⁄2026.pdf": {
|
||||
"/documents/satzung.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"7447-w933CPQdXhkWJ2AZOVdY0UgJnPo\"",
|
||||
"mtime": "2025-11-05T12:46:41.630Z",
|
||||
"size": 29767,
|
||||
"path": "../public/spielplaene/3. Mannschaft 2025⁄2026.pdf"
|
||||
},
|
||||
"/spielplaene/4. Mannschaft 2025⁄2026.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"6a9b-4TPGn1yQlFUMRj7oB43SN//Np9o\"",
|
||||
"mtime": "2025-11-05T12:46:41.630Z",
|
||||
"size": 27291,
|
||||
"path": "../public/spielplaene/4. Mannschaft 2025⁄2026.pdf"
|
||||
},
|
||||
"/spielplaene/5. Mannschaft 2025⁄2026.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"6523-5VUfCMaoiNhcwHhptHHTVJ3lSwQ\"",
|
||||
"mtime": "2025-11-05T12:46:41.630Z",
|
||||
"size": 25891,
|
||||
"path": "../public/spielplaene/5. Mannschaft 2025⁄2026.pdf"
|
||||
},
|
||||
"/spielplaene/Jugend 11 2025⁄2026.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"52e9-3Rrk9UKUxPh80pBJ0w9oLVbe5dA\"",
|
||||
"mtime": "2025-11-05T12:46:41.630Z",
|
||||
"size": 21225,
|
||||
"path": "../public/spielplaene/Jugend 11 2025⁄2026.pdf"
|
||||
"etag": "\"5c7cf-L0A3nT8D24T9sD57FFbij3QRpzw\"",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 378831,
|
||||
"path": "../public/documents/satzung.pdf"
|
||||
},
|
||||
"/_nuxt/48ve60fm.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"557-e04q6bcr4Wja5BTiX/uCY9jcaOk\"",
|
||||
"mtime": "2025-11-05T12:46:41.624Z",
|
||||
"mtime": "2025-11-05T13:24:46.321Z",
|
||||
"size": 1367,
|
||||
"path": "../public/_nuxt/48ve60fm.js"
|
||||
},
|
||||
"/_nuxt/6EY4_GXp.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"19e-CzjelZROfgCPWyXIjP0DPtd+UHQ\"",
|
||||
"mtime": "2025-11-05T12:46:41.624Z",
|
||||
"mtime": "2025-11-05T13:24:46.321Z",
|
||||
"size": 414,
|
||||
"path": "../public/_nuxt/6EY4_GXp.js"
|
||||
},
|
||||
"/_nuxt/9IiqlUD-.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"2835-WiVWi4XZDT1Nsy21w92+uGqrUhI\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.321Z",
|
||||
"size": 10293,
|
||||
"path": "../public/_nuxt/9IiqlUD-.js"
|
||||
},
|
||||
"/_nuxt/B0O50Q40.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"2346-VOt5BwQrjzVpCVYv0biaieLYXbw\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.321Z",
|
||||
"size": 9030,
|
||||
"path": "../public/_nuxt/B0O50Q40.js"
|
||||
},
|
||||
"/_nuxt/B1VMaWHv.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"16a7-ABFT0Hkz1mWSRL8qDQgQuhCoswc\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.321Z",
|
||||
"size": 5799,
|
||||
"path": "../public/_nuxt/B1VMaWHv.js"
|
||||
},
|
||||
"/_nuxt/B4mSF5Ac.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"185-hHs3mU4qOcQAkGQaPrUYGaG0yao\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.321Z",
|
||||
"size": 389,
|
||||
"path": "../public/_nuxt/B4mSF5Ac.js"
|
||||
},
|
||||
"/_nuxt/B9LKmM6w.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"2970-DQ16XS35U2+PB0IaZFhNA7g4mJk\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.321Z",
|
||||
"size": 10608,
|
||||
"path": "../public/_nuxt/B9LKmM6w.js"
|
||||
},
|
||||
"/_nuxt/B9lUQxCj.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"c21-cIS5lR8JDKJkr1N/Y8gKIIRgLqI\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 3105,
|
||||
"path": "../public/_nuxt/B9lUQxCj.js"
|
||||
},
|
||||
"/_nuxt/BC4PNGtJ.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"175-33lu59Ps/+kwbPv/hVeUdrq4wmI\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 373,
|
||||
"path": "../public/_nuxt/BC4PNGtJ.js"
|
||||
},
|
||||
"/_nuxt/BIgEmhqk.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"3513-NZ/G+tsRsF+o3kKxOyYiTJ+ozJ0\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 13587,
|
||||
"path": "../public/_nuxt/BIgEmhqk.js"
|
||||
},
|
||||
"/_nuxt/BKaEDYu3.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"5916-HkP5iPO4XP/4n2fQHChYeSwJHKc\"",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 22806,
|
||||
"path": "../public/_nuxt/BKaEDYu3.js"
|
||||
},
|
||||
"/_nuxt/BOhpfOYZ.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"b10-T1pZGL5tW1rfqWHftATE5VhNr1g\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 2832,
|
||||
"path": "../public/_nuxt/BOhpfOYZ.js"
|
||||
},
|
||||
"/_nuxt/BQ1JAN-t.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"4d4-D2c9LzMtu3t/oTdiDqO210OhOq0\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 1236,
|
||||
"path": "../public/_nuxt/BQ1JAN-t.js"
|
||||
},
|
||||
"/_nuxt/BRIjNg-H.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"2820-w8/G0Dy6wRFecNHJSR+NwEJ2AeE\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 10272,
|
||||
"path": "../public/_nuxt/BRIjNg-H.js"
|
||||
},
|
||||
"/_nuxt/BSgvdOgQ.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1827-zMnsckCG+udmw6Jb4QbWFvVU40I\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 6183,
|
||||
"path": "../public/_nuxt/BSgvdOgQ.js"
|
||||
},
|
||||
"/_nuxt/BVRiFo7f.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"dbf-bs5Lp4co8JtdiCR4NBZg2xEEpZE\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 3519,
|
||||
"path": "../public/_nuxt/BVRiFo7f.js"
|
||||
},
|
||||
"/_nuxt/BWE4JIU7.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"d97-Mz4vlsBagIqTc5OFwjogM6kfacU\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 3479,
|
||||
"path": "../public/_nuxt/BWE4JIU7.js"
|
||||
},
|
||||
"/_nuxt/BWWcyQAZ.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"685-pCeqOgBg8QXGBjOkvDQ5Miivcwk\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 1669,
|
||||
"path": "../public/_nuxt/BWWcyQAZ.js"
|
||||
},
|
||||
"/_nuxt/BeFU4xfJ.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1744-/7IohGje8DDLlxCyMiEkw3Thq+I\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 5956,
|
||||
"path": "../public/_nuxt/BeFU4xfJ.js"
|
||||
},
|
||||
"/_nuxt/Bg-xR82b.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1860-q2IZycL1/cXKqiR+YJN8gMIGQ70\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 6240,
|
||||
"path": "../public/_nuxt/Bg-xR82b.js"
|
||||
},
|
||||
"/_nuxt/BihH_yuU.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"dbb-d//fGgPcM4CQDXXuyaBVA0etrcE\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 3515,
|
||||
"path": "../public/_nuxt/BihH_yuU.js"
|
||||
},
|
||||
"/_nuxt/BteKZQ9T.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1ea-kmrGdt5SPmt15EiBI7kR9gXMQM0\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 490,
|
||||
"path": "../public/_nuxt/BteKZQ9T.js"
|
||||
},
|
||||
"/_nuxt/BuN7Api-.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"dcb-vEM3YBEgZbfkrV6Gw0ZoyLCb21I\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 3531,
|
||||
"path": "../public/_nuxt/BuN7Api-.js"
|
||||
},
|
||||
"/_nuxt/BzVlU_Az.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"3b44-YIew5nSdOke4bV7T76Jp4fRwf4k\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 15172,
|
||||
"path": "../public/_nuxt/BzVlU_Az.js"
|
||||
},
|
||||
"/_nuxt/C0hTJwq1.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1588-AjSEZ5SBctJSg8q99Sy8Zj2YxXY\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 5512,
|
||||
"path": "../public/_nuxt/C0hTJwq1.js"
|
||||
},
|
||||
"/_nuxt/C27DLx1c.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"19ec-tkDTgwwh9gT7TzWUiNpwZMn2Uhc\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 6636,
|
||||
"path": "../public/_nuxt/C27DLx1c.js"
|
||||
},
|
||||
"/_nuxt/C5SyyWEb.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"2a5-06iX+CL3i0ysaqW9nu7Eg2YzDhQ\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 677,
|
||||
"path": "../public/_nuxt/C5SyyWEb.js"
|
||||
},
|
||||
"/_nuxt/C8kQt0fa.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"197-7X99z1xphxry8OnMwU7Ofs/uE0Q\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 407,
|
||||
"path": "../public/_nuxt/C8kQt0fa.js"
|
||||
},
|
||||
"/_nuxt/CED8cBGM.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1c75-5CrjjdvSrpIsV0jgPgmLQ/Ag8Kw\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 7285,
|
||||
"path": "../public/_nuxt/CED8cBGM.js"
|
||||
},
|
||||
"/_nuxt/CGzqx_GW.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"33b7-szQpwlQCIrWy2Yt6HyekQOiyQu4\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"size": 13239,
|
||||
"path": "../public/_nuxt/CGzqx_GW.js"
|
||||
},
|
||||
"/_nuxt/CHQH_CwJ.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1168-mBOIwVjM5LsxIx13Cs7oT3+LJ+k\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 4456,
|
||||
"path": "../public/_nuxt/CHQH_CwJ.js"
|
||||
},
|
||||
"/_nuxt/CHkqy7mB.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"201d-h4fS2PBhZIObIxeqvtLlHIK7w00\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 8221,
|
||||
"path": "../public/_nuxt/CHkqy7mB.js"
|
||||
},
|
||||
"/_nuxt/CUq_0rkE.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"12d-JV4KW1fgT85/V3Ap13X4q2h9U3g\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 301,
|
||||
"path": "../public/_nuxt/CUq_0rkE.js"
|
||||
},
|
||||
"/_nuxt/Cb8HxufC.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"406f-TyhzTK47O3LPFk0luA/Fak0NoYQ\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 16495,
|
||||
"path": "../public/_nuxt/Cb8HxufC.js"
|
||||
},
|
||||
"/_nuxt/CbiYaI1j.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"faa-35cv2+MngLgY4pLUjpDBDLx6vV0\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 4010,
|
||||
"path": "../public/_nuxt/CbiYaI1j.js"
|
||||
},
|
||||
"/_nuxt/CgxdOZQk.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"16e6-XCN/eSTrNul1blsGspgsYeA873w\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 5862,
|
||||
"path": "../public/_nuxt/CgxdOZQk.js"
|
||||
},
|
||||
"/_nuxt/Ci7R5KNW.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"2320-2xahv071b+7opOi1O0ljQRcwIBY\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 8992,
|
||||
"path": "../public/_nuxt/Ci7R5KNW.js"
|
||||
},
|
||||
"/_nuxt/CkzaQq3X.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"17d-+xKrHjeww4bpFFkkjUNLD/ebn5A\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 381,
|
||||
"path": "../public/_nuxt/CkzaQq3X.js"
|
||||
},
|
||||
"/_nuxt/CrCcIvVp.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"274-9U7hEMtgHqdnQopnKeJsBKqKyKw\"",
|
||||
"mtime": "2025-11-05T12:46:41.625Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 628,
|
||||
"path": "../public/_nuxt/CrCcIvVp.js"
|
||||
},
|
||||
"/_nuxt/CtbKwxql.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"3090-xfpT19k0ndSinB29602tM2WBsKA\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 12432,
|
||||
"path": "../public/_nuxt/CtbKwxql.js"
|
||||
},
|
||||
"/_nuxt/CxKxaas7.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"35ed1-HTnyh8f24j/i4Ubtc6/SgRBeEaY\"",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 220881,
|
||||
"path": "../public/_nuxt/CxKxaas7.js"
|
||||
},
|
||||
"/_nuxt/DAACT36i.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1f9-dVOk5jAwb0VlMLJevIcT+s2NTgM\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 505,
|
||||
"path": "../public/_nuxt/DAACT36i.js"
|
||||
},
|
||||
"/_nuxt/DB4lwRH4.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"285-Fn2A1Bk4lxBPdxYBguEMM5AsI0M\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 645,
|
||||
"path": "../public/_nuxt/DB4lwRH4.js"
|
||||
},
|
||||
"/_nuxt/DEC6XYYJ.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"37e2-qLNdHj2LfvfQ4r7oP9pxJ4234W8\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 14306,
|
||||
"path": "../public/_nuxt/DEC6XYYJ.js"
|
||||
},
|
||||
"/_nuxt/DODWL2Pr.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"e7c-ZCkB0/XZCFSnRy2p9SAs25cftgQ\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.322Z",
|
||||
"size": 3708,
|
||||
"path": "../public/_nuxt/DODWL2Pr.js"
|
||||
},
|
||||
"/_nuxt/DTKApGrI.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1e3a-m8ONRzHj9hcIv08Kb9ZDGKcKKgE\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 7738,
|
||||
"path": "../public/_nuxt/DTKApGrI.js"
|
||||
},
|
||||
"/_nuxt/DUm-savV.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"378-gmau5tfuGGvvXo5HRs29CR7slTs\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 888,
|
||||
"path": "../public/_nuxt/DUm-savV.js"
|
||||
},
|
||||
"/_nuxt/DaSgy0Cl.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"11f-soKnh1qfNJj5nvt+IcgQXYvg/z4\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 287,
|
||||
"path": "../public/_nuxt/DaSgy0Cl.js"
|
||||
},
|
||||
"/_nuxt/DaUHoOti.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"177f-xSuV2n3wrsWDBysy5o9XsakrXDw\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 6015,
|
||||
"path": "../public/_nuxt/DaUHoOti.js"
|
||||
},
|
||||
"/_nuxt/DdHhmCne.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"22d-uKYfhsDcUsz2NrXOJmxptUGZdyE\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 557,
|
||||
"path": "../public/_nuxt/DdHhmCne.js"
|
||||
},
|
||||
"/_nuxt/Dhn1q0tB.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"280-soHXdceT8+kvIFtgcE96cO/TCIU\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 640,
|
||||
"path": "../public/_nuxt/Dhn1q0tB.js"
|
||||
},
|
||||
"/_nuxt/DkeYb0_S.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1ce-xiaAbRvqQ+zffTXF3Gc7rq14R0U\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 462,
|
||||
"path": "../public/_nuxt/DkeYb0_S.js"
|
||||
},
|
||||
"/_nuxt/DlAUqK2U.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"5b-eFCz/UrraTh721pgAl0VxBNR1es\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 91,
|
||||
"path": "../public/_nuxt/DlAUqK2U.js"
|
||||
},
|
||||
"/_nuxt/DoQDnat2.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"120d-q7H0efEMaeQo04Ily4N/PFQjhpA\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 4621,
|
||||
"path": "../public/_nuxt/DoQDnat2.js"
|
||||
},
|
||||
"/_nuxt/DruLqh86.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"bf0-+1jS/uLKW36S7SjKCe7lwpzJbjw\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 3056,
|
||||
"path": "../public/_nuxt/DruLqh86.js"
|
||||
},
|
||||
"/_nuxt/Dy4dzDTt.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"e61-TfoI+tUM1IJUgJ3e0hEAuF/ainI\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 3681,
|
||||
"path": "../public/_nuxt/Dy4dzDTt.js"
|
||||
},
|
||||
"/_nuxt/FF_cyd6S.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"203-rairGPfunSg/yCk4txod3zRSZus\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 515,
|
||||
"path": "../public/_nuxt/FF_cyd6S.js"
|
||||
},
|
||||
"/_nuxt/FwQxzXsL.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"4c6b-WDmRZ/fG0kITIIN7KN6MuTnrzVc\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 19563,
|
||||
"path": "../public/_nuxt/FwQxzXsL.js"
|
||||
},
|
||||
"/_nuxt/GPPAkzQU.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"24cb-KnKpZhja4N6Fk7TQXNuaa4j4dms\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 9419,
|
||||
"path": "../public/_nuxt/GPPAkzQU.js"
|
||||
},
|
||||
"/_nuxt/Harheimer TC.CKfYAfp1.svg": {
|
||||
"type": "image/svg+xml",
|
||||
"etag": "\"1d2535-Tx2lTuuFn2hBqGZOnDan3/OdRU0\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 1910069,
|
||||
"path": "../public/_nuxt/Harheimer TC.CKfYAfp1.svg"
|
||||
},
|
||||
"/_nuxt/IxY-j45w.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1b54-yd8vSMFwTUtDVcZx0AViG0asyoM\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 6996,
|
||||
"path": "../public/_nuxt/IxY-j45w.js"
|
||||
},
|
||||
"/_nuxt/KxVBmS-6.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"190-F0LVEAqwB2LwyGzW0v9yzLx0v/0\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 400,
|
||||
"path": "../public/_nuxt/KxVBmS-6.js"
|
||||
},
|
||||
"/_nuxt/PsGBFO20.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"34fa-VfXYDHH2Csr24waHFWok/UZ+AUQ\"",
|
||||
"mtime": "2025-11-05T12:46:41.626Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 13562,
|
||||
"path": "../public/_nuxt/PsGBFO20.js"
|
||||
},
|
||||
"/_nuxt/Qy3ajxTk.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"13f-LgrH17St2xFg+RPGvT3uJRaRfFw\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 319,
|
||||
"path": "../public/_nuxt/Qy3ajxTk.js"
|
||||
},
|
||||
"/_nuxt/R6Iy1jPP.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"137-QCUizOitouzMVC2drCYFTAZmqPU\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 311,
|
||||
"path": "../public/_nuxt/R6Iy1jPP.js"
|
||||
},
|
||||
"/_nuxt/YJHbYJtA.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"19f-nQw578pUen9o8yYaMA8Bwag6xho\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 415,
|
||||
"path": "../public/_nuxt/YJHbYJtA.js"
|
||||
},
|
||||
"/_nuxt/ZrOCUSmD.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"ee2-knvq//8tl4tcmRjFy6nWAy0dRBk\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 3810,
|
||||
"path": "../public/_nuxt/ZrOCUSmD.js"
|
||||
},
|
||||
"/_nuxt/entry.CYvTzE5H.css": {
|
||||
"/_nuxt/entry.wEUbgGlA.css": {
|
||||
"type": "text/css; charset=utf-8",
|
||||
"etag": "\"d359-K8d7YngVKCHFgXk5rvr7JvAWgRM\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"size": 54105,
|
||||
"path": "../public/_nuxt/entry.CYvTzE5H.css"
|
||||
"etag": "\"d470-jKdybOkNd/weF4HvmA1i+mSz4pw\"",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 54384,
|
||||
"path": "../public/_nuxt/entry.wEUbgGlA.css"
|
||||
},
|
||||
"/_nuxt/error-404.CbXQcqJW.css": {
|
||||
"type": "text/css; charset=utf-8",
|
||||
"etag": "\"97e-Ty5bTTSEudJkO/DsGUoIf37xYxc\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"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-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 1907,
|
||||
"path": "../public/_nuxt/error-500.L485xXhD.css"
|
||||
},
|
||||
"/_nuxt/index.BfDdQit3.css": {
|
||||
"type": "text/css; charset=utf-8",
|
||||
"etag": "\"1db-8CoCw55otGXQVF3jz4vWPcGWmuY\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 475,
|
||||
"path": "../public/_nuxt/index.BfDdQit3.css"
|
||||
},
|
||||
"/_nuxt/oL_Xi1h-.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"35cb0-7x109VLkapMthU3oJP0Ty7wYQpk\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"size": 220336,
|
||||
"path": "../public/_nuxt/oL_Xi1h-.js"
|
||||
},
|
||||
"/_nuxt/oN0_bS6A.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1d3-DKhiaT2RUlSXk55jBttctUuTQQI\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 467,
|
||||
"path": "../public/_nuxt/oN0_bS6A.js"
|
||||
},
|
||||
"/_nuxt/r4K7wPft.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"4c51-7zSVu+BLyGsMT1uGYy/MrmouUkg\"",
|
||||
"mtime": "2025-11-05T13:24:46.323Z",
|
||||
"size": 19537,
|
||||
"path": "../public/_nuxt/r4K7wPft.js"
|
||||
},
|
||||
"/_nuxt/rVkivqmM.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"161a-zK8c8X4CzA56nT/n6kqKbIto9uw\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.324Z",
|
||||
"size": 5658,
|
||||
"path": "../public/_nuxt/rVkivqmM.js"
|
||||
},
|
||||
"/_nuxt/t1jwZBF5.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"1dc5-WYQUOK4isA/E8pj6xugVdZaBBKo\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.324Z",
|
||||
"size": 7621,
|
||||
"path": "../public/_nuxt/t1jwZBF5.js"
|
||||
},
|
||||
"/_nuxt/t6YtlcxA.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"575-I2e3m9LKSy82cY3tyiVI/I7zXz4\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.324Z",
|
||||
"size": 1397,
|
||||
"path": "../public/_nuxt/t6YtlcxA.js"
|
||||
},
|
||||
"/_nuxt/tgU1A5jl.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"576-TwB1Mj1tLB6Z9zhK9LTHVXbtW8k\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.324Z",
|
||||
"size": 1398,
|
||||
"path": "../public/_nuxt/tgU1A5jl.js"
|
||||
},
|
||||
"/_nuxt/vygoqABK.js": {
|
||||
"type": "text/javascript; charset=utf-8",
|
||||
"etag": "\"28f-AjrUgCN/auxu+4F13IlxhdOmeFk\"",
|
||||
"mtime": "2025-11-05T12:46:41.627Z",
|
||||
"mtime": "2025-11-05T13:24:46.324Z",
|
||||
"size": 655,
|
||||
"path": "../public/_nuxt/vygoqABK.js"
|
||||
},
|
||||
"/spielplaene/1. Mannschaft 2025⁄2026.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"64c6-+477M+gD/spwpWR9NO/tMJ/inCc\"",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 25798,
|
||||
"path": "../public/spielplaene/1. Mannschaft 2025⁄2026.pdf"
|
||||
},
|
||||
"/spielplaene/2. Mannschaft 2025⁄2026.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"5bfa-DRJMHLV15iss67lEISoGqSYmZjE\"",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 23546,
|
||||
"path": "../public/spielplaene/2. Mannschaft 2025⁄2026.pdf"
|
||||
},
|
||||
"/spielplaene/3. Mannschaft 2025⁄2026.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"7447-w933CPQdXhkWJ2AZOVdY0UgJnPo\"",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 29767,
|
||||
"path": "../public/spielplaene/3. Mannschaft 2025⁄2026.pdf"
|
||||
},
|
||||
"/spielplaene/4. Mannschaft 2025⁄2026.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"6a9b-4TPGn1yQlFUMRj7oB43SN//Np9o\"",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 27291,
|
||||
"path": "../public/spielplaene/4. Mannschaft 2025⁄2026.pdf"
|
||||
},
|
||||
"/spielplaene/5. Mannschaft 2025⁄2026.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"6523-5VUfCMaoiNhcwHhptHHTVJ3lSwQ\"",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 25891,
|
||||
"path": "../public/spielplaene/5. Mannschaft 2025⁄2026.pdf"
|
||||
},
|
||||
"/spielplaene/Jugend 11 2025⁄2026.pdf": {
|
||||
"type": "application/pdf",
|
||||
"etag": "\"52e9-3Rrk9UKUxPh80pBJ0w9oLVbe5dA\"",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 21225,
|
||||
"path": "../public/spielplaene/Jugend 11 2025⁄2026.pdf"
|
||||
},
|
||||
"/documents/spielplaene/README.md": {
|
||||
"type": "text/markdown; charset=utf-8",
|
||||
"etag": "\"229-MzuxTQlQrWciSShfjF7Fr2QGclE\"",
|
||||
"mtime": "2025-11-05T12:46:41.629Z",
|
||||
"mtime": "2025-11-05T13:24:46.326Z",
|
||||
"size": 553,
|
||||
"path": "../public/documents/spielplaene/README.md"
|
||||
},
|
||||
"/_nuxt/builds/latest.json": {
|
||||
"type": "application/json",
|
||||
"etag": "\"47-DSEOTikTqgipcQKtRl6C4eJX/eM\"",
|
||||
"mtime": "2025-11-05T12:46:41.615Z",
|
||||
"etag": "\"47-VzWK2iAeXt6NcwoFjDqObFk+lQ4\"",
|
||||
"mtime": "2025-11-05T13:24:46.310Z",
|
||||
"size": 71,
|
||||
"path": "../public/_nuxt/builds/latest.json"
|
||||
},
|
||||
"/_nuxt/builds/meta/9438b35d-0d10-4203-a329-f7a1e287e2a1.json": {
|
||||
"/_nuxt/builds/meta/8af76f9e-6e85-416c-9e2f-92c68e0dfd76.json": {
|
||||
"type": "application/json",
|
||||
"etag": "\"8b-J28GxNhENWmuwxbIXkR3zmEQygk\"",
|
||||
"mtime": "2025-11-05T12:46:41.613Z",
|
||||
"etag": "\"8b-cx0wXpS3b8fz/gEJyMvOnottRyQ\"",
|
||||
"mtime": "2025-11-05T13:24:46.308Z",
|
||||
"size": 139,
|
||||
"path": "../public/_nuxt/builds/meta/9438b35d-0d10-4203-a329-f7a1e287e2a1.json"
|
||||
"path": "../public/_nuxt/builds/meta/8af76f9e-6e85-416c-9e2f-92c68e0dfd76.json"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5652,6 +5659,7 @@ const _lazy_EK_x5_ = () => import('../routes/api/galerie.get.mjs');
|
||||
const _lazy_jYLuY1 = () => import('../routes/api/members.delete.mjs');
|
||||
const _lazy_HGAbG3 = () => import('../routes/api/members.get.mjs');
|
||||
const _lazy_XNetVh = () => import('../routes/api/members.post.mjs');
|
||||
const _lazy_HlxJ2p = () => import('../routes/api/members/bulk.post.mjs');
|
||||
const _lazy_AgsX_N = () => import('../routes/api/membership/applications.get.mjs');
|
||||
const _lazy_7df2qh = () => import('../routes/api/membership/download/_id_.get.mjs');
|
||||
const _lazy_Od0rSJ = () => import('../routes/api/membership/generate-pdf.post.mjs');
|
||||
@@ -5694,6 +5702,7 @@ const handlers = [
|
||||
{ route: '/api/members', handler: _lazy_jYLuY1, lazy: true, middleware: false, method: "delete" },
|
||||
{ route: '/api/members', handler: _lazy_HGAbG3, lazy: true, middleware: false, method: "get" },
|
||||
{ route: '/api/members', handler: _lazy_XNetVh, lazy: true, middleware: false, method: "post" },
|
||||
{ route: '/api/members/bulk', handler: _lazy_HlxJ2p, lazy: true, middleware: false, method: "post" },
|
||||
{ route: '/api/membership/applications', handler: _lazy_AgsX_N, lazy: true, middleware: false, method: "get" },
|
||||
{ route: '/api/membership/download/:id', handler: _lazy_7df2qh, lazy: true, middleware: false, method: "get" },
|
||||
{ route: '/api/membership/generate-pdf', handler: _lazy_Od0rSJ, lazy: true, middleware: false, method: "post" },
|
||||
|
||||
@@ -61,6 +61,8 @@ const login_post = defineEventHandler(async (event) => {
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
token,
|
||||
// Token auch im Body für externe API-Clients
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"login.post.mjs","sources":["../../../../../../server/api/auth/login.post.js"],"sourcesContent":null,"names":[],"mappings":";;;;;;;;;;;;;;;;;AAEA,mBAAA,kBAAA,CAAA,OAAA,KAAA,KAAA;AACA,EAAA,IAAA;AACA,IAAA,MAAA,IAAA,GAAA,MAAA,QAAA,CAAA,KAAA,CAAA;AACA,IAAA,MAAA,EAAA,KAAA,EAAA,QAAA,EAAA,GAAA,IAAA;AAEA,IAAA,IAAA,CAAA,KAAA,IAAA,CAAA,QAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,KAAA,GAAA,MAAA,SAAA,EAAA;AACA,IAAA,MAAA,IAAA,GAAA,KAAA,CAAA,IAAA,CAAA,CAAA,CAAA,KAAA,CAAA,CAAA,MAAA,WAAA,EAAA,KAAA,KAAA,CAAA,WAAA,EAAA,CAAA;AAEA,IAAA,IAAA,CAAA,IAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,IAAA,IAAA,CAAA,WAAA,KAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,OAAA,GAAA,MAAA,cAAA,CAAA,QAAA,EAAA,KAAA,QAAA,CAAA;AACA,IAAA,IAAA,CAAA,OAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,KAAA,GAAA,cAAA,IAAA,CAAA;AAGA,IAAA,MAAA,aAAA,CAAA,IAAA,CAAA,EAAA,EAAA,KAAA,CAAA;AAGA,IAAA,IAAA,CAAA,SAAA,GAAA,iBAAA,IAAA,IAAA,EAAA,EAAA,WAAA,EAAA;AACA,IAAA,MAAA,YAAA,GAAA,MAAA,GAAA,CAAA,CAAA,CAAA,KAAA,EAAA,EAAA,KAAA,IAAA,CAAA,EAAA,GAAA,IAAA,GAAA,CAAA,CAAA;AACA,IAAA,MAAA,WAAA,YAAA,CAAA;AAGA,IAAA,SAAA,CAAA,KAAA,EAAA,cAAA,KAAA,EAAA;AAAA,MACA,QAAA,EAAA,IAAA;AAAA,MACA,MAAA,EAAA,KAAA;AAAA;AAAA,MACA,QAAA,EAAA,KAAA;AAAA,MACA,MAAA,EAAA,EAAA,GAAA,EAAA,GAAA,EAAA,GAAA;AAAA;AAAA,KACA,CAAA;AAGA,IAAA,OAAA;AAAA,MACA,OAAA,EAAA,IAAA;AAAA,MACA,IAAA,EAAA;AAAA,QACA,IAAA,IAAA,CAAA,EAAA;AAAA,QACA,OAAA,IAAA,CAAA,KAAA;AAAA,QACA,MAAA,IAAA,CAAA,IAAA;AAAA,QACA,MAAA,IAAA,CAAA;AAAA;AACA,KACA;AAAA,EACA,SAAA,KAAA,EAAA;AACA,IAAA,OAAA,CAAA,KAAA,CAAA,iBAAA,KAAA,CAAA;AACA,IAAA,MAAA,KAAA;AAAA,EACA;AACA,CAAA,CAAA;;;;"}
|
||||
{"version":3,"file":"login.post.mjs","sources":["../../../../../../server/api/auth/login.post.js"],"sourcesContent":null,"names":[],"mappings":";;;;;;;;;;;;;;;;;AAEA,mBAAA,kBAAA,CAAA,OAAA,KAAA,KAAA;AACA,EAAA,IAAA;AACA,IAAA,MAAA,IAAA,GAAA,MAAA,QAAA,CAAA,KAAA,CAAA;AACA,IAAA,MAAA,EAAA,KAAA,EAAA,QAAA,EAAA,GAAA,IAAA;AAEA,IAAA,IAAA,CAAA,KAAA,IAAA,CAAA,QAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,KAAA,GAAA,MAAA,SAAA,EAAA;AACA,IAAA,MAAA,IAAA,GAAA,KAAA,CAAA,IAAA,CAAA,CAAA,CAAA,KAAA,CAAA,CAAA,MAAA,WAAA,EAAA,KAAA,KAAA,CAAA,WAAA,EAAA,CAAA;AAEA,IAAA,IAAA,CAAA,IAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,IAAA,IAAA,CAAA,WAAA,KAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,OAAA,GAAA,MAAA,cAAA,CAAA,QAAA,EAAA,KAAA,QAAA,CAAA;AACA,IAAA,IAAA,CAAA,OAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,MAAA,KAAA,GAAA,cAAA,IAAA,CAAA;AAGA,IAAA,MAAA,aAAA,CAAA,IAAA,CAAA,EAAA,EAAA,KAAA,CAAA;AAGA,IAAA,IAAA,CAAA,SAAA,GAAA,iBAAA,IAAA,IAAA,EAAA,EAAA,WAAA,EAAA;AACA,IAAA,MAAA,YAAA,GAAA,MAAA,GAAA,CAAA,CAAA,CAAA,KAAA,EAAA,EAAA,KAAA,IAAA,CAAA,EAAA,GAAA,IAAA,GAAA,CAAA,CAAA;AACA,IAAA,MAAA,WAAA,YAAA,CAAA;AAGA,IAAA,SAAA,CAAA,KAAA,EAAA,cAAA,KAAA,EAAA;AAAA,MACA,QAAA,EAAA,IAAA;AAAA,MACA,MAAA,EAAA,KAAA;AAAA;AAAA,MACA,QAAA,EAAA,KAAA;AAAA,MACA,MAAA,EAAA,EAAA,GAAA,EAAA,GAAA,EAAA,GAAA;AAAA;AAAA,KACA,CAAA;AAGA,IAAA,OAAA;AAAA,MACA,OAAA,EAAA,IAAA;AAAA,MACA,KAAA;AAAA;AAAA,MACA,IAAA,EAAA;AAAA,QACA,IAAA,IAAA,CAAA,EAAA;AAAA,QACA,OAAA,IAAA,CAAA,KAAA;AAAA,QACA,MAAA,IAAA,CAAA,IAAA;AAAA,QACA,MAAA,IAAA,CAAA;AAAA;AACA,KACA;AAAA,EACA,SAAA,KAAA,EAAA;AACA,IAAA,OAAA,CAAA,KAAA,CAAA,iBAAA,KAAA,CAAA;AACA,IAAA,MAAA,KAAA;AAAA,EACA;AACA,CAAA,CAAA;;;;"}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { d as defineEventHandler, g as getCookie, c as createError, r as readBody } from '../../nitro/nitro.mjs';
|
||||
import { d as defineEventHandler, g as getCookie, b as getHeader, c as createError, r as readBody } from '../../nitro/nitro.mjs';
|
||||
import { b as verifyToken, e as getUserById } from '../../_/auth.mjs';
|
||||
import { s as saveMember } from '../../_/members.mjs';
|
||||
import 'node:http';
|
||||
@@ -18,11 +18,17 @@ import 'crypto';
|
||||
|
||||
const members_post = defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token = getCookie(event, "auth_token");
|
||||
let token = getCookie(event, "auth_token");
|
||||
if (!token) {
|
||||
const authHeader = getHeader(event, "authorization");
|
||||
if (authHeader && authHeader.startsWith("Bearer ")) {
|
||||
token = authHeader.substring(7);
|
||||
}
|
||||
}
|
||||
if (!token) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: "Nicht authentifiziert."
|
||||
message: "Nicht authentifiziert. Bitte Token im Cookie oder Authorization-Header bereitstellen."
|
||||
});
|
||||
}
|
||||
const decoded = verifyToken(token);
|
||||
@@ -33,36 +39,65 @@ const members_post = defineEventHandler(async (event) => {
|
||||
});
|
||||
}
|
||||
const user = await getUserById(decoded.id);
|
||||
if (!user || user.role !== "admin" && user.role !== "vorstand") {
|
||||
if (!user) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: "Benutzer nicht gefunden."
|
||||
});
|
||||
}
|
||||
if (user.role !== "admin" && user.role !== "vorstand") {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: "Keine Berechtigung zum Bearbeiten von Mitgliedern."
|
||||
message: "Keine Berechtigung zum Hinzuf\xFCgen/Bearbeiten von Mitgliedern. Erforderlich: admin oder vorstand Rolle."
|
||||
});
|
||||
}
|
||||
const body = await readBody(event);
|
||||
const { id, firstName, lastName, email, phone, address, notes } = body;
|
||||
const { id, firstName, lastName, geburtsdatum, email, phone, address, notes } = body;
|
||||
if (!firstName || !lastName) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: "Vorname und Nachname sind erforderlich."
|
||||
});
|
||||
}
|
||||
await saveMember({
|
||||
id: id || void 0,
|
||||
firstName,
|
||||
lastName,
|
||||
email: email || "",
|
||||
phone: phone || "",
|
||||
address: address || "",
|
||||
notes: notes || ""
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: "Mitglied erfolgreich gespeichert."
|
||||
};
|
||||
if (!geburtsdatum) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: "Geburtsdatum ist erforderlich, um Duplikate zu vermeiden."
|
||||
});
|
||||
}
|
||||
try {
|
||||
await saveMember({
|
||||
id: id || void 0,
|
||||
firstName,
|
||||
lastName,
|
||||
geburtsdatum: geburtsdatum || "",
|
||||
email: email || "",
|
||||
phone: phone || "",
|
||||
address: address || "",
|
||||
notes: notes || ""
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: "Mitglied erfolgreich gespeichert."
|
||||
};
|
||||
} catch (memberError) {
|
||||
if (memberError.message && memberError.message.includes("existiert bereits")) {
|
||||
throw createError({
|
||||
statusCode: 409,
|
||||
message: memberError.message
|
||||
});
|
||||
}
|
||||
throw memberError;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Fehler beim Speichern des Mitglieds:", error);
|
||||
throw error;
|
||||
if (error.statusCode) {
|
||||
throw error;
|
||||
}
|
||||
throw createError({
|
||||
statusCode: error.statusCode || 500,
|
||||
message: error.message || "Fehler beim Speichern des Mitglieds."
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"members.post.mjs","sources":["../../../../../server/api/members.post.js"],"sourcesContent":null,"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAGA,qBAAA,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,IAAA,SAAA,EAAA,QAAA,EAAA,OAAA,KAAA,EAAA,OAAA,EAAA,OAAA,GAAA,IAAA;AAEA,IAAA,IAAA,CAAA,SAAA,IAAA,CAAA,QAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,MAAA,UAAA,CAAA;AAAA,MACA,IAAA,EAAA,IAAA,KAAA,CAAA;AAAA,MACA,SAAA;AAAA,MACA,QAAA;AAAA,MACA,OAAA,KAAA,IAAA,EAAA;AAAA,MACA,OAAA,KAAA,IAAA,EAAA;AAAA,MACA,SAAA,OAAA,IAAA,EAAA;AAAA,MACA,OAAA,KAAA,IAAA;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,wCAAA,KAAA,CAAA;AACA,IAAA,MAAA,KAAA;AAAA,EACA;AACA,CAAA,CAAA;;;;"}
|
||||
{"version":3,"file":"members.post.mjs","sources":["../../../../../server/api/members.post.js"],"sourcesContent":null,"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAGA,qBAAA,kBAAA,CAAA,OAAA,KAAA,KAAA;AACA,EAAA,IAAA;AAEA,IAAA,IAAA,KAAA,GAAA,SAAA,CAAA,KAAA,EAAA,YAAA,CAAA;AAGA,IAAA,IAAA,CAAA,KAAA,EAAA;AACA,MAAA,MAAA,UAAA,GAAA,SAAA,CAAA,KAAA,EAAA,eAAA,CAAA;AACA,MAAA,IAAA,UAAA,IAAA,UAAA,CAAA,UAAA,CAAA,SAAA,CAAA,EAAA;AACA,QAAA,KAAA,GAAA,UAAA,CAAA,UAAA,CAAA,CAAA;AAAA,MACA;AAAA,IACA;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;AAEA,IAAA,IAAA,CAAA,IAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAGA,IAAA,IAAA,IAAA,CAAA,IAAA,KAAA,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,IAAA,SAAA,EAAA,QAAA,EAAA,cAAA,KAAA,EAAA,KAAA,EAAA,OAAA,EAAA,KAAA,EAAA,GAAA,IAAA;AAEA,IAAA,IAAA,CAAA,SAAA,IAAA,CAAA,QAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,IAAA,CAAA,YAAA,EAAA;AACA,MAAA,MAAA,WAAA,CAAA;AAAA,QACA,UAAA,EAAA,GAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA,CAAA;AAAA,IACA;AAEA,IAAA,IAAA;AACA,MAAA,MAAA,UAAA,CAAA;AAAA,QACA,IAAA,EAAA,IAAA,KAAA,CAAA;AAAA,QACA,SAAA;AAAA,QACA,QAAA;AAAA,QACA,cAAA,YAAA,IAAA,EAAA;AAAA,QACA,OAAA,KAAA,IAAA,EAAA;AAAA,QACA,OAAA,KAAA,IAAA,EAAA;AAAA,QACA,SAAA,OAAA,IAAA,EAAA;AAAA,QACA,OAAA,KAAA,IAAA;AAAA,OACA,CAAA;AAEA,MAAA,OAAA;AAAA,QACA,OAAA,EAAA,IAAA;AAAA,QACA,OAAA,EAAA;AAAA,OACA;AAAA,IACA,SAAA,WAAA,EAAA;AAEA,MAAA,IAAA,YAAA,OAAA,IAAA,WAAA,CAAA,OAAA,CAAA,QAAA,CAAA,mBAAA,CAAA,EAAA;AACA,QAAA,MAAA,WAAA,CAAA;AAAA,UACA,UAAA,EAAA,GAAA;AAAA,UACA,SAAA,WAAA,CAAA;AAAA,SACA,CAAA;AAAA,MACA;AAEA,MAAA,MAAA,WAAA;AAAA,IACA;AAAA,EACA,SAAA,KAAA,EAAA;AACA,IAAA,OAAA,CAAA,KAAA,CAAA,wCAAA,KAAA,CAAA;AAEA,IAAA,IAAA,MAAA,UAAA,EAAA;AACA,MAAA,MAAA,KAAA;AAAA,IACA;AAEA,IAAA,MAAA,WAAA,CAAA;AAAA,MACA,UAAA,EAAA,MAAA,UAAA,IAAA,GAAA;AAAA,MACA,OAAA,EAAA,MAAA,OAAA,IAAA;AAAA,KACA,CAAA;AAAA,EACA;AACA,CAAA,CAAA;;;;"}
|
||||
@@ -272,7 +272,7 @@ async function renderInlineStyles(usedModules) {
|
||||
|
||||
const renderSSRHeadOptions = {"omitLineBreaks":true};
|
||||
|
||||
const entryFileName = "oL_Xi1h-.js";
|
||||
const entryFileName = "CxKxaas7.js";
|
||||
|
||||
globalThis.__buildAssetsURL = buildAssetsURL;
|
||||
globalThis.__publicAssetsURL = publicAssetsURL;
|
||||
|
||||
@@ -5,18 +5,14 @@
|
||||
<div class="flex flex-col justify-between h-full py-2">
|
||||
<!-- Hauptmenü -->
|
||||
<div class="flex justify-between items-center">
|
||||
<!-- Logo -->
|
||||
<NuxtLink to="/" class="flex items-center space-x-3 hover:scale-105 transition-transform">
|
||||
<img
|
||||
src="~/assets/images/logos/Harheimer TC.svg"
|
||||
alt="Harheimer TC Logo"
|
||||
class="w-12 h-12"
|
||||
/>
|
||||
<div class="hidden sm:block">
|
||||
<span class="text-xl font-display font-bold text-white">Harheimer <span
|
||||
class="text-primary-400">TC</span></span>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
<!-- Logo -->
|
||||
<NuxtLink to="/" class="flex items-center space-x-3 hover:scale-105 transition-transform">
|
||||
<img src="~/assets/images/logos/Harheimer TC.svg" alt="Harheimer TC Logo" class="w-12 h-12" />
|
||||
<div class="hidden sm:block">
|
||||
<span class="text-xl font-display font-bold text-white">Harheimer <span
|
||||
class="text-primary-400">TC</span></span>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
|
||||
<div style="display:flex;flex-direction:column;">
|
||||
<!-- Desktop Navigation -->
|
||||
@@ -57,18 +53,13 @@
|
||||
Termine
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink
|
||||
v-if="hasGalleryImages"
|
||||
to="/galerie"
|
||||
@click="currentSubmenu = null"
|
||||
<NuxtLink v-if="hasGalleryImages" to="/galerie" @click="currentSubmenu = null"
|
||||
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
|
||||
active-class="text-white bg-primary-600">
|
||||
Galerie
|
||||
</NuxtLink>
|
||||
|
||||
<button
|
||||
v-if="isLoggedIn"
|
||||
@click="toggleSubmenu('intern')"
|
||||
<button v-if="isLoggedIn" @click="toggleSubmenu('intern')"
|
||||
class="px-4 py-2 text-gray-300 hover:text-white font-medium transition-all rounded-lg hover:bg-primary-700/50"
|
||||
:class="(route.path.startsWith('/mitgliederbereich') || route.path.startsWith('/cms') || currentSubmenu === 'intern') ? 'text-white bg-primary-600' : ''">
|
||||
Intern
|
||||
@@ -81,203 +72,192 @@
|
||||
</div>
|
||||
|
||||
<div class="hidden lg:flex items-center h-6 border-t border-primary-700/20">
|
||||
<div v-if="currentSubmenu" class="flex items-center space-x-1">
|
||||
<!-- Verein Submenu -->
|
||||
<template v-if="currentSubmenu === 'verein'">
|
||||
<NuxtLink to="/verein/ueber-uns"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Über uns
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/vorstand"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Vorstand
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/verein/geschichte"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Geschichte
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/verein/satzung"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Satzung
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/vereinsmeisterschaften"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Vereinsmeisterschaften
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<!-- Mannschaften Submenu -->
|
||||
<template v-if="currentSubmenu === 'mannschaften'">
|
||||
<NuxtLink to="/mannschaften"
|
||||
class="px-2.5 py-1 text-xs font-semibold text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="bg-primary-600">
|
||||
Übersicht
|
||||
</NuxtLink>
|
||||
<div class="h-3 w-px bg-primary-700" />
|
||||
<template v-for="mannschaft in mannschaften" :key="mannschaft.slug">
|
||||
<NuxtLink
|
||||
:to="`/mannschaften/${mannschaft.slug}`"
|
||||
<div v-if="currentSubmenu" class="flex items-center space-x-1">
|
||||
<!-- Verein Submenu -->
|
||||
<template v-if="currentSubmenu === 'verein'">
|
||||
<NuxtLink to="/verein/ueber-uns"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
{{ mannschaft.mannschaft }}
|
||||
Über uns
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/vorstand"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Vorstand
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/verein/geschichte"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Geschichte
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/verein/satzung"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Satzung
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/vereinsmeisterschaften"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Vereinsmeisterschaften
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<div class="h-3 w-px bg-primary-700" />
|
||||
<NuxtLink to="/mannschaften/spielplaene"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Spielpläne
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/spielsysteme"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Spielsysteme
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<!-- Training Submenu -->
|
||||
<template v-if="currentSubmenu === 'training'">
|
||||
<NuxtLink to="/training"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Trainingszeiten
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/training/trainer"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Trainer
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/training/anfaenger"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Anfänger
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/tt-regeln"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
TT-Regeln
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<!-- Intern Submenu -->
|
||||
<template v-if="currentSubmenu === 'intern'">
|
||||
<NuxtLink to="/mitgliederbereich"
|
||||
class="px-2.5 py-1 text-xs font-semibold text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="bg-primary-600">
|
||||
Übersicht
|
||||
</NuxtLink>
|
||||
<div class="h-3 w-px bg-primary-700" />
|
||||
<NuxtLink to="/mitgliederbereich/mitglieder"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Mitgliederliste
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/mitgliederbereich/news"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
News
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/mitgliederbereich/profil"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Mein Profil
|
||||
</NuxtLink>
|
||||
<template v-if="isAdmin">
|
||||
<!-- Mannschaften Submenu -->
|
||||
<template v-if="currentSubmenu === 'mannschaften'">
|
||||
<NuxtLink to="/mannschaften"
|
||||
class="px-2.5 py-1 text-xs font-semibold text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="bg-primary-600">
|
||||
Übersicht
|
||||
</NuxtLink>
|
||||
<div class="h-3 w-px bg-primary-700" />
|
||||
<div class="relative inline-block">
|
||||
<button
|
||||
@click.stop="toggleCmsDropdown"
|
||||
class="px-2.5 py-1 text-xs text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded transition-all flex items-center"
|
||||
:class="route.path.startsWith('/cms') ? 'text-white bg-primary-600' : ''"
|
||||
>
|
||||
CMS
|
||||
<ChevronDown :size="12" class="ml-1" :class="['transition-transform', showCmsDropdown ? 'rotate-180' : '']" />
|
||||
</button>
|
||||
|
||||
<!-- CMS Dropdown -->
|
||||
<div
|
||||
v-if="showCmsDropdown"
|
||||
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"
|
||||
>
|
||||
<NuxtLink to="/cms"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Übersicht
|
||||
</NuxtLink>
|
||||
<div class="border-t border-gray-700 my-1"></div>
|
||||
<NuxtLink to="/cms/ueber-uns"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Über uns
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/geschichte"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Geschichte
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/tt-regeln"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
TT-Regeln
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/satzung"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Satzung
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/vereinsmeisterschaften"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Vereinsmeisterschaften
|
||||
</NuxtLink>
|
||||
<div class="border-t border-gray-700 my-1"></div>
|
||||
<NuxtLink to="/mitgliederbereich/news"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
News
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/termine"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Termine
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/spielplaene"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Spielpläne
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/mitgliederbereich/mitglieder"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Mitglieder
|
||||
</NuxtLink>
|
||||
<div class="border-t border-gray-700 my-1"></div>
|
||||
<NuxtLink to="/cms/einstellungen"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Einstellungen
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/mitgliedschaftsantraege"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Mitgliedschaftsanträge
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/benutzer"
|
||||
@click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Benutzerverwaltung
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
<template v-for="mannschaft in mannschaften" :key="mannschaft.slug">
|
||||
<NuxtLink :to="`/mannschaften/${mannschaft.slug}`"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
{{ mannschaft.mannschaft }}
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<div class="h-3 w-px bg-primary-700" />
|
||||
<NuxtLink to="/mannschaften/spielplaene"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Spielpläne
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/spielsysteme"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Spielsysteme
|
||||
</NuxtLink>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- Training Submenu -->
|
||||
<template v-if="currentSubmenu === 'training'">
|
||||
<NuxtLink to="/training"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Trainingszeiten
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/training/trainer"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Trainer
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/training/anfaenger"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Anfänger
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/tt-regeln"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
TT-Regeln
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<!-- Intern Submenu -->
|
||||
<template v-if="currentSubmenu === 'intern'">
|
||||
<NuxtLink to="/mitgliederbereich"
|
||||
class="px-2.5 py-1 text-xs font-semibold text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="bg-primary-600">
|
||||
Übersicht
|
||||
</NuxtLink>
|
||||
<div class="h-3 w-px bg-primary-700" />
|
||||
<NuxtLink to="/mitgliederbereich/mitglieder"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Mitgliederliste
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/mitgliederbereich/news"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
News
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/mitgliederbereich/profil"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
Mein Profil
|
||||
</NuxtLink>
|
||||
<div class="h-3 w-px bg-primary-700" />
|
||||
<NuxtLink to="/mitgliederbereich/api"
|
||||
class="px-2.5 py-1 text-xs text-gray-300 hover:text-white hover:bg-primary-700/50 rounded transition-all"
|
||||
active-class="text-white bg-primary-600">
|
||||
API-Dokumentation
|
||||
</NuxtLink>
|
||||
<template v-if="isAdmin">
|
||||
<div class="h-3 w-px bg-primary-700" />
|
||||
<div class="relative inline-block">
|
||||
<button @click.stop="toggleCmsDropdown"
|
||||
class="px-2.5 py-1 text-xs text-yellow-300 hover:text-white hover:bg-primary-700/50 rounded transition-all flex items-center"
|
||||
:class="route.path.startsWith('/cms') ? 'text-white bg-primary-600' : ''">
|
||||
CMS
|
||||
<ChevronDown :size="12" class="ml-1"
|
||||
:class="['transition-transform', showCmsDropdown ? 'rotate-180' : '']" />
|
||||
</button>
|
||||
|
||||
<!-- CMS Dropdown -->
|
||||
<div v-if="showCmsDropdown"
|
||||
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">
|
||||
<NuxtLink to="/cms" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Übersicht
|
||||
</NuxtLink>
|
||||
<div class="border-t border-gray-700 my-1"></div>
|
||||
<NuxtLink to="/cms/ueber-uns" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Über uns
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/geschichte" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Geschichte
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/tt-regeln" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
TT-Regeln
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/satzung" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Satzung
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/vereinsmeisterschaften" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Vereinsmeisterschaften
|
||||
</NuxtLink>
|
||||
<div class="border-t border-gray-700 my-1"></div>
|
||||
<NuxtLink to="/mitgliederbereich/news" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
News
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/termine" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Termine
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/spielplaene" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Spielpläne
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/mitgliederbereich/mitglieder" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Mitglieder
|
||||
</NuxtLink>
|
||||
<div class="border-t border-gray-700 my-1"></div>
|
||||
<NuxtLink to="/cms/einstellungen" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Einstellungen
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/mitgliedschaftsantraege" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Mitgliedschaftsanträge
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/cms/benutzer" @click="showCmsDropdown = false"
|
||||
class="block px-4 py-2 text-sm text-gray-300 hover:bg-primary-600 hover:text-white transition-colors">
|
||||
Benutzerverwaltung
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
<button @click="isMobileMenuOpen = !isMobileMenuOpen"
|
||||
@@ -326,16 +306,16 @@
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Geschichte
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/verein/satzung" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Satzung
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/vereinsmeisterschaften" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Vereinsmeisterschaften
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
<NuxtLink to="/verein/satzung" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Satzung
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/vereinsmeisterschaften" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Vereinsmeisterschaften
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mannschaften Mobile -->
|
||||
<div>
|
||||
@@ -351,24 +331,22 @@
|
||||
Übersicht
|
||||
</NuxtLink>
|
||||
<template v-for="mannschaft in mannschaften" :key="mannschaft.slug">
|
||||
<NuxtLink
|
||||
:to="`/mannschaften/${mannschaft.slug}`"
|
||||
@click="isMobileMenuOpen = false"
|
||||
<NuxtLink :to="`/mannschaften/${mannschaft.slug}`" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
{{ mannschaft.mannschaft }}
|
||||
</NuxtLink>
|
||||
</template>
|
||||
<div class="border-t border-primary-700/20 my-2" />
|
||||
<NuxtLink to="/mannschaften/spielplaene" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Spielpläne
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/spielsysteme" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Spielsysteme
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
<NuxtLink to="/mannschaften/spielplaene" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Spielpläne
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/spielsysteme" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Spielsysteme
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Training Mobile -->
|
||||
<div>
|
||||
@@ -387,16 +365,16 @@
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Trainer
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/training/anfaenger" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Anfänger
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/tt-regeln" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
TT-Regeln
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
<NuxtLink to="/training/anfaenger" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
Anfänger
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/tt-regeln" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-2 text-sm text-gray-400 hover:text-white hover:bg-primary-700/50 rounded-lg transition-colors">
|
||||
TT-Regeln
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<NuxtLink to="/mitgliedschaft" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors">
|
||||
@@ -408,10 +386,7 @@
|
||||
Termine
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink
|
||||
v-if="hasGalleryImages"
|
||||
to="/galerie"
|
||||
@click="isMobileMenuOpen = false"
|
||||
<NuxtLink v-if="hasGalleryImages" to="/galerie" @click="isMobileMenuOpen = false"
|
||||
class="block px-4 py-3 text-gray-300 hover:text-white hover:bg-primary-700/50 rounded-lg font-medium transition-colors">
|
||||
Galerie
|
||||
</NuxtLink>
|
||||
@@ -526,24 +501,24 @@ const showCmsDropdown = ref(false)
|
||||
const isLoggedIn = computed(() => authStore.isLoggedIn)
|
||||
const isAdmin = computed(() => authStore.isAdmin)
|
||||
|
||||
// Automatisches Setzen des Submenus basierend auf der Route
|
||||
const currentSubmenu = computed(() => {
|
||||
const path = route.path
|
||||
if (path.startsWith('/verein/') || path.startsWith('/vorstand') ||
|
||||
path.startsWith('/vereinsmeisterschaften')) {
|
||||
return 'verein'
|
||||
}
|
||||
if (path.startsWith('/mannschaften') || path.startsWith('/spielsysteme')) {
|
||||
return 'mannschaften'
|
||||
}
|
||||
if (path.startsWith('/training') || path.startsWith('/tt-regeln')) {
|
||||
return 'training'
|
||||
}
|
||||
if (path.startsWith('/mitgliederbereich') || path.startsWith('/cms')) {
|
||||
return 'intern'
|
||||
}
|
||||
return null
|
||||
})
|
||||
// Automatisches Setzen des Submenus basierend auf der Route
|
||||
const currentSubmenu = computed(() => {
|
||||
const path = route.path
|
||||
if (path.startsWith('/verein/') || path.startsWith('/vorstand') ||
|
||||
path.startsWith('/vereinsmeisterschaften')) {
|
||||
return 'verein'
|
||||
}
|
||||
if (path.startsWith('/mannschaften') || path.startsWith('/spielsysteme')) {
|
||||
return 'mannschaften'
|
||||
}
|
||||
if (path.startsWith('/training') || path.startsWith('/tt-regeln')) {
|
||||
return 'training'
|
||||
}
|
||||
if (path.startsWith('/mitgliederbereich') || path.startsWith('/cms')) {
|
||||
return 'intern'
|
||||
}
|
||||
return null
|
||||
})
|
||||
|
||||
// Manuelles Toggle für Click-Events
|
||||
const manualSubmenu = ref(null)
|
||||
@@ -621,19 +596,19 @@ onMounted(() => {
|
||||
})
|
||||
})
|
||||
|
||||
const toggleSubmenu = (menu) => {
|
||||
// Wenn wir schon im richtigen Bereich sind, nichts tun (Submenu bleibt offen)
|
||||
// Wenn nicht, zur Hauptseite navigieren
|
||||
const path = route.path
|
||||
const toggleSubmenu = (menu) => {
|
||||
// Wenn wir schon im richtigen Bereich sind, nichts tun (Submenu bleibt offen)
|
||||
// Wenn nicht, zur Hauptseite navigieren
|
||||
const path = route.path
|
||||
|
||||
if (menu === 'verein' && !path.startsWith('/verein/') && !path.startsWith('/vorstand') && !path.startsWith('/vereinsmeisterschaften')) {
|
||||
navigateTo('/verein/ueber-uns')
|
||||
} else if (menu === 'mannschaften' && !path.startsWith('/mannschaften') && !path.startsWith('/spielsysteme')) {
|
||||
navigateTo('/mannschaften')
|
||||
} else if (menu === 'training' && !path.startsWith('/training') && !path.startsWith('/tt-regeln')) {
|
||||
navigateTo('/training')
|
||||
} else if (menu === 'intern' && !path.startsWith('/mitgliederbereich') && !path.startsWith('/cms')) {
|
||||
navigateTo('/mitgliederbereich')
|
||||
}
|
||||
}
|
||||
if (menu === 'verein' && !path.startsWith('/verein/') && !path.startsWith('/vorstand') && !path.startsWith('/vereinsmeisterschaften')) {
|
||||
navigateTo('/verein/ueber-uns')
|
||||
} else if (menu === 'mannschaften' && !path.startsWith('/mannschaften') && !path.startsWith('/spielsysteme')) {
|
||||
navigateTo('/mannschaften')
|
||||
} else if (menu === 'training' && !path.startsWith('/training') && !path.startsWith('/tt-regeln')) {
|
||||
navigateTo('/training')
|
||||
} else if (menu === 'intern' && !path.startsWith('/mitgliederbereich') && !path.startsWith('/cms')) {
|
||||
navigateTo('/mitgliederbereich')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
549
pages/mitgliederbereich/api.vue
Normal file
549
pages/mitgliederbereich/api.vue
Normal file
@@ -0,0 +1,549 @@
|
||||
<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="mb-8">
|
||||
<h1 class="text-4xl sm:text-5xl font-display font-bold text-gray-900 mb-4">
|
||||
API-Dokumentation
|
||||
</h1>
|
||||
<div class="w-24 h-1 bg-primary-600 mb-6" />
|
||||
<p class="text-xl text-gray-600">
|
||||
Übersicht über alle verfügbaren API-Endpoints und deren Verwendung
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Authentication Info -->
|
||||
<div class="bg-blue-50 border-l-4 border-blue-500 p-6 rounded-lg mb-8">
|
||||
<h2 class="text-xl font-semibold text-blue-900 mb-2">Authentifizierung</h2>
|
||||
<p class="text-blue-800 mb-4">
|
||||
Alle API-Endpoints erfordern Authentifizierung (außer Login). Es werden zwei Methoden unterstützt:
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<strong class="text-blue-900">1. Cookie-basiert:</strong>
|
||||
<p class="text-blue-700 text-sm mt-1">Nach dem Login über <code>/api/auth/login</code> wird automatisch ein Cookie gesetzt.</p>
|
||||
</div>
|
||||
<div>
|
||||
<strong class="text-blue-900">2. Authorization Header:</strong>
|
||||
<p class="text-blue-700 text-sm mt-1">Header: <code>Authorization: Bearer <token></code></p>
|
||||
<p class="text-blue-700 text-sm">Der Token wird im Login-Response im Feld <code>token</code> zurückgegeben.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Endpoints -->
|
||||
<div class="space-y-8">
|
||||
<!-- Authentication Endpoints -->
|
||||
<section class="bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6">Authentifizierung</h2>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Login -->
|
||||
<div class="border-l-4 border-primary-600 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">POST /api/auth/login</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-green-100 text-green-800 rounded">Öffentlich</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Benutzer einloggen und Token erhalten</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Request Body:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"email": "benutzer@example.com",
|
||||
"password": "passwort"
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Response:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"success": true,
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"user": {
|
||||
"id": "user-id",
|
||||
"email": "benutzer@example.com",
|
||||
"name": "Max Mustermann",
|
||||
"role": "mitglied"
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Logout -->
|
||||
<div class="border-l-4 border-primary-600 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">POST /api/auth/logout</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-yellow-100 text-yellow-800 rounded">Auth erforderlich</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Benutzer ausloggen</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Response:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"success": true,
|
||||
"message": "Erfolgreich ausgeloggt"
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auth Status -->
|
||||
<div class="border-l-4 border-primary-600 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">GET /api/auth/status</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-yellow-100 text-yellow-800 rounded">Auth erforderlich</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Aktuellen Authentifizierungsstatus abrufen</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Response:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"isLoggedIn": true,
|
||||
"user": {
|
||||
"id": "user-id",
|
||||
"email": "benutzer@example.com",
|
||||
"name": "Max Mustermann",
|
||||
"role": "mitglied"
|
||||
},
|
||||
"role": "mitglied"
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Members Endpoints -->
|
||||
<section class="bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6">Mitglieder</h2>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Get Members -->
|
||||
<div class="border-l-4 border-primary-600 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">GET /api/members</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-yellow-100 text-yellow-800 rounded">Auth erforderlich</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Alle Mitglieder abrufen (mit Merge aus registrierten Benutzern)</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Response:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"success": true,
|
||||
"members": [
|
||||
{
|
||||
"id": "member-id",
|
||||
"firstName": "Max",
|
||||
"lastName": "Mustermann",
|
||||
"geburtsdatum": "1990-01-15",
|
||||
"email": "max@example.com",
|
||||
"phone": "0123456789",
|
||||
"address": "Musterstraße 1",
|
||||
"source": "manual",
|
||||
"editable": true,
|
||||
"hasLogin": false
|
||||
}
|
||||
]
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Post Members -->
|
||||
<div class="border-l-4 border-red-500 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">POST /api/members</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Neues Mitglied hinzufügen oder bestehendes bearbeiten</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Request Body:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"id": "optional-für-update",
|
||||
"firstName": "Max",
|
||||
"lastName": "Mustermann",
|
||||
"geburtsdatum": "1990-01-15",
|
||||
"email": "max@example.com",
|
||||
"phone": "0123456789",
|
||||
"address": "Musterstraße 1, 12345 Musterstadt",
|
||||
"notes": "Optional"
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Response:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"success": true,
|
||||
"message": "Mitglied erfolgreich gespeichert."
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 bg-blue-50 rounded-lg p-3">
|
||||
<p class="text-xs text-blue-800">
|
||||
<strong>Hinweis:</strong> Ohne <code>id</code> wird ein neues Mitglied erstellt. Mit <code>id</code> wird ein bestehendes Mitglied aktualisiert. <code>geburtsdatum</code> ist Pflichtfeld zur Duplikatsprüfung (Format: YYYY-MM-DD).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bulk Import Members -->
|
||||
<div class="border-l-4 border-red-500 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">POST /api/members/bulk</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Mehrere Mitglieder auf einmal importieren (Bulk-Import)</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Request Body:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"members": [
|
||||
{
|
||||
"firstName": "Max",
|
||||
"lastName": "Mustermann",
|
||||
"geburtsdatum": "1990-01-15",
|
||||
"email": "max@example.com",
|
||||
"phone": "0123456789",
|
||||
"address": "Musterstraße 1",
|
||||
"notes": "Optional"
|
||||
},
|
||||
{
|
||||
"firstName": "Anna",
|
||||
"lastName": "Schmidt",
|
||||
"geburtsdatum": "1985-03-20",
|
||||
"email": "anna@example.com"
|
||||
}
|
||||
]
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Response:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"success": true,
|
||||
"summary": {
|
||||
"total": 2,
|
||||
"imported": 2,
|
||||
"duplicates": 0,
|
||||
"errors": 0
|
||||
},
|
||||
"results": {
|
||||
"success": [
|
||||
{
|
||||
"index": 1,
|
||||
"member": { ... }
|
||||
}
|
||||
],
|
||||
"duplicates": [],
|
||||
"errors": []
|
||||
}
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 bg-blue-50 rounded-lg p-3">
|
||||
<p class="text-xs text-blue-800 mb-2">
|
||||
<strong>Features:</strong>
|
||||
</p>
|
||||
<ul class="text-xs text-blue-700 list-disc list-inside space-y-1">
|
||||
<li>Duplikatsprüfung gegen bestehende Mitglieder</li>
|
||||
<li>Duplikatsprüfung innerhalb des Imports</li>
|
||||
<li>Validierung aller Daten vor dem Import</li>
|
||||
<li>Detaillierte Fehlerberichte für jeden Eintrag</li>
|
||||
<li>Nur erfolgreiche Einträge werden gespeichert</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Members -->
|
||||
<div class="border-l-4 border-red-500 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">DELETE /api/members</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Mitglied löschen</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Request Body:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"id": "member-id"
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Response:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"success": true,
|
||||
"message": "Mitglied erfolgreich gelöscht."
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- News Endpoints -->
|
||||
<section class="bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6">News</h2>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Get News -->
|
||||
<div class="border-l-4 border-primary-600 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">GET /api/news</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-yellow-100 text-yellow-800 rounded">Auth erforderlich</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Alle News abrufen (inkl. interner News)</p>
|
||||
</div>
|
||||
|
||||
<!-- Post News -->
|
||||
<div class="border-l-4 border-red-500 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">POST /api/news</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Neue News erstellen oder bestehende bearbeiten</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Request Body:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"id": "optional-für-update",
|
||||
"title": "Titel der News",
|
||||
"content": "Inhalt der News",
|
||||
"isPublic": true,
|
||||
"expiresAt": "2025-12-31T23:59:59.000Z",
|
||||
"isHidden": false
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete News -->
|
||||
<div class="border-l-4 border-red-500 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">DELETE /api/news</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">News löschen</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Request Body:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"id": "news-id"
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Termine Endpoints -->
|
||||
<section class="bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6">Termine</h2>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Get Termine -->
|
||||
<div class="border-l-4 border-red-500 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">GET /api/termine-manage</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Alle Termine abrufen (für Verwaltung)</p>
|
||||
</div>
|
||||
|
||||
<!-- Post Termine -->
|
||||
<div class="border-l-4 border-red-500 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">POST /api/termine-manage</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Neuen Termin erstellen</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Request Body:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>{
|
||||
"datum": "2025-12-25",
|
||||
"uhrzeit": "19:00",
|
||||
"titel": "Weihnachtsfeier",
|
||||
"beschreibung": "Gemeinsame Feier",
|
||||
"kategorie": "Veranstaltung"
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Termine -->
|
||||
<div class="border-l-4 border-red-500 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">DELETE /api/termine-manage</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Termin löschen</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Query Parameters:</p>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-3 rounded overflow-x-auto"><code>?datum=2025-12-25&uhrzeit=19:00&titel=Weihnachtsfeier&beschreibung=...&kategorie=...</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Config Endpoints -->
|
||||
<section class="bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6">Konfiguration</h2>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Get Config -->
|
||||
<div class="border-l-4 border-red-500 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">GET /api/config</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Vereinskonfiguration abrufen</p>
|
||||
</div>
|
||||
|
||||
<!-- Put Config -->
|
||||
<div class="border-l-4 border-red-500 pl-4">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="text-lg font-semibold text-gray-900">PUT /api/config</h3>
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-3">Vereinskonfiguration aktualisieren</p>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-3">
|
||||
<p class="text-sm font-medium text-gray-700 mb-2">Request Body:</p>
|
||||
<p class="text-xs text-gray-600">Komplettes Config-Objekt mit allen Einstellungen</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- Example Usage -->
|
||||
<div class="mt-12 bg-white rounded-xl shadow-lg p-6">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6">Beispiel-Usage</h2>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">cURL Beispiel:</h3>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-4 rounded overflow-x-auto"><code># Login und Token erhalten
|
||||
curl -X POST http://localhost:3100/api/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"email": "admin@example.com", "password": "passwort"}'
|
||||
|
||||
# Mitglied hinzufügen mit Token
|
||||
curl -X POST http://localhost:3100/api/members \
|
||||
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"firstName": "Max",
|
||||
"lastName": "Mustermann",
|
||||
"geburtsdatum": "1990-01-15",
|
||||
"email": "max@example.com",
|
||||
"phone": "0123456789"
|
||||
}'
|
||||
|
||||
# Bulk-Import von Mitgliedern
|
||||
curl -X POST http://localhost:3100/api/members/bulk \
|
||||
-H "Authorization: Bearer YOUR_TOKEN_HERE" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"members": [
|
||||
{
|
||||
"firstName": "Max",
|
||||
"lastName": "Mustermann",
|
||||
"geburtsdatum": "1990-01-15",
|
||||
"email": "max@example.com"
|
||||
},
|
||||
{
|
||||
"firstName": "Anna",
|
||||
"lastName": "Schmidt",
|
||||
"geburtsdatum": "1985-03-20",
|
||||
"email": "anna@example.com"
|
||||
}
|
||||
]
|
||||
}'</code></pre>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-2">JavaScript/Fetch Beispiel:</h3>
|
||||
<pre class="text-xs bg-gray-900 text-gray-100 p-4 rounded overflow-x-auto"><code>// Login
|
||||
const loginResponse = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
email: 'admin@example.com',
|
||||
password: 'passwort'
|
||||
})
|
||||
})
|
||||
const { token } = await loginResponse.json()
|
||||
|
||||
// Mitglied hinzufügen
|
||||
const memberResponse = await fetch('/api/members', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
firstName: 'Max',
|
||||
lastName: 'Mustermann',
|
||||
geburtsdatum: '1990-01-15',
|
||||
email: 'max@example.com'
|
||||
})
|
||||
})
|
||||
|
||||
// Bulk-Import
|
||||
const bulkResponse = await fetch('/api/members/bulk', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
members: [
|
||||
{
|
||||
firstName: 'Max',
|
||||
lastName: 'Mustermann',
|
||||
geburtsdatum: '1990-01-15',
|
||||
email: 'max@example.com'
|
||||
},
|
||||
{
|
||||
firstName: 'Anna',
|
||||
lastName: 'Schmidt',
|
||||
geburtsdatum: '1985-03-20',
|
||||
email: 'anna@example.com'
|
||||
}
|
||||
]
|
||||
})
|
||||
})
|
||||
const result = await bulkResponse.json()
|
||||
console.log(`Importiert: ${result.summary.imported}, Duplikate: ${result.summary.duplicates}`)</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Role Legend -->
|
||||
<div class="mt-8 bg-gray-50 rounded-xl p-6">
|
||||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Legende</h2>
|
||||
<div class="grid md:grid-cols-3 gap-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="px-2 py-1 text-xs font-medium bg-green-100 text-green-800 rounded">Öffentlich</span>
|
||||
<span class="text-sm text-gray-600">Keine Authentifizierung erforderlich</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="px-2 py-1 text-xs font-medium bg-yellow-100 text-yellow-800 rounded">Auth erforderlich</span>
|
||||
<span class="text-sm text-gray-600">Jeder eingeloggte Benutzer</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="px-2 py-1 text-xs font-medium bg-red-100 text-red-800 rounded">admin/vorstand</span>
|
||||
<span class="text-sm text-gray-600">Nur Admin oder Vorstand</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: 'auth',
|
||||
layout: 'default'
|
||||
})
|
||||
|
||||
useHead({
|
||||
title: 'API-Dokumentation - Harheimer TC',
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -16,6 +16,16 @@
|
||||
<component :is="viewMode === 'cards' ? Table2 : Grid3x3" :size="20" class="mr-2" />
|
||||
{{ viewMode === 'cards' ? 'Tabelle' : 'Karten' }}
|
||||
</button>
|
||||
<button
|
||||
v-if="canEdit"
|
||||
@click="showBulkImportModal = true"
|
||||
class="flex items-center px-4 py-2 bg-green-600 hover:bg-green-700 text-white font-semibold rounded-lg transition-colors"
|
||||
>
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path>
|
||||
</svg>
|
||||
Bulk-Import
|
||||
</button>
|
||||
<button
|
||||
v-if="canEdit"
|
||||
@click="openAddModal"
|
||||
@@ -234,6 +244,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">Geburtsdatum *</label>
|
||||
<input
|
||||
v-model="formData.geburtsdatum"
|
||||
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"
|
||||
/>
|
||||
<p class="text-xs text-gray-500 mt-1">Wird zur eindeutigen Identifizierung benötigt</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">E-Mail</label>
|
||||
<input
|
||||
@@ -300,6 +322,144 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bulk Import Modal -->
|
||||
<div
|
||||
v-if="showBulkImportModal"
|
||||
class="fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4"
|
||||
@click.self="closeBulkImportModal"
|
||||
>
|
||||
<div class="bg-white rounded-xl shadow-2xl max-w-4xl w-full max-h-[90vh] overflow-y-auto p-8">
|
||||
<h2 class="text-2xl font-display font-bold text-gray-900 mb-6">
|
||||
Bulk-Import von Mitgliedern
|
||||
</h2>
|
||||
|
||||
<!-- CSV Upload Section -->
|
||||
<div class="mb-6">
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">CSV-Datei hochladen</label>
|
||||
<div
|
||||
@click="triggerBulkFileInput"
|
||||
@dragover.prevent
|
||||
@dragenter.prevent="isDragOver = true"
|
||||
@dragleave.prevent="isDragOver = false"
|
||||
@drop.prevent="handleBulkFileDrop"
|
||||
class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center hover:border-primary-400 hover:bg-primary-50 transition-colors cursor-pointer"
|
||||
:class="{ 'border-primary-400 bg-primary-50': isDragOver }"
|
||||
>
|
||||
<svg class="w-12 h-12 text-gray-400 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path>
|
||||
</svg>
|
||||
<p class="text-lg font-medium text-gray-900 mb-2">CSV-Datei hochladen</p>
|
||||
<p class="text-sm text-gray-600 mb-4">Klicken Sie hier oder ziehen Sie eine CSV-Datei hierher</p>
|
||||
<p v-if="bulkSelectedFile" class="text-sm text-primary-600 font-medium">{{ bulkSelectedFile.name }}</p>
|
||||
</div>
|
||||
<input
|
||||
ref="bulkFileInput"
|
||||
type="file"
|
||||
accept=".csv"
|
||||
@change="handleBulkFileSelect"
|
||||
class="hidden"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- CSV Format Info -->
|
||||
<div class="bg-blue-50 border-l-4 border-blue-500 p-4 rounded-lg mb-6">
|
||||
<h4 class="text-sm font-medium text-blue-800 mb-2">Erwartetes CSV-Format:</h4>
|
||||
<div class="text-xs text-blue-700 space-y-1">
|
||||
<p>• Erste Zeile: Spaltenüberschriften (firstName, lastName, geburtsdatum, email, phone, address, notes)</p>
|
||||
<p>• <strong>Pflichtfelder:</strong> firstName, lastName, geburtsdatum</p>
|
||||
<p>• <strong>Geburtsdatum:</strong> Format YYYY-MM-DD (z.B. 1990-01-15)</p>
|
||||
<p>• Trennzeichen: Komma (,) oder Semikolon (;)</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Preview Section -->
|
||||
<div v-if="bulkPreviewData.length > 0" class="mb-6">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Vorschau ({{ bulkPreviewData.length }} Einträge)</h3>
|
||||
<div class="max-h-64 overflow-y-auto border border-gray-200 rounded-lg">
|
||||
<table class="min-w-full divide-y divide-gray-200 text-sm">
|
||||
<thead class="bg-gray-50 sticky top-0">
|
||||
<tr>
|
||||
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">Vorname</th>
|
||||
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">Nachname</th>
|
||||
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">Geburtsdatum</th>
|
||||
<th class="px-3 py-2 text-left text-xs font-medium text-gray-500 uppercase">E-Mail</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<tr v-for="(row, index) in bulkPreviewData.slice(0, 10)" :key="index" class="hover:bg-gray-50">
|
||||
<td class="px-3 py-2">{{ row.firstName || '-' }}</td>
|
||||
<td class="px-3 py-2">{{ row.lastName || '-' }}</td>
|
||||
<td class="px-3 py-2">{{ row.geburtsdatum || '-' }}</td>
|
||||
<td class="px-3 py-2">{{ row.email || '-' }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-if="bulkPreviewData.length > 10" class="px-3 py-2 text-xs text-gray-500 bg-gray-50 text-center">
|
||||
... und {{ bulkPreviewData.length - 10 }} weitere
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Import Results -->
|
||||
<div v-if="bulkImportResults" class="mb-6">
|
||||
<div class="bg-gray-50 rounded-lg p-4">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-3">Import-Ergebnisse</h3>
|
||||
<div class="grid grid-cols-3 gap-4 mb-4">
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-green-600">{{ bulkImportResults.summary.imported }}</div>
|
||||
<div class="text-sm text-gray-600">Importiert</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-yellow-600">{{ bulkImportResults.summary.duplicates }}</div>
|
||||
<div class="text-sm text-gray-600">Duplikate</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-red-600">{{ bulkImportResults.summary.errors }}</div>
|
||||
<div class="text-sm text-gray-600">Fehler</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="bulkImportResults.results.duplicates.length > 0" class="mt-4">
|
||||
<h4 class="text-sm font-medium text-gray-700 mb-2">Duplikate:</h4>
|
||||
<div class="text-xs text-gray-600 space-y-1 max-h-32 overflow-y-auto">
|
||||
<div v-for="dup in bulkImportResults.results.duplicates" :key="dup.index">
|
||||
Zeile {{ dup.index }}: {{ dup.member.firstName }} {{ dup.member.lastName }} - {{ dup.reason }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="bulkImportResults.results.errors.length > 0" class="mt-4">
|
||||
<h4 class="text-sm font-medium text-gray-700 mb-2">Fehler:</h4>
|
||||
<div class="text-xs text-red-600 space-y-1 max-h-32 overflow-y-auto">
|
||||
<div v-for="err in bulkImportResults.results.errors" :key="err.index">
|
||||
Zeile {{ err.index }}: {{ err.error }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-4 pt-4">
|
||||
<button
|
||||
type="button"
|
||||
@click="closeBulkImportModal"
|
||||
class="px-6 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
|
||||
:disabled="isBulkImporting"
|
||||
>
|
||||
Schließen
|
||||
</button>
|
||||
<button
|
||||
@click="processBulkImport"
|
||||
:disabled="!bulkPreviewData.length || isBulkImporting"
|
||||
class="px-6 py-2 bg-primary-600 hover:bg-primary-700 text-white font-semibold rounded-lg transition-colors flex items-center disabled:bg-gray-400"
|
||||
>
|
||||
<Loader2 v-if="isBulkImporting" :size="20" class="animate-spin mr-2" />
|
||||
<span>{{ isBulkImporting ? 'Importiert...' : 'Importieren' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -318,9 +478,19 @@ const editingMember = ref(null)
|
||||
const errorMessage = ref('')
|
||||
const viewMode = ref('cards') // 'table' or 'cards'
|
||||
|
||||
// Bulk import state
|
||||
const showBulkImportModal = ref(false)
|
||||
const bulkFileInput = ref(null)
|
||||
const bulkSelectedFile = ref(null)
|
||||
const bulkPreviewData = ref([])
|
||||
const isBulkImporting = ref(false)
|
||||
const bulkImportResults = ref(null)
|
||||
const isDragOver = ref(false)
|
||||
|
||||
const formData = ref({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
geburtsdatum: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
@@ -355,6 +525,7 @@ const openAddModal = () => {
|
||||
formData.value = {
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
geburtsdatum: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
@@ -369,6 +540,7 @@ const openEditModal = (member) => {
|
||||
formData.value = {
|
||||
firstName: member.firstName || '',
|
||||
lastName: member.lastName || '',
|
||||
geburtsdatum: member.geburtsdatum || '',
|
||||
email: member.email || '',
|
||||
phone: member.phone || '',
|
||||
address: member.address || '',
|
||||
@@ -399,8 +571,18 @@ const saveMember = async () => {
|
||||
|
||||
closeModal()
|
||||
await loadMembers()
|
||||
if (window.showSuccessModal) {
|
||||
window.showSuccessModal('Erfolg', 'Mitglied erfolgreich gespeichert.')
|
||||
}
|
||||
} catch (error) {
|
||||
errorMessage.value = error.data?.message || 'Fehler beim Speichern des Mitglieds.'
|
||||
console.error('Fehler beim Speichern:', error)
|
||||
const errorMsg = error.data?.message || error.message || 'Fehler beim Speichern des Mitglieds.'
|
||||
errorMessage.value = errorMsg
|
||||
|
||||
// If it's a duplicate error (409), show it prominently
|
||||
if ((error.statusCode === 409 || error.status === 409) && window.showErrorModal) {
|
||||
window.showErrorModal('Duplikat gefunden', errorMsg)
|
||||
}
|
||||
} finally {
|
||||
isSaving.value = false
|
||||
}
|
||||
@@ -435,6 +617,131 @@ const formatDate = (dateString) => {
|
||||
})
|
||||
}
|
||||
|
||||
// Bulk import functions
|
||||
const triggerBulkFileInput = () => {
|
||||
bulkFileInput.value?.click()
|
||||
}
|
||||
|
||||
const handleBulkFileSelect = (event) => {
|
||||
const file = event.target.files?.[0]
|
||||
if (file) {
|
||||
processBulkCSV(file)
|
||||
}
|
||||
}
|
||||
|
||||
const handleBulkFileDrop = (event) => {
|
||||
isDragOver.value = false
|
||||
const file = event.dataTransfer?.files?.[0]
|
||||
if (file && file.type === 'text/csv') {
|
||||
processBulkCSV(file)
|
||||
}
|
||||
}
|
||||
|
||||
const processBulkCSV = async (file) => {
|
||||
bulkSelectedFile.value = file
|
||||
bulkImportResults.value = null
|
||||
|
||||
try {
|
||||
const text = await file.text()
|
||||
const lines = text.split('\n').filter(line => line.trim() !== '')
|
||||
|
||||
if (lines.length < 2) {
|
||||
window.showErrorModal('Fehler', 'CSV-Datei muss mindestens eine Kopfzeile und eine Datenzeile enthalten')
|
||||
return
|
||||
}
|
||||
|
||||
// Detect delimiter
|
||||
const parseCSVLine = (line) => {
|
||||
const tabCount = (line.match(/\t/g) || []).length
|
||||
const semicolonCount = (line.match(/;/g) || []).length
|
||||
const delimiter = tabCount > semicolonCount ? '\t' : (semicolonCount > 0 ? ';' : ',')
|
||||
return line.split(delimiter).map(value => value.trim().replace(/^"|"$/g, ''))
|
||||
}
|
||||
|
||||
// Parse header
|
||||
const headers = parseCSVLine(lines[0]).map(h => h.toLowerCase())
|
||||
|
||||
// Find column indices
|
||||
const firstNameIdx = headers.findIndex(h => h.includes('firstname') || h.includes('vorname'))
|
||||
const lastNameIdx = headers.findIndex(h => h.includes('lastname') || h.includes('nachname'))
|
||||
const geburtsdatumIdx = headers.findIndex(h => h.includes('geburtsdatum') || h.includes('birthdate') || h.includes('geburt'))
|
||||
const emailIdx = headers.findIndex(h => h.includes('email') || h.includes('e-mail'))
|
||||
const phoneIdx = headers.findIndex(h => h.includes('phone') || h.includes('telefon') || h.includes('tel'))
|
||||
const addressIdx = headers.findIndex(h => h.includes('address') || h.includes('adresse'))
|
||||
const notesIdx = headers.findIndex(h => h.includes('note') || h.includes('notiz') || h.includes('bemerkung'))
|
||||
|
||||
if (firstNameIdx === -1 || lastNameIdx === -1 || geburtsdatumIdx === -1) {
|
||||
window.showErrorModal('Fehler', 'CSV muss Spalten für firstName, lastName und geburtsdatum enthalten')
|
||||
return
|
||||
}
|
||||
|
||||
// Parse data rows
|
||||
bulkPreviewData.value = lines.slice(1).map((line, index) => {
|
||||
const values = parseCSVLine(line)
|
||||
return {
|
||||
firstName: values[firstNameIdx] || '',
|
||||
lastName: values[lastNameIdx] || '',
|
||||
geburtsdatum: values[geburtsdatumIdx] || '',
|
||||
email: emailIdx !== -1 ? (values[emailIdx] || '') : '',
|
||||
phone: phoneIdx !== -1 ? (values[phoneIdx] || '') : '',
|
||||
address: addressIdx !== -1 ? (values[addressIdx] || '') : '',
|
||||
notes: notesIdx !== -1 ? (values[notesIdx] || '') : ''
|
||||
}
|
||||
}).filter(row => row.firstName && row.lastName && row.geburtsdatum)
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Parsen der CSV:', error)
|
||||
window.showErrorModal('Fehler', 'Fehler beim Lesen der CSV-Datei: ' + error.message)
|
||||
}
|
||||
}
|
||||
|
||||
const processBulkImport = async () => {
|
||||
if (!bulkPreviewData.value.length) return
|
||||
|
||||
isBulkImporting.value = true
|
||||
bulkImportResults.value = null
|
||||
|
||||
try {
|
||||
const response = await $fetch('/api/members/bulk', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
members: bulkPreviewData.value
|
||||
}
|
||||
})
|
||||
|
||||
bulkImportResults.value = response
|
||||
|
||||
if (response.summary.imported > 0) {
|
||||
await loadMembers()
|
||||
window.showSuccessModal(
|
||||
'Import erfolgreich',
|
||||
`${response.summary.imported} Mitglieder wurden erfolgreich importiert.`
|
||||
)
|
||||
}
|
||||
|
||||
if (response.summary.duplicates > 0 || response.summary.errors > 0) {
|
||||
// Results are already displayed in the modal
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Bulk-Import:', error)
|
||||
const errorMsg = error.data?.message || error.message || 'Fehler beim Import'
|
||||
window.showErrorModal('Import-Fehler', errorMsg)
|
||||
} finally {
|
||||
isBulkImporting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const closeBulkImportModal = () => {
|
||||
showBulkImportModal.value = false
|
||||
bulkSelectedFile.value = null
|
||||
bulkPreviewData.value = []
|
||||
bulkImportResults.value = null
|
||||
isDragOver.value = false
|
||||
if (bulkFileInput.value) {
|
||||
bulkFileInput.value.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadMembers()
|
||||
})
|
||||
|
||||
@@ -59,9 +59,10 @@ export default defineEventHandler(async (event) => {
|
||||
maxAge: 60 * 60 * 24 * 7 // 7 days
|
||||
})
|
||||
|
||||
// Return user data (without password)
|
||||
// Return user data (without password) and token for API usage
|
||||
return {
|
||||
success: true,
|
||||
token: token, // Token auch im Body für externe API-Clients
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
|
||||
@@ -3,12 +3,21 @@ import { saveMember } from '../utils/members.js'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
const token = getCookie(event, 'auth_token')
|
||||
// Support both Cookie and Authorization Header
|
||||
let token = getCookie(event, 'auth_token')
|
||||
|
||||
// If no cookie token, try Authorization header (Bearer token)
|
||||
if (!token) {
|
||||
const authHeader = getHeader(event, 'authorization')
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
token = authHeader.substring(7)
|
||||
}
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'Nicht authentifiziert.'
|
||||
message: 'Nicht authentifiziert. Bitte Token im Cookie oder Authorization-Header bereitstellen.'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -23,16 +32,23 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
const user = await getUserById(decoded.id)
|
||||
|
||||
// Only admin and vorstand can edit members
|
||||
if (!user || (user.role !== 'admin' && user.role !== 'vorstand')) {
|
||||
if (!user) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'Benutzer nicht gefunden.'
|
||||
})
|
||||
}
|
||||
|
||||
// Only admin and vorstand can add/edit members
|
||||
if (user.role !== 'admin' && user.role !== 'vorstand') {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: 'Keine Berechtigung zum Bearbeiten von Mitgliedern.'
|
||||
message: 'Keine Berechtigung zum Hinzufügen/Bearbeiten von Mitgliedern. Erforderlich: admin oder vorstand Rolle.'
|
||||
})
|
||||
}
|
||||
|
||||
const body = await readBody(event)
|
||||
const { id, firstName, lastName, email, phone, address, notes } = body
|
||||
const { id, firstName, lastName, geburtsdatum, email, phone, address, notes } = body
|
||||
|
||||
if (!firstName || !lastName) {
|
||||
throw createError({
|
||||
@@ -41,23 +57,51 @@ export default defineEventHandler(async (event) => {
|
||||
})
|
||||
}
|
||||
|
||||
await saveMember({
|
||||
id: id || undefined,
|
||||
firstName,
|
||||
lastName,
|
||||
email: email || '',
|
||||
phone: phone || '',
|
||||
address: address || '',
|
||||
notes: notes || ''
|
||||
})
|
||||
if (!geburtsdatum) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'Geburtsdatum ist erforderlich, um Duplikate zu vermeiden.'
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Mitglied erfolgreich gespeichert.'
|
||||
try {
|
||||
await saveMember({
|
||||
id: id || undefined,
|
||||
firstName,
|
||||
lastName,
|
||||
geburtsdatum: geburtsdatum || '',
|
||||
email: email || '',
|
||||
phone: phone || '',
|
||||
address: address || '',
|
||||
notes: notes || ''
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Mitglied erfolgreich gespeichert.'
|
||||
}
|
||||
} catch (memberError) {
|
||||
// Check if it's a duplicate error
|
||||
if (memberError.message && memberError.message.includes('existiert bereits')) {
|
||||
throw createError({
|
||||
statusCode: 409,
|
||||
message: memberError.message
|
||||
})
|
||||
}
|
||||
// Re-throw other errors
|
||||
throw memberError
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern des Mitglieds:', error)
|
||||
throw error
|
||||
// If it's already a createError, re-throw it
|
||||
if (error.statusCode) {
|
||||
throw error
|
||||
}
|
||||
// Otherwise wrap it
|
||||
throw createError({
|
||||
statusCode: error.statusCode || 500,
|
||||
message: error.message || 'Fehler beim Speichern des Mitglieds.'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
192
server/api/members/bulk.post.js
Normal file
192
server/api/members/bulk.post.js
Normal file
@@ -0,0 +1,192 @@
|
||||
import { verifyToken, getUserById } from '../../utils/auth.js'
|
||||
import { readMembers, writeMembers, normalizeDate } from '../../utils/members.js'
|
||||
import { randomUUID } from 'crypto'
|
||||
|
||||
// Helper function to check for duplicates in a list (with optional exclude)
|
||||
function findDuplicateMemberInList(members, firstName, lastName, geburtsdatum, excludeId = null) {
|
||||
const normalizedFirstName = (firstName || '').trim().toLowerCase()
|
||||
const normalizedLastName = (lastName || '').trim().toLowerCase()
|
||||
const normalizedDate = normalizeDate(geburtsdatum)
|
||||
|
||||
return members.find(m => {
|
||||
if (excludeId && m.id === excludeId) return false
|
||||
const mFirstName = (m.firstName || '').trim().toLowerCase()
|
||||
const mLastName = (m.lastName || '').trim().toLowerCase()
|
||||
const mDate = normalizeDate(m.geburtsdatum)
|
||||
|
||||
return mFirstName === normalizedFirstName &&
|
||||
mLastName === normalizedLastName &&
|
||||
mDate === normalizedDate &&
|
||||
mDate !== ''
|
||||
})
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Support both Cookie and Authorization Header
|
||||
let token = getCookie(event, 'auth_token')
|
||||
|
||||
if (!token) {
|
||||
const authHeader = getHeader(event, 'authorization')
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
token = authHeader.substring(7)
|
||||
}
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'Nicht authentifiziert. Bitte Token im Cookie oder Authorization-Header bereitstellen.'
|
||||
})
|
||||
}
|
||||
|
||||
const decoded = verifyToken(token)
|
||||
|
||||
if (!decoded) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'Ungültiges Token.'
|
||||
})
|
||||
}
|
||||
|
||||
const user = await getUserById(decoded.id)
|
||||
|
||||
if (!user) {
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
message: 'Benutzer nicht gefunden.'
|
||||
})
|
||||
}
|
||||
|
||||
// Only admin and vorstand can add members in bulk
|
||||
if (user.role !== 'admin' && user.role !== 'vorstand') {
|
||||
throw createError({
|
||||
statusCode: 403,
|
||||
message: 'Keine Berechtigung zum Bulk-Import von Mitgliedern. Erforderlich: admin oder vorstand Rolle.'
|
||||
})
|
||||
}
|
||||
|
||||
const body = await readBody(event)
|
||||
const { members: membersToImport } = body
|
||||
|
||||
if (!Array.isArray(membersToImport) || membersToImport.length === 0) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'Bitte senden Sie ein Array von Mitgliedern im Feld "members".'
|
||||
})
|
||||
}
|
||||
|
||||
// Validate all members before processing
|
||||
const validationErrors = []
|
||||
membersToImport.forEach((member, index) => {
|
||||
if (!member.firstName || !member.lastName) {
|
||||
validationErrors.push(`Zeile ${index + 1}: Vorname und Nachname sind erforderlich.`)
|
||||
}
|
||||
if (!member.geburtsdatum) {
|
||||
validationErrors.push(`Zeile ${index + 1}: Geburtsdatum ist erforderlich.`)
|
||||
}
|
||||
})
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: `Validierungsfehler:\n${validationErrors.join('\n')}`
|
||||
})
|
||||
}
|
||||
|
||||
// Read existing members
|
||||
const existingMembers = await readMembers()
|
||||
|
||||
const results = {
|
||||
success: [],
|
||||
duplicates: [],
|
||||
errors: []
|
||||
}
|
||||
|
||||
// Process each member
|
||||
for (let i = 0; i < membersToImport.length; i++) {
|
||||
const memberData = membersToImport[i]
|
||||
|
||||
try {
|
||||
// Check for duplicates in existing members
|
||||
const duplicateInExisting = findDuplicateMemberInList(
|
||||
existingMembers,
|
||||
memberData.firstName,
|
||||
memberData.lastName,
|
||||
memberData.geburtsdatum
|
||||
)
|
||||
|
||||
if (duplicateInExisting) {
|
||||
results.duplicates.push({
|
||||
index: i + 1,
|
||||
member: memberData,
|
||||
reason: `Existiert bereits (ID: ${duplicateInExisting.id})`
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// Check for duplicates within the import batch
|
||||
const duplicateInBatch = findDuplicateMemberInList(
|
||||
membersToImport.slice(0, i),
|
||||
memberData.firstName,
|
||||
memberData.lastName,
|
||||
memberData.geburtsdatum
|
||||
)
|
||||
|
||||
if (duplicateInBatch) {
|
||||
results.duplicates.push({
|
||||
index: i + 1,
|
||||
member: memberData,
|
||||
reason: 'Duplikat innerhalb des Imports'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// Add new member
|
||||
const newMember = {
|
||||
...memberData,
|
||||
id: memberData.id || randomUUID()
|
||||
}
|
||||
|
||||
existingMembers.push(newMember)
|
||||
results.success.push({
|
||||
index: i + 1,
|
||||
member: newMember
|
||||
})
|
||||
|
||||
} catch (error) {
|
||||
results.errors.push({
|
||||
index: i + 1,
|
||||
member: memberData,
|
||||
error: error.message || 'Unbekannter Fehler'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Save all successfully imported members
|
||||
if (results.success.length > 0) {
|
||||
await writeMembers(existingMembers)
|
||||
}
|
||||
|
||||
return {
|
||||
success: results.success.length > 0,
|
||||
summary: {
|
||||
total: membersToImport.length,
|
||||
imported: results.success.length,
|
||||
duplicates: results.duplicates.length,
|
||||
errors: results.errors.length
|
||||
},
|
||||
results: results
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Bulk-Import von Mitgliedern:', error)
|
||||
if (error.statusCode) {
|
||||
throw error
|
||||
}
|
||||
throw createError({
|
||||
statusCode: error.statusCode || 500,
|
||||
message: error.message || 'Fehler beim Bulk-Import von Mitgliedern.'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -106,6 +106,37 @@ export async function getMemberById(id) {
|
||||
return members.find(m => m.id === id)
|
||||
}
|
||||
|
||||
// Normalize date string for comparison (handles different date formats)
|
||||
export function normalizeDate(dateString) {
|
||||
if (!dateString) return ''
|
||||
// Try to parse and normalize to ISO format (YYYY-MM-DD)
|
||||
try {
|
||||
const date = new Date(dateString)
|
||||
if (isNaN(date.getTime())) return dateString.trim()
|
||||
return date.toISOString().split('T')[0]
|
||||
} catch (e) {
|
||||
return dateString.trim()
|
||||
}
|
||||
}
|
||||
|
||||
// Check for duplicate member based on firstName, lastName, and geburtsdatum
|
||||
function findDuplicateMember(members, firstName, lastName, geburtsdatum) {
|
||||
const normalizedFirstName = (firstName || '').trim().toLowerCase()
|
||||
const normalizedLastName = (lastName || '').trim().toLowerCase()
|
||||
const normalizedDate = normalizeDate(geburtsdatum)
|
||||
|
||||
return members.find(m => {
|
||||
const mFirstName = (m.firstName || '').trim().toLowerCase()
|
||||
const mLastName = (m.lastName || '').trim().toLowerCase()
|
||||
const mDate = normalizeDate(m.geburtsdatum)
|
||||
|
||||
return mFirstName === normalizedFirstName &&
|
||||
mLastName === normalizedLastName &&
|
||||
mDate === normalizedDate &&
|
||||
mDate !== '' // Only match if date is provided for both
|
||||
})
|
||||
}
|
||||
|
||||
// Add or update manual member
|
||||
export async function saveMember(memberData) {
|
||||
const members = await readMembers()
|
||||
@@ -114,11 +145,37 @@ export async function saveMember(memberData) {
|
||||
// Update existing
|
||||
const index = members.findIndex(m => m.id === memberData.id)
|
||||
if (index !== -1) {
|
||||
// Check for duplicate (excluding current member)
|
||||
const duplicate = findDuplicateMember(
|
||||
members.filter(m => m.id !== memberData.id),
|
||||
memberData.firstName,
|
||||
memberData.lastName,
|
||||
memberData.geburtsdatum
|
||||
)
|
||||
|
||||
if (duplicate) {
|
||||
throw new Error('Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.')
|
||||
}
|
||||
|
||||
members[index] = { ...members[index], ...memberData }
|
||||
} else {
|
||||
throw new Error('Mitglied nicht gefunden')
|
||||
}
|
||||
} else {
|
||||
// Add new - check for duplicate first
|
||||
if (memberData.firstName && memberData.lastName && memberData.geburtsdatum) {
|
||||
const duplicate = findDuplicateMember(
|
||||
members,
|
||||
memberData.firstName,
|
||||
memberData.lastName,
|
||||
memberData.geburtsdatum
|
||||
)
|
||||
|
||||
if (duplicate) {
|
||||
throw new Error('Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.')
|
||||
}
|
||||
}
|
||||
|
||||
// Add new - use UUID for guaranteed uniqueness
|
||||
const newMember = {
|
||||
...memberData,
|
||||
|
||||
Reference in New Issue
Block a user