@mixd-id/web-scaffold 0.2.240706 → 0.2.250801009

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.
Files changed (220) hide show
  1. package/docs/components/Dashboard.md +56 -0
  2. package/log.txt +7 -0
  3. package/package.json +27 -19
  4. package/src/components/404.vue +61 -0
  5. package/src/components/AccountIcon.vue +19 -0
  6. package/src/components/Ahref.vue +1 -1
  7. package/src/components/Alert.vue +4 -13
  8. package/src/components/ArrayList.vue +49 -0
  9. package/src/components/Article.vue +24 -30
  10. package/src/components/Button.vue +79 -167
  11. package/src/components/Card.vue +235 -0
  12. package/src/components/Carousel.vue +61 -60
  13. package/src/components/Cart.vue +192 -0
  14. package/src/components/CartIcon.vue +89 -0
  15. package/src/components/ChartBar.vue +2 -3
  16. package/src/components/Checkbox.vue +20 -11
  17. package/src/components/Checkout.vue +373 -0
  18. package/src/components/CheckoutDelivery.vue +267 -0
  19. package/src/components/CodeEditor.vue +5 -16
  20. package/src/components/CollapsiblePanel.vue +70 -0
  21. package/src/components/ColorPicker.vue +8 -3
  22. package/src/components/ColorPicker2.vue +41 -19
  23. package/src/components/ColorPicker3.vue +100 -0
  24. package/src/components/Confirm.vue +9 -7
  25. package/src/components/ContextMenu.vue +122 -206
  26. package/src/components/ContextMenuItem.vue +53 -0
  27. package/src/components/Dashboard.vue +243 -0
  28. package/src/components/Dashboard2.vue +118 -0
  29. package/src/components/DashboardComponentSelector.vue +96 -0
  30. package/src/components/DashboardConfigs.vue +202 -0
  31. package/src/components/Datepicker.vue +102 -41
  32. package/src/components/DayTimeRange.vue +3 -2
  33. package/src/components/Dropdown.vue +7 -4
  34. package/src/components/Flex.vue +14 -40
  35. package/src/components/GHeatMaps.vue +2 -2
  36. package/src/components/Grid.vue +6 -6
  37. package/src/components/HTMLEditor.vue +27 -14
  38. package/src/components/Image.vue +62 -108
  39. package/src/components/ImagePreview.vue +14 -4
  40. package/src/components/ImageUploader.vue +114 -0
  41. package/src/components/ImportModal.vue +3 -3
  42. package/src/components/Link.vue +62 -6
  43. package/src/components/List.vue +524 -403
  44. package/src/components/ListContextMenu.vue +88 -0
  45. package/src/components/ListItem.vue +5 -3
  46. package/src/components/ListPage1.vue +14 -15
  47. package/src/components/ListView.vue +5 -6
  48. package/src/components/ListViewSettings.vue +2 -2
  49. package/src/components/LogViewerItem.vue +1 -1
  50. package/src/components/MarkdownEdit.vue +128 -0
  51. package/src/components/MarkdownPreview.vue +102 -0
  52. package/src/components/MenuItem1.vue +36 -0
  53. package/src/components/Modal.vue +95 -43
  54. package/src/components/MultiDropdown.vue +124 -0
  55. package/src/components/MultilineText.vue +1 -4
  56. package/src/components/OTPField.vue +11 -17
  57. package/src/components/ObjectTree.vue +1 -1
  58. package/src/components/PageBuilder.vue +3 -3
  59. package/src/components/Paragraph.vue +1 -2
  60. package/src/components/PresetSelectorFilterItem.vue +107 -95
  61. package/src/components/Radio.vue +1 -1
  62. package/src/components/SearchModal.vue +153 -0
  63. package/src/components/Slider.vue +1 -1
  64. package/src/components/Svg.vue +1 -1
  65. package/src/components/SvgEditor.vue +173 -0
  66. package/src/components/Switch.vue +4 -5
  67. package/src/components/Table.vue +2 -2
  68. package/src/components/TableView.vue +2 -3
  69. package/src/components/TableViewHead.vue +2 -2
  70. package/src/components/Tabs.vue +1 -1
  71. package/src/components/Testimonial.vue +2 -2
  72. package/src/components/Text.vue +7 -22
  73. package/src/components/TextEditor.vue +3 -3
  74. package/src/components/TextWithTag.vue +61 -30
  75. package/src/components/Textarea.vue +11 -16
  76. package/src/components/Textbox.vue +9 -19
  77. package/src/components/Timepicker.vue +25 -15
  78. package/src/components/Toast.vue +5 -3
  79. package/src/components/TreeMenu.vue +122 -0
  80. package/src/components/TreeView.vue +15 -10
  81. package/src/components/TreeView2.vue +38 -0
  82. package/src/components/TreeViewItem.vue +58 -29
  83. package/src/components/TreeViewItem2.vue +55 -0
  84. package/src/components/Uploader.vue +45 -0
  85. package/src/components/Video.vue +119 -0
  86. package/src/components/VirtualGrid.vue +24 -7
  87. package/src/components/VirtualTable.vue +363 -128
  88. package/src/configs/dashboard/data-table.js +9 -0
  89. package/src/configs/web-page-builder.js +118 -0
  90. package/src/directives/intersect.js +26 -0
  91. package/src/hooks/device.js +14 -0
  92. package/src/index.js +62 -107
  93. package/src/mixin/component.js +147 -67
  94. package/src/themes/default/index.js +83 -155
  95. package/src/utils/dashboard.js +22 -962
  96. package/src/utils/helpers.cjs +635 -0
  97. package/src/utils/helpers.js +91 -60
  98. package/src/utils/helpers.mjs +245 -12
  99. package/src/utils/importer.js +22 -3
  100. package/src/utils/list.mjs +1509 -0
  101. package/src/utils/preset-selector.cjs +1455 -0
  102. package/src/utils/preset-selector.js +489 -95
  103. package/src/utils/preset-selector.mjs +59 -20
  104. package/src/utils/queue.js +63 -0
  105. package/src/utils/web.mjs +120 -0
  106. package/src/utils/wss.js +37 -29
  107. package/src/utils/wss.mjs +24 -19
  108. package/src/widgets/AhrefSetting.vue +16 -13
  109. package/src/widgets/ArticleSetting.vue +15 -27
  110. package/src/widgets/BackgroundColorSetting.vue +153 -0
  111. package/src/widgets/BorderColorSetting.vue +57 -0
  112. package/src/widgets/BotEditor/BotEditorActions.vue +3 -2
  113. package/src/widgets/BotEditor/BotEditorSettings.vue +21 -0
  114. package/src/widgets/BotEditor.vue +35 -15
  115. package/src/widgets/ButtonSetting.vue +12 -13
  116. package/src/widgets/CarouselSetting.vue +33 -45
  117. package/src/widgets/CartSetting.vue +46 -0
  118. package/src/widgets/CheckoutSetting.vue +46 -0
  119. package/src/widgets/CollapsiblePanelSetting.vue +46 -0
  120. package/src/widgets/ColumnSelector.vue +29 -5
  121. package/src/widgets/ComponentSetting.vue +1 -1
  122. package/src/widgets/ComponentSetting2.vue +112 -234
  123. package/src/widgets/ComponentSetting3.vue +1 -1
  124. package/src/widgets/ContactForm.vue +3 -3
  125. package/src/widgets/ContactFormSetting.vue +41 -30
  126. package/src/widgets/Dashboard/BarChart.vue +47 -11
  127. package/src/widgets/Dashboard/BarChartSetting.vue +1 -1
  128. package/src/widgets/Dashboard/DataTable.vue +125 -0
  129. package/src/widgets/Dashboard/DataTableSetting.vue +243 -0
  130. package/src/widgets/Dashboard/DatasourceSelector.vue +1 -1
  131. package/src/widgets/Dashboard/Doughnut.vue +49 -7
  132. package/src/widgets/Dashboard/DoughnutSetting.vue +2 -2
  133. package/src/widgets/Dashboard/Metric.vue +78 -19
  134. package/src/widgets/Dashboard/MetricSetting.vue +81 -28
  135. package/src/widgets/Dashboard/Pie.vue +55 -6
  136. package/src/widgets/Dashboard/PieSetting.vue +1 -1
  137. package/src/widgets/Dashboard/PolarArea.vue +49 -7
  138. package/src/widgets/Dashboard/PolarAreaSetting.vue +1 -1
  139. package/src/widgets/Dashboard/SharingModal.vue +4 -5
  140. package/src/widgets/Dashboard/ViewSelector.vue +2 -2
  141. package/src/widgets/Dashboard/VirtualTableSetting.vue +121 -184
  142. package/src/widgets/{Dashboard.vue → Dashboard0.vue} +426 -343
  143. package/src/widgets/EmbeddedVideoSetting.vue +7 -5
  144. package/src/widgets/FAQ.vue +16 -3
  145. package/src/widgets/FAQSetting.vue +53 -47
  146. package/src/widgets/FeatureList.vue +3 -0
  147. package/src/widgets/FeatureListSetting.vue +112 -102
  148. package/src/widgets/FlexSetting.vue +83 -106
  149. package/src/widgets/GridSetting.vue +71 -196
  150. package/src/widgets/Header2.vue +34 -71
  151. package/src/widgets/Header2Setting.vue +95 -179
  152. package/src/widgets/HeaderSetting.vue +16 -18
  153. package/src/widgets/IconListSetting.vue +69 -65
  154. package/src/widgets/ImageSetting.vue +33 -60
  155. package/src/widgets/LinkSetting.vue +60 -37
  156. package/src/widgets/LinkSettingModal.vue +173 -0
  157. package/src/widgets/LogViewer.vue +1 -1
  158. package/src/widgets/MarginSetting.vue +2 -2
  159. package/src/widgets/MenuEditor.vue +1 -1
  160. package/src/widgets/MenuItem1Setting.vue +78 -0
  161. package/src/widgets/ModalSetting.vue +42 -44
  162. package/src/widgets/MultiValueSetting.vue +2 -2
  163. package/src/widgets/MultiValueSetting2.vue +78 -45
  164. package/src/widgets/OGSettingModal.vue +103 -0
  165. package/src/widgets/PaddingSetting.vue +2 -2
  166. package/src/widgets/ParagraphSetting.vue +16 -13
  167. package/src/widgets/PositionSetting.vue +209 -0
  168. package/src/widgets/PresetBar.vue +359 -210
  169. package/src/widgets/PresetBarPivot.vue +31 -19
  170. package/src/widgets/PresetSelector.vue +29 -17
  171. package/src/widgets/SearchModalSetting.vue +70 -0
  172. package/src/widgets/Share.vue +1 -2
  173. package/src/widgets/ShareSetting.vue +67 -60
  174. package/src/widgets/StyleSetting.vue +227 -116
  175. package/src/widgets/TestimonialSetting.vue +97 -88
  176. package/src/widgets/TextBlockSetting.vue +16 -13
  177. package/src/widgets/UserActionBuilder/UserActionConsole.vue +30 -10
  178. package/src/widgets/UserActionBuilder/UserActionOutput.vue +2 -2
  179. package/src/widgets/UserActionBuilder/UserActionOutputReply.vue +64 -87
  180. package/src/widgets/UserActionBuilder/UserActionProps.vue +3 -3
  181. package/src/widgets/UserActionBuilder.vue +4 -16
  182. package/src/widgets/WebComponentSelector.vue +15 -11
  183. package/src/widgets/WebLayoutSelector.vue +41 -270
  184. package/src/widgets/WebPageBuilder.vue +693 -704
  185. package/src/widgets/WebPageBuilder2.vue +7 -7
  186. package/src/widgets/WebPageBuilder4/ButtonSetting.vue +0 -8
  187. package/src/widgets/WebPageBuilder4/CarouselSetting.vue +63 -7
  188. package/src/widgets/WebPageBuilder4/FlexAlignSetting.vue +3 -3
  189. package/src/widgets/WebPageBuilder4/FlexSetting.vue +1 -10
  190. package/src/widgets/WebPageBuilder4/MultiValueSetting.vue +2 -2
  191. package/src/widgets/WebPageBuilder4/PropertySetting.vue +0 -7
  192. package/src/widgets/WebPageBuilder4/WebPageComponentSelector.vue +1 -7
  193. package/src/widgets/WebPageBuilder4.vue +289 -575
  194. package/src/widgets/WebPageSelector.vue +1 -1
  195. package/src/widgets/YoutubeVideoSetting.vue +16 -13
  196. package/tailwind.config.js +3 -35
  197. package/docs/schema/user-action.json +0 -266
  198. package/src/App.vue +0 -25
  199. package/src/components/SearchButton.vue +0 -57
  200. package/src/entry-client.js +0 -27
  201. package/src/entry-server.js +0 -73
  202. package/src/events/event.js +0 -2
  203. package/src/main.js +0 -29
  204. package/src/mixin/website.js +0 -121
  205. package/src/router.js +0 -57
  206. package/src/widgets/MobileMenu.vue +0 -182
  207. package/src/widgets/WebPageBuilder4/ActionSetting.vue +0 -158
  208. package/src/widgets/WebPageBuilder4/ColorSetting.vue +0 -63
  209. package/src/widgets/WebPageBuilder4/DataSetting.vue +0 -92
  210. package/src/widgets/WebPageBuilder4/FontSizeSetting.vue +0 -76
  211. package/src/widgets/WebPageBuilder4/LinkSetting.vue +0 -68
  212. package/src/widgets/WebPageBuilder4/MobileMenuSetting.vue +0 -106
  213. package/src/widgets/WebPageBuilder4/Setting.vue +0 -73
  214. package/src/widgets/WebPageBuilder4/StyleSetting.vue +0 -77
  215. package/src/widgets/WebPageBuilder4/SvgSetting.vue +0 -207
  216. package/src/widgets/WebPageBuilder4/TextTransformSetting.vue +0 -70
  217. package/src/widgets/WebPageBuilder4/WebPageDataEdit.vue +0 -121
  218. package/test.json +0 -22
  219. /package/src/widgets/{Header1.vue → Header0.vue} +0 -0
  220. /package/src/widgets/{Header1Setting.vue → Header0Setting.vue} +0 -0
@@ -5,30 +5,42 @@
5
5
  :style="computedStyle">
6
6
 
7
7
  <div ref="inner" :class="computedContainerClass">
8
- <div v-for="item in cItems" :class="computedItemClass">
8
+ <div v-for="item in items" :class="computedItemClass">
9
9
  <slot v-if="$slots['default']" :item="item"></slot>
10
10
  <component v-else :is="item.type" :="item" />
11
11
  </div>
12
12
  </div>
13
13
 
14
- <div v-if="cItems && cItems.length > 1" :class="$style.carouselNext" @click="next(true)">
15
- <slot name="next"></slot>
14
+ <div v-if="useNextPrev && (items ?? []).length > 1 && index < items.length - 1" :class="$style.carouselNext" @click="next(true)">
15
+ <slot name="next">
16
+ <div class="p-3">
17
+ <button :class="$style.btn">
18
+ <svg :class="$style.btnSvg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M438.6 278.6l-160 160C272.4 444.9 264.2 448 256 448s-16.38-3.125-22.62-9.375c-12.5-12.5-12.5-32.75 0-45.25L338.8 288H32C14.33 288 .0016 273.7 .0016 256S14.33 224 32 224h306.8l-105.4-105.4c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0l160 160C451.1 245.9 451.1 266.1 438.6 278.6z"/></svg>
19
+ </button>
20
+ </div>
21
+ </slot>
16
22
  </div>
17
23
 
18
- <div v-if="cItems && cItems.length > 1" :class="$style.carouselPrev" @click="prev">
24
+ <div v-if="useNextPrev && (items ?? []).length > 1 && index > 0" :class="$style.carouselPrev" @click="prev">
19
25
  <slot name="prev">
26
+ <div class="p-3">
27
+ <button :class="$style.btn">
28
+ <svg :class="$style.btnSvg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M447.1 256C447.1 273.7 433.7 288 416 288H109.3l105.4 105.4c12.5 12.5 12.5 32.75 0 45.25C208.4 444.9 200.2 448 192 448s-16.38-3.125-22.62-9.375l-160-160c-12.5-12.5-12.5-32.75 0-45.25l160-160c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25L109.3 224H416C433.7 224 447.1 238.3 447.1 256z"/></svg>
29
+ </button>
30
+ </div>
20
31
  </slot>
21
32
  </div>
22
33
 
23
- <div v-if="useLegend && cItems && cItems.length > 1" :class="$style.legend">
24
- <div v-for="_index in cItems.length"
25
- :class="$style.legendItem + (index === _index - 1 ? ` ${$style.legendItemActive}` : '')"
26
- @click="index = _index - 1">
34
+ <div :class="$style.legend">
35
+ <div v-for="_index in items.length"
36
+ :class="$style.legendItem + (index === _index - 1 ? ` ${$style.legendItemActive}` : '')"
37
+ @click="setIndex(_index - 1)">
27
38
  <slot name="legend">
28
- <div></div>
39
+ <div></div>
29
40
  </slot>
30
41
  </div>
31
42
  </div>
43
+
32
44
  </div>
33
45
  </template>
34
46
 
@@ -38,51 +50,10 @@ import {componentMixin} from "../mixin/component";
38
50
 
39
51
  export default{
40
52
 
41
- computed: {
42
-
43
- cItems(){
44
-
45
- if(!this.dataKey)
46
- return this.items
47
-
48
- const page = this.getPage()
49
- const obj = page.data?.find(_ => _.key === this.dataKey)
50
- return obj ?
51
- obj.value.map(_ => ({
52
- ...this.items[0],
53
- data: _
54
- })) :
55
- []
56
- },
57
-
58
- computedContainerClass(){
59
- return [
60
- this.$style.inner,
61
- this.containerClass
62
- ]
63
- .filter(_ => _)
64
- .join(' ')
65
- },
66
-
67
- computedItemClass(){
68
- return [
69
- this.$style.item,
70
- this.itemClass
71
- ]
72
- .filter(_ => _)
73
- .join(' ')
74
- }
75
-
76
- },
77
-
78
53
  mixins: [ componentMixin ],
79
54
 
80
55
  props:{
81
56
 
82
- data: Object,
83
-
84
- dataKey: String,
85
-
86
57
  items: Array,
87
58
 
88
59
  infinite: {
@@ -96,10 +67,34 @@ export default{
96
67
 
97
68
  useLegend: [ Boolean, Number ],
98
69
 
70
+ useNextPrev: [ Boolean, Number ],
71
+
99
72
  },
100
73
 
101
74
  emits: [ 'change' ],
102
75
 
76
+ computed: {
77
+
78
+ computedContainerClass(){
79
+ return [
80
+ this.$style.inner,
81
+ this.containerClass
82
+ ]
83
+ .filter(_ => _)
84
+ .join(' ')
85
+ },
86
+
87
+ computedItemClass(){
88
+ return [
89
+ this.$style.item,
90
+ this.itemClass
91
+ ]
92
+ .filter(_ => _)
93
+ .join(' ')
94
+ }
95
+
96
+ },
97
+
103
98
  data(){
104
99
  return {
105
100
  index: 0,
@@ -147,7 +142,7 @@ export default{
147
142
  const index = this.index
148
143
  let left = 0
149
144
  const gapPx = parseInt(window.getComputedStyle(this.$refs.inner).getPropertyValue('column-gap'))
150
- for(let curIndex = 0 ; curIndex < this.cItems.length ; curIndex++){
145
+ for(let curIndex = 0 ; curIndex < this.items.length ; curIndex++){
151
146
  const item = this.$refs.inner.children[curIndex]
152
147
  if(curIndex < index)
153
148
  left += (item.clientWidth + (!isNaN(gapPx) ? gapPx : 0))
@@ -168,7 +163,7 @@ export default{
168
163
  },
169
164
 
170
165
  setIndex(index){
171
- if(index >= 0 && index < this.cItems.length){
166
+ if(index >= 0 && index < this.items.length){
172
167
  this.index = index
173
168
  this.setPosition()
174
169
  }
@@ -177,8 +172,8 @@ export default{
177
172
  next(useBack){
178
173
  let index = this.index + 1
179
174
 
180
- if(index >= this.cItems.length)
181
- index = useBack === true ? 0 : this.cItems.length - 1
175
+ if(index >= this.items.length)
176
+ index = useBack === true ? 0 : this.items.length - 1
182
177
 
183
178
  this.index = index
184
179
 
@@ -223,7 +218,7 @@ export default{
223
218
  }
224
219
  else if(this._props['direction'] === 'horizontal'){
225
220
  let ix
226
- if((this.index === this.cItems.length - 1 && dx < 0) ||
221
+ if((this.index === this.items.length - 1 && dx < 0) ||
227
222
  (this.index === 0 && dx > 0)){
228
223
  ix = parseInt(this._props['ix'] + (dx * .19812))
229
224
  }
@@ -268,7 +263,7 @@ export default{
268
263
  },
269
264
 
270
265
  mouseDown(e){
271
- if(!this.cItems || this.cItems.length <= 1)
266
+ if(!this.items || this.items.length <= 1)
272
267
  return
273
268
 
274
269
  this.stop()
@@ -318,9 +313,9 @@ export default{
318
313
  <style module>
319
314
 
320
315
  .comp{
321
- white-space: nowrap;
322
316
  overflow: hidden;
323
317
  position: relative;
318
+ @apply flex flex-col;
324
319
  }
325
320
 
326
321
  .comp img{
@@ -328,15 +323,14 @@ export default{
328
323
  }
329
324
 
330
325
  .carouselNext{
331
- @apply absolute top-0 bottom-0 right-0 w-[20px];
326
+ @apply absolute top-0 bottom-0 right-0 flex items-center justify-center;
332
327
  }
333
328
 
334
329
  .carouselPrev{
335
- @apply absolute top-0 bottom-0 left-0 w-[20px];
330
+ @apply absolute top-0 bottom-0 left-0 flex items-center justify-center;
336
331
  }
337
332
 
338
333
  .inner{
339
- white-space: nowrap;
340
334
  will-change: transform;
341
335
  @apply flex flex-row items-stretch;
342
336
  }
@@ -359,5 +353,12 @@ export default{
359
353
  @apply bg-primary-500;
360
354
  }
361
355
 
356
+ .btn{
357
+ @apply w-[32px] h-[32px] md:w-[48px] md:h-[48px] rounded-full bg-black/50 flex items-center justify-center opacity-50 hover:opacity-70;
358
+ }
359
+ .btnSvg{
360
+ @apply w-[19px] h-[19px] md:w-[32px] md:h-[32px] fill-white/70 hover:fill-white/90;
361
+ }
362
+
362
363
 
363
364
  </style>
@@ -0,0 +1,192 @@
1
+ <template>
2
+ <div :class="$style.el">
3
+
4
+ <div v-if="readyState === 2" class="flex justify-center items-center min-h-[80vh]">
5
+ Loading...
6
+ </div>
7
+
8
+ <div v-else-if="!cart" class="flex flex-col gap-4 justify-center items-center min-h-[80vh]">
9
+ <div class="text-center">
10
+ <h1>Cart is Empty</h1>
11
+ <p class="max-w-[480px]">Your cart is empty, if you're interested in our products, browse products from our catalog by clicking button below.</p>
12
+ </div>
13
+ <router-link to="/" class="text-primary">Browse Catalog</router-link>
14
+ </div>
15
+
16
+ <div v-else class="flex flex-col">
17
+
18
+ <div class="p-5">
19
+ <h3>Cart</h3>
20
+ </div>
21
+
22
+ <div class="divide-y divide-border-50 px-3 bg-base-300 min-h-[80vh]">
23
+ <div v-for="item in cart.items" class="flex flex-row gap-2 p-2">
24
+ <Checkbox v-model="item.checked" />
25
+ <Image :src="item.imageUrl" class="w-[56px] aspect-square rounded-lg" />
26
+ <div class="flex-1 flex flex-col">
27
+ <small>{{ item.code }}</small>
28
+ <label>{{ item.name }}</label>
29
+ <div class="flex flex-row gap-2">
30
+ <strong>Rp. {{ item.price.toLocaleString() }}</strong>
31
+ <button type="button" class="text-primary text-xs" @click="remove(item)">
32
+ <svg width="11" height="11" class="fill-text" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 80h-82.38l-34-56.75C306.1 8.827 291.4 0 274.6 0H173.4C156.6 0 141 8.827 132.4 23.25L98.38 80H16C7.125 80 0 87.13 0 96v16C0 120.9 7.125 128 16 128H32v320c0 35.35 28.65 64 64 64h256c35.35 0 64-28.65 64-64V128h16C440.9 128 448 120.9 448 112V96C448 87.13 440.9 80 432 80zM171.9 50.88C172.9 49.13 174.9 48 177 48h94c2.125 0 4.125 1.125 5.125 2.875L293.6 80H154.4L171.9 50.88zM352 464H96c-8.837 0-16-7.163-16-16V128h288v320C368 456.8 360.8 464 352 464zM224 416c8.844 0 16-7.156 16-16V192c0-8.844-7.156-16-16-16S208 183.2 208 192v208C208 408.8 215.2 416 224 416zM144 416C152.8 416 160 408.8 160 400V192c0-8.844-7.156-16-16-16S128 183.2 128 192v208C128 408.8 135.2 416 144 416zM304 416c8.844 0 16-7.156 16-16V192c0-8.844-7.156-16-16-16S288 183.2 288 192v208C288 408.8 295.2 416 304 416z"/></svg>
33
+ </button>
34
+ </div>
35
+ </div>
36
+ <div class="flex flex-col items-end gap-1">
37
+ <Textbox class="w-[100px]"
38
+ v-model="item.qty"
39
+ item-class="text-center p-1">
40
+ <template #start>
41
+ <button type="button"
42
+ class="p-2"
43
+ @click="item.qty = (res => res < 1 ? 1 : res)(item.qty - 1)">
44
+ <svg width="14" height="14" class="fill-text" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 256C432 269.3 421.3 280 408 280H40c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h368C421.3 232 432 242.8 432 256z"/></svg>
45
+ </button>
46
+ </template>
47
+ <template #end>
48
+ <button type="button"
49
+ class="p-2"
50
+ @click="item.qty = (res => res > 5 ? 5 : res)(item.qty + 1)">
51
+ <svg width="14" height="14" class="fill-text" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M432 256C432 269.3 421.3 280 408 280h-160v160c0 13.25-10.75 24.01-24 24.01S200 453.3 200 440v-160h-160c-13.25 0-24-10.74-24-23.99C16 242.8 26.75 232 40 232h160v-160c0-13.25 10.75-23.99 24-23.99S248 58.75 248 72v160h160C421.3 232 432 242.8 432 256z"/></svg>
52
+ </button>
53
+ </template>
54
+ </Textbox>
55
+ </div>
56
+ </div>
57
+ </div>
58
+
59
+ <div class="p-5 py-3 bg-base-300 flex flex-row sticky bottom-0 border-t-[1px] border-border-50">
60
+ <div v-if="cart?.total > 0" class="flex flex-col flex-1 leading-3">
61
+ <small>Total</small>
62
+ <h5>Rp. {{ cart.total.toLocaleString() }}</h5>
63
+ </div>
64
+ <div v-else class="flex-1"></div>
65
+ <Button ref="checkoutBtn"
66
+ class="px-3"
67
+ :state="canCheckout ? 1 : -1"
68
+ @click="checkout">
69
+ Checkout
70
+ </Button>
71
+ </div>
72
+
73
+ </div>
74
+
75
+ </div>
76
+ </template>
77
+
78
+ <script>
79
+
80
+ import axios from "axios";
81
+ import {invokeAfterIdle} from "../utils/helpers.mjs";
82
+
83
+ export default {
84
+
85
+ inject: [ 'alert' ],
86
+
87
+ computed: {
88
+
89
+ canCheckout(){
90
+ return this.apiUrl &&
91
+ this.cart && (this.cart.items ?? []).some(item => item.checked)
92
+ }
93
+
94
+ },
95
+
96
+ props: {
97
+
98
+ apiUrl: String
99
+
100
+ },
101
+
102
+ methods: {
103
+
104
+ calculate(){
105
+ if(!this.cart) return
106
+
107
+ let subtotal = 0;
108
+ for(let item of this.cart.items){
109
+ if(!item.checked) continue
110
+
111
+ subtotal += item.qty * item.price
112
+ }
113
+
114
+ this.cart.total = subtotal
115
+
116
+ this.update()
117
+ },
118
+
119
+ checkout(){
120
+ this.$refs.checkoutBtn.setState(2)
121
+ axios.post(import.meta.env.VITE_API_HOST + this.apiUrl, {
122
+ action: 'checkout',
123
+ cart: this.cart
124
+ })
125
+ .then(res => {
126
+ if(res.data.target)
127
+ this.$router.push(res.data.target)
128
+ })
129
+ .catch(err => this.alert(err))
130
+ .finally(_ => this.$refs.checkoutBtn.resetState())
131
+ },
132
+
133
+ load(){
134
+ this.readyState = 2
135
+ axios.get(import.meta.env.VITE_API_HOST + this.apiUrl)
136
+ .then(res => {
137
+ this.cart = res.data
138
+ this.readyState = 1
139
+ })
140
+ .catch(err => {
141
+ this.readyState = -1
142
+ this.error = err
143
+ })
144
+ },
145
+
146
+ remove(item){
147
+ this.cart.items.splice(this.cart.items.indexOf(item), 1)
148
+ },
149
+
150
+ update: invokeAfterIdle(function(){
151
+ axios.post(import.meta.env.VITE_API_HOST + this.apiUrl, {
152
+ action: 'update',
153
+ cart: this.cart
154
+ })
155
+ }, 1000),
156
+
157
+ },
158
+
159
+ data(){
160
+ return {
161
+ cart: null,
162
+ readyState: 1,
163
+ error: null,
164
+ }
165
+ },
166
+
167
+ mounted() {
168
+ this.load()
169
+ },
170
+
171
+ watch: {
172
+
173
+ cart: {
174
+ deep: true,
175
+ handler(){
176
+ this.calculate()
177
+ }
178
+ }
179
+
180
+ }
181
+
182
+ }
183
+
184
+ </script>
185
+
186
+ <style module>
187
+
188
+ .el{
189
+ @apply flex flex-col;
190
+ }
191
+
192
+ </style>
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <button type="button"
3
+ :class="$style.comp"
4
+ @click="$refs.contextMenu.open($el)">
5
+
6
+ <div :class="$style.badge">3</div>
7
+
8
+ <slot name="icon">
9
+ <component v-if="items?.length > 0"
10
+ v-for="item in items"
11
+ :is="item.type"
12
+ :key="item.key"
13
+ :="item" />
14
+
15
+ <svg v-else
16
+ width="16"
17
+ height="16px"
18
+ class="fill-text"
19
+ xmlns="http://www.w3.org/2000/svg"
20
+ viewBox="0 0 448 512"><!--! Font Awesome Pro 6.0.0-alpha3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M416 160h-72V120C344 53.83 290.2 0 224 0S104 53.83 104 120V160H32C14.33 160 0 174.3 0 192v240C0 476.2 35.82 512 80 512h288c44.18 0 80-35.82 80-80V192C448 174.3 433.7 160 416 160zM152 120C152 80.3 184.3 48 224 48s72 32.3 72 72V160h-144V120zM400 432c0 17.64-14.36 32-32 32h-288c-17.64 0-32-14.36-32-32v-224h56v56C104 277.3 114.8 288 128 288s24-10.75 24-24V208h144v56C296 277.3 306.8 288 320 288s24-10.75 24-24V208h56V432z"/></svg>
21
+ </slot>
22
+
23
+ <ContextMenu ref="contextMenu"
24
+ position="bottom-right"
25
+ class="border-transparent">
26
+ <div class="flex flex-col min-w-[300px] divide-y divide-border-50">
27
+
28
+ <div class="" @click.stop>
29
+ <div class="p-5 py-3 flex flex-row items-center">
30
+ <div class="flex-1">
31
+ <h5>Shopping Cart</h5>
32
+ </div>
33
+ </div>
34
+
35
+ <div>
36
+ <div class="min-h-[160px] flex items-center justify-center">
37
+ <label class="text-text-400">
38
+ No item in cart
39
+ </label>
40
+ </div>
41
+ </div>
42
+ </div>
43
+
44
+ <div class="text-center p-3">
45
+ <router-link to="/cart" type="button" class="text-primary">Open Cart</router-link>
46
+ </div>
47
+
48
+ </div>
49
+ </ContextMenu>
50
+
51
+ </button>
52
+ </template>
53
+
54
+ <script>
55
+
56
+ import {componentMixin} from "../mixin/component";
57
+
58
+ export default {
59
+
60
+ mixins: [ componentMixin ],
61
+
62
+ props: {
63
+
64
+ items: Array
65
+
66
+ },
67
+
68
+ methods: {
69
+
70
+
71
+ }
72
+
73
+ }
74
+
75
+ </script>
76
+
77
+ <style module>
78
+
79
+ .comp{
80
+ @apply relative;
81
+ }
82
+
83
+ .badge{
84
+ @apply rounded-full w-[16px] h-[16px] bg-text text-white pointer-events-none;
85
+ @apply absolute bottom-[-6px] right-[-12px];
86
+ @apply flex items-center justify-center text-xs;
87
+ }
88
+
89
+ </style>
@@ -7,10 +7,9 @@
7
7
 
8
8
  <script>
9
9
 
10
- import { Bar } from 'vue-chartjs';
11
- import Chart from 'chart.js/auto'
10
+ import {Bar} from 'vue-chartjs';
12
11
  import dayjs from "dayjs";
13
- import groupBy from "lodash/groupBy";
12
+ import {groupBy} from "../utils/helpers.mjs";
14
13
 
15
14
  export default{
16
15
 
@@ -1,25 +1,24 @@
1
1
  <template>
2
2
  <div :class="$style.comp">
3
3
  <input :id="id" type="checkbox" :checked="checked" @change="onChange" :disabled="isDisabled"/>
4
- <label :for="id" :class="labelClass" class="whitespace-nowrap text-ellipsis overflow-hidden">
5
- <div :class="$style.indicator" v-if="Boolean(showIcon)">
4
+ <label :for="id" :class="labelClass" class="whitespace-nowrap text-ellipsis overflow-hidden flex flex-row">
5
+ <span :class="$style.indicator" v-if="Boolean(showIcon)">
6
6
  <Transition name="checkbox">
7
7
  <div v-if="checked">
8
8
  <svg width="14" height="14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7 425.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>
9
9
  </div>
10
10
  </Transition>
11
- </div>
12
- <label v-if="!$slots.item" :class="$style.label">
11
+ </span>
12
+ <div v-if="$slots.default" class="ml-2">
13
13
  <slot name="default"></slot>
14
- </label>
15
- <slot v-else name="item"></slot>
14
+ </div>
16
15
  </label>
17
16
  </div>
18
17
  </template>
19
18
 
20
19
  <script>
21
20
 
22
- import { parseBoolean } from "../utils/helpers.mjs";
21
+ import {parseBoolean} from "../utils/helpers.mjs";
23
22
 
24
23
  export default {
25
24
 
@@ -40,7 +39,7 @@ export default {
40
39
  computed: {
41
40
 
42
41
  id(){
43
- return this.$style.comp + this.uniqid()
42
+ return this.mounted ? this.$style.comp + this.uniqid() : ''
44
43
  },
45
44
 
46
45
  isDisabled(){
@@ -78,6 +77,12 @@ export default {
78
77
 
79
78
  },
80
79
 
80
+ data(){
81
+ return {
82
+ mounted: false
83
+ }
84
+ },
85
+
81
86
  methods: {
82
87
 
83
88
  onChange(e){
@@ -118,7 +123,11 @@ export default {
118
123
  }
119
124
  }
120
125
 
121
- }
126
+ },
127
+
128
+ mounted() {
129
+ this.mounted = true
130
+ }
122
131
 
123
132
  }
124
133
 
@@ -131,7 +140,7 @@ export default {
131
140
  }
132
141
 
133
142
  .comp label{
134
- @apply flex flex-row items-start gap-2;
143
+ @apply flex flex-row items-start;
135
144
  }
136
145
 
137
146
  .comp label *{
@@ -151,7 +160,7 @@ export default {
151
160
  }
152
161
 
153
162
  .indicator{
154
- @apply w-[21px] h-[21px] rounded-lg border-[1px] border-text-200 bg-base-500;
163
+ @apply min-w-[21px] w-[21px] h-[21px] rounded-lg border-[1px] border-text-300 bg-base-300;
155
164
  @apply flex items-center justify-center overflow-hidden;
156
165
  transition: border 300ms cubic-bezier(0.25, 1, 0.5, 1);
157
166
  }