backend update

This commit is contained in:
carodej
2020-06-09 18:06:04 +02:00
parent 0b7bc27a5f
commit 95bc310def
48 changed files with 1013 additions and 657 deletions
@@ -61,6 +61,14 @@
if (args.emoji) {
this.emoji = args.emoji
}
if (args.buttonStyle) {
this.buttonStyle = args.buttonStyle
}
if (args.button) {
this.button = args.button
}
})
// Show alert
@@ -274,7 +274,10 @@
EyeIcon,
},
computed: {
...mapGetters(['app']),
...mapGetters(['user']),
favourites() {
return this.user.relationships.favourites.data.attributes.folders
},
isFolder() {
return this.item && this.item.type === 'folder'
},
@@ -285,7 +288,7 @@
return this.item && this.item.type === 'image'
},
isInFavourites() {
return this.app.favourites.find(el => el.unique_id == this.item.unique_id)
return this.favourites.find(el => el.unique_id == this.item.unique_id)
},
},
data() {
@@ -312,7 +315,7 @@
},
addToFavourites() {
// Check if folder is in favourites and then add/remove from favourites
if (this.app.favourites && !this.app.favourites.find(el => el.unique_id == this.item.unique_id)) {
if (this.favourites && !this.favourites.find(el => el.unique_id == this.item.unique_id)) {
this.$store.dispatch('addToFavourites', this.item)
} else {
this.$store.dispatch('removeFromFavourites', this.item)
@@ -27,7 +27,7 @@
<!--Files controlls-->
<div class="toolbar-button-wrapper" v-if="$checkPermission(['master', 'editor'])">
<ToolbarButtonUpload
:class="{'is-inactive': canUploadInView}"
:class="{'is-inactive': canUploadInView || ! hasCapacity}"
:action="$t('actions.upload')"
/>
<ToolbarButton
@@ -109,6 +109,9 @@
'browseHistory',
'homeDirectory',
]),
hasCapacity() {
return this.$store.getters.user.relationships.storage.data.attributes.used <= 100
},
directoryName() {
return this.currentFolder ? this.currentFolder.name : this.homeDirectory.name
},
@@ -288,6 +288,7 @@
bottom: 0;
@include transition;
overflow-y: auto;
overflow-x: hidden;
&.is-offset {
margin-top: 50px;
@@ -1,7 +1,7 @@
<template>
<div class="file-info-content" v-if="fileInfoDetail">
<div class="file-headline" spellcheck="false">
<FilePreview />
<FilePreview/>
<!--File info-->
<div class="flex">
@@ -14,45 +14,38 @@
</div>
</div>
<div class="file-info">
<span ref="name" class="name">{{ fileInfoDetail.name }}</span>
<span ref="name" class="name">{{ fileInfoDetail.name }}</span>
<span class="mimetype" v-if="fileInfoDetail.mimetype">.{{ fileInfoDetail.mimetype }}</span>
</div>
</div>
</div>
<!--Info list-->
<ul class="list-info">
<ListInfo>
<ListInfoItem v-if="fileInfoDetail.filesize"
:title="$t('file_detail.size')"
:content="fileInfoDetail.filesize">
</ListInfoItem>
<!--Filesize-->
<li v-if="fileInfoDetail.filesize" class="list-info-item">
<b>{{ $t('file_detail.size') }}</b>
<span>{{ fileInfoDetail.filesize }}</span>
</li>
<ListInfoItem v-if="$checkPermission(['master']) && fileInfoDetail.user_scope !== 'master'"
:title="$t('file_detail.author')"
:content="$t('file_detail.author_participant')">
</ListInfoItem>
<!--Latest change-->
<li v-if="$checkPermission(['master']) && fileInfoDetail.user_scope !== 'master'" class="list-info-item">
<b>{{ $t('file_detail.author') }}</b>
<span>{{ $t('file_detail.author_participant') }}</span>
</li>
<ListInfoItem
:title="$t('file_detail.created_at')"
:content="fileInfoDetail.created_at">
</ListInfoItem>
<!--Latest change-->
<li class="list-info-item">
<b>{{ $t('file_detail.created_at') }}</b>
<span>{{ fileInfoDetail.created_at }}</span>
</li>
<!--Parent-->
<li v-if="$checkPermission(['master'])" class="list-info-item">
<b>{{ $t('file_detail.where') }}</b>
<ListInfoItem v-if="$checkPermission(['master'])"
:title="$t('file_detail.where')">
<div class="action-button" @click="moveItem">
<span>{{ fileInfoDetail.parent ? fileInfoDetail.parent.name : $t('locations.home') }}</span>
<edit-2-icon size="10" class="edit-icon"></edit-2-icon>
</div>
</li>
<!--Parent-->
<li v-if="$checkPermission('master') && fileInfoDetail.shared" class="list-info-item">
<b>{{ $t('file_detail.shared') }}</b>
</ListInfoItem>
<ListInfoItem v-if="$checkPermission('master') && fileInfoDetail.shared"
:title="$t('file_detail.shared')">
<div class="action-button" @click="shareItemOptions">
<span>{{ sharedInfo }}</span>
<edit-2-icon size="10" class="edit-icon"></edit-2-icon>
@@ -60,24 +53,27 @@
<div class="sharelink">
<lock-icon v-if="isLocked" @click="shareItemOptions" class="lock-icon" size="17"></lock-icon>
<unlock-icon v-if="! isLocked" @click="shareItemOptions" class="lock-icon" size="17"></unlock-icon>
<CopyInput class="copy-sharelink" size="small" :value="fileInfoDetail.shared.link" />
<CopyInput class="copy-sharelink" size="small" :value="fileInfoDetail.shared.link"/>
</div>
</li>
</ul>
</ListInfoItem>
</ListInfo>
</div>
</template>
<script>
import { Edit2Icon, LockIcon, UnlockIcon, ImageIcon, VideoIcon, FolderIcon, FileIcon } from 'vue-feather-icons'
import {Edit2Icon, LockIcon, UnlockIcon, ImageIcon, VideoIcon, FolderIcon, FileIcon} from 'vue-feather-icons'
import FilePreview from '@/components/FilesView/FilePreview'
import CopyInput from '@/components/Others/Forms/CopyInput'
import ListInfoItem from '@/components/Others/ListInfoItem'
import ListInfo from '@/components/Others/ListInfo'
import {mapGetters} from 'vuex'
import {events} from "@/bus"
export default {
name: 'FileInfoPanel',
components: {
ListInfoItem,
ListInfo,
FilePreview,
FolderIcon,
UnlockIcon,
@@ -92,23 +88,23 @@
...mapGetters(['fileInfoDetail', 'permissionOptions']),
fileType() {
return this.fileInfoDetail.type
/* switch () {
case 'folder':
return 'folder'
break;
case 'file':
return 'file'
break;
case 'image':
return 'file-image'
break;
case 'video':
return 'file-video'
break;
case 'file':
return 'file-audio'
break;
}*/
/* switch () {
case 'folder':
return 'folder'
break;
case 'file':
return 'file'
break;
case 'image':
return 'file-image'
break;
case 'video':
return 'file-video'
break;
case 'file':
return 'file-audio'
break;
}*/
},
sharedInfo() {
@@ -123,10 +119,10 @@
switch (this.fileInfoDetail.shared.permission) {
case 'editor':
return 'user-edit'
break
break
case 'visitor':
return 'user'
break
break
default:
return 'download'
}
@@ -199,41 +195,6 @@
}
}
.list-info {
.list-info-item {
display: block;
padding-top: 20px;
&:first-child {
padding-top: 0;
}
.action-button {
cursor: pointer;
.edit-icon {
display: inline-block;
margin-left: 3px;
}
}
b {
display: block;
@include font-size(13);
color: $theme;
margin-bottom: 2px;
}
span {
display: inline-block;
@include font-size(14);
font-weight: bold;
color: $text;
}
}
}
.sharelink {
display: flex;
width: 100%;
@@ -264,23 +225,6 @@
}
}
.list-info {
.list-info-item {
span {
color: $dark_mode_text_primary
}
.action-button {
.icon {
color: $dark_mode_text_primary;
}
}
}
}
.sharelink {
.lock-icon {
@@ -258,9 +258,12 @@
EyeIcon,
},
computed: {
...mapGetters(['fileInfoDetail', 'app']),
...mapGetters(['fileInfoDetail', 'user']),
favourites() {
return this.user.relationships.favourites.data.attributes.folders
},
isInFavourites() {
return this.app.favourites.find(el => el.unique_id == this.fileInfoDetail.unique_id)
return this.favourites.find(el => el.unique_id == this.fileInfoDetail.unique_id)
},
isFile() {
return (this.fileInfoDetail && this.fileInfoDetail.type !== 'folder') && (this.fileInfoDetail && this.fileInfoDetail.type !== 'image')
@@ -292,7 +295,7 @@
}
},
addToFavourites() {
if (this.app.favourites && !this.app.favourites.find(el => el.unique_id == this.fileInfoDetail.unique_id)) {
if (this.favourites && !this.favourites.find(el => el.unique_id == this.fileInfoDetail.unique_id)) {
this.$store.dispatch('addToFavourites', this.fileInfoDetail)
} else {
this.$store.dispatch('removeFromFavourites', this.fileInfoDetail)
@@ -18,7 +18,7 @@ export default {
.progress-bar {
width: 100%;
height: 5px;
background: $dark_background;
background: $light_background;
margin-top: 5px;
border-radius: 10px;
@@ -115,7 +115,7 @@
}
.input-area {
border: 1px solid #ebebeb;
border: 1px solid transparent;
justify-content: space-between;
background: $light_mode_input_background;
@include transition(150ms);
@@ -20,11 +20,13 @@
.setup-box {
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
.title {
@include font-size(19);
margin-bottom: 15px;
@include font-size(21);
margin-bottom: 5px;
display: block;
font-weight: 700;
}
.description {
@@ -0,0 +1,16 @@
<template>
<ul class="list-info">
<slot></slot>
</ul>
</template>
<script>
export default {
name: 'ListInfo',
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
</style>
@@ -0,0 +1,67 @@
<template>
<li class="list-info-item">
<b>{{ title }}</b>
<span v-if="content">{{ content }}</span>
<slot></slot>
</li>
</template>
<script>
export default {
name: 'ListInfoItem',
props: ['title', 'content']
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.list-info-item {
display: block;
padding-bottom: 20px;
&:first-child {
padding-bottom: 0;
}
.action-button {
cursor: pointer;
.edit-icon {
display: inline-block;
margin-left: 3px;
}
}
b {
display: block;
@include font-size(13);
color: $theme;
margin-bottom: 2px;
}
span {
display: inline-block;
@include font-size(14);
font-weight: bold;
color: $text;
}
}
@media (prefers-color-scheme: dark) {
.list-info-item {
span {
color: $dark_mode_text_primary
}
.action-button {
.icon {
color: $dark_mode_text_primary;
}
}
}
}
</style>
@@ -1,5 +1,5 @@
<template>
<div class="mobile-main-navigation" v-if="app">
<div class="mobile-main-navigation" v-if="user">
<transition name="context-menu">
<nav v-if="isVisible" class="mobile-navigation">
@@ -34,7 +34,7 @@
UserAvatar,
},
computed: {
...mapGetters(['app', 'homeDirectory']),
...mapGetters(['user', 'homeDirectory']),
navigation() {
return [
{
@@ -71,7 +71,7 @@
icon: 'settings',
title: this.$t('menu.admin'),
routeName: 'AdminMobileMenu',
isVisible: this.app.user.role === 'admin',
isVisible: this.user.data.attributes.role === 'admin',
},
{
icon: 'power',
@@ -49,9 +49,15 @@
}
},
created() {
axios.get('/api/public/pricing')
.then(response => {
this.plans = response.data.data
this.plans = response.data.data.filter(plan => {
if (this.$store.getters.user.relationships.subscription)
return plan.data.attributes.capacity > this.$store.getters.user.relationships.subscription.data.attributes.capacity
return true
})
this.$emit('load', false)
})
}
@@ -133,6 +139,7 @@
display: flex;
flex-wrap: wrap;
margin: 0 -25px;
justify-content: center;
}
@media only screen and (max-width: 1024px) {
@@ -97,7 +97,7 @@
required,
},
computed: {
...mapGetters(['app', 'permissionOptions']),
...mapGetters(['permissionOptions']),
itemTypeTitle() {
return this.pickedItem && this.pickedItem.type === 'folder' ? this.$t('types.folder') : this.$t('types.file')
},
+1 -1
View File
@@ -101,7 +101,7 @@
required,
},
computed: {
...mapGetters(['app', 'permissionOptions', 'currentFolder']),
...mapGetters(['user', 'permissionOptions', 'currentFolder']),
isFolder() {
return this.pickedItem && this.pickedItem.type === 'folder'
},
@@ -8,6 +8,7 @@
@click="sort(column.field, column.sortable, index)"
:key="index"
:class="{ sortable: column.sortable }"
v-if="! column.hidden"
>
<span>{{ column.label }}</span>
@@ -0,0 +1,98 @@
<template>
<div class="upgrade-banner">
<div class="header-title">
<hard-drive-icon size="19" class="icon"></hard-drive-icon>
<span class="title">{{ storage.used }}% From {{ storage.capacity_formatted }}</span>
</div>
<div class="content">
<p v-if="storage.used > 95" class="reach-capacity">You reach your storage capacity. Please upgrade.</p>
<p v-else class="reach-capacity">You nearly reach your storage capacity.</p>
</div>
<div class="footer">
<router-link :to="{name: 'UpgradePlan'}" class="button">
Upgrade
</router-link>
</div>
</div>
</template>
<script>
import ButtonBase from '@/components/FilesView/ButtonBase'
import { HardDriveIcon } from 'vue-feather-icons'
export default {
name: 'UpgradeSidebarBanner',
components: {
HardDriveIcon,
ButtonBase,
},
computed: {
storage() {
return this.$store.getters.user.relationships.storage.data.attributes
}
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.upgrade-banner {
background: rgba($danger, 0.1);
padding: 10px;
border-radius: 6px;
margin: 0 16px;
}
.header-title {
margin-bottom: 12px;
display: flex;
align-items: center;
.icon {
margin-right: 10px;
line, path {
stroke: $danger;
}
}
.title {
@include font-size(13);
font-weight: 800;
color: $danger;
}
}
.content {
margin-bottom: 12px;
p {
@include font-size(12);
color: $danger;
font-weight: 700;
}
}
.button {
background: $danger;
border-radius: 50px;
padding: 6px 0;
width: 100%;
color: white;
text-align: center;
@include font-size(12);
font-weight: 700;
display: block;
box-shadow: 0 4px 10px rgba($danger, 0.35);
}
@media only screen and (max-width: 1024px) {
}
@media (prefers-color-scheme: dark) {
}
</style>
@@ -1,6 +1,6 @@
<template>
<div class="user-avatar" :class="size">
<img :src="app.user.avatar" :alt="app.user.name">
<img :src="user.data.attributes.avatar" :alt="user.data.attributes.name">
</div>
</template>
@@ -13,7 +13,7 @@
'size'
],
computed: {
...mapGetters(['app']),
...mapGetters(['user']),
},
}
</script>
+25 -15
View File
@@ -1,5 +1,5 @@
<template>
<nav class="menu-bar" v-if="app">
<nav class="menu-bar" v-if="user">
<!--Navigation Icons-->
<div class="icon-navigation menu">
@@ -26,13 +26,13 @@
</div>
</router-link>
<router-link :to="{name: 'Profile'}" :class="{'is-active': $isThisRoute($route, ['Password', 'Profile', 'Storage'])}" class="icon-navigation-item settings">
<router-link :to="{name: 'Profile'}" :class="{'is-active': isUserProfileRoute}" class="icon-navigation-item settings">
<div class="button-icon">
<user-icon size="19"></user-icon>
</div>
</router-link>
<router-link v-if="app.user.role === 'admin'" :to="{name: 'Users'}" :class="{'is-active': $isThisRoute($route, adminRoutes)}" class="icon-navigation-item users">
<router-link v-if="user.data.attributes.role === 'admin'" :to="{name: 'Users'}" :class="{'is-active': $isThisRoute($route, adminRoutes)}" class="icon-navigation-item users">
<div class="button-icon">
<settings-icon size="19"></settings-icon>
</div>
@@ -74,24 +74,34 @@
UserIcon,
},
computed: {
...mapGetters(['app']),
...mapGetters(['user']),
isUserProfileRoute() {
return this.$isThisRoute(this.$route, ['Profile', 'Password', 'Storage', 'Invoice', 'Subscription'])
}
},
data() {
return {
adminRoutes: [
'PlanSubscribers',
'PlanSettings',
'PlanDelete',
'GatewayTransactions',
'GatewaySettings',
'UserInvoices',
'UserInvoices',
'UserPassword',
'UserStorage',
'PlanCreate',
'UserCreate',
'UserDelete',
'UserDetail',
'Invoices',
'Gateways',
'Gateway',
'Plans',
'Users',
'User',
'UserDetail',
'UserStorage',
'UserPassword',
'UserDelete',
'Plans',
'Invoices',
'UserInvoices',
'Gateway',
'Gateways',
'GatewaySettings',
'GatewayTransactions',
]
}
},
@@ -1,7 +1,7 @@
<template>
<div class="user-meta" v-if="app">
<b class="name">{{ app.user.name }}</b>
<span class="email">{{ app.user.email }}</span>
<b class="name">{{ user.data.attributes.name }}</b>
<span class="email">{{ user.data.attributes.email }}</span>
</div>
</template>
@@ -12,7 +12,7 @@
export default {
name: 'UserHeadline',
computed: {
...mapGetters(['app']),
...mapGetters(['user']),
},
}
</script>
+10
View File
@@ -51,6 +51,7 @@ import UserDelete from './views/Admin/Users/UserTabs/UserDelete'
import UserStorage from './views/Admin/Users/UserTabs/UserStorage'
import UserPassword from './views/Admin/Users/UserTabs/UserPassword'
import UserInvoices from './views/Admin/Users/UserTabs/UserInvoices'
import UserSubscription from './views/Admin/Users/UserTabs/UserSubscription'
Vue.use(Router)
@@ -151,6 +152,15 @@ const routesAdmin = [
title: i18n.t('routes_title.users_storage_usage')
},
},
{
name: 'UserSubscription',
path: '/admin/user/:id/subscription',
component: UserSubscription,
meta: {
requiresAuth: true,
title: 'Subscription'
},
},
{
name: 'UserInvoices',
path: '/admin/user/:id/invoices',
+10 -17
View File
@@ -6,7 +6,7 @@ import router from '@/router'
const defaultState = {
authorized: undefined,
permission: 'master', // master | editor | visitor
app: undefined,
user: undefined,
}
const actions = {
@@ -15,7 +15,7 @@ const actions = {
axios
.get(getters.api + '/user')
.then((response) => {
commit('RETRIEVE_APP_DATA', response.data)
commit('RETRIEVE_USER', response.data)
}).catch((error) => {
@@ -74,8 +74,8 @@ const actions = {
}
const mutations = {
RETRIEVE_APP_DATA(state, app) {
state.app = app
RETRIEVE_USER(state, user) {
state.user = user
},
SET_PERMISSION(state, role) {
state.permission = role
@@ -85,30 +85,23 @@ const mutations = {
state.app = undefined
},
ADD_TO_FAVOURITES(state, folder) {
state.app.favourites.push({
state.user.relationships.favourites.data.attributes.folders.push({
unique_id: folder.unique_id,
name: folder.name,
type: folder.type,
})
},
UPDATE_NAME(state, name) {
state.app.user.name = name
state.user.data.attributes.name = name
},
UPDATE_AVATAR(state, avatar) {
state.app.user.avatar = avatar
},
UPDATE_RECENT_UPLOAD(state, file) {
// Remove last file from altest uploads
if (state.app.latest_uploads.length === 7) state.app.latest_uploads.pop()
// Add new file to latest uploads
state.app.latest_uploads.unshift(file)
state.user.data.attributes.avatar = avatar
},
REMOVE_ITEM_FROM_FAVOURITES(state, item) {
state.app.favourites = state.app.favourites.filter(folder => folder.unique_id !== item.unique_id)
state.user.relationships.favourites.data.attributes.folders = state.user.relationships.favourites.data.attributes.folders.filter(folder => folder.unique_id !== item.unique_id)
},
UPDATE_NAME_IN_FAVOURITES(state, data) {
state.app.favourites.find(folder => {
state.user.relationships.favourites.data.attributes.folders.find(folder => {
if (folder.unique_id == data.unique_id) folder.name = data.name
})
}
@@ -118,7 +111,7 @@ const getters = {
permission: state => state.permission,
isGuest: state => ! state.authorized,
isLogged: state => state.authorized,
app: state => state.app,
user: state => state.user,
}
export default {
+2 -1
View File
@@ -2,8 +2,9 @@
<section id="viewport">
<ContentSidebar>
<!--SaaS-->
<ContentGroup title="SaaS" class="navigator">
<ContentGroup v-if="config.isSaaS" title="SaaS" class="navigator">
<div class="menu-list-wrapper vertical">
<router-link :to="{name: 'Plans'}" class="menu-list-item link">
<div class="icon">
+10 -10
View File
@@ -30,6 +30,11 @@
<SwitchInput @input="changeStatus($event, row.data.id)" class="switch" :state="row.data.attributes.status"/>
</span>
</td>
<td>
<span class="cell-item">
{{ row.data.attributes.subscribers }}
</span>
</td>
<td>
<span class="cell-item">
${{ row.data.attributes.price }}
@@ -40,11 +45,6 @@
{{ row.data.attributes.capacity_formatted }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.data.attributes.subscribers }}
</span>
</td>
<td>
<div class="action-icons">
<router-link :to="{name: 'PlanSettings', params: {id: row.data.id}}">
@@ -109,6 +109,11 @@
field: 'attributes.status',
sortable: true
},
{
label: 'Subscribers',
field: 'attributes.subscribers',
sortable: true
},
{
label: 'Price',
field: 'attributes.price',
@@ -119,11 +124,6 @@
field: 'attributes.capacity',
sortable: true
},
{
label: 'Subscribers',
field: 'attributes.subscribers',
sortable: true
},
{
label: this.$t('admin_page_user.table.action'),
field: 'data.action',
+7 -2
View File
@@ -34,7 +34,7 @@
{{ row.data.attributes.role }}
</ColorLabel>
</td>
<td>
<td v-if="config.isSaaS">
<span class="cell-item" v-if="row.relationships.subscription">
{{ row.relationships.subscription.data.attributes.name }}
</span>
@@ -89,6 +89,7 @@
import PageHeader from '@/components/Others/PageHeader'
import ColorLabel from '@/components/Others/ColorLabel'
import Spinner from '@/components/FilesView/Spinner'
import {mapGetters} from "vuex"
import axios from 'axios'
export default {
@@ -106,6 +107,9 @@
Edit2Icon,
Spinner,
},
computed: {
...mapGetters(['config']),
},
data() {
return {
isLoading: true,
@@ -124,7 +128,8 @@
{
label: 'Subscription Plan',
field: 'data.attributes.role',
sortable: true
sortable: true,
hidden: true,
},
{
label: this.$t('admin_page_user.table.storage_used'),
+18 -5
View File
@@ -1,5 +1,5 @@
<template>
<div id="single-page" v-if="app">
<div id="single-page">
<div id="page-content" v-if="! isLoading">
<MobileHeader :title="$router.currentRoute.meta.title"/>
<PageHeader :can-back="true" :title="$router.currentRoute.meta.title"/>
@@ -42,7 +42,16 @@
</div>
</router-link>
<router-link replace :to="{name: 'UserInvoices'}" class="menu-list-item link">
<router-link v-if="config.isSaaS" replace :to="{name: 'UserSubscription'}" class="menu-list-item link">
<div class="icon">
<credit-card-icon size="17"></credit-card-icon>
</div>
<div class="label">
Subscription
</div>
</router-link>
<router-link v-if="config.isSaaS" replace :to="{name: 'UserInvoices'}" class="menu-list-item link">
<div class="icon">
<file-text-icon size="17"></file-text-icon>
</div>
@@ -60,7 +69,7 @@
</div>
</router-link>
<router-link replace :to="{name: 'UserDelete'}" v-if="user.data.attributes.name !== app.user.name"
<router-link replace :to="{name: 'UserDelete'}" v-if="user.data.attributes.name !== admin.name"
class="menu-list-item link">
<div class="icon">
<trash2-icon size="17"></trash2-icon>
@@ -82,7 +91,7 @@
</template>
<script>
import {UserIcon, HardDriveIcon, LockIcon, Trash2Icon, FileTextIcon} from 'vue-feather-icons'
import {UserIcon, HardDriveIcon, LockIcon, Trash2Icon, FileTextIcon, CreditCardIcon} from 'vue-feather-icons'
import StorageItemDetail from '@/components/Others/StorageItemDetail'
import MobileHeader from '@/components/Mobile/MobileHeader'
import SectionTitle from '@/components/Others/SectionTitle'
@@ -95,6 +104,7 @@
export default {
name: 'Profile',
components: {
CreditCardIcon,
HardDriveIcon,
StorageItemDetail,
SectionTitle,
@@ -108,7 +118,10 @@
Spinner,
},
computed: {
...mapGetters(['app']),
admin() {
return this.$store.getters.user ? this.$store.getters.user.data.attributes : undefined
},
...mapGetters(['config']),
},
data() {
return {
@@ -1,6 +1,6 @@
<template>
<PageTab v-if="invoices">
<PageTabGroup>
<PageTabGroup v-if="invoices.length > 0">
<DatatableWrapper :paginator="true" :columns="columns" :data="invoices" class="table">
<template scope="{ row }">
<tr>
@@ -35,6 +35,9 @@
</template>
</DatatableWrapper>
</PageTabGroup>
<PageTabGroup v-else>
User don't have any invoices yet.
</PageTabGroup>
</PageTab>
</template>
@@ -1,13 +1,8 @@
<template>
<PageTab v-if="storage">
<PageTabGroup>
<StorageItemDetail
type="disk"
:title="$t('storage.total_used', {used: storage.attributes.used})"
:percentage="storage.attributes.percentage"
:used="$t('storage.total_capacity', {capacity: storage.attributes.capacity})"
/>
<SetupBox
v-if="! config.isSaaS || ! user.relationships.subscription"
theme="base"
:title="$t('user_box_storage.title')"
:description="$t('user_box_storage.description')"
@@ -32,6 +27,12 @@
</ValidationProvider>
</ValidationObserver>
</SetupBox>
<StorageItemDetail
type="disk"
:title="$t('storage.total_used', {used: storage.attributes.used})"
:percentage="storage.attributes.percentage"
:used="$t('storage.total_capacity', {capacity: storage.attributes.capacity})"
/>
</PageTabGroup>
<PageTabGroup>
<b class="form-group-label">{{ $t('storage.sec_details') }}</b>
@@ -54,9 +55,11 @@
import {required} from 'vee-validate/dist/rules'
import {events} from "@/bus"
import axios from 'axios'
import {mapGetters} from "vuex";
export default {
name: 'UserStorage',
props: ['user'],
components: {
PageTabGroup,
PageTab,
@@ -67,6 +70,9 @@
SetupBox,
required,
},
computed: {
...mapGetters(['config']),
},
data() {
return {
isLoading: true,
@@ -0,0 +1,105 @@
<template>
<PageTab>
<PageTabGroup v-if="subscription">
<!--Info about active subscription-->
<div v-if="! subscription.canceled" class="state active">
<ListInfo class="list-info">
<ListInfoItem class="list-item" title="Plan" :content="subscription.attributes.name + ' - ' + subscription.attributes.capacity_formatted"/>
<ListInfoItem class="list-item" title="Billed" content="Monthly"/>
<ListInfoItem class="list-item" title="Status" :content="status"/>
<ListInfoItem class="list-item" title="Created At" :content="subscription.attributes.created_at"/>
<ListInfoItem class="list-item" title="Renews At" :content="subscription.attributes.ends_at"/>
</ListInfo>
</div>
<!--Info about canceled subscription-->
<div v-if="subscription.attributes.canceled" class="state canceled">
<ListInfo class="list-info">
<ListInfoItem class="list-item" title="Plan" :content="subscription.attributes.name"/>
<ListInfoItem class="list-item" title="Status" :content="status"/>
<ListInfoItem class="list-item" title="Canceled At" :content="subscription.attributes.canceled_at"/>
<ListInfoItem class="list-item" title="Ends At" :content="subscription.attributes.ends_at"/>
</ListInfo>
</div>
</PageTabGroup>
<PageTabGroup v-else>
User don't have any subscription yet.
</PageTabGroup>
</PageTab>
</template>
<script>
import DatatableWrapper from '@/components/Others/Tables/DatatableWrapper'
import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
import ListInfoItem from '@/components/Others/ListInfoItem'
import ButtonBase from '@/components/FilesView/ButtonBase'
import PageTab from '@/components/Others/Layout/PageTab'
import ListInfo from '@/components/Others/ListInfo'
import {ExternalLinkIcon} from "vue-feather-icons"
import { mapGetters } from 'vuex'
import {events} from "@/bus"
import axios from 'axios'
export default {
name: 'UserSubscription',
components: {
ExternalLinkIcon,
DatatableWrapper,
ListInfoItem,
PageTabGroup,
ButtonBase,
ListInfo,
PageTab,
},
computed: {
status() {
if (this.subscription.attributes.canceled) {
return 'Canceled'
}
if (this.subscription.attributes.active) {
return 'Active'
}
}
},
data() {
return {
subscription: undefined,
}
},
created() {
axios.get('/api/users/' + this.$route.params.id + '/subscription')
.then(response => {
this.subscription = response.data.data
this.isLoading = false
})
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.cancel-plan {
margin-top: 10px;
}
.list-info {
display: flex;
flex-wrap: wrap;
.list-item {
flex: 0 0 50%;
}
}
@media only screen and (max-width: 960px) {
}
@media (prefers-color-scheme: dark) {
}
</style>
+2
View File
@@ -88,6 +88,8 @@
checkedAccount: undefined,
loginPassword: 'vuefilemanager',
loginEmail: 'howdy@hi5ve.digital',
//loginPassword: '',
//loginEmail: '',
}
},
methods: {
+22 -7
View File
@@ -1,8 +1,12 @@
<template>
<section id="viewport" v-if="app">
<section id="viewport" v-if="user">
<ContentSidebar>
<ContentGroup v-if="config.isSaaS && storage.used > 95">
<UpgradeSidebarBanner />
</ContentGroup>
<!--Locations-->
<ContentGroup :title="$t('sidebar.locations_title')">
<div class="menu-list-wrapper vertical">
@@ -28,10 +32,10 @@
<!--Navigator-->
<ContentGroup :title="$t('sidebar.navigator_title')" class="navigator">
<span class="empty-note navigator" v-if="app.tree.length == 0">
<span class="empty-note navigator" v-if="tree.length == 0">
{{ $t('sidebar.folders_empty') }}
</span>
<TreeMenuNavigator class="folder-tree" :depth="0" :nodes="items" v-for="items in app.tree"
<TreeMenuNavigator class="folder-tree" :depth="0" :nodes="items" v-for="items in tree"
:key="items.unique_id"/>
</ContentGroup>
@@ -45,14 +49,14 @@
@drop="dragFinish($event)"
>
<transition-group tag="div" class="menu-list" name="folder-item">
<span class="empty-note favourites" v-if="app.favourites.length == 0" :key="0">
<span class="empty-note favourites" v-if="favourites.length == 0" :key="0">
{{ $t('sidebar.favourites_empty') }}
</span>
<a @click.stop="openFolder(folder)"
class="menu-list-item"
:class="{'is-current': (folder && currentFolder) && (currentFolder.unique_id === folder.unique_id)}"
v-for="folder in app.favourites"
v-for="folder in favourites"
:key="folder.unique_id">
<div>
<folder-icon size="17" class="folder-icon"></folder-icon>
@@ -70,6 +74,7 @@
</template>
<script>
import UpgradeSidebarBanner from '@/components/Others/UpgradeSidebarBanner'
import TreeMenuNavigator from '@/components/Others/TreeMenuNavigator'
import ContentFileView from '@/components/Others/ContentFileView'
import ContentSidebar from '@/components/Sidebar/ContentSidebar'
@@ -86,6 +91,7 @@
export default {
name: 'FilesView',
components: {
UpgradeSidebarBanner,
TreeMenuNavigator,
ContentFileView,
ContentSidebar,
@@ -96,7 +102,16 @@
XIcon,
},
computed: {
...mapGetters(['app', 'homeDirectory', 'currentFolder']),
...mapGetters(['user', 'homeDirectory', 'currentFolder', 'config']),
favourites() {
return this.user.relationships.favourites.data.attributes.folders
},
tree() {
return this.user.relationships.tree.data.attributes.folders
},
storage() {
return this.$store.getters.user.relationships.storage.data.attributes
}
},
data() {
return {
@@ -129,7 +144,7 @@
if (this.draggedItem && this.draggedItem.type !== 'folder') return
// Check if folder exist in favourites
if (this.app.favourites.find(folder => folder.unique_id == this.draggedItem.unique_id)) return
if (this.favourites.find(folder => folder.unique_id == this.draggedItem.unique_id)) return
// Store favourites folder
this.$store.dispatch('addToFavourites', this.draggedItem)
+14 -29
View File
@@ -1,5 +1,5 @@
<template>
<div id="single-page">
<div id="single-page" v-if="user">
<div id="page-content" class="medium-width" v-if="! isLoading">
<MobileHeader :title="$router.currentRoute.meta.title"/>
<PageHeader :title="$router.currentRoute.meta.title"/>
@@ -12,20 +12,20 @@
<div class="avatar">
<UserImageInput
v-model="avatar"
:avatar="profile.data.attributes.avatar"
:avatar="user.data.attributes.avatar"
/>
</div>
<div class="info">
<b class="name">
{{ profile.data.attributes.name }}
<ColorLabel :color="subscriptionColor">
{{ user.data.attributes.name }}
<ColorLabel v-if="config.isSaaS" :color="subscriptionColor">
{{ subscriptionStatus }}
</ColorLabel>
</b>
<span class="email">{{ profile.data.attributes.email }}</span>
<span class="email">{{ user.data.attributes.email }}</span>
</div>
</div>
<div class="headline-actions">
<div v-if="config.isSaaS" class="headline-actions">
<router-link :to="{name: 'UpgradePlan'}">
<ButtonBase button-style="secondary" type="button">
Upgrade Plan
@@ -54,7 +54,7 @@
</div>
</router-link>
<router-link replace :to="{name: 'Invoice'}" class="menu-list-item link">
<router-link v-if="config.isSaaS" replace :to="{name: 'Subscription'}" class="menu-list-item link">
<div class="icon">
<credit-card-icon size="17"></credit-card-icon>
</div>
@@ -63,7 +63,7 @@
</div>
</router-link>
<router-link replace :to="{name: 'Invoice'}" class="menu-list-item link">
<router-link v-if="config.isSaaS" replace :to="{name: 'Invoice'}" class="menu-list-item link">
<div class="icon">
<file-text-icon size="17"></file-text-icon>
</div>
@@ -80,19 +80,10 @@
{{ $t('menu.password') }}
</div>
</router-link>
<!--<router-link replace :to="{name: 'UserDelete'}" v-if="user.attributes.name !== app.user.name" class="menu-list-item link">
<div class="icon">
<trash2-icon size="17"></trash2-icon>
</div>
<div class="label">
{{ $t('admin_page_user.tabs.delete') }}
</div>
</router-link>-->
</div>
<!--Router Content-->
<router-view :user="profile" />
<router-view :user="user" />
</div>
</div>
<div id="loader" v-if="isLoading">
@@ -108,6 +99,7 @@
import PageHeader from '@/components/Others/PageHeader'
import ColorLabel from '@/components/Others/ColorLabel'
import Spinner from '@/components/FilesView/Spinner'
import { mapGetters } from 'vuex'
import axios from 'axios'
import {
CreditCardIcon,
@@ -133,26 +125,19 @@
LockIcon,
},
computed: {
...mapGetters(['user', 'config']),
subscriptionStatus() {
return this.profile.relationships.subscription ? 'Subscription' : 'Free'
return this.user.relationships.subscription ? 'Premium' : 'Free'
},
subscriptionColor() {
return this.profile.relationships.subscription ? 'green' : 'purple'
return this.user.relationships.subscription ? 'green' : 'purple'
},
},
data() {
return {
avatar: undefined,
profile: undefined,
isLoading: true,
isLoading: false,
}
},
created() {
axios.get('/api/profile')
.then(response => {
this.profile = response.data
this.isLoading = false
})
}
}
</script>
@@ -206,11 +206,14 @@
// Send order request
axios
.post('/api/upgrade', {
.post('/api/subscription/upgrade', {
billing: this.billing,
plan: this.requestedPlan,
})
.then(response => {
.then(() => {
// Update user data
this.$store.dispatch('getAppData')
// End loading
this.isSubmitted = false
@@ -232,7 +235,7 @@
}
},
created() {
axios.get('/api/profile')
axios.get('/api/user')
.then(response => {
if (! this.requestedPlan) {
+4 -1
View File
@@ -1,6 +1,6 @@
<template>
<PageTab v-if="invoices">
<PageTabGroup>
<PageTabGroup v-if="invoices.length > 0">
<DatatableWrapper :paginator="true" :columns="columns" :data="invoices" class="table">
<template scope="{ row }">
<tr>
@@ -35,6 +35,9 @@
</template>
</DatatableWrapper>
</PageTabGroup>
<PageTabGroup v-else>
You don't have any invoices yet.
</PageTabGroup>
</PageTab>
</template>
+25 -25
View File
@@ -3,27 +3,27 @@
<PageTabGroup>
<ValidationObserver ref="password" @submit.prevent="resetPassword" v-slot="{ invalid }" tag="form"
class="form block-form">
<div class="block-wrapper">
<label>{{ $t('page_create_password.label_new_pass') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="New Password"
rules="required" v-slot="{ errors }">
<input v-model="newPassword" :placeholder="$t('page_create_password.label_new_pass')"
type="password"
:class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>{{ $t('page_create_password.label_confirm_pass') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Confirm Your Password"
rules="required" v-slot="{ errors }">
<input v-model="newPasswordConfirmation"
:placeholder="$t('page_create_password.label_confirm_pass')" type="password"
:class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
<div class="wrapper-inline">
<div class="block-wrapper">
<label>{{ $t('page_create_password.label_new_pass') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="New Password"
rules="required" v-slot="{ errors }">
<input v-model="newPassword" :placeholder="$t('page_create_password.label_new_pass')"
type="password"
:class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>{{ $t('page_create_password.label_confirm_pass') }}:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Confirm Your Password"
rules="required" v-slot="{ errors }">
<input v-model="newPasswordConfirmation"
:placeholder="$t('page_create_password.label_confirm_pass')" type="password"
:class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
<div class="block-wrapper">
@@ -46,7 +46,6 @@
import PageHeader from '@/components/Others/PageHeader'
import ThemeLabel from '@/components/Others/ThemeLabel'
import {required} from 'vee-validate/dist/rules'
import {mapGetters} from 'vuex'
import {events} from '@/bus'
import axios from 'axios'
@@ -64,9 +63,6 @@
ThemeLabel,
required,
},
computed: {
...mapGetters(['app']),
},
data() {
return {
newPasswordConfirmation: '',
@@ -125,6 +121,10 @@
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_forms';
.block-form {
max-width: 100%;
}
@media only screen and (max-width: 960px) {
.form {
+117 -18
View File
@@ -1,38 +1,130 @@
<template>
<PageTab>
<PageTabGroup>
<PageTabGroup v-if="subscription">
<!--Info about active subscription-->
<div v-if="! subscription.canceled" class="state active">
<ListInfo class="list-info">
<ListInfoItem class="list-item" title="Plan" :content="subscription.name + ' - ' + subscription.capacity_formatted"/>
<ListInfoItem class="list-item" title="Billed" content="Monthly"/>
<ListInfoItem class="list-item" title="Status" :content="status"/>
<ListInfoItem class="list-item" title="Created At" :content="subscription.created_at"/>
<ListInfoItem class="list-item" title="Renews At" :content="subscription.ends_at"/>
</ListInfo>
<div class="cancel-plan">
<ButtonBase
@click.native="cancelSubscription"
:button-style="cancelButtonStyle"
class="cancel-button">
{{ cancelButtonText }}
</ButtonBase>
</div>
</div>
<!--Info about canceled subscription-->
<div v-if="subscription.canceled" class="state canceled">
<ListInfo class="list-info">
<ListInfoItem class="list-item" title="Plan" :content="subscription.name"/>
<ListInfoItem class="list-item" title="Status" :content="status"/>
<ListInfoItem class="list-item" title="Canceled At" :content="subscription.canceled_at"/>
<ListInfoItem class="list-item" title="Ends At" :content="subscription.ends_at"/>
</ListInfo>
</div>
</PageTabGroup>
<PageTabGroup v-else>
You don't have any subscription yet.
</PageTabGroup>
</PageTab>
</template>
<script>
import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
import PageTab from '@/components/Others/Layout/PageTab'
import DatatableWrapper from '@/components/Others/Tables/DatatableWrapper'
import {ExternalLinkIcon} from "vue-feather-icons";
import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
import ListInfoItem from '@/components/Others/ListInfoItem'
import ButtonBase from '@/components/FilesView/ButtonBase'
import PageTab from '@/components/Others/Layout/PageTab'
import ListInfo from '@/components/Others/ListInfo'
import {ExternalLinkIcon} from "vue-feather-icons"
import { mapGetters } from 'vuex'
import {events} from "@/bus"
import axios from 'axios'
export default {
name: 'UserSubscription',
components: {
PageTabGroup,
PageTab,
DatatableWrapper,
ExternalLinkIcon,
DatatableWrapper,
ListInfoItem,
PageTabGroup,
ButtonBase,
ListInfo,
PageTab,
},
computed: {
cancelButtonText() {
return this.isConfirmedCancel ? this.$t('popup_share_edit.confirm') : 'Cancel Plan'
},
cancelButtonStyle() {
return this.isConfirmedCancel ? 'danger-solid' : 'danger'
},
subscription() {
return this.$store.getters.user.relationships.subscription
? this.$store.getters.user.relationships.subscription.data.attributes
: undefined
},
status() {
if (this.subscription.canceled) {
return 'Canceled'
}
if (this.subscription.active) {
return 'Active'
}
}
},
data() {
return {
isLoading: true,
invoices: undefined,
isConfirmedCancel: false,
isDeleting: false,
}
},
created() {
/* axios.get('/api/user/subscription')
.then(response => {
this.invoices = response.data.data
this.isLoading = false
})*/
methods: {
cancelSubscription() {
// Set confirm button
if (! this.isConfirmedCancel) {
this.isConfirmedCancel = true
} else {
// Start deleting spinner button
this.isDeleting = true
// Send delete request
axios
.post('/api/subscription/cancel')
.then(() => {
// Update user data
this.$store.dispatch('getAppData')
// End deleting spinner button
this.isDeleting = false
events.$emit('alert:open', {
emoji: '👍',
title: 'Subscription Was Canceled',
message: 'You\'ll continue to have access to the features you\'ve paid for until the end of your billing cycle.',
buttonStyle: 'theme',
button: 'I\'m done'
})
})
.catch(() => {
// End deleting spinner button
this.isDeleting = false
})
}
}
}
}
</script>
@@ -40,12 +132,19 @@
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_forms';
.block-form {
max-width: 100%;
.cancel-plan {
margin-top: 10px;
}
.list-info {
display: flex;
flex-wrap: wrap;
.list-item {
flex: 0 0 50%;
}
}
@media only screen and (max-width: 960px) {
+1 -1
View File
@@ -17,7 +17,7 @@ $light_background: #f4f5f6;
$dark_background: #EBEBEB;
$shadow: 0 7px 25px 1px rgba(0, 0, 0, 0.12);
$light_mode_input_background: #f4f5f6;
$light_mode_input_background: hsla(210, 10%, 98%, 1);
$light_mode_popup_shadow: 0 15px 50px 10px rgba(26,38,74,0.12);
$light_mode_vignette: rgba(9, 8, 12, 0.35);
+1
View File
@@ -34,6 +34,7 @@
hasAuthCookie: {{ Cookie::has('token') ? 1 : 0 }},
userRegistration: {{ config('vuefilemanager.registration') ? 1 : 0 }},
storageLimit: {{ config('vuefilemanager.limit_storage_by_capacity') ? 1 : 0 }},
isSaaS: 1,
}
</script>