Some checks failed
Build Latest image / build-container (push) Has been cancelled
491 lines
19 KiB
Vue
491 lines
19 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">
|
|
<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">
|
|
<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-semibold mt-2">
|
|
{{ $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
|
|
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-semibold mt-2">
|
|
{{ $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
|
|
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-semibold mt-2">
|
|
{{ $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
|
|
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="block font-semibold mt-2">{{
|
|
$t("phone_number")
|
|
}}</label>
|
|
<div v-if="userdetails.phone !== '' && !userdetails.phone.includes('+')"
|
|
class="bg-blue-100 border border-blue-200 text-black rounded-lg p-4 mb-1" role="alert" tabindex="-1"
|
|
aria-labelledby="hs-actions-label">
|
|
<div class="flex">
|
|
<div class="shrink-0">
|
|
<svg class="shrink-0 size-4 mt-1" 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">
|
|
<circle cx="12" cy="12" r="10"></circle>
|
|
<path d="M12 16v-4"></path>
|
|
<path d="M12 8h.01"></path>
|
|
</svg>
|
|
</div>
|
|
<div class="ms-3">
|
|
<h3 id="hs-actions-label" class="font-semibold">
|
|
{{ $t('hinweis') }}
|
|
</h3>
|
|
<div class="mt-2 text-sm text-gray-800 font-medium">
|
|
{{ $t('registration_local_phone_nr') }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<input v-model="userdetails.phone" name="phone" id="phone" autocomplete="off"
|
|
:placeholder="[[$t('phone_number')]]" type="text" :class="{
|
|
'border-red-500':
|
|
!isPhoneOkay(userdetails.phone),
|
|
'border-green-300':
|
|
isPhoneOkay(userdetails.phone),
|
|
}" class="
|
|
dark:bg-gray-800
|
|
block
|
|
w-full
|
|
shadow-sm
|
|
sm:text-sm
|
|
border-2
|
|
bg-gray-50
|
|
text-gray-500
|
|
rounded-md
|
|
p-2
|
|
" />
|
|
<p v-if="!isPhoneOkay(userdetails.phone)" 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-semibold mt-2">
|
|
{{ $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
|
|
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-semibold mt-2">{{
|
|
$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
|
|
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-semibold mt-2">
|
|
{{ $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
|
|
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-semibold mt-2">
|
|
{{ $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
|
|
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>
|
|
<div class="p-8">
|
|
<Footer />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import Footer from "@/components/Footer.vue";
|
|
import { runnerSelfServiceControllerGetSelfserviceOrg, runnerSelfServiceControllerRegisterOrganizationRunner, runnerSelfServiceControllerRegisterRunner } from "@odit/lfk-client";
|
|
import isEmail from "validator/es/lib/isEmail";
|
|
import isMobilePhone from "validator/es/lib/isMobilePhone";
|
|
import isPostalCode from "validator/es/lib/isPostalCode";
|
|
import { computed, reactive, ref } from "vue";
|
|
import { useI18n } from 'vue-i18n';
|
|
import { TYPE, useToast } from "vue-toastification";
|
|
const { t } = useI18n()
|
|
const props = defineProps({
|
|
token: String,
|
|
});
|
|
if (props.token) {
|
|
runnerSelfServiceControllerGetSelfserviceOrg({ path: { token: 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: "" },
|
|
});
|
|
function formatPhoneNumber(phoneNumber, countryCode = "+49") {
|
|
// Remove all non-digit characters
|
|
const cleanedNumber = phoneNumber.replace(/\D/g, "");
|
|
|
|
// Check if the number starts with the country code
|
|
if (cleanedNumber.startsWith(countryCode.replace("+", ""))) {
|
|
return "+" + cleanedNumber; // already international
|
|
}
|
|
|
|
// Check if the number starts with 0
|
|
if (cleanedNumber.startsWith("0")) {
|
|
return countryCode + cleanedNumber.slice(1);
|
|
}
|
|
|
|
// If it doesn't start with 0 or the country code, assume it's a local number.
|
|
// In this case, prepend the country code.
|
|
return countryCode + cleanedNumber;
|
|
}
|
|
function isPhoneOkay() {
|
|
if (userdetails.value.phone === "") {
|
|
return true
|
|
}
|
|
const formattedNumber = formatPhoneNumber(userdetails.value.phone)
|
|
if (isMobilePhone(formattedNumber)) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
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 &&
|
|
isPhoneOkay() &&
|
|
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 (isPhoneOkay()) {
|
|
if (isEmail(userdetails.value.mail)) {
|
|
let postdata = {
|
|
email: userdetails.value.mail,
|
|
firstname: userdetails.value.firstname,
|
|
middlename: userdetails.value.middlename,
|
|
lastname: userdetails.value.lastname,
|
|
address: {},
|
|
};
|
|
if (userdetails.value.phone !== "") {
|
|
postdata.phone = formatPhoneNumber(userdetails.value.phone)
|
|
}
|
|
if (provide_address.value === true) {
|
|
postdata.address = {
|
|
address1: userdetails.value.address.street,
|
|
address2: userdetails.value.address.address2 || "",
|
|
city: userdetails.value.address.city,
|
|
postalcode: userdetails.value.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);
|
|
registrationState.value = "loading";
|
|
if (props.token) {
|
|
runnerSelfServiceControllerRegisterOrganizationRunner({ path: { token: props.token }, body: postdata, query: { locale: browserlocale } })
|
|
.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 });
|
|
}
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
runnerSelfServiceControllerRegisterRunner({ body: postdata, query: { locale: browserlocale } })
|
|
.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> |