Compare commits
No commits in common. "b82355ea1aa7d44d546e5c0b1fbaa486a33cd3cf" and "656e202abdc3a78d73ca610f8587ac74fa65abc4" have entirely different histories.
b82355ea1a
...
656e202abd
14 changed files with 198 additions and 122 deletions
72
ui/package-lock.json
generated
72
ui/package-lock.json
generated
|
@ -9,6 +9,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"daisyui": "^2.51.5",
|
"daisyui": "^2.51.5",
|
||||||
|
"pinia": "^2.0.34",
|
||||||
"vue": "^3.2.47",
|
"vue": "^3.2.47",
|
||||||
"vue-router": "^4.1.6"
|
"vue-router": "^4.1.6"
|
||||||
},
|
},
|
||||||
|
@ -2150,6 +2151,56 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pinia": {
|
||||||
|
"version": "2.0.34",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.34.tgz",
|
||||||
|
"integrity": "sha512-cgOoGUiyqX0SSgX8XelK9+Ri4XA2/YyNtgjogwfzIx1g7iZTaZPxm7/bZYMCLU2qHRiHhxG7SuQO0eBacFNc2Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.5.0",
|
||||||
|
"vue-demi": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.4.0",
|
||||||
|
"typescript": ">=4.4.4",
|
||||||
|
"vue": "^2.6.14 || ^3.2.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pinia/node_modules/vue-demi": {
|
||||||
|
"version": "0.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.0.tgz",
|
||||||
|
"integrity": "sha512-gt58r2ogsNQeVoQ3EhoUAvUsH9xviydl0dWJj7dabBC/2L4uBId7ujtCwDRD0JhkGsV1i0CtfLAeyYKBht9oWg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||||
|
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.0.0-rc.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pirates": {
|
"node_modules/pirates": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
|
||||||
|
@ -2781,7 +2832,7 @@
|
||||||
"version": "4.8.4",
|
"version": "4.8.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
|
||||||
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
|
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
@ -4483,6 +4534,23 @@
|
||||||
"integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
|
"integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"pinia": {
|
||||||
|
"version": "2.0.34",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.34.tgz",
|
||||||
|
"integrity": "sha512-cgOoGUiyqX0SSgX8XelK9+Ri4XA2/YyNtgjogwfzIx1g7iZTaZPxm7/bZYMCLU2qHRiHhxG7SuQO0eBacFNc2Q==",
|
||||||
|
"requires": {
|
||||||
|
"@vue/devtools-api": "^6.5.0",
|
||||||
|
"vue-demi": "*"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue-demi": {
|
||||||
|
"version": "0.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.0.tgz",
|
||||||
|
"integrity": "sha512-gt58r2ogsNQeVoQ3EhoUAvUsH9xviydl0dWJj7dabBC/2L4uBId7ujtCwDRD0JhkGsV1i0CtfLAeyYKBht9oWg==",
|
||||||
|
"requires": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"pirates": {
|
"pirates": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
|
||||||
|
@ -4905,7 +4973,7 @@
|
||||||
"version": "4.8.4",
|
"version": "4.8.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
|
||||||
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
|
"integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"unbox-primitive": {
|
"unbox-primitive": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"daisyui": "^2.51.5",
|
"daisyui": "^2.51.5",
|
||||||
|
"pinia": "^2.0.34",
|
||||||
"vue": "^3.2.47",
|
"vue": "^3.2.47",
|
||||||
"vue-router": "^4.1.6"
|
"vue-router": "^4.1.6"
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,6 +12,11 @@ import { RouterView } from 'vue-router'
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-none">
|
<div class="flex-none">
|
||||||
<ul class="menu menu-horizontal px-1">
|
<ul class="menu menu-horizontal px-1">
|
||||||
|
<li>
|
||||||
|
<a>
|
||||||
|
<RouterLink to="/login">Login</RouterLink>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,7 +4,7 @@ type ActionFunction = (index: number) => void
|
||||||
const { tableHeader, tableRows, actions } = defineProps<{
|
const { tableHeader, tableRows, actions } = defineProps<{
|
||||||
tableHeader: string[],
|
tableHeader: string[],
|
||||||
tableRows: any[][],
|
tableRows: any[][],
|
||||||
actions?: {
|
actions: {
|
||||||
content: string,
|
content: string,
|
||||||
class: string,
|
class: string,
|
||||||
event: ActionFunction
|
event: ActionFunction
|
||||||
|
@ -20,7 +20,7 @@ const { tableHeader, tableRows, actions } = defineProps<{
|
||||||
<template v-for="header in tableHeader">
|
<template v-for="header in tableHeader">
|
||||||
<th>{{ header }}</th>
|
<th>{{ header }}</th>
|
||||||
</template>
|
</template>
|
||||||
<th v-if="actions != null && actions.length > 0">Action</th>
|
<th>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -30,7 +30,7 @@ const { tableHeader, tableRows, actions } = defineProps<{
|
||||||
<template v-for="data in row">
|
<template v-for="data in row">
|
||||||
<th>{{ data }}</th>
|
<th>{{ data }}</th>
|
||||||
</template>
|
</template>
|
||||||
<th v-if="actions != null && actions.length > 0">
|
<th>
|
||||||
<template v-for="action in actions">
|
<template v-for="action in actions">
|
||||||
<button :class="action.class" @click="action.event(index)">
|
<button :class="action.class" @click="action.event(index)">
|
||||||
{{ action.content }}
|
{{ action.content }}
|
||||||
|
|
55
ui/src/helpers/fetch-wrapper.js
Normal file
55
ui/src/helpers/fetch-wrapper.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { useAuthStore } from "../stores/auth.store"
|
||||||
|
|
||||||
|
export const fetchWrapper = {
|
||||||
|
get: request('GET'),
|
||||||
|
post: request('POST'),
|
||||||
|
put: request('PUT'),
|
||||||
|
delete: request('DELETE')
|
||||||
|
};
|
||||||
|
|
||||||
|
function request(method) {
|
||||||
|
return (url, body) => {
|
||||||
|
const requestOptions = {
|
||||||
|
method,
|
||||||
|
headers: authHeader(url)
|
||||||
|
};
|
||||||
|
if (body) {
|
||||||
|
requestOptions.headers['Content-Type'] = 'application/json';
|
||||||
|
requestOptions.body = JSON.stringify(body);
|
||||||
|
}
|
||||||
|
return fetch(url, requestOptions).then(handleResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
|
||||||
|
function authHeader(url) {
|
||||||
|
// return auth header with basic auth credentials if user is logged in and request is to the api url
|
||||||
|
const { user } = useAuthStore();
|
||||||
|
const isLoggedIn = !!user?.authdata;
|
||||||
|
const isApiUrl = url.startsWith(import.meta.env.VITE_API_URL);
|
||||||
|
if (isLoggedIn && isApiUrl) {
|
||||||
|
return { Authorization: `Basic ${user.authdata}` };
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleResponse(response) {
|
||||||
|
return response.text().then(text => {
|
||||||
|
const data = text && JSON.parse(text);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const { user, logout } = useAuthStore();
|
||||||
|
if ([401, 403].includes(response.status) && user) {
|
||||||
|
// auto logout if 401 Unauthorized or 403 Forbidden response returned from api
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
const error = (data && data.message) || response.statusText;
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,15 +1,11 @@
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import HomeView from '@/views/HomeView.vue'
|
import HomeView from '@/views/HomeView.vue'
|
||||||
|
import LoginView from '@/views/LoginView.vue'
|
||||||
import AllowedServersView from '@/views/servers/AllowedServersView.vue'
|
import AllowedServersView from '@/views/servers/AllowedServersView.vue'
|
||||||
import IndexServersView from '@/views/servers/IndexServersView.vue'
|
import IndexServersView from '@/views/servers/IndexServersView.vue'
|
||||||
import ServerConfigsView from '@/views/servers/ServerConfigsView.vue'
|
import ServerConfigsView from '@/views/servers/ServerConfigsView.vue'
|
||||||
import ServersMessagesView from '@/views/servers/ServersMessagesView.vue'
|
import ServersMessagesView from '@/views/servers/ServersMessagesView.vue'
|
||||||
import ServerMessagesView from '@/views/servers/ServerMessagesView.vue'
|
import ServerMessagesView from '@/views/servers/ServerMessagesView.vue'
|
||||||
import AllowedUsersView from '@/views/users/AllowedUsersView.vue'
|
|
||||||
import IndexUsersView from '@/views/users/IndexUsersView.vue'
|
|
||||||
import UserConfigsView from '@/views/users/UserConfigsView.vue'
|
|
||||||
import UsersMessagesView from '@/views/users/UsersMessagesView.vue'
|
|
||||||
import UserMessagesView from '@/views/users/UserMessagesView.vue'
|
|
||||||
|
|
||||||
const routerConfig = createRouter({
|
const routerConfig = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
|
@ -19,6 +15,11 @@ const routerConfig = createRouter({
|
||||||
name: 'home',
|
name: 'home',
|
||||||
component: HomeView
|
component: HomeView
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/login',
|
||||||
|
name: 'login',
|
||||||
|
component: LoginView
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/servers',
|
path: '/servers',
|
||||||
name: 'servers',
|
name: 'servers',
|
||||||
|
@ -43,31 +44,6 @@ const routerConfig = createRouter({
|
||||||
path: '/servers/messages/:serverId(\\d+)',
|
path: '/servers/messages/:serverId(\\d+)',
|
||||||
name: 'serverMessages',
|
name: 'serverMessages',
|
||||||
component: ServerMessagesView
|
component: ServerMessagesView
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/users',
|
|
||||||
name: 'users',
|
|
||||||
component: IndexUsersView
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/users/allowed',
|
|
||||||
name: 'allowedUsers',
|
|
||||||
component: AllowedUsersView
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/users/configs',
|
|
||||||
name: 'userConfigs',
|
|
||||||
component: UserConfigsView
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/users/messages',
|
|
||||||
name: 'usersMessages',
|
|
||||||
component: UsersMessagesView
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/users/messages/:userId(\\d+)',
|
|
||||||
name: 'userMessages',
|
|
||||||
component: UserMessagesView
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
34
ui/src/stores/auth.store.js
Normal file
34
ui/src/stores/auth.store.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
import { fetchWrapper } from '../helpers/fetch-wrapper.js';
|
||||||
|
|
||||||
|
const baseUrl = `${import.meta.env.VITE_API_URL}/users`;
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore({
|
||||||
|
id: 'auth',
|
||||||
|
state: () => ({
|
||||||
|
// initialize state from local storage to enable user to stay logged in
|
||||||
|
user: JSON.parse(localStorage.getItem('user')),
|
||||||
|
returnUrl: null
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
async login(username, password, router) {
|
||||||
|
const user = await fetchWrapper.post(`${baseUrl}/authenticate`, { username, password });
|
||||||
|
|
||||||
|
// update pinia state with user object + basic auth data
|
||||||
|
user.authdata = window.btoa(username + ':' + password);
|
||||||
|
this.user = user;
|
||||||
|
|
||||||
|
// store user details and basic auth data in local storage to keep user logged in between page refreshes
|
||||||
|
localStorage.setItem('user', JSON.stringify(user));
|
||||||
|
|
||||||
|
// redirect to previous url or default to home page
|
||||||
|
router.push(this.returnUrl || '/');
|
||||||
|
},
|
||||||
|
logout() {
|
||||||
|
this.user = null;
|
||||||
|
localStorage.removeItem('user');
|
||||||
|
router.push('/login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
21
ui/src/views/LoginView.vue
Normal file
21
ui/src/views/LoginView.vue
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useAuthStore } from '@/stores/auth.store';
|
||||||
|
|
||||||
|
function onSubmit(values, { setErrors }) {
|
||||||
|
const authStore = useAuthStore();
|
||||||
|
const { username, password } = values;
|
||||||
|
|
||||||
|
return authStore.login(username, password, this.$router)
|
||||||
|
.catch(error => setErrors({ apiError: error }));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center m-4">
|
||||||
|
<div class="flex flex-col space-y-2">
|
||||||
|
<input type="text" placeholder="Username" class="input input-bordered w-full max-w-xs" />
|
||||||
|
<input type="password" placeholder="Username" class="input input-bordered w-full max-w-xs" />
|
||||||
|
<button class="btn btn-wide btn-outline">Login</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
|
@ -6,11 +6,13 @@ const router = useRoute()
|
||||||
const { serverId } = router.params
|
const { serverId } = router.params
|
||||||
const tableHeader = ["ID", "Server ID", "User ID", "Tokens", "Date"]
|
const tableHeader = ["ID", "Server ID", "User ID", "Tokens", "Date"]
|
||||||
const tableRows = [["1", "name", "comment", "2", Date.now()]]
|
const tableRows = [["1", "name", "comment", "2", Date.now()]]
|
||||||
|
const actions = [{ class: "btn btn-sm btn-info m-1 no-animation", content: "View", event: (_: number) => { } }]
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-between m-5">
|
<div class="flex justify-between m-5">
|
||||||
<h1 class="text-xl normal-case">Server Messages</h1>
|
<h1 class="text-xl normal-case">Server Messages</h1>
|
||||||
<h1 class="text-xl normal-case">Server ID: {{ serverId }}</h1>
|
<h1 class="text-xl normal-case">Server ID: {{ serverId }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<TableComponent :tableHeader="tableHeader" :tableRows="tableRows" />
|
<TableComponent :tableHeader="tableHeader" :tableRows="tableRows" :actions="actions" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import TableComponent from '@/components/TableComponent.vue';
|
|
||||||
|
|
||||||
const tableHeader = ["ID", "User", "Comment", "Date"]
|
|
||||||
const tableRows = [["1", "name", "comment", Date.now()]]
|
|
||||||
const actions = [{ class: "btn btn-sm btn-info m-1", content: "Edit", event: (_: number) => { } }, { class: "btn btn-sm btn-error m-1", content: "Delete", event: (_: number) => { } }]
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="flex justify-between m-5">
|
|
||||||
<h1 class="text-xl normal-case">Allowed Users</h1>
|
|
||||||
<button class="btn btn-circle btn-success">Add</button>
|
|
||||||
</div>
|
|
||||||
<TableComponent :tableHeader="tableHeader" :tableRows="tableRows" :actions="actions" />
|
|
||||||
</template>
|
|
|
@ -1,18 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<h1 class="text-xl normal-case m-5">Server overview</h1>
|
|
||||||
<div class="flex justify-center m-4">
|
|
||||||
<div class="flex flex-col space-y-2">
|
|
||||||
<RouterLink to="/users/allowed">
|
|
||||||
<button class="btn btn-wide btn-outline">Allowed</button>
|
|
||||||
</RouterLink>
|
|
||||||
<RouterLink to="/users/configs">
|
|
||||||
<button class="btn btn-wide btn-outline">Configs</button>
|
|
||||||
</RouterLink>
|
|
||||||
<RouterLink to="/users/messages">
|
|
||||||
<button class="btn btn-wide btn-outline">User with Messages</button>
|
|
||||||
</RouterLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,14 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import TableComponent from '@/components/TableComponent.vue';
|
|
||||||
|
|
||||||
const tableHeader = ["ID", "Server ID", "System Message", "Rate limit"]
|
|
||||||
const tableRows = [["1", "name", "comment", "1"]]
|
|
||||||
const actions = [{ class: "btn btn-sm btn-info m-1", content: "Edit", event: (_: number) => { } }, { class: "btn btn-sm btn-error m-1", content: "Delete", event: (_: number) => { } }]
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="flex justify-between m-5">
|
|
||||||
<h1 class="text-xl normal-case">User Configs</h1>
|
|
||||||
<button class="btn btn-circle btn-success">Add</button>
|
|
||||||
</div>
|
|
||||||
<TableComponent :tableHeader="tableHeader" :tableRows="tableRows" :actions="actions" />
|
|
||||||
</template>
|
|
|
@ -1,16 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import TableComponent from '@/components/TableComponent.vue';
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
|
|
||||||
const router = useRoute()
|
|
||||||
const { userId } = router.params
|
|
||||||
const tableHeader = ["ID", "Server ID", "User ID", "Tokens", "Date"]
|
|
||||||
const tableRows = [["1", "name", "comment", "2", Date.now()]]
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="flex justify-between m-5">
|
|
||||||
<h1 class="text-xl normal-case">User Messages</h1>
|
|
||||||
<h1 class="text-xl normal-case">User ID: {{ userId }}</h1>
|
|
||||||
</div>
|
|
||||||
<TableComponent :tableHeader="tableHeader" :tableRows="tableRows" />
|
|
||||||
</template>
|
|
|
@ -1,24 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import TableComponent from '@/components/TableComponent.vue';
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import { provide } from 'vue';
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const tableHeader = ["ID", "Server ID"]
|
|
||||||
const tableRows = [["1", "1234567890"]]
|
|
||||||
|
|
||||||
const view = (index: number) => {
|
|
||||||
const row = tableRows[index]
|
|
||||||
const id = row[0]
|
|
||||||
router.push("/servers/messages/" + id)
|
|
||||||
}
|
|
||||||
|
|
||||||
const actions = [{ class: "btn btn-sm btn-info m-1", content: "View", event: view }]
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="flex justify-between m-5">
|
|
||||||
<h1 class="text-xl normal-case">Users with Messages</h1>
|
|
||||||
</div>
|
|
||||||
<TableComponent :tableHeader="tableHeader" :tableRows="tableRows" :actions="actions" />
|
|
||||||
</template>
|
|
Loading…
Reference in a new issue