@viur/shop-components 0.0.1-dev.9 → 0.1.0
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/.editorconfig +16 -0
- package/.github/workflows/npm-publish.yml +42 -0
- package/.gitmodules +3 -0
- package/LICENSE +21 -0
- package/README.md +13 -2
- package/package.json +23 -11
- package/src/Shop.vue +232 -0
- package/src/ShopOrderStepper.vue +114 -0
- package/src/ShopSummary.vue +196 -0
- package/src/Steps/ShopCart.vue +57 -0
- package/src/Steps/ShopOrderComplete.vue +32 -0
- package/src/Steps/ShopOrderConfirm.vue +300 -0
- package/src/Steps/ShopPaymentProvider.vue +56 -0
- package/src/Steps/ShopShippingMethod.vue +67 -0
- package/src/Steps/ShopUserDataGuest.vue +82 -0
- package/src/Steps/index.js +15 -0
- package/src/components/AddressForm.vue +95 -0
- package/src/components/AddressFormLayout.vue +107 -0
- package/src/components/CardSelector.vue +68 -0
- package/src/components/CartItem.vue +353 -0
- package/src/components/CartItemSmall.vue +257 -0
- package/src/components/DiscountInput.vue +91 -0
- package/src/components/LoadingHandler.vue +76 -0
- package/src/components/PaymentOption.vue +78 -0
- package/src/components/PaymentProviderUnzer.vue +201 -0
- package/src/components/PaymentSelector.vue +55 -0
- package/src/components/ShopAlert.vue +30 -0
- package/src/components/StepperTab.vue +132 -0
- package/src/components/dialogButton.vue +49 -0
- package/src/composables/address.js +95 -0
- package/src/composables/cart.js +189 -0
- package/src/composables/order.js +80 -0
- package/src/composables/payment.js +75 -0
- package/src/composables/shipping.js +40 -0
- package/src/main.js +44 -0
- package/src/shop.js +252 -0
- package/src/translations/de.js +36 -0
- package/src/translations/en.js +5 -0
- package/src/utils.js +56 -0
- package/vite.config.js +51 -0
- package/src/components/cart/CartView.vue +0 -693
- package/src/components/cart/ConfirmView.vue +0 -314
- package/src/components/order/category/CategoryList.vue +0 -83
- package/src/components/order/category/CategoryView.vue +0 -143
- package/src/components/order/information/UserInfoMulti.vue +0 -427
- package/src/components/order/information/UserInformation.vue +0 -332
- package/src/components/order/information/adress/ShippingAdress.vue +0 -143
- package/src/components/order/item/ItemCard.vue +0 -168
- package/src/components/order/item/ItemView.vue +0 -233
- package/src/components/order/process/ExampleUsage.vue +0 -100
- package/src/components/order/process/OrderComplete.vue +0 -41
- package/src/components/order/process/OrderTabHeader.vue +0 -7
- package/src/components/order/process/OrderView.vue +0 -210
- package/src/router/index.js +0 -103
- package/src/stores/cart.js +0 -119
- package/src/views/ViewMissing.vue +0 -20
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<loading-handler :isLoading="shippingState.isLoading" :hasError="shippingState.hasError" :errorMessage="shippingState.errorMessage">
|
|
3
|
+
|
|
4
|
+
<card-selector :options="shippingState.shippingData"
|
|
5
|
+
v-model:selection="state.selectedShippingMethod"
|
|
6
|
+
@change="updateShippingMethod"
|
|
7
|
+
>
|
|
8
|
+
<template v-slot="{option, index}">
|
|
9
|
+
<img slot="image">
|
|
10
|
+
|
|
11
|
+
<sl-format-number lang="de" type="currency" currency="EUR" :value=" option['dest']['shipping_cost']" v-if="option['dest']['shipping_cost']">
|
|
12
|
+
</sl-format-number>
|
|
13
|
+
<span v-else>{{ $t('shop.free_shipping') }}</span>
|
|
14
|
+
{{ option['dest']['name'] }} - {{$t('shop.deliverytime')}}: {{ option['dest']['delivery_time_range'] }} {{ $t('shop.day',option['dest']['delivery_time_max'] - option['dest']['delivery_time_min']) }}
|
|
15
|
+
</template>
|
|
16
|
+
</card-selector>
|
|
17
|
+
</loading-handler>
|
|
18
|
+
|
|
19
|
+
<slot name="template_shippingmethod">
|
|
20
|
+
</slot>
|
|
21
|
+
|
|
22
|
+
<slot
|
|
23
|
+
nextName="weiter"
|
|
24
|
+
:activefunction="()=>shopStore.state.cartRoot?.['shipping']"
|
|
25
|
+
:nextfunction="()=>nextStep"
|
|
26
|
+
>
|
|
27
|
+
</slot>
|
|
28
|
+
</template>
|
|
29
|
+
<script setup>
|
|
30
|
+
import {reactive,onBeforeMount} from "vue";
|
|
31
|
+
import CardSelector from "../components/CardSelector.vue";
|
|
32
|
+
import LoadingHandler from "../../old/components/generic/loadinghandler.vue";
|
|
33
|
+
import {useShipping} from "../composables/shipping";
|
|
34
|
+
import {useCart} from "../composables/cart";
|
|
35
|
+
import { useOrder } from "../composables/order";
|
|
36
|
+
import {useViurShopStore} from "../shop";
|
|
37
|
+
|
|
38
|
+
const shopStore = useViurShopStore();
|
|
39
|
+
const {updateCart} = useCart();
|
|
40
|
+
const {fetchOrder} = useOrder();
|
|
41
|
+
const {state: shippingState,fetchShippingData} = useShipping();
|
|
42
|
+
|
|
43
|
+
const state = reactive({
|
|
44
|
+
selectedShippingMethod: null
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
function updateShippingMethod(selection){
|
|
48
|
+
shopStore.state.cartRoot['shipping'] = null
|
|
49
|
+
if (selection){
|
|
50
|
+
updateCart({shipping_key:selection['dest']['key']})
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function nextStep(){
|
|
55
|
+
await fetchOrder(shopStore.state.orderKey)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
onBeforeMount(()=>{
|
|
59
|
+
fetchShippingData()
|
|
60
|
+
state.selectedShippingMethod = shopStore.state.cartRoot?.['shipping']
|
|
61
|
+
})
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
<style scoped>
|
|
66
|
+
|
|
67
|
+
</style>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="viur-shop-cart-address-headline">
|
|
3
|
+
{{ $t("skeleton.address.address_type.shipping") }}
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<address-form :formtype="addressState.billingIsShipping?'both':'shipping'">
|
|
7
|
+
</address-form>
|
|
8
|
+
<div>
|
|
9
|
+
<sl-switch :checked="addressState.billingIsShipping" @sl-change="switchAddresses">{{ $t('shop.use_shipping_as_billing_address') }}</sl-switch>
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div v-show="!addressState.billingIsShipping">
|
|
13
|
+
<div class="viur-shop-cart-address-headline">
|
|
14
|
+
{{ $t("skeleton.address.address_type.billing") }}
|
|
15
|
+
</div>
|
|
16
|
+
<address-form formtype="billing" ></address-form>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<slot name="template_userdata">
|
|
20
|
+
</slot>
|
|
21
|
+
|
|
22
|
+
<slot
|
|
23
|
+
nextName="weiter"
|
|
24
|
+
:activefunction="()=>true"
|
|
25
|
+
:nextfunction="nextStep"
|
|
26
|
+
:hint="$t('messages.order_check_later')"
|
|
27
|
+
>
|
|
28
|
+
</slot>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
<script setup>
|
|
34
|
+
import {reactive, onBeforeMount, watch} from 'vue'
|
|
35
|
+
import AddressForm from '../components/AddressForm.vue'
|
|
36
|
+
import {useAddress} from "../composables/address";
|
|
37
|
+
import {useViurShopStore} from "../shop";
|
|
38
|
+
const shopStore = useViurShopStore();
|
|
39
|
+
const {state:addressState,saveAddresses} = useAddress()
|
|
40
|
+
|
|
41
|
+
const state = reactive({
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
function switchAddresses(event){
|
|
45
|
+
addressState.billingIsShipping=event.target.checked
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onBeforeMount(()=>{
|
|
49
|
+
if (shopStore.state.cartRoot?.['shipping_address']?.['dest']?.['address_type']?.includes('billing') || !shopStore.state.cartRoot?.['shipping_address']?.['dest']){
|
|
50
|
+
addressState.billingIsShipping = true
|
|
51
|
+
}else{
|
|
52
|
+
addressState.billingIsShipping = false
|
|
53
|
+
}
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async function nextStep(){
|
|
58
|
+
// form is only valid if the action field ends with Success
|
|
59
|
+
try{
|
|
60
|
+
let result = await saveAddresses(addressState.billingIsShipping)
|
|
61
|
+
if (result['action'] && result['action'].endsWith('Success')){
|
|
62
|
+
return true
|
|
63
|
+
}
|
|
64
|
+
return false
|
|
65
|
+
} catch(error){
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<style scoped>
|
|
73
|
+
.viur-shop-cart-address-headline {
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-direction: row;
|
|
76
|
+
flex-wrap: nowrap;
|
|
77
|
+
align-items: center;
|
|
78
|
+
justify-content: space-between;
|
|
79
|
+
font-weight: 600;
|
|
80
|
+
margin-bottom: var(--sl-spacing-medium);
|
|
81
|
+
}
|
|
82
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import ShopCart from './ShopCart.vue'
|
|
2
|
+
import ShopUserDataGuest from './ShopUserDataGuest.vue'
|
|
3
|
+
import ShopShippingMethod from './ShopShippingMethod.vue'
|
|
4
|
+
import ShopPaymentProvider from './ShopPaymentProvider.vue'
|
|
5
|
+
import ShopOrderConfirm from './ShopOrderConfirm.vue'
|
|
6
|
+
import ShopOrderComplete from './ShopOrderComplete.vue'
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
ShopCart,
|
|
10
|
+
ShopUserDataGuest,
|
|
11
|
+
ShopShippingMethod,
|
|
12
|
+
ShopPaymentProvider,
|
|
13
|
+
ShopOrderConfirm,
|
|
14
|
+
ShopOrderComplete
|
|
15
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<LoadingHandler :is-loading="addressState[`${state.formtype}IsLoading`]"></LoadingHandler>
|
|
3
|
+
<ViForm
|
|
4
|
+
:ref="(el)=>{addressState[`${state.formtype}Form`]=el; return el}"
|
|
5
|
+
:module="`${shopStore.state.moduleName}/address`"
|
|
6
|
+
:action="state.action"
|
|
7
|
+
:skelkey="state.skelkey"
|
|
8
|
+
:values="{'address_type':state.address_type, 'customer_type':'private'}"
|
|
9
|
+
:useCategories="false"
|
|
10
|
+
:layout="AddressFormLayout"
|
|
11
|
+
@change="formChange"
|
|
12
|
+
:default-language="state.language"
|
|
13
|
+
error-style="decent"
|
|
14
|
+
>
|
|
15
|
+
</ViForm>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<script setup>
|
|
19
|
+
import {computed, reactive, watch} from 'vue'
|
|
20
|
+
import LoadingHandler from './LoadingHandler.vue';
|
|
21
|
+
import AddressFormLayout from './AddressFormLayout.vue';
|
|
22
|
+
import ViForm from "@viur/vue-utils/forms/ViForm.vue";
|
|
23
|
+
import {useViurShopStore} from "../shop";
|
|
24
|
+
import {useAddress} from "../composables/address";
|
|
25
|
+
|
|
26
|
+
const shopStore = useViurShopStore();
|
|
27
|
+
const {state:addressState} = useAddress();
|
|
28
|
+
|
|
29
|
+
const props = defineProps({
|
|
30
|
+
formtype:{
|
|
31
|
+
type:String,
|
|
32
|
+
default:"shipping" // formtype: shipping, billing, both
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
const state = reactive({
|
|
37
|
+
formtype:computed(()=>{
|
|
38
|
+
if (['shipping','both'].includes(props.formtype)){
|
|
39
|
+
return "shipping"
|
|
40
|
+
}
|
|
41
|
+
return 'billing'
|
|
42
|
+
}),
|
|
43
|
+
|
|
44
|
+
action: computed(()=>{
|
|
45
|
+
if (state.formtype ==='shipping' && shopStore.state.cartRoot?.['shipping_address']){
|
|
46
|
+
return 'edit'
|
|
47
|
+
} else if (props.formtype === 'billing' && shopStore.state.order?.['billing_address']){
|
|
48
|
+
return 'edit'
|
|
49
|
+
} else {
|
|
50
|
+
return 'add'
|
|
51
|
+
}
|
|
52
|
+
}),
|
|
53
|
+
|
|
54
|
+
skelkey:computed(()=>{
|
|
55
|
+
if (state.formtype === 'shipping' && shopStore.state.cartRoot?.['shipping_address']){
|
|
56
|
+
return shopStore.state.cartRoot['shipping_address']?.['dest']?.['key']
|
|
57
|
+
} else if (props.formtype === 'billing' && shopStore.state.order?.['billing_address']){
|
|
58
|
+
return shopStore.state.order?.['billing_address']?.['dest']?.['key']
|
|
59
|
+
}
|
|
60
|
+
return null
|
|
61
|
+
|
|
62
|
+
}),
|
|
63
|
+
address_type:computed(()=>{
|
|
64
|
+
if (props.formtype === 'both'){
|
|
65
|
+
return ["shipping",'billing']
|
|
66
|
+
}
|
|
67
|
+
return [state.formtype]
|
|
68
|
+
}),
|
|
69
|
+
language: "de"
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
function formChange(data){
|
|
73
|
+
if (data.name === "country"){
|
|
74
|
+
state.language = data.value
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
watch(()=>addressState.billingIsShipping, (newVal,oldVal)=>{
|
|
80
|
+
if(oldVal && !newVal){
|
|
81
|
+
if(shopStore.state.order?.['billing_address']){
|
|
82
|
+
shopStore.state.order['billing_address'] = null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
addressState[`billingForm`].state.skel = {'address_type':state.address_type, 'customer_type':'private'}
|
|
86
|
+
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<style scoped>
|
|
94
|
+
|
|
95
|
+
</style>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="vi-shop-cart-form-wrap" v-if="Object.keys(formState.structure).length > 0">
|
|
3
|
+
<slot
|
|
4
|
+
boneName="salutation"
|
|
5
|
+
:widget="getBoneWidget(formState.structure['salutation']['type'])"
|
|
6
|
+
label="placeholder"
|
|
7
|
+
>
|
|
8
|
+
</slot>
|
|
9
|
+
|
|
10
|
+
<slot boneName="firstname"
|
|
11
|
+
:widget="getBoneWidget(formState.structure['firstname']['type'])"
|
|
12
|
+
label="placeholder">
|
|
13
|
+
</slot>
|
|
14
|
+
|
|
15
|
+
<slot boneName="lastname"
|
|
16
|
+
:widget="getBoneWidget(formState.structure['lastname']['type'])"
|
|
17
|
+
label="placeholder">
|
|
18
|
+
</slot>
|
|
19
|
+
|
|
20
|
+
<slot boneName="street_name"
|
|
21
|
+
:widget="getBoneWidget(formState.structure['street_name']['type'])"
|
|
22
|
+
label="placeholder">
|
|
23
|
+
</slot>
|
|
24
|
+
|
|
25
|
+
<slot boneName="street_number"
|
|
26
|
+
:widget="getBoneWidget(formState.structure['street_number']['type'])"
|
|
27
|
+
label="placeholder">
|
|
28
|
+
</slot>
|
|
29
|
+
|
|
30
|
+
<slot
|
|
31
|
+
boneName="zip_code"
|
|
32
|
+
:widget="getBoneWidget(formState.structure['zip_code']['type'])"
|
|
33
|
+
placeholder="placeholder"
|
|
34
|
+
label="placeholder"
|
|
35
|
+
>
|
|
36
|
+
</slot>
|
|
37
|
+
|
|
38
|
+
<slot boneName="city"
|
|
39
|
+
:widget="getBoneWidget(formState.structure['city']['type'])"
|
|
40
|
+
label="placeholder">
|
|
41
|
+
</slot>
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
<slot boneName="country"
|
|
45
|
+
:widget="getBoneWidget(formState.structure['country']['type'])"
|
|
46
|
+
label="placeholder">
|
|
47
|
+
</slot>
|
|
48
|
+
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
51
|
+
<script setup>
|
|
52
|
+
import { inject } from "vue";
|
|
53
|
+
import { getBoneWidget } from "@viur/vue-utils/bones/edit";
|
|
54
|
+
|
|
55
|
+
const formState = inject("formState");
|
|
56
|
+
const formUpdate = inject("formUpdate");
|
|
57
|
+
</script>
|
|
58
|
+
<style scoped>
|
|
59
|
+
|
|
60
|
+
.vi-shop-cart-form-wrap{
|
|
61
|
+
display: grid;
|
|
62
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
63
|
+
gap: var(--sl-spacing-small);
|
|
64
|
+
margin-bottom: var(--sl-spacing-medium);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
:deep(.bone-wrapper){
|
|
68
|
+
margin: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
:deep(.wrapper-bone-firstname){
|
|
72
|
+
grid-column: 1 / span 2;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
:deep(.wrapper-bone-lastname){
|
|
76
|
+
grid-column: 3 / span 2;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
:deep(.wrapper-bone-street_name){
|
|
80
|
+
grid-column: 1 / span 3;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
:deep(.wrapper-bone-street_number){
|
|
84
|
+
grid-column: 4 / span 1;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
:deep(.wrapper-bone-zip_code){
|
|
88
|
+
grid-column: 1 / span 2;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
:deep(.wrapper-bone-city){
|
|
92
|
+
grid-column: 3 / span 2;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
:deep(.wrapper-bone-country){
|
|
96
|
+
grid-column: 1 / span 4;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
:deep(.wrapper-bone-is_default){
|
|
100
|
+
padding: var(--sl-spacing-x-small) 0;
|
|
101
|
+
grid-column: 1 / span 4;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
</style>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<sl-radio-group :value="state.currentSelection">
|
|
3
|
+
<template v-for="(option,i) in options">
|
|
4
|
+
<sl-card selectable @sl-change="changeSelection(i)" :selected="i===state.currentSelection" horizontal>
|
|
5
|
+
<slot :option="option" :index="i">
|
|
6
|
+
{{i}}: {{ option }}
|
|
7
|
+
</slot>
|
|
8
|
+
<sl-radio :value="i"></sl-radio>
|
|
9
|
+
</sl-card>
|
|
10
|
+
</template>
|
|
11
|
+
</sl-radio-group>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script setup>
|
|
15
|
+
import {onMounted, reactive, watch} from 'vue'
|
|
16
|
+
|
|
17
|
+
const selection = defineModel("selection")
|
|
18
|
+
|
|
19
|
+
const emits = defineEmits(['change'])
|
|
20
|
+
|
|
21
|
+
const props = defineProps({
|
|
22
|
+
options:{
|
|
23
|
+
type: Array,
|
|
24
|
+
default:[]
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const state = reactive({
|
|
29
|
+
currentSelection:null
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
function changeSelection(i){
|
|
33
|
+
if (state.currentSelection === i){
|
|
34
|
+
state.currentSelection = null
|
|
35
|
+
selection.value = null
|
|
36
|
+
}else{
|
|
37
|
+
state.currentSelection = i
|
|
38
|
+
selection.value = props.options[i]
|
|
39
|
+
}
|
|
40
|
+
emits("change", selection.value)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
onMounted(()=>{
|
|
44
|
+
if (selection){
|
|
45
|
+
state.currentSelection = props.options.findIndex((option)=>option['dest']['key']===selection.value?.['dest']?.['key'])
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<style scoped>
|
|
53
|
+
sl-card{
|
|
54
|
+
width: 100%;
|
|
55
|
+
&[selected]::part(base){
|
|
56
|
+
border:1px solid var(--sl-color-primary-500);
|
|
57
|
+
box-shadow: 0 0 0 var(--sl-focus-ring-width) var(--sl-input-focus-ring-color);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&::part(body){
|
|
61
|
+
display:flex;
|
|
62
|
+
flex-direction: row;
|
|
63
|
+
justify-content: space-between;
|
|
64
|
+
align-items: center;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
</style>
|