Files
selfservice/src/views/Register.vue

454 lines
17 KiB
Vue

<template>
<div class="min-h-screen flex items-center justify-center" v-if="registrationState === 'registered'">
<div class="max-w-md w-full py-12 px-6 font-[Athiti]">
<img class="mx-auto h-24 w-auto" src="/favicon-lfk.png" alt />
<h1 class="sm:text-3xl text-2xl font-semibold title-font mb-4 text-center">
Lauf für Kaya! - {{ $t('registriert') }}
</h1>
<p class="mx-auto leading-relaxed text-base text-center">
Bitte klicken Sie zum Fortfahren auf den Link, den wir an
<b class="font-bold">{{ userdetails.mail }}</b> geschickt haben.
</p>
</div>
</div>
<div class="min-h-screen flex items-center justify-center" v-else>
<div class="max-w-md w-full py-12 px-6 font-[Athiti]">
<img class="mx-auto h-24 w-auto" src="/favicon-lfk.png" alt />
<h1 class="sm:text-3xl text-2xl font-semibold title-font mb-4 text-center">
Lauf für Kaya! - {{ $t("registrieren") }}
</h1>
<p class="mx-auto leading-relaxed text-base text-center font-medium">
{{ $t("register.register_now") }}
</p>
<p v-if="state.org_name !== ''" class="mx-auto leading-relaxed text-base text-center font-medium">
{{ $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">
{{ $t('buergerlauf') }}
</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-600 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-600 select-none">
{{ $t("i_accept", { tos: $t("privacy_policy") }) }}
<a target="_blank" rel="noreferrer,noopener" href="https://lauf-fuer-kaya.de/datenschutz/"
class="underline">{{ $t("privacy_policy") }}</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-600 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
not-disabled:hover:border-gray-400
not-disabled:hover:bg-blue-600
not-disabled:cursor-pointer
not-disabled:focus:outline-none focus:border-gray-400
sm:text-sm
">
{{ $t("registrieren") }}
</button>
</div>
</div>
</div>
</div>
<Footer></Footer>
</template>
<script setup>
import { computed, ref, reactive } 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 { TYPE, useToast } from "vue-toastification";
import Footer from "@/components/Footer.vue";
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const props = defineProps({
token: String,
});
if (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("");
let registrationState = ref("pending");
//
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(t('registration_running'));
const browserlocale = (
(navigator.languages && navigator.languages[0]) ||
""
).substr(0, 2);
let url = `${config.baseurl}api/runners/register/?locale=${browserlocale}`;
if (props.token) {
url = `${config.baseurl}api/runners/register/${props.token}/?locale=${browserlocale}`;
}
registrationState.value = "loading";
axios
.post(url, postdata)
.then(() => {
registrationState.value = "registered";
})
.catch((error) => {
console.log(error);
if (error.data.message === "E-Mail already registered") {
toast(t('already_registered'), { type: TYPE.ERROR });
} else if (error.data.message === "Invalid body, check 'errors' property for more info.") {
error.data.errors.forEach(e => {
if (e.property === "phone") {
toast(t('invalid_input_phone_number_should_be_international_format'), { type: TYPE.ERROR });
}
});
}
});
}
}
}
</script>