@mixd-id/web-scaffold 0.1.230406088 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mixd-id/web-scaffold",
3
3
  "private": false,
4
- "version": "0.1.230406088",
4
+ "version": "0.1.230406089",
5
5
  "scripts": {
6
6
  "dev": "vite serve",
7
7
  "build": "vite build",
@@ -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))
@@ -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
- <div :class="compClass">
4
-
5
- <div v-if="mode === '' || mode === 'simple'">
6
- <select v-model="DD">
7
- <option disabled selected>Tanggal</option>
8
- <option v-for="d in 31" :value="d.toString().padStart(2, '0')">{{ d }}</option>
9
- </select>
10
- <select v-model="MM">
11
- <option disabled selected>Bulan</option>
12
- <option v-for="mmm in months" :value="mmm[0]">{{ mmm[1] }}</option>
13
- </select>
14
- <select v-model="YYYY">
15
- <option disabled selected>Tahun</option>
16
- <option v-for="yyyy in years" :value="yyyy">{{ yyyy }}</option>
17
- </select>
18
- </div>
19
-
20
- <div v-else-if="mode === 'popup'" ref="popup"
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
- .datepicker.mode-calendar{
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")))
@@ -1,28 +1,72 @@
1
1
  <template>
2
2
  <div :class="compClass">
3
- <div :class="$style.form">
3
+ <div>
4
+ <h2 v-if="title">{{ title }}</h2>
5
+ <p v-if="description" v-html="description"></p>
6
+ </div>
4
7
 
5
- <div>
6
- <h2 v-if="title">{{ title }}</h2>
7
- <p v-if="description" v-html="description"></p>
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
- <div class="flex flex-col gap-4">
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
- <Textbox v-if="field.type === 'email'" v-model="form[field.type]" class="bg-white text-xl md:w-[300px] max-w-full" :readonly="completed" />
15
- <Textbox v-else-if="field.type === 'mobileNumber'" v-model="form[field.type]" class="bg-white text-xl" type="tel" :readonly="completed" />
16
- <Textbox v-else-if="field.type === 'referralCode'" v-model="form[field.type]" class="bg-white text-xl md:w-[200px] max-w-full" :readonly="completed" />
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
- <Button ref="submitBtn" class="w-full" @click="submitForm" :state="!canSubmit ? -1 : 1">
22
- <h5>{{ completed ? 'Form berhasil dikirim' : 'Kirim' }}</h5>
23
- </Button>
65
+ <CitySelector ref="citySelector" @change="(city) => form['birthPlace'] = city.alias"/>
24
66
 
25
- </div>
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
 
@@ -69,8 +113,12 @@ export default{
69
113
  this.$refs.submitBtn.setState(2)
70
114
  axios({
71
115
  method: this.submitMethod ?? 'post',
72
- url: this.submitUrl,
73
- data: this.form,
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
+ },
74
122
  params: this.form
75
123
  })
76
124
  .then(_ => this.completed = true)
@@ -83,7 +131,9 @@ export default{
83
131
  computed: {
84
132
 
85
133
  canSubmit(){
86
- return this.fields.every(field => [ 'referralCode', 'email', 'remark' ].includes(field.type) || this.form[field.type]) &&
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 &&
87
137
  !this.completed
88
138
  },
89
139
 
@@ -119,16 +169,11 @@ export default{
119
169
  <style module>
120
170
 
121
171
  .comp{
122
- @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;
123
174
  background-image: v-bind(bgImages[0]);
124
175
  }
125
176
 
126
- .form{
127
- @apply mx-auto bg-gray-100 border-text-200 border-[1px] p-6 rounded-lg;
128
- @apply w-full max-w-[420px];
129
- @apply flex flex-col gap-8;
130
- }
131
-
132
177
  @media screen and (min-width: 768px){
133
178
  .comp{
134
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" bodyClass="flex flex-col gap-1"
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 p-3 bg-base-500 rounded-lg">
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 class="flex-1 px-2">
26
+ <div>
27
+ <Checkbox v-model="field.required" @change="$emit('change')"/>
28
+ </div>
29
+ <div class="flex-1 px-2 text-ellipsis overflow-hidden whitespace-nowrap">
26
30
  <label>{{ field.label ?? field.type }}</label>
27
31
  </div>
28
- <button type="button" @click="confirm($t('Remove this item?'), '', () => { item.props.fields.splice(index, 1); $emit('change') })">
29
- <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>
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 item?'), '', () => { item.props.onSubmit.splice(index, 1); $emit('change') })">
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,11 +111,12 @@
106
111
  defaultDisplay="flex"
107
112
  @change="$emit('change')" />
108
113
 
109
- <ContextMenu ref="fieldOpt" position="bottom-right">
110
- <div class="flex flex-col gap-1 min-w-[120px]">
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">
111
116
  <button v-for="field in fields"
112
- class="p-3 bg-base-500 text-left pl-8 hover:bg-text-50"
113
- @click="item.props.fields.push(field);$emit('change')">
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">
114
120
  {{ field.label }}
115
121
  </button>
116
122
  </div>
@@ -120,11 +126,62 @@
120
126
  <div class="flex flex-col gap-1 min-w-[120px]">
121
127
  <button class="p-3 bg-base-500 text-left pl-8 hover:bg-text-50"
122
128
  @click="item.props.onSubmit.push({ type:'send-whatsapp', _edit:true })">Send Whatsapp</button>
123
- <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')"
124
130
  @click="item.props.onSubmit.push({ type:'send-whatsapp-referral' });$emit('change')">Send Whatsapp to Referral</button>
125
131
  </div>
126
132
  </ContextMenu>
127
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
+
128
185
  </div>
129
186
  </template>
130
187
 
@@ -158,12 +215,12 @@ export default{
158
215
  { type:'remark', label:'Pertanyaan' },
159
216
  { type:'referralCode', label:'Kode Referral (optional)' },
160
217
  { type:'ktpNumber', label:'Nomor KTP' },
161
- { type:'birthDate', label:'Tanggal Lahir' },
162
- { type:'position', label:'Jabatan yang Dilamar' },
218
+ { type:'birthDate', label:'Tempat/Tanggal Lahir' },
219
+ { type:'position', label:'Jabatan yang Dilamar', _edit:'positionEdit', items:[] },
163
220
  { type:'provinceName', label:'Propinsi Domisili' },
164
- { type:'education', label:'Pendidikan Terakhir' },
165
- { type:'major', label:'Jurusan' },
166
- { type:'university', label:'Nama Sekolah/Institusi/Universitas' },
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:[] },
167
224
  ]
168
225
  }
169
226
  },
@@ -830,9 +830,14 @@ export default{
830
830
 
831
831
  { type:'ContactForm', name:'Contact Form', group:'Widgets', props:{
832
832
  fields:[
833
- { type:'name', label:'Nama' },
834
- { 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' },
835
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',
836
841
  onSubmit:[],
837
842
  }},
838
843