Skip to content

Commit a8bdc99

Browse files
Hoang Nguyenhoangnm92rohityadavcloud
committed
project: dashboard, custom actions and tabs (#73)
This fixes #41 Adds project specific dashboard tabs, custom actions and tabs for project view. Also adds quickview and other list/details view improvements. Co-authored-by: hoangnm <[email protected]> Co-authored-by: Rohit Yadav <[email protected]> Signed-off-by: Rohit Yadav <[email protected]>
1 parent b866f23 commit a8bdc99

27 files changed

+1595
-273
lines changed

ui/src/assets/logo.svg

Lines changed: 163 additions & 152 deletions
Loading

ui/src/components/header/ProjectMenu.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export default {
106106
<style lang="less" scoped>
107107
.project {
108108
&-select {
109-
width: 40%;
109+
width: 30vw;
110110
}
111111
112112
&-icon {

ui/src/components/menu/SideMenu.vue

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,23 @@ export default {
8585
height: auto;
8686
8787
/deep/ .ant-layout-sider-children {
88-
overflow-y: auto;
88+
overflow-y: hidden;
89+
&:hover {
90+
overflow-y: auto;
91+
}
92+
}
93+
94+
/deep/ .ant-menu-vertical .ant-menu-item {
95+
margin-top: 0px;
96+
margin-bottom: 0px;
97+
}
98+
99+
/deep/ .ant-menu-inline .ant-menu-item:not(:last-child) {
100+
margin-bottom: 0px;
101+
}
102+
103+
/deep/ .ant-menu-inline .ant-menu-item {
104+
margin-top: 0px;
89105
}
90106
91107
&.ant-fixed-sidemenu {
@@ -99,14 +115,14 @@ export default {
99115
100116
.ant-menu-light {
101117
border-right-color: transparent;
102-
padding: 10px 0;
118+
padding: 14px 0;
103119
}
104120
}
105121
106122
&.dark {
107123
.ant-menu-dark {
108124
border-right-color: transparent;
109-
padding: 10px 0;
125+
padding: 14px 0;
110126
}
111127
}
112128
}

ui/src/components/page/GlobalFooter.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818
<template>
1919
<div class="footer">
2020
<div class="links">
21-
<a href="https://github.com/apache/cloudstack-primate" target="_blank">
21+
CloudStack Server {{ $store.getters.features.cloudstackversion }}
22+
<a-divider type="vertical" />
23+
<a href="https://github.com/apache/cloudstack-primate/issues/new/choose" target="_blank">
2224
<a-icon type="github"/>
25+
Report Bug
2326
</a>
2427
</div>
2528
</div>
@@ -51,9 +54,6 @@ export default {
5154
color: rgba(0, 0, 0, .65);
5255
}
5356
54-
&:not(:last-child) {
55-
margin-right: 40px;
56-
}
5757
}
5858
}
5959
.copyright {
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
<template>
19+
<span class="row-action-button">
20+
<a-tooltip
21+
v-for="(action, actionIndex) in actions"
22+
:key="actionIndex"
23+
arrowPointAtCenter
24+
placement="bottomRight">
25+
<template slot="title">
26+
{{ $t(action.label) }}
27+
</template>
28+
<a-badge
29+
class="button-action-badge"
30+
:overflowCount="9"
31+
:count="actionBadge[action.api] ? actionBadge[action.api].badgeNum : 0"
32+
v-if="action.api in $store.getters.apis &&
33+
action.showBadge &&
34+
((!dataView && (action.listView || action.groupAction && selectedRowKeys.length > 0)) || (dataView && action.dataView)) &&
35+
('show' in action ? action.show(resource, $store.getters.userInfo) : true)">
36+
<a-button
37+
:icon="action.icon"
38+
:type="action.icon === 'delete' ? 'danger' : (action.icon === 'plus' ? 'primary' : 'default')"
39+
shape="circle"
40+
style="margin-right: 5px"
41+
@click="execAction(action)" />
42+
</a-badge>
43+
<a-button
44+
v-if="action.api in $store.getters.apis &&
45+
!action.showBadge &&
46+
((!dataView && (action.listView || action.groupAction && selectedRowKeys.length > 0)) || (dataView && action.dataView)) &&
47+
('show' in action ? action.show(resource, $store.getters.userInfo) : true)"
48+
:icon="action.icon"
49+
:type="action.icon === 'delete' ? 'danger' : (action.icon === 'plus' ? 'primary' : 'default')"
50+
shape="circle"
51+
style="margin-left: 5px"
52+
@click="execAction(action)" />
53+
</a-tooltip>
54+
</span>
55+
</template>
56+
57+
<script>
58+
import { api } from '@/api'
59+
60+
export default {
61+
name: 'ActionButton',
62+
data () {
63+
return {
64+
actionBadge: []
65+
}
66+
},
67+
mounted () {
68+
this.handleShowBadge()
69+
},
70+
props: {
71+
actions: {
72+
type: Array,
73+
default () {
74+
return []
75+
}
76+
},
77+
resource: {
78+
type: Object,
79+
default () {
80+
return {}
81+
}
82+
},
83+
dataView: {
84+
type: Boolean,
85+
default: false
86+
},
87+
selectedRowKeys: {
88+
type: Array,
89+
default () {
90+
return []
91+
}
92+
},
93+
loading: {
94+
type: Boolean,
95+
default: false
96+
}
97+
},
98+
watch: {
99+
resource (newItem, oldItem) {
100+
if (!newItem || !newItem.id) {
101+
return
102+
}
103+
this.handleShowBadge()
104+
}
105+
},
106+
methods: {
107+
execAction (action) {
108+
this.$emit('exec-action', action)
109+
},
110+
handleShowBadge () {
111+
const dataBadge = {}
112+
const arrAsync = []
113+
const actionBadge = this.actions.filter(action => action.showBadge === true)
114+
115+
if (actionBadge && actionBadge.length > 0) {
116+
const dataLength = actionBadge.length
117+
118+
for (let i = 0; i < dataLength; i++) {
119+
const action = actionBadge[i]
120+
121+
arrAsync.push(new Promise((resolve, reject) => {
122+
api(action.api, action.param).then(json => {
123+
let responseJsonName
124+
const response = {}
125+
126+
response.api = action.api
127+
response.count = 0
128+
129+
for (const key in json) {
130+
if (key.includes('response')) {
131+
responseJsonName = key
132+
break
133+
}
134+
}
135+
136+
if (json[responseJsonName].count && json[responseJsonName].count > 0) {
137+
response.count = json[responseJsonName].count
138+
}
139+
140+
resolve(response)
141+
}).catch(error => {
142+
reject(error)
143+
})
144+
}))
145+
}
146+
147+
Promise.all(arrAsync).then(response => {
148+
for (let j = 0; j < response.length; j++) {
149+
this.$set(dataBadge, response[j].api, {})
150+
this.$set(dataBadge[response[j].api], 'badgeNum', response[j].count)
151+
}
152+
})
153+
154+
this.actionBadge = dataBadge
155+
}
156+
}
157+
}
158+
}
159+
</script>
160+
161+
<style scoped >
162+
.button-action-badge {
163+
margin-left: 5px;
164+
}
165+
166+
/deep/.button-action-badge .ant-badge-count {
167+
right: 10px;
168+
z-index: 8;
169+
}
170+
</style>

ui/src/components/view/DetailSettings.vue

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,27 +43,6 @@
4343
<a-list-item-meta>
4444
<span slot="title">
4545
{{ item.name }}
46-
<a-button shape="circle" size="small" @click="updateDetail(index)" v-if="item.edit">
47-
<a-icon type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
48-
</a-button>
49-
<a-button shape="circle" size="small" @click="hideEditDetail(index)" v-if="item.edit" style="margin-left: 5px">
50-
<a-icon type="close-circle" theme="twoTone" twoToneColor="#f5222d" />
51-
</a-button>
52-
<a-button shape="circle" size="small" @click="showEditDetail(index)" v-if="!item.edit">
53-
<a-icon type="edit" />
54-
</a-button>
55-
<a-divider type="vertical" />
56-
<a-popconfirm
57-
title="Delete setting?"
58-
@confirm="deleteDetail(index)"
59-
okText="Yes"
60-
cancelText="No"
61-
placement="right"
62-
>
63-
<a-button shape="circle" size="small">
64-
<a-icon type="delete" theme="twoTone" twoToneColor="#f5222d" />
65-
</a-button>
66-
</a-popconfirm>
6746
</span>
6847
<span slot="description" style="word-break: break-all">
6948
<span v-if="item.edit" style="display: flex">
@@ -77,6 +56,30 @@
7756
<span v-else @click="showEditDetail(index)">{{ item.value }}</span>
7857
</span>
7958
</a-list-item-meta>
59+
<div slot="actions">
60+
<a-button shape="circle" size="default" @click="updateDetail(index)" v-if="item.edit">
61+
<a-icon type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
62+
</a-button>
63+
<a-button shape="circle" size="default" @click="hideEditDetail(index)" v-if="item.edit">
64+
<a-icon type="close-circle" theme="twoTone" twoToneColor="#f5222d" />
65+
</a-button>
66+
<a-button shape="circle" @click="showEditDetail(index)" v-if="!item.edit">
67+
<a-icon type="edit" />
68+
</a-button>
69+
</div>
70+
<div slot="actions">
71+
<a-popconfirm
72+
title="Delete setting?"
73+
@confirm="deleteDetail(index)"
74+
okText="Yes"
75+
cancelText="No"
76+
placement="left"
77+
>
78+
<a-button shape="circle">
79+
<a-icon type="delete" theme="twoTone" twoToneColor="#f5222d" />
80+
</a-button>
81+
</a-popconfirm>
82+
</div>
8083
</a-list-item>
8184
</a-list>
8285
</a-spin>

ui/src/components/view/InfoCard.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@
430430
:value="annotation"
431431
placeholder="Add Note" />
432432
<a-button
433+
style="margin-top: 10px"
433434
@click="saveNote"
434435
type="primary"
435436
>
@@ -643,12 +644,15 @@ export default {
643644

644645
<style lang="less" scoped>
645646
647+
/deep/ .ant-card-body {
648+
padding: 36px;
649+
}
650+
646651
.resource-details {
647652
text-align: center;
648653
margin-bottom: 24px;
649654
& > .avatar {
650655
margin: 0 auto;
651-
padding-top: 20px;
652656
width: 104px;
653657
//height: 104px;
654658
margin-bottom: 20px;

ui/src/components/view/ListView.vue

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
</template>
3535

3636
<div slot="expandedRowRender" slot-scope="resource">
37-
<info-card :resource="resource" style="margin-right: 50px">
37+
<info-card :resource="resource" style="margin-left: 0px; width: 50%">
3838
<div slot="actions" style="padding-top: 12px">
3939
<a-tooltip
4040
v-for="(action, actionIndex) in $route.meta.actions"
@@ -48,12 +48,10 @@
4848
('show' in action ? action.show(resource, $store.getters.userInfo) : true)"
4949
:icon="action.icon"
5050
:type="action.icon === 'delete' ? 'danger' : (action.icon === 'plus' ? 'primary' : 'default')"
51-
shape="round"
52-
size="small"
51+
shape="circle"
5352
style="margin-right: 5px; margin-top: 12px"
5453
@click="$parent.execAction(action)"
5554
>
56-
{{ $t(action.label) }}
5755
</a-button>
5856
</a-tooltip>
5957
</div>

ui/src/components/view/ResourceView.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
v-for="tab in tabs"
3737
:tab="$t(tab.name)"
3838
:key="tab.name"
39-
v-if="'show' in tab ? tab.show(resource, $route) : true">
39+
v-if="'show' in tab ? tab.show(resource, $route, $store.getters.userInfo) : true">
4040
<component :is="tab.component" :resource="resource" :loading="loading" :tab="activeTab" />
4141
</a-tab-pane>
4242
</a-tabs>
@@ -46,7 +46,6 @@
4646
</template>
4747

4848
<script>
49-
5049
import DetailsTab from '@/components/view/DetailsTab'
5150
import InfoCard from '@/components/view/InfoCard'
5251
import ResourceLayout from '@/layouts/ResourceLayout'

0 commit comments

Comments
 (0)