mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-10-04 16:26:18 +00:00
Finished SignUp and SignIn frontend and backend
This commit is contained in:
@@ -5,13 +5,19 @@ import NotificationList from "@/components/notification/notificationList.vue";
|
||||
|
||||
<template>
|
||||
<div data-bs-theme="dark" class="text-body bg-body w-100 h-100">
|
||||
<Suspense>
|
||||
<RouterView v-slot="{ Component }">
|
||||
<Transition name="app" mode="out-in" type="transition" appear>
|
||||
<Component :is="Component"></Component>
|
||||
</Transition>
|
||||
</RouterView>
|
||||
</Suspense>
|
||||
<div class="d-flex vh-100 vw-100 p-4 overflow-y-scroll">
|
||||
<div class="m-auto p-5 bg-body-tertiary rounded-4 shadow-lg border" style="width: 700px">
|
||||
<Suspense>
|
||||
<RouterView v-slot="{ Component }">
|
||||
<Transition name="app" mode="out-in" type="transition" appear>
|
||||
<Component :is="Component"></Component>
|
||||
</Transition>
|
||||
</RouterView>
|
||||
</Suspense>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<NotificationList></NotificationList>
|
||||
</div>
|
||||
</template>
|
||||
@@ -19,12 +25,12 @@ import NotificationList from "@/components/notification/notificationList.vue";
|
||||
<style scoped>
|
||||
.app-enter-active,
|
||||
.app-leave-active {
|
||||
transition: all 0.5s cubic-bezier(0.82, 0.58, 0.17, 1);
|
||||
transition: all 0.3s cubic-bezier(0.82, 0.58, 0.17, 1);
|
||||
}
|
||||
.app-enter-from,
|
||||
.app-leave-to{
|
||||
opacity: 0;
|
||||
filter: blur(5px);
|
||||
transform: scale(0.95);
|
||||
transform: scale(0.97);
|
||||
}
|
||||
</style>
|
||||
|
@@ -56,4 +56,20 @@
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slide-right-enter-active,
|
||||
.slide-right-leave-active {
|
||||
transition: all 0.3s cubic-bezier(0.82, 0.58, 0.17, 1);
|
||||
}
|
||||
.slide-right-enter-from,
|
||||
.slide-right-leave-to{
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.slide-right-enter-from{
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
.slide-right-leave-to{
|
||||
transform: translateX(20px);
|
||||
}
|
@@ -16,7 +16,7 @@ const show = () => {
|
||||
props.notificationData.show = true;
|
||||
timeout = setTimeout(() => {
|
||||
dismiss()
|
||||
}, 50000)
|
||||
}, 5000)
|
||||
}
|
||||
const clearTime = () => clearTimeout(timeout)
|
||||
const dismiss = () => props.notificationData.show = false;
|
||||
@@ -57,7 +57,7 @@ onMounted(() => {
|
||||
<style scoped>
|
||||
.notification{
|
||||
width: 100%;
|
||||
word-break: break-all;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 576px) {
|
||||
|
@@ -6,10 +6,6 @@ const store = clientStore()
|
||||
const notifications = computed(() => {
|
||||
return store.notifications.filter(x => x.show).slice().reverse()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
store.newNotification("Hi!!lskadjlkasjdlkasjkldjaslkdjklasjdlkjaslkdjlkasjdlkjsalkdjlkasjdlk", "warning")
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import {createWebHashHistory, createRouter} from "vue-router";
|
||||
import Index from "@/views/index.vue";
|
||||
import Signin from "@/views/signin.vue";
|
||||
import Signup from "@/views/signup.vue";
|
||||
import SignIn from "@/views/signin.vue";
|
||||
import SignUp from "@/views/signup.vue";
|
||||
import Totp from "@/views/totp.vue";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
@@ -13,13 +14,18 @@ const router = createRouter({
|
||||
},
|
||||
{
|
||||
path: '/signin',
|
||||
component: Signin,
|
||||
component: SignIn,
|
||||
name: "Sign In"
|
||||
},
|
||||
{
|
||||
path: '/signup',
|
||||
component: Signup,
|
||||
component: SignUp,
|
||||
name: "Sign Up"
|
||||
},
|
||||
{
|
||||
path: '/totp',
|
||||
component: Totp,
|
||||
name: "Verify TOTP"
|
||||
}
|
||||
]
|
||||
})
|
||||
|
5
src/static/client/src/utilities/request.js
Normal file
5
src/static/client/src/utilities/request.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export const requestURl = (url) => {
|
||||
return import.meta.env.MODE === 'development' ? url
|
||||
: `${window.location.protocol}//${(window.location.host + window.location.pathname + url).replace(/\/\//g, '/')}`
|
||||
}
|
||||
|
@@ -1,74 +1,105 @@
|
||||
<script setup>
|
||||
import {reactive} from "vue";
|
||||
|
||||
import {computed, reactive, ref} from "vue";
|
||||
import {clientStore} from "@/stores/clientStore.js";
|
||||
import axios from "axios";
|
||||
import {requestURl} from "@/utilities/request.js";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
const loading = ref(false)
|
||||
const formData = reactive({
|
||||
email: "",
|
||||
password: ""
|
||||
Email: "",
|
||||
Password: ""
|
||||
});
|
||||
|
||||
const submit = (e) => {
|
||||
const store = clientStore()
|
||||
const signIn = async (e) => {
|
||||
e.preventDefault();
|
||||
for (let key in formData){
|
||||
if (formData[key].length === 0){
|
||||
|
||||
break
|
||||
}
|
||||
if (!formFilled){
|
||||
store.newNotification("Please fill in all fields", "warning")
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
await axios.post(requestURl("/client/api/signin"), formData).then(res => {
|
||||
let data = res.data;
|
||||
if (!data.status){
|
||||
store.newNotification(data.message, "danger")
|
||||
loading.value = false;
|
||||
}else{
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const formFilled = computed(() => {
|
||||
return Object.values(formData).find(x => !x) === undefined
|
||||
})
|
||||
|
||||
// const router = useRouter()
|
||||
const route = useRoute()
|
||||
if (route.query.Email){
|
||||
formData.Email = route.query.Email
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="d-flex vh-100 vw-100 p-4 overflow-y-scroll">
|
||||
<div class="m-auto" style="width: 600px">
|
||||
<h1>Sign In</h1>
|
||||
<p>to your WGDashboard Client account</p>
|
||||
<form class="mt-4 d-flex flex-column gap-3" @submit="e => submit(e)">
|
||||
<div class="form-floating">
|
||||
<input type="text"
|
||||
required
|
||||
v-model="formData.email"
|
||||
name="email"
|
||||
autocomplete="email"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="email" placeholder="email">
|
||||
<label for="email" class="d-flex">
|
||||
<i class="bi bi-person-circle me-2"></i>
|
||||
Email
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input type="password"
|
||||
required
|
||||
v-model="formData.password"
|
||||
name="password"
|
||||
autocomplete="current-password"
|
||||
class="form-control rounded-3" id="password" placeholder="Password">
|
||||
<label for="password" class="d-flex">
|
||||
<i class="bi bi-key me-2"></i>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
<a href="#" class="text-body text-decoration-none">
|
||||
<div>
|
||||
<h1>Sign In</h1>
|
||||
<p>to your WGDashboard Client account</p>
|
||||
<form class="mt-4 d-flex flex-column gap-3" @submit="e => signIn(e)">
|
||||
<div class="form-floating">
|
||||
<input type="text"
|
||||
required
|
||||
:disabled="loading"
|
||||
v-model="formData.Email"
|
||||
name="email"
|
||||
autocomplete="email"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="email" placeholder="email">
|
||||
<label for="email" class="d-flex">
|
||||
<i class="bi bi-person-circle me-2"></i>
|
||||
Email
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input type="password"
|
||||
required
|
||||
:disabled="loading"
|
||||
v-model="formData.Password"
|
||||
name="password"
|
||||
autocomplete="current-password"
|
||||
class="form-control rounded-3" id="password" placeholder="Password">
|
||||
<label for="password" class="d-flex">
|
||||
<i class="bi bi-key me-2"></i>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#" class="text-body text-decoration-none ms-0">
|
||||
Forgot Password?
|
||||
</a>
|
||||
<div class="d-flex">
|
||||
<button class="ms-auto btn btn-primary rounded-3 btn-brand px-3 py-2">
|
||||
Continue
|
||||
<i class="ms-2 bi bi-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<hr class="my-4">
|
||||
<div class="d-flex align-items-center">
|
||||
</div>
|
||||
<button
|
||||
:disabled="!formFilled"
|
||||
class="btn btn-primary rounded-3 btn-brand px-3 py-2">
|
||||
<Transition name="slide-right" mode="out-in">
|
||||
<span v-if="!loading" class="d-block">
|
||||
Continue <i class="ms-2 bi bi-arrow-right"></i>
|
||||
</span>
|
||||
<span v-else class="d-block">
|
||||
<i class="spinner-border spinner-border-sm"></i>
|
||||
</span>
|
||||
</Transition>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<hr class="my-4">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="text-muted">
|
||||
Don't have an account yet?
|
||||
</span>
|
||||
<RouterLink to="/signup" class="text-body text-decoration-none ms-auto fw-bold">
|
||||
Sign Up
|
||||
</RouterLink>
|
||||
</div>
|
||||
<RouterLink to="/signup" class="text-body text-decoration-none ms-auto fw-bold">
|
||||
Sign Up
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -1,77 +1,146 @@
|
||||
<script setup>
|
||||
import {reactive} from "vue";
|
||||
import {computed, onMounted, reactive, ref} from "vue";
|
||||
import axios from "axios";
|
||||
import {clientStore} from "@/stores/clientStore.js";
|
||||
import {requestURl} from "@/utilities/request.js";
|
||||
import {useRouter} from "vue-router";
|
||||
const store = clientStore()
|
||||
|
||||
const formData = reactive({
|
||||
Email: "",
|
||||
Password: "",
|
||||
ConfirmPassword: ""
|
||||
})
|
||||
const loading = ref(false)
|
||||
const router = useRouter()
|
||||
|
||||
const signUp = async (e) => {
|
||||
e.preventDefault()
|
||||
if (!formFilled){
|
||||
store.newNotification("Please fill in all fields", "warning")
|
||||
return;
|
||||
}
|
||||
if (validatePassword){
|
||||
loading.value = true
|
||||
await axios.post(requestURl('/client/api/signup'), formData).then((res) => {
|
||||
let data = res.data
|
||||
if (!data.status){
|
||||
store.newNotification(data.message, "danger")
|
||||
loading.value = false
|
||||
}else{
|
||||
store.newNotification("Sign up successfully!", "success")
|
||||
router.push({
|
||||
path: '/signin',
|
||||
query: {
|
||||
Email: formData.Email
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const validatePassword = computed(() => {
|
||||
if (formData.Password && formData.ConfirmPassword){
|
||||
return formData.Password === formData.ConfirmPassword;
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
const formFilled = computed(() => {
|
||||
return Object.values(formData).find(x => !x) === undefined
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
document.querySelectorAll("input[type=password]").forEach(e => e.addEventListener('blur', () => {
|
||||
if (formData.Password && formData.ConfirmPassword){
|
||||
document.querySelectorAll("input[type=password]").forEach(x => {
|
||||
if (!validatePassword.value){
|
||||
x.classList.add("is-invalid")
|
||||
}else {
|
||||
x.classList.remove("is-invalid")
|
||||
}
|
||||
})
|
||||
}
|
||||
}))
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="d-flex vh-100 vw-100 p-4 overflow-y-scroll">
|
||||
<div class="m-auto" style="width: 600px">
|
||||
<h1>Sign Up</h1>
|
||||
<p>to use WGDashboard Client</p>
|
||||
<div class="mt-4 d-flex flex-column gap-3">
|
||||
<div class="form-floating">
|
||||
<input type="text"
|
||||
required
|
||||
v-model="formData.email"
|
||||
name="email"
|
||||
autocomplete="email"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="email" placeholder="email">
|
||||
<label for="email" class="d-flex">
|
||||
<i class="bi bi-person-circle me-2"></i>
|
||||
Email
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input type="password"
|
||||
required
|
||||
v-model="formData.password"
|
||||
name="password"
|
||||
autocomplete="new-password"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="password" placeholder="password">
|
||||
<label for="password" class="d-flex">
|
||||
<i class="bi bi-key me-2"></i>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input type="password"
|
||||
required
|
||||
v-model="formData.password"
|
||||
name="confirm_password"
|
||||
autocomplete="new-password"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="confirm_password" placeholder="confirm_password">
|
||||
<label for="confirm_password" class="d-flex">
|
||||
<i class="bi bi-key me-2"></i>
|
||||
Confirm Password
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="d-flex">
|
||||
<button class="ms-auto btn btn-primary rounded-3 btn-brand px-3 py-2">
|
||||
Continue
|
||||
<i class="ms-2 bi bi-arrow-right"></i>
|
||||
</button>
|
||||
<div>
|
||||
<h1>Sign Up</h1>
|
||||
<p>to use WGDashboard Client</p>
|
||||
<form class="mt-4 d-flex flex-column gap-3" @submit="e => signUp(e)">
|
||||
<div class="form-floating">
|
||||
<input type="text"
|
||||
:disabled="loading"
|
||||
required
|
||||
v-model="formData.Email"
|
||||
name="email"
|
||||
autocomplete="email"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="email" placeholder="email">
|
||||
<label for="email" class="d-flex">
|
||||
<i class="bi bi-person-circle me-2"></i>
|
||||
Email
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input type="password"
|
||||
required
|
||||
:disabled="loading"
|
||||
v-model="formData.Password"
|
||||
name="password"
|
||||
autocomplete="new-password"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="password" placeholder="password">
|
||||
<label for="password" class="d-flex">
|
||||
<i class="bi bi-key me-2"></i>
|
||||
Password
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input type="password"
|
||||
required
|
||||
:disabled="loading"
|
||||
v-model="formData.ConfirmPassword"
|
||||
name="confirm_password"
|
||||
autocomplete="new-password"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="confirm_password" placeholder="confirm_password">
|
||||
<label for="confirm_password" class="d-flex">
|
||||
<i class="bi bi-key me-2"></i>
|
||||
Confirm Password
|
||||
</label>
|
||||
<div id="validationServer03Feedback" class="invalid-feedback">
|
||||
Passwords does not match
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<hr class="my-4">
|
||||
<div class="d-flex align-items-center">
|
||||
|
||||
|
||||
<button
|
||||
:disabled="!formFilled || !validatePassword || loading"
|
||||
class=" btn btn-primary rounded-3 btn-brand px-3 py-2">
|
||||
<Transition name="slide-right" mode="out-in">
|
||||
<span v-if="!loading" class="d-block">
|
||||
Continue <i class="ms-2 bi bi-arrow-right"></i>
|
||||
</span>
|
||||
<span v-else class="d-block">
|
||||
<i class="spinner-border spinner-border-sm"></i>
|
||||
</span>
|
||||
</Transition>
|
||||
</button>
|
||||
</form>
|
||||
<div>
|
||||
<hr class="my-4">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="text-muted">
|
||||
Already have an account?
|
||||
</span>
|
||||
<RouterLink to="/signin" class="text-body text-decoration-none ms-auto fw-bold">
|
||||
Sign In
|
||||
</RouterLink>
|
||||
</div>
|
||||
<RouterLink to="/signin" class="text-body text-decoration-none ms-auto fw-bold">
|
||||
Sign In
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
11
src/static/client/src/views/totp.vue
Normal file
11
src/static/client/src/views/totp.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
Reference in New Issue
Block a user