@shopbite-de/storefront 1.5.2 → 1.6.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/.env.example +3 -1
- package/app/components/Address/Fields.vue +128 -0
- package/app/components/Checkout/PaymentAndDelivery.vue +49 -27
- package/app/components/Header.vue +26 -13
- package/app/components/User/LoginForm.vue +32 -11
- package/app/components/User/RegistrationForm.vue +105 -180
- package/app/composables/useAddressAutocomplete.ts +84 -0
- package/app/composables/useAddressValidation.ts +95 -0
- package/app/composables/useBusinessHours.ts +2 -1
- package/app/composables/useValidCitiesForDelivery.ts +12 -0
- package/app/validation/registrationSchema.ts +105 -124
- package/content/unternehmen/zahlung-und-versand.md +29 -0
- package/nuxt.config.ts +1 -0
- package/package.json +1 -1
- package/server/api/address/autocomplete.get.ts +33 -0
- package/test/nuxt/AddressFields.test.ts +284 -0
- package/test/nuxt/Header.test.ts +124 -0
- package/test/nuxt/LoginForm.test.ts +141 -0
- package/test/nuxt/PaymentAndDelivery.test.ts +78 -0
- package/test/nuxt/RegistrationForm.test.ts +255 -0
- package/test/nuxt/RegistrationValidation.test.ts +39 -0
- package/test/nuxt/registrationSchema.test.ts +242 -0
- package/test/nuxt/useAddressAutocomplete.test.ts +161 -0
- package/test/nuxt/useBusinessHours.test.ts +48 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { createRegistrationSchema } from "~/validation/registrationSchema";
|
|
3
3
|
import type { RegistrationSchema } from "~/validation/registrationSchema";
|
|
4
|
+
import { ApiClientError } from "@shopware/api-client";
|
|
4
5
|
import type { FormSubmitEvent } from "@nuxt/ui";
|
|
5
6
|
|
|
6
7
|
const config = useRuntimeConfig();
|
|
@@ -18,38 +19,40 @@ watch(isLoggedIn, (isLoggedIn) => {
|
|
|
18
19
|
|
|
19
20
|
const state = reactive({
|
|
20
21
|
accountType: "private",
|
|
21
|
-
salutationId:
|
|
22
|
-
firstName:
|
|
23
|
-
lastName:
|
|
24
|
-
email:
|
|
22
|
+
salutationId: "",
|
|
23
|
+
firstName: "",
|
|
24
|
+
lastName: "",
|
|
25
|
+
email: "",
|
|
25
26
|
guest: true,
|
|
26
|
-
password:
|
|
27
|
-
passwordConfirm:
|
|
28
|
-
acceptedDataProtection:
|
|
27
|
+
password: "",
|
|
28
|
+
passwordConfirm: "",
|
|
29
|
+
acceptedDataProtection: false,
|
|
29
30
|
isShippingAddressDifferent: false,
|
|
30
31
|
storefrontUrl: config.public.shopware.devStorefrontUrl,
|
|
31
32
|
billingAddress: {
|
|
32
|
-
company:
|
|
33
|
-
department:
|
|
34
|
-
salutationId:
|
|
35
|
-
firstName:
|
|
36
|
-
lastName:
|
|
37
|
-
phoneNumber:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
company: "",
|
|
34
|
+
department: "",
|
|
35
|
+
salutationId: "",
|
|
36
|
+
firstName: "",
|
|
37
|
+
lastName: "",
|
|
38
|
+
phoneNumber: "",
|
|
39
|
+
additionalAddressLine1: "",
|
|
40
|
+
street: "",
|
|
41
|
+
zipcode: "",
|
|
42
|
+
city: "",
|
|
41
43
|
countryId: config.public.site.countryId,
|
|
42
44
|
},
|
|
43
45
|
shippingAddress: {
|
|
44
|
-
company:
|
|
45
|
-
department:
|
|
46
|
-
salutationId:
|
|
47
|
-
firstName:
|
|
48
|
-
lastName:
|
|
49
|
-
phoneNumber:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
company: "",
|
|
47
|
+
department: "",
|
|
48
|
+
salutationId: "",
|
|
49
|
+
firstName: "",
|
|
50
|
+
lastName: "",
|
|
51
|
+
phoneNumber: "",
|
|
52
|
+
additionalAddressLine1: "",
|
|
53
|
+
street: "",
|
|
54
|
+
zipcode: "",
|
|
55
|
+
city: "",
|
|
53
56
|
countryId: config.public.site.countryId,
|
|
54
57
|
},
|
|
55
58
|
});
|
|
@@ -58,9 +61,37 @@ const schema = computed(() => createRegistrationSchema(state));
|
|
|
58
61
|
|
|
59
62
|
const toast = useToast();
|
|
60
63
|
|
|
64
|
+
const billingAddressFields = ref();
|
|
65
|
+
const shippingAddressFields = ref();
|
|
66
|
+
|
|
61
67
|
async function onSubmit(event: FormSubmitEvent<RegistrationSchema>) {
|
|
62
68
|
const registrationData = { ...event.data };
|
|
63
69
|
|
|
70
|
+
// Check for address corrections
|
|
71
|
+
// Only check if correction is not already shown (user might have ignored it)
|
|
72
|
+
let billingCorrectionFound = false;
|
|
73
|
+
if (!billingAddressFields.value?.showCorrection) {
|
|
74
|
+
billingCorrectionFound = await billingAddressFields.value?.checkAddress();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let shippingCorrectionFound = false;
|
|
78
|
+
if (
|
|
79
|
+
state.isShippingAddressDifferent &&
|
|
80
|
+
!shippingAddressFields.value?.showCorrection
|
|
81
|
+
) {
|
|
82
|
+
shippingCorrectionFound = await shippingAddressFields.value?.checkAddress();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (billingCorrectionFound || shippingCorrectionFound) {
|
|
86
|
+
toast.add({
|
|
87
|
+
title: "Adresskorrektur vorgeschlagen",
|
|
88
|
+
description:
|
|
89
|
+
"Bitte überprüfen Sie die vorgeschlagene Adresskorrektur, bevor Sie fortfahren.",
|
|
90
|
+
color: "info",
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
64
95
|
if (
|
|
65
96
|
!registrationData.billingAddress.firstName &&
|
|
66
97
|
registrationData.firstName
|
|
@@ -76,7 +107,13 @@ async function onSubmit(event: FormSubmitEvent<RegistrationSchema>) {
|
|
|
76
107
|
delete registrationData.shippingAddress;
|
|
77
108
|
}
|
|
78
109
|
|
|
110
|
+
if (registrationData.guest) {
|
|
111
|
+
delete registrationData.password;
|
|
112
|
+
delete registrationData.passwordConfirm;
|
|
113
|
+
}
|
|
114
|
+
|
|
79
115
|
try {
|
|
116
|
+
// @ts-expect-error - password is required in the API type but not for guests
|
|
80
117
|
await register(registrationData);
|
|
81
118
|
|
|
82
119
|
toast.add({
|
|
@@ -86,9 +123,19 @@ async function onSubmit(event: FormSubmitEvent<RegistrationSchema>) {
|
|
|
86
123
|
emit("registration-success", registrationData);
|
|
87
124
|
} catch (error) {
|
|
88
125
|
console.error("Registration failed:", error);
|
|
126
|
+
let description = "Bitte versuchen Sie es erneut.";
|
|
127
|
+
if (error instanceof ApiClientError) {
|
|
128
|
+
const errors = error.details?.errors;
|
|
129
|
+
if (Array.isArray(errors) && errors.length > 0) {
|
|
130
|
+
description = errors
|
|
131
|
+
.map((e) => e.detail || e.title)
|
|
132
|
+
.filter(Boolean)
|
|
133
|
+
.join("\n");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
89
136
|
toast.add({
|
|
90
137
|
title: "Registrierung fehlgeschlagen",
|
|
91
|
-
description
|
|
138
|
+
description,
|
|
92
139
|
color: "error",
|
|
93
140
|
});
|
|
94
141
|
}
|
|
@@ -101,15 +148,13 @@ const accountTypes = ref([
|
|
|
101
148
|
},
|
|
102
149
|
{
|
|
103
150
|
label: "Geschäftskunde",
|
|
104
|
-
value: "
|
|
151
|
+
value: "business",
|
|
105
152
|
},
|
|
106
153
|
]);
|
|
107
154
|
|
|
108
155
|
const emit = defineEmits<{
|
|
109
156
|
"registration-success": [data: RegistrationSchema];
|
|
110
157
|
}>();
|
|
111
|
-
|
|
112
|
-
const allowedCities = ref(["Obertshausen", "Lämmerspiel"]);
|
|
113
158
|
</script>
|
|
114
159
|
|
|
115
160
|
<template>
|
|
@@ -118,7 +163,7 @@ const allowedCities = ref(["Obertshausen", "Lämmerspiel"]);
|
|
|
118
163
|
:state="state"
|
|
119
164
|
class="space-y-4"
|
|
120
165
|
@submit="onSubmit"
|
|
121
|
-
@error="(error) => console.log('Form validation error:', error)"
|
|
166
|
+
@error="(error: any) => console.log('Form validation error:', error)"
|
|
122
167
|
>
|
|
123
168
|
<UFormField name="accountType">
|
|
124
169
|
<USelect
|
|
@@ -151,14 +196,6 @@ const allowedCities = ref(["Obertshausen", "Lämmerspiel"]);
|
|
|
151
196
|
<UInput v-model="state.email" class="w-full" />
|
|
152
197
|
</UFormField>
|
|
153
198
|
|
|
154
|
-
<UFormField label="Telefon" name="billingAddress.phoneNumber" required>
|
|
155
|
-
<UInput
|
|
156
|
-
v-model="state.billingAddress.phoneNumber"
|
|
157
|
-
type="text"
|
|
158
|
-
class="w-full"
|
|
159
|
-
/>
|
|
160
|
-
</UFormField>
|
|
161
|
-
|
|
162
199
|
<UFormField v-if="!state.guest" label="Password" name="password">
|
|
163
200
|
<UInput v-model="state.password" type="password" class="w-full" />
|
|
164
201
|
</UFormField>
|
|
@@ -180,70 +217,14 @@ const allowedCities = ref(["Obertshausen", "Lämmerspiel"]);
|
|
|
180
217
|
"
|
|
181
218
|
/>
|
|
182
219
|
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
v-model="state.billingAddress.company"
|
|
190
|
-
type="text"
|
|
191
|
-
class="w-full"
|
|
192
|
-
/>
|
|
193
|
-
</UFormField>
|
|
194
|
-
|
|
195
|
-
<UFormField
|
|
196
|
-
v-if="state.accountType === 'commercial'"
|
|
197
|
-
label="Abteilung"
|
|
198
|
-
name="billingAddress.department"
|
|
199
|
-
>
|
|
200
|
-
<UInput
|
|
201
|
-
v-model="state.billingAddress.department"
|
|
202
|
-
type="text"
|
|
203
|
-
class="w-full"
|
|
204
|
-
/>
|
|
205
|
-
</UFormField>
|
|
206
|
-
|
|
207
|
-
<UFormField
|
|
208
|
-
label="Straße und Hausnr."
|
|
209
|
-
name="billingAddress.street"
|
|
210
|
-
required
|
|
211
|
-
>
|
|
212
|
-
<UInput
|
|
213
|
-
v-model="state.billingAddress.street"
|
|
214
|
-
type="text"
|
|
215
|
-
class="w-full"
|
|
216
|
-
/>
|
|
217
|
-
</UFormField>
|
|
218
|
-
|
|
219
|
-
<UFormField
|
|
220
|
-
v-if="state.isShippingAddressDifferent"
|
|
221
|
-
label="Postleitzahl"
|
|
222
|
-
name="billingAddress.zipcode"
|
|
223
|
-
>
|
|
224
|
-
<UInput
|
|
225
|
-
v-model="state.billingAddress.zipcode"
|
|
226
|
-
type="text"
|
|
227
|
-
class="w-full"
|
|
228
|
-
/>
|
|
229
|
-
</UFormField>
|
|
230
|
-
|
|
231
|
-
<UFormField label="Ort" name="billingAddress.city" required>
|
|
232
|
-
<USelect
|
|
233
|
-
v-if="!state.isShippingAddressDifferent"
|
|
234
|
-
v-model="state.billingAddress.city"
|
|
235
|
-
:items="allowedCities"
|
|
236
|
-
class="w-full"
|
|
237
|
-
/>
|
|
238
|
-
<UInput
|
|
239
|
-
v-else
|
|
240
|
-
v-model="state.billingAddress.city"
|
|
241
|
-
type="text"
|
|
242
|
-
class="w-full"
|
|
243
|
-
/>
|
|
244
|
-
</UFormField>
|
|
220
|
+
<AddressFields
|
|
221
|
+
ref="billingAddressFields"
|
|
222
|
+
v-model="state.billingAddress"
|
|
223
|
+
prefix="billingAddress"
|
|
224
|
+
:account-type="state.accountType"
|
|
225
|
+
/>
|
|
245
226
|
|
|
246
|
-
<UFormField name="
|
|
227
|
+
<UFormField name="isShippingAddressDifferent">
|
|
247
228
|
<UCheckbox
|
|
248
229
|
v-model="state.isShippingAddressDifferent"
|
|
249
230
|
label="Rechnungsadresse weicht von Lieferadresse ab"
|
|
@@ -251,87 +232,31 @@ const allowedCities = ref(["Obertshausen", "Lämmerspiel"]);
|
|
|
251
232
|
/>
|
|
252
233
|
</UFormField>
|
|
253
234
|
|
|
254
|
-
<
|
|
255
|
-
|
|
256
|
-
color="primary"
|
|
257
|
-
label="Lieferadresse"
|
|
258
|
-
/>
|
|
259
|
-
|
|
260
|
-
<UFormField
|
|
261
|
-
v-if="
|
|
262
|
-
state.accountType === 'commercial' && state.isShippingAddressDifferent
|
|
263
|
-
"
|
|
264
|
-
label="Unternehmen"
|
|
265
|
-
name="shippingAddress.company"
|
|
266
|
-
>
|
|
267
|
-
<UInput
|
|
268
|
-
v-model="state.shippingAddress.company"
|
|
269
|
-
type="text"
|
|
270
|
-
class="w-full"
|
|
271
|
-
/>
|
|
272
|
-
</UFormField>
|
|
273
|
-
|
|
274
|
-
<UFormField
|
|
275
|
-
v-if="
|
|
276
|
-
state.accountType === 'commercial' && state.isShippingAddressDifferent
|
|
277
|
-
"
|
|
278
|
-
label="Abteilung"
|
|
279
|
-
name="billingAddress.department"
|
|
280
|
-
>
|
|
281
|
-
<UInput
|
|
282
|
-
v-model="state.shippingAddress.department"
|
|
283
|
-
type="text"
|
|
284
|
-
class="w-full"
|
|
285
|
-
/>
|
|
286
|
-
</UFormField>
|
|
287
|
-
|
|
288
|
-
<UFormField
|
|
289
|
-
v-if="state.isShippingAddressDifferent"
|
|
290
|
-
label="Vorname"
|
|
291
|
-
name="shippingAddress.firstName"
|
|
292
|
-
required
|
|
293
|
-
>
|
|
294
|
-
<UInput
|
|
295
|
-
v-model="state.shippingAddress.firstName"
|
|
296
|
-
type="text"
|
|
297
|
-
class="w-full"
|
|
298
|
-
/>
|
|
299
|
-
</UFormField>
|
|
300
|
-
|
|
301
|
-
<UFormField
|
|
302
|
-
v-if="state.isShippingAddressDifferent"
|
|
303
|
-
label="Nachname"
|
|
304
|
-
name="shippingAddress.lastName"
|
|
305
|
-
>
|
|
306
|
-
<UInput
|
|
307
|
-
v-model="state.shippingAddress.lastName"
|
|
308
|
-
type="text"
|
|
309
|
-
class="w-full"
|
|
310
|
-
/>
|
|
311
|
-
</UFormField>
|
|
312
|
-
|
|
313
|
-
<UFormField
|
|
314
|
-
v-if="state.isShippingAddressDifferent"
|
|
315
|
-
label="Straße und Hausnr."
|
|
316
|
-
name="shippingAddress.street"
|
|
317
|
-
>
|
|
318
|
-
<UInput
|
|
319
|
-
v-model="state.shippingAddress.street"
|
|
320
|
-
type="text"
|
|
321
|
-
class="w-full"
|
|
322
|
-
/>
|
|
323
|
-
</UFormField>
|
|
235
|
+
<template v-if="state.isShippingAddressDifferent">
|
|
236
|
+
<USeparator color="primary" label="Lieferadresse" />
|
|
324
237
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
:items="allowedCities"
|
|
333
|
-
class="w-full"
|
|
238
|
+
<AddressFields
|
|
239
|
+
ref="shippingAddressFields"
|
|
240
|
+
v-model="state.shippingAddress"
|
|
241
|
+
prefix="shippingAddress"
|
|
242
|
+
:account-type="state.accountType"
|
|
243
|
+
show-names
|
|
244
|
+
is-shipping
|
|
334
245
|
/>
|
|
246
|
+
</template>
|
|
247
|
+
|
|
248
|
+
<UFormField name="acceptedDataProtection">
|
|
249
|
+
<UCheckbox v-model="state.acceptedDataProtection" class="w-full">
|
|
250
|
+
<template #label>
|
|
251
|
+
<span>
|
|
252
|
+
Ich habe die
|
|
253
|
+
<ULink to="/unternehmen/datenschutz">
|
|
254
|
+
Datenschutzbestimmungen
|
|
255
|
+
</ULink>
|
|
256
|
+
gelesen und akzeptiere diese.
|
|
257
|
+
</span>
|
|
258
|
+
</template>
|
|
259
|
+
</UCheckbox>
|
|
335
260
|
</UFormField>
|
|
336
261
|
|
|
337
262
|
<UButton block type="submit">Speichern</UButton>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export interface AddressSuggestion {
|
|
2
|
+
street: string;
|
|
3
|
+
city: string;
|
|
4
|
+
zipcode: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface GeoapifyAddressProperties {
|
|
9
|
+
street?: string;
|
|
10
|
+
housenumber?: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
city?: string;
|
|
13
|
+
town?: string;
|
|
14
|
+
village?: string;
|
|
15
|
+
postcode?: string;
|
|
16
|
+
formatted?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface GeoapifyFeature {
|
|
20
|
+
properties: GeoapifyAddressProperties;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface GeoapifyResponse {
|
|
24
|
+
features: GeoapifyFeature[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useAddressAutocomplete() {
|
|
28
|
+
const { boundingBoxCoordinates, validCities } = useValidCitiesForDelivery();
|
|
29
|
+
|
|
30
|
+
async function getSuggestions(text: string): Promise<AddressSuggestion[]> {
|
|
31
|
+
if (!text || text.length < 3) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const query: Record<string, string | number> = {
|
|
37
|
+
text,
|
|
38
|
+
lang: "de",
|
|
39
|
+
limit: 5,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (boundingBoxCoordinates.value) {
|
|
43
|
+
query.filter = `rect:${boundingBoxCoordinates.value}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { data } = await useFetch<GeoapifyResponse>(
|
|
47
|
+
"/api/address/autocomplete",
|
|
48
|
+
{
|
|
49
|
+
query,
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (!data.value?.features) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return data.value.features
|
|
58
|
+
.map((feature: GeoapifyFeature) => {
|
|
59
|
+
const props = feature.properties;
|
|
60
|
+
return {
|
|
61
|
+
street: props.street
|
|
62
|
+
? `${props.street}${props.housenumber ? " " + props.housenumber : ""}`
|
|
63
|
+
: props.name || "",
|
|
64
|
+
city: props.city || "",
|
|
65
|
+
zipcode: props.postcode || "",
|
|
66
|
+
label: props.formatted || "",
|
|
67
|
+
};
|
|
68
|
+
})
|
|
69
|
+
.filter((suggestion: AddressSuggestion) => {
|
|
70
|
+
if (validCities.value.length === 0) return true;
|
|
71
|
+
return validCities.value.some(
|
|
72
|
+
(city) => city.toLowerCase() === suggestion.city.toLowerCase(),
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error("Error fetching address suggestions:", error);
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
getSuggestions,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { ref, computed, watch, toValue, onUnmounted } from "vue";
|
|
2
|
+
import type { Ref, ComputedRef } from "vue";
|
|
3
|
+
import type { AddressSchema } from "~/validation/registrationSchema";
|
|
4
|
+
import type { AddressSuggestion } from "~/composables/useAddressAutocomplete";
|
|
5
|
+
|
|
6
|
+
export function useAddressValidation(
|
|
7
|
+
model: Ref<AddressSchema>,
|
|
8
|
+
options: {
|
|
9
|
+
isShipping?: Ref<boolean> | ComputedRef<boolean> | boolean;
|
|
10
|
+
getSuggestions: (query: string) => Promise<AddressSuggestion[] | undefined>;
|
|
11
|
+
validCities: Ref<string[]>;
|
|
12
|
+
},
|
|
13
|
+
) {
|
|
14
|
+
const { isShipping = ref(true), getSuggestions, validCities } = options;
|
|
15
|
+
|
|
16
|
+
const showCorrection = ref(false);
|
|
17
|
+
const correction = ref<AddressSuggestion | null>(null);
|
|
18
|
+
|
|
19
|
+
async function checkAddress() {
|
|
20
|
+
const m = toValue(model);
|
|
21
|
+
if (m.street) {
|
|
22
|
+
const searchText = `${m.street}, ${m.zipcode} ${m.city}`;
|
|
23
|
+
const results = await getSuggestions(searchText);
|
|
24
|
+
if (results && results.length > 0) {
|
|
25
|
+
const bestMatch = results[0];
|
|
26
|
+
// Check if it's different enough to suggest correction
|
|
27
|
+
if (
|
|
28
|
+
bestMatch &&
|
|
29
|
+
(bestMatch.street.toLowerCase() !== m.street.toLowerCase() ||
|
|
30
|
+
bestMatch.city.toLowerCase() !== m.city.toLowerCase() ||
|
|
31
|
+
bestMatch.zipcode !== m.zipcode)
|
|
32
|
+
) {
|
|
33
|
+
correction.value = bestMatch;
|
|
34
|
+
showCorrection.value = true;
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
showCorrection.value = false;
|
|
40
|
+
correction.value = null;
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const isInvalidCity = computed(() => {
|
|
45
|
+
if (!toValue(isShipping)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const m = toValue(model);
|
|
49
|
+
if (!m.city || validCities.value.length === 0) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return !validCities.value.some(
|
|
53
|
+
(city) => city.toLowerCase() === m.city.toLowerCase(),
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
58
|
+
|
|
59
|
+
watch(
|
|
60
|
+
() => ({ ...model.value }),
|
|
61
|
+
() => {
|
|
62
|
+
showCorrection.value = false;
|
|
63
|
+
correction.value = null;
|
|
64
|
+
|
|
65
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
66
|
+
debounceTimer = setTimeout(() => {
|
|
67
|
+
if (model.value.street) {
|
|
68
|
+
checkAddress();
|
|
69
|
+
}
|
|
70
|
+
}, 500);
|
|
71
|
+
},
|
|
72
|
+
{ deep: true },
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
onUnmounted(() => {
|
|
76
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
function applyCorrection() {
|
|
80
|
+
if (correction.value) {
|
|
81
|
+
model.value.street = correction.value.street;
|
|
82
|
+
model.value.city = correction.value.city;
|
|
83
|
+
model.value.zipcode = correction.value.zipcode;
|
|
84
|
+
showCorrection.value = false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
showCorrection,
|
|
90
|
+
correction,
|
|
91
|
+
isInvalidCity,
|
|
92
|
+
checkAddress,
|
|
93
|
+
applyCorrection,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -22,7 +22,7 @@ export function useBusinessHours() {
|
|
|
22
22
|
const getServiceIntervals = (date: Date): Array<ServiceInterval> => {
|
|
23
23
|
if (!data.value) return [];
|
|
24
24
|
|
|
25
|
-
const dayOfWeek = date.getDay();
|
|
25
|
+
const dayOfWeek = date.getDay() === 0 ? 7 : date.getDay();
|
|
26
26
|
const dayBusinessHours = data.value.filter(
|
|
27
27
|
(bh) => bh.dayOfWeek === dayOfWeek,
|
|
28
28
|
);
|
|
@@ -38,6 +38,7 @@ export function useBusinessHours() {
|
|
|
38
38
|
end: setTime(date, endH, endM),
|
|
39
39
|
};
|
|
40
40
|
})
|
|
41
|
+
.sort((a, b) => a.start.getTime() - b.start.getTime())
|
|
41
42
|
.filter((interval): interval is ServiceInterval => interval !== null);
|
|
42
43
|
};
|
|
43
44
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function useValidCitiesForDelivery() {
|
|
2
|
+
const validCities = computed(() => ["Obertshausen", "Lämmerspiel", "Hausen"]);
|
|
3
|
+
|
|
4
|
+
const boundingBoxCoordinates = computed(
|
|
5
|
+
() => "8.822251,50.055026,8.899077,50.104327",
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
validCities,
|
|
10
|
+
boundingBoxCoordinates,
|
|
11
|
+
};
|
|
12
|
+
}
|