refactor: convert to minimal club website for Haumdaucher n.e.V.
This commit is contained in:
parent
36c3bcc98b
commit
fdc6a6df23
|
|
@ -0,0 +1,127 @@
|
|||
# **Satzung des „HAUMDAUCHER Wurst und Spezialitäten n.e.V.“**
|
||||
|
||||
**§ 1 Name, Sitz und Online-Präsenz**
|
||||
|
||||
(1) Der Verein führt den Namen „HAUMDAUCHER Wurst und Spezialitäten n.e.V.“. Er ist ein nicht eingetragener Verein.
|
||||
|
||||
(2) Der Sitz des Vereins ist Regensburg.
|
||||
|
||||
(3) Der Verein präsentiert sich online unter www.haumdaucher.de.
|
||||
|
||||
**§ 2 Vereinszweck und „Spirit“**
|
||||
|
||||
(1) Der Verein pflegt die Kultur der Kulinarik. Es liegt in der DNA der Gemeinschaft, andere Menschen mit Spezialitäten zu bewirten und sich gegenseitig mit Köstlichkeiten zu verwöhnen. Der Fokus liegt auf, aber nicht exklusiv bei, Wurstspezialitäten.
|
||||
|
||||
(2) Der Verein ist idealistisch und nicht auf wirtschaftlichen Gewinn ausgerichtet.
|
||||
|
||||
**§ 3 Der „Wursttober“**
|
||||
|
||||
Das Hauptevent des Vereinsjahres ist der „Wursttober“. Diese Veranstaltung findet jährlich, vorzugsweise im Oktober, statt. Fester Bestandteil und Tradition dieses Events ist es, gemeinschaftlich selbst frische Wurst herzustellen. Die Veranstaltung dient darüber hinaus der Präsentation der handwerklichen Ergebnisse und der Stärkung des Gemeinschaftsgeistes.
|
||||
|
||||
**§ 4 Der Vorstand und Vertretung**
|
||||
|
||||
(1) Der **geschäftsführende Vorstand** im Sinne des § 26 BGB besteht aus:
|
||||
|
||||
a) dem 1\. Vorsitzenden
|
||||
|
||||
b) dem stellvertretenden (2.) Vorsitzenden
|
||||
|
||||
c) dem Finanzvorstand (Kassenwart)
|
||||
|
||||
(2) Der **erweiterte Vorstand** besteht aus dem geschäftsführenden Vorstand sowie den jeweiligen Abteilungsvorsitzenden. Der erweiterte Vorstand berät über die strategische und kulinarische Ausrichtung des Vereins.
|
||||
|
||||
(3) **Vertretungsbefugnis:** Der Verein wird gerichtlich und außergerichtlich durch den 1\. Vorsitzenden oder den 2\. Vorsitzenden vertreten (Einzelvertretungsbefugnis).
|
||||
|
||||
(4) **Verfügungsbeschränkung:** Für Rechtsgeschäfte und Verträge, die einen Gegenwert von 500,00 Euro überschreiten, ist die gemeinsame Unterschrift von zwei Mitgliedern des geschäftsführenden Vorstands zwingend erforderlich.
|
||||
|
||||
**§ 5 Abteilungen des Vereins**
|
||||
|
||||
(1) Zur Erfüllung der vielfältigen kulinarischen Aufgaben können innerhalb des Vereins Abteilungen gebildet werden.
|
||||
|
||||
(2) Die Gründung einer Abteilung erfolgt durch Beschluss des geschäftsführenden Vorstands.
|
||||
|
||||
(3) Jede Abteilung wird durch einen Abteilungsvorsitzenden geleitet. Dieser wird durch seine Ernennung automatisch Mitglied des erweiterten Vorstands.
|
||||
|
||||
(4) Die Einzelheiten der Abteilungsführung werden in einem separaten Abteilungsgründungsdokument festgelegt.
|
||||
|
||||
**§ 6 Mitgliedschaft, Beendigung und Ausschluss**
|
||||
|
||||
(1) Mitglied kann jeder werden, der den „HAUMDAUCHER-Spirit“ teilt. Über die Aufnahme entscheidet der Vorstand.
|
||||
|
||||
(2) Die Mitgliedschaft endet durch Austritt, Tod oder Ausschluss.
|
||||
|
||||
(3) **Ausschluss:** Mitglieder, die gegen den Vereinszweck oder den „HAUMDAUCHER-Spirit“ verstoßen, können durch einen mehrheitlichen Beschluss des geschäftsführenden Vorstands mit sofortiger Wirkung aus dem Verein ausgeschlossen werden.
|
||||
|
||||
**§ 7 Die Mitgliederversammlung und Satzungsänderungen**
|
||||
|
||||
(1) Die Mitgliederversammlung ist das oberste Organ des Vereins. Sie ist insbesondere zuständig für die Wahl des Vorstands, die Entlastung des Vorstands sowie für Satzungsänderungen.
|
||||
|
||||
(2) Eine ordentliche Mitgliederversammlung findet mindestens einmal jährlich statt (vorzugsweise im Rahmen des „Wursttobers“).
|
||||
|
||||
(3) Die Einberufung erfolgt formlos, in der Regel per E-Mail oder über die offizielle Vereins-WhatsApp-Gruppe, durch den Vorstand mit einer Frist von mindestens 7 Tagen unter Angabe der Tagesordnung.
|
||||
|
||||
(4) **Satzungsänderungen:** Änderungen der Satzung bedürfen einer Mehrheit von zwei Dritteln (2/3) der anwesenden Mitglieder in der Mitgliederversammlung.
|
||||
|
||||
**§ 8 Kassenprüfung**
|
||||
|
||||
(1) Die Mitgliederversammlung wählt für die Dauer von zwei Jahren einen Kassenprüfer, der nicht dem Vorstand angehören darf.
|
||||
|
||||
(2) Der Kassenprüfer prüft mindestens einmal jährlich die Vereinskasse und erstattet der Mitgliederversammlung Bericht, um die Entlastung des Finanzvorstands zu empfehlen.
|
||||
|
||||
(3) Haben im abgelaufenen Geschäftsjahr keine finanziellen Transaktionen (Einnahmen oder Ausgaben) über die Vereinskasse stattgefunden, kann durch Beschluss des Vorstands auf die jährliche Kassenprüfung verzichtet werden.
|
||||
|
||||
**§ 9 Datenschutz**
|
||||
|
||||
(1) Zur Erfüllung der Zwecke und Aufgaben des Vereins werden unter Beachtung der Vorgaben der EU-Datenschutz-Grundverordnung (DSGVO) und des Bundesdatenschutzgesetzes (BDSG) personenbezogene Daten der Mitglieder (Name, Mobilfunknummer, E-Mail-Adresse) verarbeitet und vereinsintern gespeichert.
|
||||
|
||||
(2) Die Kommunikation im Verein erfolgt maßgeblich über E-Mail und Messenger-Dienste (z. B. WhatsApp). Jedes Mitglied stimmt der Aufnahme in entsprechende Verteiler und Gruppen mit seinem Beitritt zu.
|
||||
|
||||
**§ 10 Haftungsbeschränkung**
|
||||
|
||||
(1) Die Haftung der Mitglieder des Vorstands (geschäftsführend und erweitert) gegenüber dem Verein und seinen Mitgliedern ist auf Vorsatz und grobe Fahrlässigkeit beschränkt.
|
||||
|
||||
(2) Werden Vorstandsmitglieder durch Dritte auf Ersatz eines Schadens in Anspruch genommen, der bei der Wahrnehmung der Vorstandspflichten entstanden ist, so stellt der Verein sie im Innenverhältnis von der Haftung frei, sofern sie nicht vorsätzlich oder grob fahrlässig gehandelt haben.
|
||||
|
||||
**§ 11 Auflösung**
|
||||
|
||||
Bei Auflösung des Vereins, welche nur mit einer 3/4-Mehrheit der Mitgliederversammlung beschlossen werden kann, fällt das verbleibende Vermögen (sowie Ausrüstung und Vorräte) zu gleichen Teilen an die zum Zeitpunkt der Auflösung bestehenden Mitglieder.
|
||||
|
||||
*Beschlossen und unterschrieben in der Gründungsversammlung am \_\_\_\_\_\_\_\_\_ in Etterzhausen.*
|
||||
|
||||
*Liste der Gründungsmitglieder*
|
||||
|
||||
*Name Unterschrift*
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
||||
\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_ \_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_\_
|
||||
|
|
@ -74,6 +74,7 @@
|
|||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
|
|
@ -2118,6 +2119,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.6.tgz",
|
||||
"integrity": "sha512-4uyt8BOrBsSq6i4yiOV/gG6BnnrvTeyymlNcaN/dKvyU1GoolxAafvIvaNP1RCGPlNab3OuE4MKUQuv2lH+PLQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@firebase/component": "0.7.0",
|
||||
"@firebase/logger": "0.5.0",
|
||||
|
|
@ -2184,6 +2186,7 @@
|
|||
"resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.6.tgz",
|
||||
"integrity": "sha512-YYGARbutghQY4zZUWMYia0ib0Y/rb52y72/N0z3vglRHL7ii/AaK9SA7S/dzScVOlCdnbHXz+sc5Dq+r8fwFAg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@firebase/app": "0.14.6",
|
||||
"@firebase/component": "0.7.0",
|
||||
|
|
@ -2199,7 +2202,8 @@
|
|||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz",
|
||||
"integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==",
|
||||
"license": "Apache-2.0"
|
||||
"license": "Apache-2.0",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@firebase/auth": {
|
||||
"version": "1.12.0",
|
||||
|
|
@ -2650,6 +2654,7 @@
|
|||
"integrity": "sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
|
|
@ -3680,6 +3685,7 @@
|
|||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
|
|
@ -3908,6 +3914,7 @@
|
|||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
|
|
@ -4741,6 +4748,7 @@
|
|||
"resolved": "https://registry.npmjs.org/firebase/-/firebase-12.7.0.tgz",
|
||||
"integrity": "sha512-ZBZg9jFo8uH4Emd7caOqtalKJfDGHnHQSrCPiqRAdTFQd0wL3ERilUBfhnhBLnlernugkN/o7nJa0p+sE71Izg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@firebase/ai": "2.6.1",
|
||||
"@firebase/analytics": "0.10.19",
|
||||
|
|
@ -5053,6 +5061,7 @@
|
|||
"integrity": "sha512-QsCdAUHAmiDeKeaNojb1OHOPF7NjcWPBR7obdu3NwH2a/oyQaLg5d0aaCy/9My6CdPChYF07dvz5chaXBGaD4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/whatwg-mimetype": "^3.0.2",
|
||||
|
|
@ -6369,6 +6378,7 @@
|
|||
"integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
|
|
@ -7167,6 +7177,7 @@
|
|||
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
|
@ -7315,6 +7326,7 @@
|
|||
"integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.18.10",
|
||||
"postcss": "^8.4.27",
|
||||
|
|
@ -8012,6 +8024,7 @@
|
|||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz",
|
||||
"integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.26",
|
||||
"@vue/compiler-sfc": "3.5.26",
|
||||
|
|
@ -8483,6 +8496,7 @@
|
|||
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,25 @@
|
|||
import { describe, it, expect } from 'vitest'
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import App from './App.vue'
|
||||
|
||||
vi.mock('./firebase', () => ({
|
||||
auth: {},
|
||||
db: {}
|
||||
}))
|
||||
|
||||
vi.mock('./composables/useAuth', () => {
|
||||
const { ref } = require('vue')
|
||||
return {
|
||||
useAuth: () => ({
|
||||
isAllowed: true,
|
||||
user: ref(null),
|
||||
login: vi.fn(),
|
||||
logout: vi.fn(),
|
||||
error: null
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe('Smoke Test', () => {
|
||||
it('mounts properly', () => {
|
||||
const wrapper = mount(App)
|
||||
|
|
|
|||
19
src/App.vue
19
src/App.vue
|
|
@ -2,9 +2,9 @@
|
|||
import { ref, onMounted } from 'vue'
|
||||
import Header from './components/layout/Header.vue'
|
||||
import Hero from './components/sections/Hero.vue'
|
||||
import About from './components/sections/About.vue'
|
||||
import History from './components/sections/History.vue'
|
||||
import Beer from './components/sections/Beer.vue'
|
||||
import ClubSpirit from './components/sections/ClubSpirit.vue'
|
||||
import Impressum from './components/sections/Legal/Impressum.vue'
|
||||
import Datenschutz from './components/sections/Legal/Datenschutz.vue'
|
||||
import HaumdaucherGame from './components/layout/HaumdaucherGame.vue'
|
||||
import { messages } from './locales/i18n'
|
||||
import { useAuth } from './composables/useAuth'
|
||||
|
|
@ -102,9 +102,11 @@ const t = (key: string) => {
|
|||
<!-- Member Banner -->
|
||||
|
||||
<Hero :theme="theme" :t="t" />
|
||||
<About :t="t" />
|
||||
<History :t="t" />
|
||||
<Beer :t="t" />
|
||||
<ClubSpirit :t="t" />
|
||||
|
||||
<!-- Legal Pages Section -->
|
||||
<Impressum :t="t" />
|
||||
<Datenschutz :t="t" />
|
||||
|
||||
<!-- Hidden Backdoor Pixel -->
|
||||
<div class="backdoor" @click.stop="handleBackdoor"></div>
|
||||
|
|
@ -126,11 +128,12 @@ const t = (key: string) => {
|
|||
</div>
|
||||
|
||||
<div v-for="boar in boars" :key="boar.id" class="running-boar" :style="{ top: boar.top + '%' }">🐗💨</div>
|
||||
<footer class="container" style="padding: 40px 0; opacity: 0.6; font-size: 0.9em;">
|
||||
© {{ new Date().getFullYear() }} Haumdaucher Regensburg. Alles für den Vogel.
|
||||
<footer class="container" style="padding: 40px 0; opacity: 0.6; font-size: 0.9em; text-align: center;">
|
||||
© {{ new Date().getFullYear() }} HAUMDAUCHER Wurst und Spezialitäten n.e.V. (in Gründung)
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
|
||||
<style>
|
||||
@import './assets/styles/global.css';
|
||||
|
||||
|
|
|
|||
|
|
@ -92,18 +92,17 @@ watch(user, (u) => {
|
|||
<!-- Mobile Bottom Nav -->
|
||||
<nav class="fancy-glass mobile-nav">
|
||||
<a href="#home" class="nav-item">🏠<span>{{ t('nav.home') }}</span></a>
|
||||
<a href="#about" class="nav-item">👥<span>{{ t('nav.about') }}</span></a>
|
||||
<a href="#spirit" class="nav-item">✨<span>{{ t('nav.spirit') }}</span></a>
|
||||
<button class="nav-item game-btn" @click="emit('open:game')">🕹️<span>{{ t('nav.game') }}</span></button>
|
||||
<a href="#history" class="nav-item">📜<span>{{ t('nav.history') }}</span></a>
|
||||
<a href="#beer" class="nav-item">🍺<span>{{ t('nav.beer') }}</span></a>
|
||||
<a href="#impressum" class="nav-item">⚖️<span>Impressum</span></a>
|
||||
</nav>
|
||||
|
||||
<!-- Desktop Side Nav / Links -->
|
||||
<nav class="desktop-links">
|
||||
<div class="container">
|
||||
<a href="#about">{{ t('nav.about') }}</a>
|
||||
<a href="#history">{{ t('nav.history') }}</a>
|
||||
<a href="#beer">{{ t('nav.beer') }}</a>
|
||||
<a href="#spirit">{{ t('nav.spirit') }}</a>
|
||||
<a href="#impressum">Impressum</a>
|
||||
<a href="#datenschutz">Datenschutz</a>
|
||||
<button class="game-nav-btn" @click="emit('open:game')">{{ t('nav.game') }}</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
t: (key: string) => string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="about" class="about">
|
||||
<div class="container fancy-glass alternate-bg">
|
||||
<h2 class="section-title">{{ t('about.title') }}</h2>
|
||||
<p class="section-text">{{ t('about.content') }}</p>
|
||||
<div class="friends-grid">
|
||||
<div v-for="i in 5" :key="i" class="friend-avatar">🍻</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.about {
|
||||
background: linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.02) 100%);
|
||||
}
|
||||
|
||||
.alternate-bg {
|
||||
padding: 60px;
|
||||
border-radius: 40px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: clamp(1.8rem, 8vw, 3rem);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-text {
|
||||
font-size: clamp(1rem, 4vw, 1.3rem);
|
||||
max-width: 800px;
|
||||
margin: 0 auto 30px;
|
||||
}
|
||||
|
||||
.friends-grid {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.friend-avatar {
|
||||
background: rgba(var(--primary-color), 0.1);
|
||||
padding: 20px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
t: (key: string) => string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="beer" class="beer">
|
||||
<div class="container fancy-glass alternate-bg">
|
||||
<h2 class="section-title">{{ t('beer.title') }}</h2>
|
||||
<p class="section-text">{{ t('beer.content') }}</p>
|
||||
<div class="beer-animation">🍺 🍺 🍺</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.history {
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: clamp(1.8rem, 8vw, 3rem);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-text {
|
||||
font-size: clamp(1rem, 4vw, 1.3rem);
|
||||
max-width: 800px;
|
||||
margin: 0 auto 30px;
|
||||
}
|
||||
|
||||
.bird-accent {
|
||||
font-size: 5rem;
|
||||
margin-top: 40px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
t: (key: string) => string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="spirit" class="glass-section container">
|
||||
<h2>{{ t('spirit.title') }}</h2>
|
||||
<div class="content-text">
|
||||
<p>{{ t('spirit.content') }}</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.glass-section {
|
||||
background: var(--glass-bg);
|
||||
border: var(--glass-border);
|
||||
box-shadow: var(--glass-shadow);
|
||||
backdrop-filter: var(--glass-blur);
|
||||
-webkit-backdrop-filter: var(--glass-blur);
|
||||
border-radius: 20px;
|
||||
padding: 40px;
|
||||
margin-bottom: 40px;
|
||||
color: var(--text-color);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 20px;
|
||||
color: var(--primary-color);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.8;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.glass-section {
|
||||
padding: 20px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
.content-text {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
t: (key: string) => string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="history" class="history">
|
||||
<div class="container fancy-glass alternate-bg">
|
||||
<h2 class="section-title">{{ t('history.title') }}</h2>
|
||||
<p class="section-text">{{ t('history.content') }}</p>
|
||||
<div class="bird-accent">🦢</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.beer {
|
||||
padding-bottom: 150px;
|
||||
}
|
||||
|
||||
.alternate-bg {
|
||||
padding: 60px;
|
||||
border-radius: 40px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: clamp(1.8rem, 8vw, 3rem);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-text {
|
||||
font-size: clamp(1rem, 4vw, 1.3rem);
|
||||
max-width: 800px;
|
||||
margin: 0 auto 30px;
|
||||
}
|
||||
|
||||
.beer-animation {
|
||||
font-size: 4rem;
|
||||
animation: clink 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes clink {
|
||||
0%, 100% { transform: rotate(0); }
|
||||
50% { transform: rotate(10deg) scale(1.1); }
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
t: (key: string) => string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="datenschutz" class="glass-section container legal-section">
|
||||
<h2>{{ t('datenschutz.title') }}</h2>
|
||||
<div class="content-text">
|
||||
<p>{{ t('datenschutz.content') }}</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.legal-section {
|
||||
background: rgba(0, 0, 0, 0.02);
|
||||
border: var(--glass-border);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 15px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
white-space: pre-line;
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
t: (key: string) => string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section id="impressum" class="glass-section container legal-section">
|
||||
<h2>{{ t('impressum.title') }}</h2>
|
||||
<div class="content-text">
|
||||
<p>{{ t('impressum.content') }}</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.legal-section {
|
||||
margin-top: 60px;
|
||||
background: rgba(0, 0, 0, 0.02);
|
||||
border: var(--glass-border);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 15px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
white-space: pre-line;
|
||||
opacity: 0.8;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -10,18 +10,22 @@ const isLoading = ref(true)
|
|||
const error = ref<string | null>(null)
|
||||
|
||||
// Watch auth state
|
||||
auth.onAuthStateChanged((u) => {
|
||||
if (auth) {
|
||||
auth.onAuthStateChanged((u) => {
|
||||
console.log("🔥 Auth State Changed:", u ? u.email : "Logged Out")
|
||||
user.value = u
|
||||
if (!u) {
|
||||
isAllowed.value = false
|
||||
isLoading.value = false
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
// Check allowlist
|
||||
watchEffect((onCleanup) => {
|
||||
if (!user.value) return
|
||||
if (!user.value || !db) return
|
||||
|
||||
// Subscribe to the config/allowlist document
|
||||
const allowlistRef = doc(db, 'config', 'allowlist')
|
||||
|
|
@ -46,6 +50,10 @@ watchEffect((onCleanup) => {
|
|||
export function useAuth() {
|
||||
const login = async () => {
|
||||
error.value = null
|
||||
if (!auth) {
|
||||
error.value = "Firebase is not configured!"
|
||||
return
|
||||
}
|
||||
try {
|
||||
const provider = new GoogleAuthProvider()
|
||||
await signInWithPopup(auth, provider)
|
||||
|
|
@ -58,6 +66,7 @@ export function useAuth() {
|
|||
|
||||
const logout = async () => {
|
||||
error.value = null
|
||||
if (!auth) return
|
||||
try {
|
||||
await signOut(auth)
|
||||
user.value = null
|
||||
|
|
|
|||
|
|
@ -13,9 +13,14 @@ const firebaseConfig = {
|
|||
appId: import.meta.env.VITE_FIREBASE_APP_ID
|
||||
}
|
||||
|
||||
// Initialize Firebase
|
||||
const app = initializeApp(firebaseConfig)
|
||||
// Initialize Firebase safely
|
||||
export let auth: ReturnType<typeof getAuth> | null = null;
|
||||
export let db: ReturnType<typeof getFirestore> | null = null;
|
||||
|
||||
// Initialize services
|
||||
export const auth = getAuth(app)
|
||||
export const db = getFirestore(app)
|
||||
if (firebaseConfig.apiKey) {
|
||||
const app = initializeApp(firebaseConfig)
|
||||
auth = getAuth(app)
|
||||
db = getFirestore(app)
|
||||
} else {
|
||||
console.warn("⚠️ Firebase API Key missing. Firebase features are disabled.")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,26 +2,24 @@ export const messages = {
|
|||
de: {
|
||||
nav: {
|
||||
home: 'Start',
|
||||
about: 'Über uns',
|
||||
history: 'Geschichte',
|
||||
beer: 'Bier & Gaudi',
|
||||
spirit: 'Spirit',
|
||||
game: 'Haumdaucher Game'
|
||||
},
|
||||
hero: {
|
||||
title: 'D\'Haumdaucher',
|
||||
subtitle: 'Eine Gemeinschaft aus Regensburg – offen für alle.'
|
||||
title: 'Haumdaucher',
|
||||
subtitle: 'Wurst und Spezialitäten n.e.V. – in Gründung'
|
||||
},
|
||||
about: {
|
||||
title: 'Wer wir sind',
|
||||
content: 'Wir sind eine Truppe von etwa 15 Freunden aus Regensburg, die sich schon ewig kennen. Doch wir sind kein geschlossener Kreis: Wir freuen uns über jede neue Bekanntschaft und sind offen für die Welt.'
|
||||
spirit: {
|
||||
title: 'Unser Spirit',
|
||||
content: 'Wir pflegen die Kultur der Kulinarik. Es liegt in unserer DNA, andere Menschen mit Spezialitäten zu bewirten und uns gegenseitig mit Köstlichkeiten zu verwöhnen. Der Fokus liegt bei uns auf – aber nicht exklusiv bei – echtem Wurst-Handwerk.'
|
||||
},
|
||||
history: {
|
||||
title: 'Die Geschichte der Haumdaucher',
|
||||
content: 'Der "Haubentaucher" ist ein edler Vogel. Wir haben daraus den "Haumdaucher" gemacht – bayerisch, bodenständig, aber mit dem Blick über den Tellerrand.'
|
||||
impressum: {
|
||||
title: 'Impressum',
|
||||
content: 'HAUMDAUCHER Wurst und Spezialitäten n.e.V. (in Gründung)\n\nVertreten durch:\n[Name des Vertreters]\n[Straße Hausnummer]\n[PLZ Ort]\n\nKontakt:\nE-Mail: info@haumdaucher.de'
|
||||
},
|
||||
beer: {
|
||||
title: 'Bier & Gaudi',
|
||||
content: 'Geselligkeit steht bei uns an erster Stelle. Ob beim bayerischen Hellen oder beim Austausch mit neuen Gesichtern – bei uns ist jeder willkommen.'
|
||||
datenschutz: {
|
||||
title: 'Datenschutzerklärung',
|
||||
content: 'Wir nehmen den Schutz Ihrer persönlichen Daten sehr ernst. Als reine Repräsentationsseite erheben wir keine personenbezogenen Daten von Besuchern, es sei denn, dies ist technisch für die Bereitstellung der Website absolut notwendig (z.B. Server-Logfiles). Wir verwenden keine Cookies zur Nachverfolgung Ihres Surfverhaltens.'
|
||||
},
|
||||
themes: {
|
||||
classic: 'Klassisch',
|
||||
|
|
@ -41,26 +39,24 @@ export const messages = {
|
|||
bar: {
|
||||
nav: {
|
||||
home: 'Dahoam',
|
||||
about: 'Wer mir san',
|
||||
history: 'Wia ois o’gfanga hod',
|
||||
beer: 'Bia & Gaudi',
|
||||
spirit: 'Unsara Spirit',
|
||||
game: 'Haumdaucher Spui'
|
||||
},
|
||||
hero: {
|
||||
title: 'D\'Haumdaucher',
|
||||
subtitle: 'A echte Rengschbuaga G’schicht – offn fia olle.'
|
||||
subtitle: 'Wuarscht und Spezialitätn n.e.V. – owei no am werkeln'
|
||||
},
|
||||
about: {
|
||||
title: 'Wer mir san',
|
||||
content: 'Mir san a Hauffa vo ca. 15 Spezln aus Rengschbuag. Mia hoidn zam, oba mir san fia jedn offn, dea a bisserl a Gmiatlichkeit midbringt.'
|
||||
spirit: {
|
||||
title: 'Unsara Spirit',
|
||||
content: 'Mir lem d\'Kultur vom guatn Essn. Bei uns in da DNA steht gschriem, dass ma andre Leid mid Spezialitätn gmiatlich bekocht und si a moi gegenseitig verwehnt. Da Fokus liegt hoid – oba ned nua – af echtem Wuarscht-Handwerk.'
|
||||
},
|
||||
history: {
|
||||
title: 'Vom Haubndaucha zum Haumdaucha',
|
||||
content: 'Da "Haubentaucher" is a scheena Vogl. Mia hom an "Haumdaucher" draus gmocht – boarisch, ehrlich und offn fia de ganze Welt.'
|
||||
impressum: {
|
||||
title: 'Impressum (Rechtlichs Zoig)',
|
||||
content: 'HAUMDAUCHER Wurst und Spezialitäten n.e.V. (in Gründung)\n\nVadredn durch:\n[Name des Vertreters]\n[Straße Hausnummer]\n[PLZ Ort]\n\nKontakt:\nE-Mail: info@haumdaucher.de'
|
||||
},
|
||||
beer: {
|
||||
title: 'Bia & Gaudi',
|
||||
content: 'Gmiatlickheit is des Wichtigste. Egal ob mid am gscheidn Hellen oda mid neie Leit – bei uns ghead jeda dazua.'
|
||||
datenschutz: {
|
||||
title: 'Datenschutz',
|
||||
content: 'Mir passn af dei Datn auf wia af a rohes Ei. Weil des nua a Schauseitn is, sammln mia a koane Datn vo dir eib, außa des wos da Server unbedingt braucht damits lafft. Mir schiabn dir a koane Cookies unda.'
|
||||
},
|
||||
themes: {
|
||||
classic: 'Klassisch',
|
||||
|
|
|
|||
Loading…
Reference in New Issue