@viur/shop-components 0.0.1-dev.9 → 0.1.1

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 (56) hide show
  1. package/.editorconfig +16 -0
  2. package/.github/workflows/npm-publish.yml +42 -0
  3. package/.gitmodules +3 -0
  4. package/LICENSE +21 -0
  5. package/README.md +13 -2
  6. package/package.json +23 -11
  7. package/src/Shop.vue +232 -0
  8. package/src/ShopOrderStepper.vue +114 -0
  9. package/src/ShopSummary.vue +196 -0
  10. package/src/Steps/ShopCart.vue +57 -0
  11. package/src/Steps/ShopOrderComplete.vue +32 -0
  12. package/src/Steps/ShopOrderConfirm.vue +300 -0
  13. package/src/Steps/ShopPaymentProvider.vue +56 -0
  14. package/src/Steps/ShopShippingMethod.vue +67 -0
  15. package/src/Steps/ShopUserDataGuest.vue +82 -0
  16. package/src/Steps/index.js +15 -0
  17. package/src/components/AddressForm.vue +95 -0
  18. package/src/components/AddressFormLayout.vue +107 -0
  19. package/src/components/CardSelector.vue +68 -0
  20. package/src/components/CartItem.vue +353 -0
  21. package/src/components/CartItemSmall.vue +257 -0
  22. package/src/components/DiscountInput.vue +91 -0
  23. package/src/components/LoadingHandler.vue +76 -0
  24. package/src/components/PaymentOption.vue +78 -0
  25. package/src/components/PaymentProviderUnzer.vue +201 -0
  26. package/src/components/PaymentSelector.vue +55 -0
  27. package/src/components/ShopAlert.vue +30 -0
  28. package/src/components/StepperTab.vue +132 -0
  29. package/src/components/dialogButton.vue +49 -0
  30. package/src/composables/address.js +95 -0
  31. package/src/composables/cart.js +189 -0
  32. package/src/composables/order.js +80 -0
  33. package/src/composables/payment.js +75 -0
  34. package/src/composables/shipping.js +40 -0
  35. package/src/main.js +44 -0
  36. package/src/shop.js +252 -0
  37. package/src/translations/de.js +36 -0
  38. package/src/translations/en.js +5 -0
  39. package/src/utils.js +56 -0
  40. package/vite.config.js +51 -0
  41. package/src/components/cart/CartView.vue +0 -693
  42. package/src/components/cart/ConfirmView.vue +0 -314
  43. package/src/components/order/category/CategoryList.vue +0 -83
  44. package/src/components/order/category/CategoryView.vue +0 -143
  45. package/src/components/order/information/UserInfoMulti.vue +0 -427
  46. package/src/components/order/information/UserInformation.vue +0 -332
  47. package/src/components/order/information/adress/ShippingAdress.vue +0 -143
  48. package/src/components/order/item/ItemCard.vue +0 -168
  49. package/src/components/order/item/ItemView.vue +0 -233
  50. package/src/components/order/process/ExampleUsage.vue +0 -100
  51. package/src/components/order/process/OrderComplete.vue +0 -41
  52. package/src/components/order/process/OrderTabHeader.vue +0 -7
  53. package/src/components/order/process/OrderView.vue +0 -210
  54. package/src/router/index.js +0 -103
  55. package/src/stores/cart.js +0 -119
  56. package/src/views/ViewMissing.vue +0 -20
@@ -0,0 +1,76 @@
1
+ <template>
2
+ <template v-if="isLoading">
3
+ <slot name="loading">
4
+ <shop-alert duration="Infinity" variant="info" :closeable="false">
5
+ <template #icon>
6
+ <sl-spinner class="spinner-loading" slot="icon"></sl-spinner>
7
+ </template>
8
+ <template #alertMsg>
9
+ {{ $t('messages.loading') }}
10
+ </template>
11
+ </shop-alert>
12
+ </slot>
13
+ </template>
14
+ <template v-else-if="isUpdating">
15
+ <slot name="updating">
16
+ <shop-alert duration="Infinity" variant="info" :closeable="false">
17
+ <template #icon>
18
+ <sl-spinner class="spinner-loading" slot="icon"></sl-spinner>
19
+ </template>
20
+ <template #alertMsg>
21
+ {{ $t('messages.updating') }}
22
+ </template>
23
+ </shop-alert>
24
+ </slot>
25
+ </template>
26
+ <template v-else-if="hasError">
27
+ <slot name="error">
28
+ <shop-alert duration="Infinity" iconName="x-lg" variant="danger" :closeable="false">
29
+ <template #alertMsg>
30
+ {{ errorMessage }}
31
+ </template>
32
+ </shop-alert>
33
+ </slot>
34
+ </template>
35
+ <template v-else>
36
+ <slot>
37
+ </slot>
38
+ </template>
39
+
40
+
41
+
42
+ </template>
43
+
44
+ <script setup>
45
+ import ShopAlert from './ShopAlert.vue'
46
+
47
+ const props = defineProps({
48
+ isLoading:{
49
+ type:Boolean,
50
+ default:true
51
+ },
52
+ isUpdating:{
53
+ type:Boolean,
54
+ default:false
55
+ },
56
+ hasError:{
57
+ type:Boolean,
58
+ default:false
59
+ },
60
+ errorMessage:{
61
+ type:String,
62
+ default:"Ein Fehler ist aufgetreten."
63
+ }
64
+
65
+ })
66
+ </script>
67
+
68
+ <style scoped>
69
+
70
+ .spinner-loading{
71
+ --indicator-color:var(--sl-color-info-500);
72
+ }
73
+ .spinner-error{
74
+ --indicator-color:var(--sl-color-danger-500);
75
+ }
76
+ </style>
@@ -0,0 +1,78 @@
1
+ <template>
2
+ <sl-details :class="{
3
+ simple:widget==='simple'
4
+ }"
5
+ @sl-show="selection" >
6
+ <div slot="summary" class="payment-selection">
7
+ <div class="start">
8
+ <sl-icon :name="icon"></sl-icon>
9
+ {{ name }}
10
+ </div>
11
+ <sl-radio :value="paymenttype" ></sl-radio>
12
+ </div>
13
+ <slot>
14
+ {{description}}
15
+ </slot>
16
+
17
+ <template v-if="widget==='simple'">
18
+ <div slot="expand-icon" style="width:1em"></div>
19
+ <div slot="collapse-icon" style="width:1em"></div>
20
+ </template>
21
+ </sl-details>
22
+ </template>
23
+
24
+ <script setup>
25
+ const emits = defineEmits(['change'])
26
+ const props = defineProps({
27
+ paymenttype: {
28
+ type: String,
29
+ default: 'prepayment'
30
+ },
31
+ name: {
32
+ type: String,
33
+ default: 'Paymentname'
34
+ },
35
+ icon: {
36
+ type: String,
37
+ default: 'currency-euro'
38
+ },
39
+ description: {
40
+ type: String,
41
+ default: 'Payment description'
42
+ },
43
+ widget:{
44
+ type:String,
45
+ default:'default' //simple
46
+ }
47
+ })
48
+
49
+ function selection(){
50
+ emits('change',props.paymenttype)
51
+ }
52
+
53
+
54
+
55
+ </script>
56
+
57
+ <style scoped>
58
+ .payment-selection{
59
+ display: flex;
60
+ align-items: center;
61
+ flex-wrap: nowrap;
62
+ flex-direction: row;
63
+ align-content: center;
64
+ justify-content: space-between;
65
+ width: 100%;
66
+
67
+ .start{
68
+ display: flex;
69
+ align-items: center;
70
+ flex-wrap: nowrap;
71
+ gap:10px;
72
+ }
73
+ }
74
+
75
+ .simple::part(content){
76
+ display:none;
77
+ }
78
+ </style>
@@ -0,0 +1,201 @@
1
+ <template>
2
+ <div class="loading-wrapper" v-if="state.loading">
3
+ <sl-spinner class="loading"></sl-spinner>
4
+ </div>
5
+
6
+ <div class="loading-wrapper" v-if="PaymentCheckIsActive">
7
+ <sl-spinner class="loading"></sl-spinner>
8
+ {{ $t('messages.wait_for_payment') }}
9
+ </div>
10
+ <div class="form-wrapper">
11
+ <sl-alert :open="state.hasError" variant="danger">{{state.errorMessage}}</sl-alert>
12
+ <form class="unzerUI form" novalidate>
13
+ <template v-if="shopStore.state.order?.['payment_provider'] === 'unzer-card'">
14
+ <div class="field">
15
+ <div id="card-element-id-number" class="unzerInput">
16
+ <!-- Card number UI Element is inserted here. -->
17
+ </div>
18
+ </div>
19
+ <div class="two fields">
20
+ <div class="field ten wide">
21
+ <div id="card-element-id-expiry" class="unzerInput">
22
+ <!-- Card expiry date UI Element is inserted here. -->
23
+ </div>
24
+ </div>
25
+ <div class="field six wide">
26
+ <div id="card-element-id-cvc" class="unzerInput">
27
+ <!-- Card CVC UI Element is inserted here. -->
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <template v-else-if="shopStore.state.order?.['payment_provider'] === 'unzer-paypal'">
34
+ <div id="paypal-element" class="field"></div>
35
+ </template>
36
+
37
+ <template v-else-if="shopStore.state.order?.['payment_provider'] === 'unzer-ideal'">
38
+ <div id="ideal-element" class="field"></div>
39
+ </template>
40
+ </form>
41
+ <button :disabled="state.loading" class="unzerUI primary button fluid" @click="submitFormToUnzer">{{ $t('shop.pay') }}</button>
42
+ <sl-button :disabled="state.loading" variant="danger" @click="cancelPayment">{{ $t('actions.cancel') }}</sl-button>
43
+ </div>
44
+ </template>
45
+
46
+ <script setup>
47
+ import {computed, onBeforeMount, onMounted, reactive} from 'vue'
48
+ import { useViurShopStore } from '../shop';
49
+ import { useOrder } from '../composables/order';
50
+ import {Request} from '@viur/vue-utils'
51
+ import { useIntervalFn } from '@vueuse/core'
52
+ const shopStore = useViurShopStore()
53
+ const {fetchOrder} = useOrder()
54
+
55
+ const emits = defineEmits(['cancel'])
56
+
57
+ const { pause:PaymentCheckPause, resume:PaymentCheckResume, isActive:PaymentCheckIsActive } = useIntervalFn(() => {
58
+ fetchOrder(shopStore.state.orderKey)
59
+
60
+ if (shopStore.state.order?.['is_paid']){
61
+ PaymentCheckPause()
62
+ }
63
+
64
+ }, 2000,{immediate:false})
65
+
66
+
67
+ const state = reactive({
68
+ unzer:computed(()=>{
69
+ if (!shopStore.state.paymentProviderData) return null
70
+ return new unzer(shopStore.state.paymentProviderData["public_key"], {locale: 'de-DE'})
71
+ }),
72
+ paymentHandler:{},
73
+ loading:false,
74
+ hasError:false,
75
+ errorMessage:"Bei der Zahlung ist ein Fehler aufgetreten.",
76
+ waitPayment:false
77
+ })
78
+
79
+
80
+ function initUnzerForm(){
81
+ //Unzer field definition
82
+ if (shopStore.state.order?.['payment_provider'] === 'unzer-card') {
83
+ const card = state.unzer.Card();
84
+ // Rendering input field card number
85
+ card.create('number', {
86
+ containerId: 'card-element-id-number',
87
+ onlyIframe: false,
88
+ });
89
+ // Rendering input field card expiry
90
+ card.create('expiry', {
91
+ containerId: 'card-element-id-expiry',
92
+ onlyIframe: false,
93
+ });
94
+ // Rendering input field card cvc
95
+ card.create('cvc', {
96
+ containerId: 'card-element-id-cvc',
97
+ onlyIframe: false,
98
+ });
99
+
100
+ state.paymentHandler['unzer-card'] = card
101
+ } else if (shopStore.state.order?.['payment_provider'] === 'unzer-paypal') {
102
+ // Creating a PayPal instance
103
+ const paypal = state.unzer.Paypal()
104
+ // Rendering input field
105
+ //paypal.create('email', {
106
+ // containerId: 'paypal-element',
107
+ //});
108
+ state.paymentHandler['unzer-paypal']= paypal;
109
+ } else if (shopStore.state.order?.['payment_provider'] === 'unzer-sofort') {
110
+ const sofort = state.unzer.Sofort()
111
+ state.paymentHandler['unzer-sofort'] = sofort;
112
+ } else if (shopStore.state.order?.['payment_provider'] === 'unzer-ideal') {
113
+ const ideal = state.unzer.Ideal()
114
+ ideal.create('ideal', {
115
+ containerId: 'ideal-element',
116
+ });
117
+ state.paymentHandler['unzer-ideal'] = ideal;
118
+ }
119
+ state.loading = false
120
+ }
121
+
122
+ function paymentError(error){
123
+ state.loading = false
124
+ state.hasError = true
125
+ //reset session id
126
+ //state.paymentHandler[shopStore.state.order?.['payment_provider']].jsessionId = state.paymentHandler[shopStore.state.order?.['payment_provider']].requestJSessionId()
127
+ }
128
+
129
+
130
+ function submitFormToUnzer(){
131
+ PaymentCheckPause()
132
+ state.loading = true
133
+ let paymenttarget = shopStore.state.order?.['payment_provider'].split("-")[1]
134
+ console.log(state.paymentHandler)
135
+ //send to unzer
136
+ state.paymentHandler[shopStore.state.order?.['payment_provider']].createResource().then((result)=>{
137
+ Request.post(`${shopStore.state.shopUrl}/pp_unzer_${paymenttarget}/save_type`, {dataObj:{
138
+ order_key: shopStore.state.orderKey,
139
+ type_id: result.id,
140
+ }}).then(async (resp)=>{
141
+ let data = await resp.json()
142
+ shopStore.state.order = data
143
+
144
+ shopStore.checkoutOrder().then((resp)=>{
145
+ state.loading = false
146
+ state.hasError = false
147
+ if(shopStore.state.paymentProviderData?.redirectUrl){
148
+ state.waitPayment = true
149
+ window.open(shopStore.state.paymentProviderData.redirectUrl,"_blank","popup")
150
+ PaymentCheckResume()
151
+ }
152
+ }).catch(async (error)=>{
153
+ paymentError(error)
154
+ })
155
+
156
+ }).catch(error => {
157
+ paymentError(error)
158
+ })
159
+ }).catch((error)=> {
160
+ paymentError(error)
161
+ })
162
+ }
163
+
164
+
165
+ function cancelPayment(){
166
+ PaymentCheckPause()
167
+ emits('cancel')
168
+ }
169
+
170
+ onBeforeMount(()=>{
171
+ state.loading = true
172
+ if (!shopStore.state.paymentProviderData){
173
+ shopStore.checkout().then(()=>{
174
+ initUnzerForm()
175
+ }).catch((error)=>{
176
+ console.log(error)
177
+ })
178
+ }else{
179
+ initUnzerForm()
180
+ }
181
+ })
182
+
183
+ </script>
184
+
185
+ <style scoped>
186
+ .loading-wrapper{
187
+ display:flex;
188
+ flex-direction: column;
189
+ align-items: center;
190
+ }
191
+
192
+ .form-wrapper{
193
+ display:flex;
194
+ flex-direction: column;
195
+ gap:20px;
196
+ }
197
+
198
+ .loading{
199
+ font-size:3rem;
200
+ }
201
+ </style>
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <sl-radio-group :value="state.selection">
3
+ <sl-details-group>
4
+
5
+ <payment-option v-for="option in options" :key="option.id"
6
+ :paymenttype="option.paymenttype"
7
+ :name="option.name"
8
+ :icon="option.icon"
9
+ :widget="option.widget"
10
+ :description="option.description"
11
+ @change="optionChanged"
12
+
13
+ >
14
+
15
+ </payment-option>
16
+
17
+ </sl-details-group>
18
+ </sl-radio-group>
19
+
20
+ </template>
21
+
22
+ <script setup>
23
+ import {usePaymentStore} from "../../../stores/payment"
24
+ import PaymentOption from "./PaymentOption.vue"
25
+ import {computed, reactive, ref} from 'vue'
26
+ import {Request} from "@viur/vue-utils";
27
+
28
+ const paymentStore = usePaymentStore()
29
+ const props = defineProps({
30
+ selection: {
31
+ type: String,
32
+ default: null
33
+ },
34
+ options: {
35
+ type: Array,
36
+ default: () => []
37
+ }
38
+ })
39
+
40
+ const state = reactive({
41
+ selection:null,
42
+ hasError:false
43
+ })
44
+
45
+ function optionChanged(type){
46
+ state.selection=type
47
+ paymentStore.state.paymentSelection = state.selection
48
+ }
49
+
50
+
51
+ </script>
52
+
53
+ <style scoped>
54
+
55
+ </style>
@@ -0,0 +1,30 @@
1
+ <template>
2
+ <sl-alert :variant="variant" open :closable="closeable" :duration="duration" @sl-hide="onHide">
3
+ <slot name="icon">
4
+ <sl-icon slot="icon" :name="iconName"></sl-icon>
5
+ </slot>
6
+ <slot name="alertMsg">{{ msg }}</slot>
7
+ </sl-alert>
8
+ </template>
9
+
10
+ <script setup>
11
+ const props = defineProps({
12
+ variant: { type: String, default: "primary" },
13
+ iconName: { type: String, default: "info-circle" },
14
+ closeable:{
15
+ type:Boolean,
16
+ default:true
17
+ },
18
+ duration:{
19
+ type: [Number, String],
20
+ default: 3000 // use "Infinity" for fix message
21
+ },
22
+ msg: { type: String, default: "ALERT!" },
23
+ });
24
+
25
+ const emit = defineEmits(["onHide"]);
26
+
27
+ function onHide() {
28
+ emit("onHide");
29
+ }
30
+ </script>
@@ -0,0 +1,132 @@
1
+ <template>
2
+ <sl-tab
3
+ class="viur-shop-order-tab"
4
+ slot="nav"
5
+ :panel="tab"
6
+ :disabled="!shopStore.state.tabs[tab]['active']"
7
+ @click="shopStore.navigateToTab(tab)"
8
+ >
9
+ <div class="viur-shop-order-step">
10
+ <sl-icon
11
+ class="viur-shop-order-step-icon"
12
+ v-if="shopStore.state.tabs[tab]?.icon?.name"
13
+ :name="shopStore.state.tabs[tab].icon.name"
14
+ :library="shopStore.state.tabs[tab].icon.library ? shopStore.state.tabs[tab].icon.library : 'default'"
15
+ >
16
+ </sl-icon>
17
+
18
+ <div class="viur-shop-order-status-text">
19
+ <template v-if="tab && shopStore.state.tabs[tab]">
20
+ {{ shopStore.state.tabIndexMap[tab] + 1 }}.
21
+ <span class="viur-shop-order-status-span">
22
+ {{ shopStore.state.tabs[tab]?.displayName }}
23
+ </span>
24
+ </template>
25
+ <template v-else>
26
+ {{ tab }}
27
+ </template>
28
+ </div>
29
+ </div>
30
+
31
+ <sl-icon
32
+ name="chevron-right"
33
+ class="viur-shop-order-tab-check"
34
+ v-if="shopStore.state.tabIndexMap[tab] < shopStore.state.stepperLength - 1"
35
+ >
36
+ </sl-icon>
37
+ </sl-tab>
38
+ </template>
39
+
40
+ <script setup>
41
+ import { watch, onBeforeMount, inject } from "vue";
42
+ import {useViurShopStore} from '../shop'
43
+ const shopStore = useViurShopStore()
44
+
45
+ const props = defineProps({
46
+ tab: {
47
+ type: String,
48
+ required: true,
49
+ default:"-"
50
+ }
51
+ });
52
+ </script>
53
+
54
+ <style scoped>
55
+ .viur-shop-order-tab {
56
+ width: 25%;
57
+ position: relative;
58
+
59
+ &::part(base) {
60
+ color: var(--shop-tab-color);
61
+ display: flex;
62
+ height: 100%;
63
+ }
64
+
65
+ &[aria-selected="true"] {
66
+ --shop-tab-color: var(--shop-tab-color--active);
67
+ }
68
+
69
+ @media (max-width: 900px) {
70
+ &::part(base) {
71
+ height: 100%;
72
+ padding: var(--sl-spacing-small) var(--sl-spacing-medium);
73
+ }
74
+ }
75
+
76
+ @media (max-width: 600px) {
77
+ &[aria-selected="true"] {
78
+ width: 100%;
79
+ }
80
+
81
+ &:not([aria-selected="true"]) {
82
+ .viur-shop-order-status-span {
83
+ display: none;
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ .viur-shop-order-step {
90
+ width: 100%;
91
+ height: 100%;
92
+ display: flex;
93
+ flex-direction: column;
94
+ justify-content: center;
95
+ align-items: center;
96
+
97
+ &:has(sl-icon) {
98
+ justify-content: flex-start;
99
+ }
100
+
101
+ sl-icon {
102
+ font-size: 2.5em;
103
+ margin-bottom: 15px;
104
+ }
105
+
106
+ @media (max-width: 900px) {
107
+ justify-content: center;
108
+
109
+ sl-icon {
110
+ display: none;
111
+ }
112
+ }
113
+ }
114
+
115
+ .viur-shop-order-status-text {
116
+ font-size: 1em;
117
+ color: inherit;
118
+ text-align: center;
119
+ white-space: initial;
120
+ }
121
+
122
+ .viur-shop-order-tab-check {
123
+ position: absolute;
124
+ right: -0.5em;
125
+
126
+ @media (max-width: 900px) {
127
+ font-size: 0.7em;
128
+ right: -0.35em;
129
+ top: calc(50% - 0.35em);
130
+ }
131
+ }
132
+ </style>
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <sl-button v-bind="$attrs" @click="state.opened=true">
3
+ <slot>
4
+
5
+ </slot>
6
+ </sl-button>
7
+ <teleport to="#dialogs" v-if="state.opened">
8
+ <sl-dialog :open="state.opened" @sl-after-hide="state.opened=false" noHeader>
9
+ <slot name="dialog" :close="close">
10
+
11
+ </slot>
12
+ </sl-dialog>
13
+ </teleport>
14
+ </template>
15
+
16
+ <script setup>
17
+ import {reactive} from 'vue'
18
+ defineOptions({
19
+ inheritAttrs: false
20
+ })
21
+
22
+ const state = reactive({
23
+ opened:false
24
+ })
25
+
26
+ function close(){
27
+ state.opened=false
28
+ }
29
+
30
+ </script>
31
+
32
+ <style scoped>
33
+
34
+ .decent{
35
+ margin: 0;
36
+ transition: all ease .3s;
37
+
38
+ &::part(base){
39
+ border: none;
40
+ }
41
+
42
+ &::part(label){
43
+ padding: 0;
44
+ height: var(--sl-input-height-medium);
45
+ width: calc(var(--sl-input-height-medium) / 5 * 4);
46
+ }
47
+ }
48
+
49
+ </style>