@shopbite-de/storefront 1.18.2 → 1.18.3
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/app/app.vue +1 -7
- package/app/components/Category/Listing.vue +105 -87
- package/app/components/Header/Right.vue +2 -2
- package/app/components/ImageGallery.vue +26 -13
- package/app/composables/useShopBiteConfig.ts +5 -20
- package/nuxt.config.ts +1 -1
- package/package.json +2 -2
- package/test/nuxt/useShopBiteConfig.test.ts +16 -20
package/app/app.vue
CHANGED
|
@@ -89,14 +89,8 @@ if (import.meta.client) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
const { refresh: refreshToppings } = useShopBiteConfig();
|
|
93
|
-
|
|
94
92
|
onMounted(async () => {
|
|
95
|
-
await Promise.all([
|
|
96
|
-
refreshHolidays(),
|
|
97
|
-
refreshBusinessHours(),
|
|
98
|
-
refreshToppings(),
|
|
99
|
-
]);
|
|
93
|
+
await Promise.all([refreshHolidays(), refreshBusinessHours()]);
|
|
100
94
|
refreshCart();
|
|
101
95
|
displayStoreStatus();
|
|
102
96
|
});
|
|
@@ -159,61 +159,61 @@ const moreThanOneFilterAndOption = computed<boolean>(
|
|
|
159
159
|
:items="getSortingOrders"
|
|
160
160
|
placeholder="Sortierung"
|
|
161
161
|
/>
|
|
162
|
-
<
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
icon="i-lucide-sliders-horizontal"
|
|
171
|
-
color="neutral"
|
|
172
|
-
variant="subtle"
|
|
173
|
-
/>
|
|
162
|
+
<ClientOnly v-if="moreThanOneFilterAndOption">
|
|
163
|
+
<UDrawer class="lg:hidden" title="Filter" direction="right">
|
|
164
|
+
<UButton
|
|
165
|
+
label="Filter"
|
|
166
|
+
icon="i-lucide-sliders-horizontal"
|
|
167
|
+
color="neutral"
|
|
168
|
+
variant="subtle"
|
|
169
|
+
/>
|
|
174
170
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
>
|
|
182
|
-
<UCollapsible
|
|
183
|
-
class="flex flex-col gap-2 w-48"
|
|
184
|
-
:default-open="true"
|
|
171
|
+
<template #body>
|
|
172
|
+
<div class="flex flex-col gap-4">
|
|
173
|
+
<div
|
|
174
|
+
v-for="filter in propertyFilters"
|
|
175
|
+
:key="filter.id"
|
|
176
|
+
class="flex flex-col gap-4"
|
|
185
177
|
>
|
|
186
|
-
<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
v-model="selectedPropertyFilters"
|
|
201
|
-
:items="filter.options"
|
|
202
|
-
value-key="id"
|
|
203
|
-
label-key="translated.name"
|
|
178
|
+
<UCollapsible
|
|
179
|
+
class="flex flex-col gap-2 w-48"
|
|
180
|
+
:default-open="true"
|
|
181
|
+
>
|
|
182
|
+
<UButton
|
|
183
|
+
:label="filter.translated.name"
|
|
184
|
+
color="neutral"
|
|
185
|
+
variant="subtle"
|
|
186
|
+
trailing-icon="i-lucide-chevron-down"
|
|
187
|
+
block
|
|
188
|
+
:ui="{
|
|
189
|
+
trailingIcon:
|
|
190
|
+
'group-data-[state=open]:rotate-180 transition-transform duration-200',
|
|
191
|
+
}"
|
|
204
192
|
/>
|
|
205
|
-
|
|
206
|
-
|
|
193
|
+
|
|
194
|
+
<template #content>
|
|
195
|
+
<UCheckboxGroup
|
|
196
|
+
v-model="selectedPropertyFilters"
|
|
197
|
+
:items="filter.options"
|
|
198
|
+
value-key="id"
|
|
199
|
+
label-key="translated.name"
|
|
200
|
+
/>
|
|
201
|
+
</template>
|
|
202
|
+
</UCollapsible>
|
|
203
|
+
</div>
|
|
204
|
+
<UButton
|
|
205
|
+
label="Zurücksetzen"
|
|
206
|
+
variant="outline"
|
|
207
|
+
block
|
|
208
|
+
@click="handleFilterRest"
|
|
209
|
+
/>
|
|
207
210
|
</div>
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
@click="handleFilterRest"
|
|
213
|
-
/>
|
|
214
|
-
</div>
|
|
211
|
+
</template>
|
|
212
|
+
</UDrawer>
|
|
213
|
+
<template #fallback>
|
|
214
|
+
<USkeleton class="h-8 w-20 lg:hidden" />
|
|
215
215
|
</template>
|
|
216
|
-
</
|
|
216
|
+
</ClientOnly>
|
|
217
217
|
</div>
|
|
218
218
|
|
|
219
219
|
<div
|
|
@@ -243,46 +243,64 @@ const moreThanOneFilterAndOption = computed<boolean>(
|
|
|
243
243
|
|
|
244
244
|
<template #right>
|
|
245
245
|
<UPageAside>
|
|
246
|
-
<
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
<UCollapsible
|
|
254
|
-
class="flex flex-col gap-2 w-48"
|
|
255
|
-
:default-open="true"
|
|
246
|
+
<ClientOnly v-if="moreThanOneFilterAndOption">
|
|
247
|
+
<div class="flex flex-col gap-4">
|
|
248
|
+
<h2 class="text-3xl md:text-4xl mb-3 pb-2">Filter</h2>
|
|
249
|
+
<div
|
|
250
|
+
v-for="filter in propertyFilters"
|
|
251
|
+
:key="filter.id"
|
|
252
|
+
class="flex flex-col gap-4"
|
|
256
253
|
>
|
|
257
|
-
<
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
v-model="selectedPropertyFilters"
|
|
272
|
-
:items="filter.options"
|
|
273
|
-
value-key="id"
|
|
274
|
-
label-key="translated.name"
|
|
254
|
+
<UCollapsible
|
|
255
|
+
class="flex flex-col gap-2 w-48"
|
|
256
|
+
:default-open="true"
|
|
257
|
+
>
|
|
258
|
+
<UButton
|
|
259
|
+
:label="filter.translated.name"
|
|
260
|
+
color="neutral"
|
|
261
|
+
variant="subtle"
|
|
262
|
+
trailing-icon="i-lucide-chevron-down"
|
|
263
|
+
block
|
|
264
|
+
:ui="{
|
|
265
|
+
trailingIcon:
|
|
266
|
+
'group-data-[state=open]:rotate-180 transition-transform duration-200',
|
|
267
|
+
}"
|
|
275
268
|
/>
|
|
276
|
-
|
|
277
|
-
|
|
269
|
+
|
|
270
|
+
<template #content>
|
|
271
|
+
<UCheckboxGroup
|
|
272
|
+
v-model="selectedPropertyFilters"
|
|
273
|
+
:items="filter.options"
|
|
274
|
+
value-key="id"
|
|
275
|
+
label-key="translated.name"
|
|
276
|
+
/>
|
|
277
|
+
</template>
|
|
278
|
+
</UCollapsible>
|
|
279
|
+
</div>
|
|
280
|
+
<UButton
|
|
281
|
+
label="Zurücksetzen"
|
|
282
|
+
variant="outline"
|
|
283
|
+
block
|
|
284
|
+
@click="handleFilterRest"
|
|
285
|
+
/>
|
|
278
286
|
</div>
|
|
279
|
-
<
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
287
|
+
<template #fallback>
|
|
288
|
+
<div class="flex flex-col gap-4">
|
|
289
|
+
<USkeleton class="h-9 w-20" />
|
|
290
|
+
<div class="flex flex-col gap-2">
|
|
291
|
+
<USkeleton class="h-8 w-48" />
|
|
292
|
+
<USkeleton class="h-4 w-36" />
|
|
293
|
+
<USkeleton class="h-4 w-32" />
|
|
294
|
+
<USkeleton class="h-4 w-40" />
|
|
295
|
+
</div>
|
|
296
|
+
<div class="flex flex-col gap-2">
|
|
297
|
+
<USkeleton class="h-8 w-48" />
|
|
298
|
+
<USkeleton class="h-4 w-36" />
|
|
299
|
+
<USkeleton class="h-4 w-32" />
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
</template>
|
|
303
|
+
</ClientOnly>
|
|
286
304
|
</UPageAside>
|
|
287
305
|
</template>
|
|
288
306
|
</UPage>
|
|
@@ -110,7 +110,7 @@ const dropDownMenu = computed<DropdownMenuItem[][]>(() => {
|
|
|
110
110
|
/>
|
|
111
111
|
</UChip>
|
|
112
112
|
</div>
|
|
113
|
-
<
|
|
113
|
+
<LazyUDrawer
|
|
114
114
|
v-if="isCheckoutEnabled"
|
|
115
115
|
v-model:open="cartQuickViewOpen"
|
|
116
116
|
title="Warenkorb"
|
|
@@ -140,5 +140,5 @@ const dropDownMenu = computed<DropdownMenuItem[][]>(() => {
|
|
|
140
140
|
@go-to-cart="cartQuickViewOpen = false"
|
|
141
141
|
/>
|
|
142
142
|
</template>
|
|
143
|
-
</
|
|
143
|
+
</LazyUDrawer>
|
|
144
144
|
</template>
|
|
@@ -26,19 +26,32 @@ defineProps<{
|
|
|
26
26
|
:links="links"
|
|
27
27
|
>
|
|
28
28
|
<template #body>
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
<ClientOnly>
|
|
30
|
+
<UCarousel
|
|
31
|
+
v-slot="{ item, index }"
|
|
32
|
+
arrows
|
|
33
|
+
:items="images"
|
|
34
|
+
class="w-full max-w-2xl mx-auto"
|
|
35
|
+
>
|
|
36
|
+
<img
|
|
37
|
+
:src="item.image"
|
|
38
|
+
:alt="item.alt"
|
|
39
|
+
:fetchpriority="index === 0 ? 'high' : 'auto'"
|
|
40
|
+
class="rounded-lg"
|
|
41
|
+
/>
|
|
42
|
+
</UCarousel>
|
|
43
|
+
<template #fallback>
|
|
44
|
+
<div class="w-full max-w-2xl mx-auto">
|
|
45
|
+
<img
|
|
46
|
+
v-if="images?.[0]"
|
|
47
|
+
:src="images[0].image"
|
|
48
|
+
:alt="images[0].alt"
|
|
49
|
+
fetchpriority="high"
|
|
50
|
+
class="rounded-lg"
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
</ClientOnly>
|
|
42
55
|
</template>
|
|
43
56
|
</UPageSection>
|
|
44
57
|
</template>
|
|
@@ -1,31 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Schemas } from "#shopware";
|
|
3
|
-
|
|
4
|
-
type useShopBiteConfigReturn = {
|
|
5
|
-
deliveryTime: ComputedRef<number>;
|
|
6
|
-
isCheckoutEnabled: ComputedRef<boolean>;
|
|
7
|
-
refresh(): Promise<Schemas["ShopBiteConfig"]>;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export function useShopBiteConfig(): useShopBiteConfigReturn {
|
|
1
|
+
export function useShopBiteConfig() {
|
|
11
2
|
const { apiClient } = useShopwareContext();
|
|
12
3
|
|
|
13
|
-
const
|
|
14
|
-
const _isCheckoutEnabled = useContext<boolean>("isCheckoutActive");
|
|
15
|
-
|
|
16
|
-
async function refresh(): Promise<Schemas["ShopBiteConfig"]> {
|
|
4
|
+
const { data, refresh } = useAsyncData("shopbite-config", async () => {
|
|
17
5
|
const { data } = await apiClient.invoke(
|
|
18
6
|
"shopbite.config.get get /shopbite/config",
|
|
19
7
|
);
|
|
20
|
-
_deliveryTime.value = data.deliveryTime;
|
|
21
|
-
_isCheckoutEnabled.value = data.isCheckoutEnabled;
|
|
22
|
-
|
|
23
8
|
return data;
|
|
24
|
-
}
|
|
9
|
+
});
|
|
25
10
|
|
|
26
11
|
return {
|
|
27
|
-
deliveryTime: computed(() =>
|
|
28
|
-
isCheckoutEnabled: computed(() =>
|
|
12
|
+
deliveryTime: computed(() => data.value?.deliveryTime ?? 30),
|
|
13
|
+
isCheckoutEnabled: computed(() => data.value?.isCheckoutEnabled ?? false),
|
|
29
14
|
refresh,
|
|
30
15
|
};
|
|
31
16
|
}
|
package/nuxt.config.ts
CHANGED
|
@@ -104,7 +104,6 @@ export default defineNuxtConfig({
|
|
|
104
104
|
"nuxt-vitalizer",
|
|
105
105
|
"@nuxt/eslint",
|
|
106
106
|
"@pinia/nuxt",
|
|
107
|
-
"@nuxt/hints",
|
|
108
107
|
],
|
|
109
108
|
|
|
110
109
|
content: {
|
|
@@ -202,6 +201,7 @@ export default defineNuxtConfig({
|
|
|
202
201
|
"@nuxt/scripts",
|
|
203
202
|
"@nuxt/test-utils/module",
|
|
204
203
|
"@nuxt/eslint",
|
|
204
|
+
"@nuxt/hints",
|
|
205
205
|
],
|
|
206
206
|
},
|
|
207
207
|
$production: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopbite-de/storefront",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.3",
|
|
4
4
|
"main": "nuxt.config.ts",
|
|
5
5
|
"description": "Shopware storefront for food delivery shops",
|
|
6
6
|
"keywords": [
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
"@iconify-json/lucide": "^1.2.97",
|
|
24
24
|
"@iconify-json/simple-icons": "^1.2.73",
|
|
25
25
|
"@nuxt/content": "3.12.0",
|
|
26
|
-
"@nuxt/hints": "1.0.2",
|
|
27
26
|
"@nuxt/image": "^2.0.0",
|
|
28
27
|
"@nuxt/scripts": "0.13.2",
|
|
29
28
|
"@nuxt/ui": "^4.5.1",
|
|
@@ -45,6 +44,7 @@
|
|
|
45
44
|
"uuid": "^13.0.0"
|
|
46
45
|
},
|
|
47
46
|
"devDependencies": {
|
|
47
|
+
"@nuxt/hints": "1.0.2",
|
|
48
48
|
"@iconify-json/heroicons": "^1.2.3",
|
|
49
49
|
"@iconify-json/hugeicons": "^1.2.23",
|
|
50
50
|
"@nuxt/devtools-kit": "^3.2.3",
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
2
|
import { mockNuxtImport } from "@nuxt/test-utils/runtime";
|
|
3
|
+
import { ref } from "vue";
|
|
3
4
|
import { useShopBiteConfig } from "~/composables/useShopBiteConfig";
|
|
4
5
|
|
|
5
|
-
const { mockInvoke
|
|
6
|
-
()
|
|
7
|
-
|
|
8
|
-
mockDeliveryTime: { value: 0 },
|
|
9
|
-
mockIsCheckoutEnabled: { value: false },
|
|
10
|
-
}),
|
|
11
|
-
);
|
|
6
|
+
const { mockInvoke } = vi.hoisted(() => ({
|
|
7
|
+
mockInvoke: vi.fn(),
|
|
8
|
+
}));
|
|
12
9
|
|
|
13
10
|
mockNuxtImport("useShopwareContext", () => () => ({
|
|
14
11
|
apiClient: {
|
|
@@ -16,25 +13,26 @@ mockNuxtImport("useShopwareContext", () => () => ({
|
|
|
16
13
|
},
|
|
17
14
|
}));
|
|
18
15
|
|
|
19
|
-
mockNuxtImport(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
mockNuxtImport(
|
|
17
|
+
"useAsyncData",
|
|
18
|
+
() => (_key: string, fetcher: () => Promise<unknown>) => {
|
|
19
|
+
const data = ref<unknown>(null);
|
|
20
|
+
const refresh = async () => {
|
|
21
|
+
data.value = await fetcher();
|
|
22
|
+
};
|
|
23
|
+
return { data, refresh };
|
|
24
|
+
},
|
|
25
|
+
);
|
|
24
26
|
|
|
25
27
|
describe("useShopBiteConfig", () => {
|
|
26
28
|
beforeEach(() => {
|
|
27
29
|
vi.clearAllMocks();
|
|
28
|
-
mockDeliveryTime.value = 0;
|
|
29
|
-
mockIsCheckoutEnabled.value = false;
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
it("should
|
|
33
|
-
mockDeliveryTime.value = 30;
|
|
34
|
-
mockIsCheckoutEnabled.value = true;
|
|
32
|
+
it("should return default values when no data is loaded", () => {
|
|
35
33
|
const { deliveryTime, isCheckoutEnabled } = useShopBiteConfig();
|
|
36
34
|
expect(deliveryTime.value).toBe(30);
|
|
37
|
-
expect(isCheckoutEnabled.value).toBe(
|
|
35
|
+
expect(isCheckoutEnabled.value).toBe(false);
|
|
38
36
|
});
|
|
39
37
|
|
|
40
38
|
it("should refresh values from API", async () => {
|
|
@@ -53,7 +51,5 @@ describe("useShopBiteConfig", () => {
|
|
|
53
51
|
);
|
|
54
52
|
expect(deliveryTime.value).toBe(45);
|
|
55
53
|
expect(isCheckoutEnabled.value).toBe(true);
|
|
56
|
-
expect(mockDeliveryTime.value).toBe(45);
|
|
57
|
-
expect(mockIsCheckoutEnabled.value).toBe(true);
|
|
58
54
|
});
|
|
59
55
|
});
|