mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-06-28 01:06:58 +00:00
Adjusted styles
This commit is contained in:
parent
c6af129960
commit
c6a44bfe09
@ -63,6 +63,7 @@ class DashboardClientsPeerAssignment:
|
|||||||
conn.execute(
|
conn.execute(
|
||||||
self.dashboardClientsPeerAssignmentTable.insert().values(data)
|
self.dashboardClientsPeerAssignmentTable.insert().values(data)
|
||||||
)
|
)
|
||||||
|
self.__getAssignments()
|
||||||
return True, data
|
return True, data
|
||||||
return False, None
|
return False, None
|
||||||
|
|
||||||
@ -73,6 +74,8 @@ class DashboardClientsPeerAssignment:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def GetAssignedPeers(self, ClientID):
|
def GetAssignedPeers(self, ClientID):
|
||||||
|
self.__getAssignments()
|
||||||
|
|
||||||
peers = []
|
peers = []
|
||||||
assigned = filter(lambda e:
|
assigned = filter(lambda e:
|
||||||
e['ClientID'] == ClientID, self.assignments)
|
e['ClientID'] == ClientID, self.assignments)
|
||||||
|
@ -135,7 +135,7 @@ class DashboardConfig:
|
|||||||
fKeys.append(DashboardAPIKey(k[0], k[1].strftime("%Y-%m-%d %H:%M:%S"), (k[2].strftime("%Y-%m-%d %H:%M:%S") if k[2] else None)))
|
fKeys.append(DashboardAPIKey(k[0], k[1].strftime("%Y-%m-%d %H:%M:%S"), (k[2].strftime("%Y-%m-%d %H:%M:%S") if k[2] else None)))
|
||||||
return fKeys
|
return fKeys
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("")
|
print(e)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def createAPIKeys(self, ExpiredAt = None):
|
def createAPIKeys(self, ExpiredAt = None):
|
||||||
|
41
src/static/client/dist/assets/index-3dBm_9kI.js
vendored
41
src/static/client/dist/assets/index-3dBm_9kI.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
41
src/static/client/dist/assets/index-mUZgUftB.js
vendored
Normal file
41
src/static/client/dist/assets/index-mUZgUftB.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
src/static/client/dist/client.html
vendored
4
src/static/client/dist/client.html
vendored
@ -5,8 +5,8 @@
|
|||||||
<link rel="icon" href="/static/client/dist/img/Logo-2-128x128.png">
|
<link rel="icon" href="/static/client/dist/img/Logo-2-128x128.png">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Vite App</title>
|
<title>Vite App</title>
|
||||||
<script type="module" crossorigin src="/static/client/dist/assets/index-3dBm_9kI.js"></script>
|
<script type="module" crossorigin src="/static/client/dist/assets/index-mUZgUftB.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/static/client/dist/assets/index-BGU_XeJ4.css">
|
<link rel="stylesheet" crossorigin href="/static/client/dist/assets/index-C1_D12BO.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
@ -5,8 +5,9 @@ import NotificationList from "@/components/Notification/notificationList.vue";
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div data-bs-theme="dark" class="text-body bg-body w-100 h-100">
|
<div data-bs-theme="dark" class="text-body bg-body w-100 h-100">
|
||||||
<div class="d-flex vh-100 vw-100 p-4 overflow-y-scroll">
|
<div class="d-flex vh-100 vw-100 p-sm-4 overflow-y-scroll">
|
||||||
<div class="mx-auto my-sm-auto bg-body-tertiary rounded-4 shadow-lg border position-relative"
|
<div class="mx-auto my-sm-auto bg-body-tertiary rounded-4 shadow-sm position-relative"
|
||||||
|
id="listContainer"
|
||||||
style="width: 700px">
|
style="width: 700px">
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<RouterView v-slot="{ Component }">
|
<RouterView v-slot="{ Component }">
|
||||||
@ -22,5 +23,10 @@ import NotificationList from "@/components/Notification/notificationList.vue";
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@media screen and (max-width: 576px) {
|
||||||
|
#listContainer{
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
BIN
src/static/client/src/assets/images/bg1.jpg
Normal file
BIN
src/static/client/src/assets/images/bg1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 MiB |
@ -91,4 +91,32 @@
|
|||||||
|
|
||||||
.app-leave-to{
|
.app-leave-to{
|
||||||
transform: scale(0.97);
|
transform: scale(0.97);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-body{
|
||||||
|
border-color: #000000 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
background-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-body:hover{
|
||||||
|
border-color: #373737 !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
background-color: #373737 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme=dark] .btn-body{
|
||||||
|
border-color: #ffffff !important;
|
||||||
|
color: #000000 !important;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-bs-theme=dark] .btn-body:hover{
|
||||||
|
border-color: #e8e8e8 !important;
|
||||||
|
color: #000000 !important;
|
||||||
|
background-color: #e8e8e8 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control{
|
||||||
|
border-width: 0;
|
||||||
}
|
}
|
@ -10,68 +10,46 @@ const showQRCode = ref(false)
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="card shadow rounded-3">
|
<div class="card rounded-3 border-0">
|
||||||
<div class="card-header d-flex align-items-center">
|
<div class="card-body p-3">
|
||||||
<div>
|
<div class="row g-2">
|
||||||
<small v-if="props.config.status === 'stopped'">
|
<div class="d-flex gap-2 col-12">
|
||||||
<i class="bi bi-lightbulb text-secondary me-2"></i>
|
<small class="text-muted">
|
||||||
</small>
|
<i class="bi bi-tag me-1"></i> Name
|
||||||
<small v-else>
|
</small>
|
||||||
<i class="bi bi-lightbulb-fill text-success me-2"></i>
|
<small class="fw-bold flex-grow-1 text-end">
|
||||||
</small>
|
{{ props.config.name }}
|
||||||
<small style="word-break: break-all">
|
</small>
|
||||||
{{ props.config.name }}
|
</div>
|
||||||
</small>
|
<div class="d-flex gap-2 col-12">
|
||||||
</div>
|
<small class="text-muted">
|
||||||
<div class="ms-auto d-flex gap-2 button-group">
|
<i class="bi bi-bar-chart-fill me-1"></i> Data Usage
|
||||||
<a role="button" class="px-2 py-1 text-white rounded-3" aria-label="Download Configuration">
|
</small>
|
||||||
<i class="bi bi-download"></i>
|
<small class="fw-bold flex-grow-1 text-end">
|
||||||
</a>
|
3.42 / 4.00 GB
|
||||||
<a role="button"
|
</small>
|
||||||
@click="showQRCode = true"
|
</div>
|
||||||
class="px-2 py-1 text-white rounded-3" aria-label="Display QR Code">
|
<div class="d-flex gap-2 col-12">
|
||||||
<i class="bi bi-qr-code"></i>
|
<small class="text-muted">
|
||||||
</a>
|
<i class="bi bi-calendar me-1"></i> Valid Until
|
||||||
<Transition name="app">
|
</small>
|
||||||
<ConfigurationQRCode
|
<small class="fw-bold flex-grow-1 text-end">
|
||||||
v-if="showQRCode"
|
2025-08-31 00:00:00
|
||||||
@back="showQRCode = false"
|
</small>
|
||||||
:qrcode-data="config.peer_configuration_data.file"></ConfigurationQRCode>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div>
|
|
||||||
<small class="d-block text-muted" style="font-size: 0.8rem">Public Key</small>
|
|
||||||
<small>
|
|
||||||
<samp style="word-break: break-word">{{ props.config.id }}</samp>
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div>
|
|
||||||
<h6 class="text-center">Data Usage</h6>
|
|
||||||
<div class="row text-center">
|
|
||||||
<div class="col-4">
|
|
||||||
<small class="d-block text-muted">
|
|
||||||
Total
|
|
||||||
</small>
|
|
||||||
<small>3.20 GB / 4GB</small>
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
<small class="d-block text-muted">
|
|
||||||
Received
|
|
||||||
</small>
|
|
||||||
<small>3.20 GB</small>
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
<small class="d-block text-muted">
|
|
||||||
Sent
|
|
||||||
</small>
|
|
||||||
<small>3.20 GB</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mt-3 d-flex">
|
||||||
|
<button class="btn btn-body rounded-3 flex-grow-1 fw-bold" @click="showQRCode = true">
|
||||||
|
<i class="bi bi-link-45deg me-2"></i><small>Connect</small>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<Transition name="app">
|
||||||
|
<ConfigurationQRCode
|
||||||
|
v-if="showQRCode"
|
||||||
|
@back="showQRCode = false"
|
||||||
|
:qrcode-data="config.peer_configuration_data.file"></ConfigurationQRCode>
|
||||||
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -79,4 +57,18 @@ const showQRCode = ref(false)
|
|||||||
.button-group a:hover{
|
.button-group a:hover{
|
||||||
background-color: #ffffff20;
|
background-color: #ffffff20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dot{
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: auto !important;
|
||||||
|
background-color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dot.active {
|
||||||
|
background-color: #28a745 !important;
|
||||||
|
box-shadow: 0 0 0 .2rem #28a74545;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
@ -1,7 +1,4 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import * as uuid from "uuid";
|
|
||||||
import {onMounted} from "vue";
|
|
||||||
import QRCode from "qrcode";
|
|
||||||
import Qrcode from "@/components/SignIn/qrcode.vue";
|
import Qrcode from "@/components/SignIn/qrcode.vue";
|
||||||
|
|
||||||
const props = defineProps([
|
const props = defineProps([
|
||||||
@ -15,19 +12,26 @@ const emits = defineEmits([
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="p-2 position-fixed top-0 start-0 vw-100 vh-100 d-flex qrcodeContainer">
|
<div class="p-2 position-fixed top-0 start-0 vw-100 vh-100 d-flex qrcodeContainer p-3 overflow-scroll">
|
||||||
<div class="m-auto d-flex gap-3 flex-column">
|
<div class="m-auto d-flex gap-3 flex-column p-3">
|
||||||
<a role="button" @click="emits('back')">
|
<div>
|
||||||
<i class="me-2 bi bi-chevron-left"></i> Back
|
<a role="button" @click="emits('back')" class="btn btn-body rounded-3 btn-sm">
|
||||||
</a>
|
<i class="me-2 bi bi-x-lg"></i> Dismiss
|
||||||
<Qrcode :content="props.qrcodeData" ></Qrcode>
|
</a>
|
||||||
|
</div>
|
||||||
|
<Qrcode :content="props.qrcodeData"></Qrcode>
|
||||||
|
<button class="btn bg-primary-subtle border-primary-subtle rounded-3">
|
||||||
|
<i class="bi bi-download me-2"></i>Download
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.qrcodeContainer{
|
.qrcodeContainer{
|
||||||
background-color: #00000050;
|
background-color: #00000050;
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px) brightness(0.8);
|
||||||
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -15,7 +15,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<canvas :id="'qrcode_' + id" class="rounded-3 shadow"></canvas>
|
<canvas :id="'qrcode_' + id" class="rounded-3"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ if (route.query.Email){
|
|||||||
name="email"
|
name="email"
|
||||||
autocomplete="email"
|
autocomplete="email"
|
||||||
autofocus
|
autofocus
|
||||||
class="form-control rounded-3" id="email" placeholder="email">
|
class="form-control rounded-3 border-0" id="email" placeholder="email">
|
||||||
<label for="email" class="d-flex">
|
<label for="email" class="d-flex">
|
||||||
<i class="bi bi-person-circle me-2"></i>
|
<i class="bi bi-person-circle me-2"></i>
|
||||||
Email
|
Email
|
||||||
@ -67,20 +67,20 @@ if (route.query.Email){
|
|||||||
v-model="formData.Password"
|
v-model="formData.Password"
|
||||||
name="password"
|
name="password"
|
||||||
autocomplete="current-password"
|
autocomplete="current-password"
|
||||||
class="form-control rounded-3" id="password" placeholder="Password">
|
class="form-control rounded-3 border-0" id="password" placeholder="Password">
|
||||||
<label for="password" class="d-flex">
|
<label for="password" class="d-flex">
|
||||||
<i class="bi bi-key me-2"></i>
|
<i class="bi bi-key me-2"></i>
|
||||||
Password
|
Password
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="d-flex">
|
||||||
<a href="#" class="text-body text-decoration-none ms-0">
|
<a href="#" class="text-body text-decoration-none ms-auto btn btn-sm rounded-3">
|
||||||
Forgot Password?
|
Forgot Password?
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
:disabled="!formFilled || loading"
|
:disabled="!formFilled || loading"
|
||||||
class="btn btn-primary rounded-3 btn-brand px-3 py-2">
|
class="btn btn-primary rounded-3 btn-body px-3 py-2 fw-bold">
|
||||||
<span v-if="!loading" class="d-block">
|
<span v-if="!loading" class="d-block">
|
||||||
Continue <i class="ms-2 bi bi-arrow-right"></i>
|
Continue <i class="ms-2 bi bi-arrow-right"></i>
|
||||||
</span>
|
</span>
|
||||||
@ -95,7 +95,8 @@ if (route.query.Email){
|
|||||||
<span class="text-muted">
|
<span class="text-muted">
|
||||||
Don't have an account yet?
|
Don't have an account yet?
|
||||||
</span>
|
</span>
|
||||||
<RouterLink to="/signup" class="text-body text-decoration-none ms-auto fw-bold">
|
<RouterLink to="/signup"
|
||||||
|
class="text-body text-decoration-none ms-auto fw-bold btn btn-sm btn-body rounded-3">
|
||||||
Sign Up
|
Sign Up
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,7 +56,7 @@ const verify = async (e) => {
|
|||||||
loading.value = false
|
loading.value = false
|
||||||
if (data){
|
if (data){
|
||||||
if (data.status){
|
if (data.status){
|
||||||
store.clientProfile = data.data
|
console.log(data.data)
|
||||||
router.push('/')
|
router.push('/')
|
||||||
}else{
|
}else{
|
||||||
store.newNotification(data.message, "danger")
|
store.newNotification(data.message, "danger")
|
||||||
@ -76,7 +76,7 @@ watch(formFilled, () => {
|
|||||||
<template>
|
<template>
|
||||||
<form class="d-flex flex-column gap-3" @submit="e => verify(e)">
|
<form class="d-flex flex-column gap-3" @submit="e => verify(e)">
|
||||||
<div>
|
<div>
|
||||||
<a role="button" @click="emits('clearToken')">
|
<a role="button" @click="emits('clearToken')" class="btn btn-body btn-sm rounded-3">
|
||||||
<i class="me-2 bi bi-chevron-left"></i> Back
|
<i class="me-2 bi bi-chevron-left"></i> Back
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -117,7 +117,7 @@ watch(formFilled, () => {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
:disabled="!formFilled || loading"
|
:disabled="!formFilled || loading"
|
||||||
class="btn btn-primary rounded-3 btn-brand px-3 py-2">
|
class="btn btn-body rounded-3 px-3 py-2 fw-bold">
|
||||||
<span v-if="!loading" class="d-block">
|
<span v-if="!loading" class="d-block">
|
||||||
Continue <i class="ms-2 bi bi-arrow-right"></i>
|
Continue <i class="ms-2 bi bi-arrow-right"></i>
|
||||||
</span>
|
</span>
|
||||||
|
@ -17,46 +17,41 @@ const configurations = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// loading.value = true;
|
|
||||||
await loadConfigurations();
|
await loadConfigurations();
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="">
|
<div class="p-sm-3">
|
||||||
<ul class="nav gap-0 border-bottom">
|
<div class="w-100 d-flex align-items-center">
|
||||||
<li class="nav-item">
|
<a class="nav-link text-body border-start-0" aria-current="page" href="#">
|
||||||
<a class="nav-link text-body border-start-0" aria-current="page" href="#">
|
<strong>WGDashboard Client</strong>
|
||||||
<strong>WGDashboard Client</strong>
|
</a>
|
||||||
</a>
|
<div class="ms-auto px-3 d-flex gap-2 nav-links">
|
||||||
</li>
|
<a class=" text-body btn btn-body rounded-3 ms-auto btn-sm" aria-current="page" href="#">
|
||||||
<li class="nav-item ms-auto">
|
|
||||||
<a class="nav-link text-body" aria-current="page" href="#">
|
|
||||||
<i class="bi bi-gear-fill me-sm-2"></i>
|
<i class="bi bi-gear-fill me-sm-2"></i>
|
||||||
<span>Settings</span>
|
<span>Settings</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
<RouterLink to="/signout" class="btn btn-danger rounded-3 btn-sm" aria-current="page">
|
||||||
<li class="nav-item">
|
|
||||||
<RouterLink to="/signout" class="nav-link text-danger" aria-current="page">
|
|
||||||
<i class="bi bi-box-arrow-left me-sm-2"></i>
|
<i class="bi bi-box-arrow-left me-sm-2"></i>
|
||||||
<span>Sign Out</span>
|
<span>Sign Out</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
</div>
|
||||||
|
|
||||||
<Transition name="app" mode="out-in">
|
<Transition name="app" mode="out-in">
|
||||||
<div class="d-flex flex-column gap-3" v-if="!loading">
|
<div class="d-flex flex-column gap-3" v-if="!loading">
|
||||||
<div class="px-3 border-bottom py-4">
|
<div class="p-3 d-flex flex-column gap-3">
|
||||||
<h6>Hi donaldzou@live.hk!</h6>
|
|
||||||
<h5 class="mb-0">You have <strong>
|
|
||||||
{{ configurations.length }}
|
|
||||||
</strong> configuration{{ configurations.length > 1 ? 's':''}} available</h5>
|
|
||||||
</div>
|
|
||||||
<div class="px-3">
|
|
||||||
<Configuration v-for="config in configurations" :config="config"></Configuration>
|
<Configuration v-for="config in configurations" :config="config"></Configuration>
|
||||||
|
<!-- <h6 class="mb-0 text-center text-muted">-->
|
||||||
|
<!-- <small>-->
|
||||||
|
<!-- <strong>-->
|
||||||
|
<!-- {{ configurations.length }}-->
|
||||||
|
<!-- </strong> configuration{{ configurations.length > 1 ? 's':''}}-->
|
||||||
|
<!-- </small>-->
|
||||||
|
<!-- </h6>-->
|
||||||
</div>
|
</div>
|
||||||
<div></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="d-flex py-4">
|
<div v-else class="d-flex py-4">
|
||||||
<div class="spinner-border m-auto"></div>
|
<div class="spinner-border m-auto"></div>
|
||||||
@ -68,11 +63,10 @@ onMounted(async () => {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.nav-link{
|
.nav-link{
|
||||||
padding: 1rem 1rem;
|
padding: 1rem 1rem;
|
||||||
border-left: 1px solid var(--bs-border-color)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 576px) {
|
@media screen and (max-width: 576px) {
|
||||||
.nav-link span{
|
.nav-links a span{
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ onMounted(() => {
|
|||||||
<span class="text-muted">
|
<span class="text-muted">
|
||||||
Already have an account?
|
Already have an account?
|
||||||
</span>
|
</span>
|
||||||
<RouterLink to="/signin" class="text-body text-decoration-none ms-auto fw-bold">
|
<RouterLink to="/signin" class="text-body text-decoration-none ms-auto fw-bold btn btn-sm btn-body rounded-3">
|
||||||
Sign In
|
Sign In
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user