aboutsummaryrefslogtreecommitdiff
path: root/front-end/src/views/Register
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2023-07-12 01:28:23 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2023-07-12 01:28:23 -0400
commitf64955c69d91e578e580b409ba31ac4b3477da96 (patch)
tree16f01392ddf1abfea13d7d1ede3bfb0459fe8f0d /front-end/src/views/Register
Initial commit
Diffstat (limited to 'front-end/src/views/Register')
-rw-r--r--front-end/src/views/Register/components/CompleteReg.vue116
-rw-r--r--front-end/src/views/Register/index.vue161
2 files changed, 277 insertions, 0 deletions
diff --git a/front-end/src/views/Register/components/CompleteReg.vue b/front-end/src/views/Register/components/CompleteReg.vue
new file mode 100644
index 0000000..b8f8ef0
--- /dev/null
+++ b/front-end/src/views/Register/components/CompleteReg.vue
@@ -0,0 +1,116 @@
+<template>
+ <div id="reg-submit-template">
+ <form
+ id="complete-registration"
+ method="POST"
+ action="#"
+ :disabled="waiting"
+ @submit.prevent="onSubmit"
+ >
+ <fieldset class="input-group">
+ <div class="input-container">
+ <label for="reg-password" class="pl-1">
+ Password
+ </label>
+ <input
+ id="reg-password"
+ v-model="v$.password.$model"
+ type="password"
+ class="input-field primary input"
+ @input="onInput"
+ >
+ </div>
+ <div class="input-container">
+ <label for="reg-repeat-pass" class="pl-1">
+ Repeat Password
+ </label>
+ <input
+ id="reg-repeat-pass"
+ v-model="v$.repeatPass.$model"
+ type="password"
+ class="input-field primary input"
+ @input="onInput"
+ >
+ </div>
+ <div class="flex flex-row justify-end gap-4 pt-4">
+ <button form="complete-registration" type="submit" class="btn primary">
+ Submit
+ </button>
+ <button class="text-red-500 cursor-pointer" @click.prevent="cancel">
+ Cancel
+ </button>
+ </div>
+ </fieldset>
+ </form>
+ </div>
+</template>
+
+<script setup lang="ts">
+
+import { isEqual } from 'lodash'
+import useVuelidate from '@vuelidate/core'
+import { required, minLength, maxLength, helpers } from '@vuelidate/validators'
+import { apiCall, useMessage, useWait, useVuelidateWrapper, WebMessage } from '@vnuge/vnlib.browser'
+import { computed, reactive, toRefs } from 'vue'
+
+const emit = defineEmits(['complete', 'cancel'])
+
+const props = defineProps<{
+ token: string
+ regPath: string
+}>()
+
+const { token, regPath } = toRefs(props)
+const { onInput } = useMessage()
+const { waiting } = useWait()
+
+const vState = reactive({ password: '', repeatPass: ''})
+
+const rules = computed(() => {
+ return {
+ password: {
+ required: helpers.withMessage('Password cannot be empty', required),
+ minLength: helpers.withMessage('Password must be at least 8 characters', minLength(8)),
+ maxLength: helpers.withMessage(' must have less than 128 characters', maxLength(128))
+ },
+ repeatPass: {
+ sameAs: helpers.withMessage('Your passwords do not match', (value : string) => isEqual(value, v$.value.password.$model)),
+ required: helpers.withMessage('Repeat password cannot be empty', required),
+ minLength: helpers.withMessage('Repeat password must be at least 8 characters', minLength(8)),
+ maxLength: helpers.withMessage('Repeat password must have less than 128 characters', maxLength(128))
+ },
+ }
+})
+
+const v$ = useVuelidate(rules, vState, { $lazy: true })
+
+const { validate } = useVuelidateWrapper(v$)
+
+const onSubmit = async function () {
+
+ if (!await validate()) {
+ return
+ }
+
+ await apiCall(async ({ axios }) => {
+ // finalize by passing the token and the new password
+ const { data } = await axios.post<WebMessage>(regPath.value, {
+ token: token.value,
+ password: v$.value.password.$model
+ })
+
+ //Throw if not successful
+ data.getResultOrThrow()
+ v$.value.$reset()
+ emit('complete')
+
+ })
+}
+
+const cancel = () => emit('cancel')
+
+</script>
+
+<style>
+
+</style>
diff --git a/front-end/src/views/Register/index.vue b/front-end/src/views/Register/index.vue
new file mode 100644
index 0000000..92a5992
--- /dev/null
+++ b/front-end/src/views/Register/index.vue
@@ -0,0 +1,161 @@
+<template>
+ <div id="reg-template" class="app-component-entry">
+ <div class="container flex flex-col m-auto my-2 duration-150 ease-linear lg:mt-16">
+ <div class="text-center">
+ <h2>Sign Up</h2>
+ </div>
+ <div class="mt-4 content-container">
+ <form v-if="formState === 0" @submit.prevent="OnSubmit" :disabled="waiting">
+ <fieldset class="input-group">
+ <div class="input-container">
+ <label for="reg-email" class="pl-1 text-sm">Email Address</label>
+ <input
+ id="reg-email"
+ v-model="v$.emailAddress.$model"
+ type="email"
+ placeholder="user@example.com"
+ required
+ class="input-field primary"
+ @input="onInput"
+ >
+ </div>
+ </fieldset>
+ <fieldset class="flex flex-row justify-between mt-6">
+ <div>
+ <label class="checkbox primary">
+ <input v-model="acceptedTerms" type="checkbox">
+ <span class="check" />
+ <span class="mx-2 text-sm">
+ I agree to the
+ <a class="link" href="#">Terms of Service</a>
+ </span>
+ </label>
+ </div>
+ <div>
+ <button type="submit" :disabled="!acceptedTerms || waiting" class="btn primary">
+ Submit
+ </button>
+ </div>
+ </fieldset>
+ </form>
+ <complete-reg
+ v-else-if="formState === 1"
+ :reg-path="regPath"
+ :token="token"
+ @cancel="formState = 0"
+ @complete="formState = 2"
+ />
+ <div v-else>
+ <div class="text-center">
+ <h3>Success</h3>
+ <fa-icon
+ :icon="['fa','check-circle']"
+ class="text-primary-500 dark:text-primary-600"
+ size="3x"
+ />
+ <p class="mt-4">
+ You may log in now.
+ </p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script setup lang="ts">
+import { isNil } from 'lodash';
+import useVuelidate from '@vuelidate/core'
+import { required, maxLength, email, helpers } from '@vuelidate/validators'
+import { ref, reactive, watch } from 'vue'
+import { useSession, apiCall, useMessage, useWait, useTitle, useVuelidateWrapper } from '@vnuge/vnlib.browser'
+import CompleteReg from './components/CompleteReg.vue'
+import { useRouter } from 'vue-router';
+import { useRouteQuery } from '@vueuse/router';
+
+const regPath = "/account/registration"
+
+useTitle('Registration')
+
+const { setMessage, onInput } = useMessage()
+const { waiting } = useWait()
+const { browserId } = useSession()
+const router = useRouter();
+
+//Token is the t query argument
+const token = useRouteQuery('t', null)
+
+const acceptedTerms = ref(false)
+const formState = ref(isNil(token.value) ? 0 : 1)
+
+const vState = reactive({
+ emailAddress: ''
+})
+
+const rules = {
+ emailAddress: {
+ required: helpers.withMessage('Email cannot be empty', required),
+ email: helpers.withMessage('Your email address is not valid', email),
+ maxLength: helpers.withMessage('Email address must be less than 50 characters', maxLength(50))
+ }
+}
+const v$ = useVuelidate(rules, vState, { $lazy: true })
+const { validate } = useVuelidateWrapper(v$)
+
+const OnSubmit = async function () {
+ if (!acceptedTerms.value) {
+ setMessage('You must accept the terms of service to continue.')
+ return
+ }
+ if (!await validate()) {
+ return
+ }
+ await apiCall(async ({ axios, toaster }) => {
+ const response = await axios.put(regPath, {
+ username: v$.value.emailAddress.$model,
+ clientid: browserId.value,
+ localtime: new Date().toISOString()
+ })
+ if (response.data.success) {
+ toaster.form.success({
+ id: 'logout-success',
+ title: 'Success',
+ text: response.data.result,
+ duration: 5000
+ }
+ )
+
+ acceptedTerms.value = false
+ v$.value.emailAddress.$model = '';
+ //Clear form
+ v$.value.$reset()
+ } else {
+ setMessage(response.data.result)
+ }
+ })
+}
+
+watch(formState, () => {
+ //Clear token if formState is not 1
+ v$.value.$reset()
+ acceptedTerms.value = false
+ v$.value.emailAddress.$model = '';
+ router.push({ query: {} })
+})
+
+</script>
+
+<style>
+#reg-template .content-container{
+ @apply mx-auto p-4 bg-white dark:bg-dark-700 border border-gray-200 dark:border-dark-500;
+ @apply sm:p-6 sm:rounded-md sm:shadow-sm w-full max-w-sm;
+}
+
+#reg-template input.input-field {
+ @apply block w-full p-2 border-b-2 my-2;
+}
+
+.content-container fieldset.input-group {
+ @apply mx-auto;
+}
+</style>