29
src/views/Home.vue
Normal file
29
src/views/Home.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="bg-cover bg-fixed m-0 h-screen" style="background-image: url('/background.jpg');">
|
||||
<section class="container px-4 py-24 mx-auto">
|
||||
<div class="w-full mx-auto text-center">
|
||||
<img src="/favicon.png" class="h-32 mx-auto" />
|
||||
<h1
|
||||
class="mb-6 text-4xl font-extrabold leading-none tracking-normal text-white md:text-6xl md:tracking-tight"
|
||||
>Lauf Für Kaya!</h1>
|
||||
<p class="px-0 mb-6 text-lg text-gray-100 md:text-xl lg:px-24">Selfservice Portal</p>
|
||||
<p class="px-0 mb-6 text-md text-gray-100 lg:px-24">{{ $t('main_page_text') }}</p>
|
||||
<div class="mt-6 sm:flex place-content-center">
|
||||
<router-link
|
||||
class="w-full sm:w-auto inline-flex px-6 py-3 border border-transparent text-base font-semibold rounded-md text-gray-900 bg-white shadow-sm hover:text-gray-600 focus:outline-none focus:text-gray-600 transition ease-in-out duration-150 xl:text-lg xl:py-4"
|
||||
to="/register"
|
||||
>{{ $t('register_now') }}</router-link>
|
||||
<router-link
|
||||
to="/profile"
|
||||
class="mt-4 sm:ml-4 sm:mt-0 w-full sm:w-auto inline-flex px-6 py-3 border border-transparent text-base font-semibold rounded-md text-white bg-gray-800 shadow-sm hover:bg-gray-700 focus:outline-none focus:bg-gray-700 transition ease-in-out duration-150 xl:text-lg xl:py-4"
|
||||
>{{ $t('view_my_data') }}</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<Footer />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Footer from "@/components/Footer.vue";
|
||||
</script>
|
||||
38
src/views/Imprint.vue
Normal file
38
src/views/Imprint.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<section class="container px-4 py-24 mx-auto">
|
||||
<div class="simplecontent">
|
||||
<div class="mb-24 text-left md:text-center">
|
||||
<h1
|
||||
class="mb-4 text-4xl font-bold leading-tight text-gray-900 dark:text-gray-50 md:text-5xl"
|
||||
>{{$t('imprint')}}</h1>
|
||||
</div>
|
||||
<div class="mx-auto prose" v-html="content"></div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<style src="../simple.css">
|
||||
</style>
|
||||
<script>
|
||||
import marked from "marked";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
content: ""
|
||||
}
|
||||
},
|
||||
async beforeMount() {
|
||||
const browserlocale = ((navigator.languages && navigator.languages[0]) || '').substr(0, 2);
|
||||
let md = "";
|
||||
try {
|
||||
md = await fetch(`/imprint_${browserlocale}.md`);
|
||||
} catch (error) {
|
||||
try {
|
||||
md = await fetch(`/imprint_en.md`);
|
||||
} catch (error) {
|
||||
md = "Error loading Imprint";
|
||||
}
|
||||
}
|
||||
this.content = marked(await md.text());
|
||||
},
|
||||
}
|
||||
</script>
|
||||
38
src/views/Privacy.vue
Normal file
38
src/views/Privacy.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<section class="container px-4 py-24 mx-auto">
|
||||
<div class="simplecontent">
|
||||
<div class="mb-24 text-left md:text-center">
|
||||
<h1
|
||||
class="mb-4 text-4xl font-bold leading-tight text-gray-900 dark:text-gray-50 md:text-5xl"
|
||||
>{{ $t('privacy_policy') }}</h1>
|
||||
</div>
|
||||
<div class="mx-auto prose" v-html="content"></div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<style src="../simple.css">
|
||||
</style>
|
||||
<script>
|
||||
import marked from "marked";
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
content: ""
|
||||
}
|
||||
},
|
||||
async beforeMount() {
|
||||
const browserlocale = ((navigator.languages && navigator.languages[0]) || '').substr(0, 2);
|
||||
let md = "";
|
||||
try {
|
||||
md = await fetch(`/privacy_${browserlocale}.md`);
|
||||
} catch (error) {
|
||||
try {
|
||||
md = await fetch(`/privacy_en.md`);
|
||||
} catch (error) {
|
||||
md = "Error loading Privacy Policy";
|
||||
}
|
||||
}
|
||||
this.content = marked(await md.text());
|
||||
},
|
||||
}
|
||||
</script>
|
||||
209
src/views/Profile.vue
Normal file
209
src/views/Profile.vue
Normal file
@@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<div class="min-h-screen w-full p-4">
|
||||
<section class="text-white body-font">
|
||||
<div class="container mx-auto flex items-center md:flex-row flex-col">
|
||||
<div
|
||||
class="flex flex-col md:pr-10 md:mb-0 mb-6 pr-0 w-full md:w-auto md:text-left text-center text-black dark:text-gray-200"
|
||||
>
|
||||
<p
|
||||
class="text-3xl font-bold whitespace-nowrap"
|
||||
v-text="(state.firstname || '') + ' ' + (state.middlename || '') + ' ' + (state.lastname || '')"
|
||||
></p>
|
||||
<p class="text-md whitespace-nowrap">{{ state.group }}</p>
|
||||
</div>
|
||||
<div class="inline-flex md:ml-auto md:mr-0 mx-auto items-center">
|
||||
<button
|
||||
type="button"
|
||||
class="focus:border-black focus:ring-2 focus:ring-black text-white text-sm py-2.5 px-5 rounded-md bg-blue-500 hover:bg-blue-600 hover:shadow-lg"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="feather feather-download"
|
||||
style="display: inline;height: 1rem;vertical-align: sub;"
|
||||
>
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||
<polyline points="7 10 12 15 17 10" />
|
||||
<line x1="12" y1="15" x2="12" y2="3" />
|
||||
</svg>
|
||||
{{ $t('download_certificate') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-full p-4">
|
||||
<div class="flex flex-wrap flex-col w-full tabs">
|
||||
<div class="flex lg:flex-wrap flex-row lg:space-x-2">
|
||||
<div class="flex-none">
|
||||
<button
|
||||
@click="() => { state.activetab = 'profile' }"
|
||||
:class="{ 'tab-active border-b-2 font-medium border-blue-500': (state.activetab === 'profile') }"
|
||||
class="tab tab-underline py-4 px-6 block"
|
||||
type="button"
|
||||
>{{ $t('profile') }}</button>
|
||||
</div>
|
||||
<div class="flex-none">
|
||||
<button
|
||||
@click="() => { state.activetab = 'laptimes' }"
|
||||
:class="{ 'tab-active border-b-2 font-medium border-blue-500': (state.activetab === 'laptimes') }"
|
||||
class="tab tab-underline py-4 px-6 block"
|
||||
type="button"
|
||||
>{{ $t('lap_times') }}</button>
|
||||
</div>
|
||||
<div class="flex-none">
|
||||
<button
|
||||
@click="() => { state.activetab = 'sponsorings' }"
|
||||
:class="{ 'tab-active border-b-2 font-medium border-blue-500': (state.activetab === 'sponsorings') }"
|
||||
class="tab tab-underline py-4 px-6 block"
|
||||
type="button"
|
||||
>{{ $t('sponsoring') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="(state.activetab === 'profile')" class="tab-content block">
|
||||
<div class="py-4 w-full">
|
||||
<div class="flex flex-col">
|
||||
<form class="form flex flex-wrap w-full">
|
||||
<div class="w-full">
|
||||
<div class="form-element">
|
||||
<div class="text-lg">{{ $t('vorname') }}</div>
|
||||
<p
|
||||
class="h-10 w-full dark:bg-gray-800 rounded text-base outline-none dark:text-gray-100 text-gray-600 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
|
||||
v-text="state.firstname"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-element">
|
||||
<div class="text-lg">{{ $t('mittelname') }}</div>
|
||||
<p
|
||||
class="h-10 w-full dark:bg-gray-800 rounded text-base outline-none dark:text-gray-100 text-gray-600 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
|
||||
v-text="state.middlename"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-element">
|
||||
<div class="text-lg">{{ $t('nachname') }}</div>
|
||||
<p
|
||||
class="h-10 w-full dark:bg-gray-800 rounded text-base outline-none dark:text-gray-100 text-gray-600 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
|
||||
v-text="state.lastname"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-element">
|
||||
<div class="text-lg">{{ $t('e_mail_adress') }}</div>
|
||||
<p
|
||||
class="h-10 w-full dark:bg-gray-800 rounded text-base outline-none dark:text-gray-100 text-gray-600 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
|
||||
v-text="state.email"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-element">
|
||||
<div class="text-lg">{{ $t('phone_number') }}</div>
|
||||
<p
|
||||
class="h-10 w-full dark:bg-gray-800 rounded text-base outline-none dark:text-gray-100 text-gray-600 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
|
||||
v-text="state.phone"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="(state.activetab === 'laptimes')" class="tab-content block">
|
||||
<div class="py-4 w-full">
|
||||
<section class="text-gray-400 dark:bg-gray-900 body-font">
|
||||
<div class="container mx-auto">
|
||||
<div class="lg:w-2/3 w-full mx-auto overflow-auto">
|
||||
<table
|
||||
v-if="state.scans.length > 0"
|
||||
class="table-auto w-full text-left whitespace-no-wrap"
|
||||
>
|
||||
<thead
|
||||
class="text-black bg-gray-300 dark:text-white text-sm dark:bg-gray-800"
|
||||
>
|
||||
<tr>
|
||||
<th
|
||||
class="px-4 py-3 title-font tracking-wider font-medium"
|
||||
>{{ $t('distance') }}</th>
|
||||
<th
|
||||
class="px-4 py-3 title-font tracking-wider font-medium"
|
||||
>{{ $t('lap_time') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-gray-900 dark:text-gray-50">
|
||||
<tr v-for="s in state.scans" :key="s.id">
|
||||
<td class="px-4 py-3">
|
||||
<span v-text="s.distance"></span>m
|
||||
</td>
|
||||
<td class="px-4 py-3" v-text="s.lapTime"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div v-else class="text-center font-bold text-black dark:text-white text-2xl">
|
||||
<img
|
||||
src="../assets/empty_laps.svg"
|
||||
style="height:25rem; margin:0 auto;"
|
||||
:alt="[[$t('no_laps_scans_were_recorded_yet')]]"
|
||||
/>
|
||||
{{ $t('no_laps_scans_were_recorded_yet') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="(state.activetab === 'sponsorings')" class="tab-content block">
|
||||
<div class="py-4 w-full">coming soon...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import { useToast } from "vue-toastification";
|
||||
import axios from "redaxios";
|
||||
|
||||
const state = reactive({
|
||||
phone: "",
|
||||
email: "",
|
||||
firstname: "",
|
||||
middlename: "",
|
||||
lastname: "",
|
||||
scans: [],
|
||||
group: "",
|
||||
activetab: "profile",
|
||||
})
|
||||
const toast = useToast();
|
||||
const props = defineProps({
|
||||
token: String
|
||||
})
|
||||
const accesstoken = atob(props.token);
|
||||
axios.get(`${config.baseurl_selfservice}/api/runners/me/${accesstoken}`)
|
||||
.then(({ data }) => {
|
||||
state.phone = data.phone;
|
||||
state.email = data.email;
|
||||
state.firstname = data.firstname;
|
||||
state.middlename = data.middlename;
|
||||
state.lastname = data.lastname;
|
||||
state.group = data.group;
|
||||
}).catch((error) => {
|
||||
toast.error("An error occured while loading your profile data");
|
||||
})
|
||||
axios.get(`${config.baseurl}api/runners/me/${accesstoken}/scans`)
|
||||
.then(({ data }) => {
|
||||
data.map(function(s) {
|
||||
s.lapTime = Math.floor(s.lapTime / 60) + 'min ' + (Math.floor(s.lapTime % 60) + "").padStart(2, "0") + "s"
|
||||
return s;
|
||||
})
|
||||
data.filter(s => s.valid === true);
|
||||
state.scans = data;
|
||||
}).catch((error) => {
|
||||
toast.error("An error occured while loading your profile data");
|
||||
})
|
||||
</script>
|
||||
339
src/views/Register.vue
Normal file
339
src/views/Register.vue
Normal file
@@ -0,0 +1,339 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex items-center justify-center">
|
||||
<div class="max-w-md w-full py-12 px-6">
|
||||
<img class="mx-auto h-24 w-auto" src="/favicon.png" alt />
|
||||
<h1
|
||||
class="sm:text-3xl text-2xl font-medium title-font mb-4 text-center"
|
||||
>Lauf für Kaya! - {{ $t('registrieren') }}</h1>
|
||||
<p class="mx-auto leading-relaxed text-base text-center">{{ $t('register.register_now') }}</p>
|
||||
<p
|
||||
v-if="state.org_name !== ''"
|
||||
class="mx-auto leading-relaxed text-base text-center"
|
||||
>{{ $t('organization') }}: {{ state.org_name }}</p>
|
||||
<p
|
||||
v-if="state.org_name !== '' && state.org_teams.length > 0"
|
||||
class="mx-auto leading-relaxed text-base text-center"
|
||||
>Team:</p>
|
||||
<select
|
||||
v-model="org_team"
|
||||
v-if="state.org_name !== '' && state.org_teams.length > 0"
|
||||
class="w-full border bg-white rounded px-3 py-2 outline-none block mt-1 text-sm dark:text-gray-300 dark:border-gray-600 dark:bg-gray-700 form-select focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:focus:shadow-outline-gray"
|
||||
>
|
||||
<option v-for="t in state.org_teams" :key="t.id" :value="t.id">{{ t.name }}</option>
|
||||
</select>
|
||||
<p
|
||||
v-if="state.org_name === ''"
|
||||
class="mx-auto leading-relaxed text-base text-center"
|
||||
>Bürgerlauf</p>
|
||||
<div class="mt-4">
|
||||
<label for="first_name" class="block font-medium">
|
||||
{{ $t('vorname') }}
|
||||
<span class="font-bold">*</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="userdetails.firstname"
|
||||
name="firstname"
|
||||
id="first_name"
|
||||
autocomplete="off"
|
||||
:placeholder="[[$t('vorname')]]"
|
||||
type="text"
|
||||
:class="{ 'border-red-500': (!userdetails.firstname.trim()), 'border-green-300': (userdetails.firstname.trim()) }"
|
||||
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2"
|
||||
/>
|
||||
<!-- -->
|
||||
<label for="middle_name" class="block font-medium">{{ $t('mittelname') }}</label>
|
||||
<input
|
||||
v-model="userdetails.middlename"
|
||||
name="middlename"
|
||||
id="middle_name"
|
||||
autocomplete="off"
|
||||
:placeholder="[[$t('mittelname')]]"
|
||||
type="text"
|
||||
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2"
|
||||
/>
|
||||
<!-- -->
|
||||
<label for="last_name" class="block font-medium">
|
||||
{{ $t('nachname') }}
|
||||
<span class="font-bold">*</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="userdetails.lastname"
|
||||
name="lastname"
|
||||
id="last_name"
|
||||
autocomplete="off"
|
||||
:placeholder="[[$t('nachname')]]"
|
||||
type="text"
|
||||
:class="{ 'border-red-500': (!userdetails.lastname.trim()), 'border-green-300': (userdetails.lastname.trim()) }"
|
||||
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2"
|
||||
/>
|
||||
<!-- -->
|
||||
<label for="email_address" class="block font-medium">
|
||||
{{ $t('e_mail_adress') }}
|
||||
<span class="font-bold">*</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="userdetails.mail"
|
||||
name="email_address"
|
||||
id="email_address"
|
||||
autocomplete="off"
|
||||
:placeholder="[[$t('e_mail_adress')]]"
|
||||
type="email"
|
||||
:class="{ 'border-red-500': (!isEmail(userdetails.mail)), 'border-green-300': (isEmail(userdetails.mail)) }"
|
||||
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-2 bg-gray-50 text-gray-500 rounded-md p-2"
|
||||
/>
|
||||
<p v-if="!isEmail(userdetails.mail)" class="text-sm">{{ $t('please_provide_valid_mail') }}</p>
|
||||
<!-- -->
|
||||
<label for="phone" class="select-none block font-medium">{{ $t('phone_number') }}</label>
|
||||
<input
|
||||
v-model="userdetails.phone"
|
||||
name="phone"
|
||||
id="phone"
|
||||
autocomplete="off"
|
||||
:placeholder="[[$t('phone_number')]]"
|
||||
type="text"
|
||||
:class="{ 'border-red-500': (!isMobilePhone(userdetails.phone) && userdetails.phone.trim()), 'border-green-300': (isMobilePhone(userdetails.phone) && userdetails.phone.trim()) }"
|
||||
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-2 bg-gray-50 text-gray-500 rounded-md p-2"
|
||||
/>
|
||||
<p
|
||||
v-if="(!isMobilePhone(userdetails.phone) && userdetails.phone.trim())"
|
||||
class="text-sm"
|
||||
>{{ $t('this_is_not_a_valid_international_phone_number') }}</p>
|
||||
<!-- -->
|
||||
<div class="grid grid-cols-6 mt-6">
|
||||
<div class="col-span-6"></div>
|
||||
<div class="flex items-start col-span-6">
|
||||
<div class="flex items-center h-5">
|
||||
<input
|
||||
v-model="provide_address"
|
||||
id="address_activated"
|
||||
name="address_activated"
|
||||
type="checkbox"
|
||||
class="h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3 text-sm">
|
||||
<label
|
||||
for="address_activated"
|
||||
class="font-medium text-gray-400 select-none"
|
||||
>{{ $t('provide_address') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="provide_address === true" class="col-span-6">
|
||||
<div class="col-span-6">
|
||||
<label for="street" class="block font-medium">
|
||||
{{ $t('strasse') }}
|
||||
<span class="font-bold">*</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="userdetails.address.street"
|
||||
type="text"
|
||||
name="street"
|
||||
:placeholder="[[$t('strasse')]]"
|
||||
id="street"
|
||||
autocomplete="street-address"
|
||||
:class="{ 'border-red-500': (!userdetails.address.street.trim()), 'border-green-300': (userdetails.address.street.trim()) }"
|
||||
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6">
|
||||
<label for="address2" class="block font-medium">{{ $t('apartment_suite_etc') }}</label>
|
||||
<input
|
||||
v-model="userdetails.address.address2"
|
||||
type="text"
|
||||
name="address2"
|
||||
:placeholder="[[$t('apartment_suite_etc')]]"
|
||||
id="address2"
|
||||
autocomplete="street-address"
|
||||
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-6 lg:col-span-2">
|
||||
<label for="city" class="block font-medium">
|
||||
{{ $t('ort') }}
|
||||
<span class="font-bold">*</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="userdetails.address.city"
|
||||
type="text"
|
||||
name="city"
|
||||
:placeholder="[[$t('ort')]]"
|
||||
id="city"
|
||||
:class="{ 'border-red-500': (!userdetails.address.city.trim()), 'border-green-300': (userdetails.address.city.trim()) }"
|
||||
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-6 sm:col-span-3 lg:col-span-2">
|
||||
<label for="postal_code" class="block font-medium">
|
||||
{{ $t('plz') }}
|
||||
<span class="font-bold">*</span>
|
||||
</label>
|
||||
<input
|
||||
v-model="userdetails.address.zipcode"
|
||||
type="text"
|
||||
name="postal_code"
|
||||
:placeholder="[[$t('plz')]]"
|
||||
id="postal_code"
|
||||
autocomplete="postal-code"
|
||||
:class="{ 'border-red-500': (!isPostalCode(userdetails.address.zipcode, 'DE')), 'border-green-300': (isPostalCode(userdetails.address.zipcode, 'DE')) }"
|
||||
class="dark:bg-gray-800 mt-1 block w-full shadow-sm sm:text-sm border-gray-300 border-2 bg-gray-50 text-gray-500 rounded-md p-2"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
v-if="!isPostalCode(userdetails.address.zipcode, 'DE')"
|
||||
class="text-sm"
|
||||
>{{ $t('please_provide_a_valid_zipcode') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start mt-6">
|
||||
<div class="flex items-center h-5">
|
||||
<input
|
||||
v-model="agb_accepted"
|
||||
id="agb_accepted"
|
||||
name="agb_accepted"
|
||||
type="checkbox"
|
||||
class="h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3 text-sm">
|
||||
<label for="agb_accepted" class="font-medium text-gray-400 select-none">
|
||||
{{ $t('i_accept', { tos: $t('tos') }) }}
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer,noopener"
|
||||
href
|
||||
class="underline"
|
||||
>{{ $t('tos') }}</a>
|
||||
{{ $t('i_accept_end') }}
|
||||
<span class="font-bold">*</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start mt-6">
|
||||
<div class="flex items-center h-5">
|
||||
<input
|
||||
v-model="data_confirmed"
|
||||
id="data_confirmed"
|
||||
name="data_confirmed"
|
||||
type="checkbox"
|
||||
class="h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3 text-sm">
|
||||
<label for="data_confirmed" class="font-medium text-gray-400 select-none">
|
||||
{{ $t('confirm_personal_data') }}
|
||||
<span class="font-bold">*</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<button
|
||||
@click="login"
|
||||
:disabled="(!state.submit_enabled)"
|
||||
:class="{ 'opacity-50': (!state.submit_enabled), 'cursor-not-allowed': (!state.submit_enabled) }"
|
||||
class="text-white block w-full text-center py-2 px-3 border-2 border-gray-300 rounded-md p-1 bg-blue-800 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"
|
||||
>{{ $t('registrieren') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<div class="relative">
|
||||
<div class="absolute inset-0 flex items-center">
|
||||
<div class="w-full border-t border-gray-300"></div>
|
||||
</div>
|
||||
<div class="relative flex justify-center text-sm">
|
||||
<span class="px-2 bg-white dark:bg-gray-900">{{ $t('already_have_an_account') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<a
|
||||
href="./login"
|
||||
class="block w-full text-center py-2 px-3 border-2 border-gray-300 rounded-md p-1 dark:bg-gray-800 font-medium hover:border-gray-400 focus:outline-none focus:border-gray-400 sm:text-sm"
|
||||
>{{ $t('go_to_login') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref, reactive, defineProps } from "vue";
|
||||
import axios from "redaxios";
|
||||
import isEmail from 'validator/es/lib/isEmail';
|
||||
import isMobilePhone from 'validator/es/lib/isMobilePhone';
|
||||
import isPostalCode from 'validator/es/lib/isPostalCode';
|
||||
import { useToast } from "vue-toastification";
|
||||
// import { router } from '../router';
|
||||
import { i18n } from '../language';
|
||||
|
||||
const props = defineProps({
|
||||
token: String
|
||||
})
|
||||
if (props.token) {
|
||||
props.token = atob(props.token);
|
||||
axios.get(`${config.baseurl}api/organizations/selfservice/${props.token}`)
|
||||
.then(({ data }) => {
|
||||
state.org_name = data.name;
|
||||
state.org_teams = data.teams;
|
||||
org_team.value = data.teams[0]?.id;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
let userdetails = ref({ firstname: "", lastname: "", middlename: "", mail: "", phone: "", address: { street: "", address2: "", city: "", zipcode: "" } });
|
||||
let provide_address = ref(false);
|
||||
let agb_accepted = ref(false);
|
||||
let data_confirmed = ref(false);
|
||||
let org_team = ref("");
|
||||
//
|
||||
const state = reactive({
|
||||
org_name: "",
|
||||
org_teams: [],
|
||||
submit_enabled: computed(() => agb_accepted.value === true && data_confirmed.value === true && (isMobilePhone(userdetails.value.phone) || !userdetails.value.phone.trim()) && isEmail(userdetails.value.mail)
|
||||
&& userdetails.value.firstname
|
||||
&& userdetails.value.lastname && (provide_address.value === false || provide_address.value === true && (userdetails.value.address.street.trim() && userdetails.value.address.city.trim() && isPostalCode(userdetails.value.address.zipcode, "DE"))))
|
||||
})
|
||||
const toast = useToast();
|
||||
function login() {
|
||||
userdetails = userdetails.value;
|
||||
if (userdetails.phone === "" || isMobilePhone(userdetails.phone)) {
|
||||
if (isEmail(userdetails.mail)) {
|
||||
let postdata = {
|
||||
"email": userdetails.mail,
|
||||
"firstname": userdetails.firstname,
|
||||
"middlename": userdetails.middlename,
|
||||
"lastname": userdetails.lastname,
|
||||
"address": {}
|
||||
}
|
||||
if (isMobilePhone(userdetails.phone)) {
|
||||
postdata.phone = userdetails.phone;
|
||||
}
|
||||
if (provide_address.value === true) {
|
||||
postdata.address = {
|
||||
address1: userdetails.address.street,
|
||||
address2: userdetails.address.address2 || "",
|
||||
city: userdetails.address.city,
|
||||
postalcode: userdetails.address.zipcode,
|
||||
country: "DE",
|
||||
}
|
||||
}
|
||||
if (state.org_name !== '' && state.org_teams.length > 0) {
|
||||
postdata.team = org_team.value;
|
||||
}
|
||||
toast("registration in progress...");
|
||||
let url = `${config.baseurl}api/runners/register`;
|
||||
if (props.token) {
|
||||
url = `${config.baseurl}api/runners/register/${props.token}`
|
||||
}
|
||||
axios.post(url, postdata)
|
||||
.then(({ data }) => {
|
||||
const token = btoa(data.token);
|
||||
// router.push(`${config.baseurl_selfservice}/profile/${token}`);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user