1
0
Fork 0
mirror of https://gitlab.com/bramw/baserow.git synced 2025-04-11 16:01:20 +00:00

Merge branch '276-redesign-left-sidebar-2' into 'develop'

Resolve "Redesign left sidebar"

Closes 

See merge request 
This commit is contained in:
Bram Wiepjes 2021-02-10 17:14:38 +00:00
commit 273c76ef16
18 changed files with 475 additions and 296 deletions

View file

@ -2,6 +2,7 @@
## Unreleased
* Redesigned the left sidebar.
* Fixed error when a very long user file name is provided when uploading.
* Upgraded DRF Spectacular dependency to the latest version.
* Added single select field form option validation.

Binary file not shown.

Before

(image error) Size: 134 KiB

After

(image error) Size: 119 KiB

View file

@ -5,9 +5,8 @@
@import 'form';
@import 'box';
@import 'layout';
@import 'menu';
@import 'sidebar';
@import 'tree';
@import 'sidebar';
@import 'header';
@import 'scrollbar';
@import 'modal';

View file

@ -12,42 +12,33 @@
left: 0;
top: 0;
bottom: 0;
width: 52px;
width: 240px;
.layout--collapsed & {
width: 48px;
}
}
.layout__col-2 {
position: absolute;
z-index: $z-index-layout-col-2;
left: 52px;
top: 0;
bottom: 0;
width: 226px;
.layout--collapsed & {
display: none;
}
}
.layout__col-3 {
position: absolute;
z-index: $z-index-layout-col-3;
left: 278px;
left: 240px;
top: 0;
bottom: 0;
right: 0;
.layout--collapsed & {
left: 52px;
left: 48px;
}
}
.layout__col-3-scroll {
.layout__col-2-scroll {
@include absolute(0);
overflow: auto;
}
.layout__col-3-1 {
.layout__col-2-1 {
position: absolute;
z-index: $z-index-layout-col-3-1;
left: 0;
@ -56,7 +47,7 @@
height: 52px;
}
.layout__col-3-2 {
.layout__col-2-2 {
position: absolute;
z-index: $z-index-layout-col-3-2;
left: 0;
@ -64,11 +55,3 @@
right: 0;
bottom: 0;
}
.layout__uncollapse {
display: none;
.layout--collapsed & {
display: block;
}
}

View file

@ -1,64 +0,0 @@
.menu {
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: $color-primary-600;
color: $white;
}
.menu__items {
list-style: none;
padding: 0;
margin: 0;
}
.menu__item {
margin: 10px;
}
.menu__link {
position: relative;
display: block;
text-decoration: none;
border-radius: 3px;
color: $white;
@include center-text(32px, 16px);
&:hover {
background-color: rgba(0, 0, 0, 0.1);
text-decoration: none;
}
&.active {
background-color: rgba(0, 0, 0, 0.3);
}
}
.menu__link-text {
display: none;
position: absolute;
left: 36px;
top: 50%;
margin-top: -10.5px;
background-color: $color-neutral-900;
border-radius: 3px;
padding: 0 4px;
white-space: nowrap;
font-weight: 400;
@include center-text(auto, 11px, 21px);
a:hover & {
display: block;
}
}
.menu__user-item {
border-radius: 100%;
background-color: $color-primary-500;
color: $white;
font-weight: 700;
@include center-text(32px, 13px);
}

View file

@ -1,63 +1,184 @@
.sidebar {
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: $white;
border-right: 1px solid $color-neutral-200;
}
@include absolute(0);
.sidebar__content-wrapper {
overflow-y: auto;
background-color: $color-neutral-10;
border-right: solid 1px $color-neutral-200;
height: 100%;
.layout--collapsed & {
overflow: visible;
}
}
.sidebar__content {
padding: 12px;
.sidebar__inner {
position: relative;
min-height: 100%;
padding-bottom: 46px;
.layout--collapsed & {
padding-bottom: 56px;
}
}
.sidebar__footer {
flex-grow: 0;
flex-shrink: 0;
border-top: 1px solid $color-neutral-200;
}
.sidebar__collapse {
display: block;
padding: 0 16px;
color: $color-primary-900;
font-weight: bold;
@include fixed-height(47px, 14px);
.sidebar__user {
display: flex;
align-items: center;
width: 100%;
padding: 16px;
margin-bottom: 4px;
&:hover {
text-decoration: none;
background-color: $color-neutral-100;
text-decoration: none;
}
.layout--collapsed & {
padding: 8px;
}
}
.sidebar__title {
font-family: $logo-font-stack;
font-size: 20px;
font-weight: 700;
margin-bottom: 16px;
.sidebar__user-initials {
flex: 0 0 36px;
font-weight: bold;
color: $white;
background-color: $color-primary-500;
border-radius: 100%;
margin-right: 12px;
img {
max-width: 104px;
@include center-text(36px, 15px);
.layout--collapsed & {
flex-basis: 32px;
margin-right: 0;
@include center-text(32px, 12px);
}
}
.sidebar__group-title {
font-size: 14px;
font-weight: 700;
margin-bottom: 10px;
.sidebar__user-info {
width: 100%;
min-width: 0;
.layout--collapsed & {
display: none;
}
}
.sidebar__user-info-top {
display: flex;
width: 100%;
justify-items: center;
margin-bottom: 4px;
}
.sidebar__user-name {
@extend %ellipsis;
min-width: 0;
color: $color-primary-900;
}
.sidebar__user-icon {
flex: 0 0 20px;
text-align: center;
font-size: 12px;
color: $color-primary-900;
}
.sidebar__user-email {
@extend %ellipsis;
font-size: 12px;
color: $color-neutral-600;
}
.sidebar__nav {
padding: 0 10px;
.layout--collapsed & {
padding: 0 8px;
}
}
.sidebar__new-wrapper {
margin-top: 12px;
}
.sidebar__new {
font-size: 13px;
color: $color-neutral-300;
margin-left: 7px;
margin-left: 6px;
&:hover {
color: $color-neutral-500;
text-decoration: none;
}
}
.sidebar__foot {
@include absolute(auto, 0, 0, 0);
display: flex;
width: 100%;
padding: 0 16px 16px 16px;
align-items: center;
justify-content: space-between;
.layout--collapsed & {
flex-direction: column;
height: 56px;
padding: 0 8px 8px 8px;
}
}
.sidebar__collapse-link {
color: $color-neutral-700;
border-radius: 3px;
@include center-text(20px, 12px);
&:hover {
display: inline-block;
text-decoration: none;
background-color: $color-neutral-100;
}
}
.layout--collapsed {
// Some minor changes regarding the tree items within the collapsed sidebar.
.sidebar__action {
.tree__link {
text-align: center;
}
.tree__icon {
margin-right: 0;
}
.sidebar__item-name {
margin-top: -10.5px;
background-color: $color-neutral-900;
color: $white;
border-radius: 3px;
padding: 0 4px;
white-space: nowrap;
font-weight: 400;
display: none;
@include absolute(50%, auto, auto, 36px);
@include center-text(auto, 11px, 21px);
}
&:hover .sidebar__item-name {
display: block;
}
}
.sidebar__logo {
display: inline-block;
order: 2;
width: 18px;
overflow: hidden;
}
}

View file

@ -35,7 +35,7 @@
.tree__action {
@extend %tree__size;
padding: 0 32px 0 6px;
padding: 0 6px;
border-radius: 3px;
&:hover {
@ -45,6 +45,11 @@
.tree__item.active &:hover {
background-color: transparent;
}
&.tree__action--has-options,
&.tree__action--has-right-icon {
padding-right: 32px;
}
}
.tree__link {
@ -58,16 +63,24 @@
&:hover {
text-decoration: none;
}
&.tree__link--group {
font-weight: 600;
}
}
.tree__type {
.tree__icon {
@extend %tree__size;
text-align: center;
width: $fa-fw-width;
color: $color-neutral-300;
color: $color-neutral-700;
margin-right: 4px;
font-size: 11px;
&.tree__icon--type {
color: $color-neutral-300;
}
}
%tree_sub-size {
@ -141,7 +154,7 @@
}
}
.tree__options {
.tree__right-icon {
display: none;
position: absolute;
z-index: 1;
@ -149,14 +162,10 @@
top: 0;
text-align: center;
width: 32px;
color: $color-neutral-300;
color: $color-neutral-700;
height: inherit;
line-height: inherit;
&:hover {
color: $color-neutral-700;
}
:hover > & {
display: block;
}
@ -165,3 +174,13 @@
display: none;
}
}
.tree__options {
@extend .tree__right-icon;
color: $color-neutral-300;
&:hover {
color: $color-neutral-700;
}
}

View file

@ -19,6 +19,7 @@ $color-primary-700: #0f6499 !default;
$color-primary-800: #0a4970 !default;
$color-primary-900: #062e47 !default;
$color-neutral-10: #fcfcfc !default;
$color-neutral-50: #fafafa !default;
$color-neutral-100: #f5f5f5 !default;
$color-neutral-200: #d9dbde !default;

View file

@ -33,10 +33,15 @@ export default {
* @param vertical Bottom positions the context under the target.
* Top positions the context above the target.
* Over-bottom positions the context over and under the target.
* Over-top positions the context over and above the target
* @param horizontal Left aligns the context with the left side of the target.
* Right aligns the context with the right side of the target.
* @param offset The distance between the target element and the context.
* Over-top positions the context over and above the target.
* @param horizontal `left` aligns the context with the left side of the target.
* `right` aligns the context with the right side of the target.
* @param verticalOffset
* The offset indicates how many pixels the context is moved
* top from the original calculated position.
* @param horizontalOffset
* The offset indicates how many pixels the context is moved
* left from the original calculated position.
* @param value True if context must be shown, false if not and undefine
* will invert the current state.
*/
@ -44,7 +49,8 @@ export default {
target,
vertical = 'bottom',
horizontal = 'left',
offset = 10,
verticalOffset = 10,
horizontalOffset = 0,
value
) {
if (value === undefined) {
@ -52,7 +58,13 @@ export default {
}
if (value) {
this.show(target, vertical, horizontal, offset)
this.show(
target,
vertical,
horizontal,
verticalOffset,
horizontalOffset
)
} else {
this.hide()
}
@ -61,12 +73,24 @@ export default {
* Calculate the position, show the context menu and register a click event on the
* body to check if the user has clicked outside the context.
*/
show(target, vertical, horizontal, offset) {
show(target, vertical, horizontal, verticalOffset, horizontalOffset) {
const isElementOrigin = isDomElement(target)
const updatePosition = () => {
const css = isElementOrigin
? this.calculatePositionElement(target, vertical, horizontal, offset)
: this.calculatePositionFixed(target, vertical, horizontal, offset)
? this.calculatePositionElement(
target,
vertical,
horizontal,
verticalOffset,
horizontalOffset
)
: this.calculatePositionFixed(
target,
vertical,
horizontal,
verticalOffset,
horizontalOffset
)
// Set the calculated positions of the context.
for (const key in css) {
@ -132,7 +156,13 @@ export default {
* figure out the correct position, so in that case we force the element to be
* visible.
*/
calculatePositionElement(target, vertical, horizontal, offset) {
calculatePositionElement(
target,
vertical,
horizontal,
verticalOffset,
horizontalOffset
) {
const visible =
window.getComputedStyle(target).getPropertyValue('display') !== 'none'
@ -151,15 +181,29 @@ export default {
}
// Calculate if top, bottom, left and right positions are possible.
const canTop = targetRect.top - contextRect.height - offset > 0
const canTop = targetRect.top - contextRect.height - verticalOffset > 0
const canBottom =
window.innerHeight - targetRect.bottom - contextRect.height - offset > 0
const canOverTop = targetRect.bottom - contextRect.height - offset > 0
window.innerHeight -
targetRect.bottom -
contextRect.height -
verticalOffset >
0
const canOverTop =
targetRect.bottom - contextRect.height - verticalOffset > 0
const canOverBottom =
window.innerHeight - targetRect.bottom - contextRect.height - offset > 0
const canRight = targetRect.right - contextRect.width > 0
window.innerHeight -
targetRect.bottom -
contextRect.height -
verticalOffset >
0
const canRight =
targetRect.right - contextRect.width - horizontalOffset > 0
const canLeft =
window.innerWidth - targetRect.left - contextRect.width > 0
window.innerWidth -
targetRect.left -
contextRect.width -
horizontalOffset >
0
// If bottom, top, left or right doesn't fit, but their opposite does we switch to
// that.
@ -189,27 +233,29 @@ export default {
// Calculate the correct positions for horizontal and vertical values.
if (horizontal === 'left') {
positions.left = targetRect.left
positions.left = targetRect.left + horizontalOffset
}
if (horizontal === 'right') {
positions.right = window.innerWidth - targetRect.right
positions.right =
window.innerWidth - targetRect.right - horizontalOffset
}
if (vertical === 'bottom') {
positions.top = targetRect.bottom + offset
positions.top = targetRect.bottom + verticalOffset
}
if (vertical === 'top') {
positions.bottom = window.innerHeight - targetRect.top + offset
positions.bottom = window.innerHeight - targetRect.top + verticalOffset
}
if (vertical === 'over-bottom') {
positions.top = targetRect.top + offset
positions.top = targetRect.top + verticalOffset
}
if (vertical === 'over-top') {
positions.bottom = window.innerHeight - targetRect.bottom + offset
positions.bottom =
window.innerHeight - targetRect.bottom + verticalOffset
}
return positions

View file

@ -8,7 +8,7 @@
</a>
</li>
<li>
<a @click="$refs.groupMembersModal.show()">
<a @click=";[$refs.groupMembersModal.show(), hide()]">
<i class="context__menu-icon fas fa-fw fa-users"></i>
Members
</a>

View file

@ -1,30 +1,179 @@
<template>
<div>
<div v-if="hasSelectedGroup">
<div class="sidebar__group-title">{{ selectedGroup.name }}</div>
<ul class="tree">
<SidebarApplication
v-for="application in applications"
:key="application.id"
:application="application"
></SidebarApplication>
</ul>
<div class="sidebar">
<div class="sidebar__inner">
<a
ref="createApplicationContextLink"
class="sidebar__new"
ref="userContextAnchor"
class="sidebar__user"
@click="
$refs.createApplicationContext.toggle(
$refs.createApplicationContextLink
$refs.userContext.toggle(
$refs.userContextAnchor,
'bottom',
'left',
isCollapsed ? -4 : -10,
isCollapsed ? 8 : 16
)
"
>
<i class="fas fa-plus"></i>
Create new
<div class="sidebar__user-initials">
{{ name | nameAbbreviation }}
</div>
<div class="sidebar__user-info">
<div class="sidebar__user-info-top">
<div class="sidebar__user-name">{{ name }}</div>
<div class="sidebar__user-icon">
<i class="fas fa-caret-down"></i>
</div>
</div>
<div class="sidebar__user-email">{{ email }}</div>
</div>
</a>
<CreateApplicationContext
ref="createApplicationContext"
:group="selectedGroup"
></CreateApplicationContext>
<Context ref="userContext">
<div class="context__menu-title">{{ name }}</div>
<ul class="context__menu">
<li>
<a
@click="
;[
$refs.settingsModal.show('password'),
$refs.userContext.hide(),
]
"
>
<i class="context__menu-icon fas fa-fw fa-cogs"></i>
Settings
</a>
<SettingsModal ref="settingsModal"></SettingsModal>
</li>
<li>
<a @click="logoff()">
<i class="context__menu-icon fas fa-fw fa-sign-out-alt"></i>
Logoff
</a>
</li>
</ul>
</Context>
<div class="sidebar__nav">
<ul class="tree">
<li
class="tree__item"
:class="{
active: $route.matched.some(({ name }) => name === 'dashboard'),
}"
>
<div class="tree__action sidebar__action">
<nuxt-link :to="{ name: 'dashboard' }" class="tree__link">
<i class="tree__icon fas fa-tachometer-alt"></i>
<span class="sidebar__item-name">Dashboard</span>
</nuxt-link>
</div>
</li>
<template v-if="hasSelectedGroup && !isCollapsed">
<li class="tree__item margin-top-2">
<div class="tree__action">
<a
ref="groupSelectToggle"
class="tree__link tree__link--group"
@click="
$refs.groupSelect.toggle(
$refs.groupSelectToggle,
'bottom',
'left',
0
)
"
>{{ selectedGroup.name }}</a
>
<GroupsContext ref="groupSelect"></GroupsContext>
</div>
</li>
<li class="tree__item">
<div class="tree__action">
<a class="tree__link" @click="$refs.groupMembersModal.show()">
<i class="tree__icon tree__icon--type fas fa-users"></i>
Invite others
</a>
<GroupMembersModal
ref="groupMembersModal"
:group="selectedGroup"
></GroupMembersModal>
</div>
</li>
<ul class="tree">
<SidebarApplication
v-for="application in applications"
:key="application.id"
:application="application"
></SidebarApplication>
</ul>
<li class="sidebar__new-wrapper">
<a
ref="createApplicationContextLink"
class="sidebar__new"
@click="
$refs.createApplicationContext.toggle(
$refs.createApplicationContextLink
)
"
>
<i class="fas fa-plus"></i>
Create new
</a>
</li>
<CreateApplicationContext
ref="createApplicationContext"
:group="selectedGroup"
></CreateApplicationContext>
</template>
<template v-else-if="!hasSelectedGroup && !isCollapsed">
<li v-if="groups.length === 0" class="tree_item margin-top-2">
<p>You dont have any groups.</p>
</li>
<li
v-for="(group, index) in groups"
:key="group.id"
class="tree__item"
:class="{ 'margin-top-2': index === 0 }"
>
<div class="tree__action tree__action--has-right-icon">
<a
class="tree__link tree__link--group"
@click="$store.dispatch('group/select', group)"
>{{ group.name }}</a
>
<i class="tree__right-icon fas fa-arrow-right"></i>
</div>
</li>
<li class="sidebar__new-wrapper">
<a class="sidebar__new" @click="$refs.createGroupModal.show()">
<i class="fas fa-plus"></i>
Create group
</a>
</li>
<CreateGroupModal ref="createGroupModal"></CreateGroupModal>
</template>
</ul>
</div>
<div class="sidebar__foot">
<div class="sidebar__logo">
<img
height="14"
src="@baserow/modules/core/static/img/logo.svg"
alt="Baserow logo"
/>
</div>
<a
class="sidebar__collapse-link"
@click="$store.dispatch('sidebar/toggleCollapsed')"
>
<i
class="fas"
:class="{
'fa-angle-double-right': isCollapsed,
'fa-angle-double-left': !isCollapsed,
}"
></i>
</a>
</div>
</div>
</div>
</template>
@ -32,14 +181,22 @@
<script>
import { mapGetters, mapState } from 'vuex'
import SettingsModal from '@baserow/modules/core/components/settings/SettingsModal'
import SidebarApplication from '@baserow/modules/core/components/sidebar/SidebarApplication'
import CreateApplicationContext from '@baserow/modules/core/components/application/CreateApplicationContext'
import GroupsContext from '@baserow/modules/core/components/group/GroupsContext'
import CreateGroupModal from '@baserow/modules/core/components/group/CreateGroupModal'
import GroupMembersModal from '@baserow/modules/core/components/group/GroupMembersModal'
export default {
name: 'Sidebar',
components: {
SettingsModal,
CreateApplicationContext,
SidebarApplication,
GroupsContext,
CreateGroupModal,
GroupMembersModal,
},
computed: {
/**
@ -53,12 +210,21 @@ export default {
},
...mapState({
allApplications: (state) => state.application.items,
groups: (state) => state.group.items,
selectedGroup: (state) => state.group.selected,
}),
...mapGetters({
isLoading: 'application/isLoading',
name: 'auth/getName',
email: 'auth/getUsername',
hasSelectedGroup: 'group/hasSelected',
isCollapsed: 'sidebar/isCollapsed',
}),
},
methods: {
logoff() {
this.$store.dispatch('auth/logoff')
this.$nuxt.$router.push({ name: 'login' })
},
},
}
</script>

View file

@ -6,10 +6,10 @@
'tree__item--loading': application._.loading,
}"
>
<div class="tree__action">
<div class="tree__action tree__action--has-options">
<a class="tree__link" @click="selectApplication(application)">
<i
class="tree__type fas"
class="tree__icon tree__icon--type fas"
:class="'fa-' + application._.type.iconClass"
></i>
<Editable

View file

@ -1,87 +1,11 @@
<template>
<div>
<Notifications></Notifications>
<SettingsModal ref="settingsModal"></SettingsModal>
<div :class="{ 'layout--collapsed': isCollapsed }" class="layout">
<div class="layout__col-1 menu">
<ul class="menu__items">
<li class="menu__item">
<nuxt-link :to="{ name: 'dashboard' }" class="menu__link">
<i class="fas fa-tachometer-alt"></i>
<span class="menu__link-text">Dashboard</span>
</nuxt-link>
</li>
<li class="menu__item">
<a
ref="groupSelectToggle"
class="menu__link"
@click="$refs.groupSelect.toggle($refs.groupSelectToggle)"
>
<i class="fas fa-layer-group"></i>
<span class="menu__link-text">Groups</span>
</a>
<GroupsContext ref="groupSelect"></GroupsContext>
</li>
</ul>
<ul class="menu__items">
<li class="menu__item layout__uncollapse">
<a class="menu__link" @click="toggleCollapsed()">
<i class="fas fa-angle-double-right"></i>
<span class="menu__link-text">Uncollapse</span>
</a>
</li>
<li class="menu__item">
<a
class="menu__link menu__user-item"
@click="$refs.userContext.toggle($event.target)"
>
{{ name | nameAbbreviation }}
<span class="menu__link-text">{{ name }}</span>
</a>
<Context ref="userContext">
<div class="context__menu-title">{{ name }}</div>
<ul class="context__menu">
<li>
<a
@click="
;[
$refs.settingsModal.show('password'),
$refs.userContext.hide(),
]
"
>
<i class="context__menu-icon fas fa-fw fa-cogs"></i>
Settings
</a>
</li>
<li>
<a @click="logoff()">
<i class="context__menu-icon fas fa-fw fa-sign-out-alt"></i>
Logoff
</a>
</li>
</ul>
</Context>
</li>
</ul>
<div class="layout__col-1">
<Sidebar></Sidebar>
</div>
<div class="layout__col-2 sidebar">
<div class="sidebar__content-wrapper">
<nav class="sidebar__content">
<div class="sidebar__title">
<img src="@baserow/modules/core/static/img/logo.svg" alt="" />
</div>
<Sidebar></Sidebar>
</nav>
</div>
<div class="sidebar__footer">
<a class="sidebar__collapse" @click="toggleCollapsed()">
<i class="fas fa-angle-double-left"></i>
Collapse sidebar
</a>
</div>
</div>
<div class="layout__col-3">
<div class="layout__col-2">
<nuxt />
</div>
</div>
@ -89,29 +13,20 @@
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { mapGetters } from 'vuex'
import SettingsModal from '@baserow/modules/core/components/settings/SettingsModal'
import Notifications from '@baserow/modules/core/components/notifications/Notifications'
import GroupsContext from '@baserow/modules/core/components/group/GroupsContext'
import Sidebar from '@baserow/modules/core/components/sidebar/Sidebar'
export default {
components: {
SettingsModal,
GroupsContext,
Notifications,
Sidebar,
},
// Application pages are pages that have the edit sidebar on the left side which
// contains the groups and applications. In order to be able to fetch them the user
// must be authenticated. And in order to show them we must fetch all the groups and
// applications.
middleware: ['authenticated', 'groupsAndApplications'],
computed: {
...mapGetters({
isCollapsed: 'sidebar/isCollapsed',
name: 'auth/getName',
}),
},
mounted() {
@ -121,14 +36,5 @@ export default {
beforeDestroy() {
this.$realtime.disconnect()
},
methods: {
logoff() {
this.$store.dispatch('auth/logoff')
this.$nuxt.$router.push({ name: 'login' })
},
...mapActions({
toggleCollapsed: 'sidebar/toggleCollapsed',
}),
},
}
</script>

View file

@ -9,7 +9,7 @@ export default async function GroupsAndApplications({ store, req, app }) {
if (process.server && !req) return
// Get the selected group id
const groupId = getGroupCookie(app)
let groupId = getGroupCookie(app)
// If the groups haven't already been selected we will
if (store.getters['auth/isAuthenticated']) {
@ -17,6 +17,12 @@ export default async function GroupsAndApplications({ store, req, app }) {
if (!store.getters['group/isLoaded']) {
await store.dispatch('group/fetchAll')
// If the user only has one group we then that one must be selected.
const groups = store.getters['group/getAll']
if (store.getters['group/getAll'].length === 1) {
groupId = groups[0].id
}
// If there is a groupId cookie we will select that group.
if (groupId) {
try {

View file

@ -29,17 +29,9 @@ export default {
this.setLoading(group, false)
},
async selectGroup(group) {
this.setLoading(group, true)
try {
await this.$store.dispatch('group/select', group)
this.$emit('selected')
} catch (error) {
notifyIf(error, 'group')
}
this.setLoading(group, false)
selectGroup(group) {
this.$store.dispatch('group/select', group)
this.$emit('selected')
},
},
}

View file

@ -1,5 +1,5 @@
<template>
<div class="layout__col-3-scroll">
<div class="layout__col-2-scroll">
<div
class="alert alert--simple alert--warning alert--has-icon dashboard__alert"
>

View file

@ -208,6 +208,9 @@ export const getters = {
get: (state) => (id) => {
return state.items.find((item) => item.id === id)
},
getAll(state) {
return state.items
},
hasSelected(state) {
return Object.prototype.hasOwnProperty.call(state.selected, 'id')
},

View file

@ -1,6 +1,6 @@
<template>
<div>
<header class="layout__col-3-1 header">
<header class="layout__col-2-1 header">
<div v-show="tableLoading" class="header__loading"></div>
<ul v-if="!tableLoading" class="header__filter">
<li class="header__filter-item header__filter-item--grids">
@ -69,7 +69,7 @@
<li>{{ table.name }}</li>
</ul>
</header>
<div class="layout__col-3-2 content">
<div class="layout__col-2-2 content">
<component
:is="getViewComponent(view)"
v-if="hasSelectedView && !tableLoading"