@mixd-id/web-scaffold 0.1.230406087 → 0.1.230406089
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/Alert.vue +3 -2
- package/src/components/Button.vue +4 -0
- package/src/components/Carousel.vue +5 -1
- package/src/components/CitySelector.vue +59 -0
- package/src/components/Datepicker.vue +46 -27
- package/src/components/Textbox.vue +20 -1
- package/src/index.js +9 -0
- package/src/widgets/CarouselSetting.vue +8 -29
- package/src/widgets/ContactForm.vue +72 -28
- package/src/widgets/ContactFormSetting.vue +92 -31
- package/src/widgets/WebPageBuilder.vue +18 -5
package/package.json
CHANGED
package/src/components/Alert.vue
CHANGED
|
@@ -22,14 +22,14 @@
|
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
24
|
<div class="mt-4" v-if="mode === 'alert'">
|
|
25
|
-
<Button @click="$emit('dismiss')" class="min-w-[88px]">
|
|
25
|
+
<Button ref="btnOK" @click="$emit('dismiss')" class="min-w-[88px]">
|
|
26
26
|
<strong class="px-4">
|
|
27
27
|
{{ text }}
|
|
28
28
|
</strong>
|
|
29
29
|
</Button>
|
|
30
30
|
</div>
|
|
31
31
|
<div class="mt-4 flex flex-row gap-2" v-else-if="mode === 'confirm'">
|
|
32
|
-
<Button @click="$emit('confirm')" class="min-w-[88px]">
|
|
32
|
+
<Button ref="btnOK" @click="$emit('confirm')" class="min-w-[88px]" tabindex="0">
|
|
33
33
|
<strong class="px-4">
|
|
34
34
|
{{ text }}
|
|
35
35
|
</strong>
|
|
@@ -123,6 +123,7 @@ export default{
|
|
|
123
123
|
methods: {
|
|
124
124
|
|
|
125
125
|
onAfterAppear(){
|
|
126
|
+
this.$refs.btnOK.focus()
|
|
126
127
|
this.$emit('appear')
|
|
127
128
|
}
|
|
128
129
|
|
|
@@ -136,6 +136,10 @@ export default{
|
|
|
136
136
|
outline: solid 1px rgb(var(--primary-700));
|
|
137
137
|
border: solid 1px rgb(var(--primary-400));
|
|
138
138
|
}
|
|
139
|
+
.button-primary:focus{
|
|
140
|
+
outline-color: rgb(var(--primary-800));
|
|
141
|
+
border-color: rgb(var(--primary-600));
|
|
142
|
+
}
|
|
139
143
|
.button-primary:hover{
|
|
140
144
|
@apply bg-primary-600;
|
|
141
145
|
outline-color: rgb(var(--primary-800))
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
<div ref="inner" :class="innerClass">
|
|
5
5
|
<div v-for="item in items" :class="itemClass">
|
|
6
|
-
<component :is="item.type"
|
|
6
|
+
<component :is="item.type"
|
|
7
|
+
:uid="item.uid"
|
|
8
|
+
:="item.props"
|
|
9
|
+
:ratio="ratio"
|
|
10
|
+
:ext-class="item.extClass" />
|
|
7
11
|
</div>
|
|
8
12
|
</div>
|
|
9
13
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Modal :state="isOpen" width="480" height="480">
|
|
3
|
+
<template v-slot:head>
|
|
4
|
+
<div class="relative p-5">
|
|
5
|
+
<h3>Pilih Kota</h3>
|
|
6
|
+
<div class="absolute top-0 right-0">
|
|
7
|
+
<button type="button" class="p-2" @click="close">
|
|
8
|
+
<svg width="24" height="24" viewBox="0 0 24 24" class="fill-text-300" xmlns="http://www.w3.org/2000/svg">
|
|
9
|
+
<path d="M6.53034 5.46965C6.23745 5.17676 5.76257 5.17676 5.46968 5.46965C5.17679 5.76255 5.17679 6.23742 5.46968 6.53031L10.9393 12L5.46967 17.4697C5.17678 17.7626 5.17678 18.2374 5.46967 18.5303C5.76256 18.8232 6.23744 18.8232 6.53033 18.5303L12 13.0606L17.4697 18.5303C17.7626 18.8232 18.2375 18.8232 18.5303 18.5303C18.8232 18.2374 18.8232 17.7626 18.5303 17.4697L13.0607 12L18.5303 6.53032C18.8232 6.23743 18.8232 5.76256 18.5303 5.46966C18.2374 5.17677 17.7626 5.17677 17.4697 5.46966L12 10.9393L6.53034 5.46965Z"/>
|
|
10
|
+
</svg>
|
|
11
|
+
</button>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
<template v-slot:foot>
|
|
16
|
+
<div class="p-5">
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
<div class="flex-1 p-5">
|
|
21
|
+
</div>
|
|
22
|
+
</Modal>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script>
|
|
26
|
+
|
|
27
|
+
export default{
|
|
28
|
+
|
|
29
|
+
inject: [ 'socketEmit2' ],
|
|
30
|
+
|
|
31
|
+
data(){
|
|
32
|
+
return {
|
|
33
|
+
isOpen: false
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
methods: {
|
|
38
|
+
|
|
39
|
+
open(id){
|
|
40
|
+
this.isOpen = true
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
close(){
|
|
44
|
+
this.isOpen = false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<style module>
|
|
54
|
+
|
|
55
|
+
.comp{
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
</style>
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
</
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
</
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<div v-
|
|
3
|
+
<div v-if="mode === ''" :class="compClass">
|
|
4
|
+
<select v-model="DD">
|
|
5
|
+
<option disabled selected>Tanggal</option>
|
|
6
|
+
<option v-for="d in 31" :value="d.toString().padStart(2, '0')">{{ d }}</option>
|
|
7
|
+
</select>
|
|
8
|
+
<select v-model="MM">
|
|
9
|
+
<option disabled selected>Bulan</option>
|
|
10
|
+
<option v-for="mmm in months" :value="mmm[0]">{{ mmm[1] }}</option>
|
|
11
|
+
</select>
|
|
12
|
+
<select v-model="YYYY">
|
|
13
|
+
<option disabled selected>Tahun</option>
|
|
14
|
+
<option v-for="yyyy in years" :value="yyyy">{{ yyyy }}</option>
|
|
15
|
+
</select>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div v-else :class="compClass">
|
|
19
|
+
|
|
20
|
+
<div v-if="mode === 'popup'" ref="popup"
|
|
21
21
|
@click="!readonly ? contextMenu = { caller:$refs.popup, value:this.modelValue } : null"
|
|
22
22
|
class="flex-1">
|
|
23
23
|
<input class="flex-1" type="text" readonly :value="DMMMYYYY"/>
|
|
@@ -102,9 +102,9 @@ export default{
|
|
|
102
102
|
|
|
103
103
|
setup(props, { emit }){
|
|
104
104
|
|
|
105
|
-
const DD = ref(dayjs(props.modelValue).format('DD'))
|
|
106
|
-
const MM = ref(dayjs(props.modelValue).format('MM'))
|
|
107
|
-
const YYYY = ref(dayjs(props.modelValue).format('YYYY'))
|
|
105
|
+
const DD = ref(dayjs(props.modelValue ?? props.defaultValue).format('DD'))
|
|
106
|
+
const MM = ref(dayjs(props.modelValue ?? props.defaultValue).format('MM'))
|
|
107
|
+
const YYYY = ref(dayjs(props.modelValue ?? props.defaultValue).format('YYYY'))
|
|
108
108
|
|
|
109
109
|
watch([ YYYY, MM, DD ], (to) => {
|
|
110
110
|
emit('update:modelValue', to.join('-'))
|
|
@@ -136,6 +136,8 @@ export default{
|
|
|
136
136
|
|
|
137
137
|
modelValue:String,
|
|
138
138
|
|
|
139
|
+
defaultValue: String,
|
|
140
|
+
|
|
139
141
|
readonly: undefined,
|
|
140
142
|
|
|
141
143
|
},
|
|
@@ -262,9 +264,8 @@ export default{
|
|
|
262
264
|
<style module>
|
|
263
265
|
|
|
264
266
|
.datepicker {
|
|
265
|
-
@apply h-[var(--h-cp)];
|
|
267
|
+
@apply min-h-[var(--h-cp)];
|
|
266
268
|
@apply flex items-center rounded-lg overflow-hidden cursor-pointer relative;
|
|
267
|
-
@apply border-[1px] border-text-200 bg-base-50;
|
|
268
269
|
@apply cursor-pointer;
|
|
269
270
|
}
|
|
270
271
|
.datepicker:not(.readonly){
|
|
@@ -301,16 +302,34 @@ export default{
|
|
|
301
302
|
@apply block fill-text-300;
|
|
302
303
|
}
|
|
303
304
|
|
|
304
|
-
.
|
|
305
|
+
.selected{
|
|
306
|
+
@apply bg-primary;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.mode-calendar{
|
|
305
310
|
@apply p-2 h-auto;
|
|
306
311
|
}
|
|
307
312
|
|
|
313
|
+
.mode-{
|
|
314
|
+
@apply flex flex-row gap-2;
|
|
315
|
+
}
|
|
316
|
+
.mode- select{
|
|
317
|
+
@apply bg-base border-[1px] border-text-200 rounded-lg;
|
|
318
|
+
@apply min-h-[var(--h-cp)];
|
|
319
|
+
}
|
|
320
|
+
.mode- select:first-child{
|
|
321
|
+
@apply w-12;
|
|
322
|
+
}
|
|
323
|
+
.mode- select:nth-child(2){
|
|
324
|
+
@apply w-72;
|
|
325
|
+
}
|
|
326
|
+
.mode- select:last-child{
|
|
327
|
+
@apply w-48;
|
|
328
|
+
}
|
|
329
|
+
|
|
308
330
|
.datepicker-date--1, .datepicker-date-1{
|
|
309
331
|
@apply text-gray-500;
|
|
310
332
|
}
|
|
311
333
|
|
|
312
|
-
.selected{
|
|
313
|
-
@apply bg-primary;
|
|
314
|
-
}
|
|
315
334
|
|
|
316
335
|
</style>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<slot name="start"></slot>
|
|
4
4
|
<input :type="computedType" :disabled="isDisabled" @focus="isActive = true" @input="onInput" @blur="onBlur"
|
|
5
5
|
:placeholder="placeholder" :maxlength="maxlength" ref="input" autocomplete="new-password"
|
|
6
|
-
:value="displayedValue" :readonly="Boolean(readonly)"
|
|
6
|
+
:value="displayedValue" :readonly="Boolean(readonly)" @paste="onPaste"
|
|
7
7
|
@keydown="onKeyDown"/>
|
|
8
8
|
<button class="mr-2" v-if="Boolean(clearable) && !Boolean(readonly) && state >= 1 && modelValue" type="button" @click="$emit('clear')">
|
|
9
9
|
<svg :class="$style.svg" width="19" height="19" viewBox="0 0 24 24" class="fill-text-200" xmlns="http://www.w3.org/2000/svg">
|
|
@@ -128,12 +128,31 @@ export default{
|
|
|
128
128
|
this.$emit('blur', e)
|
|
129
129
|
},
|
|
130
130
|
|
|
131
|
+
onPaste(e){
|
|
132
|
+
e.preventDefault()
|
|
133
|
+
|
|
134
|
+
let text = (e.clipboardData || window.clipboardData).getData("text");
|
|
135
|
+
|
|
136
|
+
switch(this.type){
|
|
137
|
+
case 'tel':
|
|
138
|
+
text = text.replace(/[^0-9]/g, '')
|
|
139
|
+
break
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if(parseInt(this.maxlength) > 0){
|
|
143
|
+
text = text.substring(0, parseInt(this.maxlength))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.$emit('update:modelValue', text)
|
|
147
|
+
},
|
|
148
|
+
|
|
131
149
|
onKeyDown(e){
|
|
132
150
|
if([ 'number', 'tel' ].includes(this.type)){
|
|
133
151
|
if(((e.keyCode >= 48 && e.keyCode <= 57) || (e.keyCode >= 96 && e.keyCode <= 105) ||
|
|
134
152
|
[ 8, 9, 13, 27, 37, 38, 39, 40, 46 ].includes(e.keyCode)) && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey);
|
|
135
153
|
else if(e.keyCode === 65 && (e.ctrlKey || e.metaKey));
|
|
136
154
|
else if(e.keyCode === 82 && (e.ctrlKey || e.metaKey));
|
|
155
|
+
else if(e.keyCode === 86 && (e.ctrlKey || e.metaKey)); // ctrl-v
|
|
137
156
|
else{
|
|
138
157
|
e.preventDefault()
|
|
139
158
|
}
|
package/src/index.js
CHANGED
|
@@ -169,6 +169,14 @@ const util = {
|
|
|
169
169
|
}
|
|
170
170
|
},
|
|
171
171
|
|
|
172
|
+
removeEmpty(array, emptyValues = [ '', '{}', '[]' ]){
|
|
173
|
+
for (let i = array.length - 1; i >= 0; i--) {
|
|
174
|
+
if (!array[i] || emptyValues.includes(JSON.stringify(array[i])) || Object.values(array[i]).join('').length <= 0){
|
|
175
|
+
array.splice(i, 1);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
|
|
172
180
|
onEvent: (arr, updates, opt = {}) => {
|
|
173
181
|
|
|
174
182
|
const [ model, event, items ] = updates
|
|
@@ -252,6 +260,7 @@ export default{
|
|
|
252
260
|
app.component('Block', defineAsyncComponent(() => import("./components/Block.vue")))
|
|
253
261
|
app.component('Button', defineAsyncComponent(() => import("./components/Button.vue")))
|
|
254
262
|
app.component('Box', defineAsyncComponent(() => import("./components/Box.vue")))
|
|
263
|
+
app.component('CitySelector', defineAsyncComponent(() => import("./components/CitySelector.vue")))
|
|
255
264
|
app.component('ColorPicker', defineAsyncComponent(() => import("./components/ColorPicker.vue")))
|
|
256
265
|
app.component('SearchButton', defineAsyncComponent(() => import("./components/SearchButton.vue")))
|
|
257
266
|
app.component('ChatTyping', defineAsyncComponent(() => import("./components/ChatTyping.vue")))
|
|
@@ -5,33 +5,6 @@
|
|
|
5
5
|
v-show="viewType === _.value">
|
|
6
6
|
<div class="grid grid-cols-2 gap-4">
|
|
7
7
|
|
|
8
|
-
<div class="col-span-2">
|
|
9
|
-
<div class="flex flex-row gap-2">
|
|
10
|
-
<label class="flex-1 text-text-400">Items</label>
|
|
11
|
-
<button type="button" class="text-primary"
|
|
12
|
-
@click="$refs.imageModal.open({ viewType, item:{ type:'Image', props:{ src:[], margin:[], padding:[], bdColor:[], bdSize:[ '0' ], bdRadius:[], bdStyle:[ 'solid' ] } } })">Add Item</button>
|
|
13
|
-
</div>
|
|
14
|
-
<ListItem :items="item.props.items" class="mt-1"
|
|
15
|
-
@reorder="(from, to) => { item.props.items.splice(to, 0, item.props.items.splice(from, 1)[0]); $emit('change') }">
|
|
16
|
-
<template v-slot="{ item:comp, index }">
|
|
17
|
-
<div class="flex flex-row gap-3 p-2 bg-base-500 items-center rounded-lg mb-2">
|
|
18
|
-
<div data-reorder>
|
|
19
|
-
<svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
|
|
20
|
-
</div>
|
|
21
|
-
<div class="flex-1"
|
|
22
|
-
@click="$refs.imageModal.open({ item:comp, index, viewType })">
|
|
23
|
-
<component :is="`${comp.type}Item`" :item="comp" :viewType="viewType" :viewTypes="viewTypes" />
|
|
24
|
-
</div>
|
|
25
|
-
<div>
|
|
26
|
-
<button type="button" @click="confirm('Remove item?', { onConfirm: () => { item.props.items.splice(index, 1); $emit('change') }})">
|
|
27
|
-
<svg width="16" height="16" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
|
|
28
|
-
</button>
|
|
29
|
-
</div>
|
|
30
|
-
</div>
|
|
31
|
-
</template>
|
|
32
|
-
</ListItem>
|
|
33
|
-
</div>
|
|
34
|
-
|
|
35
8
|
<div>
|
|
36
9
|
<label class="text-text-400">Ratio</label>
|
|
37
10
|
<Dropdown v-model="item.props.ratio[idx]" class="mt-1" @change="$emit('change')">
|
|
@@ -105,8 +78,8 @@
|
|
|
105
78
|
<template #foot="{ context }">
|
|
106
79
|
<div class="p-5">
|
|
107
80
|
<Button type="button" class="w-full" @click="context.index >= 0 ?
|
|
108
|
-
item.
|
|
109
|
-
item.
|
|
81
|
+
item.items[context.index] = context.item :
|
|
82
|
+
item.items.push(context.item);
|
|
110
83
|
$refs.imageModal.close();
|
|
111
84
|
$emit('change')
|
|
112
85
|
">
|
|
@@ -154,6 +127,12 @@ export default{
|
|
|
154
127
|
|
|
155
128
|
imageRatio(){
|
|
156
129
|
return 'aspect-[4/1] md:aspect-[8/1]'
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
items(){
|
|
133
|
+
if(!Array.isArray(this.item.items))
|
|
134
|
+
this.item.items = []
|
|
135
|
+
return this.item.items
|
|
157
136
|
}
|
|
158
137
|
|
|
159
138
|
}
|
|
@@ -1,28 +1,72 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div :class="
|
|
3
|
-
<div
|
|
2
|
+
<div :class="compClass">
|
|
3
|
+
<div>
|
|
4
|
+
<h2 v-if="title">{{ title }}</h2>
|
|
5
|
+
<p v-if="description" v-html="description"></p>
|
|
6
|
+
</div>
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
<
|
|
8
|
-
</div>
|
|
8
|
+
<div class="flex flex-col gap-4">
|
|
9
|
+
<div v-for="field in fields">
|
|
10
|
+
<label class="text-gray-600">{{ field.label ?? field.type }}</label>
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
<div v-for="field in fields">
|
|
12
|
-
<label class="text-gray-700">{{ field.label ?? field.type }}</label>
|
|
12
|
+
<div class="mt-1">
|
|
13
13
|
<Textbox v-if="field.type === 'name'" v-model="form[field.type]" class="bg-white text-xl" :readonly="completed" />
|
|
14
|
-
|
|
15
|
-
<Textbox v-else-if="field.type === '
|
|
16
|
-
|
|
14
|
+
|
|
15
|
+
<Textbox v-else-if="field.type === 'university'" v-model="form[field.type]"
|
|
16
|
+
class="bg-white text-xl" :readonly="completed" />
|
|
17
|
+
|
|
18
|
+
<Textbox v-else-if="field.type === 'ktpNumber'" maxlength="16" type="tel" v-model="form[field.type]"
|
|
19
|
+
class="bg-white text-xl font-mono" :readonly="completed" />
|
|
20
|
+
|
|
21
|
+
<Textbox v-else-if="field.type === 'email'" v-model="form[field.type]"
|
|
22
|
+
class="bg-white text-xl md:w-[300px] max-w-full" :readonly="completed" />
|
|
23
|
+
|
|
24
|
+
<Textbox v-else-if="field.type === 'mobileNumber'" v-model="form[field.type]"
|
|
25
|
+
class="bg-white text-xl font-mono" maxlength="18" type="tel" :readonly="completed" />
|
|
26
|
+
|
|
27
|
+
<Textbox v-else-if="field.type === 'referralCode'" v-model="form[field.type]"
|
|
28
|
+
class="bg-white text-xl md:w-[200px] max-w-full" :readonly="completed" />
|
|
29
|
+
|
|
30
|
+
<div v-else-if="field.type === 'birthDate'" class="flex flex-col md:flex-row gap-2">
|
|
31
|
+
<Textbox v-model="form['birthPlace']" class="bg-base cursor-pointer" @click="$refs.citySelector.open()" readonly="true"
|
|
32
|
+
placeholder="Kota">
|
|
33
|
+
<template #end>
|
|
34
|
+
<div class="p-3">
|
|
35
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" class="fill-text-200" viewBox="0 0 320 512"><path d="M287.968 160H32.038c-28.425 0-42.767 34.488-22.627 54.627l127.962 128c12.496 12.496 32.758 12.497 45.255 0l127.968-128C330.695 194.528 316.45 160 287.968 160zM160 320L32 192h256L160 320z"/></svg>
|
|
36
|
+
</div>
|
|
37
|
+
</template>
|
|
38
|
+
</Textbox>
|
|
39
|
+
<Datepicker v-model="form['birthDate']" class="w-[200px] text-lg" default-value="2000-01-01" :readonly="completed" />
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<Dropdown v-else-if="field.type === 'position'" v-model="form[field.type]" class="text-xl">
|
|
43
|
+
<option v-for="item in field.items" :value="item.text">{{ item.text }}</option>
|
|
44
|
+
</Dropdown>
|
|
45
|
+
|
|
46
|
+
<Dropdown v-else-if="field.type === 'provinceName'" v-model="form[field.type]" class="text-xl">
|
|
47
|
+
<option v-for="item in field.items" :value="item.text">{{ item.text }}</option>
|
|
48
|
+
</Dropdown>
|
|
49
|
+
|
|
50
|
+
<Dropdown v-else-if="field.type === 'education'" v-model="form[field.type]" class="text-xl">
|
|
51
|
+
<option v-for="item in field.items" :value="item.text">{{ item.text }}</option>
|
|
52
|
+
</Dropdown>
|
|
53
|
+
|
|
54
|
+
<Dropdown v-else-if="field.type === 'major'" v-model="form[field.type]" class="text-xl">
|
|
55
|
+
<option v-for="item in field.items" :value="item.text">{{ item.text }}</option>
|
|
56
|
+
</Dropdown>
|
|
57
|
+
|
|
17
58
|
<Textarea v-else-if="field.type === 'remark'" v-model="form[field.type]" rows="2" :readonly="completed"></Textarea>
|
|
59
|
+
|
|
60
|
+
<pre v-else>{{ field.type }}</pre>
|
|
18
61
|
</div>
|
|
19
62
|
</div>
|
|
63
|
+
</div>
|
|
20
64
|
|
|
21
|
-
|
|
22
|
-
<h5>{{ completed ? 'Form berhasil dikirim' : 'Kirim' }}</h5>
|
|
23
|
-
</Button>
|
|
65
|
+
<CitySelector ref="citySelector" @change="(city) => form['birthPlace'] = city.alias"/>
|
|
24
66
|
|
|
25
|
-
|
|
67
|
+
<Button ref="submitBtn" class="w-full" @click="submitForm" :state="!canSubmit ? -1 : 1">
|
|
68
|
+
<h5>{{ completed ? 'Form berhasil dikirim' : 'Kirim' }}</h5>
|
|
69
|
+
</Button>
|
|
26
70
|
</div>
|
|
27
71
|
</template>
|
|
28
72
|
|
|
@@ -49,6 +93,7 @@ export default{
|
|
|
49
93
|
padding: Array,
|
|
50
94
|
enabled: undefined,
|
|
51
95
|
name: String,
|
|
96
|
+
class: String,
|
|
52
97
|
|
|
53
98
|
title: String,
|
|
54
99
|
description: String,
|
|
@@ -68,8 +113,12 @@ export default{
|
|
|
68
113
|
this.$refs.submitBtn.setState(2)
|
|
69
114
|
axios({
|
|
70
115
|
method: this.submitMethod ?? 'post',
|
|
71
|
-
url: this.submitUrl,
|
|
72
|
-
data:
|
|
116
|
+
url: import.meta.env.VITE_API_HOST + this.submitUrl,
|
|
117
|
+
data: {
|
|
118
|
+
url: this.$route.path,
|
|
119
|
+
componentUid: this.uid,
|
|
120
|
+
...this.form
|
|
121
|
+
},
|
|
73
122
|
params: this.form
|
|
74
123
|
})
|
|
75
124
|
.then(_ => this.completed = true)
|
|
@@ -82,15 +131,15 @@ export default{
|
|
|
82
131
|
computed: {
|
|
83
132
|
|
|
84
133
|
canSubmit(){
|
|
85
|
-
return this.fields.
|
|
134
|
+
return this.fields.length > 0 &&
|
|
135
|
+
this.fields.every(field => !field.required || this.form[field.type]) &&
|
|
136
|
+
Object.values(this.form).join('').length > 0 &&
|
|
86
137
|
!this.completed
|
|
87
138
|
},
|
|
88
139
|
|
|
89
140
|
compClass(){
|
|
90
141
|
return [
|
|
91
142
|
this.$style.comp,
|
|
92
|
-
this.status === 1 && this.spinnerType === 'shimmer' && !this.$slots['loading'] ?
|
|
93
|
-
this.$style.shimmer : '',
|
|
94
143
|
this.extClass,
|
|
95
144
|
this.class ?? ''
|
|
96
145
|
]
|
|
@@ -120,16 +169,11 @@ export default{
|
|
|
120
169
|
<style module>
|
|
121
170
|
|
|
122
171
|
.comp{
|
|
123
|
-
@apply flex-1;
|
|
172
|
+
@apply flex-1 w-full max-w-[480px] mx-auto rounded-lg overflow-hidden border-text-200 border-[1px] p-6;
|
|
173
|
+
@apply flex flex-col gap-8 bg-base;
|
|
124
174
|
background-image: v-bind(bgImages[0]);
|
|
125
175
|
}
|
|
126
176
|
|
|
127
|
-
.form{
|
|
128
|
-
@apply mx-auto bg-gray-100 border-text-200 border-[1px] p-6 rounded-lg;
|
|
129
|
-
@apply w-full max-w-[420px];
|
|
130
|
-
@apply flex flex-col gap-8;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
177
|
@media screen and (min-width: 768px){
|
|
134
178
|
.comp{
|
|
135
179
|
background-image: v-bind(bgImages[1]);
|
|
@@ -17,16 +17,21 @@
|
|
|
17
17
|
Add Field
|
|
18
18
|
</button>
|
|
19
19
|
</div>
|
|
20
|
-
<ListItem :items="item.props.fields" class="mt-2
|
|
20
|
+
<ListItem :items="item.props.fields" class="mt-2 border-text-200 border-[1px] rounded-lg overflow-hidden"
|
|
21
|
+
bodyClass="flex flex-col gap-1 divide-y divide-text-200"
|
|
21
22
|
@reorder="(from, to) => { item.props.fields.splice(to, 0, item.props.fields.splice(from, 1)[0]); $emit('change') }">
|
|
22
23
|
<template v-slot="{ item:field, index }">
|
|
23
|
-
<div class="flex flex-row items-center gap-2
|
|
24
|
+
<div class="flex flex-row items-center gap-2 px-3 bg-base-500">
|
|
24
25
|
<svg data-reorder width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
|
|
25
|
-
<div
|
|
26
|
-
<
|
|
26
|
+
<div>
|
|
27
|
+
<Checkbox v-model="field.required" @change="$emit('change')"/>
|
|
27
28
|
</div>
|
|
28
|
-
<
|
|
29
|
-
<
|
|
29
|
+
<div class="flex-1 px-2 text-ellipsis overflow-hidden whitespace-nowrap">
|
|
30
|
+
<label>{{ field.label ?? field.type }}</label>
|
|
31
|
+
</div>
|
|
32
|
+
<button class="text-sm text-primary" v-if="field._edit" @click="$refs[field._edit].open(field)">Edit</button>
|
|
33
|
+
<button type="button" @click="confirm($t('Remove this field?'), '', () => { item.props.fields.splice(index, 1); $emit('change') })">
|
|
34
|
+
<svg width="14" height="14" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
|
|
30
35
|
</button>
|
|
31
36
|
</div>
|
|
32
37
|
</template>
|
|
@@ -49,10 +54,10 @@
|
|
|
49
54
|
<div class="flex-1 flex flex-col gap-2">
|
|
50
55
|
<label class="text-text-400 leading-1">Send whatsapp to</label>
|
|
51
56
|
<div class="flex flex-row gap-2">
|
|
52
|
-
<Textbox class="flex-1" v-model="action.number" type="tel"
|
|
57
|
+
<Textbox class="flex-1" v-model="action.number" type="tel"/>
|
|
53
58
|
<Button type="button" class="text-primary px-3"
|
|
54
59
|
:state="!action.number ? -1 : 1"
|
|
55
|
-
@click="delete action._edit">OK</Button>
|
|
60
|
+
@click="delete action._edit;$emit('change')">OK</Button>
|
|
56
61
|
</div>
|
|
57
62
|
</div>
|
|
58
63
|
</div>
|
|
@@ -64,7 +69,7 @@
|
|
|
64
69
|
<button type="button" @click="action._edit = true">
|
|
65
70
|
<svg width="13" height="13" class="fill-text-300 hover:fill-primary-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M402.6 83.2l90.2 90.2c3.8 3.8 3.8 10 0 13.8L274.4 405.6l-92.8 10.3c-12.4 1.4-22.9-9.1-21.5-21.5l10.3-92.8L388.8 83.2c3.8-3.8 10-3.8 13.8 0zm162-22.9l-48.8-48.8c-15.2-15.2-39.9-15.2-55.2 0l-35.4 35.4c-3.8 3.8-3.8 10 0 13.8l90.2 90.2c3.8 3.8 10 3.8 13.8 0l35.4-35.4c15.2-15.3 15.2-40 0-55.2zM384 346.2V448H64V128h229.8c3.2 0 6.2-1.3 8.5-3.5l40-40c7.6-7.6 2.2-20.5-8.5-20.5H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V306.2c0-10.7-12.9-16-20.5-8.5l-40 40c-2.2 2.3-3.5 5.3-3.5 8.5z"/></svg>
|
|
66
71
|
</button>
|
|
67
|
-
<button type="button" @click="confirm($t('Remove this
|
|
72
|
+
<button type="button" @click="confirm($t('Remove this action?'), '', () => { item.props.onSubmit.splice(index, 1); $emit('change') })">
|
|
68
73
|
<svg width="16" height="16" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
|
|
69
74
|
</button>
|
|
70
75
|
</div>
|
|
@@ -106,27 +111,13 @@
|
|
|
106
111
|
defaultDisplay="flex"
|
|
107
112
|
@change="$emit('change')" />
|
|
108
113
|
|
|
109
|
-
<ContextMenu ref="fieldOpt" position="bottom-right">
|
|
110
|
-
<div class="flex flex-col
|
|
111
|
-
<button
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
@click="item.props.fields.push({ type:'mobileNumber', label:'Nomor HP' });$emit('change')">
|
|
117
|
-
Mobile Number
|
|
118
|
-
</button>
|
|
119
|
-
<button class="p-3 bg-base-500 text-left pl-8 hover:bg-text-50"
|
|
120
|
-
@click="item.props.fields.push({ type:'email', label:'Email' });$emit('change')">
|
|
121
|
-
Email
|
|
122
|
-
</button>
|
|
123
|
-
<button class="p-3 bg-base-500 text-left pl-8 hover:bg-text-50"
|
|
124
|
-
@click="item.props.fields.push({ type:'remark', label:'Pertanyaan' });$emit('change')">
|
|
125
|
-
Pertanyaan
|
|
126
|
-
</button>
|
|
127
|
-
<button class="p-3 bg-base-500 text-left pl-8 hover:bg-text-50"
|
|
128
|
-
@click="item.props.fields.push({ type:'referralCode', label:'Kode Referral (optional)' });$emit('change')">
|
|
129
|
-
Referral Code
|
|
114
|
+
<ContextMenu ref="fieldOpt" position="bottom-right" class="border-text-200">
|
|
115
|
+
<div class="flex flex-col min-w-[120px] divide-y divide-text-100">
|
|
116
|
+
<button v-for="field in fields"
|
|
117
|
+
class="p-3 bg-base-500 text-left pl-8 hover:bg-text-50 disabled:bg-base-500 disabled:text-text-300"
|
|
118
|
+
@click="item.props.fields.push(field);$emit('change')"
|
|
119
|
+
:disabled="item.props.fields.findIndex((_) => _.type === field.type) >= 0">
|
|
120
|
+
{{ field.label }}
|
|
130
121
|
</button>
|
|
131
122
|
</div>
|
|
132
123
|
</ContextMenu>
|
|
@@ -135,11 +126,62 @@
|
|
|
135
126
|
<div class="flex flex-col gap-1 min-w-[120px]">
|
|
136
127
|
<button class="p-3 bg-base-500 text-left pl-8 hover:bg-text-50"
|
|
137
128
|
@click="item.props.onSubmit.push({ type:'send-whatsapp', _edit:true })">Send Whatsapp</button>
|
|
138
|
-
<button class="p-3 bg-base-500 text-left pl-8 hover:bg-text-50"
|
|
129
|
+
<button class="p-3 bg-base-500 text-left pl-8 hover:bg-text-50" v-if="item.props.fields.includes('referralCode')"
|
|
139
130
|
@click="item.props.onSubmit.push({ type:'send-whatsapp-referral' });$emit('change')">Send Whatsapp to Referral</button>
|
|
140
131
|
</div>
|
|
141
132
|
</ContextMenu>
|
|
142
133
|
|
|
134
|
+
<Modal ref="positionEdit" width="360" height="480">
|
|
135
|
+
<template v-slot:head>
|
|
136
|
+
<div class="relative p-5">
|
|
137
|
+
<h3>Daftar Posisi</h3>
|
|
138
|
+
<div class="absolute top-0 right-0 p-2">
|
|
139
|
+
<button type="button" class="p-2" @click="$refs.positionEdit.close()">
|
|
140
|
+
<svg width="24" height="24" viewBox="0 0 24 24" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg">
|
|
141
|
+
<path d="M6.53034 5.46965C6.23745 5.17676 5.76257 5.17676 5.46968 5.46965C5.17679 5.76255 5.17679 6.23742 5.46968 6.53031L10.9393 12L5.46967 17.4697C5.17678 17.7626 5.17678 18.2374 5.46967 18.5303C5.76256 18.8232 6.23744 18.8232 6.53033 18.5303L12 13.0606L17.4697 18.5303C17.7626 18.8232 18.2375 18.8232 18.5303 18.5303C18.8232 18.2374 18.8232 17.7626 18.5303 17.4697L13.0607 12L18.5303 6.53032C18.8232 6.23743 18.8232 5.76256 18.5303 5.46966C18.2374 5.17677 17.7626 5.17677 17.4697 5.46966L12 10.9393L6.53034 5.46965Z"/>
|
|
142
|
+
</svg>
|
|
143
|
+
</button>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</template>
|
|
147
|
+
<template v-slot:foot="{ context }">
|
|
148
|
+
<div class="p-5">
|
|
149
|
+
<Button type="button" class="w-[100px]" @click="$util.removeEmpty(context.items);$refs.positionEdit.close();$emit('change')">OK</Button>
|
|
150
|
+
</div>
|
|
151
|
+
</template>
|
|
152
|
+
<template #default="{ context }">
|
|
153
|
+
<div class="flex-1 p-5">
|
|
154
|
+
<ListItem :items="context.items"
|
|
155
|
+
body-class="flex flex-col gap-2"
|
|
156
|
+
@reorder="(from, to) => { context.items.splice(to, 0, context.items.splice(from, 1)[0]); }">
|
|
157
|
+
<template v-slot="{ item, index }">
|
|
158
|
+
<div class="flex flex-row items-center gap-2">
|
|
159
|
+
<div class="flex-1">
|
|
160
|
+
<Textbox v-model="item.text" @keyup.enter="context.items.push({})">
|
|
161
|
+
<template #start>
|
|
162
|
+
<div v-if="context.items.length > 1" data-reorder class="self-stretch flex items-center justify-center px-2 bg-text-50">
|
|
163
|
+
<svg width="14" height="14" class="fill-text-300" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M496 288H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16zm0-128H16c-8.8 0-16 7.2-16 16v32c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16v-32c0-8.8-7.2-16-16-16z"/></svg>
|
|
164
|
+
</div>
|
|
165
|
+
</template>
|
|
166
|
+
</Textbox>
|
|
167
|
+
</div>
|
|
168
|
+
<div>
|
|
169
|
+
<button type="button" @click="context.items.splice(index, 1)">
|
|
170
|
+
<svg width="16" height="16" class="fill-text-300 hover:fill-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"/></svg>
|
|
171
|
+
</button>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
</template>
|
|
175
|
+
</ListItem>
|
|
176
|
+
|
|
177
|
+
<div class="text-center p-3">
|
|
178
|
+
<button type="button" class="text-primary" @click="context.items.push({})">Tambah Posisi</button>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
</div>
|
|
182
|
+
</template>
|
|
183
|
+
</Modal>
|
|
184
|
+
|
|
143
185
|
</div>
|
|
144
186
|
</template>
|
|
145
187
|
|
|
@@ -164,6 +206,25 @@ export default{
|
|
|
164
206
|
|
|
165
207
|
},
|
|
166
208
|
|
|
209
|
+
data(){
|
|
210
|
+
return {
|
|
211
|
+
fields: [
|
|
212
|
+
{ type:'name', label:'Nama' },
|
|
213
|
+
{ type:'mobileNumber', label:'Nomor HP' },
|
|
214
|
+
{ type:'email', label:'Email' },
|
|
215
|
+
{ type:'remark', label:'Pertanyaan' },
|
|
216
|
+
{ type:'referralCode', label:'Kode Referral (optional)' },
|
|
217
|
+
{ type:'ktpNumber', label:'Nomor KTP' },
|
|
218
|
+
{ type:'birthDate', label:'Tempat/Tanggal Lahir' },
|
|
219
|
+
{ type:'position', label:'Jabatan yang Dilamar', _edit:'positionEdit', items:[] },
|
|
220
|
+
{ type:'provinceName', label:'Propinsi Domisili' },
|
|
221
|
+
{ type:'education', label:'Pendidikan Terakhir', _edit:'positionEdit', items:[] },
|
|
222
|
+
{ type:'major', label:'Jurusan', _edit:'positionEdit', items:[] },
|
|
223
|
+
{ type:'university', label:'Nama Sekolah/Institusi/Universitas', _edit:'positionEdit', items:[] },
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
|
|
167
228
|
methods: {
|
|
168
229
|
|
|
169
230
|
addField(type){
|
|
@@ -564,6 +564,7 @@ export default{
|
|
|
564
564
|
},
|
|
565
565
|
|
|
566
566
|
generateExtClass(props){
|
|
567
|
+
if(!props || typeof props !== 'object') return ''
|
|
567
568
|
|
|
568
569
|
return [
|
|
569
570
|
props.padding ? props.padding.join(' ') : '',
|
|
@@ -604,7 +605,6 @@ export default{
|
|
|
604
605
|
]
|
|
605
606
|
.filter(_ => _)
|
|
606
607
|
.join(' ')
|
|
607
|
-
|
|
608
608
|
},
|
|
609
609
|
|
|
610
610
|
setExtClass(components){
|
|
@@ -618,6 +618,14 @@ export default{
|
|
|
618
618
|
comp.items = this.setExtClass(comp.items)
|
|
619
619
|
}
|
|
620
620
|
|
|
621
|
+
if(comp.props && Array.isArray(comp.props.items)){
|
|
622
|
+
comp.props.items = this.setExtClass(comp.props.items)
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if(comp.type === 'Carousel'){
|
|
626
|
+
console.log('Carousel', comp)
|
|
627
|
+
}
|
|
628
|
+
|
|
621
629
|
return comp
|
|
622
630
|
})
|
|
623
631
|
},
|
|
@@ -815,16 +823,21 @@ export default{
|
|
|
815
823
|
|
|
816
824
|
components: [
|
|
817
825
|
|
|
818
|
-
{ type:'Carousel', name:'Carousel', group:'Widgets', props:{
|
|
826
|
+
{ type:'Carousel', name:'Carousel', group:'Widgets', items:[], props:{
|
|
819
827
|
name: 'Untitled Carousel',
|
|
820
|
-
ratio:[ '4/3', '8/3' ]
|
|
828
|
+
ratio:[ '4/3', '8/3' ]
|
|
821
829
|
}},
|
|
822
830
|
|
|
823
831
|
{ type:'ContactForm', name:'Contact Form', group:'Widgets', props:{
|
|
824
832
|
fields:[
|
|
825
|
-
{ type:'name', label:'Nama' },
|
|
826
|
-
{ type:'mobileNumber', label:'Nomor HP' }
|
|
833
|
+
{ type:'name', label:'Nama', required:true },
|
|
834
|
+
{ type:'mobileNumber', label:'Nomor HP', required:true },
|
|
835
|
+
{ type:'remark', label:'Pertanyaan' },
|
|
827
836
|
],
|
|
837
|
+
title: 'Contact Us',
|
|
838
|
+
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent gravida erat eget nisi',
|
|
839
|
+
submitMethod: 'post',
|
|
840
|
+
submitUrl: '/inquiry',
|
|
828
841
|
onSubmit:[],
|
|
829
842
|
}},
|
|
830
843
|
|