@viur/shop-components 0.0.1-dev.8 → 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.
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 -118
  56. package/src/views/ViewMissing.vue +0 -20
package/src/shop.js ADDED
@@ -0,0 +1,252 @@
1
+
2
+ import { reactive, computed, watch, shallowRef } from "vue";
3
+ import {ShopCart, ShopUserDataGuest, ShopShippingMethod, ShopPaymentProvider, ShopOrderComplete, ShopOrderConfirm} from './Steps/index'
4
+ import { defineStore } from "pinia";
5
+ import { useUrlSearchParams } from '@vueuse/core'
6
+
7
+ import { Request } from "@viur/vue-utils";
8
+
9
+ export const useViurShopStore = defineStore("viurshopStore", () => {
10
+ const state = reactive({
11
+ //shop module name
12
+ showNodes:false,
13
+ language:"de",
14
+ moduleName:'shop',
15
+ hostUrl: computed(()=>(import.meta.env.VITE_API_URL ? import.meta.env.VITE_API_URL : window.location.origin)),
16
+ shopUrl:computed(()=>{
17
+ return `${state.hostUrl}/json/${state.moduleName}`
18
+ }),
19
+ shopApiUrl:computed(()=>{
20
+ return `${state.hostUrl}/json/${state.moduleName}/api`
21
+ }),
22
+ debug:false,
23
+ //default Tabs
24
+ tabs:{
25
+ cart: {
26
+ component: shallowRef(ShopCart),
27
+ displayName: "Warenkorb",
28
+ icon: { name: "cart3" },
29
+ active:computed(()=>!state.order?.['is_checkout_in_progress'] && !state.order?.['is_ordered']), //active if no orderkey or checkout not startet
30
+ validating:false,
31
+ valid:false
32
+ },
33
+ userdata: {
34
+ component: shallowRef(ShopUserDataGuest),
35
+ displayName: "Daten Eingeben",
36
+ icon: { name: "card-list" },
37
+ active:computed(()=>!state.order?.['is_checkout_in_progress'] && !state.order?.['is_ordered'] && state.cartList.length>0), //active if checkout not startet and cart is not empty
38
+ validating:false,
39
+ valid:false
40
+ },
41
+ shippingmethod: {
42
+ component: shallowRef(ShopShippingMethod),
43
+ displayName: "Versandart",
44
+ icon: { name: "truck" },
45
+ active:computed(()=>!state.order?.['is_checkout_in_progress'] && !state.order?.['is_ordered'] && state.cartRoot?.['shipping_address']), // we need a shipping country
46
+ validating:false,
47
+ valid:false
48
+ },
49
+ paymentprovider: {
50
+ component: shallowRef(ShopPaymentProvider),
51
+ displayName: "Zahlungsart auswählen",
52
+ icon: { name: "credit-card" },
53
+ active:computed(()=>!state.order?.['is_checkout_in_progress'] && state.order && state.cartRoot?.['shipping']), // we need a active order
54
+ validating:false,
55
+ valid:false
56
+ },
57
+ confirm: {
58
+ component: shallowRef(ShopOrderConfirm),
59
+ displayName: "Bestellung prüfen",
60
+ icon: { name: "clipboard-check" },
61
+ active:computed(()=>!state.order?.['is_paid'] && state.canCheckout?.['status']), // active if canCheckout and not already paid
62
+ validating:false,
63
+ valid:false
64
+ },
65
+ complete: {
66
+ component: shallowRef(ShopOrderComplete),
67
+ displayName: "Bestellung Abgeschlossen",
68
+ icon: { name: "bag-check" },
69
+ active:computed(()=>state.order?.['is_ordered']), // active if ordered
70
+ validating:false,
71
+ valid:false
72
+ },
73
+ },
74
+ // active Tab
75
+ currentTab: "cart", // set cart as default to avoid console errors
76
+ // tabname to index mapping
77
+ tabIndexMap: computed(()=>{
78
+ let map = {}
79
+ for (const [index, item] of Object.entries(Object.keys(state.tabs))){
80
+ map[[item]] = parseInt(index)
81
+ }
82
+ return map
83
+ }),
84
+ // index to tabname mapping
85
+ indexTabMap: computed(()=>{
86
+ let map = {}
87
+ for (const [index, item] of Object.entries(Object.keys(state.tabs))){
88
+ map[[index]] = item
89
+ }
90
+ return map
91
+ }),
92
+ // length of stepper
93
+ stepperLength:computed(()=>{
94
+ return Object.keys(state.tabs).length
95
+ }),
96
+
97
+ // SHOP DATA
98
+ cartList:[], //articles in cart
99
+ cartRoot:null, //actual cart rootnode entry ( used for shipping etc)
100
+ order:null, // actual order entry ( used for billing etc.)
101
+ orderKey:null, // key of the order, maybe injected from ?order param
102
+
103
+ //CART READY
104
+ cartReady:false,
105
+ orderReady:false,
106
+ canCheckout:null,
107
+ canOrder:null,
108
+
109
+ //Address Structure
110
+ addressStructure:null,
111
+ paymentMeta:null,
112
+
113
+
114
+ //checkout
115
+ paymentProviderData:null
116
+
117
+ })
118
+
119
+ function addTab({name, component, displayname, iconname, iconlibrary,active})
120
+ {
121
+ if(Object.keys(state.tabs).includes(name)){
122
+ state.tabs[name] = {
123
+ component: component?shallowRef(component):state.tabs[name].component,
124
+ displayName: displayname?displayname:state.tabs[name].displayName,
125
+ icon: {
126
+ name: iconname?iconname:state.tabs[name].iconname,
127
+ library:iconlibrary?iconlibrary:state.tabs[name].iconlibrary
128
+ },
129
+ active:active?active:state.tabs[name].active
130
+ }
131
+ }else{
132
+ state.tabs[name] = {
133
+ component: shallowRef(component),
134
+ displayName: displayname,
135
+ icon: { name: iconname, library:iconlibrary},
136
+ active:active
137
+ }
138
+ }
139
+
140
+
141
+ }
142
+
143
+ function removeTab(name){
144
+ delete state.tabs[name]
145
+ }
146
+
147
+
148
+
149
+ function navigateToTab(name){
150
+ // navigate to Tab
151
+ state.currentTab = name
152
+
153
+ const params = useUrlSearchParams('hash')
154
+ params['step'] = state.currentTab
155
+ }
156
+
157
+ function navigateToNext(){
158
+ // shorthand for next Tab
159
+ if (state.tabIndexMap[state.currentTab] === state.stepperLength-1) return false
160
+ navigateToTab(state.indexTabMap[state.tabIndexMap[state.currentTab]+1])
161
+ }
162
+
163
+ function navigateToPrevious(){
164
+ // shorthand for previous Tab
165
+ if (state.tabIndexMap[state.currentTab] === 0) return false
166
+ navigateToTab(state.indexTabMap[state.tabIndexMap[state.currentTab]-1])
167
+ }
168
+
169
+ function fetchAddressStructure(){
170
+ Request.get(`${state.shopUrl}/address/add`).then(async (resp)=>{
171
+ let data = await resp.json()
172
+ state.addressStructure = data['structure']
173
+ })
174
+ }
175
+
176
+ function fetchPaymentMeta(){
177
+ Request.get(`${state.shopUrl}/order/payment_providers_list`).then(async (resp)=>{
178
+ let data = await resp.json()
179
+ state.paymentMeta = data
180
+ })
181
+ }
182
+
183
+ function fetchMetaData(){
184
+ fetchAddressStructure()
185
+ fetchPaymentMeta()
186
+ }
187
+
188
+ function checkout(){
189
+ //request Payment
190
+ return new Promise((resolve,reject)=>{
191
+ Request.post(`${state.shopUrl}/order/checkout_start`,{dataObj:{
192
+ order_key:state.orderKey
193
+ }}).then(async (resp)=>{
194
+ let data = await resp.json()
195
+ state.paymentProviderData = data['payment']
196
+
197
+ if (!data['payment']){
198
+ try{
199
+ await checkoutOrder() // direct Checkout
200
+ resolve()
201
+ } catch(error){
202
+ reject(error)
203
+ }
204
+ }else{
205
+ resolve()
206
+ }
207
+ }).catch(error=>{
208
+ reject(error)
209
+ })
210
+ })
211
+ }
212
+
213
+ function checkoutOrder(){
214
+ //payment is finished
215
+ return new Promise((resolve,reject)=>{
216
+ Request.post(`${state.shopUrl}/order/checkout_order`,{dataObj:{
217
+ order_key:state.orderKey
218
+ }}).then(async (resp)=>{
219
+ let data = await resp.json()
220
+ if (!resp.ok){
221
+ reject(data)
222
+ } else {
223
+ state.order = data['skel']
224
+ state.paymentProviderData = data['payment']
225
+
226
+ if(state.order?.['is_ordered']){
227
+ // order is finished
228
+ navigateToTab('complete')
229
+ }
230
+ resolve(data)
231
+ }
232
+
233
+
234
+ }).catch(error=>{
235
+ reject(error)
236
+ })
237
+ })
238
+ }
239
+
240
+ return {
241
+ state,
242
+ navigateToTab,
243
+ navigateToNext,
244
+ navigateToPrevious,
245
+ fetchMetaData,
246
+ checkout,
247
+ checkoutOrder,
248
+ addTab,
249
+ removeTab
250
+
251
+ }
252
+ })
@@ -0,0 +1,36 @@
1
+ export default {
2
+ actions:{
3
+ add: "Hinzufügen",
4
+ cancel: "Abbrechen",
5
+ delete: "Löschen"
6
+ },
7
+ shop:{
8
+ "check_order":"Bestellung prüfen",
9
+ "order_pay":"Zahlungspflichtig bestellen",
10
+ "order_number":"Ihre Bestellnummer",
11
+ "order_thanks":"Vielen Dank für Ihre Bestellung",
12
+ "order_message":"Wir haben Ihre Bestellung erhalten und werden diese schenllstmöglich bearbeiten.<br /> Sie erhalten in wenigen Minuten eine Bestätigung per E-Mail.",
13
+ "order_paid":"Ihre Zahlung ist bei uns eingegangen.",
14
+ "order_ready_to_ship":"Ihre Bestellung ist fertig für den Versand.",
15
+ "cart_empty":"Der Warenkorb ist leer.",
16
+ "order_summary":"Bestellzusammenfassung",
17
+ "deliverytime":"Lieferzeit",
18
+ "day": "Tag | Tage",
19
+ "unit_price":"Stückpreis",
20
+ "total_price":"Gesamtpreis",
21
+ "articlenumber":"Artikelnummer",
22
+ "pay":"Bezahlen",
23
+ "add_discount":"Rabattcode hinzufügen",
24
+ "use_shipping_as_billing_address": "Verwende Lieferadresse als Rechnungsadresse",
25
+ "no_valid_shipping_found":"Keine passenden Versandarten gefunden.",
26
+ "error_message":"Eine Fehler ist aufgetreten.",
27
+ "free_shipping":"Kostenlos"
28
+ },
29
+ messages:{
30
+ loading:"Daten werden abgefragt...",
31
+ updating:"Daten werden aktualisiert...",
32
+ wait_for_payment:"warte auf Zahlung...",
33
+ remove_article_from_cart:"Wollen sie den Artikel wirklich entfernen?",
34
+ order_check_later:"Sie können Ihre Bestellung am Ende noch einmal überprüfen."
35
+ }
36
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ actions:{
3
+ add: "Add"
4
+ }
5
+ }
package/src/utils.js ADDED
@@ -0,0 +1,56 @@
1
+ import {Request} from '@viur/vue-utils'
2
+
3
+ export function uuid() {
4
+ return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) =>
5
+ (
6
+ +c ^
7
+ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (+c / 4)))
8
+ ).toString(16),
9
+ );
10
+ }
11
+
12
+ export function struct2dict(structure) {
13
+ if (!Array.isArray(structure)) {
14
+ return structure;
15
+ }
16
+
17
+ let result = {};
18
+ structure.forEach((bone) => (result[bone[0]] = bone[1]));
19
+
20
+ return result;
21
+ }
22
+
23
+ export async function getTranslations(languages=["de"]){
24
+ // fetch translations from server
25
+ let retVal = languages.reduce((acc,item)=>{acc[item]={}; return acc;},{})
26
+ try {
27
+ let translations = await Request.get("/json/_translation/get_public",{dataObj:{
28
+ languages:languages
29
+ }})
30
+ const data = await translations.json()
31
+ for (let country in data) {
32
+ data[country] = Object.fromEntries(
33
+ Object.entries(data[country]).map(
34
+ ([key, value], idx) => [key, value.replaceAll('{{', '{').replaceAll('}}', '}').replace(/([@$|])/g, '{\'$1\'}')],
35
+ ),
36
+ )
37
+ }
38
+ retVal = data
39
+ }catch(error){
40
+ console.log("No Translation from server", error)
41
+ }
42
+ return retVal
43
+ }
44
+
45
+ export function getImage(image) {
46
+ if (image !== undefined) return Request.downloadUrlFor(image);
47
+
48
+ return 'PLACEHOLDER';
49
+ }
50
+
51
+ export function removeUndefinedValues(obj) {
52
+ return Object.fromEntries(
53
+ Object.entries(obj)
54
+ .filter(([key, value]) => value !== undefined && value !== null),
55
+ );
56
+ }
package/vite.config.js ADDED
@@ -0,0 +1,51 @@
1
+ import { defineConfig } from "vite";
2
+ import vue from "@vitejs/plugin-vue";
3
+
4
+ export default defineConfig({
5
+ plugins: [
6
+ vue({
7
+ template: {
8
+ compilerOptions: {
9
+ isCustomElement: (tag) => tag.startsWith("sl-"),
10
+ },
11
+ },
12
+ }),
13
+ ],
14
+ build: {
15
+ lib: {
16
+ entry: "./src/main.js", // Adjust the entry point as needed
17
+ name: "ViurShopComponents",
18
+ fileName: (format) => `viur-shop-components.${format}.js`,
19
+ },
20
+ cssCodeSplit: false,
21
+ rollupOptions: {
22
+ external: [
23
+ "vue",
24
+ "pinia",
25
+ "@viur/shoelace",
26
+ "@viur/ignite",
27
+ "@viur/vue-utils",
28
+ "vue-router",
29
+ "@ckeditor/ckeditor5-vue",
30
+ "@ckeditor/ckeditor5-build-classic",
31
+ "@viur/ckeditor5-build-classic",
32
+ ],
33
+ output: {
34
+ assetFileNames: (assetInfo) => {
35
+ return assetInfo.name;
36
+ },
37
+ globals: {
38
+ vue: "Vue",
39
+ pinia: "Pinia",
40
+ "@viur/shoelace": "ViurShoelace",
41
+ "@viur/ignite": "ViurIgnite",
42
+ "vue-router": "VueRouter",
43
+ "@ckeditor/ckeditor5-vue": "CKEditor5Vue",
44
+ "@ckeditor/ckeditor5-build-classic": "ClassicEditor",
45
+ "@viur/ckeditor5-build-classic": "ViurCkeditor",
46
+ "@viur/vue-utils": "ViurVueUtils",
47
+ },
48
+ },
49
+ },
50
+ },
51
+ });