@mixd-id/web-scaffold 0.1.240411023 → 0.1.240411025
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 +3 -1
- package/src/components/404.vue +61 -0
- package/src/components/AccountIcon.vue +19 -0
- package/src/components/Cart.vue +192 -0
- package/src/components/CartIcon.vue +89 -0
- package/src/components/Checkout.vue +373 -0
- package/src/components/CheckoutDelivery.vue +267 -0
- package/src/components/ColorPicker.vue +5 -0
- package/src/components/ContextMenu.vue +112 -201
- package/src/components/Modal.vue +3 -12
- package/src/components/SearchModal.vue +153 -0
- package/src/components/SvgEditor.vue +1 -1
- package/src/components/TreeMenu.vue +122 -0
- package/src/configs/web-page-builder.js +8 -0
- package/src/hooks/device.js +14 -0
- package/src/index.js +19 -8
- package/src/utils/helpers.mjs +1 -2
- package/src/widgets/CartSetting.vue +46 -0
- package/src/widgets/CheckoutSetting.vue +46 -0
- package/src/widgets/ComponentSetting2.vue +1 -2
- package/src/widgets/Dashboard/BarChart.vue +2 -1
- package/src/widgets/Dashboard.vue +42 -21
- package/src/widgets/SearchModalSetting.vue +70 -0
- package/src/widgets/StyleSetting.vue +175 -51
- package/src/widgets/WebPageBuilder.vue +1 -1
- package/src/components/SearchButton.vue +0 -57
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.
|
|
4
|
+
"version": "0.1.240411025",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite serve",
|
|
7
7
|
"build": "vite build",
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"require": "./src/utils/preset-selector.cjs",
|
|
38
38
|
"import": "./src/utils/preset-selector.mjs"
|
|
39
39
|
},
|
|
40
|
+
"./hooks/*": "./src/hooks/*",
|
|
40
41
|
"./preset-selector.cjs": "./src/utils/preset-selector.cjs",
|
|
41
42
|
"./preset-selector.js": "./src/utils/preset-selector.js",
|
|
42
43
|
"./preset-selector.mjs": "./src/utils/preset-selector.mjs",
|
|
@@ -70,6 +71,7 @@
|
|
|
70
71
|
"redis": "^4.6.13",
|
|
71
72
|
"sequelize": "^6.37.6",
|
|
72
73
|
"tailwindcss": "^3.2.4",
|
|
74
|
+
"tinycolor2": "^1.6.0",
|
|
73
75
|
"vue": "^3.2.25",
|
|
74
76
|
"vue-chartjs": "^5.2.0",
|
|
75
77
|
"vue-router": "^4.0.14",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :class="$style.el">
|
|
3
|
+
|
|
4
|
+
<div>
|
|
5
|
+
<h1>{{ title }}</h1>
|
|
6
|
+
<p>{{ description }}</p>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<slot>
|
|
10
|
+
<component v-if="items?.length > 0"
|
|
11
|
+
v-for="item in items"
|
|
12
|
+
:is="item.type"
|
|
13
|
+
:key="item.key"
|
|
14
|
+
:="item" />
|
|
15
|
+
|
|
16
|
+
<router-link v-else :to="{ path:'/' }" class="text-primary">
|
|
17
|
+
Back to Home
|
|
18
|
+
</router-link>
|
|
19
|
+
</slot>
|
|
20
|
+
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script>
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
|
|
28
|
+
props: {
|
|
29
|
+
|
|
30
|
+
description: {
|
|
31
|
+
type: String,
|
|
32
|
+
default: 'The page you are looking for does not exist.'
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
title: {
|
|
36
|
+
type: String,
|
|
37
|
+
default: 'Page Not Found'
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
items: Array
|
|
41
|
+
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
methods: {
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
</script>
|
|
52
|
+
|
|
53
|
+
<style module>
|
|
54
|
+
|
|
55
|
+
.el{
|
|
56
|
+
@apply w-full max-w-[600px] mx-auto;
|
|
57
|
+
@apply flex flex-col gap-8 items-center justify-center;
|
|
58
|
+
@apply min-h-[60vh]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
</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-text-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-text-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-text-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>
|