@mixd-id/web-scaffold 0.1.230406366 → 0.1.230406368
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/components/Image.vue +4 -2
- package/src/components/TextWithTag.vue +40 -27
- package/src/configs/dashboard/data-table.js +9 -0
- package/src/index.js +1 -0
- package/src/utils/dashboard.js +1 -0
- package/src/utils/helpers.js +3 -5
- package/src/utils/helpers.mjs +1 -1
- package/src/utils/preset-selector.mjs +25 -4
- package/src/utils/wss.js +0 -1
- package/src/widgets/Dashboard/BarChart.vue +42 -7
- package/src/widgets/Dashboard/DataTable.vue +125 -0
- package/src/widgets/Dashboard/DataTableSetting.vue +239 -0
- package/src/widgets/Dashboard/Doughnut.vue +47 -6
- package/src/widgets/Dashboard/DoughnutSetting.vue +1 -1
- package/src/widgets/Dashboard/Metric.vue +98 -13
- package/src/widgets/Dashboard/MetricSetting.vue +80 -27
- package/src/widgets/Dashboard/Pie.vue +52 -4
- package/src/widgets/Dashboard/PolarArea.vue +46 -5
- package/src/widgets/Dashboard/ViewSelector.vue +1 -1
- package/src/widgets/Dashboard.vue +25 -4
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$style.comp">
|
|
3
|
+
|
|
4
|
+
<div class="flex justify-center border-b-[1px] border-text-50" v-if="tabItems.length > 1">
|
|
5
|
+
<Tabs :items="tabItems" v-model="value.tabIndex" />
|
|
6
|
+
</div>
|
|
7
|
+
|
|
8
|
+
<div class="flex-1 overflow-y-auto flex flex-col">
|
|
9
|
+
<div v-if="value.tabIndex === 1" class="flex-1 flex flex-col gap-5 p-6">
|
|
10
|
+
|
|
11
|
+
<div class="flex flex-row items-center ">
|
|
12
|
+
<label class="flex-1">Active</label>
|
|
13
|
+
<Switch v-model="value.props.enabled" :readonly="readonly" />
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<div class="flex flex-col gap-1">
|
|
17
|
+
<label class="text-text-400">Label</label>
|
|
18
|
+
<Textbox v-model="value.props.label" maxlength="50" placeholder="Label" :readonly="readonly" />
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div class="flex flex-row items-center">
|
|
22
|
+
<label class="flex-1">Datasource</label>
|
|
23
|
+
<div>
|
|
24
|
+
<Dropdown class="min-w-[150px]"
|
|
25
|
+
:readonly="readonly"
|
|
26
|
+
v-model="value.props.datasourceUid">
|
|
27
|
+
<option v-for="_datasource in datasource"
|
|
28
|
+
:value="_datasource.uid">
|
|
29
|
+
{{ _datasource.name }}
|
|
30
|
+
</option>
|
|
31
|
+
</Dropdown>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div v-else-if="value.tabIndex === 2" class="flex-1 flex flex-col gap-5 p-6">
|
|
38
|
+
|
|
39
|
+
<div>
|
|
40
|
+
<Textbox placeholder="Search..." v-model="search">
|
|
41
|
+
<template #start>
|
|
42
|
+
<div class="pl-3">
|
|
43
|
+
<svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z"/></svg>
|
|
44
|
+
</div>
|
|
45
|
+
</template>
|
|
46
|
+
</Textbox>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<VirtualGrid class="flex-1 border-[1px] border-text-50 rounded-xl"
|
|
50
|
+
container-class="divide-y divide-text-50"
|
|
51
|
+
:items="columns">
|
|
52
|
+
<template #item="{ item }">
|
|
53
|
+
<div class="flex-1 flex flex-row gap-2 items-center p-1 px-3 group">
|
|
54
|
+
<div>
|
|
55
|
+
<Checkbox v-model="item.visible"
|
|
56
|
+
default="true"
|
|
57
|
+
:disabled="readonly"
|
|
58
|
+
@change="$emit('change')" />
|
|
59
|
+
</div>
|
|
60
|
+
<Textbox v-model="item.label2" :placeholder="item.label" :readonly="readonly"
|
|
61
|
+
class="flex-1 border-none bg-transparent" :class="$style.columnTextbox"
|
|
62
|
+
item-class="p-1 px-0" />
|
|
63
|
+
<div class="hidden flex-col justify-center group-hover:flex">
|
|
64
|
+
<button type="button" @click="moveUp(item)">
|
|
65
|
+
<svg width="11" height="11" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M9.39 265.4l127.1-128C143.6 131.1 151.8 128 160 128s16.38 3.125 22.63 9.375l127.1 128c9.156 9.156 11.9 22.91 6.943 34.88S300.9 320 287.1 320H32.01c-12.94 0-24.62-7.781-29.58-19.75S.2333 274.5 9.39 265.4z"/></svg>
|
|
66
|
+
</button>
|
|
67
|
+
<button type="button" @click="moveDown(item)">
|
|
68
|
+
<svg width="11" height="11" class="fill-text-300 hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M310.6 246.6l-127.1 128C176.4 380.9 168.2 384 160 384s-16.38-3.125-22.63-9.375l-127.1-128C.2244 237.5-2.516 223.7 2.438 211.8S19.07 192 32 192h255.1c12.94 0 24.62 7.781 29.58 19.75S319.8 237.5 310.6 246.6z"/></svg>
|
|
69
|
+
</button>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</template>
|
|
73
|
+
</VirtualGrid>
|
|
74
|
+
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
</div>
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
<script>
|
|
82
|
+
|
|
83
|
+
import WebPagePropertySelector from "../WebPageBuilder4/WebPagePropertySelector.vue";
|
|
84
|
+
import {defineAsyncComponent} from "vue";
|
|
85
|
+
import InteractionEdit from "./InteractionEdit.vue";
|
|
86
|
+
|
|
87
|
+
export default{
|
|
88
|
+
components: {
|
|
89
|
+
InteractionEdit,
|
|
90
|
+
HeightSetting: defineAsyncComponent(() => import('../WebPageBuilder4/HeightSetting.vue')),
|
|
91
|
+
WebPagePropertySelector
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
computed: {
|
|
95
|
+
|
|
96
|
+
interactions(){
|
|
97
|
+
if(!Array.isArray(this.value.props.interactions))
|
|
98
|
+
this.value.props.interactions = []
|
|
99
|
+
return this.value.props.interactions
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
selectedDatasource(){
|
|
103
|
+
return this.datasource.find(d => d.uid === this.value.props.datasourceUid)
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
definedProperties(){
|
|
107
|
+
const settings = []
|
|
108
|
+
|
|
109
|
+
for(let key in this.properties){
|
|
110
|
+
if(key in this.value.props){
|
|
111
|
+
settings.push(this.properties[key])
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return settings
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
tabItems(){
|
|
119
|
+
return [
|
|
120
|
+
{ text:'General', value:1 },
|
|
121
|
+
this.value.props.datasourceUid ? { text:'Columns', value:2 } : null,
|
|
122
|
+
]
|
|
123
|
+
.filter(_ => _)
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
columns(){
|
|
127
|
+
const searches = this.search.toLowerCase().split(' ').filter(_ => _.length > 2)
|
|
128
|
+
return (this.value.props.datasourceColumns ?? []).filter(_ => {
|
|
129
|
+
return !_.key.startsWith('_') &&
|
|
130
|
+
(searches.length < 1 || searches.every(search => _.label.toLowerCase().includes(search)))
|
|
131
|
+
})
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
data(){
|
|
137
|
+
return {
|
|
138
|
+
properties: {
|
|
139
|
+
'height': { component:"HeightSetting", text:'Height', group:'Layout' }
|
|
140
|
+
},
|
|
141
|
+
search: ''
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
emits: [ 'change' ],
|
|
146
|
+
|
|
147
|
+
inject: [ 'uploadImage', 'viewTypes' ],
|
|
148
|
+
|
|
149
|
+
props: {
|
|
150
|
+
|
|
151
|
+
datasource: Object,
|
|
152
|
+
|
|
153
|
+
index: Number,
|
|
154
|
+
|
|
155
|
+
readonly: [ Boolean, Number ],
|
|
156
|
+
|
|
157
|
+
value: {
|
|
158
|
+
type: Object,
|
|
159
|
+
required: true
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
viewType: String,
|
|
163
|
+
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
methods: {
|
|
167
|
+
|
|
168
|
+
addInteraction(obj){
|
|
169
|
+
this.$util.push(this.interactions, obj)
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
applyDefault(){
|
|
173
|
+
if(!this.value.props.datasourceUid && this.datasource?.length === 1){
|
|
174
|
+
this.value.props.datasourceUid = this.datasource[0].uid
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if(!this.value.tabIndex)
|
|
178
|
+
this.value.tabIndex = 1
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
generateColumns(){
|
|
182
|
+
this.value.props.datasourceColumns = (this.selectedDatasource.pivot?.enabled ?
|
|
183
|
+
this.selectedDatasource.pivot.columns :
|
|
184
|
+
this.selectedDatasource.columns)
|
|
185
|
+
.map((_, __) => ({
|
|
186
|
+
..._,
|
|
187
|
+
visible: __ < 10
|
|
188
|
+
}))
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
moveDown(item){
|
|
192
|
+
const index = this.value.props.datasourceColumns.indexOf(item)
|
|
193
|
+
if(index + 1 < this.value.props.datasourceColumns.length){
|
|
194
|
+
this.value.props.datasourceColumns.splice(index + 1, 0, this.value.props.datasourceColumns.splice(index, 1)[0])
|
|
195
|
+
this.$emit('change')
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
moveUp(item){
|
|
200
|
+
const index = this.value.props.datasourceColumns.indexOf(item)
|
|
201
|
+
if(index - 1 >= 0){
|
|
202
|
+
this.value.props.datasourceColumns.splice(index - 1, 0, this.value.props.datasourceColumns.splice(index, 1)[0])
|
|
203
|
+
this.$emit('change')
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
mounted() {
|
|
211
|
+
this.applyDefault()
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
watch: {
|
|
215
|
+
|
|
216
|
+
'value.props.datasourceUid'(to){
|
|
217
|
+
if(to){
|
|
218
|
+
this.generateColumns()
|
|
219
|
+
this.$emit('change')
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
</script>
|
|
228
|
+
|
|
229
|
+
<style module>
|
|
230
|
+
|
|
231
|
+
.comp{
|
|
232
|
+
@apply flex-1 flex flex-col;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.columnTextbox input::placeholder{
|
|
236
|
+
@apply text-text;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
</style>
|
|
@@ -22,7 +22,7 @@ import { Doughnut } from 'vue-chartjs'
|
|
|
22
22
|
import Chart from 'chart.js/auto'
|
|
23
23
|
import {color} from "chart.js/helpers";
|
|
24
24
|
import {strVars} from "../../utils/helpers.mjs";
|
|
25
|
-
import {
|
|
25
|
+
import {useEmitter} from "../../utils/event-bus";
|
|
26
26
|
|
|
27
27
|
export default{
|
|
28
28
|
|
|
@@ -93,14 +93,43 @@ export default{
|
|
|
93
93
|
'#FFCE54',
|
|
94
94
|
'#ED5565',
|
|
95
95
|
'#EC87C0'
|
|
96
|
-
]
|
|
96
|
+
],
|
|
97
|
+
readyState: 1,
|
|
98
|
+
value: null,
|
|
97
99
|
}
|
|
98
100
|
},
|
|
99
101
|
|
|
100
|
-
inject: [ 'selectPreset' ],
|
|
102
|
+
inject: [ 'getSrc', 'getViewedPreset', 'getQueryFilters', 'selectPreset', 'socket' ],
|
|
101
103
|
|
|
102
104
|
methods: {
|
|
103
105
|
|
|
106
|
+
load(){
|
|
107
|
+
const preset = this.getViewedPreset()
|
|
108
|
+
const {name, datasource} = preset
|
|
109
|
+
|
|
110
|
+
this.readyState = 2
|
|
111
|
+
this.socket.send(this.getSrc(), {
|
|
112
|
+
name,
|
|
113
|
+
views: [{
|
|
114
|
+
...this.$props,
|
|
115
|
+
type: 'Doughnut'
|
|
116
|
+
}],
|
|
117
|
+
datasource: datasource.map(_datasource => {
|
|
118
|
+
return {
|
|
119
|
+
..._datasource,
|
|
120
|
+
filters: [
|
|
121
|
+
...(_datasource.filters ?? []),
|
|
122
|
+
...(this.getQueryFilters()[_datasource.uid] ?? [])
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
.then(_ => {
|
|
128
|
+
this.value = _[this.uid]
|
|
129
|
+
})
|
|
130
|
+
.finally(_ => this.readyState = 1)
|
|
131
|
+
},
|
|
132
|
+
|
|
104
133
|
onClick(e, segments){
|
|
105
134
|
|
|
106
135
|
const clickInteractions = (this.interactions ?? []).filter(_ => _.event === 'click')
|
|
@@ -135,15 +164,27 @@ export default{
|
|
|
135
164
|
|
|
136
165
|
},
|
|
137
166
|
|
|
138
|
-
|
|
167
|
+
mounted() {
|
|
168
|
+
this.load()
|
|
169
|
+
|
|
170
|
+
this.emitter = useEmitter()
|
|
171
|
+
this.emitter.on(`${this.uid}.load`, () => {
|
|
172
|
+
this.load()
|
|
173
|
+
})
|
|
174
|
+
this.emitter.on(`dashboard.load`, () => {
|
|
175
|
+
this.load()
|
|
176
|
+
})
|
|
177
|
+
},
|
|
139
178
|
|
|
140
179
|
props: {
|
|
141
180
|
|
|
142
181
|
label: String,
|
|
143
182
|
|
|
144
|
-
value: Object,
|
|
145
|
-
|
|
146
183
|
column: String,
|
|
184
|
+
columnModifier: String,
|
|
185
|
+
|
|
186
|
+
rows: String,
|
|
187
|
+
rowsModifier: String,
|
|
147
188
|
|
|
148
189
|
datasourceUid: String,
|
|
149
190
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<Dropdown class="min-w-[150px]"
|
|
12
12
|
:readonly="readonly"
|
|
13
13
|
v-model="value.props.datasourceUid"
|
|
14
|
-
@change="delete value.props.columns;">
|
|
14
|
+
@change="delete value.props.columns; $emit('change')">
|
|
15
15
|
<option disabled selected>Select Datasource</option>
|
|
16
16
|
<option v-for="obj in datasource"
|
|
17
17
|
:value="obj.uid">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div :class="$style.comp">
|
|
3
3
|
<label class="text-text-400 text-ellipsis overflow-hidden whitespace-nowrap">{{ label }}</label>
|
|
4
|
-
<div class="flex flex-row gap-2">
|
|
4
|
+
<div class="flex flex-row items-end gap-2">
|
|
5
5
|
<h1 v-if="readyState === 1" class="text-ellipsis overflow-hidden whitespace-nowrap" :class="column2Enabled ? '' : 'text-green-600'">
|
|
6
6
|
{{ cValue }}
|
|
7
7
|
</h1>
|
|
@@ -10,8 +10,16 @@
|
|
|
10
10
|
</h1>
|
|
11
11
|
|
|
12
12
|
<div class="flex flex-col" v-if="column2Enabled && readyState === 1">
|
|
13
|
-
<div class="text-sm text-ellipsis whitespace-nowrap overflow-hidden"
|
|
14
|
-
|
|
13
|
+
<div class="text-sm text-ellipsis whitespace-nowrap overflow-hidden"
|
|
14
|
+
:class="showPercentage ? '' : 'text-green-600'">
|
|
15
|
+
{{ ccValue }}
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div v-if="showPercentage"
|
|
19
|
+
class="text-sm text-ellipsis whitespace-nowrap overflow-hidden"
|
|
20
|
+
:class="value?.comparedPercent <= 50 ? 'text-red-600' : 'text-green-600'">
|
|
21
|
+
{{ value?.comparedPercent }}
|
|
22
|
+
</div>
|
|
15
23
|
</div>
|
|
16
24
|
</div>
|
|
17
25
|
</div>
|
|
@@ -19,29 +27,44 @@
|
|
|
19
27
|
|
|
20
28
|
<script>
|
|
21
29
|
|
|
22
|
-
import {
|
|
30
|
+
import {useEmitter} from "../../utils/event-bus";
|
|
31
|
+
import dayjs from "dayjs";
|
|
23
32
|
|
|
24
33
|
export default{
|
|
25
34
|
|
|
26
35
|
computed: {
|
|
27
36
|
|
|
28
37
|
cValue(){
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return
|
|
38
|
+
if(!this.column) return 'N/A'
|
|
39
|
+
const column = this.datasource?.columns.find(_ => _.key === this.column)
|
|
40
|
+
return this.getValue(this.value?.value, column)
|
|
32
41
|
},
|
|
33
42
|
|
|
34
43
|
ccValue(){
|
|
35
44
|
if(!this.column2Enabled) return
|
|
45
|
+
if(!this.value?.comparedValue) return
|
|
36
46
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
47
|
+
const column = this.datasource?.columns.find(_ => _.key === this.column2)
|
|
48
|
+
return this.getValue(this.value?.comparedValue, column)
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
datasource(){
|
|
52
|
+
const preset = this.getViewedPreset()
|
|
53
|
+
if(preset){
|
|
54
|
+
return preset.datasource.find(_ => _.uid === this.datasourceUid)
|
|
55
|
+
}
|
|
40
56
|
}
|
|
41
57
|
|
|
42
58
|
},
|
|
43
59
|
|
|
44
|
-
|
|
60
|
+
data(){
|
|
61
|
+
return {
|
|
62
|
+
value: null,
|
|
63
|
+
readyState: 1,
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
inject: [ 'getSrc', 'getViewedPreset', 'getQueryFilters', 'socket' ],
|
|
45
68
|
|
|
46
69
|
props: {
|
|
47
70
|
|
|
@@ -54,12 +77,74 @@ export default{
|
|
|
54
77
|
|
|
55
78
|
datasourceUid: String,
|
|
56
79
|
|
|
80
|
+
showPercentage: [ Number, Boolean ],
|
|
81
|
+
|
|
57
82
|
label: String,
|
|
58
83
|
|
|
59
|
-
|
|
84
|
+
uid: String
|
|
85
|
+
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
methods: {
|
|
89
|
+
|
|
90
|
+
getValue(val, column){
|
|
91
|
+
let value
|
|
92
|
+
|
|
93
|
+
switch(column?.type){
|
|
94
|
+
|
|
95
|
+
case 'date':
|
|
96
|
+
value = dayjs(val).format('D MMM')
|
|
97
|
+
break
|
|
98
|
+
|
|
99
|
+
default:
|
|
100
|
+
value = parseFloat(val)
|
|
101
|
+
value = isNaN(value) ?
|
|
102
|
+
'N/A' :
|
|
103
|
+
value.toLocaleString()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return value
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
load(){
|
|
110
|
+
const preset = this.getViewedPreset()
|
|
111
|
+
const {name, datasource} = preset
|
|
112
|
+
|
|
113
|
+
this.readyState = 2
|
|
114
|
+
this.socket.send(this.getSrc(), {
|
|
115
|
+
name,
|
|
116
|
+
views: [{
|
|
117
|
+
...this.$props,
|
|
118
|
+
type: 'Metric'
|
|
119
|
+
}],
|
|
120
|
+
datasource: datasource.map(_datasource => {
|
|
121
|
+
return {
|
|
122
|
+
..._datasource,
|
|
123
|
+
filters: [
|
|
124
|
+
...(_datasource.filters ?? []),
|
|
125
|
+
...(this.getQueryFilters()[_datasource.uid] ?? [])
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
.then(_ => {
|
|
131
|
+
this.value = _[this.uid]
|
|
132
|
+
})
|
|
133
|
+
.finally(_ => this.readyState = 1)
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
},
|
|
60
137
|
|
|
61
|
-
|
|
138
|
+
mounted() {
|
|
139
|
+
this.load()
|
|
62
140
|
|
|
141
|
+
this.emitter = useEmitter()
|
|
142
|
+
this.emitter.on(`${this.uid}.load`, () => {
|
|
143
|
+
this.load()
|
|
144
|
+
})
|
|
145
|
+
this.emitter.on(`dashboard.load`, () => {
|
|
146
|
+
this.load()
|
|
147
|
+
})
|
|
63
148
|
}
|
|
64
149
|
|
|
65
150
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="flex flex-col gap-
|
|
2
|
+
<div class="flex flex-col gap-8 p-6">
|
|
3
3
|
|
|
4
|
-
<div class="flex flex-row items-center">
|
|
4
|
+
<div class="flex flex-row items-center" @click="log(value)">
|
|
5
5
|
<label class="flex-1">Active</label>
|
|
6
6
|
<Switch v-model="value.props.enabled" :readonly="readonly" @change="$emit('change')" />
|
|
7
7
|
</div>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<Dropdown class="min-w-[150px]"
|
|
16
16
|
:readonly="readonly"
|
|
17
17
|
v-model="value.props.datasourceUid"
|
|
18
|
-
@change="delete value.props.columns">
|
|
18
|
+
@change="delete value.props.columns;$emit('change')">
|
|
19
19
|
<option v-for="obj in datasource"
|
|
20
20
|
:value="obj.uid">
|
|
21
21
|
{{ obj.name }}
|
|
@@ -28,25 +28,13 @@
|
|
|
28
28
|
<Textbox v-model="value.props.label"
|
|
29
29
|
maxlength="30"
|
|
30
30
|
placeholder="Label"
|
|
31
|
-
:readonly="readonly"
|
|
32
|
-
@keyup.enter="$emit('change')"
|
|
33
|
-
@blur="$emit('change')" />
|
|
31
|
+
:readonly="readonly" />
|
|
34
32
|
</div>
|
|
35
33
|
|
|
36
|
-
<div v-if="value.props.datasourceUid" class="flex flex-col gap-
|
|
34
|
+
<div v-if="value.props.datasourceUid" class="flex flex-col gap-8">
|
|
37
35
|
<div class="flex flex-col gap-1">
|
|
38
36
|
<label class="flex-1">Column</label>
|
|
39
37
|
<div class="flex flex-row gap-2">
|
|
40
|
-
<Dropdown class="min-w-[150px] flex-1"
|
|
41
|
-
:readonly="readonly"
|
|
42
|
-
v-model="value.props.column"
|
|
43
|
-
@change="$emit('change')">
|
|
44
|
-
<option v-for="column in selectedDatasourceColumns"
|
|
45
|
-
:value="column.key">
|
|
46
|
-
{{ selectedDatasource.pivot?.enabled ? column.key : column.label }}
|
|
47
|
-
</option>
|
|
48
|
-
</Dropdown>
|
|
49
|
-
|
|
50
38
|
<Dropdown class="w-[125px]"
|
|
51
39
|
:readonly="readonly"
|
|
52
40
|
v-model="value.props.columnModifier"
|
|
@@ -60,6 +48,16 @@
|
|
|
60
48
|
<option value="first">First</option>
|
|
61
49
|
<option value="last">Last</option>
|
|
62
50
|
</Dropdown>
|
|
51
|
+
|
|
52
|
+
<Dropdown class="min-w-[150px] flex-1"
|
|
53
|
+
:readonly="readonly"
|
|
54
|
+
v-model="value.props.column"
|
|
55
|
+
@change="$emit('change')">
|
|
56
|
+
<option v-for="column in selectedDatasourceColumns"
|
|
57
|
+
:value="column.key">
|
|
58
|
+
{{ selectedDatasource.pivot?.enabled ? column.key : column.label }}
|
|
59
|
+
</option>
|
|
60
|
+
</Dropdown>
|
|
63
61
|
</div>
|
|
64
62
|
</div>
|
|
65
63
|
|
|
@@ -70,16 +68,6 @@
|
|
|
70
68
|
</Checkbox>
|
|
71
69
|
</div>
|
|
72
70
|
<div class="flex flex-row gap-2">
|
|
73
|
-
<Dropdown class="min-w-[150px] flex-1"
|
|
74
|
-
:readonly="readonly || !value.props.column2Enabled"
|
|
75
|
-
v-model="value.props.column2"
|
|
76
|
-
@change="$emit('change')">
|
|
77
|
-
<option v-for="column in selectedDatasourceColumns"
|
|
78
|
-
:value="column.key">
|
|
79
|
-
{{ selectedDatasource.pivot?.enabled ? column.key : column.label }}
|
|
80
|
-
</option>
|
|
81
|
-
</Dropdown>
|
|
82
|
-
|
|
83
71
|
<Dropdown class="w-[125px]"
|
|
84
72
|
:readonly="readonly || !value.props.column2Enabled"
|
|
85
73
|
v-model="value.props.column2Modifier"
|
|
@@ -93,6 +81,32 @@
|
|
|
93
81
|
<option value="first">First</option>
|
|
94
82
|
<option value="last">Last</option>
|
|
95
83
|
</Dropdown>
|
|
84
|
+
|
|
85
|
+
<Dropdown class="min-w-[150px] flex-1"
|
|
86
|
+
:readonly="readonly || !value.props.column2Enabled"
|
|
87
|
+
v-model="value.props.column2"
|
|
88
|
+
@change="$emit('change')">
|
|
89
|
+
<option v-for="column in selectedDatasourceColumns"
|
|
90
|
+
:value="column.key">
|
|
91
|
+
{{ selectedDatasource.pivot?.enabled ? column.key : column.label }}
|
|
92
|
+
</option>
|
|
93
|
+
</Dropdown>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div class="flex flex-col gap-2" v-if="canShowPercentage">
|
|
98
|
+
<div>
|
|
99
|
+
<Checkbox v-model="value.props.showPercentage" :disabled="readonly">
|
|
100
|
+
Show Percentage
|
|
101
|
+
</Checkbox>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="flex flex-row gap-2">
|
|
104
|
+
<Dropdown class="w-full"
|
|
105
|
+
:readonly="readonly || !value.props.column2Enabled"
|
|
106
|
+
v-model="value.props.showPercentageOpt"
|
|
107
|
+
@change="$emit('change')">
|
|
108
|
+
<option v-for="arr in showPercentageOptValues" :value="arr[0]">{{ arr[1] }}</option>
|
|
109
|
+
</Dropdown>
|
|
96
110
|
</div>
|
|
97
111
|
</div>
|
|
98
112
|
|
|
@@ -113,10 +127,49 @@ export default{
|
|
|
113
127
|
|
|
114
128
|
computed: {
|
|
115
129
|
|
|
130
|
+
column1(){
|
|
131
|
+
if(!this.value || !this.value.props.column) return
|
|
132
|
+
return this.selectedDatasource.columns.find(_ => _.key === this.value.props.column)
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
column2(){
|
|
136
|
+
if(!this.value || !this.value.props.column2) return
|
|
137
|
+
return this.selectedDatasource.columns.find(_ => _.key === this.value.props.column2)
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
canShowPercentage(){
|
|
141
|
+
return this.column1 && this.column2 && this.column1.type === this.column2.type
|
|
142
|
+
},
|
|
143
|
+
|
|
116
144
|
selectedDatasource(){
|
|
117
145
|
return this.datasource.find(d => d.uid === this.value.props.datasourceUid)
|
|
118
146
|
},
|
|
119
147
|
|
|
148
|
+
showPercentageOptValues(){
|
|
149
|
+
if(!this.canShowPercentage) return
|
|
150
|
+
|
|
151
|
+
switch(this.column1.type){
|
|
152
|
+
|
|
153
|
+
case 'date':
|
|
154
|
+
return [
|
|
155
|
+
[ 'day', 'Day' ],
|
|
156
|
+
[ 'hour', 'Hour' ],
|
|
157
|
+
[ 'minute', 'Minute' ],
|
|
158
|
+
[ 'month', 'Month' ],
|
|
159
|
+
[ 'week', 'Week' ],
|
|
160
|
+
[ 'year', 'Year' ],
|
|
161
|
+
]
|
|
162
|
+
|
|
163
|
+
case 'number':
|
|
164
|
+
default:
|
|
165
|
+
return [
|
|
166
|
+
[ 'percentage', 'Percentage' ],
|
|
167
|
+
[ 'number', 'Number' ],
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
|
|
120
173
|
selectedDatasourceColumns(){
|
|
121
174
|
if(!this.selectedDatasource) return []
|
|
122
175
|
|