@mixd-id/web-scaffold 0.1.230406245 → 0.1.230406247

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.230406245",
4
+ "version": "0.1.230406247",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -26,6 +26,7 @@
26
26
  "./importer": "./src/utils/importer.js",
27
27
  "./listpage1": "./src/utils/listpage1.js",
28
28
  "./listview": "./src/utils/listview.js",
29
+ "./preset-selector": "./src/utils/preset-selector.js",
29
30
  "./web": "./src/utils/web.js"
30
31
  },
31
32
  "dependencies": {
@@ -16,8 +16,8 @@
16
16
  </div>
17
17
 
18
18
  <div :class="$style.details">
19
- <p class="text-center break-words" v-if="message">{{ message }}</p>
20
- <p class="text-center break-words max-w-[420px]" v-if="stack">{{ stack }}</p>
19
+ <p class="text-center break-words whitespace-pre-line" v-if="message">{{ message }}</p>
20
+ <p class="text-center break-words max-w-[420px] whitespace-pre-line" v-if="stack">{{ stack }}</p>
21
21
  </div>
22
22
 
23
23
  <div :class="$style.details" v-if="details">
@@ -183,7 +183,7 @@ export default{
183
183
  }
184
184
 
185
185
  .button-outline{
186
- @apply bg-transparent text-primary-500;
186
+ @apply bg-transparent text-primary-500 border-[1px] border-primary;
187
187
  }
188
188
  .button-outline:hover{
189
189
  }
@@ -0,0 +1,295 @@
1
+ <template>
2
+ <div v-if="readyState === 1" :class="$style.comp">
3
+
4
+ <div class="p-3 flex flex-col items-start leading-tight">
5
+ <small class="text-text-400">Users</small>
6
+ <h5 class="inline align-top cursor-pointer hover:text-primary group" @click="$refs.presetSelector.open()">
7
+ {{ preset.name ?? 'Preset Name' }}
8
+ <span v-if="badgeCount > 0" class="mx-1 min-w-[19px] px-2 h-[19px] relative top-[-2px] rounded-full bg-primary text-white inline-flex items-center justify-center">
9
+ {{ badgeCount }}
10
+ </span>
11
+ <svg width="13" height="13" class="inline fill-text pointer-events-none group-hover:fill-primary" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z"/></svg>
12
+ </h5>
13
+ </div>
14
+
15
+ <Bar class="w-full"
16
+ id="my-chart-id"
17
+ :options="chartOptions"
18
+ :data="chartData"
19
+ />
20
+
21
+ <PresetSelector ref="presetSelector" :config="config" @select="load" type="chart" />
22
+
23
+ </div>
24
+
25
+ <div v-else-if="readyState < 0" :class="$style.comp"
26
+ class="flex items-center justify-center gap-3">
27
+ <svg width="32" height="32" class="fill-red-500" 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="M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 464c-114.7 0-208-93.31-208-208S141.3 48 256 48s208 93.31 208 208S370.7 464 256 464zM359.5 133.7c-10.11-8.578-25.28-7.297-33.83 2.828L256 218.8L186.3 136.5C177.8 126.4 162.6 125.1 152.5 133.7C142.4 142.2 141.1 157.4 149.7 167.5L224.6 256l-74.88 88.5c-8.562 10.11-7.297 25.27 2.828 33.83C157 382.1 162.5 384 167.1 384c6.812 0 13.59-2.891 18.34-8.5L256 293.2l69.67 82.34C330.4 381.1 337.2 384 344 384c5.469 0 10.98-1.859 15.48-5.672c10.12-8.562 11.39-23.72 2.828-33.83L287.4 256l74.88-88.5C370.9 157.4 369.6 142.2 359.5 133.7z"/></svg>
28
+ <h5>{{ (error ?? {}).message ?? 'An error occurred' }}</h5>
29
+ </div>
30
+ </template>
31
+
32
+ <script>
33
+
34
+ import { Bar } from 'vue-chartjs'
35
+ import { Chart as ChartJS, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js'
36
+ import PresetSelector from "../widgets/PresetSelector.vue";
37
+ import throttle from "lodash/throttle";
38
+
39
+ ChartJS.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
40
+
41
+ export default{
42
+
43
+ components: {Bar, PresetSelector},
44
+
45
+ inject: [ 'alert', 'socket' ],
46
+
47
+ props: {
48
+ config: {
49
+ type: Object,
50
+ default: {}
51
+ },
52
+
53
+ presetKey: undefined,
54
+ src: undefined,
55
+ subscribeKey: String,
56
+ },
57
+
58
+ data(){
59
+ return {
60
+ badgeCount: 0,
61
+ readyState: 0,
62
+ data: {},
63
+ error: null,
64
+
65
+ backgroundColors: [
66
+ '#5D9CEC',
67
+ '#A0D468',
68
+ '#FFCE54',
69
+ '#FC6E51',
70
+ '#48CFAD',
71
+ '#AC92EC',
72
+ '#4FC1E9',
73
+ '#FFCE54',
74
+ '#ED5565',
75
+ '#EC87C0'
76
+ ],
77
+ borderColors: [
78
+ '#4A89DC',
79
+ '#8CC152',
80
+ '#F6BB42',
81
+ '#E9573F',
82
+ '#37BC9B',
83
+ '#967ADC',
84
+ '#3BAFDA',
85
+ '#F6BB42',
86
+ '#DA4453',
87
+ '#D770AD'
88
+ ],
89
+ }
90
+ },
91
+
92
+ computed: {
93
+
94
+ chartData(){
95
+
96
+ const { type, xAxis = [], yAxis = [] } = this.preset.chart ?? {}
97
+
98
+ const items = this.data.items ?? []
99
+
100
+ const datasets = []
101
+ if((yAxis[0] ?? {}).aggregrate === 'count'){
102
+ datasets.push({
103
+ label: 'count',
104
+ data: [],
105
+ borderColor: this.borderColors[datasets.length % this.borderColors.length],
106
+ backgroundColor: this.backgroundColors[datasets.length % this.backgroundColors.length],
107
+ })
108
+ }
109
+ else{
110
+ for(let key in items[0] ?? {}){
111
+ if(key !== 'date'){
112
+ datasets.push({
113
+ label: key,
114
+ data: [],
115
+ borderColor: this.borderColors[datasets.length % this.borderColors.length],
116
+ backgroundColor: this.backgroundColors[datasets.length % this.backgroundColors.length],
117
+ })
118
+ }
119
+ }
120
+ }
121
+
122
+ const labels = []
123
+ for(let item of this.data.items ?? []){
124
+ labels.push(item[xAxis[0].key])
125
+
126
+ if((yAxis[0] ?? {}).aggregrate === 'count'){
127
+ datasets[0].data.push(item['count'])
128
+ }
129
+ else{
130
+ for(let i in datasets){
131
+ datasets[i].data.push(item[datasets[i].label])
132
+ }
133
+ }
134
+ }
135
+
136
+ return {
137
+ labels,
138
+ datasets
139
+ }
140
+ },
141
+
142
+ chartOptions(){
143
+ if(typeof window === 'undefined') return
144
+
145
+ var style = getComputedStyle(document.body)
146
+ var gridColor = style.getPropertyValue('--text-100')
147
+ var gridColor2 = style.getPropertyValue('--text-200')
148
+
149
+ return {
150
+ responsive: true,
151
+ maintainAspectRatio: false,
152
+ animation: false,
153
+ plugins: {
154
+ legend: {
155
+ display: false, // Disable the legend
156
+ },
157
+ },
158
+ scales: {
159
+ x: {
160
+ //display: false, // Disable x-axis labels
161
+ grid: {
162
+ display: true,
163
+ color: function(context){
164
+ return `rgb(${gridColor2})`
165
+ }
166
+ },
167
+ },
168
+ y: {
169
+ beginAtZero: true, // Adjust y-axis as needed
170
+ grid: {
171
+ display: true,
172
+ color: function(context){
173
+ return `rgb(${gridColor})`
174
+ }
175
+ },
176
+ },
177
+ },
178
+ }
179
+ },
180
+
181
+ preset(){
182
+ return ((this.config ?? {}).presets ?? [])[(this.config ?? {}).presetIdx ?? 0] ?? {}
183
+ },
184
+
185
+ },
186
+
187
+ methods: {
188
+
189
+ load(){
190
+ this.socket.send(this.src, this.preset)
191
+ .then(data => this.data = data)
192
+ .catch(err => {
193
+ this.readyState = -1
194
+ this.error = err
195
+ })
196
+ },
197
+
198
+ loadPreset(){
199
+ if(this.presetKey){
200
+ return this.socket.send('user.preset', { key:this.presetKey, reset:'reset' in this.$route.query })
201
+ .then(config => {
202
+ Object.assign(this.config, config)
203
+
204
+ if('reset' in this.$route.query){
205
+ this.savePreset()
206
+ this.$router.replace({ name:this.$route.name })
207
+ }
208
+ })
209
+ }
210
+ return new Promise(resolve => resolve())
211
+ },
212
+
213
+ saveConfig: throttle(function() {
214
+ if(this.presetKey){
215
+ this.socket.send('user.preset',
216
+ { key:this.presetKey, config:this.config })
217
+ }
218
+ }, 1000, { leading:true }),
219
+
220
+ onSignal(event, items){
221
+ console.log('onSignal', event, items)
222
+
223
+ const hasData = items.some(item => item._data)
224
+ if(hasData){
225
+ for(let item of items){
226
+ if(item._data){
227
+ const length = this.data.items.length
228
+ this.$util.push(this.data.items, { id:item.id, ...item._data })
229
+ this.data.items.length > length ? this.data.items.shift() : null
230
+ }
231
+ }
232
+ }
233
+ else{
234
+ this.socket.send(this.src, {
235
+ ...this.preset,
236
+ id: items.map(item => item.id)
237
+ })
238
+ .then(data => {
239
+ if(!Array.isArray((data ?? {}).items)) return
240
+
241
+ for(let item of data.items){
242
+ const length = this.data.items.length
243
+ this.$util.push(this.data.items, item)
244
+ this.data.items.length > length ? this.data.items.shift() : null
245
+ }
246
+ })
247
+ }
248
+
249
+ },
250
+ },
251
+
252
+ mounted() {
253
+ this.loadPreset()
254
+ .then(() => {
255
+ this.readyState = 1
256
+ this.load()
257
+ })
258
+
259
+ if(this.subscribeKey){
260
+ this.socket.send('user.subscribe', { name:this.subscribeKey })
261
+ this.socket.on(this.subscribeKey, this.onSignal)
262
+ }
263
+ },
264
+
265
+ unmounted() {
266
+ if(this.subscribeKey) {
267
+ this.socket.send('user.unsubscribe', {name: this.subscribeKey})
268
+ this.socket.off(this.subscribeKey, this.onSignal)
269
+ }
270
+ },
271
+
272
+ watch: {
273
+
274
+ config: {
275
+ deep: true,
276
+ handler(){
277
+ this.saveConfig()
278
+ }
279
+ }
280
+
281
+ },
282
+
283
+ }
284
+
285
+ </script>
286
+
287
+ <style module>
288
+
289
+ .comp{
290
+ @apply flex flex-col;
291
+ @apply bg-base-400;
292
+ padding-bottom: 50px;
293
+ }
294
+
295
+ </style>
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <div :class="$style.comp">
3
+ <slot name="default"></slot>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+
9
+ export default{
10
+
11
+ }
12
+
13
+ </script>
14
+
15
+ <style module>
16
+
17
+ .comp{
18
+ @apply flex flex-col md:grid grid-cols-12 gap-5 md:items-stretch;
19
+ }
20
+
21
+ </style>
@@ -0,0 +1,196 @@
1
+ <template>
2
+ <div :class="computedClass">
3
+ <slot name="default"></slot>
4
+
5
+ <div :class="`${$style.left} ${editMode ? $style.editMode : ''}`"
6
+ @click="editMode = true"
7
+ @mousedown="(e) => onMouseDown(e, 'left')"></div>
8
+ <div :class="`${$style.top} ${editMode ? $style.editMode : ''}`"
9
+ @click="editMode = true"
10
+ @mousedown="(e) => onMouseDown(e, 'top')"></div>
11
+ <div :class="`${$style.right} ${editMode ? $style.editMode : ''}`"
12
+ @click="editMode = true"
13
+ @mousedown="(e) => onMouseDown(e, 'right')"></div>
14
+ <div :class="`${$style.bottom} ${editMode ? $style.editMode : ''}`"
15
+ @click="editMode = true"
16
+ @mousedown="(e) => onMouseDown(e, 'bottom')"></div>
17
+
18
+ <button type="button" :class="`${$style.removeBtn} ${editMode ? $style.editMode : ''}`" @click="$emit('remove')">
19
+ <svg width="11" height="11" class="fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M378.4 440.6c8.531 10.16 7.203 25.28-2.938 33.81C370.9 478.2 365.5 480 360 480c-6.844 0-13.64-2.906-18.39-8.562L192 293.3l-149.6 178.1C37.63 477.1 30.83 480 23.98 480c-5.453 0-10.92-1.844-15.42-5.625c-10.14-8.531-11.47-23.66-2.938-33.81L160.7 256L5.625 71.44C-2.906 61.28-1.578 46.16 8.563 37.63C18.69 29.08 33.84 30.39 42.38 40.56L192 218.7l149.6-178.1c8.547-10.17 23.67-11.47 33.81-2.938s11.47 23.66 2.938 33.81L223.3 256L378.4 440.6z"/></svg>
20
+ </button>
21
+
22
+ </div>
23
+ </template>
24
+
25
+ <script>
26
+
27
+ export default{
28
+
29
+ emits: [ 'class-change', 'remove' ],
30
+
31
+ props: {
32
+
33
+ class: {
34
+ type: String,
35
+ default: ''
36
+ }
37
+
38
+ },
39
+
40
+ computed: {
41
+
42
+ computedClass(){
43
+ return [
44
+ this.$style.comp,
45
+ this.class
46
+ ]
47
+ .join(' ')
48
+ }
49
+
50
+ },
51
+
52
+ data(){
53
+ return {
54
+ drag: null,
55
+ editMode: false,
56
+ }
57
+ },
58
+
59
+ methods: {
60
+
61
+ onMouseDown(e, direction){
62
+ if(!this.editMode) return
63
+
64
+ this.drag = {
65
+ x: e.clientX,
66
+ y: e.clientY,
67
+ colSize: Math.round(this.$el.clientWidth / 12),
68
+ target: e.target,
69
+ direction
70
+ }
71
+
72
+ window.addEventListener('mousemove', this.onMouseMove)
73
+ window.addEventListener('mouseup', this.onMouseUp)
74
+ },
75
+
76
+ onMouseMove(e){
77
+
78
+ switch(this.drag.direction){
79
+
80
+ case 'right':
81
+ const distanceX = e.clientX - this.drag.x
82
+
83
+ if(Math.abs(distanceX) > 35){
84
+ const match = this.class.match(/col-span-(\d+)/)
85
+ console.log(match)
86
+ const colSpan = match ? parseInt(match[1]) : 1
87
+ const nextColSpan = distanceX > 0 ? colSpan + 1 <= 12 ? colSpan + 1 : 12 :
88
+ colSpan - 1 >= 1 ? colSpan - 1 : 1
89
+ console.log(colSpan, nextColSpan)
90
+
91
+ console.log(2, this.class)
92
+ const nextClass = match ? this.class.replace(`col-span-${colSpan}`, `col-span-${nextColSpan}`) :
93
+ `${this.class} col-span-${nextColSpan}`
94
+ console.log(2, nextClass)
95
+
96
+ this.drag.x = e.clientX
97
+ this.$emit('class-change', nextClass)
98
+ }
99
+ break
100
+
101
+ case 'bottom':
102
+ const distanceY = e.clientY - this.drag.y
103
+ if(Math.abs(distanceY) > 20){
104
+ const match = this.class.match(/row-span-(\d+)/)
105
+ const rowSpan = match ? parseInt(match[1]) : 1
106
+ const nextRowSpan = distanceY > 0 ? rowSpan + 1 <= 12 ? rowSpan + 1 : 12 :
107
+ rowSpan - 1 >= 1 ? rowSpan - 1 : 1
108
+
109
+ match ? this.class.replace(`row-span-${rowSpan}`, `row-span-${nextRowSpan}`) :
110
+ this.class += ` row-span-${nextRowSpan}`
111
+
112
+ this.drag.y = e.clientY
113
+ this.$emit('class-change', this.class)
114
+ }
115
+ break
116
+ }
117
+
118
+ },
119
+
120
+ onMouseUp(e){
121
+
122
+ window.removeEventListener('mousemove', this.onMouseMove)
123
+ window.removeEventListener('mouseup', this.onMouseUp)
124
+ },
125
+
126
+ checkEditMode(e){
127
+
128
+ if(e.target.closest(`.${this.$style.comp}`) !== this.$el){
129
+ this.editMode = false
130
+ }
131
+
132
+ },
133
+
134
+ },
135
+
136
+ watch: {
137
+
138
+ editMode(to){
139
+ to ? window.addEventListener('click', this.checkEditMode) :
140
+ window.removeEventListener('click', this.checkEditMode)
141
+ }
142
+
143
+ }
144
+
145
+ }
146
+
147
+ </script>
148
+
149
+ <style module>
150
+
151
+ .comp{
152
+ @apply relative flex;
153
+ }
154
+
155
+ .top{
156
+ @apply h-[3px];
157
+ @apply absolute top-[-6px] right-[-6px] left-[-6px] cursor-move bg-transparent hover:bg-text-50;
158
+ }
159
+ .top.editMode{
160
+ @apply cursor-n-resize bg-primary hover:bg-primary;
161
+ }
162
+
163
+ .bottom{
164
+ @apply h-[3px];
165
+ @apply absolute bottom-[-6px] right-[-6px] left-[-6px] cursor-move bg-transparent hover:bg-text-50;
166
+ }
167
+ .bottom.editMode{
168
+ @apply cursor-s-resize bg-primary hover:bg-primary;
169
+ }
170
+
171
+ .left{
172
+ @apply w-[3px];
173
+ @apply absolute top-[-6px] left-[-6px] bottom-[-6px] cursor-move bg-transparent hover:bg-text-50;
174
+ }
175
+ .left.editMode{
176
+ @apply cursor-w-resize bg-primary hover:bg-primary;
177
+ }
178
+
179
+ .right{
180
+ @apply w-[3px];
181
+ @apply absolute top-[-6px] right-[-6px] bottom-[-6px] cursor-move bg-transparent hover:bg-text-50;
182
+ }
183
+ .right.editMode{
184
+ @apply cursor-e-resize bg-primary hover:bg-primary;
185
+ }
186
+
187
+ .removeBtn{
188
+ @apply absolute top-[.5rem] right-[.5rem] hidden;
189
+ @apply w-[24px] h-[24px] rounded-full bg-primary;
190
+ @apply items-center justify-center;
191
+ }
192
+ .removeBtn.editMode{
193
+ @apply flex;
194
+ }
195
+
196
+ </style>
@@ -26,7 +26,7 @@
26
26
  </template>
27
27
 
28
28
  <div class="flex-1 p-5 flex flex-col items-center justify-center" v-if="step === 1">
29
- <button type="button" @click="$refs.uploader.click()" class="rounded-3xl w-[200px] bg-base-300 px-6 aspect-square flex items-center justify-center text-center border-dashed border-[3px] border-text-100">
29
+ <button type="button" @click="$refs.uploader.click()" class="rounded-3xl w-[160px] bg-base-300 px-6 aspect-square flex items-center justify-center text-center border-dashed border-[3px] border-text-100">
30
30
  <div class="text-xl" v-if="!isLoading">
31
31
  Click to <br />
32
32
  <span class="text-primary text-lg">Upload</span>
@@ -55,7 +55,7 @@
55
55
  <label v-else>{{ key.text }}</label>
56
56
  </div>
57
57
  <div class="flex-1">
58
- <Dropdown v-model="importData.keys[index].value">
58
+ <Dropdown v-model="importData.keys[index].value" :class="key.required ? 'w-full' : 'w-[210px]'">
59
59
  <option disabled selected>Pilih Kolom</option>
60
60
  <option v-for="column in importData.columns" :value="column">{{ column }}</option>
61
61
  </Dropdown>