awedasdads
This commit is contained in:
parent
4d744d92c4
commit
33112cb7ae
18 changed files with 12668 additions and 2935 deletions
62
assets/js/linkExpander.js
Normal file
62
assets/js/linkExpander.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
let cache = {};
|
||||
|
||||
export default {
|
||||
install (Vue) {
|
||||
//Takes a HTML string then parses it and replaces appropriate
|
||||
//links with the relevant expansion
|
||||
//Returns a callback with the 'expanded' HTML string
|
||||
Vue.prototype.$linkExpander = function (HTML, cb) {
|
||||
let completed = 0;
|
||||
let completedAPICall = () => {
|
||||
completed++;
|
||||
|
||||
if(completed === links.length) {
|
||||
cb(parsed.innerHTML);
|
||||
}
|
||||
};
|
||||
|
||||
let replaceLink = (html, link) => {
|
||||
if(html.length) {
|
||||
let div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
|
||||
link.parentNode.replaceChild(
|
||||
div.children[0],
|
||||
link
|
||||
);
|
||||
}
|
||||
|
||||
completedAPICall();
|
||||
};
|
||||
|
||||
let parsed = document.createElement('div');
|
||||
parsed.innerHTML = HTML;
|
||||
|
||||
let links = Array
|
||||
.from(parsed.querySelectorAll('p a[href]'))
|
||||
.filter(a => {
|
||||
return (
|
||||
a.parentNode.parentNode === parsed &&
|
||||
a.parentNode.childNodes.length === 1 &&
|
||||
a.innerHTML === a.href
|
||||
)
|
||||
});
|
||||
|
||||
links.forEach(link => {
|
||||
let cached = cache[link.href];
|
||||
|
||||
if(cached) {
|
||||
replaceLink(cached, link);
|
||||
} else {
|
||||
Vue.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'forums/link_preview?url=' + link.href)
|
||||
.then(res => {
|
||||
cache[link.href] = res.data;
|
||||
replaceLink(res.data, link);
|
||||
})
|
||||
.catch(completedAPICall);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'"
|
||||
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales 'src/locales/**/*.json'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/cli": "^4.5.10",
|
||||
|
|
|
@ -68,6 +68,75 @@
|
|||
</div>
|
||||
</form>
|
||||
</b-modal>
|
||||
<b-modal :active="settingsModal" @update:active="value => settingsModal = value" :width="640" scroll="keep">
|
||||
<form>
|
||||
<div class="modal-card" style="width: auto">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">{{ $t('settings.title') }}</p>
|
||||
<button
|
||||
type="button"
|
||||
class="delete"
|
||||
@click="loginModal = false"/>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<b-tabs position="is-centered" class="block">
|
||||
<b-tab-item :label="$t('settings.general.title')">
|
||||
<h1 class="title">{{$t('settings.general.title')}}</h1>
|
||||
<div>
|
||||
<h1 class="subtitle">{{$t('settings.general.about')}}</h1>
|
||||
<h4>
|
||||
{{ $t('settings.general.description') }}
|
||||
</h4>
|
||||
<b-input type="textarea"
|
||||
:placeholder="$t('settings.general.hi') + ' ' + $store.state.user.username"
|
||||
maxlength="256"
|
||||
v-model='settings.general.description.value'
|
||||
:error='settings.general.description.error'
|
||||
></b-input>
|
||||
<b-button
|
||||
class='button is-info'
|
||||
:loading='settings.general.description.loading'
|
||||
@click='fakeUser()'
|
||||
>
|
||||
{{$t('settings.general.saveDesc')}}
|
||||
</b-button>
|
||||
</div>
|
||||
<div>
|
||||
<h2>{{$t('settings.general.preferences')}}</h2>
|
||||
<b-switch class="is-info" v-model="settings.general.preferences.developerMode">{{$t('settings.general.devMode')}}</b-switch><br>
|
||||
<b-button
|
||||
class='button is-info'
|
||||
:loading='settings.general.preferences.loading'
|
||||
@click='fakeUser()'
|
||||
>
|
||||
{{$t('settings.general.savePref')}}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-tab-item>
|
||||
<b-tab-item :label="$t('settings.security.title')">
|
||||
Debug
|
||||
</b-tab-item>
|
||||
<b-tab-item :label="$t('settings.privacy.title')">
|
||||
Debug
|
||||
</b-tab-item>
|
||||
<b-tab-item :label="$t('settings.experiments.title')">
|
||||
Debug
|
||||
</b-tab-item>
|
||||
<b-tab-item :label="$t('settings.about.title')">
|
||||
<div>
|
||||
<center>
|
||||
<img src="https://cdn.kaverti.com/icon.png" width="10%">
|
||||
<h1>Kaverti v{{this.$store.state.client.clientVersion}}</h1>
|
||||
<p>Latest client version: v{{this.$store.state.client.latestClientVersion}}</p>
|
||||
<p>API version: v{{this.$store.state.client.latestAPIVersion}}</p>
|
||||
</center>
|
||||
</div>
|
||||
</b-tab-item>
|
||||
</b-tabs>
|
||||
</section>
|
||||
</div>
|
||||
</form>
|
||||
</b-modal>
|
||||
<b-modal :active="registerModal" @update:active="value => registerModal = value" :width="640" scroll="keep">
|
||||
<form>
|
||||
<div class="modal-card" style="width: auto">
|
||||
|
@ -145,16 +214,19 @@
|
|||
</b-navbar-item>
|
||||
</template>
|
||||
<template #start>
|
||||
<b-navbar-item href="#">
|
||||
<b-navbar-item tag="router-link" to="/">
|
||||
{{$t('navbar.home')}}
|
||||
</b-navbar-item>
|
||||
<b-navbar-item href="#">
|
||||
<b-navbar-item tag="router-link" to="/marketplace">
|
||||
{{$t('navbar.marketplace')}}
|
||||
</b-navbar-item>
|
||||
<b-navbar-item href="#">
|
||||
<b-navbar-item tag="router-link" to="/users">
|
||||
{{$t('navbar.users')}}
|
||||
</b-navbar-item>
|
||||
<b-navbar-item tag="router-link" to="/teams">
|
||||
{{$t('navbar.teams')}}
|
||||
</b-navbar-item>
|
||||
<b-navbar-item href="#">
|
||||
<b-navbar-item tag="router-link" to="/games">
|
||||
{{$t('navbar.games')}}
|
||||
</b-navbar-item>
|
||||
<div v-if="$store.state.debug" class="navbar-item has-dropdown is-hoverable is-info">
|
||||
|
@ -166,9 +238,25 @@
|
|||
<b-navbar-item @click="fakeUser()">{{$t('navbar.dev.fakeUser')}}</b-navbar-item>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="$store.state.debug" class="navbar-item has-dropdown is-hoverable is-info">
|
||||
<a class="navbar-link">
|
||||
<p>{{$t('navbar.more.title')}}</p>
|
||||
</a>
|
||||
<div class="navbar-dropdown">
|
||||
<b-navbar-item tag="a" href="https://twitter.com/Kaverti">{{$t('navbar.more.twitter')}}</b-navbar-item>
|
||||
<b-navbar-item tag="a" href="https://discord.gg/Q3HAWFdBPK">{{$t('navbar.more.discord')}}</b-navbar-item>
|
||||
<b-navbar-item tag="router-link" to="/roadmap">{{$t('navbar.more.roadmap')}}</b-navbar-item>
|
||||
<b-navbar-item tag="a" href="soon">{{$t('navbar.more.documentation')}}</b-navbar-item>
|
||||
<b-navbar-item tag="router-link" to="/downloads">{{$t('navbar.downloads')}}</b-navbar-item>
|
||||
<b-navbar-item tag="router-link" to="/stats">{{$t('navbar.more.stats')}}</b-navbar-item>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #end>
|
||||
<b-navbar-item v-if="$store.state.user.username">
|
||||
{{$store.state.user.koins}} {{$t('currency')}}
|
||||
</b-navbar-item>
|
||||
<div v-if="!loading">
|
||||
<b-navbar-item v-if="!$store.state.user.username" tag="div">
|
||||
<div class="buttons">
|
||||
|
@ -180,14 +268,14 @@
|
|||
</b-button>
|
||||
</div>
|
||||
</b-navbar-item>
|
||||
<b-navbar-item v-if="$store.state.user.username">
|
||||
<b-navbar-item v-if="$store.state.user.username">
|
||||
<div class="navbar-item has-dropdown is-hoverable is-info">
|
||||
<a class="navbar-link">
|
||||
<p>{{$store.state.user.username}}</p>
|
||||
</a>
|
||||
<div class="navbar-dropdown">
|
||||
<b-navbar-item tag="router-link" :to="'/u/' + $store.state.user.username">{{$t('navbar.user.profile')}}</b-navbar-item>
|
||||
<b-navbar-item tag="router-link" to="/settings">{{$t('navbar.user.settings')}}</b-navbar-item>
|
||||
<b-navbar-item @click="settingsModal = true">{{$t('navbar.user.settings')}}</b-navbar-item>
|
||||
<b-navbar-item tag="router-link" to="/transactions">{{$t('navbar.user.transactions')}}</b-navbar-item>
|
||||
<b-navbar-item tag="router-link" to="/character">{{$t('navbar.user.avatar')}}</b-navbar-item>
|
||||
<b-navbar-item tag="router-link" to="/creations">{{$t('navbar.user.creations')}}</b-navbar-item>
|
||||
|
@ -218,6 +306,89 @@ export default {
|
|||
loginModal: false,
|
||||
registerModal: false,
|
||||
loading: true,
|
||||
settingsModal: false,
|
||||
settings: {
|
||||
tab: 0,
|
||||
general: {
|
||||
description: {
|
||||
value: '',
|
||||
loading: false,
|
||||
error: ''
|
||||
},
|
||||
preferences: {
|
||||
theme: '',
|
||||
developerMode: '',
|
||||
loading: false,
|
||||
error: ''
|
||||
},
|
||||
},
|
||||
privacy: {
|
||||
wall: false,
|
||||
loading: false
|
||||
},
|
||||
experiments: {
|
||||
theme: 'light',
|
||||
loading: false,
|
||||
error: '',
|
||||
relationships: false,
|
||||
wall: false,
|
||||
marketplace: false,
|
||||
teams: false,
|
||||
newsettings: true,
|
||||
corrupt: false,
|
||||
local: []
|
||||
},
|
||||
account: {
|
||||
showLogoutModal: false,
|
||||
password: {
|
||||
loading: false,
|
||||
|
||||
current: '',
|
||||
new: '',
|
||||
|
||||
errors: {
|
||||
'new password': '',
|
||||
'current password': ''
|
||||
}
|
||||
},
|
||||
email: {
|
||||
loading: false,
|
||||
loadingChange: false,
|
||||
|
||||
currentPassword: '',
|
||||
new: '',
|
||||
|
||||
errors: {
|
||||
'new password': '',
|
||||
'email current password': ''
|
||||
}
|
||||
},
|
||||
username: {username: '',
|
||||
password: '',
|
||||
errors: {
|
||||
'username': '',
|
||||
'password': '',
|
||||
},
|
||||
loading: false,
|
||||
},
|
||||
deleteAcc: {
|
||||
loading: false,
|
||||
|
||||
password: '',
|
||||
|
||||
errors: {
|
||||
'current password': ''
|
||||
}
|
||||
},
|
||||
totp: {loading: false
|
||||
},
|
||||
|
||||
deleteAccountLoading: false,
|
||||
invalidateSessionLoading: false,
|
||||
emailCheckDelete: false,
|
||||
showLoginModal: false
|
||||
}
|
||||
},
|
||||
login: {
|
||||
username: '',
|
||||
password: '',
|
||||
|
|
68
src/components/Post.vue
Normal file
68
src/components/Post.vue
Normal file
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<div class="post">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div class="media-content">
|
||||
<p class="title is-4">{{post.Thread.name | truncateMid(50)}}</p>
|
||||
<p class="subtitle is-6">{{username}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div tabindex='-1'
|
||||
class='post__content'
|
||||
v-html='postContentHTML'
|
||||
@mouseup='setShowQuote'
|
||||
@blur='showQuote = false'
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'ThreadPost',
|
||||
props: [
|
||||
'post',
|
||||
'highlight',
|
||||
'showReply',
|
||||
'showThread',
|
||||
'showSelect',
|
||||
'clickForPost',
|
||||
'allowQuote'
|
||||
],
|
||||
data() {
|
||||
let post = this.post
|
||||
|
||||
return {
|
||||
hover: false,
|
||||
showShareModal: false,
|
||||
showReportPostModal: false,
|
||||
postURL: `${location.origin}/p/${post.id}`,
|
||||
selected: false,
|
||||
|
||||
showQuote: false,
|
||||
quoteX: 0,
|
||||
quoteY: 0,
|
||||
quoteSelection: '',
|
||||
|
||||
postContentHTML: post.content
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
username () {
|
||||
if(this.post.User) {
|
||||
return this.post.User.username
|
||||
} else {
|
||||
return '[deleted]'}
|
||||
},
|
||||
showActions () {
|
||||
return this.hover || this.showShareModal || this.showReportPostModal
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.$linkExpander(this.post.content, v => this.postContentHTML = v);
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -125,6 +125,14 @@
|
|||
"settings": "Settings",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"more": {
|
||||
"title": "More",
|
||||
"twitter": "Twitter",
|
||||
"discord": "Discord",
|
||||
"roadmap": "Roadmap",
|
||||
"documentation": "API Docs",
|
||||
"stats": "Kaverti Stats"
|
||||
},
|
||||
"register": "Register",
|
||||
"login": "Login"
|
||||
},
|
||||
|
@ -150,6 +158,38 @@
|
|||
"disableDebug": "Debug mode disabled, you will no longer have access to development features until you refresh.",
|
||||
"authSuccess": "Request successful, your token is valid, and the Kaverti server instance is running correctly."
|
||||
},
|
||||
"user": {
|
||||
"title": "Loading",
|
||||
"about": "About",
|
||||
"more": "More of",
|
||||
"posts": "Posts",
|
||||
"threads": "Threads",
|
||||
"inventory": "Inventory",
|
||||
"awards": "Awards",
|
||||
"items": "Items",
|
||||
"wall": "User Wall",
|
||||
"description": "Description",
|
||||
"created": "Registered at"
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats",
|
||||
"users": "Registered users",
|
||||
"purchased": "Purchased items",
|
||||
"items": "Uploaded items",
|
||||
"posts": "Forum posts",
|
||||
"threads": "Threads",
|
||||
"teams": "Teams"
|
||||
},
|
||||
"settings": {
|
||||
"title": "User Settings",
|
||||
"general": {
|
||||
"title": "General"
|
||||
},
|
||||
"security": {
|
||||
"title": "Security"
|
||||
}
|
||||
},
|
||||
"currency": "Koins",
|
||||
"close": "Close",
|
||||
"tos": "Terms of Service",
|
||||
"gotIt": "Got it!",
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
{
|
||||
"404": {
|
||||
"title": "systemctl start httpd??",
|
||||
"text": "Error 521 Ray ID: 4f3226667865dc3b how to fix",
|
||||
"quoteText": "There is currently 2900 WIND quotes available",
|
||||
"windQuote": "without https works",
|
||||
"home": "£1m"
|
||||
},
|
||||
"login": {
|
||||
"title": "dont tell anyone the new domain",
|
||||
"login": "im never firing you btw your my best friend xzd xd",
|
||||
|
@ -17,14 +24,99 @@
|
|||
"login": "can i get my twitter handle back lol",
|
||||
"agree": "Price has been reduced to £10 "
|
||||
},
|
||||
"teams": {
|
||||
"createTeam": "Create Team",
|
||||
"joinTeam": "Join Team",
|
||||
"join": "Join",
|
||||
"invite": "Invite",
|
||||
"viewPermissions": "View role permissions",
|
||||
"devBanner": "Teams are currently in development, expect missing features.",
|
||||
"view": "View",
|
||||
"viewTeam": "View Team",
|
||||
"memberRoles": "Members and Roles",
|
||||
"members": "Members",
|
||||
"roles": "Roles",
|
||||
"items": "Created Items",
|
||||
"foundedAt": "Team was founded at",
|
||||
"teamWall": "Team Wall",
|
||||
"teamWallText": "'s Team Wall",
|
||||
"verified": "Verified Team",
|
||||
"admin": {
|
||||
"text": "Team Administration",
|
||||
"nav": {
|
||||
"general": "General",
|
||||
"roles": "Roles",
|
||||
"members": "Members",
|
||||
"privacy": "Privacy",
|
||||
"invites": "Invites",
|
||||
"forum": "Forum",
|
||||
"verification": "Verification"
|
||||
},
|
||||
"general": {
|
||||
"title": "General",
|
||||
"name": "Name",
|
||||
"description": "Description",
|
||||
"saveTeam": "Save Team",
|
||||
"modifyPicture": "Modify Team avatar"
|
||||
},
|
||||
"roles": {
|
||||
"title": "Roles",
|
||||
"addRole": "Add Role",
|
||||
"saveOrder": "Save role order",
|
||||
"modifying": "Modifying",
|
||||
"name": "Chosen role name",
|
||||
"permissions": "Permissions",
|
||||
"creating": "Creating a role"
|
||||
},
|
||||
"members": {
|
||||
"title": "Members",
|
||||
"modifyRoles": "Modify user roles",
|
||||
"removeAllRoles": "Remove all roles from user"
|
||||
},
|
||||
"privacy": {
|
||||
"title": "Team Privacy",
|
||||
"teamWall": "Opt out of team walls",
|
||||
"disallowForum": "Disallow anyone from viewing/creating on your Team Forum"
|
||||
},
|
||||
"invites": {
|
||||
"title": "Invites",
|
||||
"code": "Code",
|
||||
"uses": "Uses",
|
||||
"maxUses": "Max Uses",
|
||||
"createdBy": "Created by",
|
||||
"date": "Date",
|
||||
"delete": "Delete selected",
|
||||
"generate": "Generate invite"
|
||||
},
|
||||
"invite": {
|
||||
"title": "Invite people to",
|
||||
"amountUses": "Amount of uses (0 is Unlimited)",
|
||||
"role": "Please select a role for the user to be auto assigned (optional)",
|
||||
"refresh": "Refresh",
|
||||
"inviteURL": "Invite URL",
|
||||
"domain": "https://kaverti.com/invite/"
|
||||
}
|
||||
},
|
||||
"permissionTypes": {
|
||||
"inviteUsers": "Invite users",
|
||||
"administrator": "Administrator",
|
||||
"modifyRoles": "Modify roles/permissions/users",
|
||||
"modifyTeamSettings": "Modify team settings",
|
||||
"teamForum": "Modify team forum configuration",
|
||||
"forumModerator": "Forum moderation permissions",
|
||||
"forumAdmin": "Forum administrator",
|
||||
"submitMarketplace": "Submit Marketplace items",
|
||||
"priorityValue": "Set priority value"
|
||||
}
|
||||
},
|
||||
"navbar": {
|
||||
"home": "ok £50 extra",
|
||||
"forums": "can u fix that for £10 extra",
|
||||
"marketplace": "£30 Its the last thing im asking for troplo.",
|
||||
"downloads": "dont tell anyone who i am here",
|
||||
"games": "have u seen bloxtopia's games",
|
||||
"users": "Users",
|
||||
"teams": "gonna help kyle also bring tetrimus back",
|
||||
"users": "as i was not payed yet",
|
||||
"teams": "leave me alone",
|
||||
"dev": {
|
||||
"title": "i might come back to development",
|
||||
"fakeUser": "user : root",
|
||||
|
@ -32,7 +124,7 @@
|
|||
},
|
||||
"user": {
|
||||
"title": "Unknown",
|
||||
"profile": "",
|
||||
"profile": "My Profile",
|
||||
"creations": "My Creations",
|
||||
"downloads": "Downloads",
|
||||
"avatar": "My Avatar",
|
||||
|
@ -40,16 +132,17 @@
|
|||
"settings": "Settings",
|
||||
"logout": "Logout"
|
||||
},
|
||||
"more": {
|
||||
"title": "a",
|
||||
"twitter": "Twitter",
|
||||
"discord": "Discord",
|
||||
"roadmap": "Roadmap",
|
||||
"documentation": "API Docs",
|
||||
"stats": "Kaverti Stats"
|
||||
},
|
||||
"register": "u got the .net?",
|
||||
"login": "/phpmyadmin"
|
||||
},
|
||||
"404": {
|
||||
"title": "systemctl start httpd??",
|
||||
"text": "Error 521 Ray ID: 4f3226667865dc3b how to fix",
|
||||
"quoteText": "There is currently 2900 WIND quotes available",
|
||||
"windQuote": "without https works",
|
||||
"home": "£1m"
|
||||
},
|
||||
"debug": {
|
||||
"title": "Debug mode enabled",
|
||||
"authUser": "Authenticated user",
|
||||
|
@ -65,6 +158,38 @@
|
|||
"disableDebug": "Debug mode disabled, you will no longer have access to development features until you refresh.",
|
||||
"authSuccess": "Request successful, your token is valid, and the Kaverti server instance is running correctly."
|
||||
},
|
||||
"user": {
|
||||
"title": "Loading",
|
||||
"about": "About",
|
||||
"more": "More of",
|
||||
"posts": "Posts",
|
||||
"threads": "Threads",
|
||||
"inventory": "Inventory",
|
||||
"awards": "Awards",
|
||||
"items": "Items",
|
||||
"wall": "User Wall",
|
||||
"description": "Description",
|
||||
"created": "Registered at"
|
||||
},
|
||||
"stats": {
|
||||
"title": "Stats",
|
||||
"users": "Registered users",
|
||||
"purchased": "Purchased items",
|
||||
"items": "Uploaded items",
|
||||
"posts": "Forum posts",
|
||||
"threads": "Threads",
|
||||
"teams": "Teams"
|
||||
},
|
||||
"settings": {
|
||||
"title": "User Settings",
|
||||
"general": {
|
||||
"title": "General"
|
||||
},
|
||||
"security": {
|
||||
"title": "Security"
|
||||
}
|
||||
},
|
||||
"currency": "fuck you",
|
||||
"close": "I've just told the full truth to logan",
|
||||
"tos": "get the fuck over it, it was never a scam",
|
||||
"gotIt": "that is wind for you",
|
||||
|
|
|
@ -23,6 +23,30 @@ const routes = [
|
|||
name: 'Debug',
|
||||
component: route('Debug')
|
||||
},
|
||||
{ path: '/u/:username', redirect: '/u/:username/posts', component: route('User'), children: [
|
||||
{ path: 'posts', component: route('UserPosts') },
|
||||
{ path: 'threads', component: route('UserThreads') },
|
||||
{ path: 'items', component: route('UserMarketplace') },
|
||||
{ path: 'wall', component: route('UserWall') },
|
||||
{ path: 'inventory', component: route('UserInventory') },
|
||||
{ path: 'friends', component: route('UserFriends') },
|
||||
{ path: 'awards', component: route('UserAwards') }
|
||||
] },
|
||||
{
|
||||
path: '/users',
|
||||
name: 'Users',
|
||||
component: route('Users')
|
||||
},
|
||||
{
|
||||
path: '/roadmap',
|
||||
name: 'Roadmap',
|
||||
component: route('Roadmap')
|
||||
},
|
||||
{
|
||||
path: '/stats',
|
||||
name: 'Stats',
|
||||
component: route('Stats')
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
name: '404',
|
||||
|
|
|
@ -15,6 +15,11 @@ export default new Vuex.Store({
|
|||
name: "Kaverti",
|
||||
debug: tb(process.env.VUE_APP_STAGING),
|
||||
wind: false,
|
||||
client: {
|
||||
clientVersion: '1.0.0-prerelease',
|
||||
latestClientVersion: '',
|
||||
latestAPIVersion: '',
|
||||
},
|
||||
errors: {
|
||||
errors: null,
|
||||
modal: false
|
||||
|
@ -80,6 +85,12 @@ export default new Vuex.Store({
|
|||
},
|
||||
setWind (state, value) {
|
||||
state.wind = value
|
||||
},
|
||||
setKoins (state, value) {
|
||||
state.user.koins = value
|
||||
},
|
||||
setAdmin (state, value) {
|
||||
state.user.admin = value
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
|
14507
src/views/404.vue
14507
src/views/404.vue
File diff suppressed because it is too large
Load diff
|
@ -10,20 +10,7 @@
|
|||
<div class="column is-6">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<div class="media">
|
||||
<div class="media-content">
|
||||
<p class="title is-4">John Smith</p>
|
||||
<p class="subtitle is-6">@johnsmith</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Phasellus nec iaculis mauris. <a>@bulmaio</a>.
|
||||
<a href="#">#css</a> <a href="#">#responsive</a>
|
||||
<br>
|
||||
<time datetime="2016-1-1">11:09 PM - 1 Jan 2016</time>
|
||||
</div>
|
||||
test
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
5
src/views/Roadmap.vue
Normal file
5
src/views/Roadmap.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<main>
|
||||
|
||||
</main>
|
||||
</template>
|
92
src/views/Stats.vue
Normal file
92
src/views/Stats.vue
Normal file
|
@ -0,0 +1,92 @@
|
|||
<template>
|
||||
<main>
|
||||
<div class="column is-vcentered">
|
||||
<h1 class="title">{{$t('stats.title')}}</h1>
|
||||
</div>
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-vcentered has-text-centered">
|
||||
<h1 class="title">{{$t('stats.users')}}</h1>
|
||||
<div class="box">
|
||||
<h1 class="title" v-if="!loading">
|
||||
{{users}}
|
||||
</h1>
|
||||
<b-skeleton size="is-large" :active="loading" :count="2"></b-skeleton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-vcentered has-text-centered">
|
||||
<h1 class="title">{{$t('stats.posts')}}</h1>
|
||||
<div class="box">
|
||||
<h1 class="title" v-if="!loading">
|
||||
{{posts}}
|
||||
</h1>
|
||||
<b-skeleton size="is-large" :active="loading" :count="2"></b-skeleton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-vcentered has-text-centered">
|
||||
<h1 class="title">{{$t('stats.purchased')}}</h1>
|
||||
<div class="box">
|
||||
<h1 class="title" v-if="!loading">
|
||||
{{inventory}}
|
||||
</h1>
|
||||
<b-skeleton size="is-large" :active="loading" :count="2"></b-skeleton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-vcentered has-text-centered">
|
||||
<h1 class="title">{{$t('stats.teams')}}</h1>
|
||||
<div class="box">
|
||||
<h1 class="title" v-if="!loading">
|
||||
{{teams}}
|
||||
</h1>
|
||||
<b-skeleton size="is-large" :active="loading" :count="2"></b-skeleton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-vcentered has-text-centered">
|
||||
<h1 class="title">{{$t('stats.items')}}</h1>
|
||||
<div class="box">
|
||||
<h1 class="title" v-if="!loading">
|
||||
{{items}}
|
||||
</h1>
|
||||
<b-skeleton size="is-large" :active="loading" :count="2"></b-skeleton>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-vcentered has-text-centered">
|
||||
<h1 class="title">{{$t('stats.threads')}}</h1>
|
||||
<div class="box">
|
||||
<h1 class="title" v-if="!loading">
|
||||
{{threads}}
|
||||
</h1>
|
||||
<b-skeleton size="is-large" :active="loading" :count="2"></b-skeleton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
users: 0,
|
||||
posts: 0,
|
||||
inventory: 0,
|
||||
items: 0,
|
||||
teams: 0,
|
||||
threads: 0,
|
||||
loading: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.axios.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'kaverti/stats')
|
||||
.then(res => {
|
||||
this.users = res.data.users
|
||||
this.posts = res.data.posts
|
||||
this.inventory = res.data.inventory
|
||||
this.items = res.data.items
|
||||
this.teams = res.data.teams
|
||||
this.threads = res.data.threads
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.$buefy.snackbar.open({message:this.$t('errors.authFail'), type: 'is-warning'})
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
333
src/views/User.vue
Normal file
333
src/views/User.vue
Normal file
|
@ -0,0 +1,333 @@
|
|||
<style>
|
||||
.limit{
|
||||
margin-top: 0.5rem;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<main>
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-4 is-vcentered has-text-centered">
|
||||
<h1 class="title">{{user.username}}</h1>
|
||||
<div class="box">
|
||||
<img :src="'https://cdn.kaverti.com/user/avatars/full/' + $store.state.user.avatar + '.png'" :alt="$store.state.user.username + '\'s avatar'" width="70%">
|
||||
</div>
|
||||
<h1 class="subtitle">
|
||||
{{ $t('user.about') }} {{user.username}}
|
||||
</h1>
|
||||
<div class="box limit">
|
||||
{{ $t('user.description') }}: {{user.description}}<br>
|
||||
{{ $t('user.created') }}: {{user.createdAt}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-6 is-vcentered has-text-centered">
|
||||
<h1 class="title">{{ $t('user.more') }} {{user.username}}</h1>
|
||||
<div>
|
||||
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
<router-link tag="li" :to="'/u/' + user.username + '/posts'" exact><a>{{ $t('user.posts') }}</a></router-link>
|
||||
<router-link tag="li" :to="'/u/' + user.username + '/threads'" exact><a>{{ $t('user.threads') }}</a></router-link>
|
||||
<router-link tag="li" :to="'/u/' + user.username + '/wall'" exact><a>{{ $t('user.wall') }}</a></router-link>
|
||||
<router-link tag="li" :to="'/u/' + user.username + '/items'" exact><a>{{ $t('user.items') }}</a></router-link>
|
||||
<router-link tag="li" :to="'/u/' + user.username + '/inventory'" exact><a>{{ $t('user.inventory') }}</a></router-link>
|
||||
<router-link tag="li" :to="'/u/' + user.username + '/awards'" exact><a>{{ $t('user.awards') }}</a></router-link>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AjaxErrorHandler from '../../assets/js/errorHandler'
|
||||
|
||||
export default {
|
||||
name: 'user',
|
||||
data () {
|
||||
return {
|
||||
menuItems: [
|
||||
/* { name: 'Wall', route: 'wall' }, */
|
||||
{ name: 'Posts', route: 'posts' },
|
||||
{ name: 'Threads', route: 'threads' },
|
||||
/* { name: 'Friends', route: 'friends' } */
|
||||
],
|
||||
selected: 0,
|
||||
|
||||
username: this.$route.params.username,
|
||||
user: {
|
||||
username: "Loading",
|
||||
description: "Loading",
|
||||
createdAt: "2020-01-01T00:00:00.000Z"
|
||||
},
|
||||
relationship: false,
|
||||
relationships: {
|
||||
type: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route (to) {
|
||||
this.selected = this.getIndexFromRoute(to.path)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
userColor () {
|
||||
if(this.user) {
|
||||
return this.user.color
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
userPicture () {
|
||||
if(this.user && this.user.picture) {
|
||||
return 'https://cdn.kaverti.com/user/avatars/full/' + this.user.picture + '.png'
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetFetchData () {
|
||||
this.offset = 0;
|
||||
this.users = [];
|
||||
|
||||
this.fetchData();
|
||||
},
|
||||
fetchData () {
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `user/${this.$route.params.username}`)
|
||||
.then(res => this.user = res.data)
|
||||
.catch(e => {
|
||||
let invalidId = e.response.data.errors.find(error => {
|
||||
return error.name === 'accountDoesNotExist'
|
||||
})
|
||||
|
||||
if(invalidId) {
|
||||
this.$store.commit('set404Page', true)
|
||||
} else {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
}
|
||||
})
|
||||
},
|
||||
scrubDesc () {
|
||||
this.axios
|
||||
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/user/scrub', {
|
||||
description: "descscram",
|
||||
user: this.username
|
||||
})
|
||||
.then(() => {
|
||||
this.resetFetchData()
|
||||
})
|
||||
.catch(AjaxErrorHandler(this.$store))
|
||||
},
|
||||
refreshAvatar () {
|
||||
this.axios
|
||||
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/user/avatar', {
|
||||
user: this.username
|
||||
})
|
||||
.then(() => {
|
||||
this.resetFetchData()
|
||||
})
|
||||
.catch(AjaxErrorHandler(this.$store))
|
||||
},
|
||||
scrubUsername () {
|
||||
this.axios
|
||||
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'admin/user/scrub', {
|
||||
username: "usernamescram",
|
||||
user: this.username
|
||||
})
|
||||
.then(() => {
|
||||
this.description.loading = false
|
||||
})
|
||||
.catch(AjaxErrorHandler(this.$store))
|
||||
},
|
||||
refreshFriend() {
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `relationships/get/${this.$route.params.username}`)
|
||||
.then(res => this.relationship = res.data)
|
||||
},
|
||||
removeFriend () {
|
||||
this.axios
|
||||
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'relationships/remove', {
|
||||
friend: this.$route.params.username
|
||||
})
|
||||
.then(() => {
|
||||
this.refreshFriend()
|
||||
this.description.loading = false
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `relationships/get/${this.$route.params.username}`)
|
||||
.then(res => this.relationship = res.data, this.refreshFriend())
|
||||
.catch(e => {
|
||||
this.refreshFriend()
|
||||
let invalidId = e.response.data.errors.find(error => {
|
||||
return error.name === 'accountDoesNotExist'
|
||||
})
|
||||
|
||||
if(invalidId) {
|
||||
this.$store.commit('set404Page', true)
|
||||
} else {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(e => {
|
||||
this.refreshFriend()
|
||||
this.description.loading = false
|
||||
|
||||
AjaxErrorHandler(this.$store)(e, error => {
|
||||
this.description.error = error.message
|
||||
})
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `relationships/get/${this.$route.params.username}`)
|
||||
.then(res => this.relationship = res.data, this.refreshFriend())
|
||||
.catch(e => {
|
||||
this.refreshFriend()
|
||||
let invalidId = e.response.data.errors.find(error => {
|
||||
return error.name === 'accountDoesNotExist'
|
||||
})
|
||||
|
||||
if(invalidId) {
|
||||
this.$store.commit('set404Page', true)
|
||||
} else {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
doRelationship () {
|
||||
this.axios
|
||||
.post(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'relationships/send', {
|
||||
friend: this.$route.params.username
|
||||
})
|
||||
.then(() => {
|
||||
this.refreshFriend()
|
||||
this.description.loading = false
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `relationships/get/${this.$route.params.username}`)
|
||||
.then(res => this.relationship = res.data)
|
||||
.catch(e => {
|
||||
let invalidId = e.response.data.errors.find(error => {
|
||||
return error.name === 'accountDoesNotExist'
|
||||
})
|
||||
|
||||
if(invalidId) {
|
||||
this.$store.commit('set404Page', true)
|
||||
} else {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(e => {
|
||||
this.refreshFriend()
|
||||
this.description.loading = false
|
||||
|
||||
AjaxErrorHandler(this.$store)(e, error => {
|
||||
this.description.error = error.message
|
||||
})
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `relationships/get/${this.$route.params.username}`)
|
||||
.then(res => this.relationship = res.data)
|
||||
.catch(e => {
|
||||
let invalidId = e.response.data.errors.find(error => {
|
||||
return error.name === 'accountDoesNotExist'
|
||||
})
|
||||
|
||||
if(invalidId) {
|
||||
this.$store.commit('set404Page', true)
|
||||
} else {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
doRelationshipAccept () {
|
||||
this.axios
|
||||
.put(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + '/' + 'relationships/accept', {
|
||||
friend: this.$route.params.username
|
||||
})
|
||||
.then(() => {
|
||||
this.refreshFriend()
|
||||
this.description.loading = false
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `relationships/get/${this.$route.params.username}`)
|
||||
.then(res => this.relationship = res.data)
|
||||
.catch(e => {
|
||||
let invalidId = e.response.data.errors.find(error => {
|
||||
return error.name === 'accountDoesNotExist'
|
||||
})
|
||||
|
||||
if(invalidId) {
|
||||
this.$store.commit('set404Page', true)
|
||||
} else {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(e => {
|
||||
this.refreshFriend()
|
||||
this.description.loading = false
|
||||
|
||||
AjaxErrorHandler(this.$store)(e, error => {
|
||||
this.description.error = error.message
|
||||
})
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `relationships/get/${this.$route.params.username}`)
|
||||
.then(res => this.relationship = res.data)
|
||||
.catch(e => {
|
||||
let invalidId = e.response.data.errors.find(error => {
|
||||
return error.name === 'accountDoesNotExist'
|
||||
})
|
||||
|
||||
if(invalidId) {
|
||||
this.$store.commit('set404Page', true)
|
||||
} else {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
getIndexFromRoute (path) {
|
||||
let selectedIndex
|
||||
let route = path.split('/')[3]
|
||||
|
||||
this.menuItems.forEach((item, index) => {
|
||||
if(item.route === route) {
|
||||
selectedIndex = index
|
||||
}
|
||||
})
|
||||
|
||||
return selectedIndex
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.resetFetchData()
|
||||
this.selected = this.getIndexFromRoute(this.$route.path)
|
||||
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `user/${this.$route.params.username}`)
|
||||
.then(res => this.user = res.data)
|
||||
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `relationships/get/${this.$route.params.username}`)
|
||||
.then(res => this.relationship = res.data)
|
||||
.catch(e => {
|
||||
let invalidId = e.response.data.errors.find(error => {
|
||||
return error.name === 'accountDoesNotExist'
|
||||
})
|
||||
|
||||
if(invalidId) {
|
||||
this.$store.commit('set404Page', true)
|
||||
} else {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
3
src/views/UserMarketplace.vue
Normal file
3
src/views/UserMarketplace.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<main>TEST2</main>
|
||||
</template>
|
102
src/views/UserPosts.vue
Normal file
102
src/views/UserPosts.vue
Normal file
|
@ -0,0 +1,102 @@
|
|||
<template>
|
||||
<div class='' :class='{ "user_posts--no_border_bottom": posts && !posts.length }'>
|
||||
<div class='user_posts__title'>Posts by {{username}}</div>
|
||||
|
||||
<template v-if='!posts'>
|
||||
<thread-post-placeholder v-if='!posts'>
|
||||
</thread-post-placeholder>
|
||||
</template>
|
||||
|
||||
<scroll-load
|
||||
:loading='loadingPosts'
|
||||
@loadNext='loadNewPosts'
|
||||
v-else-if='posts.length'
|
||||
>
|
||||
<thread-post
|
||||
v-for='(post, index) in posts'
|
||||
:key='"thread-post-" + post.id'
|
||||
|
||||
:post='post'
|
||||
:show-thread='true'
|
||||
:click-for-post='true'
|
||||
:class='{"post--last": index === posts.length-1}'
|
||||
></thread-post>
|
||||
<template v-if='loadingPosts'>
|
||||
<b-skeleton
|
||||
></b-skeleton>
|
||||
</template>
|
||||
</scroll-load>
|
||||
<template v-else>This user hasn't posted anything yet</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AjaxErrorHandler from '../../assets/js/errorHandler'
|
||||
import ThreadPost from '../components/Post'
|
||||
|
||||
export default {
|
||||
name: 'UserPosts',
|
||||
props: ['username'],
|
||||
components: {
|
||||
ThreadPost
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
posts: null,
|
||||
loadingPosts: false,
|
||||
nextPostsCount: 0,
|
||||
nextURL: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadNewPosts () {
|
||||
if(this.nextURL === null) return
|
||||
|
||||
this.loadingPosts = true
|
||||
|
||||
this.axios
|
||||
.get(this.nextURL)
|
||||
.then(res => {
|
||||
this.loadingPosts = false
|
||||
|
||||
if(!this.posts) this.posts = []
|
||||
|
||||
let currentPostsIds = this.posts.map(p => p.id)
|
||||
let filteredPosts =
|
||||
res.data.Posts.filter(p => !currentPostsIds.includes(p.id))
|
||||
|
||||
this.posts.push(...filteredPosts)
|
||||
this.nextURL = res.data.meta.nextURL
|
||||
this.nextPostsCount = res.data.meta.nextPostsCount
|
||||
})
|
||||
.catch((e) => {
|
||||
this.loadingPosts = false
|
||||
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
})
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('setTitle', this.$route.params.username + ' - Posts')
|
||||
|
||||
this.axios
|
||||
.get(process.env.VUE_APP_APIENDPOINT + process.env.VUE_APP_APIVERSION + `/` + `user/${this.$route.params.username}?posts=true`)
|
||||
.then(res => {
|
||||
this.posts = res.data.Posts
|
||||
this.nextURL = res.data.meta.nextURL
|
||||
this.nextPostsCount = res.data.meta.nextPostsCount
|
||||
})
|
||||
.catch(e => {
|
||||
let invalidId = e.response.data.errors.find(error => {
|
||||
return error.name === 'accountDoesNotExist'
|
||||
})
|
||||
|
||||
if(invalidId) {
|
||||
this.$store.commit('set404Page', true)
|
||||
} else {
|
||||
AjaxErrorHandler(this.$store)(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
3
src/views/UserThreads.vue
Normal file
3
src/views/UserThreads.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<main>TEST</main>
|
||||
</template>
|
3
src/views/UserWall.vue
Normal file
3
src/views/UserWall.vue
Normal file
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<main>TEST</main>
|
||||
</template>
|
5
src/views/Users.vue
Normal file
5
src/views/Users.vue
Normal file
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<main>
|
||||
|
||||
</main>
|
||||
</template>
|
Loading…
Reference in a new issue