@mixd-id/web-scaffold 0.1.230406345 → 0.1.230406347

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mixd-id/web-scaffold",
3
3
  "private": false,
4
- "version": "0.1.230406345",
4
+ "version": "0.1.230406347",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -0,0 +1,256 @@
1
+ <template>
2
+ <div :class="$style.comp">
3
+ <div v-if="apiKey" ref="map" class="flex-1"></div>
4
+ <div v-else class="flex-1 flex items-center justify-center text-text-400">{{ $t('Google maps api required') }}</div>
5
+ </div>
6
+ </template>
7
+
8
+ <script>
9
+
10
+ import { Loader } from '@googlemaps/js-api-loader';
11
+
12
+ export default{
13
+
14
+ name: "GHeatMaps",
15
+
16
+ props: {
17
+ column: String,
18
+
19
+ value: Object,
20
+
21
+ apiKey: {
22
+ type: String,
23
+ default: 'AIzaSyBmzMlmrR5St-njtN--64y_oLTa9FDLYfQ'
24
+ },
25
+ },
26
+
27
+ data(){
28
+ return {
29
+ map: null,
30
+ zoom: 4,
31
+ center: null,
32
+ }
33
+ },
34
+
35
+ mounted() {
36
+ this.load()
37
+ },
38
+
39
+ methods: {
40
+
41
+ load(){
42
+ if(!this.apiKey) return
43
+ if(!Array.isArray(this.datasets) || this.datasets.length < 1)
44
+ return
45
+
46
+ const loader = new Loader({
47
+ apiKey: this.apiKey // "AIzaSyBmzMlmrR5St-njtN--64y_oLTa9FDLYfQ"
48
+ })
49
+
50
+ loader.load()
51
+ .then(async () => {
52
+
53
+ const { Map } = await google.maps.importLibrary([ "maps" ]);
54
+ await google.maps.importLibrary("visualization", ["HeatmapLayer"]);
55
+
56
+ var center = new google.maps.LatLng(
57
+ this.center ? this.center[0] : -6.2088,
58
+ this.center ? this.center[1] : 106.8456);
59
+
60
+ this.map = new Map(this.$refs.map, {
61
+ center: center,
62
+ zoom: this.zoom ?? 4,
63
+ mapTypeId: 'roadmap',
64
+ streetViewControl: false,
65
+ keyboardShortcuts: false,
66
+ styles: [
67
+ { elementType: "geometry", stylers: [{ color: "#242f3e" }] },
68
+ { elementType: "labels.text.stroke", stylers: [{ color: "#242f3e" }] },
69
+ { elementType: "labels.text.fill", stylers: [{ color: "#746855" }] },
70
+ {
71
+ featureType: "administrative.locality",
72
+ elementType: "labels.text.fill",
73
+ stylers: [{ color: "#d59563" }],
74
+ },
75
+ {
76
+ featureType: "poi",
77
+ elementType: "labels.text.fill",
78
+ stylers: [{ color: "#d59563" }],
79
+ },
80
+ {
81
+ featureType: "poi.park",
82
+ elementType: "geometry",
83
+ stylers: [{ color: "#263c3f" }],
84
+ },
85
+ {
86
+ featureType: "poi.park",
87
+ elementType: "labels.text.fill",
88
+ stylers: [{ color: "#6b9a76" }],
89
+ },
90
+ {
91
+ featureType: "road",
92
+ elementType: "geometry",
93
+ stylers: [{ color: "#38414e" }],
94
+ },
95
+ {
96
+ featureType: "road",
97
+ elementType: "geometry.stroke",
98
+ stylers: [{ color: "#212a37" }],
99
+ },
100
+ {
101
+ featureType: "road",
102
+ elementType: "labels.text.fill",
103
+ stylers: [{ color: "#9ca5b3" }],
104
+ },
105
+ {
106
+ featureType: "road.highway",
107
+ elementType: "geometry",
108
+ stylers: [{ color: "#746855" }],
109
+ },
110
+ {
111
+ featureType: "road.highway",
112
+ elementType: "geometry.stroke",
113
+ stylers: [{ color: "#1f2835" }],
114
+ },
115
+ {
116
+ featureType: "road.highway",
117
+ elementType: "labels.text.fill",
118
+ stylers: [{ color: "#f3d19c" }],
119
+ },
120
+ {
121
+ featureType: "transit",
122
+ elementType: "geometry",
123
+ stylers: [{ color: "#2f3948" }],
124
+ },
125
+ {
126
+ featureType: "transit.station",
127
+ elementType: "labels.text.fill",
128
+ stylers: [{ color: "#d59563" }],
129
+ },
130
+ {
131
+ featureType: "water",
132
+ elementType: "geometry",
133
+ stylers: [{ color: "#17263c" }],
134
+ },
135
+ {
136
+ featureType: "water",
137
+ elementType: "labels.text.fill",
138
+ stylers: [{ color: "#515c6d" }],
139
+ },
140
+ {
141
+ featureType: "water",
142
+ elementType: "labels.text.stroke",
143
+ stylers: [{ color: "#17263c" }],
144
+ },
145
+ ]
146
+ });
147
+
148
+ google.maps.event.addListener(this.map, 'zoom_changed', () => {
149
+ this.zoom = this.map.getZoom();
150
+ });
151
+
152
+ google.maps.event.addListener(this.map, 'center_changed', () => {
153
+ this.center = [
154
+ this.map.getCenter().lat(),
155
+ this.map.getCenter().lng()
156
+ ]
157
+ });
158
+
159
+ var heatMapData = [
160
+ /*{location: new google.maps.LatLng(37.782, -122.447), weight: 0.5},
161
+ new google.maps.LatLng(37.782, -122.445),
162
+ {location: new google.maps.LatLng(37.782, -122.443), weight: 2},
163
+ {location: new google.maps.LatLng(37.782, -122.441), weight: 3},
164
+ {location: new google.maps.LatLng(37.782, -122.439), weight: 2},
165
+ new google.maps.LatLng(37.782, -122.437),
166
+ {location: new google.maps.LatLng(37.782, -122.435), weight: 0.5},
167
+
168
+ {location: new google.maps.LatLng(37.785, -122.447), weight: 3},
169
+ {location: new google.maps.LatLng(37.785, -122.445), weight: 2},
170
+ new google.maps.LatLng(37.785, -122.443),
171
+ {location: new google.maps.LatLng(37.785, -122.441), weight: 0.5},
172
+ new google.maps.LatLng(37.785, -122.439),
173
+ {location: new google.maps.LatLng(37.785, -122.437), weight: 2},
174
+ {location: new google.maps.LatLng(37.785, -122.435), weight: 3}*/
175
+ ];
176
+
177
+ this.datasets.forEach((item) => {
178
+ if(item[0] && item[1] && item[2]){
179
+ heatMapData.push({
180
+ location: new google.maps.LatLng(item[0], item[1]),
181
+ weight: item[2]
182
+ })
183
+ }
184
+ })
185
+
186
+ var heatmap = new google.maps.visualization.HeatmapLayer({
187
+ data: heatMapData,
188
+ dissipating: true
189
+ });
190
+ heatmap.set('radius', parseInt(this.mapRadius ?? 12))
191
+ heatmap.setMap(this.map);
192
+
193
+ const gradient = [
194
+ "rgba(0, 255, 255, 0)",
195
+ "rgba(0, 255, 255, 1)",
196
+ "rgba(0, 191, 255, 1)",
197
+ "rgba(0, 127, 255, 1)",
198
+ "rgba(0, 63, 255, 1)",
199
+ "rgba(0, 0, 255, 1)",
200
+ "rgba(0, 0, 223, 1)",
201
+ "rgba(0, 0, 191, 1)",
202
+ "rgba(0, 0, 159, 1)",
203
+ "rgba(0, 0, 127, 1)",
204
+ "rgba(63, 0, 91, 1)",
205
+ "rgba(127, 0, 63, 1)",
206
+ "rgba(191, 0, 31, 1)",
207
+ "rgba(255, 0, 0, 1)",
208
+ ];
209
+ heatmap.set("gradient", gradient);
210
+
211
+ //console.log('Gmap, data count:', this.data.length)
212
+ });
213
+ }
214
+
215
+ },
216
+
217
+ computed: {
218
+
219
+ datasets(){
220
+ if(!Array.isArray(this.value?.items)) return []
221
+
222
+ const grouped = this.value.items.reduce((acc, item) => {
223
+ if(!acc[item[this.column]]) acc[item[this.column]] = 0
224
+ acc[item[this.column]] += 1
225
+ return acc
226
+ }, {})
227
+
228
+ const datasets = []
229
+ for(let coord in grouped){
230
+ datasets.push([parseFloat(coord.split(',')[0]), parseFloat(coord.split(',')[1]), grouped[coord]])
231
+ }
232
+
233
+ return datasets
234
+ }
235
+
236
+ },
237
+
238
+ watch: {
239
+
240
+ datasets(){
241
+ this.load()
242
+ }
243
+
244
+ }
245
+
246
+ }
247
+
248
+ </script>
249
+
250
+ <style module>
251
+
252
+ .comp{
253
+ @apply flex flex-col h-[40vh] bg-base-500;
254
+ }
255
+
256
+ </style>
@@ -464,7 +464,6 @@ export default{
464
464
  columns: [
465
465
  { key:attr, visible:true }
466
466
  ],
467
- itemsPerPage: 1000,
468
467
  filters: [
469
468
  {
470
469
  key:key, value: [ { operator:'in', value:reqItems } ]
@@ -112,9 +112,10 @@ export default{
112
112
  this.context = context
113
113
 
114
114
  if(this.hash){
115
+ const hash = context.hash ?? this.hash
115
116
  this.$router.push({
116
117
  ...this.$route,
117
- hash: this.$route.hash.replace(this.hash, '') + this.hash
118
+ hash: this.$route.hash.replace(hash, '') + hash
118
119
  })
119
120
  }
120
121
  else{
@@ -573,8 +573,8 @@ export default{
573
573
  enumText = typeParam && typeParam.text ? typeParam.text : enumText
574
574
  }
575
575
 
576
- if(this.enumCache && this.enumCache[column.key] && this.enumCache[column.key][value]){
577
- enumText = this.enumCache[column.key][value].text ?? enumText
576
+ else if(this.enumCache && this.enumCache[column.key] && this.enumCache[column.key][value?.toString()]){
577
+ enumText = this.enumCache[column.key][value?.toString()].text ?? enumText
578
578
  }
579
579
 
580
580
  text = enumText ? enumText : text
@@ -0,0 +1,9 @@
1
+ export const component = {
2
+
3
+ type:"GHeatMaps",
4
+
5
+ props:{
6
+
7
+ }
8
+
9
+ }
package/src/index.js CHANGED
@@ -336,8 +336,6 @@ export default{
336
336
  app.directive('tooltip', (el, binding) => {
337
337
  if(!binding.value) return
338
338
 
339
- console.log(binding)
340
-
341
339
  if(!el.getAttribute('data-tooltip')) {
342
340
 
343
341
  const uid = uniqid()
@@ -528,6 +526,7 @@ export default{
528
526
  app.component('ErrorText', defineAsyncComponent(() => import("./components/ErrorText.vue")))
529
527
  app.component('Feed', defineAsyncComponent(() => import("./components/Feed.vue")))
530
528
  app.component('Gmaps', defineAsyncComponent(() => import("./components/Gmaps.vue")))
529
+ app.component('GHeatMaps', defineAsyncComponent(() => import("./components/GHeatMaps.vue")))
531
530
  app.component('HTMLEditor', defineAsyncComponent(() => import("./components/HTMLEditor.vue")))
532
531
  app.component('Switch', defineAsyncComponent(() => import("./components/Switch.vue")))
533
532
  app.component('IconMenu', defineAsyncComponent(() => import("./components/IconMenu.vue")))
@@ -45,15 +45,23 @@ const backgroundColors = [
45
45
  ]
46
46
 
47
47
  const getModelFromDatasource = async (datasource, opt) => {
48
- if(!opt.conn.models[datasource.datasourceUid]){
49
- const tableColumns = await getSequelizeColumns(datasource.columns, opt)
50
48
 
51
- opt.conn.define(datasource.datasourceUid, tableColumns, {
52
- tableName: `datasource_${datasource.datasourceUid}`
53
- })
54
- }
49
+ switch(datasource.type){
50
+
51
+ case 2:
52
+ return opt.conn.models[datasource.key]
53
+
54
+ default:
55
+ if(!opt.conn.models[datasource.datasourceUid]){
56
+ const tableColumns = await getSequelizeColumns(datasource.columns, opt)
55
57
 
56
- return opt.conn.models[datasource.datasourceUid]
58
+ opt.conn.define(datasource.datasourceUid, tableColumns, {
59
+ tableName: `datasource_${datasource.datasourceUid}`
60
+ })
61
+ }
62
+
63
+ return opt.conn.models[datasource.datasourceUid]
64
+ }
57
65
  }
58
66
 
59
67
  const getDatasourceItems = async (datasource, opt) => {
@@ -524,8 +524,16 @@ const dayTimeRange = (params, value) => {
524
524
  })
525
525
  }
526
526
 
527
+ function capitalize(str) {
528
+ return (str ?? '')
529
+ .toString()
530
+ .split(' ')
531
+ .map(_ => _.charAt(0).toUpperCase() + _.slice(1).toLowerCase())
532
+ .join(' ')
533
+ }
527
534
 
528
535
  module.exports = {
536
+ capitalize,
529
537
  ceil,
530
538
  floor,
531
539
  ftWildcard,
@@ -1270,9 +1270,11 @@ const presetToSequelizeList = async(preset, {
1270
1270
  order:initialOrder
1271
1271
  }) => {
1272
1272
 
1273
+ const updatedAtField = model.rawAttributes['updatedAt'] ?? model.rawAttributes['updated_at']
1274
+
1273
1275
  if(!initialOrder){
1274
1276
  initialOrder = [
1275
- [ 'updatedAt', 'desc' ],
1277
+ [ updatedAtField.fieldName, 'desc' ],
1276
1278
  [ 'id', 'desc' ],
1277
1279
  ]
1278
1280
  }
@@ -1381,7 +1383,8 @@ const presetToSequelizeList = async(preset, {
1381
1383
  ...replacements,
1382
1384
  ...filterReplacements,
1383
1385
  ...searchReplacements
1384
- ]
1386
+ ],
1387
+ itemsPerPage
1385
1388
  }
1386
1389
  }
1387
1390
  }
@@ -1,19 +1,34 @@
1
1
  <template>
2
- <div :class="$style.comp" class="flex flex-col gap-3">
2
+ <div :class="$style.comp">
3
3
 
4
- <label class="text-text-400 text-ellipsis overflow-hidden whitespace-nowrap">{{ label }}</label>
4
+ <div v-if="readyState === 2" class="flex-1 flex">
5
+ <div :style="barStyles" class="flex-1 flex items-center justify-center">
6
+ <svg class="animate-spin aspect-square w-[24px] h-[24px]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
7
+ </div>
8
+ </div>
5
9
 
6
- <div class="flex-1">
7
- <Bar :style="barStyles"
8
- :options="chartOptions"
9
- :data="chartData"/>
10
+ <div v-else-if="isError" class="flex-1 flex">
11
+ <div :style="barStyles" class="flex-1 flex items-center justify-center">
12
+ <p class="text-text-300">{{ value.message }}</p>
13
+ </div>
10
14
  </div>
11
15
 
12
- <div v-if="yAxeMultiple" class="flex flex-row justify-center gap-1">
13
- <div v-for="(yLegend, idx) in yLegends"
14
- class="p-0.5 px-1 rounded-lg text-black cursor-default text-xs w-[50px] text-center text-ellipsis overflow-hidden whitespace-nowrap capitalize"
15
- :style="{ backgroundColor:yLegend.color }">
16
- {{ (yLegend.key ?? '').toString().split(' ')[0].toLowerCase() }}
16
+ <div v-else class="flex flex-col gap-3">
17
+ <label class="text-text-400 text-ellipsis overflow-hidden whitespace-nowrap">{{ label }}</label>
18
+
19
+ <div class="flex-1">
20
+ <Bar :style="barStyles"
21
+ :options="chartOptions"
22
+ :data="chartData"/>
23
+ </div>
24
+
25
+ <div v-if="yAxeMultiple && yLegends?.length < 10" class="flex flex-row flex-wrap justify-center gap-1">
26
+ <div v-for="yLegend in yLegends"
27
+ v-tooltip="`${yLegend.key}`"
28
+ class="p-0.5 px-1 rounded-lg text-black cursor-default text-xs min-w-[50px] max-w-[60px] text-center text-ellipsis overflow-hidden whitespace-nowrap capitalize"
29
+ :style="{ backgroundColor:yLegend.color }">
30
+ {{ (yLegend.key ?? '').toString().split(' ')[0].toLowerCase() }}
31
+ </div>
17
32
  </div>
18
33
  </div>
19
34
 
@@ -25,7 +40,7 @@
25
40
  import {Bar} from 'vue-chartjs'
26
41
  import Chart from 'chart.js/auto'
27
42
  import { color } from 'chart.js/helpers'
28
- import {useDatasource} from "../../stores/datasource";
43
+ import {strVars} from "../../utils/helpers.mjs";
29
44
 
30
45
  export default{
31
46
 
@@ -51,7 +66,7 @@ export default{
51
66
  datasets: this.value.datasets.map(_ => {
52
67
  return {
53
68
  ..._,
54
- borderWidth: 1,
69
+ borderWidth: 0,
55
70
  borderColor: function(context, options) {
56
71
  const hex = options.color;
57
72
  return color(hex).darken(0.5).rgbString()
@@ -76,7 +91,7 @@ export default{
76
91
  return `${tooltipItems[0].label}`;
77
92
  },
78
93
  label: (tooltipItem) => {
79
- if(this.usePercentage)
94
+ if(this.yAxeMultiple && this.usePercentage)
80
95
  return `${tooltipItem.dataset.label}: ${tooltipItem.dataset.data0[tooltipItem.dataIndex]} (${tooltipItem.raw}%)`
81
96
  return `${tooltipItem.dataset.label}: ${tooltipItem.raw}`;
82
97
  }
@@ -94,10 +109,13 @@ export default{
94
109
  ticks: {
95
110
  callback: function(value) {
96
111
  const label = this.getLabelForValue(value) ?? '';
97
- return label.length > 10 ? label.slice(0, 10) + '...' : label;
112
+ return label.length > 10 ? label.slice(0, 30) + '...' : label;
98
113
  },
99
114
  minRotation: 0,
100
- maxRotation: 0
115
+ maxRotation: 0,
116
+ font: {
117
+ size: 11
118
+ }
101
119
  },
102
120
  stacked: !!this.stacked,
103
121
  },
@@ -105,6 +123,9 @@ export default{
105
123
  beginAtZero: true,
106
124
  ticks: {
107
125
  display: !this.yAxeMultiple || this.usePercentage,
126
+ font: {
127
+ size: 11,
128
+ },
108
129
  },
109
130
  grid: {
110
131
  display: true,
@@ -112,20 +133,15 @@ export default{
112
133
  return `rgba(255,255,255, .1)`
113
134
  }
114
135
  },
115
- stacked: !!this.stacked,
136
+ stacked: !!this.stacked
116
137
  }
117
138
  },
118
- onClick: function(evt, evt2) {
119
- console.log(evt);
120
- console.log(evt2);
121
- }
139
+ onClick: this.onClick
122
140
  }
123
141
  },
124
142
 
125
- cValue(){
126
- return this.datasourceUid && this.datasource ?
127
- this.datasource.keys[this.datasourceUid] :
128
- this.value
143
+ isError(){
144
+ return this.value?.error === 1
129
145
  },
130
146
 
131
147
  yLegends(){
@@ -150,9 +166,57 @@ export default{
150
166
  ],
151
167
 
152
168
  datasets: null,
169
+
170
+ readyState: 1,
153
171
  }
154
172
  },
155
173
 
174
+ inject: [ 'selectPreset' ],
175
+
176
+ methods: {
177
+
178
+ onClick(e, segments){
179
+
180
+ const clickInteractions = (this.interactions ?? []).filter(_ => _.event === 'click')
181
+ const segment = segments[0]
182
+ if(!segment) return
183
+
184
+ const dataset = this.value.datasets[segment.datasetIndex]
185
+ const obj = {
186
+ segment: this.value.labels[segment.index],
187
+ key: dataset.label,
188
+ value: dataset.data[segment.index]
189
+ }
190
+
191
+ for(let click of clickInteractions){
192
+ const { action, presetUid, filters } = click
193
+
194
+ const params = filters.reduce((res, cur) => {
195
+ res[cur.datasourceUid + '.filters.' + cur.key + '.' + cur.operator] = strVars(cur.value, obj)
196
+ return res
197
+ }, {})
198
+
199
+ switch(action){
200
+
201
+ case 'openDashboardPreset':
202
+ if(typeof this.selectPreset === 'function')
203
+ this.selectPreset(presetUid, params)
204
+ break
205
+ }
206
+ }
207
+
208
+ },
209
+
210
+ resetState(){
211
+ this.setState(1)
212
+ },
213
+
214
+ setState(state){
215
+ this.readyState = state
216
+ },
217
+
218
+ },
219
+
156
220
  props: {
157
221
 
158
222
  datasourceUid: String,
@@ -177,15 +241,10 @@ export default{
177
241
 
178
242
  yAxeOnClick: Array,
179
243
 
180
- value: Object
244
+ value: Object,
181
245
 
182
- },
246
+ interactions: Array
183
247
 
184
- setup(){
185
- const datasource = useDatasource()
186
- return {
187
- datasource
188
- }
189
248
  },
190
249
 
191
250
  }
@@ -196,7 +255,7 @@ export default{
196
255
 
197
256
  .comp{
198
257
  @apply border-[1px] border-text-50 bg-base-500 rounded-lg;
199
- @apply p-3;
258
+ @apply p-3 flex flex-col;
200
259
  }
201
260
 
202
261
  </style>