Initial commit

This commit is contained in:
Mindboost
2025-07-01 10:53:26 +00:00
commit 38050e5c69
416 changed files with 48708 additions and 0 deletions

207
pages/settings/_index.vue Normal file
View File

@@ -0,0 +1,207 @@
<template>
<div>
<div class="row justify-content-center">
<div class="col-12 col-lg-9 col-md-11 col-sm-12 col-xl-8">
<!-- Header Section -->
<div class="row">
<div class="col-12">
<h1 class="h3 fw-bold text-center pt-3">
{{ t('Account') }}
</h1>
</div>
</div>
<!-- Account Details -->
<div class="border-bottom row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('First Name') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-end text-muted">
{{ user.first_name }}
</p>
</div>
</div>
<div class="border-bottom row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Last Name') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-end text-muted">
{{ user.last_name }}
</p>
</div>
</div>
<div class="border-bottom row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Language') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-end text-muted">
{{ user.language == 'de' ? 'German' : 'English' }}
</p>
</div>
</div>
<div class="border-bottom row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Email') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-end text-muted">
{{ user.email }}
</p>
</div>
</div>
<div class="border-bottom row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Password') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-end text-muted"></p>
</div>
</div>
<!-- Conditional Manager Details -->
<div v-if="user.inviter" class="border-bottom row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Manager Name') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-end text-muted">
{{ user.inviter.first_name ?? '' }} {{ user.inviter.last_name ?? '' }}
</p>
</div>
</div>
<div v-if="user.inviter" class="border-bottom row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Manager Email') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-end text-muted">
{{ user.inviter.email ?? '' }}
</p>
</div>
</div>
<div v-if="user.inviter" class="row pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Current Package') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-end text-muted">
{{ showManagerPlan() }}
</p>
</div>
</div>
<!-- Logout Button -->
<div class="account-buttons d-flex justify-content-center pt-5">
<nuxt-link
:to="localePath('/settings/editaccount')"
class="btn btn-primary-custom text-decoration-none"
>
{{ t('Edit_') }}
</nuxt-link>
<button class="btn fw-bold btn-outline-dark" @click="logoutNow">
{{ t("Log Out") }}
</button>
</div>
<!-- Subscription Call-to-Action -->
<div class="row pt-5 pb-4 px-2 mx-0">
<div
class="col-12 rounded text-center py-3 py-md-5"
style="background-image: linear-gradient(40.53deg, #E9A446 6.68%, #E9C046 100%)"
>
<h4 class="text-center text-white">
{{ t("Get another free 30 days") }}
</h4>
<h5 class="text-center px-2 px-md-5 text-white pt-2">
{{ t("You can still use Mindboost for 30 days for free. Subscribe to increase your productivity with Mindboost afterwards.") }}
</h5>
<NuxtLink
disabled
type="button"
class="btn px-4 py-2 mt-2 mt-md-3 fs-5"
style="background-color: white; gap: 10px;"
>
{{ t("Subscribe") }}
</NuxtLink>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { useI18n } from 'vue-i18n'
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user'
// Composition API setup
const { t } = useI18n()
const localePath = useLocalePath()
const userStore = useUserStore()
const router = useRouter()
// Computed property for user state
const user = computed(() => userStore.user)
// Logout method
const logoutNow = async () => {
await userStore.logout()
try {
await $axios.post('/api/logout')
} catch (error) {
// Handle the error if needed
}
router.push(localePath('/auth/login'))
}
function showManagerPlan () {
if (user.value.inviter.current_subscription_plan === 1) {
return 'Basic'
} else if (user.value.inviter.current_subscription_plan === 2) {
return 'Team'
} else if (user.value.inviter.current_subscription_plan === 3) {
return 'Enterprise'
}
// this.$toast.success(`Manager already has the ${managerPlan} plan`) // Fixed typo
}
</script>
<style>
.account-buttons {
gap: 1.5em;
}
@media only screen and (max-width: 576px) {
.account-buttons {
flex-direction: column;
gap: 0.75em;
}
}
</style>

60
pages/settings/about.vue Normal file
View File

@@ -0,0 +1,60 @@
<template>
<div>
<div class="row justify-content-center">
<div class="col-12 col-lg-9 col-md-11 col-sm-12 col-xl-8">
<h1 class="h3 fw-bold text-center py-3">
{{ t('About Mindboost') }}
</h1>
<div class="row pt-4">
<div class="col-12">
<h2 class="h6 fw-bold">
{{ t('WHAT IS MINDBOOST?') }}
</h2>
<p class="text-muted ">
{{ t('Mindboost creates soundscapes for your headphones that help you focus up to 35% better. Mindboost combines voice masking, binaural beats and alpha waves.') }}
</p>
</div>
<div class="col-12 pt-3">
<h2 class="h6 fw-bold">
{{ t('HOW DOES MINDBOOST WORK?') }}
</h2>
<p class="text-muted ">
{{ t('Our algorithm measures the acoustics of your cell phone or other device in the room in real time and generates a soundscape that optimally blocks out disruptive background noise such as colleagues on the phone.') }}
</p>
</div>
<div class="col-12 pt-3">
<h2 class="h6 fw-bold">
{{ t('WHY DOES MINDBOOST WORK?') }}
</h2>
<p class="text-muted ">
{{ t('All soundscapes in Mindboost have been tested and optimized in listening tests in cooperation with the Fraunhofer IBP. So you can be sure that Mindboost supports you optimally with your concentration.') }}
</p>
</div>
<div class="col-12 pt-3">
<h2 class="h6 fw-bold">
{{ t('WHO WE ARE?') }}
</h2>
<p class="text-muted ">
{{ t('We are a small team of audio developers. Inspiried by the power of sound, well known from music, we cannot stop exploring the digital audio world. We are happy that we can be part of a community that uses audio next to practical services mainly to generate fun.') }}
</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SettingPage',
setup () {
const { t } = useI18n()
const localePath = useLocalePath()
return { t, localePath }
}
}
</script>
<style>
</style>

271
pages/settings/account.vue Normal file
View File

@@ -0,0 +1,271 @@
<template>
<div>
<div class="row justify-content-center">
<div class="col-12 col-lg-9 col-md-11 col-sm-12 col-xl-8">
<!-- Header Section -->
<div class="row">
<div class="col-12">
<h1 class="h3 fw-bold text-center pt-3">
{{ t('Account') }}
</h1>
</div>
</div>
<!-- Account Details -->
<div class="border-bottom row flex-column flex-sm-row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('First Name') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-sm-end text-muted">
{{ user.first_name }}
</p>
</div>
</div>
<div class="border-bottom row flex-column flex-sm-row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Last Name') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-sm-end text-muted">
{{ user.surname }}
</p>
</div>
</div>
<div class="border-bottom row flex-column flex-sm-row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Language') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-sm-end text-muted">
{{ user.language == 'de' ? 'German' : 'English' }}
</p>
</div>
</div>
<div class="border-bottom row flex-column flex-sm-row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Email') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-sm-end text-muted">
{{ user.email }}
</p>
</div>
</div>
<div class="border-bottom row flex-column flex-sm-row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Password') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-sm-end text-muted"></p>
</div>
</div>
<!-- Conditional Manager Details -->
<div v-if="user.inviter" class="border-bottom row flex-column flex-sm-row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Manager Name') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-sm-end text-muted">
{{ user.inviter.first_name ?? '' }} {{ user.inviter.surname ?? '' }}
</p>
</div>
</div>
<div v-if="user.inviter" class="border-bottom row flex-column flex-sm-row mx-0 pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Manager Email') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-sm-end text-muted">
{{ user.inviter.email ?? '' }}
</p>
</div>
</div>
<div v-if="user.inviter" class="row flex-column flex-sm-row pt-4">
<div class="col-6">
<p class="fw-bold text-start">
{{ t('Current Package') }}
</p>
</div>
<div class="col-6">
<p class="fw-bold text-sm-end text-muted">
{{ showManagerPlan() }}
</p>
</div>
</div>
<!-- Logout Button -->
<div class="account-buttons d-flex justify-content-center pt-5">
<nuxt-link
:to="localePath('/settings/editaccount')"
class="btn btn-primary-custom text-decoration-none"
>
{{ t('Edit_') }}
</nuxt-link>
<button class="btn fw-bold btn-outline-dark" @click="logoutNow">
{{ t("Log Out") }}
</button>
</div>
</div>
</div>
<div class="row">
<button type="button" class="btn btn-link position-absolute bottom-0 start-50 translate-middle-x mb-3" @click="showModal = true">
{{ t("Delete Account") }}
</button>
</div>
<div
class="modal fade"
tabindex="-1"
:class="{ show: showModal }"
:style="showModal ? 'display: block;' : 'display: none;'"
aria-modal="true"
role="dialog"
@click.self="closeModal"
>
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">{{ t("Delete Account") }}</h5>
<button type="button" class="btn-close" @click="closeModal" />
</div>
<div class="modal-body">
<p>{{ t('Delete Account Text') }}</p>
</div>
<div class="modal-footer">
<button class="btn btn-dark-custom" @click="closeModal">{{ t('Cancel') }}</button>
<button class="btn btn-primary-custom" @click="deleteAccount">{{ t('Delete Account') }}</button>
</div>
</div>
</div>
</div>
<!-- Hintergrund-Overlay -->
<div v-if="showModal" class="modal-backdrop fade show" />
</div>
</template>
<script>
import { mapActions, mapState } from 'pinia'
import { useI18n } from 'vue-i18n'
import { computed } from 'vue'
import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user'
export default {
name: 'Account',
setup () {
const { t } = useI18n()
const localePath = useLocalePath()
const userStore = useUserStore()
const user = useUserStore().user
const router = useRouter()
return { t, localePath, router, user, userStore }
},
data () {
return {
showModal: false,
loading: false
}
},
methods: {
async logoutNow () {
await this.userStore.logout()
try {
await this.$axios.post('/api/logout')
} catch (error) {
// Handle the error if needed
}
this.$router.push(this.localePath('/auth/login'))
},
closeModal () {
this.showModal = false
},
async deleteAccount () {
this.loading = true
try {
await this.$axios.post('/api/account/delete')
await this.userStore.logout()
this.$router.push(this.localePath('/auth/login'))
this.$toast.success('Account gelöscht. Du wurdest ausgeloggt.')
} catch (error) {
this.$toast.error('Fehler beim Löschen: ' + (error.response?.data?.message || 'Unbekannter Fehler'))
} finally {
this.loading = false
this.closeModal()
}
},
showManagerPlan () {
if (user.value.inviter.current_subscription_plan === 1) {
return 'Basic'
} else if (user.value.inviter.current_subscription_plan === 2) {
return 'Team'
} else if (user.value.inviter.current_subscription_plan === 3) {
return 'Enterprise'
}
}
}
}
</script>
<style lang="scss" scoped>
.btn-primary-custom {
background-color: #e9c046;
border-color: #e9c046;
color: white;
font-weight: 700;
}
.btn-primary-custom:hover, .btn-primary-custom:focus{
background-color: transparent;
border-color: #e9c046;
color: #e9c046;
}
.account-buttons {
gap: 1.5em;
}
@media only screen and (max-width: 576px) {
.account-buttons {
flex-direction: column;
gap: 0.75em;
}
}
.modal {
z-index: 1050;
}
.modal-backdrop {
z-index: 1040;
}
.btn-link {
color: black;
transition: 250ms ease-in-out;
&:hover, &:focus {
color: #e9c046;
}
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<div>
<div class="row justify-content-center">
<div class="col-12 col-lg-9 col-md-11 col-sm-12 col-xl-8">
<h1 class="h3 fw-bold py-3">Datenschutz&shy;erkl&auml;rung</h1>
<div class="row pt-4">
<div class="col-12">
<h2 class="h4">1. Datenschutz auf einen Blick</h2>
<h3 class="h5">Allgemeine Hinweise</h3> <p>Die folgenden Hinweise geben einen einfachen &Uuml;berblick dar&uuml;ber, was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind alle Daten, mit denen Sie pers&ouml;nlich identifiziert werden k&ouml;nnen. Ausf&uuml;hrliche Informationen zum Thema Datenschutz entnehmen Sie unserer unter diesem Text aufgef&uuml;hrten Datenschutzerkl&auml;rung.</p>
<h3 class="h5">Datenerfassung auf dieser Website</h3>
<h4 class="h6">Wer ist verantwortlich f&uuml;r die Datenerfassung auf dieser Website?</h4> <p>Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen Kontaktdaten k&ouml;nnen Sie dem Abschnitt &bdquo;Hinweis zur Verantwortlichen Stelle&ldquo; in dieser Datenschutzerkl&auml;rung entnehmen.</p> <h4 class="h6">Wie erfassen wir Ihre Daten?</h4> <p>Ihre Daten werden zum einen dadurch erhoben, dass Sie uns diese mitteilen. Hierbei kann es sich z.&nbsp;B. um Daten handeln, die Sie in ein Kontaktformular eingeben.</p> <p>Andere Daten werden automatisch oder nach Ihrer Einwilligung beim Besuch der Website durch unsere IT-Systeme erfasst. Das sind vor allem technische Daten (z.&nbsp;B. Internetbrowser, Betriebssystem oder Uhrzeit des Seitenaufrufs). Die Erfassung dieser Daten erfolgt automatisch, sobald Sie diese Website betreten.</p> <h4 class="h6">Wof&uuml;r nutzen wir Ihre Daten?</h4> <p>Ein Teil der Daten wird erhoben, um eine fehlerfreie Bereitstellung der Website zu gew&auml;hrleisten. Andere Daten k&ouml;nnen zur Analyse Ihres Nutzerverhaltens verwendet werden.</p> <h4 class="h6">Welche Rechte haben Sie bez&uuml;glich Ihrer Daten?</h4> <p>Sie haben jederzeit das Recht, unentgeltlich Auskunft &uuml;ber Herkunft, Empf&auml;nger und Zweck Ihrer gespeicherten personenbezogenen Daten zu erhalten. Sie haben au&szlig;erdem ein Recht, die Berichtigung oder L&ouml;schung dieser Daten zu verlangen. Wenn Sie eine Einwilligung zur Datenverarbeitung erteilt haben, k&ouml;nnen Sie diese Einwilligung jederzeit f&uuml;r die Zukunft widerrufen. Au&szlig;erdem haben Sie das Recht, unter bestimmten Umst&auml;nden die Einschr&auml;nkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen. Des Weiteren steht Ihnen ein Beschwerderecht bei der zust&auml;ndigen Aufsichtsbeh&ouml;rde zu.</p> <p>Hierzu sowie zu weiteren Fragen zum Thema Datenschutz k&ouml;nnen Sie sich jederzeit an uns wenden.</p>
<h2 class="h4">2. Hosting</h2>
<p>Wir hosten die Inhalte unserer Website bei folgendem Anbieter:</p>
<h3 class="h5">Strato</h3> <p>Anbieter ist die Strato AG, Otto-Ostrowski-Stra&szlig;e 7, 10249 Berlin (nachfolgend &bdquo;Strato&ldquo;). Wenn Sie unsere Website besuchen, erfasst Strato verschiedene Logfiles inklusive Ihrer IP-Adressen.</p> <p>Weitere Informationen entnehmen Sie der Datenschutzerkl&auml;rung von Strato: <a href="https://www.strato.de/datenschutz/" target="_blank" rel="noopener noreferrer">https://www.strato.de/datenschutz/</a>.</p> <p>Die Verwendung von Strato erfolgt auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO. Wir haben ein berechtigtes Interesse an einer m&ouml;glichst zuverl&auml;ssigen Darstellung unserer Website. Sofern eine entsprechende Einwilligung abgefragt wurde, erfolgt die Verarbeitung ausschlie&szlig;lich auf Grundlage von Art. 6 Abs. 1 lit. a DSGVO und &sect; 25 Abs. 1 TTDSG, soweit die Einwilligung die Speicherung von Cookies oder den Zugriff auf Informationen im Endger&auml;t des Nutzers (z.&nbsp;B. Device-Fingerprinting) im Sinne des TTDSG umfasst. Die Einwilligung ist jederzeit widerrufbar.</p>
<h4 class="h6">Auftragsverarbeitung</h4> <p>Wir haben einen Vertrag &uuml;ber Auftragsverarbeitung (AVV) zur Nutzung des oben genannten Dienstes geschlossen. Hierbei handelt es sich um einen datenschutzrechtlich vorgeschriebenen Vertrag, der gew&auml;hrleistet, dass dieser die personenbezogenen Daten unserer Websitebesucher nur nach unseren Weisungen und unter Einhaltung der DSGVO verarbeitet.</p>
<h2 class="h4">3. Allgemeine Hinweise und Pflicht&shy;informationen</h2>
<h3 class="h5">Datenschutz</h3> <p>Die Betreiber dieser Seiten nehmen den Schutz Ihrer pers&ouml;nlichen Daten sehr ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend den gesetzlichen Datenschutzvorschriften sowie dieser Datenschutzerkl&auml;rung.</p> <p>Wenn Sie diese Website benutzen, werden verschiedene personenbezogene Daten erhoben. Personenbezogene Daten sind Daten, mit denen Sie pers&ouml;nlich identifiziert werden k&ouml;nnen. Die vorliegende Datenschutzerkl&auml;rung erl&auml;utert, welche Daten wir erheben und wof&uuml;r wir sie nutzen. Sie erl&auml;utert auch, wie und zu welchem Zweck das geschieht.</p> <p>Wir weisen darauf hin, dass die Daten&uuml;bertragung im Internet (z.&nbsp;B. bei der Kommunikation per E-Mail) Sicherheitsl&uuml;cken aufweisen kann. Ein l&uuml;ckenloser Schutz der Daten vor dem Zugriff durch Dritte ist nicht m&ouml;glich.</p>
<h3 class="h5">Hinweis zur verantwortlichen Stelle</h3> <p>Die verantwortliche Stelle f&uuml;r die Datenverarbeitung auf dieser Website ist:</p> <p>
Robert-Carl Rapp<br>
Sindlingerstr. 23,<br>
71083 Herrenberg
</p>
<p>
Telefon: (+49) 15784037946<br>
E-Mail: kontakt@mindboost.team
</p>
<p>Verantwortliche Stelle ist die nat&uuml;rliche oder juristische Person, die allein oder gemeinsam mit anderen &uuml;ber die Zwecke und Mittel der Verarbeitung von personenbezogenen Daten (z.&nbsp;B. Namen, E-Mail-Adressen o. &Auml;.) entscheidet.</p>
<h3 class="h5">Speicherdauer</h3> <p>Soweit innerhalb dieser Datenschutzerkl&auml;rung keine speziellere Speicherdauer genannt wurde, verbleiben Ihre personenbezogenen Daten bei uns, bis der Zweck f&uuml;r die Datenverarbeitung entf&auml;llt. Wenn Sie ein berechtigtes L&ouml;schersuchen geltend machen oder eine Einwilligung zur Datenverarbeitung widerrufen, werden Ihre Daten gel&ouml;scht, sofern wir keine anderen rechtlich zul&auml;ssigen Gr&uuml;nde f&uuml;r die Speicherung Ihrer personenbezogenen Daten haben (z.&nbsp;B. steuer- oder handelsrechtliche Aufbewahrungsfristen); im letztgenannten Fall erfolgt die L&ouml;schung nach Fortfall dieser Gr&uuml;nde.</p>
<h3 class="h5">Allgemeine Hinweise zu den Rechtsgrundlagen der Datenverarbeitung auf dieser Website</h3> <p>Sofern Sie in die Datenverarbeitung eingewilligt haben, verarbeiten wir Ihre personenbezogenen Daten auf Grundlage von Art. 6 Abs. 1 lit. a DSGVO bzw. Art. 9 Abs. 2 lit. a DSGVO, sofern besondere Datenkategorien nach Art. 9 Abs. 1 DSGVO verarbeitet werden. Im Falle einer ausdr&uuml;cklichen Einwilligung in die &Uuml;bertragung personenbezogener Daten in Drittstaaten erfolgt die Datenverarbeitung au&szlig;erdem auf Grundlage von Art. 49 Abs. 1 lit. a DSGVO. Sofern Sie in die Speicherung von Cookies oder in den Zugriff auf Informationen in Ihr Endger&auml;t (z.&nbsp;B. via Device-Fingerprinting) eingewilligt haben, erfolgt die Datenverarbeitung zus&auml;tzlich auf Grundlage von &sect; 25 Abs. 1 TTDSG. Die Einwilligung ist jederzeit widerrufbar. Sind Ihre Daten zur Vertragserf&uuml;llung oder zur Durchf&uuml;hrung vorvertraglicher Ma&szlig;nahmen erforderlich, verarbeiten wir Ihre Daten auf Grundlage des Art. 6 Abs. 1 lit. b DSGVO. Des Weiteren verarbeiten wir Ihre Daten, sofern diese zur Erf&uuml;llung einer rechtlichen Verpflichtung erforderlich sind auf Grundlage von Art. 6 Abs. 1 lit. c DSGVO. Die Datenverarbeitung kann ferner auf Grundlage unseres berechtigten Interesses nach Art. 6 Abs. 1 lit. f DSGVO erfolgen. &Uuml;ber die jeweils im Einzelfall einschl&auml;gigen Rechtsgrundlagen wird in den folgenden Abs&auml;tzen dieser Datenschutzerkl&auml;rung informiert.</p>
<h3 class="h5">Empfänger von personenbezogenen Daten</h3> <p>Im Rahmen unserer Gesch&auml;ftst&auml;tigkeit arbeiten wir mit verschiedenen externen Stellen zusammen. Dabei ist teilweise auch eine &Uuml;bermittlung von personenbezogenen Daten an diese externen Stellen erforderlich. Wir geben personenbezogene Daten nur dann an externe Stellen weiter, wenn dies im Rahmen einer Vertragserf&uuml;llung erforderlich ist, wenn wir gesetzlich hierzu verpflichtet sind (z.&nbsp;B. Weitergabe von Daten an Steuerbeh&ouml;rden), wenn wir ein berechtigtes Interesse nach Art. 6 Abs. 1 lit. f DSGVO an der Weitergabe haben oder wenn eine sonstige Rechtsgrundlage die Datenweitergabe erlaubt. Beim Einsatz von Auftragsverarbeitern geben wir personenbezogene Daten unserer Kunden nur auf Grundlage eines g&uuml;ltigen Vertrags &uuml;ber Auftragsverarbeitung weiter. Im Falle einer gemeinsamen Verarbeitung wird ein Vertrag &uuml;ber gemeinsame Verarbeitung geschlossen.</p>
<h3 class="h5">Widerruf Ihrer Einwilligung zur Datenverarbeitung</h3> <p>Viele Datenverarbeitungsvorg&auml;nge sind nur mit Ihrer ausdr&uuml;cklichen Einwilligung m&ouml;glich. Sie k&ouml;nnen eine bereits erteilte Einwilligung jederzeit widerrufen. Die Rechtm&auml;&szlig;igkeit der bis zum Widerruf erfolgten Datenverarbeitung bleibt vom Widerruf unber&uuml;hrt.</p>
<h3 class="h5">Widerspruchsrecht gegen die Datenerhebung in besonderen F&auml;llen sowie gegen Direktwerbung (Art. 21 DSGVO)</h3> <p>WENN DIE DATENVERARBEITUNG AUF GRUNDLAGE VON ART. 6 ABS. 1 LIT. E ODER F DSGVO ERFOLGT, HABEN SIE JEDERZEIT DAS RECHT, AUS GR&Uuml;NDEN, DIE SICH AUS IHRER BESONDEREN SITUATION ERGEBEN, GEGEN DIE VERARBEITUNG IHRER PERSONENBEZOGENEN DATEN WIDERSPRUCH EINZULEGEN; DIES GILT AUCH F&Uuml;R EIN AUF DIESE BESTIMMUNGEN GEST&Uuml;TZTES PROFILING. DIE JEWEILIGE RECHTSGRUNDLAGE, AUF DENEN EINE VERARBEITUNG BERUHT, ENTNEHMEN SIE DIESER DATENSCHUTZERKL&Auml;RUNG. WENN SIE WIDERSPRUCH EINLEGEN, WERDEN WIR IHRE BETROFFENEN PERSONENBEZOGENEN DATEN NICHT MEHR VERARBEITEN, ES SEI DENN, WIR K&Ouml;NNEN ZWINGENDE SCHUTZW&Uuml;RDIGE GR&Uuml;NDE F&Uuml;R DIE VERARBEITUNG NACHWEISEN, DIE IHRE INTERESSEN, RECHTE UND FREIHEITEN &Uuml;BERWIEGEN ODER DIE VERARBEITUNG DIENT DER GELTENDMACHUNG, AUS&Uuml;BUNG ODER VERTEIDIGUNG VON RECHTSANSPR&Uuml;CHEN (WIDERSPRUCH NACH ART. 21 ABS. 1 DSGVO).</p> <p>WERDEN IHRE PERSONENBEZOGENEN DATEN VERARBEITET, UM DIREKTWERBUNG ZU BETREIBEN, SO HABEN SIE DAS RECHT, JEDERZEIT WIDERSPRUCH GEGEN DIE VERARBEITUNG SIE BETREFFENDER PERSONENBEZOGENER DATEN ZUM ZWECKE DERARTIGER WERBUNG EINZULEGEN; DIES GILT AUCH F&Uuml;R DAS PROFILING, SOWEIT ES MIT SOLCHER DIREKTWERBUNG IN VERBINDUNG STEHT. WENN SIE WIDERSPRECHEN, WERDEN IHRE PERSONENBEZOGENEN DATEN ANSCHLIESSEND NICHT MEHR ZUM ZWECKE DER DIREKTWERBUNG VERWENDET (WIDERSPRUCH NACH ART. 21 ABS. 2 DSGVO).</p>
<h3 class="h5">Beschwerde&shy;recht bei der zust&auml;ndigen Aufsichts&shy;beh&ouml;rde</h3> <p>Im Falle von Verst&ouml;&szlig;en gegen die DSGVO steht den Betroffenen ein Beschwerderecht bei einer Aufsichtsbeh&ouml;rde, insbesondere in dem Mitgliedstaat ihres gew&ouml;hnlichen Aufenthalts, ihres Arbeitsplatzes oder des Orts des mutma&szlig;lichen Versto&szlig;es zu. Das Beschwerderecht besteht unbeschadet anderweitiger verwaltungsrechtlicher oder gerichtlicher Rechtsbehelfe.</p>
<h3 class="h5">Recht auf Daten&shy;&uuml;bertrag&shy;barkeit</h3> <p>Sie haben das Recht, Daten, die wir auf Grundlage Ihrer Einwilligung oder in Erf&uuml;llung eines Vertrags automatisiert verarbeiten, an sich oder an einen Dritten in einem g&auml;ngigen, maschinenlesbaren Format aush&auml;ndigen zu lassen. Sofern Sie die direkte &Uuml;bertragung der Daten an einen anderen Verantwortlichen verlangen, erfolgt dies nur, soweit es technisch machbar ist.</p>
<h3 class="h5">Auskunft, Berichtigung und L&ouml;schung</h3> <p>Sie haben im Rahmen der geltenden gesetzlichen Bestimmungen jederzeit das Recht auf unentgeltliche Auskunft &uuml;ber Ihre gespeicherten personenbezogenen Daten, deren Herkunft und Empf&auml;nger und den Zweck der Datenverarbeitung und ggf. ein Recht auf Berichtigung oder L&ouml;schung dieser Daten. Hierzu sowie zu weiteren Fragen zum Thema personenbezogene Daten k&ouml;nnen Sie sich jederzeit an uns wenden.</p>
<h3 class="h5">Recht auf Einschr&auml;nkung der Verarbeitung</h3> <p>Sie haben das Recht, die Einschr&auml;nkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen. Hierzu k&ouml;nnen Sie sich jederzeit an uns wenden. Das Recht auf Einschr&auml;nkung der Verarbeitung besteht in folgenden F&auml;llen:</p> <ul> <li>Wenn Sie die Richtigkeit Ihrer bei uns gespeicherten personenbezogenen Daten bestreiten, ben&ouml;tigen wir in der Regel Zeit, um dies zu &uuml;berpr&uuml;fen. F&uuml;r die Dauer der Pr&uuml;fung haben Sie das Recht, die Einschr&auml;nkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.</li> <li>Wenn die Verarbeitung Ihrer personenbezogenen Daten unrechtm&auml;&szlig;ig geschah/geschieht, k&ouml;nnen Sie statt der L&ouml;schung die Einschr&auml;nkung der Datenverarbeitung verlangen.</li> <li>Wenn wir Ihre personenbezogenen Daten nicht mehr ben&ouml;tigen, Sie sie jedoch zur Aus&uuml;bung, Verteidigung oder Geltendmachung von Rechtsanspr&uuml;chen ben&ouml;tigen, haben Sie das Recht, statt der L&ouml;schung die Einschr&auml;nkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.</li> <li>Wenn Sie einen Widerspruch nach Art. 21 Abs. 1 DSGVO eingelegt haben, muss eine Abw&auml;gung zwischen Ihren und unseren Interessen vorgenommen werden. Solange noch nicht feststeht, wessen Interessen &uuml;berwiegen, haben Sie das Recht, die Einschr&auml;nkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.</li> </ul> <p>Wenn Sie die Verarbeitung Ihrer personenbezogenen Daten eingeschr&auml;nkt haben, d&uuml;rfen diese Daten &ndash; von ihrer Speicherung abgesehen &ndash; nur mit Ihrer Einwilligung oder zur Geltendmachung, Aus&uuml;bung oder Verteidigung von Rechtsanspr&uuml;chen oder zum Schutz der Rechte einer anderen nat&uuml;rlichen oder juristischen Person oder aus Gr&uuml;nden eines wichtigen &ouml;ffentlichen Interesses der Europ&auml;ischen Union oder eines Mitgliedstaats verarbeitet werden.</p>
<h3 class="h5">SSL- bzw. TLS-Verschl&uuml;sselung</h3> <p>Diese Seite nutzt aus Sicherheitsgr&uuml;nden und zum Schutz der &Uuml;bertragung vertraulicher Inhalte, wie zum Beispiel Bestellungen oder Anfragen, die Sie an uns als Seitenbetreiber senden, eine SSL- bzw. TLS-Verschl&uuml;sselung. Eine verschl&uuml;sselte Verbindung erkennen Sie daran, dass die Adresszeile des Browsers von &bdquo;http://&ldquo; auf &bdquo;https://&ldquo; wechselt und an dem Schloss-Symbol in Ihrer Browserzeile.</p> <p>Wenn die SSL- bzw. TLS-Verschl&uuml;sselung aktiviert ist, k&ouml;nnen die Daten, die Sie an uns &uuml;bermitteln, nicht von Dritten mitgelesen werden.</p>
<h3 class="h5">Widerspruch gegen Werbe-E-Mails</h3> <p>Der Nutzung von im Rahmen der Impressumspflicht ver&ouml;ffentlichten Kontaktdaten zur &Uuml;bersendung von nicht ausdr&uuml;cklich angeforderter Werbung und Informationsmaterialien wird hiermit widersprochen. Die Betreiber der Seiten behalten sich ausdr&uuml;cklich rechtliche Schritte im Falle der unverlangten Zusendung von Werbeinformationen, etwa durch Spam-E-Mails, vor.</p>
<h2>4. Datenerfassung auf dieser Website</h2>
<h3 class="h5">Kontaktformular</h3> <p>Wenn Sie uns per Kontaktformular Anfragen zukommen lassen, werden Ihre Angaben aus dem Anfrageformular inklusive der von Ihnen dort angegebenen Kontaktdaten zwecks Bearbeitung der Anfrage und f&uuml;r den Fall von Anschlussfragen bei uns gespeichert. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.</p> <p>Die Verarbeitung dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO, sofern Ihre Anfrage mit der Erf&uuml;llung eines Vertrags zusammenh&auml;ngt oder zur Durchf&uuml;hrung vorvertraglicher Ma&szlig;nahmen erforderlich ist. In allen &uuml;brigen F&auml;llen beruht die Verarbeitung auf unserem berechtigten Interesse an der effektiven Bearbeitung der an uns gerichteten Anfragen (Art. 6 Abs. 1 lit. f DSGVO) oder auf Ihrer Einwilligung (Art. 6 Abs. 1 lit. a DSGVO) sofern diese abgefragt wurde; die Einwilligung ist jederzeit widerrufbar.</p> <p>Die von Ihnen im Kontaktformular eingegebenen Daten verbleiben bei uns, bis Sie uns zur L&ouml;schung auffordern, Ihre Einwilligung zur Speicherung widerrufen oder der Zweck f&uuml;r die Datenspeicherung entf&auml;llt (z.&nbsp;B. nach abgeschlossener Bearbeitung Ihrer Anfrage). Zwingende gesetzliche Bestimmungen &ndash; insbesondere Aufbewahrungsfristen &ndash; bleiben unber&uuml;hrt.</p>
<h3 class="h5">Anfrage per E-Mail, Telefon oder Telefax</h3> <p>Wenn Sie uns per E-Mail, Telefon oder Telefax kontaktieren, wird Ihre Anfrage inklusive aller daraus hervorgehenden personenbezogenen Daten (Name, Anfrage) zum Zwecke der Bearbeitung Ihres Anliegens bei uns gespeichert und verarbeitet. Diese Daten geben wir nicht ohne Ihre Einwilligung weiter.</p> <p>Die Verarbeitung dieser Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b DSGVO, sofern Ihre Anfrage mit der Erf&uuml;llung eines Vertrags zusammenh&auml;ngt oder zur Durchf&uuml;hrung vorvertraglicher Ma&szlig;nahmen erforderlich ist. In allen &uuml;brigen F&auml;llen beruht die Verarbeitung auf unserem berechtigten Interesse an der effektiven Bearbeitung der an uns gerichteten Anfragen (Art. 6 Abs. 1 lit. f DSGVO) oder auf Ihrer Einwilligung (Art. 6 Abs. 1 lit. a DSGVO) sofern diese abgefragt wurde; die Einwilligung ist jederzeit widerrufbar.</p> <p>Die von Ihnen an uns per Kontaktanfragen &uuml;bersandten Daten verbleiben bei uns, bis Sie uns zur L&ouml;schung auffordern, Ihre Einwilligung zur Speicherung widerrufen oder der Zweck f&uuml;r die Datenspeicherung entf&auml;llt (z.&nbsp;B. nach abgeschlossener Bearbeitung Ihres Anliegens). Zwingende gesetzliche Bestimmungen &ndash; insbesondere gesetzliche Aufbewahrungsfristen &ndash; bleiben unber&uuml;hrt.</p>
<h2>5. Newsletter</h2>
<h3 class="h5">Newsletter&shy;daten</h3> <p>Wenn Sie den auf der Website angebotenen Newsletter beziehen m&ouml;chten, ben&ouml;tigen wir von Ihnen eine E-Mail-Adresse sowie Informationen, welche uns die &Uuml;berpr&uuml;fung gestatten, dass Sie der Inhaber der angegebenen E-Mail-Adresse sind und mit dem Empfang des Newsletters einverstanden sind. Weitere Daten werden nicht bzw. nur auf freiwilliger Basis erhoben. Diese Daten verwenden wir ausschlie&szlig;lich f&uuml;r den Versand der angeforderten Informationen und geben diese nicht an Dritte weiter.</p> <p>Die Verarbeitung der in das Newsletteranmeldeformular eingegebenen Daten erfolgt ausschlie&szlig;lich auf Grundlage Ihrer Einwilligung (Art. 6 Abs. 1 lit. a DSGVO). Die erteilte Einwilligung zur Speicherung der Daten, der E-Mail-Adresse sowie deren Nutzung zum Versand des Newsletters k&ouml;nnen Sie jederzeit widerrufen, etwa &uuml;ber den &bdquo;Austragen&ldquo;-Link im Newsletter. Die Rechtm&auml;&szlig;igkeit der bereits erfolgten Datenverarbeitungsvorg&auml;nge bleibt vom Widerruf unber&uuml;hrt.</p> <p>Die von Ihnen zum Zwecke des Newsletter-Bezugs bei uns hinterlegten Daten werden von uns bis zu Ihrer Austragung aus dem Newsletter bei uns bzw. dem Newsletterdiensteanbieter gespeichert und nach der Abbestellung des Newsletters oder nach Zweckfortfall aus der Newsletterverteilerliste gel&ouml;scht. Wir behalten uns vor, E-Mail-Adressen aus unserem Newsletterverteiler nach eigenem Ermessen im Rahmen unseres berechtigten Interesses nach Art. 6 Abs. 1 lit. f DSGVO zu l&ouml;schen oder zu sperren.</p> <p>Daten, die zu anderen Zwecken bei uns gespeichert wurden, bleiben hiervon unber&uuml;hrt.</p> <p>Nach Ihrer Austragung aus der Newsletterverteilerliste wird Ihre E-Mail-Adresse bei uns bzw. dem Newsletterdiensteanbieter ggf. in einer Blacklist gespeichert, sofern dies zur Verhinderung k&uuml;nftiger Mailings erforderlich ist. Die Daten aus der Blacklist werden nur f&uuml;r diesen Zweck verwendet und nicht mit anderen Daten zusammengef&uuml;hrt. Dies dient sowohl Ihrem Interesse als auch unserem Interesse an der Einhaltung der gesetzlichen Vorgaben beim Versand von Newslettern (berechtigtes Interesse im Sinne des Art. 6 Abs. 1 lit. f DSGVO). Die Speicherung in der Blacklist ist zeitlich nicht befristet. <strong>Sie k&ouml;nnen der Speicherung widersprechen, sofern Ihre Interessen unser berechtigtes Interesse &uuml;berwiegen.</strong></p>
<h2>6. Plugins und Tools</h2>
<h3 class="h5">Google Fonts (lokales Hosting)</h3> <p>Diese Seite nutzt zur einheitlichen Darstellung von Schriftarten so genannte Google Fonts, die von Google bereitgestellt werden. Die Google Fonts sind lokal installiert. Eine Verbindung zu Servern von Google findet dabei nicht statt.</p> <p>Weitere Informationen zu Google Fonts finden Sie unter <a href="https://developers.google.com/fonts/faq" target="_blank" rel="noopener noreferrer">https://developers.google.com/fonts/faq</a> und in der Datenschutzerkl&auml;rung von Google: <a href="https://policies.google.com/privacy?hl=de" target="_blank" rel="noopener noreferrer">https://policies.google.com/privacy?hl=de</a>.</p>
<h3 class="h5">Font Awesome (lokales Hosting)</h3> <p>Diese Seite nutzt zur einheitlichen Darstellung von Schriftarten Font Awesome. Font Awesome ist lokal installiert. Eine Verbindung zu Servern von Fonticons, Inc. findet dabei nicht statt.</p> <p>Weitere Informationen zu Font Awesome finden Sie in der Datenschutzerkl&auml;rung f&uuml;r Font Awesome unter: <a href="https://fontawesome.com/privacy" target="_blank" rel="noopener noreferrer">https://fontawesome.com/privacy</a>.</p>
<p>Quelle: <a href="https://www.e-recht24.de">https://www.e-recht24.de</a></p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
setup () {
const { t } = useI18n()
const localePath = useLocalePath()
return { t, localePath }
}
}
</script>

310
pages/settings/donation.vue Normal file
View File

@@ -0,0 +1,310 @@
<template>
<div>
<div class="row justify-content-center">
<div class="col-12 col-lg-9 col-md-11 col-sm-12 col-xl-8">
<h1 class="h3 fw-bold text-center pt-3">
{{ t("Donation") }}
</h1>
<div class="row">
<div class="col-12">
<p class="fw-light text-center py-3">
{{ t("DonationHeading") }}
</p>
</div>
</div>
<div class="d-flex flex-column flex-md-row gap-3">
<div class="col-12 py-1 flex-auto">
<div class="card" :class="{'active-sub':oneMonth,'sub-card':!oneMonth}" @click="oneMonth=true;twoMonth=false;threeMonth=false">
<div class="card-body py-4">
<h2 class="text-center fw-bolder">
3.00
</h2>
<p class="text-center">
{{ t("SmallAmount") }}
</p>
</div>
</div>
</div>
<div class="col-12 py-1 flex-auto">
<div class="card" :class="{'active-sub':twoMonth,'sub-card':!twoMonth}" @click="twoMonth=true;oneMonth=false;threeMonth=false">
<div class="card-body py-4">
<h2 class="text-center fw-bolder">
10.00
</h2>
<p class=" text-center">
{{ t("MediumAmount") }}
</p>
</div>
</div>
</div>
<div class="col-12 py-1 flex-auto">
<div class="card" :class="{'active-sub':threeMonth,'sub-card':!threeMonth}" @click="threeMonth=true;twoMonth=false;oneMonth=false">
<div class="card-body py-4">
<h2 class="text-center fw-bolder">
15.00
</h2>
<p class=" text-center">
{{ t("BigAmount") }}
</p>
</div>
</div>
</div>
</div>
<div class="row pt-5">
<div class="col-12 px-md-5 px-0">
<p class="text-center text-muted">
{{ t("EndStatement") }}
</p>
</div>
<div class="col-12 text-end" />
</div>
<div class="row pt-5 justify-content-center ">
<div class="col-12 col-md-6 col-lg-6 text-center">
<div class="row">
<div class="col-12">
<p class="fw-medium text-center"><strong>{{ t("bankaccount") }}:</strong><br></p>
<p class="fw-light text-center">
{{ t("Name") }}: Robert Rapp<br>
</p>
<p class="fw-light text-center">
IBAN: DE42 1001 1001 2629 4590 53
<span class="tooltip-container">
<i class="fas fa-copy ml-2 cursor-pointer" title="In die Zwischenablage kopieren" @click="copyIBANToClipboard('DE42 1001 1001 2629 4590 53')" />
<span class="tooltiptext" :class="{ 'show': showTooltip }">Kopiert!</span>
</span>
</p>
<p class="fw-light text-center">
BIC: NTSBDEB1XXX
</p>
<p class="fw-light text-center">
{{ userVerwendungszweck }}
<span class="tooltip-container">
<i class="fas fa-copy ml-2 cursor-pointer" title="In die Zwischenablage kopieren" @click="copyToClipboard(userVerwendungszweck)" />
<span class="tooltiptext" :class="{ 'show': showTooltip1 }">Kopiert!</span>
</span>
</p>
</div>
</div>
</div>
<!-- <StripeElements-->
<!-- v-if="stripeLoaded"-->
<!-- v-slot="{ elements, instance }" ref="elms"-->
<!-- :stripe-key="stripeKey"-->
<!-- :instance-options="instanceOptions"-->
<!-- :elements-options="elementsOptions"-->
<!-- >-->
<!-- <StripeElement-->
<!-- ref="card"-->
<!-- :elements="elements"-->
<!-- :options="cardOptions"-->
<!-- />-->
<!-- </StripeElements>-->
<!-- <button type="button" class="btn text-white fs-5 col-12 fw-bold py-2" @click="pay" style="background-color: #e9c046">Pay</button>-->
</div>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapStores } from 'pinia'
import { useUserStore } from '~/stores/user'
export default {
setup () {
const { t } = useI18n()
const localePath = useLocalePath()
const userVerwendungszweck = ref(useUserStore().user.email)
const usermail = useUserStore().user.email
return { t, localePath, userVerwendungszweck, usermail }
},
data () {
return {
oneMonth: false,
twoMonth: false,
threeMonth: false,
loading: false,
cancel_loading: false,
showTooltip: false,
showTooltip1: false,
subscription: '',
form: {
type: 'one'
}
}
},
computed: {
// ...mapState(useUserStore, ['user']),
...mapStores(useUserStore)
},
mounted () {
this.fetchSubscription()
},
methods: {
...mapActions(useUserStore, ['updateUser', 'logout']),
copyToClipboard (text) {
navigator.clipboard.writeText(text).then(() => {
this.showTooltip1 = true
setTimeout(() => {
this.showTooltip1 = false
}, 2000) // Tooltip verschwindet nach 2 Sekunden
}).catch((err) => {
useNuxtApp().$logger.error('Fehler beim Kopieren: ', err)
})
},
copyIBANToClipboard (text) {
navigator.clipboard.writeText(text).then(() => {
this.showTooltip = true
setTimeout(() => {
this.showTooltip = false
}, 2000) // Tooltip verschwindet nach 2 Sekunden
}).catch((err) => {
useNuxtApp().$logger.error('Fehler beim Kopieren: ', err)
})
},
updateUserNow () {
this.$axios.post('/api/auth/me').then(({ data }) => {
this.updateUser(data.user)
}).catch(() => {
this.logout()
this.$router.push(this.localePath('/auth/login'))
})
},
paypal () {
window.open('https://www.paypal.com/pools/c/93YzF9GqEP', '_blank')
},
fetchSubscription () {
this.$axios.post('/api/fetch-subscription').then(({ data }) => {
this.subscription = data.name
if (data.name === 'monthly') {
this.oneMonth = true
this.twoMonth = false
this.threeMonth = false
}
if (data.name === 'sixmonth') {
this.oneMonth = false
this.twoMonth = true
this.threeMonth = false
}
if (data.name === 'yearly') {
this.oneMonth = false
this.twoMonth = false
this.threeMonth = true
}
})
}
}
}
// export default {
// name: 'CardOnly',
// data() {
// return {
// stripeKey: 'pk_test_0S0H9CTYtkhPlArgg4KkPFcZ',
// instanceOptions: {},
// elementsOptions: {},
// cardOptions: {},
// stripeLoaded: false,
// card: null,
// elms: null,
// oneMonth:false,
// twoMonth:false,
// threeMonth:false,
// };
// },
// components: {
// StripeElements,
// StripeElement,
// },
// beforeMount() {
// const stripePromise = loadStripe(this.stripeKey);
// stripePromise.then(() => {
// this.stripeLoaded = true;
// });
// },
// methods: {
// pay() {
// const cardElement = this.$refs.card.stripeElement;
//
// this.$refs.elms.instance
// .createPaymentMethod({ type: 'card', card: cardElement })
// .then((result) => {
// // useNuxtApp().$logger.log(result);
// // useNuxtApp().$logger.log(result.paymentMethod);
// this.$axios.post('/api/subscribe', result).then((response) => {
// // useNuxtApp().$logger.log(response.data);
// });
// });
// },
// },
// computed: {
//
// },
// created() {
//
// },
// };
</script>
<style>
.flex-auto {
flex: 1 1 auto;
}
.active-sub{
background: #E9C046;
box-shadow: 0px 0px 16px 1px rgba(108, 97, 97, 0.05);
border-radius: 12px;
color:white;
background-color:#E9C046 !important;
}
.sub-card{
background: #FFFFFF;
box-shadow: 0px 0px 16px 1px rgba(108, 97, 97, 0.05);
border-radius: 12px;
color:black;
height: 100%;
transition: 250ms ease-in-out;
}
.sub-card:hover, .sub-card:focus{
background: #E9C046;
box-shadow: 0px 0px 16px 1px rgba(108, 97, 97, 0.05);
border-radius: 12px;
color:white;
border-color: #E9C046;
background-color:#E9C046 !important;
}
.tooltip-container {
margin-left: 10px;
font-size: smaller;
position: relative;
display: inline-block;
}
.tooltiptext {
visibility: hidden;
font-family: 'Montserrat', sans-serif;
font-size: 14px;
width: auto;
background-color: #e9c046;
color: #000000;
text-align: center;
border-radius: 6px;
padding: 5px;
position: absolute;
z-index: 1;
bottom: 125%;
left: 20%;
margin-left: -30px;
opacity: 0;
transition: opacity 0.3s;
}
.tooltiptext.show {
visibility: visible;
opacity: 1;
}
</style>

View File

@@ -0,0 +1,632 @@
<template>
<div>
<div class="row justify-content-center">
<div class="col-12 col-sm-9">
<h2 class="fw-bold text-center me-5 pt-5">
<span class="float-start" style="cursor: pointer" @click="$router.go(-1)"><i
class="fa-solid fa-arrow-left-long"
/></span> {{ t("Donation") }}
</h2>
<div class="row">
<div class="col-12">
<h4 class="fw-light text-center pt-3">
{{ t("DonationHeading") }}
</h4>
</div>
</div>
<div class="row">
<section class="pricing py-5">
<div class="row">
<!-- Basic Tier -->
<div class="col-md-4">
<div id="basic-plan" class="card mb-5 mb-lg-0" data-plan="starter">
<div class="card-body">
<h5 class="card-title text-muted text-uppercase text-center">
Basic
</h5>
<h6 class="card-price text-center">
<span class="price" data-price-type="starter">8.00</span><span class="period">/month</span>
</h6>
<div v-if="currentSubscription() == 1" class="d-flex justify-content-center my-2">
<span class="badge bg-warning">Active</span>
</div>
<hr>
<ul class="fa-ul">
<li><span class="fa-li"><i class="fas fa-check" /></span>Good for students, employees</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>Full access to app</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>3 Professional soundscapes</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>mindboost@technology</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>30 Days Trial</li>
<li>
<div class="pricing-toggle my-2 d-flex align-items-center">
<div class="me-3">
<input
id="starter-month"
type="radio"
class="me-1"
name="cycle-starter"
value="month"
:checked="currentSubscription() === 1 && dataStore.user.montly === '1' || currentSubscription() !== 1"
>
<label for="starter-month" class="form-check-label">Monthly</label>
</div>
<div>
<input
id="starter-year"
type="radio"
class="me-1"
name="cycle-starter"
:checked="currentSubscription() == 1 && dataStore.user.yearly === '1'"
value="year"
>
<label for="starter-year" class="form-check-label">Yearly <sup>save 20%</sup></label>
</div>
</div>
</li>
</ul>
<div class="d-grid">
<span v-if="isInvited">
<div class="row">
<button class="btn btn-warning text-uppercase buy-now" @click="showManagerPlan()">Buy
Now</button>
</div>
</span>
<span v-else>
<div class="row">
<button
class="btn btn-warning text-uppercase buy-now"
:disabled="currentSubscription() === 1"
>Active</button>
</div>
</span>
</div>
</div>
</div>
</div>
<!-- Team Tier -->
<div class="col-md-4">
<div id="team-plan" class="card mb-5 mb-lg-0" data-plan="pro">
<div class="card-body">
<h5 class="card-title text-muted text-uppercase text-center">
Team
</h5>
<h6 class="card-price text-center">
<span class="price" data-price-type="pro">5</span><span class="period">/month</span>
</h6>
<div v-if="currentSubscription() == 2" class="d-flex justify-content-center my-2">
<span class="badge bg-warning">Active</span>
</div>
<hr>
<ul class="fa-ul">
<li>
<span class="fa-li"><i class="fas fa-check" /></span>Good for colleagues, groups, teams,
departments
</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>Full access to app</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>3 Professional soundscapes</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>mindboost@technology</li>
<li><span class="fa-li"><i class="fas fa-plus" /></span>Individual User Management</li>
<li><span class="fa-li"><i class="fas fa-plus" /></span>Flexible Trial Version</li>
<li><span class="fa-li"><i class="fas fa-plus" /></span>Coherent invoicing</li>
<li><span class="fa-li"><i class="fas fa-plus" /></span> + Basic Per User</li>
<li><span class="fa-li"><i class="fas fa-plus" /></span>30 Days Trial</li>
<li>
<div class="pricing-toggle my-2 d-flex align-items-center">
<div class="me-3">
<input
id="pro-month"
type="radio"
class="me-1"
name="cycle-pro"
value="month"
:checked="currentSubscription() === 2 && dataStore.user.montly === '1' || currentSubscription() !== 2"
>
<label for="pro-month" class="form-check-label">Monthly</label>
</div>
<div>
<input
id="pro-year"
type="radio"
class="me-1"
name="cycle-pro"
:checked="currentSubscription() === 2 && dataStore.user.yearly === '1'"
value="year"
>
<label for="pro-year" class="form-check-label">Yearly <sup>save 20%</sup></label>
</div>
</div>
</li>
</ul>
<div class="d-grid">
<span v-if="isInvited">
<div class="row">
<button class="btn btn-warning text-uppercase team-plan" @click="showManagerPlan()">Buy
Now</button>
</div>
</span>
<span v-else>
<div class="row">
<button
class="btn btn-warning text-uppercase team-plan"
:disabled="currentSubscription() === 2"
>Active</button>
</div>
</span>
</div>
</div>
</div>
</div>
<!-- Enterprise Tier -->
<div class="col-md-4">
<div id="enterprise-plan" class="card" data-plan="enterprise">
<div class="card-body">
<h5 class="card-title text-muted text-uppercase text-center">
Enterprise
</h5>
<h6 class="card-price text-center">
<span>Depends</span>
</h6>
<div v-if="currentSubscription() == 3" class="d-flex justify-content-center my-2">
<span class="badge bg-warning">Active</span>
</div>
<hr>
<ul class="fa-ul">
<li><span class="fa-li"><i class="fas fa-check" /></span>Good For Enterprise > 200</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>Full access to app</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>3 Professional soundscapes</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>mindboost@technology</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>Individual User Management</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>Flexible Trial Version</li>
<li><span class="fa-li"><i class="fas fa-check" /></span>Coherent invoicing</li>
<li><span class="fa-li"><i class="fas fa-plus" /></span>Dedicated Partnership</li>
<li><span class="fa-li"><i class="fas fa-plus" /></span>In-person Onboarding</li>
<li><span class="fa-li"><i class="fas fa-plus" /></span>Individual Pricing</li>
<li><span class="fa-li"><i class="fas fa-plus" /></span>30 Days Trial</li>
<li>
<div class="pricing-toggle my-2 d-flex align-items-center">
<div class="me-3">
<input
id="enterprise-month"
type="radio"
class="me-1"
name="cycle-enterprise"
value="month"
checked
>
<label for="enterprise-month" class="form-check-label">Monthly</label>
</div>
<div>
<input
id="enterprise-year"
type="radio"
class="me-1"
name="cycle-enterprise"
value="year"
>
<label for="enterprise-year" class="form-check-label">Yearly <sup>save 20%</sup></label>
</div>
</div>
</li>
</ul>
<div class="d-grid">
<span v-if="isInvited">
<div class="row">
<button type="button" class="btn btn-warning text-uppercase" @click="showManagerPlan()">
contact us
</button>
</div>
</span>
<span v-else>
<div class="row">
<button
type="button"
class="btn btn-warning text-uppercase"
data-bs-toggle="modal"
data-bs-target="#exampleModal"
>
contact us
</button>
</div>
</span>
<!-- Modal for enterprise contact us form -->
<div
id="exampleModal"
class="modal fade"
tabindex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 id="exampleModalLabel" class="modal-title fs-5">
Contact Us
</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" />
</div>
<form @submit.prevent="submitForm">
<div class="modal-body">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input
id="name"
v-model="contact_form.name"
type="text"
class="form-control"
required
>
</div>
<div class="mb-3">
<label for="companyName" class="form-label">Company Name</label>
<input
id="companyName"
v-model="contact_form.company_name"
type="text"
class="form-control"
required
>
</div>
<div class="mb-3">
<label for="message" class="form-label">Message</label>
<textarea id="message" v-model="contact_form.message" class="form-control" required />
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-warning text-uppercase">
Send Message
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapStores } from 'pinia'
import { useUserStore } from '~/stores/user'
// entery point
export default {
components: { },
setup () {
const { t } = useI18n()
const localePath = useLocalePath()
const dataStore = useUserStore() // Initialize store in setup()
return {
t,
localePath,
dataStore
}
},
// dara store
data () {
return {
oneMonth: false,
twoMonth: false,
threeMonth: false,
loading: false,
cancel_loading: false,
subscription: '',
form: {
type: 'one'
},
contact_form: {
name: '',
company_name: '',
email: 'dowhf@access.bro', // Removed direct reference to userStore
message: ''
},
isInvited: 0,
isManagerPlan: 0
}
},
// computed
computed: {
...mapStores(useUserStore)
},
// mounted
mounted () {
const urlBackend = useRuntimeConfig().public.BACKEND_URL || 'https://b.mindboost.team'
this.fetchSubscription()
this.GetManagerDetails()
if (window.Paddle) {
// Initialize Paddle
Paddle.Environment.set('sandbox')
Paddle.Initialize({
token: 'test_27bb608597d1a4d75fe1bce2be7',
eventCallback: async function (data) {
if (data.name === 'checkout.completed') {
// useNuxtApp().$logger.log(data)
try {
const response = await axios.post(urlBackend + '/api/paddle-webhook', data)
// useNuxtApp().$logger.log('this is subscription data data', response)
// Access the Pinia store and update the user data
const userStore = useUserStore()
userStore.updateUser(response.data.user)
} catch (error) {
// useNuxtApp().$logger.log(error)
}
}
}
})
// Product definitions for each plan
const products = {
starter: {
productId: 'pro_01j508hk3rp5f5g68n7tqv1thd',
monthPriceId: 'pri_01j508ssstj4n6xra9j8ymr3d1',
yearPriceId: 'pri_01j508x0rwntqs581rj3xfx49n'
},
pro: {
productId: 'pro_01j5090jy3jkf4w9vr0j279fm5',
monthPriceId: 'pri_01j50947nd6t9ztgsrq5h6fsmx',
yearPriceId: 'pri_01j5097bzyp9zrevw0nsagb477'
},
enterprise: {
productId: 'pro_01j509nre47wbdtmqmd6x098s5',
monthPriceId: 'pri_01j509qknvfx3hv0459kpkvvar',
yearPriceId: 'pri_01j509ryg22rzs0ahwrb2vwzhq'
}
}
// Functions to handle pricing updates
const updateBasicPlan = () => {
const plan = 'starter'
const cycle = document.querySelector('input[name="cycle-starter"]:checked').value
const priceId = cycle === 'month' ? products[plan].monthPriceId : products[plan].yearPriceId
Paddle.PricePreview({ items: [{ quantity: 1, priceId }] })
.then((result) => {
document.querySelector('#basic-plan .price').textContent = result.data.details.lineItems[0].formattedTotals.subtotal
document.querySelector('#basic-plan .period').textContent = cycle === 'month' ? '/month' : '/year'
})
}
// Update the basic plan pricing on page load
const updateTeamPlan = () => {
const plan = 'pro'
const cycle = document.querySelector('input[name="cycle-pro"]:checked').value
const priceId = cycle === 'month' ? products[plan].monthPriceId : products[plan].yearPriceId
Paddle.PricePreview({ items: [{ quantity: 5, priceId }] })
.then((result) => {
document.querySelector('#team-plan .price').textContent = result.data.details.lineItems[0].formattedTotals.subtotal
document.querySelector('#team-plan .period').textContent = cycle === 'month' ? '/month' : '/year'
})
}
// Update the enterprise plan pricing on page load
const updateEnterprisePlan = () => {
const plan = 'enterprise'
const cycle = document.querySelector('input[name="cycle-enterprise"]:checked').value
const priceId = cycle === 'month' ? products[plan].monthPriceId : products[plan].yearPriceId
Paddle.PricePreview({ items: [{ quantity: 1, priceId }] })
.then((result) => {
document.querySelector('#enterprise-plan .price').textContent = result.data.details.lineItems[0].formattedTotals.subtotal
document.querySelector('#enterprise-plan .period').textContent = cycle === 'month' ? '/month' : '/year'
})
}
// Event handlers for plan and cycle changes
const handlePlanChange = (event) => {
const plan = event.target.closest('.card').getAttribute('data-plan')
if (plan === 'starter') {
updateBasicPlan()
} else if (plan === 'pro') {
updateTeamPlan()
} else if (plan === 'enterprise') {
updateEnterprisePlan()
}
}
// Set event listeners for plan and cycle changes
document.querySelectorAll('input[name^="cycle-"]').forEach((input) => {
input.addEventListener('change', handlePlanChange)
})
// Event handlers for buy now buttons
document.querySelectorAll('.buy-now').forEach((button) => {
button.addEventListener('click', () => {
const plan = button.closest('.card').getAttribute('data-plan')
const cycle = document.querySelector(`input[name="cycle-${plan}"]:checked`).value
const priceId = cycle === 'month' ? products[plan].monthPriceId : products[plan].yearPriceId
Paddle.Checkout.open({
items: [{ priceId, quantity: 1 }],
customer: {
email: this.dataStore.user.email,
address: { countryCode: 'PK', postalCode: '59351' }
},
source: window.location.href
})
})
})
// Event handlers for team-plan class
document.querySelectorAll('.team-plan').forEach((button) => {
button.addEventListener('click', () => {
const plan = button.closest('.card').getAttribute('data-plan')
const cycle = document.querySelector(`input[name="cycle-${plan}"]:checked`).value
const priceId = cycle === 'month' ? products[plan].monthPriceId : products[plan].yearPriceId
Paddle.Checkout.open({
items: [{ priceId, quantity: 5 }],
customer: {
email: this.dataStore.user.email,
address: { countryCode: 'PK', postalCode: '59351' }
},
source: window.location.href
})
})
})
// Initialize prices
updateBasicPlan()
updateTeamPlan()
updateEnterprisePlan()
}
},
// ======================= methods =======================
methods: {
// check user plan
isDisabled () {
const dataStore = useUserStore()
const userasdf = dataStore
// useNuxtApp().$logger.log('store', user); // Debugging
return !(userasdf.user.team_subscription_plan === '1' ||
userasdf.user.enterprise_subscription_plan === '1') &&
(userasdf.user.basic_subscription_plan !== '0')
},
// get user current plan
currentSubscription () {
const dataStore = useUserStore()
const userasdf = dataStore
return (userasdf.user.current_subscription_plan)
},
/**
* showManagerPlan in toster form
*/
showManagerPlan () {
let managerPlan = '' // Changed from const to let
if (this.isManagerPlan === 1) {
managerPlan = 'Basic'
} else if (this.isManagerPlan === 2) {
managerPlan = 'Team'
} else if (this.isManagerPlan === 3) {
managerPlan = 'Enterprise'
}
this.$toast.success(`Manager already has the ${managerPlan} plan`) // Fixed typo
},
/**
* Submits the contact form to the server
*/
async submitForm () {
try {
await axios.post(urlBackend + '/api/enterprise-plan-contact', this.contact_form)
// useNuxtApp().$logger.log('Success:', response.data)
Swal.fire({
title: 'Good job!',
text: 'Message sent successfully! Support team will contact you soon.',
icon: 'success'
})
} catch (error) {
// useNuxtApp().$logger.error('Error:', error.response);
this.$toast.error('Oops, something went wrong!')
}
},
...mapActions(useUserStore, ['updateUser', 'logout']),
updateUserNow () {
this.$axios.post('/api/auth/me').then(({ data }) => {
this.updateUser(data.user)
}).catch(() => {
this.logout()
this.$router.push(this.localePath('/auth/login'))
})
},
paypal () {
window.open('https://www.paypal.com/pools/c/93YzF9GqEP', '_blank')
},
fetchSubscription () {
this.$axios.post('/api/fetch-subscription').then(({ data }) => {
this.subscription = data.name
this.oneMonth = data.name === 'monthly'
this.twoMonth = data.name === 'sixmonth'
this.threeMonth = data.name === 'yearly'
})
},
async GetManagerDetails () {
const userStore = useUserStore()
if (userStore.user.inviter_id) {
const response = await axios.post(urlBackend + '/api/fetch-manger-detail', { manager_id: userStore.user.inviter_id })
// useNuxtApp().$logger.log('manager_id', response.data.data)
this.isInvited = 1
// useNuxtApp().$logger.log('response.data.data.inviter_id', response.data.data.inviter_id)
this.isManagerPlan = response.data.data.current_subscription_plan
}
}
}
}
</script>
<style>
.active-sub {
background: #E9C046;
box-shadow: 0px 0px 16px 1px rgba(108, 97, 97, 0.05);
border-radius: 12px;
color: white;
background-color: #E9C046 !important;
}
.sub-card {
background: #FFFFFF;
box-shadow: 0px 0px 16px 1px rgba(108, 97, 97, 0.05);
border-radius: 12px;
color: black;
}
/* style for plan cards */
.pricing .card {
border: none;
border-radius: 1rem;
transition: all 0.2s;
box-shadow: 0 0.5rem 1rem 0 rgba(0, 0, 0, 0.1);
}
.pricing hr {
margin: 1.5rem 0;
}
.pricing .card-title {
margin: 0.5rem 0;
font-size: 0.9rem;
letter-spacing: .1rem;
font-weight: bold;
}
.pricing .card-price {
font-size: 3rem;
margin: 0;
}
.pricing .card-price .period {
font-size: 0.8rem;
}
.pricing ul li {
margin-bottom: 1rem;
}
.pricing .text-muted {
opacity: 0.7;
}
.pricing .btn {
font-size: 80%;
border-radius: 5rem;
letter-spacing: .1rem;
font-weight: bold;
padding: 1rem;
opacity: 0.7;
transition: all 0.2s;
}
/* Hover Effects on Card */
@media (min-width: 992px) {
.pricing .card:hover {
margin-top: -.25rem;
margin-bottom: .25rem;
box-shadow: 0 0.5rem 1rem 0 rgba(0, 0, 0, 0.3);
}
.pricing .card:hover .btn {
opacity: 1;
}
}
</style>

View File

@@ -0,0 +1,175 @@
<template>
<div>
<div class="row justify-content-center">
<div class="col-12 col-lg-9 col-md-11 col-sm-12 col-xl-8">
<h1 class="h3 fw-bold text-center py-3">
<span class="float-start" style="cursor: pointer" @click="$router.go(-1)"><i class="fa-solid fa-arrow-left-long" /></span>
{{ t("Edit Account") }}
</h1>
<div class="row pt-4">
<div class="col-12">
<form @submit.prevent="validateAndSave">
<div class="row">
<div class="col-12">
<label for="firstname" class="text-muted ">{{ t("First Name") }} </label>
<input id="firstname" v-model="form.first_name" type="text" class="form-control" :placeholder="t('First Name')">
<div v-if="errors.first_name" class="invalid-feedback d-block">
{{ errors.first_name[0] }}
</div>
</div>
</div>
<div class="row pt-3">
<div class="col-12">
<label for="surname" class="text-muted ">{{ t("Surname") }} </label>
<input id="surname" v-model="form.surname" type="text" class="form-control" :placeholder="t('Surname')">
<div v-if="errors.surname" class="invalid-feedback d-block">
{{ errors.surname[0] }}
</div>
</div>
</div>
<div class="row pt-3">
<div class="col-12">
<label for="email" class="text-muted ">{{ t("Email") }} </label>
<input id="email" v-model="form.email" type="email" class="form-control" :placeholder="t('Email')">
<div v-if="errors.email" class="invalid-feedback d-block">
{{ errors.email[0] }}
</div>
</div>
</div>
<div class="row pt-3">
<div class="col-12 col-sm-6">
<label for="password" class="text-muted ">{{ t("Password") }} </label>
<input id="password" v-model="form.password" type="password" class="form-control" placeholder="***">
<div v-if="errors.password" class="invalid-feedback d-block">
{{ errors.password[0] }}
</div>
</div>
<div class="col-12 col-sm-6">
<label for="confirm-password" class="text-muted ">{{ t("Confirm Password") }} </label>
<input id="confirm-password" v-model="form.confirm_password" type="password" class="form-control" placeholder="***">
<div v-if="errors.confirm_password" class="invalid-feedback d-block">
{{ errors.confirm_password[0] }}
</div>
</div>
</div>
<div class="row pt-3">
<div class="col-12">
<label for="language" class="text-muted ">{{ t("Language") }} </label>
<select id="language" v-model="form.language" class="form-select" @change="changeLanguage">
<option value="en">
English
</option>
<option value="de">
German
</option>
</select>
<div v-if="errors.language" class="invalid-feedback d-block">
{{ errors.language[0] }}
</div>
</div>
</div>
<div class="row pt-5 g-2">
<button type="submit" class="btn btn-primary-custom mx-auto col-4" style="background-color: #e9c046">
{{ t("Save Changes") }} <div v-if="loading" class="spinner-border spinner-border-sm" role="status">
<span class="sr-only">{{ t("Loading...") }} </span>
</div>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'pinia'
// import Index from '~/pages/index.vue'
import { useUserStore } from '~/stores/user'
export default {
name: 'EditAccount',
components: {
// Index
},
setup () {
const { t } = useI18n()
const localePath = useLocalePath()
const switchLocalePath = useSwitchLocalePath()
const changeLanguage = (locale) => {
// useNuxtApp().$logger.log('switch', locale)
useRouter().push(switchLocalePath(locale))
// i18n.global.locale.value=
}
return { t, localePath, changeLanguage }
},
data () {
return {
loading: false,
form: {
first_name: '',
surname: '',
email: 'email',
password: '',
confirm_password: '',
language: 'de'
},
errors: []
}
},
computed: {
...mapState(useUserStore, ['user'])
},
mounted () {
this.form.first_name = this.user.first_name
this.form.email = this.user.email
this.form.surname = this.user.surname
this.form.language = this.user.language
},
methods: {
...mapActions(useUserStore, ['updateUser']),
validateAndSave () {
this.errors = []
// Wenn Passwort leer ist, lassen wir die Prüfung zu Änderung des Passworts ist optional
if (this.form.password !== '') {
if (this.form.password !== this.form.confirm_password) {
this.errors.confirm_password = ['Passwörter stimmen nicht überein.']
return
}
}
this.saveUser()
},
saveUser () {
this.loading = true
const payload = { ...this.form }
if (!payload.password) { delete payload.password }
if (!payload.confirm_password) { delete payload.confirm_password }
this.$axios.post('/api/account/update', payload).then(({ data }) => {
this.changeLanguage(this.form.language)
this.loading = false
if (data.success) {
this.$toast.success(data.message)
this.updateUser(data.user)
this.$router.push(this.localePath('/settings/account'))
}
}).catch((error) => {
this.loading = false
this.$toast.error('Something wrong while saving...')
if (error.response.status === 422) {
this.errors = error.response.data.errors
}
})
}
}
}
</script>

238
pages/settings/faq.vue Normal file
View File

@@ -0,0 +1,238 @@
<template>
<div>
<div class="row justify-content-center m-0 p-0">
<div class="col-12 col-lg-9 col-md-11 col-sm-12 col-xl-8">
<div class="row">
<div class="col-12">
<div class="me-4">
<h1 class="h3 fw-bold text-center py-3">
{{ t("FAQ") }}
</h1>
</div>
<div id="accordionExample" class="accordion">
<div class="accordion-item">
<h2 id="headingOne" class="accordion-header">
<button
class="accordion-button fw-semibold"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseOne"
aria-expanded="true"
aria-controls="collapseOne"
>
{{ t("question1") }}
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
<div class="accordion-body">
<p>
{{ t("answer_part_1") }}
<br>{{ t("answer_part_2") }}
</p>
</div>
</div>
</div>
<div class="accordion-item">
<h2 id="headingTwo" class="accordion-header">
<button
class="accordion-button fw-semibold collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseTwo"
aria-expanded="false"
aria-controls="collapseTwo"
>
{{ t("question_2") }}
</button>
</h2>
<div id="collapseTwo" class="accordion-collapse collapse" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
<div class="accordion-body">
{{ t("answer_2") }} 01
</div>
</div>
</div>
<div class="accordion-item">
<h2 id="headingThree" class="accordion-header">
<button
class="accordion-button fw-semibold collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseThree"
aria-expanded="false"
aria-controls="collapseThree"
>
{{ t("question_3") }}
</button>
</h2>
<div id="collapseThree" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
<div class="accordion-body">
{{ t("answer_3") }}
</div>
</div>
</div>
<div class="accordion-item">
<h2 id="headingFour" class="accordion-header">
<button
class="accordion-button fw-semibold collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseFour"
aria-expanded="false"
aria-controls="collapseThree"
>
{{ t("question_4") }}
</button>
</h2>
<div id="collapseFour" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
<div class="accordion-body">
{{ t("answer_4") }}
</div>
</div>
</div>
<div class="accordion-item">
<h2 id="headingFive" class="accordion-header">
<button
class="accordion-button fw-semibold collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseFive"
aria-expanded="false"
aria-controls="collapseThree"
>
{{ t("question_5") }}
</button>
</h2>
<div id="collapseFive" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
<div class="accordion-body">
{{ t("answer_5") }}
</div>
</div>
</div>
<div class="accordion-item">
<h2 id="headingsix" class="accordion-header">
<button
class="accordion-button fw-semibold collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapsesix"
aria-expanded="false"
aria-controls="collapseThree"
>
{{ t("question_6") }}
</button>
</h2>
<div id="collapsesix" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
<div class="accordion-body">
{{ t("answer_6") }}
</div>
</div>
</div>
<div class="accordion-item">
<h2 id="headingsaven" class="accordion-header">
<button
class="accordion-button fw-semibold collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseseven"
aria-expanded="false"
aria-controls="collapseThree"
>
{{ t("question_7") }}
</button>
</h2>
<div id="collapseseven" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
<div class="accordion-body">
{{ t("answer_7") }}
</div>
</div>
</div>
<div class="accordion-item">
<h2 id="headingeight" class="accordion-header">
<button
class="accordion-button fw-semibold collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseeight"
aria-expanded="false"
aria-controls="collapseThree"
>
{{ t("question_8") }}
</button>
</h2>
<div id="collapseeight" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
<div class="accordion-body">
<ol class="px-0 mx-0">
<li>{{ t("answer_8_1") }}</li>
<li>{{ t("answer_8_2") }}</li>
<li>{{ t("answer_8_3") }}</li>
<li>{{ t("answer_8_4") }}</li>
</ol>
</div>
</div>
</div>
<div class="accordion-item">
<h2 id="headingnine" class="accordion-header">
<button
class="accordion-button fw-semibold collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapsenine"
aria-expanded="false"
aria-controls="collapseThree"
>
{{ t("question_9") }}
</button>
</h2>
<div id="collapsenine" class="accordion-collapse collapse" aria-labelledby="headingThree" data-bs-parent="#accordionExample">
<div class="accordion-body">
{{ t("answer_9") }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'Subscription',
setup () {
const { t } = useI18n()
const localePath = useLocalePath()
return { t, localePath }
}
}
</script>
<style scoped>
.accordion-item {
border: none;
margin-bottom: 0.5em;
}
.accordion-button {
background-color: rgb(244, 245, 247) !important;
}
.accordion-button:focus {
z-index: 3;
border:none;
outline: 0;
box-shadow: none;
}
.accordion-button:not(.open) {
color: black;
box-shadow: none;
background-color: rgb(244, 245, 247) !important;
font-weight: 600;
}
.accordion-collapse {
background-color: rgb(244, 245, 247);
}
.accordion-button:not(.open):hover, .accordion-button:not(.open):focus {
background-color: rgb(226, 229, 234) !important;
}
</style>

View File

@@ -0,0 +1,51 @@
<template>
<div>
<div class="row justify-content-center">
<div class="col-12 col-lg-9 col-md-11 col-sm-12 col-xl-8">
<h1 class="h3 fw-bold py-3">Impressum</h1>
<div class="row pt-4">
<div class="col-12">
<h2 class="h4 mt-4">Angaben gem&auml;&szlig; &sect; 5 TMG</h2>
<p>
Robert-Carl Rapp<br>
Sindlingerstr. 23<br>
71083 Herrenberg
</p>
<h2 class="h4 mt-4">Postadresse</h2>
<p>
Sindlingerstr. 23</br>
71083 Herrenberg
</p>
<h2 class="h4 mt-4">Kontakt</h2>
<p>
Telefon: +(49)15784037946<br>
E-Mail: kontakt@mindboost.team
</p>
<p>Quelle: <a href="https://www.e-recht24.de/impressum-generator.html">Generated by eRecht24</a></p>
<h2 class="h4 mt-4">Bildnachweise</h2>
<a href="https://de.vecteezy.com/kostenlos-videos/wei%C3%9F">Weiß Stockvideos von Vecteezy</a>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'SettingPage',
setup () {
const { t } = useI18n()
const localePath = useLocalePath()
return { t, localePath }
}
}
</script>
<style>
</style>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,54 @@
<template>
<div>
<div class="row justify-content-center">
<div class="col-12 col-lg-9 col-md-11 col-sm-12 col-xl-8">
<h1 class="h3 fw-bold py-3">Nutzungsbedingungen der App von Mindboost</h1>
<div class="row pt-4">
<div class="col-12">
<h2 class="h4">1. Geltungsbereich</h2>
<p>Diese Nutzungsbedingungen gelten für alle Dienste und Inhalte, die durch die App von Mindboost bereitgestellt werden. Mit der Registrierung bei oder der Nutzung unserer Dienste erklären Sie sich mit diesen Bedingungen einverstanden.</p>
<h2 class="h4">2. Nutzung von Audiodateien</h2>
<p>Die in dieser App verfügbaren Audiodateien dürfen ausschließlich auf der Webseite von Mindboost genutzt werden. Jede Verwendung dieser Audiodateien außerhalb dieser Domain ist strengstens untersagt.</p>
<h2 class="h4">3. Nutzung des Algorithmus</h2>
<p>Der in dieser App enthaltene Algorithmus darf nur von Nutzern mit einer aktiven und gültigen Lizenz verwendet werden. Die Lizenz muss während der gesamten Nutzungsdauer des Algorithmus gültig sein. Eine Nutzung ohne gültige Lizenz verstößt gegen unsere Nutzungsbedingungen.</p>
<h2 class="h4">4. Trial-Version</h2>
<p>Jede natürliche oder juristische Person darf die Trial-Version dieser App nur einmal nutzen. Eine erneute Nutzung der Trial-Version ohne vorherige Absprache mit uns ist nicht erlaubt.</p>
<h2 class="h4">5. Account-Registrierung und Verantwortlichkeit</h2>
<p>Die Nutzer sind verantwortlich für das sichere Aufbewahren ihrer Passwörter und für alle Aktivitäten, die unter ihrem Account stattfinden. Jeder Verdacht auf unbefugte Nutzung des Accounts sollte unverzüglich an unseren Kundenservice gemeldet werden.</p>
<h2 class="h4">6. Zahlungsbedingungen</h2>
<p>Für bestimmte Teile unserer Dienste kann eine Gebühr anfallen. Alle Gebühren sind klar angegeben und sind zum Zeitpunkt der Fälligkeit zahlbar. Zahlungsverzögerungen können zur Deaktivierung des Zugangs führen.</p>
<h2 class="h4">7. Datenschutz</h2>
<p>Ihre persönlichen Daten werden gemäß unserer Datenschutzrichtlinie behandelt, die Sie auf unserer Webseite einsehen können. Mit der Nutzung unserer Dienste stimmen Sie der Verarbeitung Ihrer Daten zu.</p>
<h2 class="h4">8. Änderungen der Dienste</h2>
<p>Wir behalten uns das Recht vor, jederzeit Änderungen an unseren Diensten vorzunehmen oder Dienste einzustellen. Wir werden uns bemühen, die Nutzer rechtzeitig über solche Änderungen zu informieren.</p>
<h2 class="h4">9. Kündigung</h2>
<p>Die Nutzer können ihr Abonnement jederzeit kündigen. Mindboost behält sich das Recht vor, den Service eines Nutzers zu kündigen oder zu suspendieren, wenn dieser gegen die Nutzungsbedingungen verstößt.</p>
<h2 class="h4">10. Haftungsbeschränkung</h2>
<p>Mindboost haftet nicht für indirekte, zufällige, spezielle oder folgeschädigte Schäden, die aus der Nutzung oder Unfähigkeit zur Nutzung der Dienste entstehen.</p>
<h2 class="h4">11. Geltendes Recht und Gerichtsstand</h2>
<p>Diese Nutzungsbedingungen unterliegen dem Recht des Landes, in dem Mindboost seinen Sitz hat, und alle Streitigkeiten werden ausschließlich vor den dortigen Gerichten verhandelt. </p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
setup () {
const { t } = useI18n()
const localePath = useLocalePath()
return { t, localePath }
}
}
</script>

View File

@@ -0,0 +1,298 @@
<template>
<div>
<div class="row justify-content-center">
<div class="col-12 col-lg-9 col-md-11 col-sm-12 col-xl-8">
<h1 class="h3 fw-bold text-center py-3">
Timer Settings
</h1>
<div class="timer__settings">
<!-- Timer Settings -->
<v-list class="px-2 px-md-16">
<!-- Work Time -->
<div class="slider">
<input
id="gain-control"
v-model="timer().settings.timer.work"
type="range"
min="5"
max="60"
step="5"
data-toggle="tooltip"
data-placement="top"
:title="tooltipTitle"
@wheel.prevent="changeVolumeOnWheel"
>
<span
class="slider-progress-bar"
:style="{ width: `${volume * 100}%` }"
/>
</div>
<!-- <v-list-item title="Work Sessions">
<v-slider
v-model="settings.timer.work"
min="5"
max="60"
step="5"
show-ticks
thumb-label
:color="timer().getCurrentColor"
class="w-100"
:ticks="sliderTicks.workTimer"
:rules="rules.workRules"
>
<template #append>
<v-text-field
v-model="settings.timer.work"
hide-details
single-line
density="compact"
type="number"
style="width: 70px"
min="5"
max="60"
/>
</template>
</v-slider> -->
<!-- </v-list-item> -->
<!-- Breaks Sessions -->
<v-list-item
v-for="value, i in ['short-break', 'long-break']"
:key="i"
:title="timer().settings.timer[value].text"
>
<v-slider
v-model="settings.timer[value]"
min="0"
max="60"
step="5"
show-ticks
thumb-label
:color="timer().getCurrentColor"
class="w-100"
:ticks="sliderTicks.breakTimers"
:rules="rules.breakRules"
>
<template #append>
<v-text-field
v-model="settings.timer[value]"
hide-details
single-line
density="compact"
type="number"
style="width: 70px"
min="0"
max="60"
/>
</template>
</v-slider>
</v-list-item>
<v-list-item
title="Max Sessions"
subtitle="The number of work sessions before a long break"
>
<v-slider
v-model="settings.maxSessions"
min="1"
max="10"
step="1"
show-ticks
thumb-label
:color="timer().getCurrentColor"
class="w-100"
:ticks="sliderTicks.maxSessions"
:rules="rules.maxSessionsRules"
>
<template #append>
<v-text-field
v-model="settings.maxSessions"
hide-details
single-line
density="compact"
type="number"
style="width: 70px"
min="1"
max="10"
/>
</template>
</v-slider>
</v-list-item>
<v-list-item>
<v-checkbox
v-model="timer().settings.autoStart"
:color="timer().getCurrentColor"
label="Auto Start Sessions"
true-icon="mdi-timer-play-outline"
false-icon="mdi-timer-play-outline"
/>
</v-list-item>
<v-divider />
<!-- General Settings -->
<v-list-subheader>General Settings</v-list-subheader>
<v-list-item>
<v-checkbox
v-model="settings.notificationsEnabled"
:color="timer().getCurrentColor"
label="Notifications"
true-icon="mdi-bell"
false-icon="mdi-bell-off"
/>
</v-list-item>
<v-list-item>
<v-checkbox
v-model="timer().playSessionEndSound"
:color="timer().getCurrentColor"
label="Sounds"
true-icon="mdi-volume-high"
false-icon="mdi-volume-off"
/>
</v-list-item>
<!-- <v-list-item>
<v-checkbox
v-model="app().showThemeToggle"
:color="timer().getCurrentColor"
label="Show Theme Toggle"
true-icon="mdi-theme-light-dark"
false-icon="mdi-theme-light-dark"
/>
</v-list-item> -->
</v-list>
</div>
</div>
</div>
</div>
</template>
<script>
import { useTimerStore as timer } from '~~/stores/timer'
export default {
name: 'TimerSettings',
setup () {
const { t } = useI18n()
const localePath = useLocalePath()
const sliderTicks = reactive({
workTimer: {
5: '5',
60: '60'
},
breakTimers: {
0: '0',
60: '60'
},
maxSessions: {
1: '1',
10: '10'
}
})
// only the settings that need validation
const settings = reactive({
timer: {
work: 25,
'short-break': 5,
'long-break': 15
},
maxSessions: 4,
notificationsEnabled: false
})
const state = reactive({
dialog: false,
snackbar: false
})
// rules for validating inputs
const rules = reactive({
workRules: [
(value) => {
if (value >= 5 && value <= 60) { return true }
if (value <= 0) { return "Are you kidding me? if you don't wanna work then just go find something better to do than playing with my settings." }
if (value < 5) { return 'Do you want to work for less than 5 mins?! You can do better.' }
return "Don't be so hard on yourself, work for an hour or less before taking a break."
}
],
breakRules: [
(value) => {
if (value >= 0 && value <= 60) { return true }
if (value < 0) { return 'How tf would you take a negative break time you moron?' }
return "That's too much time for a break, stop wasting your time."
}
],
maxSessionsRules: [
(value) => {
if (value >= 1) { return true }
return "Max sessions can't be less than one!"
}
]
})
// save settings to localstorage
const saveSettings = () => {
saveNotification()
// validate user inputs before saving
if (rules.workRules[0](settings.timer.work) === true) {
timer().settings.timer.work.time = settings.timer.work
}
if (rules.breakRules[0](settings.timer['short-break']) === true) {
timer().settings.timer['short-break'].time = settings.timer['short-break']
}
if (rules.breakRules[0](settings.timer['long-break']) === true) {
timer().settings.timer['long-break'].time = settings.timer['long-break']
}
if (rules.maxSessionsRules[0](settings.maxSessions) === true) {
timer().settings.maxSessions = settings.maxSessions
}
storeSettings()
state.dialog = false
state.snackbar = true
}
return { t, localePath }
}
}
// watch(
// () => state.dialog,
// (dialog) => {
// if (!dialog) { // if dialog is closed
// saveSettings()
// } else {
// // retrieve settings from state when dialog is opened
// // settings.notificationsEnabled = timer().showNotification
// settings.maxSessions = timer().settings.maxSessions
// for (const session of Object.keys(settings.timer)) {
// settings.timer[session] = timer().settings.timer[session].time
// }
// }
// }
// )
// get notification permission from the browser
// const saveNotification = () => {
// const { isSupported, show } = useWebNotification({
// title: 'Notifications enabled.'
// })
// if (isSupported.value) {
// if (!timer().showNotification && settings.notificationsEnabled) { show() }
// } else {
// useNuxtApp().$logger.error('Your browser does not support notifications.')
// }
// timer().showNotification = settings.notificationsEnabled
// }
</script>
<style>
.dialog-bottom-transition-enter-active,
.dialog-bottom-transition-leave-active {
transition: transform .1s ease-in-out;
}
.v-footer {
row-gap: 0.4rem;
}
</style>