mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-06-28 01:06:58 +00:00
Reconstruct notification center for client side
This commit is contained in:
parent
4c8ba6b0a8
commit
173cc57490
21
src/static/client/package-lock.json
generated
21
src/static/client/package-lock.json
generated
@ -10,7 +10,9 @@
|
||||
"dependencies": {
|
||||
"bootstrap": "^5.3.6",
|
||||
"bootstrap-icons": "^1.13.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"pinia": "^3.0.2",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
@ -1708,6 +1710,12 @@
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz",
|
||||
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.1.tgz",
|
||||
@ -2645,6 +2653,19 @@
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz",
|
||||
"integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/esm/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.3.5",
|
||||
"resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.5.tgz",
|
||||
|
@ -11,7 +11,9 @@
|
||||
"dependencies": {
|
||||
"bootstrap": "^5.3.6",
|
||||
"bootstrap-icons": "^1.13.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"pinia": "^3.0.2",
|
||||
"uuid": "^11.1.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
|
@ -1,13 +1,30 @@
|
||||
<script setup>
|
||||
import './assets/main.css'
|
||||
import NotificationList from "@/components/notification/notificationList.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div data-bs-theme="dark" class="text-body bg-body">
|
||||
<RouterView></RouterView>
|
||||
<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>
|
||||
<NotificationList></NotificationList>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.app-enter-active,
|
||||
.app-leave-active {
|
||||
transition: all 0.5s cubic-bezier(0.82, 0.58, 0.17, 1);
|
||||
}
|
||||
.app-enter-from,
|
||||
.app-leave-to{
|
||||
opacity: 0;
|
||||
filter: blur(5px);
|
||||
transform: scale(0.95);
|
||||
}
|
||||
</style>
|
||||
|
@ -52,4 +52,8 @@
|
||||
.btn-brand:hover{
|
||||
--brandColor1: rgb(0, 142, 216);
|
||||
--brandColor2: rgba(249, 70, 71)
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
<script setup>
|
||||
import {onMounted} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
notificationData: {
|
||||
id: "",
|
||||
show: true,
|
||||
content: "",
|
||||
time: "",
|
||||
status: ""
|
||||
}
|
||||
})
|
||||
|
||||
let timeout = undefined;
|
||||
const show = () => {
|
||||
props.notificationData.show = true;
|
||||
timeout = setTimeout(() => {
|
||||
dismiss()
|
||||
}, 50000)
|
||||
}
|
||||
const clearTime = () => clearTimeout(timeout)
|
||||
const dismiss = () => props.notificationData.show = false;
|
||||
|
||||
onMounted(() => {
|
||||
show()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
@mouseenter="clearTime()"
|
||||
@mouseleave="notificationData.show ? show():undefined"
|
||||
:class="{
|
||||
'text-bg-success': notificationData.status === 'success',
|
||||
'text-bg-warning': notificationData.status === 'warning',
|
||||
'text-bg-danger': notificationData.status === 'danger'
|
||||
}"
|
||||
class="card shadow rounded-3 position-relative message ms-auto notification">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<small>
|
||||
{{ notificationData.time.format("hh:mm A") }}
|
||||
</small>
|
||||
<small class="ms-auto">
|
||||
<a role="button" @click="dismiss()">
|
||||
Dismiss<i class="bi bi-x-lg ms-2"></i>
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<span class="fw-medium">{{ notificationData.content }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.notification{
|
||||
width: 100%;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 576px) {
|
||||
.notification{
|
||||
width: 400px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,57 @@
|
||||
<script setup>
|
||||
import {clientStore} from "@/stores/clientStore.js";
|
||||
import Notification from "@/components/notification/notification.vue";
|
||||
import {computed, onMounted} from "vue";
|
||||
const store = clientStore()
|
||||
const notifications = computed(() => {
|
||||
return store.notifications.filter(x => x.show).slice().reverse()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
store.newNotification("Hi!!lskadjlkasjdlkasjkldjaslkdjklasjdlkjaslkdjlkasjdlkjsalkdjlkasjdlk", "warning")
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="messageCentre text-body position-absolute d-flex">
|
||||
<TransitionGroup name="message" tag="div"
|
||||
class="position-relative flex-sm-grow-0 flex-grow-1 d-flex align-items-end ms-sm-auto flex-column gap-2">
|
||||
<Notification v-for="n in notifications"
|
||||
:notificationData="n" :key="n.id"></Notification>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.message-move, /* apply transition to moving elements */
|
||||
.message-enter-active,
|
||||
.message-leave-active {
|
||||
transition: all 0.5s cubic-bezier(0.82, 0.58, 0.17, 1);
|
||||
}
|
||||
|
||||
.message-enter-from,
|
||||
.message-leave-to {
|
||||
filter: blur(2px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.message-enter-from{
|
||||
transform: translateY(-30px);
|
||||
}
|
||||
|
||||
.message-leave-to{
|
||||
transform: translateY(30px);
|
||||
}
|
||||
|
||||
.messageCentre{
|
||||
z-index: 9999;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.messageCentre{
|
||||
width: calc(100% - 2rem);
|
||||
}
|
||||
}
|
||||
</style>
|
22
src/static/client/src/stores/clientStore.js
Normal file
22
src/static/client/src/stores/clientStore.js
Normal file
@ -0,0 +1,22 @@
|
||||
import {defineStore} from "pinia";
|
||||
import {ref} from "vue";
|
||||
import {v4} from "uuid"
|
||||
import dayjs from "dayjs";
|
||||
|
||||
|
||||
export const clientStore = defineStore('clientStore', () => {
|
||||
const notifications = ref([])
|
||||
function newNotification(content, status) {
|
||||
notifications.value.push({
|
||||
id: v4().toString(),
|
||||
status: status,
|
||||
content: content,
|
||||
time: dayjs(),
|
||||
show: true
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
notifications, newNotification
|
||||
}
|
||||
})
|
@ -2,38 +2,47 @@
|
||||
import {reactive} from "vue";
|
||||
|
||||
const formData = reactive({
|
||||
username: "",
|
||||
email: "",
|
||||
password: ""
|
||||
})
|
||||
});
|
||||
|
||||
const submit = (e) => {
|
||||
e.preventDefault();
|
||||
for (let key in formData){
|
||||
if (formData[key].length === 0){
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="d-flex vh-100 vw-100 p-4 overflow-y-scroll">
|
||||
<div class="m-auto" style="width: 600px">
|
||||
<h4 class="mb-0">Welcome to</h4>
|
||||
<h1 class="fw-bold display-4">WGDashboard Client</h1>
|
||||
<div class="mt-4 d-flex flex-column gap-3">
|
||||
<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.username"
|
||||
name="username"
|
||||
autocomplete="username"
|
||||
v-model="formData.email"
|
||||
name="email"
|
||||
autocomplete="email"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="username" placeholder="Username">
|
||||
class="form-control rounded-3" id="email" placeholder="email">
|
||||
<label for="floatingInput" class="d-flex">
|
||||
<i class="bi bi-person-circle me-2"></i>
|
||||
Username
|
||||
Email
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-floating">
|
||||
<input type="password"
|
||||
required
|
||||
v-model="formData.password"
|
||||
name="username"
|
||||
autocomplete="username"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="username" placeholder="Username">
|
||||
name="password"
|
||||
autocomplete="password"
|
||||
class="form-control rounded-3" id="password" placeholder="Password">
|
||||
<label for="floatingInput" class="d-flex">
|
||||
<i class="bi bi-key me-2"></i>
|
||||
Password
|
||||
@ -42,8 +51,13 @@ const formData = reactive({
|
||||
<a href="#" class="text-body text-decoration-none">
|
||||
Forgot Password?
|
||||
</a>
|
||||
<button class="btn btn-primary rounded-3 btn-lg btn-brand w-100 fw-bold">Sign In</button>
|
||||
</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>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<hr class="my-4">
|
||||
|
@ -2,16 +2,77 @@
|
||||
import {reactive} from "vue";
|
||||
|
||||
const formData = reactive({
|
||||
username: "",
|
||||
email: "",
|
||||
password: ""
|
||||
password: "",
|
||||
confirmPassword: ""
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="d-flex vh-100 vw-100 p-4 overflow-y-scroll">
|
||||
<div class="m-auto" style="width: 600px">
|
||||
<h1 class="fw-bold display-4">Sign Up</h1>
|
||||
<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="floatingInput" 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="password"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="password" placeholder="password">
|
||||
<label for="floatingInput" 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="confirm_password"
|
||||
autofocus
|
||||
class="form-control rounded-3" id="confirm_password" placeholder="confirm_password">
|
||||
<label for="floatingInput" 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>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
Loading…
x
Reference in New Issue
Block a user