Support to dynamic dashboard (#219)

* First test for dynamic load

* add support to list dashboard items

* change some dashboards names

Co-authored-by: Yamel Senih <ysenih@erpya.com>
pull/3759/head
Leonel Matos 2020-01-14 10:51:28 -04:00 committed by Yamel Senih
parent d622eec48b
commit f0630d79d6
7 changed files with 280 additions and 86 deletions

View File

@ -347,6 +347,10 @@ export function requestPrintFormats({ tableName, reportViewUuid, processUuid })
return Instance.call(this).requestPrintFormats({ tableName: tableName, reportViewUuid: reportViewUuid, processUuid: processUuid })
}
export function requestLisDashboards(roleUuid) {
return Instance.call(this).requestDashboards(roleUuid)
}
export function requestLanguages() {
return Instance.call(this).requestLanguages()
}

View File

@ -0,0 +1,55 @@
<template>
<component
:is="renderDashboard"
:ref="dashboard.dashboardName"
:metadata="dashboard"
/>
</template>
<script>
export default {
name: 'Dashboard',
props: {
metadata: {
type: Object,
required: true
}
},
data() {
return {
dashboard: this.metadata
}
},
computed: {
// load the component that is indicated in the attributes of received property
renderDashboard() {
if (this.metadata.fileName === 'userfavorites') {
return () => import('@/components/ADempiere/Dashboard/favourites')
}
return () => import(`@/components/ADempiere/Dashboard/${this.metadata.fileName}`)
}
}
}
</script>
<style lang="scss" scoped>
.dashboard-editor-container {
padding: 32px;
background-color: rgb(240, 242, 245);
position: relative;
.github-corner {
position: absolute;
top: 0px;
border: 0;
right: 0;
}
.chart-wrapper {
background: #fff;
padding: 16px 16px 0;
margin-bottom: 32px;
}
}
</style>

View File

@ -0,0 +1,156 @@
<template>
<el-collapse v-model="activeRecentItems" accordion>
<el-collapse-item name="recentItems">
<template slot="title">
<i class="el-icon-time" style="margin-right: 4px;margin-left: 10px;" /> {{ $t('profile.recentItems') }}
</template>
<el-card class="box-card" :body-style="{ padding: '0px' }" shadow="never">
<div class="recent-items">
<el-table :data="search.length ? filterResult(search) : recentItems" max-height="455" @row-click="handleClick">
<el-table-column width="40">
<template slot-scope="{row}">
<svg-icon :icon-class="row.icon" class="icon-window" />
</template>
</el-table-column>
<el-table-column>
<template slot="header" slot-scope="scope" class="clearfix">
<el-input
v-model="search"
size="mini"
:metadata="scope"
:placeholder="$t('table.dataTable.search')"
/>
</template>
<template slot-scope="{row}">
<span>{{ row.displayName }}</span>
<el-tag class="action-tag">{{ $t(`views.${row.action}`) }}</el-tag>
<br>
<span class="time">{{ translateDate(row.updated) }}</span>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</el-collapse-item>
</el-collapse>
</template>
<script>
import { getRecentItems as getRecentItemsFromServer } from '@/api/ADempiere'
import { convertAction } from '@/utils/ADempiere/dictionaryUtils'
export default {
name: 'RecentItems',
data() {
return {
activeRecentItems: 'recentItems',
recentItems: [],
isLoaded: true,
search: '',
accentRegexp: /[\u0300-\u036f]/g
}
},
computed: {
getterRecentItems() {
return this.$store.getters.getRecentItems
},
cachedViews() {
return this.$store.getters.cachedViews
}
},
mounted() {
this.getRecentItems()
this.subscribeChanges()
},
methods: {
checkOpened(uuid) {
return this.cachedViews.includes(uuid)
},
getRecentItems() {
return new Promise((resolve, reject) => {
getRecentItemsFromServer()
.then(response => {
const recentItems = response.getRecentitemsList().map(item => {
const actionConverted = convertAction(item.getAction())
return {
action: actionConverted.name,
icon: actionConverted.icon,
displayName: item.getDisplayname(),
menuUuid: item.getMenuuuid(),
menuName: item.getMenuname(),
windowUuid: item.getWindowuuid(),
tableId: item.getTableid(),
recordId: item.getRecordid(),
uuidRecord: item.getRecorduuid(),
tabUuid: item.getTabuuid(),
updated: new Date(item.getUpdated()),
description: item.getMenudescription()
}
})
this.recentItems = recentItems
this.isLoaded = false
resolve(recentItems)
})
.catch(error => {
reject(error)
})
})
},
handleClick(row) {
if (!this.isEmptyValue(row.uuidRecord)) {
this.$router.push({ name: row.menuUuid, query: { action: row.uuidRecord, tabParent: 0 }})
} else {
this.$router.push({ name: row.menuUuid })
}
},
subscribeChanges() {
this.$store.subscribe((mutation, state) => {
if (mutation.type === 'notifyDashboardRefresh') {
this.getRecentItems()
}
})
},
filterResult(search) {
return this.recentItems.filter(item => this.ignoreAccent(item.displayName).toLowerCase().includes(this.ignoreAccent(search.toLowerCase())))
},
ignoreAccent(s) {
if (!s) { return '' }
return s.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
},
translateDate(value) {
return this.$d(new Date(value), 'long', this.language)
}
}
}
</script>
<style scoped>
.search_recent {
width: 50%!important;
float: right;
}
.header {
padding-bottom: 10px;
}
.recent-items {
height: 455px;
overflow: auto;
}
.time {
float: left;
font-size: 11px;
color: #999;
}
.card-box {
cursor: pointer;
}
.card-content {
font-size: 15px;
}
.icon-window {
font-size: x-large;
color: #36a3f7;
}
.action-tag {
float: right;
}
</style>

View File

@ -1,4 +1,5 @@
// Default store for handle dashboard refresh and other functionalities
import { requestLisDashboards } from '@/api/ADempiere/data'
const dashboard = {
state: {
dashboard: []
@ -14,6 +15,39 @@ const dashboard = {
actions: {
refreshDashboard({ commit, getters }, parameters) {
commit('notifyDashboardRefresh', parameters)
},
listDashboard({ commit }, roleUuid) {
return new Promise((resolve, reject) => {
requestLisDashboards(roleUuid)
.then(response => {
const dashboards = response.getDashboardsList().map(item => {
return {
windowUuid: item.getWindowuuid(),
browserUuid: item.getBrowseruuid(),
dashboardName: item.getDashboardname(),
dashboardDescription: item.getDashboarddescription(),
dashboardHtml: item.getDashboardhtml(),
columnNo: item.getColumnno(),
lineNo: item.getLineno(),
isCollapsible: item.getIscollapsible(),
isOpenByDefault: item.getIsopenbydefault(),
isEventRequired: item.getIseventrequired(),
fileName: item.getFilename()
}
})
const roleDashboards = {
roleUuid: roleUuid,
recordCount: response.getRecordcount(),
dashboardList: dashboards,
nextPageToken: response.getNextPageToken()
}
commit('addDashboard', roleDashboards)
resolve(roleDashboards)
})
.catch(error => {
reject(error)
})
})
}
},
getters: {
@ -21,6 +55,11 @@ const dashboard = {
return state.dashboard.find(
item => item.uuid === dashboardUuid
)
},
getDashboardByRole: (state) => (roleUuid) => {
return state.dashboard.find(
item => item.roleUuid === roleUuid
)
}
}
}

View File

@ -1,108 +1,48 @@
<template>
<div class="dashboard-editor-container">
<github-corner class="github-corner" />
<panel-group @handleSetLineChartData="handleSetLineChartData" />
<el-row style="background:#fff;padding:16px 16px 0;margin-bottom:32px;">
<line-chart :chart-data="lineChartData" />
</el-row>
<el-row :gutter="32">
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<raddar-chart />
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<pie-chart />
</div>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<div class="chart-wrapper">
<bar-chart />
</div>
</el-col>
</el-row>
<el-row :gutter="8">
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
<recent-items />
</el-col>
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
<favorites />
</el-col>
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 6}" style="margin-bottom:30px;">
<todo-list />
</el-col>
<el-col :xs="{span: 24}" :sm="{span: 12}" :md="{span: 12}" :lg="{span: 6}" :xl="{span: 6}" style="margin-bottom:30px;">
<box-card />
</el-col>
<el-col :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
<pending-documents />
</el-col>
<template v-for="(dashboard, index) in dashboardList">
<el-col :key="index" :xs="{span: 24}" :sm="{span: 24}" :md="{span: 24}" :lg="{span: 12}" :xl="{span: 12}" style="padding-right:8px;margin-bottom:30px;">
<dashboard :metadata="dashboard" />
</el-col>
</template>
</el-row>
</div>
</template>
<script>
import GithubCorner from '@/components/GithubCorner'
import PanelGroup from './components/PanelGroup'
import LineChart from './components/LineChart'
import RaddarChart from './components/RaddarChart'
import PieChart from './components/PieChart'
import BarChart from './components/BarChart'
// import TransactionTable from './components/TransactionTable'
import TodoList from './components/TodoList'
import BoxCard from './components/BoxCard'
import RecentItems from '@/components/ADempiere/RecentItems'
import Favorites from '@/components/ADempiere/Favorites'
import PendingDocuments from '@/components/ADempiere/PendingDocuments'
const lineChartData = {
newVisitis: {
expectedData: [100, 120, 161, 134, 105, 160, 165],
actualData: [120, 82, 91, 154, 162, 140, 145]
},
messages: {
expectedData: [200, 192, 120, 144, 160, 130, 140],
actualData: [180, 160, 151, 106, 145, 150, 130]
},
purchases: {
expectedData: [80, 100, 121, 104, 105, 90, 100],
actualData: [120, 90, 100, 138, 142, 130, 130]
},
shoppings: {
expectedData: [130, 140, 141, 142, 145, 150, 160],
actualData: [120, 82, 91, 154, 162, 140, 130]
}
}
import Dashboard from '@/components/ADempiere/Dashboard'
export default {
name: 'DashboardAdmin',
components: {
GithubCorner,
PanelGroup,
LineChart,
RaddarChart,
PieChart,
BarChart,
// TransactionTable,
TodoList,
BoxCard,
RecentItems,
Favorites,
PendingDocuments
Dashboard
},
data() {
return {
lineChartData: lineChartData.newVisitis
roleUuid: this.$store.getters.getRoleUuid,
dashboardList: []
}
},
computed: {
getterDashboard() {
return this.$store.getters.getDashboardByRole(this.roleUuid)
}
},
mounted() {
this.getDashboardListFromServer()
},
methods: {
handleSetLineChartData(type) {
this.lineChartData = lineChartData[type]
getDashboardListFromServer() {
if (this.getterDashboard) {
this.dashboardList = this.getterDashboard
} else {
this.$store.dispatch('listDashboard', this.roleUuid)
.then(response => {
this.dashboardList = response.dashboardList
})
}
}
}
}