@shopbite-de/storefront 1.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/.dockerignore +28 -0
- package/.env.example +11 -0
- package/.github/workflows/build.yaml +48 -0
- package/.github/workflows/ci.yaml +102 -0
- package/.prettierignore +6 -0
- package/.prettierrc +1 -0
- package/api-types/storeApiSchema.json +13863 -0
- package/api-types/storeApiTypes.d.ts +7010 -0
- package/app/app.config.ts +18 -0
- package/app/app.vue +99 -0
- package/app/assets/css/main.css +60 -0
- package/app/assets/fonts/Courier_Prime/CourierPrime-Bold.ttf +0 -0
- package/app/assets/fonts/Courier_Prime/CourierPrime-BoldItalic.ttf +0 -0
- package/app/assets/fonts/Courier_Prime/CourierPrime-Italic.ttf +0 -0
- package/app/assets/fonts/Courier_Prime/CourierPrime-Regular.ttf +0 -0
- package/app/assets/fonts/Courier_Prime/OFL.txt +93 -0
- package/app/assets/fonts/Kalam/Kalam-Bold.ttf +0 -0
- package/app/assets/fonts/Kalam/Kalam-Light.ttf +0 -0
- package/app/assets/fonts/Kalam/Kalam-Regular.ttf +0 -0
- package/app/assets/fonts/Kalam/OFL.txt +93 -0
- package/app/assets/fonts/Marcellus/Marcellus-Regular.ttf +0 -0
- package/app/assets/fonts/Marcellus/OFL.txt +93 -0
- package/app/assets/fonts/Sora/OFL.txt +93 -0
- package/app/assets/fonts/Sora/README.txt +70 -0
- package/app/assets/fonts/Sora/Sora-VariableFont_wght.ttf +0 -0
- package/app/assets/fonts/Sora/static/Sora-Bold.ttf +0 -0
- package/app/assets/fonts/Sora/static/Sora-ExtraBold.ttf +0 -0
- package/app/assets/fonts/Sora/static/Sora-ExtraLight.ttf +0 -0
- package/app/assets/fonts/Sora/static/Sora-Light.ttf +0 -0
- package/app/assets/fonts/Sora/static/Sora-Medium.ttf +0 -0
- package/app/assets/fonts/Sora/static/Sora-Regular.ttf +0 -0
- package/app/assets/fonts/Sora/static/Sora-SemiBold.ttf +0 -0
- package/app/assets/fonts/Sora/static/Sora-Thin.ttf +0 -0
- package/app/components/AddToWishlist.vue +55 -0
- package/app/components/Address/Card.vue +32 -0
- package/app/components/Address/Detail.vue +22 -0
- package/app/components/Address/Form.vue +117 -0
- package/app/components/AnimatedSection.vue +77 -0
- package/app/components/BottomNavi.vue +63 -0
- package/app/components/Cart/Item.vue +112 -0
- package/app/components/Cart/QuickView.vue +55 -0
- package/app/components/Category/Header.vue +53 -0
- package/app/components/Category/Listing.vue +295 -0
- package/app/components/Checkout/DeliveryTimeSelect.vue +177 -0
- package/app/components/Checkout/LoginOrRegister.vue +43 -0
- package/app/components/Checkout/PaymentAndDelivery.vue +101 -0
- package/app/components/Checkout/PaymentMethod.vue +30 -0
- package/app/components/Checkout/ShippingMethod.vue +30 -0
- package/app/components/Checkout/Summary.vue +125 -0
- package/app/components/Cta.vue +34 -0
- package/app/components/Features.vue +36 -0
- package/app/components/Food/Marquee.vue +35 -0
- package/app/components/Food/MarqueeItem.vue +72 -0
- package/app/components/Footer.vue +51 -0
- package/app/components/Header.vue +160 -0
- package/app/components/Hero.vue +77 -0
- package/app/components/ImageGallery.vue +46 -0
- package/app/components/Loading.vue +29 -0
- package/app/components/Navigation/DesktopLeft.vue +51 -0
- package/app/components/Navigation/DesktopLeft2.vue +43 -0
- package/app/components/Navigation/MobileTop.vue +59 -0
- package/app/components/Navigation/MobileTop2.vue +42 -0
- package/app/components/Order/Detail.vue +84 -0
- package/app/components/Product/Card.vue +132 -0
- package/app/components/Product/Category.vue +153 -0
- package/app/components/Product/Configurator.vue +65 -0
- package/app/components/Product/CrossSelling.vue +95 -0
- package/app/components/Product/DeselectIngredient.vue +46 -0
- package/app/components/Product/Detail.vue +187 -0
- package/app/components/Product/SearchBar.vue +109 -0
- package/app/components/PublicAnnouncement.vue +17 -0
- package/app/components/Topseller.vue +43 -0
- package/app/components/User/Detail.vue +47 -0
- package/app/components/User/LoginForm.vue +105 -0
- package/app/components/User/RegistrationForm.vue +340 -0
- package/app/components/Wishlist.vue +102 -0
- package/app/composables/useDeliveryTime.ts +139 -0
- package/app/composables/useInterval.ts +15 -0
- package/app/composables/usePizzaToppings.ts +31 -0
- package/app/composables/useProductEvents.test.ts +111 -0
- package/app/composables/useProductEvents.ts +22 -0
- package/app/composables/useProductVariants.ts +61 -0
- package/app/composables/useScrollAnimation.ts +39 -0
- package/app/composables/useTopSellers.ts +34 -0
- package/app/error.vue +30 -0
- package/app/layouts/account.vue +74 -0
- package/app/layouts/default.vue +6 -0
- package/app/layouts/listing.vue +32 -0
- package/app/layouts/listing2.vue +8 -0
- package/app/middleware/trailing-slash.global.ts +19 -0
- package/app/pages/account/recover/password/index.vue +143 -0
- package/app/pages/anmelden.vue +32 -0
- package/app/pages/bestellung.vue +103 -0
- package/app/pages/c/[...all].vue +49 -0
- package/app/pages/index.vue +59 -0
- package/app/pages/konto/adressen.vue +135 -0
- package/app/pages/konto/bestellung/[id].vue +41 -0
- package/app/pages/konto/bestellungen.vue +53 -0
- package/app/pages/konto/index.vue +74 -0
- package/app/pages/konto/profil.vue +160 -0
- package/app/pages/merkliste.vue +11 -0
- package/app/pages/order/[id].vue +69 -0
- package/app/pages/passwort-vergessen.vue +103 -0
- package/app/pages/registrierung/bestaetigen.vue +44 -0
- package/app/pages/registrierung/index.vue +24 -0
- package/app/pages/speisekarte.vue +58 -0
- package/app/pages/unternehmen/[slug].vue +66 -0
- package/app/types/Association.d.ts +11 -0
- package/app/utils/businessHours.ts +119 -0
- package/app/utils/formatDate.ts +9 -0
- package/app/utils/holidays.ts +43 -0
- package/app/utils/storeHours.ts +8 -0
- package/app/utils/time.ts +20 -0
- package/app/validation/addressSchema.ts +34 -0
- package/app/validation/registrationSchema.ts +156 -0
- package/bun.dockerfile +60 -0
- package/compose.yml +17 -0
- package/container +7 -0
- package/content/index.yml +91 -0
- package/content/navigation.yml +67 -0
- package/content/unternehmen/agb.md +1 -0
- package/content/unternehmen/datenschutz.md +1 -0
- package/content/unternehmen/impressum.md +39 -0
- package/content.config.ts +134 -0
- package/eslint.config.mjs +8 -0
- package/node.dockerfile +33 -0
- package/nuxt.config.ts +153 -0
- package/package.json +70 -0
- package/public/dark/Logo.svg +32 -0
- package/public/favicon.ico +0 -0
- package/public/light/Logo.svg +32 -0
- package/renovate.json +4 -0
- package/server/tsconfig.json +3 -0
- package/shopware.d.ts +19 -0
- package/tsconfig.json +4 -0
- package/vitest.config.mts +26 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import LoginOrRegister from "~/components/Checkout/LoginOrRegister.vue";
|
|
3
|
+
import type { StepperItem } from "@nuxt/ui";
|
|
4
|
+
|
|
5
|
+
const { isLoggedIn, isGuestSession } = useUser();
|
|
6
|
+
const { isEmpty } = useCart();
|
|
7
|
+
|
|
8
|
+
const items = [
|
|
9
|
+
{
|
|
10
|
+
slot: "user" as const,
|
|
11
|
+
title: "Deine Daten",
|
|
12
|
+
icon: "i-lucide-user",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
slot: "shipping" as const,
|
|
16
|
+
title: "Zahlung & Versand",
|
|
17
|
+
icon: "i-lucide-truck",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
slot: "checkout" as const,
|
|
21
|
+
title: "Prüfen & Bestellen",
|
|
22
|
+
icon: "i-lucide-check",
|
|
23
|
+
},
|
|
24
|
+
] satisfies StepperItem[];
|
|
25
|
+
|
|
26
|
+
const activeStep = ref(0);
|
|
27
|
+
|
|
28
|
+
const isCustomerAvailable = computed<boolean>(() => {
|
|
29
|
+
return isLoggedIn.value || isGuestSession.value;
|
|
30
|
+
});
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<UContainer>
|
|
35
|
+
<UPageHeader title="Bestellung aufgeben" />
|
|
36
|
+
<UPageBody>
|
|
37
|
+
<UStepper v-model="activeStep" :items="items">
|
|
38
|
+
<template #user>
|
|
39
|
+
<div class="flex flex-col md:flex-row w-full gap-18 my-16">
|
|
40
|
+
<LoginOrRegister
|
|
41
|
+
v-if="!isLoggedIn && !isGuestSession"
|
|
42
|
+
class="basis-1/2"
|
|
43
|
+
/>
|
|
44
|
+
<div v-else class="basis-1/2">
|
|
45
|
+
<UserDetail />
|
|
46
|
+
<UButton
|
|
47
|
+
label="Daten überarbeiten"
|
|
48
|
+
variant="subtle"
|
|
49
|
+
block
|
|
50
|
+
icon="i-lucide-pen"
|
|
51
|
+
class="my-4"
|
|
52
|
+
to="/konto"
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="basis-1/2">
|
|
56
|
+
<h3 class="text-2xl mb-6 font-semibold">Warenkorb</h3>
|
|
57
|
+
<CartQuickView />
|
|
58
|
+
<UButton
|
|
59
|
+
v-if="!isEmpty && isCustomerAvailable"
|
|
60
|
+
:disabled="!isLoggedIn && !isGuestSession"
|
|
61
|
+
label="Zahlungs- und Versandart auswählen"
|
|
62
|
+
size="xl"
|
|
63
|
+
block
|
|
64
|
+
trailing-icon="i-lucide-arrow-right"
|
|
65
|
+
class="my-4"
|
|
66
|
+
@click="activeStep = 1"
|
|
67
|
+
/>
|
|
68
|
+
<UButton
|
|
69
|
+
v-if="isEmpty"
|
|
70
|
+
label="Zur Speisekarte"
|
|
71
|
+
size="xl"
|
|
72
|
+
block
|
|
73
|
+
icon="i-lucide-arrow-left"
|
|
74
|
+
class="my-4"
|
|
75
|
+
to="/speisekarte"
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
<template #shipping>
|
|
82
|
+
<div class="my-14">
|
|
83
|
+
<CheckoutPaymentAndDelivery />
|
|
84
|
+
<div class="w-full flex justify-end">
|
|
85
|
+
<UButton
|
|
86
|
+
label="Weiter zu Prüfen & Bestellen"
|
|
87
|
+
trailing-icon="i-lucide-arrow-right"
|
|
88
|
+
class="m-8 md:max-w-96"
|
|
89
|
+
size="xl"
|
|
90
|
+
block
|
|
91
|
+
@click="activeStep = 2"
|
|
92
|
+
/>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</template>
|
|
96
|
+
|
|
97
|
+
<template #checkout>
|
|
98
|
+
<CheckoutSummary />
|
|
99
|
+
</template>
|
|
100
|
+
</UStepper>
|
|
101
|
+
</UPageBody>
|
|
102
|
+
</UContainer>
|
|
103
|
+
</template>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { Schemas } from "#shopware";
|
|
3
|
+
|
|
4
|
+
definePageMeta({
|
|
5
|
+
layout: "listing2",
|
|
6
|
+
});
|
|
7
|
+
const { clearBreadcrumbs } = useBreadcrumbs();
|
|
8
|
+
const { resolvePath } = useNavigationSearch();
|
|
9
|
+
const route = useRoute();
|
|
10
|
+
const routePath = route.path;
|
|
11
|
+
|
|
12
|
+
const { data: seoResult } = await useAsyncData(
|
|
13
|
+
`cmsResponse${routePath}`,
|
|
14
|
+
async () => {
|
|
15
|
+
// For client links if the history state contains seo url information we can omit the api call
|
|
16
|
+
if (import.meta.client) {
|
|
17
|
+
if (history.state?.routeName) {
|
|
18
|
+
return {
|
|
19
|
+
routeName: history.state?.routeName,
|
|
20
|
+
foreignKey: history.state?.foreignKey,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const seoUrl = await resolvePath(routePath);
|
|
25
|
+
return seoUrl;
|
|
26
|
+
},
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (!seoResult.value?.foreignKey) {
|
|
30
|
+
console.error("c/[...all].vue:", `No data found in API for ${routePath}`);
|
|
31
|
+
|
|
32
|
+
throw createError({
|
|
33
|
+
statusCode: 404,
|
|
34
|
+
statusMessage: `No data fetched from API for ${routePath}`,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { foreignKey } = useNavigationContext(
|
|
39
|
+
seoResult as Ref<Schemas["SeoUrl"]>,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
onBeforeRouteLeave(() => {
|
|
43
|
+
clearBreadcrumbs();
|
|
44
|
+
});
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<template>
|
|
48
|
+
<CategoryListing :id="foreignKey" />
|
|
49
|
+
</template>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
const { data: page } = await useAsyncData("index", () =>
|
|
3
|
+
queryCollection("home").first(),
|
|
4
|
+
);
|
|
5
|
+
if (!page.value) {
|
|
6
|
+
throw createError({
|
|
7
|
+
statusCode: 404,
|
|
8
|
+
statusMessage: "Page not found",
|
|
9
|
+
fatal: true,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
useSeoMeta({
|
|
14
|
+
title: page.value.seo?.title || page.value.title,
|
|
15
|
+
ogTitle: page.value.seo?.title || page.value.title,
|
|
16
|
+
description: page.value.seo?.description || page.value.description,
|
|
17
|
+
ogDescription: page.value.seo?.description || page.value.description,
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
<template>
|
|
21
|
+
<div v-if="page" class="relative">
|
|
22
|
+
<Hero
|
|
23
|
+
:title="page.title"
|
|
24
|
+
:background-video="page.hero.backgroundVideo"
|
|
25
|
+
:description="page.description"
|
|
26
|
+
:headline="page.hero.headline"
|
|
27
|
+
:links="page.hero.links"
|
|
28
|
+
:usps="page.hero.usps"
|
|
29
|
+
/>
|
|
30
|
+
|
|
31
|
+
<USeparator :ui="{ border: 'border-primary/30' }" />
|
|
32
|
+
|
|
33
|
+
<Features
|
|
34
|
+
:title="page.features.title"
|
|
35
|
+
:description="page.features.description"
|
|
36
|
+
:headline="page.features.headline"
|
|
37
|
+
:features="page.features.features"
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
<USeparator :ui="{ border: 'border-primary/30' }" />
|
|
41
|
+
|
|
42
|
+
<FoodMarquee
|
|
43
|
+
v-if="page.marquee.items?.length > 0"
|
|
44
|
+
:title="page.marquee.title"
|
|
45
|
+
:description="page.marquee.description"
|
|
46
|
+
:headline="page.marquee.headline"
|
|
47
|
+
:items="page.marquee.items"
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
<ImageGallery
|
|
51
|
+
:title="page.gallery.title"
|
|
52
|
+
:description="page.gallery.description"
|
|
53
|
+
:headline="page.gallery.headline"
|
|
54
|
+
:images="page.gallery.images"
|
|
55
|
+
:links="page.gallery.links"
|
|
56
|
+
/>
|
|
57
|
+
<Cta />
|
|
58
|
+
</div>
|
|
59
|
+
</template>
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useAddress, useUser } from "@shopware/composables";
|
|
3
|
+
|
|
4
|
+
definePageMeta({
|
|
5
|
+
layout: "account",
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const toast = useToast();
|
|
9
|
+
const { userDefaultShippingAddress, userDefaultBillingAddress, refreshUser } =
|
|
10
|
+
useUser();
|
|
11
|
+
const {
|
|
12
|
+
customerAddresses,
|
|
13
|
+
loadCustomerAddresses,
|
|
14
|
+
setDefaultCustomerBillingAddress,
|
|
15
|
+
setDefaultCustomerShippingAddress,
|
|
16
|
+
} = useAddress();
|
|
17
|
+
|
|
18
|
+
onMounted(async () => {
|
|
19
|
+
await loadCustomerAddresses();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const reloadCustomerData = async () => {
|
|
23
|
+
openEditModal.value = false;
|
|
24
|
+
openNewModal.value = false;
|
|
25
|
+
await loadCustomerAddresses();
|
|
26
|
+
await refreshUser();
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
async function makeDefaultShippingAddress(addressId: string) {
|
|
30
|
+
await setDefaultCustomerShippingAddress(addressId);
|
|
31
|
+
await reloadCustomerData();
|
|
32
|
+
toast.add({
|
|
33
|
+
title: "Standard Lieferadresse wurde geändert!",
|
|
34
|
+
color: "success",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function makeDefaultBillingAddress(addressId: string) {
|
|
39
|
+
await setDefaultCustomerBillingAddress(addressId);
|
|
40
|
+
await reloadCustomerData();
|
|
41
|
+
toast.add({
|
|
42
|
+
title: "Standard Rechnungsadresse wurde geändert!",
|
|
43
|
+
color: "success",
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const openEditModal = ref(false);
|
|
48
|
+
const openNewModal = ref(false);
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<template>
|
|
52
|
+
<UContainer>
|
|
53
|
+
<UPageHeader
|
|
54
|
+
headline="KONTO"
|
|
55
|
+
title="Adressen"
|
|
56
|
+
description="Verwalte deine Adressen."
|
|
57
|
+
/>
|
|
58
|
+
<UPageBody>
|
|
59
|
+
<div class="relative grid grid-cols-1 sm:grid-cols-2 gap-8">
|
|
60
|
+
<AddressCard
|
|
61
|
+
v-if="userDefaultShippingAddress"
|
|
62
|
+
:address="userDefaultShippingAddress"
|
|
63
|
+
title="Standard Lieferadresse"
|
|
64
|
+
icon="i-lucide-house"
|
|
65
|
+
/>
|
|
66
|
+
<AddressCard
|
|
67
|
+
v-if="userDefaultBillingAddress"
|
|
68
|
+
:address="userDefaultBillingAddress"
|
|
69
|
+
title="Standard Rechnungsadresse"
|
|
70
|
+
icon="i-lucide-receipt-text"
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
<USeparator label="Alle Adressen" color="primary" />
|
|
74
|
+
<UPageGrid>
|
|
75
|
+
<div
|
|
76
|
+
v-for="address in customerAddresses ?? []"
|
|
77
|
+
:key="address.id"
|
|
78
|
+
class="space-y-4"
|
|
79
|
+
>
|
|
80
|
+
<AddressCard :address="address" />
|
|
81
|
+
<div class="flex justify-between">
|
|
82
|
+
<UModal v-model:open="openEditModal" title="Adresse bearbeiten">
|
|
83
|
+
<UButton
|
|
84
|
+
label="Bearbeiten"
|
|
85
|
+
variant="outline"
|
|
86
|
+
icon="i-lucide-pen"
|
|
87
|
+
/>
|
|
88
|
+
<template #body>
|
|
89
|
+
<div class="p-8">
|
|
90
|
+
<AddressForm
|
|
91
|
+
:address="address"
|
|
92
|
+
@submit-success="reloadCustomerData"
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
</template>
|
|
96
|
+
</UModal>
|
|
97
|
+
<div class="flex gap-2">
|
|
98
|
+
<UTooltip
|
|
99
|
+
v-if="address.id !== userDefaultShippingAddress?.id"
|
|
100
|
+
text="Als Standard Lieferaddresse setzten"
|
|
101
|
+
>
|
|
102
|
+
<UButton
|
|
103
|
+
color="secondary"
|
|
104
|
+
variant="subtle"
|
|
105
|
+
icon="i-lucide-house"
|
|
106
|
+
@click="makeDefaultShippingAddress(address.id)"
|
|
107
|
+
/>
|
|
108
|
+
</UTooltip>
|
|
109
|
+
<UTooltip
|
|
110
|
+
v-if="address.id !== userDefaultBillingAddress?.id"
|
|
111
|
+
text="Als Standard Rechnungsaddresse setzten"
|
|
112
|
+
>
|
|
113
|
+
<UButton
|
|
114
|
+
color="secondary"
|
|
115
|
+
variant="subtle"
|
|
116
|
+
icon="i-lucide-receipt-text"
|
|
117
|
+
@click="makeDefaultBillingAddress(address.id)"
|
|
118
|
+
/>
|
|
119
|
+
</UTooltip>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</UPageGrid>
|
|
124
|
+
<UModal v-model:open="openNewModal" title="Neue Adresse erstellen">
|
|
125
|
+
<UButton label="Neue Adresse hinzufügen" icon="i-lucide-plus" />
|
|
126
|
+
|
|
127
|
+
<template #body>
|
|
128
|
+
<div class="p-8">
|
|
129
|
+
<AddressForm :address="{}" @submit-success="reloadCustomerData" />
|
|
130
|
+
</div>
|
|
131
|
+
</template>
|
|
132
|
+
</UModal>
|
|
133
|
+
</UPageBody>
|
|
134
|
+
</UContainer>
|
|
135
|
+
</template>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useOrderDetails } from "@shopware/composables";
|
|
3
|
+
import { formatDate } from "~/utils/formatDate";
|
|
4
|
+
|
|
5
|
+
import type { RouteParams } from "vue-router";
|
|
6
|
+
|
|
7
|
+
definePageMeta({
|
|
8
|
+
layout: "account",
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
interface OrderRouteParams extends RouteParams {
|
|
12
|
+
id: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const route = useRoute();
|
|
16
|
+
const { id } = route.params as OrderRouteParams;
|
|
17
|
+
const { order, loadOrderDetails, status } = useOrderDetails(id);
|
|
18
|
+
|
|
19
|
+
const isLoadingData = ref(true);
|
|
20
|
+
|
|
21
|
+
onMounted(async () => {
|
|
22
|
+
isLoadingData.value = true;
|
|
23
|
+
|
|
24
|
+
await loadOrderDetails();
|
|
25
|
+
|
|
26
|
+
isLoadingData.value = false;
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<UContainer>
|
|
32
|
+
<UPageHeader
|
|
33
|
+
headline="BESTELLUNG"
|
|
34
|
+
:title="order?.orderNumber"
|
|
35
|
+
:description="formatDate(order?.createdAt)"
|
|
36
|
+
/>
|
|
37
|
+
<UPageBody>
|
|
38
|
+
<OrderDetail :order="order" :status="status ?? 'laden...'" />
|
|
39
|
+
</UPageBody>
|
|
40
|
+
</UContainer>
|
|
41
|
+
</template>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { formatDate } from "~/utils/formatDate";
|
|
3
|
+
|
|
4
|
+
definePageMeta({
|
|
5
|
+
layout: "account",
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
const { orders, loadOrders } = useCustomerOrders();
|
|
9
|
+
|
|
10
|
+
onMounted(() => {
|
|
11
|
+
loadOrders();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const { getFormattedPrice } = usePrice({
|
|
15
|
+
currencyCode: "EUR",
|
|
16
|
+
localeCode: "de-DE",
|
|
17
|
+
});
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<UContainer>
|
|
22
|
+
<UPageHeader
|
|
23
|
+
headline="KONTO"
|
|
24
|
+
title="Meine Bestellungen"
|
|
25
|
+
description="Historie deiner Bestellungen."
|
|
26
|
+
/>
|
|
27
|
+
<UPageBody>
|
|
28
|
+
<UPageList divide>
|
|
29
|
+
<UPageCard
|
|
30
|
+
v-for="order in orders"
|
|
31
|
+
:key="order.id"
|
|
32
|
+
variant="ghost"
|
|
33
|
+
:to="'/konto/bestellung/' + order.id"
|
|
34
|
+
:ui="{
|
|
35
|
+
root: 'shadow-md rounded-md',
|
|
36
|
+
footer: 'w-full',
|
|
37
|
+
}"
|
|
38
|
+
>
|
|
39
|
+
<template #default>
|
|
40
|
+
<div class="flex flex-row justify-between">
|
|
41
|
+
<div>Bestellung: {{ order.orderNumber }}</div>
|
|
42
|
+
<div>vom: {{ formatDate(order.createdAt) }}</div>
|
|
43
|
+
<div>
|
|
44
|
+
Gesamtbetrag: {{ getFormattedPrice(order.amountTotal) }}
|
|
45
|
+
</div>
|
|
46
|
+
<UIcon name="i-lucide-eye" class="size-6" />
|
|
47
|
+
</div>
|
|
48
|
+
</template>
|
|
49
|
+
</UPageCard>
|
|
50
|
+
</UPageList>
|
|
51
|
+
</UPageBody>
|
|
52
|
+
</UContainer>
|
|
53
|
+
</template>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
definePageMeta({
|
|
3
|
+
layout: "account",
|
|
4
|
+
});
|
|
5
|
+
|
|
6
|
+
const { user, userDefaultShippingAddress, userDefaultBillingAddress } =
|
|
7
|
+
useUser();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<UContainer>
|
|
12
|
+
<UPageHeader
|
|
13
|
+
headline="KONTO"
|
|
14
|
+
title="Übersicht"
|
|
15
|
+
description="Verwalte deine Nutzerdaten und Bestellungen."
|
|
16
|
+
/>
|
|
17
|
+
<UPageBody>
|
|
18
|
+
<UPageGrid>
|
|
19
|
+
<div class="flex flex-col gap-2 w-full">
|
|
20
|
+
<UPageCard
|
|
21
|
+
class="h-full"
|
|
22
|
+
icon="i-lucide-user"
|
|
23
|
+
title="Persönliches Profil"
|
|
24
|
+
:ui="{
|
|
25
|
+
root: 'shadow-md rounded-md',
|
|
26
|
+
footer: 'w-full',
|
|
27
|
+
}"
|
|
28
|
+
>
|
|
29
|
+
<div class="h-full">
|
|
30
|
+
<p>{{ user?.firstName }} {{ user?.lastName }}</p>
|
|
31
|
+
<p>{{ user?.email }}</p>
|
|
32
|
+
</div>
|
|
33
|
+
</UPageCard>
|
|
34
|
+
<UButton
|
|
35
|
+
variant="outline"
|
|
36
|
+
label="Bearbeiten"
|
|
37
|
+
icon="i-lucide-pen"
|
|
38
|
+
block
|
|
39
|
+
to="/konto/profil"
|
|
40
|
+
/>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="flex flex-col gap-2 w-full">
|
|
43
|
+
<AddressCard
|
|
44
|
+
:address="userDefaultShippingAddress"
|
|
45
|
+
title="Standard Lieferadresse"
|
|
46
|
+
icon="i-lucide-house"
|
|
47
|
+
/>
|
|
48
|
+
<UButton
|
|
49
|
+
variant="outline"
|
|
50
|
+
label="Bearbeiten"
|
|
51
|
+
icon="i-lucide-pen"
|
|
52
|
+
block
|
|
53
|
+
to="/konto/adressen"
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="flex flex-col gap-2 w-full">
|
|
57
|
+
<AddressCard
|
|
58
|
+
:address="userDefaultBillingAddress"
|
|
59
|
+
title="Standard Rechnungsadresse"
|
|
60
|
+
icon="i-lucide-receipt-text"
|
|
61
|
+
:with-edit-button="false"
|
|
62
|
+
/>
|
|
63
|
+
<UButton
|
|
64
|
+
variant="outline"
|
|
65
|
+
label="Bearbeiten"
|
|
66
|
+
icon="i-lucide-pen"
|
|
67
|
+
block
|
|
68
|
+
to="/konto/adressen"
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
</UPageGrid>
|
|
72
|
+
</UPageBody>
|
|
73
|
+
</UContainer>
|
|
74
|
+
</template>
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useUser } from "@shopware/composables";
|
|
3
|
+
import type { FormSubmitEvent } from "@nuxt/ui";
|
|
4
|
+
import * as z from "zod";
|
|
5
|
+
|
|
6
|
+
definePageMeta({
|
|
7
|
+
layout: "account",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const { apiClient } = useShopwareContext();
|
|
11
|
+
const loading = ref(true);
|
|
12
|
+
const { user, refreshUser, updatePersonalInfo, updateEmail, logout } =
|
|
13
|
+
useUser();
|
|
14
|
+
const toast = useToast();
|
|
15
|
+
const open = ref(false);
|
|
16
|
+
|
|
17
|
+
const schema = z.object({
|
|
18
|
+
email: z.string().email("Keine gültige Emailadresse"),
|
|
19
|
+
firstName: z.string().min(1),
|
|
20
|
+
lastName: z.string().min(1),
|
|
21
|
+
});
|
|
22
|
+
type Schema = z.output<typeof schema>;
|
|
23
|
+
|
|
24
|
+
function initializeFormState() {
|
|
25
|
+
return reactive({
|
|
26
|
+
email: user.value?.email,
|
|
27
|
+
firstName: user.value?.firstName,
|
|
28
|
+
lastName: user.value?.lastName,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const state = initializeFormState();
|
|
33
|
+
|
|
34
|
+
async function handleEmailUpdate(newEmail: string) {
|
|
35
|
+
if (newEmail !== user.value?.email) {
|
|
36
|
+
await updateEmail(newEmail);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function handlePersonalInfoUpdate(firstName: string, lastName: string) {
|
|
41
|
+
await updatePersonalInfo({
|
|
42
|
+
firstName,
|
|
43
|
+
lastName,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function showSuccessMessage(message: string) {
|
|
48
|
+
toast.add({
|
|
49
|
+
title: message,
|
|
50
|
+
color: "success",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function showErrorMessage(title: string, description: string) {
|
|
55
|
+
toast.add({
|
|
56
|
+
title,
|
|
57
|
+
description,
|
|
58
|
+
icon: "i-lucide-x",
|
|
59
|
+
color: "error" as const,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function onSubmit(event: FormSubmitEvent<Schema>) {
|
|
64
|
+
try {
|
|
65
|
+
loading.value = true;
|
|
66
|
+
const eventData = event.data;
|
|
67
|
+
|
|
68
|
+
await handleEmailUpdate(eventData.email);
|
|
69
|
+
await handlePersonalInfoUpdate(eventData.firstName, eventData.lastName);
|
|
70
|
+
await refreshUser();
|
|
71
|
+
|
|
72
|
+
showSuccessMessage("Erfolgreich gespeichert");
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error("Profile update error:", error);
|
|
75
|
+
showErrorMessage("Fehler!", "Bitte versuchen Sie es später erneut.");
|
|
76
|
+
} finally {
|
|
77
|
+
loading.value = false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function deleteCustomerAccount() {
|
|
82
|
+
await apiClient.invoke("deleteCustomer delete /account/customer");
|
|
83
|
+
toast.add({
|
|
84
|
+
title: "Tschüss!",
|
|
85
|
+
icon: "i-lucide-check",
|
|
86
|
+
color: "success" as const,
|
|
87
|
+
});
|
|
88
|
+
await logout();
|
|
89
|
+
navigateTo("/");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function onDeleteProfile() {
|
|
93
|
+
try {
|
|
94
|
+
await deleteCustomerAccount();
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error("Customer delete error:", error);
|
|
97
|
+
showErrorMessage("Fehler!", "Bitte versuchen Sie es später erneut.");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
onMounted(async () => {
|
|
102
|
+
await refreshUser();
|
|
103
|
+
loading.value = false;
|
|
104
|
+
});
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<template>
|
|
108
|
+
<UContainer>
|
|
109
|
+
<UPageHeader
|
|
110
|
+
headline="KONTO"
|
|
111
|
+
title="Mein Profil"
|
|
112
|
+
description="Ändere hier deine prerönlichen Daten."
|
|
113
|
+
/>
|
|
114
|
+
<UPageBody v-if="!loading">
|
|
115
|
+
<UForm
|
|
116
|
+
:schema="schema"
|
|
117
|
+
:state="state"
|
|
118
|
+
class="space-y-4"
|
|
119
|
+
@submit="onSubmit"
|
|
120
|
+
@error="(error) => console.log('Form validation error:', error)"
|
|
121
|
+
>
|
|
122
|
+
<UFormField label="Vorname" name="firstName">
|
|
123
|
+
<UInput v-model="state.firstName" type="text" class="w-full" />
|
|
124
|
+
</UFormField>
|
|
125
|
+
<UFormField label="Nachname" name="lastName">
|
|
126
|
+
<UInput v-model="state.lastName" type="text" class="w-full" />
|
|
127
|
+
</UFormField>
|
|
128
|
+
<UFormField label="Emailadresse" name="email">
|
|
129
|
+
<UInput v-model="state.email" type="email" class="w-full" />
|
|
130
|
+
</UFormField>
|
|
131
|
+
<div class="flex flex-row justify-between">
|
|
132
|
+
<UButton label="Speichern" type="submit" />
|
|
133
|
+
<UButton label="Konto löschen" color="error" @click="open = !open" />
|
|
134
|
+
</div>
|
|
135
|
+
</UForm>
|
|
136
|
+
<UModal
|
|
137
|
+
v-model:open="open"
|
|
138
|
+
title="Konto löschen"
|
|
139
|
+
description="Ihre Daten werden unwiederruflich gelöscht."
|
|
140
|
+
:ui="{ footer: 'justify-end' }"
|
|
141
|
+
>
|
|
142
|
+
<template #body>
|
|
143
|
+
<p>
|
|
144
|
+
Löscht unwiederuflich ihr Kundenkonto zusammen mit Ihren Adressen,
|
|
145
|
+
Merklisten und verknüpften Daten.
|
|
146
|
+
</p>
|
|
147
|
+
</template>
|
|
148
|
+
<template #footer="{ close }">
|
|
149
|
+
<UButton
|
|
150
|
+
label="Abbrechen"
|
|
151
|
+
color="neutral"
|
|
152
|
+
variant="outline"
|
|
153
|
+
@click="close"
|
|
154
|
+
/>
|
|
155
|
+
<UButton label="Löschen" color="error" @click="onDeleteProfile" />
|
|
156
|
+
</template>
|
|
157
|
+
</UModal>
|
|
158
|
+
</UPageBody>
|
|
159
|
+
</UContainer>
|
|
160
|
+
</template>
|