|
@@ -1,70 +1,39 @@
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
-import { defineAsyncComponent, ref, watch, onMounted } from "vue";
|
|
|
|
-import { useRoute } from "vue-router";
|
|
|
|
|
|
+import { defineAsyncComponent, ref, onMounted } from "vue";
|
|
import Toast from "toasters";
|
|
import Toast from "toasters";
|
|
import { storeToRefs } from "pinia";
|
|
import { storeToRefs } from "pinia";
|
|
import { useConfigStore } from "@/stores/config";
|
|
import { useConfigStore } from "@/stores/config";
|
|
import { useUserAuthStore } from "@/stores/userAuth";
|
|
import { useUserAuthStore } from "@/stores/userAuth";
|
|
import { useModalsStore } from "@/stores/modals";
|
|
import { useModalsStore } from "@/stores/modals";
|
|
import validation from "@/validation";
|
|
import validation from "@/validation";
|
|
|
|
+import { useEvents } from "@/composables/useEvents";
|
|
|
|
+import { useWebsocketStore } from "@/stores/websocket";
|
|
|
|
+import { useForm } from "@/composables/useForm";
|
|
|
|
|
|
const Modal = defineAsyncComponent(() => import("@/components/Modal.vue"));
|
|
const Modal = defineAsyncComponent(() => import("@/components/Modal.vue"));
|
|
const InputHelpBox = defineAsyncComponent(
|
|
const InputHelpBox = defineAsyncComponent(
|
|
() => import("@/components/InputHelpBox.vue")
|
|
() => import("@/components/InputHelpBox.vue")
|
|
);
|
|
);
|
|
|
|
|
|
-const route = useRoute();
|
|
|
|
-
|
|
|
|
-const username = ref({
|
|
|
|
- value: "",
|
|
|
|
- valid: false,
|
|
|
|
- entered: false,
|
|
|
|
- message: "Only letters, numbers and underscores are allowed."
|
|
|
|
-});
|
|
|
|
-const email = ref({
|
|
|
|
- value: "",
|
|
|
|
- valid: false,
|
|
|
|
- entered: false,
|
|
|
|
- message: "Please enter a valid email address."
|
|
|
|
-});
|
|
|
|
-const password = ref({
|
|
|
|
- value: "",
|
|
|
|
- valid: false,
|
|
|
|
- entered: false,
|
|
|
|
- visible: false,
|
|
|
|
- message:
|
|
|
|
- "Include at least one lowercase letter, one uppercase letter, one number and one special character."
|
|
|
|
-});
|
|
|
|
|
|
+const passwordVisible = ref(false);
|
|
const passwordElement = ref();
|
|
const passwordElement = ref();
|
|
|
|
|
|
-const { register } = useUserAuthStore();
|
|
|
|
-
|
|
|
|
const configStore = useConfigStore();
|
|
const configStore = useConfigStore();
|
|
const { registrationDisabled } = storeToRefs(configStore);
|
|
const { registrationDisabled } = storeToRefs(configStore);
|
|
const { openModal, closeCurrentModal } = useModalsStore();
|
|
const { openModal, closeCurrentModal } = useModalsStore();
|
|
|
|
|
|
-const submitModal = () => {
|
|
|
|
- if (!username.value.valid || !email.value.valid || !password.value.valid)
|
|
|
|
- return new Toast("Please ensure all fields are valid.");
|
|
|
|
|
|
+const { runJob } = useWebsocketStore();
|
|
|
|
+const { onReady } = useEvents();
|
|
|
|
|
|
- return register({
|
|
|
|
- username: username.value.value,
|
|
|
|
- email: email.value.value,
|
|
|
|
- password: password.value.value,
|
|
|
|
- })
|
|
|
|
- .then((res: any) => {
|
|
|
|
- if (res.status === "success") window.location.reload();
|
|
|
|
- })
|
|
|
|
- .catch(err => new Toast(err.message));
|
|
|
|
-};
|
|
|
|
|
|
+const { login } = useUserAuthStore();
|
|
|
|
|
|
const togglePasswordVisibility = () => {
|
|
const togglePasswordVisibility = () => {
|
|
if (passwordElement.value.type === "password") {
|
|
if (passwordElement.value.type === "password") {
|
|
passwordElement.value.type = "text";
|
|
passwordElement.value.type = "text";
|
|
- password.value.visible = true;
|
|
|
|
|
|
+ passwordVisible.value = true;
|
|
} else {
|
|
} else {
|
|
passwordElement.value.type = "password";
|
|
passwordElement.value.type = "password";
|
|
- password.value.visible = false;
|
|
|
|
|
|
+ passwordVisible.value = false;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
@@ -73,72 +42,77 @@ const changeToLoginModal = () => {
|
|
openModal("login");
|
|
openModal("login");
|
|
};
|
|
};
|
|
|
|
|
|
-watch(
|
|
|
|
- () => username.value.value,
|
|
|
|
- value => {
|
|
|
|
- username.value.entered = true;
|
|
|
|
- if (!validation.isLength(value, 2, 32)) {
|
|
|
|
- username.value.message =
|
|
|
|
- "Username must have between 2 and 32 characters.";
|
|
|
|
- username.value.valid = false;
|
|
|
|
- } else if (!validation.regex.azAZ09_.test(value)) {
|
|
|
|
- username.value.message =
|
|
|
|
- "Invalid format. Allowed characters: a-z, A-Z, 0-9 and _.";
|
|
|
|
- username.value.valid = false;
|
|
|
|
- } else if (value.replaceAll(/[_]/g, "").length === 0) {
|
|
|
|
- username.value.message =
|
|
|
|
- "Invalid format. Allowed characters: a-z, A-Z, 0-9 and _, and there has to be at least one letter or number.";
|
|
|
|
- username.value.valid = false;
|
|
|
|
- } else {
|
|
|
|
- username.value.message = "Everything looks great!";
|
|
|
|
- username.value.valid = true;
|
|
|
|
|
|
+const { inputs, validate, save } = useForm(
|
|
|
|
+ {
|
|
|
|
+ username: {
|
|
|
|
+ value: null,
|
|
|
|
+ validate: (value: string) => {
|
|
|
|
+ if (!validation.isLength(value, 2, 32))
|
|
|
|
+ return "Username must have between 2 and 32 characters.";
|
|
|
|
+ if (!validation.regex.azAZ09_.test(value))
|
|
|
|
+ return "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _.";
|
|
|
|
+ if (value.replaceAll(/[_]/g, "").length === 0)
|
|
|
|
+ return "Invalid username format. Allowed characters: a-z, A-Z, 0-9 and _, and there has to be at least one letter or number.";
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ emailAddress: {
|
|
|
|
+ value: null,
|
|
|
|
+ validate: (value: string) => {
|
|
|
|
+ if (!validation.isLength(value, 3, 254))
|
|
|
|
+ return "Email address must have between 3 and 254 characters.";
|
|
|
|
+ if (
|
|
|
|
+ value.indexOf("@") !== value.lastIndexOf("@") ||
|
|
|
|
+ !validation.regex.emailSimple.test(value)
|
|
|
|
+ )
|
|
|
|
+ return "Invalid email address format.";
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ password: {
|
|
|
|
+ value: null,
|
|
|
|
+ validate: (value: string) => {
|
|
|
|
+ if (!validation.isLength(value, 6, 200))
|
|
|
|
+ return "Password must have between 6 and 200 characters.";
|
|
|
|
+ if (!validation.regex.password.test(value))
|
|
|
|
+ return "Include at least one lowercase letter, one uppercase letter, one number and one special character.";
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- }
|
|
|
|
-);
|
|
|
|
-watch(
|
|
|
|
- () => email.value.value,
|
|
|
|
- value => {
|
|
|
|
- email.value.entered = true;
|
|
|
|
- if (!validation.isLength(value, 3, 254)) {
|
|
|
|
- email.value.message =
|
|
|
|
- "Email must have between 3 and 254 characters.";
|
|
|
|
- email.value.valid = false;
|
|
|
|
- } else if (
|
|
|
|
- value.indexOf("@") !== value.lastIndexOf("@") ||
|
|
|
|
- !validation.regex.emailSimple.test(value)
|
|
|
|
- ) {
|
|
|
|
- email.value.message = "Invalid format.";
|
|
|
|
- email.value.valid = false;
|
|
|
|
- } else {
|
|
|
|
- email.value.message = "Everything looks great!";
|
|
|
|
- email.value.valid = true;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-);
|
|
|
|
-watch(
|
|
|
|
- () => password.value.value,
|
|
|
|
- value => {
|
|
|
|
- password.value.entered = true;
|
|
|
|
- if (!validation.isLength(value, 6, 200)) {
|
|
|
|
- password.value.message =
|
|
|
|
- "Password must have between 6 and 200 characters.";
|
|
|
|
- password.value.valid = false;
|
|
|
|
- } else if (!validation.regex.password.test(value)) {
|
|
|
|
- password.value.message =
|
|
|
|
- "Include at least one lowercase letter, one uppercase letter, one number and one special character.";
|
|
|
|
- password.value.valid = false;
|
|
|
|
|
|
+ },
|
|
|
|
+ ({ status, messages, values }, resolve, reject) => {
|
|
|
|
+ if (status === "success") {
|
|
|
|
+ runJob('data.users.register', {
|
|
|
|
+ query: values
|
|
|
|
+ })
|
|
|
|
+ .then(async data => {
|
|
|
|
+ await login(data.sessionId);
|
|
|
|
+
|
|
|
|
+ window.location.reload();
|
|
|
|
+
|
|
|
|
+ resolve();
|
|
|
|
+ })
|
|
|
|
+ .catch(reject);
|
|
} else {
|
|
} else {
|
|
- password.value.message = "Everything looks great!";
|
|
|
|
- password.value.valid = true;
|
|
|
|
|
|
+ if (status === "unchanged") new Toast(messages.unchanged);
|
|
|
|
+ else if (status === "error")
|
|
|
|
+ Object.values(messages).forEach(message => {
|
|
|
|
+ new Toast({ content: message, timeout: 8000 });
|
|
|
|
+ });
|
|
|
|
+ resolve();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
);
|
|
);
|
|
|
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
- if (registrationDisabled.value) {
|
|
|
|
- new Toast("Registration is disabled.");
|
|
|
|
- closeCurrentModal();
|
|
|
|
- }
|
|
|
|
|
|
+ await onReady(async () => {
|
|
|
|
+ if (registrationDisabled.value) {
|
|
|
|
+ new Toast("Registration is disabled.");
|
|
|
|
+ closeCurrentModal();
|
|
|
|
+
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
});
|
|
});
|
|
</script>
|
|
</script>
|
|
|
|
|
|
@@ -156,20 +130,23 @@ onMounted(async () => {
|
|
<p class="control">
|
|
<p class="control">
|
|
<label class="label">Email</label>
|
|
<label class="label">Email</label>
|
|
<input
|
|
<input
|
|
- v-model="email.value"
|
|
|
|
|
|
+ v-model="inputs.emailAddress.value"
|
|
class="input"
|
|
class="input"
|
|
type="email"
|
|
type="email"
|
|
- autocomplete="username"
|
|
|
|
|
|
+ autocomplete="email"
|
|
placeholder="Email..."
|
|
placeholder="Email..."
|
|
- @keyup.enter="submitModal()"
|
|
|
|
|
|
+ @input="validate('emailAddress')"
|
|
|
|
+ @keyup.enter="save()"
|
|
autofocus
|
|
autofocus
|
|
/>
|
|
/>
|
|
</p>
|
|
</p>
|
|
<transition name="fadein-helpbox">
|
|
<transition name="fadein-helpbox">
|
|
<input-help-box
|
|
<input-help-box
|
|
- :entered="email.entered"
|
|
|
|
- :valid="email.valid"
|
|
|
|
- :message="email.message"
|
|
|
|
|
|
+ :entered="inputs.emailAddress.value?.length > 1"
|
|
|
|
+ :valid="inputs.emailAddress.errors.length === 0"
|
|
|
|
+ :message="
|
|
|
|
+ inputs.emailAddress.errors[0] ?? 'Everything looks great!'
|
|
|
|
+ "
|
|
/>
|
|
/>
|
|
</transition>
|
|
</transition>
|
|
|
|
|
|
@@ -177,19 +154,22 @@ onMounted(async () => {
|
|
<p class="control">
|
|
<p class="control">
|
|
<label class="label">Username</label>
|
|
<label class="label">Username</label>
|
|
<input
|
|
<input
|
|
- v-model="username.value"
|
|
|
|
|
|
+ v-model="inputs.username.value"
|
|
class="input"
|
|
class="input"
|
|
type="text"
|
|
type="text"
|
|
autocomplete="username"
|
|
autocomplete="username"
|
|
placeholder="Username..."
|
|
placeholder="Username..."
|
|
- @keyup.enter="submitModal()"
|
|
|
|
|
|
+ @input="validate('username')"
|
|
|
|
+ @keyup.enter="save()"
|
|
/>
|
|
/>
|
|
</p>
|
|
</p>
|
|
<transition name="fadein-helpbox">
|
|
<transition name="fadein-helpbox">
|
|
<input-help-box
|
|
<input-help-box
|
|
- :entered="username.entered"
|
|
|
|
- :valid="username.valid"
|
|
|
|
- :message="username.message"
|
|
|
|
|
|
+ :entered="inputs.username.value?.length > 1"
|
|
|
|
+ :valid="inputs.username.errors.length === 0"
|
|
|
|
+ :message="
|
|
|
|
+ inputs.username.errors[0] ?? 'Everything looks great!'
|
|
|
|
+ "
|
|
/>
|
|
/>
|
|
</transition>
|
|
</transition>
|
|
|
|
|
|
@@ -200,18 +180,19 @@ onMounted(async () => {
|
|
|
|
|
|
<div id="password-visibility-container">
|
|
<div id="password-visibility-container">
|
|
<input
|
|
<input
|
|
- v-model="password.value"
|
|
|
|
|
|
+ v-model="inputs.password.value"
|
|
class="input"
|
|
class="input"
|
|
type="password"
|
|
type="password"
|
|
autocomplete="new-password"
|
|
autocomplete="new-password"
|
|
ref="passwordElement"
|
|
ref="passwordElement"
|
|
placeholder="Password..."
|
|
placeholder="Password..."
|
|
- @keyup.enter="submitModal()"
|
|
|
|
|
|
+ @input="validate('password')"
|
|
|
|
+ @keyup.enter="save()"
|
|
/>
|
|
/>
|
|
<a @click="togglePasswordVisibility()">
|
|
<a @click="togglePasswordVisibility()">
|
|
<i class="material-icons">
|
|
<i class="material-icons">
|
|
{{
|
|
{{
|
|
- !password.visible
|
|
|
|
|
|
+ !passwordVisible
|
|
? "visibility"
|
|
? "visibility"
|
|
: "visibility_off"
|
|
: "visibility_off"
|
|
}}
|
|
}}
|
|
@@ -221,9 +202,11 @@ onMounted(async () => {
|
|
|
|
|
|
<transition name="fadein-helpbox">
|
|
<transition name="fadein-helpbox">
|
|
<input-help-box
|
|
<input-help-box
|
|
- :valid="password.valid"
|
|
|
|
- :entered="password.entered"
|
|
|
|
- :message="password.message"
|
|
|
|
|
|
+ :entered="inputs.password.value?.length > 1"
|
|
|
|
+ :valid="inputs.password.errors.length === 0"
|
|
|
|
+ :message="
|
|
|
|
+ inputs.password.errors[0] ?? 'Everything looks great!'
|
|
|
|
+ "
|
|
/>
|
|
/>
|
|
</transition>
|
|
</transition>
|
|
|
|
|
|
@@ -243,7 +226,7 @@ onMounted(async () => {
|
|
</template>
|
|
</template>
|
|
<template #footer>
|
|
<template #footer>
|
|
<div id="actions">
|
|
<div id="actions">
|
|
- <button class="button is-primary" @click="submitModal()">
|
|
|
|
|
|
+ <button class="button is-primary" @click="save()">
|
|
Register
|
|
Register
|
|
</button>
|
|
</button>
|
|
</div>
|
|
</div>
|