flowrix 1.0.1-beta.128 → 1.0.1-beta.129
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/dist/module.json +1 -1
- package/dist/runtime/composables/Product/useService.d.ts +39 -15
- package/dist/runtime/composables/Product/useService.js +121 -45
- package/dist/runtime/composables/useLocation.d.ts +59 -9
- package/dist/runtime/composables/useLocation.js +350 -25
- package/dist/runtime/stores/Checkout.js +5 -1
- package/dist/runtime/stores/IpLocation.d.ts +95 -4
- package/dist/runtime/stores/IpLocation.js +93 -15
- package/dist/runtime/stores/Services.d.ts +2 -0
- package/dist/runtime/stores/Services.js +26 -10
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -1,17 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
checkAvailability(data: any): Promise<any>;
|
|
9
|
-
}>;
|
|
10
|
-
CurrentService: import("vue").Ref<never[], never[]>;
|
|
11
|
-
findItemBySlug: (slug: string) => any;
|
|
1
|
+
interface ServiceAvailabilityPayload {
|
|
2
|
+
shipping_country: number;
|
|
3
|
+
shipping_state: number;
|
|
4
|
+
shipping_postcode: string | number;
|
|
5
|
+
}
|
|
6
|
+
export declare function useService(): {
|
|
7
|
+
currentService: import("vue").Ref<never[], never[]>;
|
|
12
8
|
servicesAvailability: import("vue").Ref<boolean, boolean>;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
serviceSuccess: import("vue").Ref<string, string>;
|
|
10
|
+
checkingAvailability: import("vue").Ref<boolean, boolean>;
|
|
11
|
+
addingToCart: import("vue").Ref<boolean, boolean>;
|
|
12
|
+
location: import("vue").ComputedRef<{
|
|
13
|
+
city?: string | undefined;
|
|
14
|
+
country_name?: string | undefined;
|
|
15
|
+
country_code?: string | undefined;
|
|
16
|
+
country_id?: number | undefined;
|
|
17
|
+
ip?: string | undefined;
|
|
18
|
+
proxy?: boolean | undefined;
|
|
19
|
+
region?: string | undefined;
|
|
20
|
+
region_short?: string | undefined;
|
|
21
|
+
region_id?: number | undefined;
|
|
22
|
+
zip_code?: string | undefined;
|
|
23
|
+
} | null>;
|
|
24
|
+
services: import("vue").ComputedRef<never[]>;
|
|
25
|
+
cart: import("vue").ComputedRef<{
|
|
26
|
+
items?: Record<string, import("../../stores/Cart.js").CartItem> | undefined;
|
|
27
|
+
totals?: Record<string, any> | undefined;
|
|
28
|
+
abndToken?: string | undefined;
|
|
29
|
+
fields?: Record<string, any> | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
loading: import("vue").ComputedRef<boolean>;
|
|
32
|
+
checkAvailability: (payload: ServiceAvailabilityPayload) => Promise<any>;
|
|
33
|
+
checkAvailabilityByLocation: () => Promise<any>;
|
|
34
|
+
addToCart: (service: any) => Promise<boolean>;
|
|
35
|
+
findItemBySlug: (slug: string) => string | false;
|
|
36
|
+
isServiceInCart: (slug: string) => boolean;
|
|
37
|
+
getAllServices: () => Promise<never[]>;
|
|
38
|
+
clearSuccessMessage: () => void;
|
|
39
|
+
closeServiceModal: () => void;
|
|
17
40
|
};
|
|
41
|
+
export {};
|
|
@@ -1,64 +1,140 @@
|
|
|
1
|
-
import { ref,
|
|
1
|
+
import { ref, computed, watch } from "vue";
|
|
2
2
|
import { useServiceStore } from "../../stores/Services.js";
|
|
3
3
|
import { useCartStore } from "../../stores/Cart.js";
|
|
4
4
|
import { useIpLocation } from "../../stores/IpLocation.js";
|
|
5
|
-
export
|
|
6
|
-
const
|
|
7
|
-
const
|
|
5
|
+
export function useService() {
|
|
6
|
+
const serviceStore = useServiceStore();
|
|
7
|
+
const cartStore = useCartStore();
|
|
8
|
+
const locationStore = useIpLocation();
|
|
9
|
+
const currentService = ref([]);
|
|
8
10
|
const servicesAvailability = ref(false);
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const serviceSuccess = ref("");
|
|
12
|
+
const checkingAvailability = ref(false);
|
|
13
|
+
const addingToCart = ref(false);
|
|
14
|
+
const location = computed(() => locationStore.location);
|
|
15
|
+
const services = computed(() => serviceStore.services);
|
|
16
|
+
const cart = computed(() => cartStore.cart);
|
|
17
|
+
const loading = computed(() => serviceStore.loading);
|
|
18
|
+
const checkAvailability = async (payload) => {
|
|
19
|
+
if (!payload.shipping_country || !payload.shipping_state || !payload.shipping_postcode) {
|
|
20
|
+
console.error("Invalid availability check payload:", payload);
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
checkingAvailability.value = true;
|
|
24
|
+
try {
|
|
25
|
+
const response = await serviceStore.checkAvailability(payload);
|
|
26
|
+
if (response?.service !== void 0) {
|
|
27
|
+
servicesAvailability.value = response.service;
|
|
28
|
+
}
|
|
29
|
+
return response;
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("Error checking service availability:", error);
|
|
32
|
+
return null;
|
|
33
|
+
} finally {
|
|
34
|
+
checkingAvailability.value = false;
|
|
35
|
+
}
|
|
14
36
|
};
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
37
|
+
const checkAvailabilityByLocation = async () => {
|
|
38
|
+
if (!location.value) {
|
|
39
|
+
console.warn("No location available for availability check");
|
|
40
|
+
return null;
|
|
19
41
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
42
|
+
const payload = {
|
|
43
|
+
shipping_country: location.value.country_id,
|
|
44
|
+
shipping_state: location.value.region_id,
|
|
45
|
+
shipping_postcode: location.value.zip_code
|
|
46
|
+
};
|
|
47
|
+
return await checkAvailability(payload);
|
|
48
|
+
};
|
|
49
|
+
const addToCart = async (service) => {
|
|
50
|
+
if (!service) {
|
|
51
|
+
console.error("No service provided");
|
|
52
|
+
return false;
|
|
25
53
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
54
|
+
addingToCart.value = true;
|
|
55
|
+
serviceSuccess.value = "";
|
|
56
|
+
try {
|
|
57
|
+
await cartStore.addToCart("", 1, "", "", service);
|
|
58
|
+
if (cartStore.addedResponse === "success") {
|
|
59
|
+
serviceSuccess.value = "Service added to cart successfully.";
|
|
60
|
+
closeServiceModal();
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error("Error adding service to cart:", error);
|
|
66
|
+
return false;
|
|
67
|
+
} finally {
|
|
68
|
+
addingToCart.value = false;
|
|
30
69
|
}
|
|
31
70
|
};
|
|
32
71
|
const findItemBySlug = (slug) => {
|
|
33
|
-
if (Object.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
if (!cart.value?.items || Object.keys(cart.value.items).length === 0) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const entries = Object.values(cart.value.items);
|
|
76
|
+
const item = entries.find((entry) => entry.slug === slug);
|
|
77
|
+
return item ? item.rowId : false;
|
|
78
|
+
};
|
|
79
|
+
const isServiceInCart = (slug) => {
|
|
80
|
+
return findItemBySlug(slug) !== false;
|
|
81
|
+
};
|
|
82
|
+
const closeServiceModal = () => {
|
|
83
|
+
const modalEl = document.querySelector(".serviceModelSuccess");
|
|
84
|
+
if (modalEl && typeof bootstrap !== "undefined") {
|
|
85
|
+
const serviceModal = bootstrap.Modal.getInstance(modalEl);
|
|
86
|
+
if (serviceModal) {
|
|
87
|
+
serviceModal.hide();
|
|
40
88
|
}
|
|
41
89
|
}
|
|
42
|
-
return false;
|
|
43
90
|
};
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
91
|
+
const getAllServices = async () => {
|
|
92
|
+
try {
|
|
93
|
+
await serviceStore.getAllServices();
|
|
94
|
+
return services.value;
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error("Error fetching services:", error);
|
|
97
|
+
return [];
|
|
49
98
|
}
|
|
50
|
-
});
|
|
51
|
-
const GetServiceLocations = async () => {
|
|
52
99
|
};
|
|
100
|
+
const clearSuccessMessage = () => {
|
|
101
|
+
serviceSuccess.value = "";
|
|
102
|
+
};
|
|
103
|
+
const autoClearSuccess = () => {
|
|
104
|
+
if (serviceSuccess.value) {
|
|
105
|
+
setTimeout(() => {
|
|
106
|
+
clearSuccessMessage();
|
|
107
|
+
}, 5e3);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
watch(location, async (newLocation) => {
|
|
111
|
+
if (newLocation) {
|
|
112
|
+
await checkAvailabilityByLocation();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
watch(() => serviceSuccess.value, () => {
|
|
116
|
+
autoClearSuccess();
|
|
117
|
+
});
|
|
53
118
|
return {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
CurrentService,
|
|
57
|
-
findItemBySlug,
|
|
119
|
+
// State
|
|
120
|
+
currentService,
|
|
58
121
|
servicesAvailability,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
122
|
+
serviceSuccess,
|
|
123
|
+
checkingAvailability,
|
|
124
|
+
addingToCart,
|
|
125
|
+
// Computed
|
|
126
|
+
location,
|
|
127
|
+
services,
|
|
128
|
+
cart,
|
|
129
|
+
loading,
|
|
130
|
+
// Methods
|
|
131
|
+
checkAvailability,
|
|
132
|
+
checkAvailabilityByLocation,
|
|
133
|
+
addToCart,
|
|
134
|
+
findItemBySlug,
|
|
135
|
+
isServiceInCart,
|
|
136
|
+
getAllServices,
|
|
137
|
+
clearSuccessMessage,
|
|
138
|
+
closeServiceModal
|
|
63
139
|
};
|
|
64
140
|
}
|
|
@@ -1,13 +1,63 @@
|
|
|
1
|
-
|
|
1
|
+
interface LocationInputs {
|
|
2
2
|
country: string;
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
country_id: string | number;
|
|
4
|
+
zip_code: string | number;
|
|
5
|
+
region: string;
|
|
6
|
+
region_id: string | number | null;
|
|
7
|
+
}
|
|
8
|
+
interface LocationData {
|
|
5
9
|
city?: string;
|
|
10
|
+
country_name?: string;
|
|
11
|
+
country_code?: string;
|
|
12
|
+
country_id?: number;
|
|
13
|
+
ip?: string;
|
|
14
|
+
proxy?: boolean;
|
|
15
|
+
region?: string;
|
|
16
|
+
region_short?: string;
|
|
17
|
+
region_id?: number;
|
|
18
|
+
zip_code?: string;
|
|
6
19
|
}
|
|
7
|
-
export declare
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
export declare function useLocation(): {
|
|
21
|
+
states: import("vue").Ref<never[], never[]>;
|
|
22
|
+
manualLocationHeading: import("vue").Ref<string | null, string | null>;
|
|
23
|
+
locationInputs: import("vue").Ref<{
|
|
24
|
+
country: string;
|
|
25
|
+
country_id: string | number;
|
|
26
|
+
zip_code: string | number;
|
|
27
|
+
region: string;
|
|
28
|
+
region_id: string | number | null;
|
|
29
|
+
}, LocationInputs | {
|
|
30
|
+
country: string;
|
|
31
|
+
country_id: string | number;
|
|
32
|
+
zip_code: string | number;
|
|
33
|
+
region: string;
|
|
34
|
+
region_id: string | number | null;
|
|
35
|
+
}>;
|
|
36
|
+
location: import("vue").ComputedRef<{
|
|
37
|
+
city?: string | undefined;
|
|
38
|
+
country_name?: string | undefined;
|
|
39
|
+
country_code?: string | undefined;
|
|
40
|
+
country_id?: number | undefined;
|
|
41
|
+
ip?: string | undefined;
|
|
42
|
+
proxy?: boolean | undefined;
|
|
43
|
+
region?: string | undefined;
|
|
44
|
+
region_short?: string | undefined;
|
|
45
|
+
region_id?: number | undefined;
|
|
46
|
+
zip_code?: string | undefined;
|
|
47
|
+
} | null>;
|
|
48
|
+
countries: import("vue").ComputedRef<any>;
|
|
49
|
+
loading: import("vue").ComputedRef<boolean>;
|
|
50
|
+
companyCountryId: import("vue").ComputedRef<any>;
|
|
51
|
+
getStateShort: (stateName: string) => string;
|
|
52
|
+
getStates: (countryId: number | string) => any;
|
|
53
|
+
changeStates: (selectedCountry: string | number) => void;
|
|
54
|
+
changeLocation: (data?: Partial<LocationInputs>) => Promise<void>;
|
|
55
|
+
updateLocationData: (data?: Partial<LocationInputs>) => Promise<void>;
|
|
56
|
+
getLocation: () => Promise<void>;
|
|
57
|
+
checkCurrentLocation: () => void;
|
|
58
|
+
initLocationInputs: () => void;
|
|
59
|
+
loadLocation: () => LocationData | null;
|
|
60
|
+
checkLocationPermission: () => Promise<string>;
|
|
61
|
+
initializeCountriesAndStates: () => Promise<void>;
|
|
13
62
|
};
|
|
63
|
+
export {};
|
|
@@ -1,32 +1,357 @@
|
|
|
1
|
-
import { computed,
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { ref, computed, watch, nextTick } from "vue";
|
|
2
|
+
import { useIpLocation } from "../stores/IpLocation.js";
|
|
3
|
+
import { useCountriesStore } from "../stores/countries.js";
|
|
4
|
+
import { useCompanyProfile } from "../stores/useCompanyProfile.js";
|
|
5
|
+
import { useCheckoutStore } from "../stores/Checkout.js";
|
|
6
|
+
export function useLocation() {
|
|
7
|
+
const locationStore = useIpLocation();
|
|
8
|
+
const countriesStore = useCountriesStore();
|
|
9
|
+
const companyProfileStore = useCompanyProfile();
|
|
10
|
+
const checkoutStore = useCheckoutStore();
|
|
11
|
+
const states = ref([]);
|
|
12
|
+
const manualLocationHeading = ref(null);
|
|
13
|
+
const locationInputs = ref({
|
|
14
|
+
country: "Australia",
|
|
15
|
+
country_id: 14,
|
|
16
|
+
zip_code: 3370,
|
|
17
|
+
region: "",
|
|
18
|
+
region_id: null
|
|
10
19
|
});
|
|
11
|
-
const location = computed(() =>
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
const location = computed(() => locationStore.location);
|
|
21
|
+
const countries = computed(() => {
|
|
22
|
+
const storeCountries = countriesStore.countries;
|
|
23
|
+
if (Array.isArray(storeCountries)) {
|
|
24
|
+
return storeCountries;
|
|
25
|
+
} else if (storeCountries?.data && Array.isArray(storeCountries.data)) {
|
|
26
|
+
return storeCountries.data;
|
|
27
|
+
}
|
|
28
|
+
return [];
|
|
29
|
+
});
|
|
30
|
+
const loading = computed(() => locationStore.loading);
|
|
31
|
+
const profile = computed(() => companyProfileStore.profile);
|
|
32
|
+
const companyCountryId = computed(() => profile.value?.country_id || 14);
|
|
33
|
+
const stateShortCodes = {
|
|
34
|
+
"New South Wales": "NSW",
|
|
35
|
+
"Victoria": "VIC",
|
|
36
|
+
"Queensland": "QLD",
|
|
37
|
+
"South Australia": "SA",
|
|
38
|
+
"Western Australia": "WA",
|
|
39
|
+
"Tasmania": "TAS",
|
|
40
|
+
"Northern Territory": "NT",
|
|
41
|
+
"Australian Capital Territory": "ACT"
|
|
42
|
+
};
|
|
43
|
+
const getStateShort = (stateName) => {
|
|
44
|
+
return stateShortCodes[stateName] || stateName;
|
|
45
|
+
};
|
|
46
|
+
const getStates = (countryId) => {
|
|
47
|
+
if (!countries.value || countries.value.length === 0) {
|
|
48
|
+
console.warn("No countries available");
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const activeCountry = countries.value.find(
|
|
52
|
+
(country) => country.id == countryId || country.name == countryId
|
|
53
|
+
);
|
|
54
|
+
if (activeCountry && activeCountry.states) {
|
|
55
|
+
states.value = activeCountry.states;
|
|
56
|
+
return activeCountry.states;
|
|
57
|
+
} else {
|
|
58
|
+
states.value = [];
|
|
59
|
+
console.warn("No states found for country:", countryId);
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const changeStates = (selectedCountry) => {
|
|
64
|
+
const country = countries.value?.find(
|
|
65
|
+
(c) => c.name === selectedCountry || c.id === selectedCountry
|
|
66
|
+
);
|
|
67
|
+
if (country) {
|
|
68
|
+
states.value = country.states || [];
|
|
69
|
+
locationInputs.value.country_id = country.id;
|
|
70
|
+
locationInputs.value.region = "";
|
|
71
|
+
locationInputs.value.region_id = null;
|
|
72
|
+
if (window.locationcountry === country.name && window.locationstate) {
|
|
73
|
+
locationInputs.value.region = window.locationstate;
|
|
74
|
+
const state = country.states?.find((s) => s.name === window.locationstate);
|
|
75
|
+
if (state) {
|
|
76
|
+
locationInputs.value.region_id = state.id;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
console.warn("Country not found:", selectedCountry);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const updateLocationData = async (data = {}) => {
|
|
84
|
+
try {
|
|
85
|
+
const currentLocation = location.value;
|
|
86
|
+
const updateData = {
|
|
87
|
+
region: data.region || currentLocation?.region || "",
|
|
88
|
+
region_id: data.region_id || currentLocation?.region_id || null,
|
|
89
|
+
zip_code: data.zip_code || currentLocation?.zip_code || "",
|
|
90
|
+
country: data.country || currentLocation?.country_name || "Australia",
|
|
91
|
+
country_id: data.country_id || currentLocation?.country_id || companyCountryId.value
|
|
92
|
+
};
|
|
93
|
+
const country = countries.value?.find(
|
|
94
|
+
(c) => c.name === updateData.country || c.id === updateData.country_id
|
|
95
|
+
);
|
|
96
|
+
if (!country) {
|
|
97
|
+
console.error("Country not found:", updateData.country);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const state = country.states?.find(
|
|
101
|
+
(s) => s.name === updateData.region || s.id === updateData.region_id
|
|
102
|
+
);
|
|
103
|
+
if (!state) {
|
|
104
|
+
console.error("State not found:", updateData.region);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const newLocation = {
|
|
108
|
+
zip_code: updateData.zip_code,
|
|
109
|
+
region: state.name,
|
|
110
|
+
region_short: getStateShort(state.name),
|
|
111
|
+
region_id: state.id,
|
|
112
|
+
country_name: country.name,
|
|
113
|
+
country_id: country.id
|
|
114
|
+
};
|
|
115
|
+
locationStore.location = newLocation;
|
|
116
|
+
checkoutStore.saveToCheckoutSession(newLocation, "changelocation");
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error("Error updating location:", error);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const changeLocation = async (data) => {
|
|
122
|
+
if (data && Object.keys(data).length > 0) {
|
|
123
|
+
await updateLocationData(data);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const checkLocationPermission = async () => {
|
|
127
|
+
try {
|
|
128
|
+
const permissionStatus = await navigator.permissions.query({
|
|
129
|
+
name: "geolocation"
|
|
130
|
+
});
|
|
131
|
+
return permissionStatus.state;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error("Permission check failed:", error);
|
|
134
|
+
return "error";
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const getBrowserLocation = async () => {
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
if (!navigator.geolocation) {
|
|
140
|
+
reject(new Error("Geolocation not supported"));
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
navigator.geolocation.getCurrentPosition(
|
|
144
|
+
async (position) => {
|
|
145
|
+
try {
|
|
146
|
+
await processGeolocation(position);
|
|
147
|
+
resolve();
|
|
148
|
+
} catch (error) {
|
|
149
|
+
reject(error);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
async (error) => {
|
|
153
|
+
console.error("Geolocation error:", error);
|
|
154
|
+
if (profile.value?.iplocation && !location.value) {
|
|
155
|
+
await locationStore.getLocation();
|
|
156
|
+
} else {
|
|
157
|
+
manualLocationHeading.value = "We need your location to check service availability in your area. You can add your location manually.";
|
|
158
|
+
const changeLocationBtn = document.querySelector(".change_location");
|
|
159
|
+
if (changeLocationBtn) {
|
|
160
|
+
changeLocationBtn.click();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
reject(error);
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
const processGeolocation = async (position) => {
|
|
169
|
+
const lat = position.coords.latitude;
|
|
170
|
+
const lng = position.coords.longitude;
|
|
171
|
+
const apiKey = window.GeocodingAPI;
|
|
172
|
+
if (!apiKey) {
|
|
173
|
+
throw new Error("Geocoding API key not found");
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const response = await fetch(
|
|
177
|
+
`https://maps.googleapis.com/maps/api/geocode/json?latlng=${lat},${lng}&key=${apiKey}`
|
|
178
|
+
);
|
|
179
|
+
const data = await response.json();
|
|
180
|
+
if (data.status !== "OK") {
|
|
181
|
+
throw new Error("Geocoding failed");
|
|
182
|
+
}
|
|
183
|
+
const locationData = extractLocationData(data.results);
|
|
184
|
+
const country = countries.value?.find((c) => c.name === locationData.country);
|
|
185
|
+
if (country) {
|
|
186
|
+
const state = country.states?.find((s) => s.name === locationData.state);
|
|
187
|
+
locationStore.location = {
|
|
188
|
+
city: locationData.city,
|
|
189
|
+
country_name: country.name,
|
|
190
|
+
country_code: locationData.country_code,
|
|
191
|
+
country_id: country.id,
|
|
192
|
+
ip: "",
|
|
193
|
+
proxy: false,
|
|
194
|
+
region: locationData.state,
|
|
195
|
+
region_short: locationData.state_short,
|
|
196
|
+
region_id: state?.id,
|
|
197
|
+
zip_code: locationData.postal_code
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error("Geocoding error:", error);
|
|
202
|
+
if (profile.value?.iplocation && !location.value) {
|
|
203
|
+
await locationStore.getLocation();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const extractLocationData = (results) => {
|
|
208
|
+
const data = {
|
|
209
|
+
city: "N/A",
|
|
210
|
+
country: "N/A",
|
|
211
|
+
country_code: "N/A",
|
|
212
|
+
state: "N/A",
|
|
213
|
+
state_short: "N/A",
|
|
214
|
+
postal_code: "N/A"
|
|
16
215
|
};
|
|
216
|
+
for (const result of results) {
|
|
217
|
+
for (const component of result.address_components) {
|
|
218
|
+
if (component.types.includes("administrative_area_level_2")) {
|
|
219
|
+
data.city = component.long_name;
|
|
220
|
+
}
|
|
221
|
+
if (component.types.includes("country")) {
|
|
222
|
+
data.country = component.long_name;
|
|
223
|
+
data.country_code = component.short_name;
|
|
224
|
+
}
|
|
225
|
+
if (component.types.includes("administrative_area_level_1")) {
|
|
226
|
+
data.state = component.long_name;
|
|
227
|
+
data.state_short = component.short_name;
|
|
228
|
+
}
|
|
229
|
+
if (component.types.includes("postal_code")) {
|
|
230
|
+
data.postal_code = component.long_name;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return data;
|
|
235
|
+
};
|
|
236
|
+
const initializeCountriesAndStates = async () => {
|
|
237
|
+
try {
|
|
238
|
+
if (!countries.value || countries.value.length === 0) {
|
|
239
|
+
const fetchedCountries = await countriesStore.getCountries();
|
|
240
|
+
}
|
|
241
|
+
await nextTick();
|
|
242
|
+
if (countries.value && countries.value.length > 0) {
|
|
243
|
+
const statesResult = getStates(companyCountryId.value);
|
|
244
|
+
const defaultCountry = countries.value.find((c) => c.id === companyCountryId.value);
|
|
245
|
+
if (defaultCountry) {
|
|
246
|
+
locationInputs.value.country = defaultCountry.name;
|
|
247
|
+
locationInputs.value.country_id = defaultCountry.id;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.error("Error initializing countries and states:", error);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
const getLocation = async () => {
|
|
255
|
+
try {
|
|
256
|
+
await initializeCountriesAndStates();
|
|
257
|
+
if (location.value) {
|
|
258
|
+
initLocationInputs();
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (profile.value) {
|
|
262
|
+
profile.value.iplocation = true;
|
|
263
|
+
}
|
|
264
|
+
if (window.GeocodingAPI && !location.value) {
|
|
265
|
+
try {
|
|
266
|
+
await getBrowserLocation();
|
|
267
|
+
return;
|
|
268
|
+
} catch (error) {
|
|
269
|
+
console.log("Browser location failed, falling back to IP location");
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (profile.value?.iplocation && !location.value) {
|
|
273
|
+
await locationStore.getLocation();
|
|
274
|
+
}
|
|
275
|
+
if (location.value) {
|
|
276
|
+
initLocationInputs();
|
|
277
|
+
}
|
|
278
|
+
} catch (error) {
|
|
279
|
+
console.error("Error getting location:", error);
|
|
280
|
+
}
|
|
17
281
|
};
|
|
18
|
-
const
|
|
19
|
-
|
|
282
|
+
const checkCurrentLocation = () => {
|
|
283
|
+
if (location.value?.region) {
|
|
284
|
+
locationInputs.value.region = location.value.region;
|
|
285
|
+
locationInputs.value.region_id = location.value.region_id || null;
|
|
286
|
+
if (location.value.country_id) {
|
|
287
|
+
getStates(location.value.country_id);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
20
290
|
};
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
291
|
+
const initLocationInputs = () => {
|
|
292
|
+
if (location.value) {
|
|
293
|
+
locationInputs.value = {
|
|
294
|
+
country: location.value.country_name || "Australia",
|
|
295
|
+
country_id: location.value.country_id || companyCountryId.value,
|
|
296
|
+
zip_code: location.value.zip_code || 3370,
|
|
297
|
+
region: location.value.region || "",
|
|
298
|
+
region_id: location.value.region_id || null
|
|
299
|
+
};
|
|
300
|
+
if (location.value.country_id) {
|
|
301
|
+
getStates(location.value.country_id);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
24
304
|
};
|
|
305
|
+
const loadLocation = () => {
|
|
306
|
+
const stored = localStorage.getItem("IpLocation");
|
|
307
|
+
if (stored) {
|
|
308
|
+
try {
|
|
309
|
+
const parsed = JSON.parse(stored);
|
|
310
|
+
return parsed.location || null;
|
|
311
|
+
} catch (error) {
|
|
312
|
+
console.error("Invalid IpLocation data:", error);
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return null;
|
|
317
|
+
};
|
|
318
|
+
watch(location, (newLocation) => {
|
|
319
|
+
if (newLocation) {
|
|
320
|
+
initLocationInputs();
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
watch(countries, (newCountries) => {
|
|
324
|
+
if (newCountries && newCountries.length > 0) {
|
|
325
|
+
getStates(companyCountryId.value);
|
|
326
|
+
if (locationInputs.value.country_id) {
|
|
327
|
+
const country = newCountries.find((c) => c.id === locationInputs.value.country_id);
|
|
328
|
+
if (country) {
|
|
329
|
+
locationInputs.value.country = country.name;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
25
334
|
return {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
335
|
+
// State
|
|
336
|
+
states,
|
|
337
|
+
manualLocationHeading,
|
|
338
|
+
locationInputs,
|
|
339
|
+
// Computed
|
|
340
|
+
location,
|
|
341
|
+
countries,
|
|
342
|
+
loading,
|
|
343
|
+
companyCountryId,
|
|
344
|
+
// Methods
|
|
345
|
+
getStateShort,
|
|
346
|
+
getStates,
|
|
347
|
+
changeStates,
|
|
348
|
+
changeLocation,
|
|
349
|
+
updateLocationData,
|
|
350
|
+
getLocation,
|
|
351
|
+
checkCurrentLocation,
|
|
352
|
+
initLocationInputs,
|
|
353
|
+
loadLocation,
|
|
354
|
+
checkLocationPermission,
|
|
355
|
+
initializeCountriesAndStates
|
|
31
356
|
};
|
|
32
|
-
}
|
|
357
|
+
}
|
|
@@ -78,7 +78,11 @@ export const useCheckoutStore = defineStore("checkout", {
|
|
|
78
78
|
if (useCartStore().coupon?.length > 0) {
|
|
79
79
|
formData.vouchercodes = useCartStore().coupon;
|
|
80
80
|
}
|
|
81
|
-
|
|
81
|
+
if (useCartStore().cart.items != void 0 && Object.keys(useCartStore().cart.items).length > 0) {
|
|
82
|
+
formData.cart = useCartStore().cart.items;
|
|
83
|
+
} else {
|
|
84
|
+
delete formData.cart;
|
|
85
|
+
}
|
|
82
86
|
formData.abndToken = useCartStore().cart.abndToken;
|
|
83
87
|
const apiUrl = `checkout/configs`;
|
|
84
88
|
const config = useRuntimeConfig();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface LocationData {
|
|
1
|
+
export interface LocationData {
|
|
2
2
|
city?: string;
|
|
3
3
|
country_name?: string;
|
|
4
4
|
country_code?: string;
|
|
@@ -10,9 +10,100 @@ interface LocationData {
|
|
|
10
10
|
region_id?: number;
|
|
11
11
|
zip_code?: string;
|
|
12
12
|
}
|
|
13
|
+
export interface LocationResponse {
|
|
14
|
+
status: string;
|
|
15
|
+
message?: string;
|
|
16
|
+
data: LocationData | null;
|
|
17
|
+
}
|
|
13
18
|
export declare const useIpLocation: import("pinia").StoreDefinition<"IpLocation", {
|
|
14
19
|
location: LocationData | null;
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
loading: boolean;
|
|
21
|
+
error: string | null;
|
|
22
|
+
}, {
|
|
23
|
+
hasLocation: (state: {
|
|
24
|
+
location: {
|
|
25
|
+
city?: string | undefined;
|
|
26
|
+
country_name?: string | undefined;
|
|
27
|
+
country_code?: string | undefined;
|
|
28
|
+
country_id?: number | undefined;
|
|
29
|
+
ip?: string | undefined;
|
|
30
|
+
proxy?: boolean | undefined;
|
|
31
|
+
region?: string | undefined;
|
|
32
|
+
region_short?: string | undefined;
|
|
33
|
+
region_id?: number | undefined;
|
|
34
|
+
zip_code?: string | undefined;
|
|
35
|
+
} | null;
|
|
36
|
+
loading: boolean;
|
|
37
|
+
error: string | null;
|
|
38
|
+
} & import("pinia").PiniaCustomStateProperties<{
|
|
39
|
+
location: LocationData | null;
|
|
40
|
+
loading: boolean;
|
|
41
|
+
error: string | null;
|
|
42
|
+
}>) => boolean;
|
|
43
|
+
isLoading: (state: {
|
|
44
|
+
location: {
|
|
45
|
+
city?: string | undefined;
|
|
46
|
+
country_name?: string | undefined;
|
|
47
|
+
country_code?: string | undefined;
|
|
48
|
+
country_id?: number | undefined;
|
|
49
|
+
ip?: string | undefined;
|
|
50
|
+
proxy?: boolean | undefined;
|
|
51
|
+
region?: string | undefined;
|
|
52
|
+
region_short?: string | undefined;
|
|
53
|
+
region_id?: number | undefined;
|
|
54
|
+
zip_code?: string | undefined;
|
|
55
|
+
} | null;
|
|
56
|
+
loading: boolean;
|
|
57
|
+
error: string | null;
|
|
58
|
+
} & import("pinia").PiniaCustomStateProperties<{
|
|
59
|
+
location: LocationData | null;
|
|
60
|
+
loading: boolean;
|
|
61
|
+
error: string | null;
|
|
62
|
+
}>) => boolean;
|
|
63
|
+
locationString: (state: {
|
|
64
|
+
location: {
|
|
65
|
+
city?: string | undefined;
|
|
66
|
+
country_name?: string | undefined;
|
|
67
|
+
country_code?: string | undefined;
|
|
68
|
+
country_id?: number | undefined;
|
|
69
|
+
ip?: string | undefined;
|
|
70
|
+
proxy?: boolean | undefined;
|
|
71
|
+
region?: string | undefined;
|
|
72
|
+
region_short?: string | undefined;
|
|
73
|
+
region_id?: number | undefined;
|
|
74
|
+
zip_code?: string | undefined;
|
|
75
|
+
} | null;
|
|
76
|
+
loading: boolean;
|
|
77
|
+
error: string | null;
|
|
78
|
+
} & import("pinia").PiniaCustomStateProperties<{
|
|
79
|
+
location: LocationData | null;
|
|
80
|
+
loading: boolean;
|
|
81
|
+
error: string | null;
|
|
82
|
+
}>) => string;
|
|
83
|
+
fullAddress: (state: {
|
|
84
|
+
location: {
|
|
85
|
+
city?: string | undefined;
|
|
86
|
+
country_name?: string | undefined;
|
|
87
|
+
country_code?: string | undefined;
|
|
88
|
+
country_id?: number | undefined;
|
|
89
|
+
ip?: string | undefined;
|
|
90
|
+
proxy?: boolean | undefined;
|
|
91
|
+
region?: string | undefined;
|
|
92
|
+
region_short?: string | undefined;
|
|
93
|
+
region_id?: number | undefined;
|
|
94
|
+
zip_code?: string | undefined;
|
|
95
|
+
} | null;
|
|
96
|
+
loading: boolean;
|
|
97
|
+
error: string | null;
|
|
98
|
+
} & import("pinia").PiniaCustomStateProperties<{
|
|
99
|
+
location: LocationData | null;
|
|
100
|
+
loading: boolean;
|
|
101
|
+
error: string | null;
|
|
102
|
+
}>) => string;
|
|
103
|
+
}, {
|
|
104
|
+
getLocation(): Promise<LocationData | null>;
|
|
105
|
+
setLocation(locationData: LocationData): void;
|
|
106
|
+
updateLocation(partialData: Partial<LocationData>): void;
|
|
107
|
+
clearLocation(): void;
|
|
108
|
+
clearError(): void;
|
|
17
109
|
}>;
|
|
18
|
-
export {};
|
|
@@ -1,29 +1,107 @@
|
|
|
1
1
|
import { defineStore } from "pinia";
|
|
2
|
+
import { flowrixApi } from "../middleware/flowrix.js";
|
|
3
|
+
import { useRuntimeConfig } from "#imports";
|
|
4
|
+
function formatErrorMessage(message) {
|
|
5
|
+
if (Array.isArray(message)) {
|
|
6
|
+
return message.join(", ");
|
|
7
|
+
}
|
|
8
|
+
return message;
|
|
9
|
+
}
|
|
2
10
|
export const useIpLocation = defineStore("IpLocation", {
|
|
3
11
|
state: () => ({
|
|
4
|
-
location: null
|
|
12
|
+
location: null,
|
|
13
|
+
loading: false,
|
|
14
|
+
error: null
|
|
5
15
|
}),
|
|
16
|
+
getters: {
|
|
17
|
+
hasLocation: (state) => state.location !== null,
|
|
18
|
+
isLoading: (state) => state.loading,
|
|
19
|
+
locationString: (state) => {
|
|
20
|
+
if (!state.location) return "";
|
|
21
|
+
const parts = [
|
|
22
|
+
state.location.city,
|
|
23
|
+
state.location.region_short || state.location.region,
|
|
24
|
+
state.location.zip_code
|
|
25
|
+
].filter(Boolean);
|
|
26
|
+
return parts.join(", ");
|
|
27
|
+
},
|
|
28
|
+
fullAddress: (state) => {
|
|
29
|
+
if (!state.location) return "";
|
|
30
|
+
const parts = [
|
|
31
|
+
state.location.city,
|
|
32
|
+
state.location.region,
|
|
33
|
+
state.location.zip_code,
|
|
34
|
+
state.location.country_name
|
|
35
|
+
].filter(Boolean);
|
|
36
|
+
return parts.join(", ");
|
|
37
|
+
}
|
|
38
|
+
},
|
|
6
39
|
actions: {
|
|
7
40
|
async getLocation() {
|
|
41
|
+
if (this.loading || this.location !== null) {
|
|
42
|
+
console.log("Location already loaded or loading:", this.location);
|
|
43
|
+
return this.location;
|
|
44
|
+
}
|
|
45
|
+
this.loading = true;
|
|
46
|
+
this.error = null;
|
|
8
47
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
48
|
+
const config = useRuntimeConfig();
|
|
49
|
+
let rawCookies = "";
|
|
50
|
+
if (process.client) {
|
|
51
|
+
rawCookies = document.cookie || "";
|
|
52
|
+
}
|
|
53
|
+
const apiConfig = {
|
|
54
|
+
...config,
|
|
55
|
+
cookies: rawCookies
|
|
56
|
+
};
|
|
57
|
+
const apiUrl = "location";
|
|
58
|
+
const response = await flowrixApi.post(apiUrl, apiConfig);
|
|
59
|
+
if (response.status === "Success" && response.data !== null) {
|
|
60
|
+
this.location = response.data;
|
|
61
|
+
console.log("Location loaded:", this.location);
|
|
62
|
+
return response.data;
|
|
63
|
+
} else {
|
|
64
|
+
const errorMessage = response.message ? formatErrorMessage(response.message) : "Failed to fetch location";
|
|
65
|
+
this.error = errorMessage;
|
|
66
|
+
console.error("Failed to fetch location:", errorMessage);
|
|
67
|
+
this.location = null;
|
|
68
|
+
return null;
|
|
21
69
|
}
|
|
22
70
|
} catch (error) {
|
|
23
|
-
console.error("Error fetching
|
|
71
|
+
console.error("Error fetching location:", error);
|
|
72
|
+
this.error = error.message || "Unknown error occurred";
|
|
24
73
|
this.location = null;
|
|
74
|
+
return null;
|
|
75
|
+
} finally {
|
|
76
|
+
this.loading = false;
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
setLocation(locationData) {
|
|
80
|
+
this.location = locationData;
|
|
81
|
+
this.error = null;
|
|
82
|
+
console.log("Location set manually:", this.location);
|
|
83
|
+
},
|
|
84
|
+
updateLocation(partialData) {
|
|
85
|
+
if (this.location) {
|
|
86
|
+
this.location = { ...this.location, ...partialData };
|
|
87
|
+
console.log("Location updated:", this.location);
|
|
88
|
+
} else {
|
|
89
|
+
console.warn("Cannot update location: no location data exists");
|
|
25
90
|
}
|
|
91
|
+
},
|
|
92
|
+
clearLocation() {
|
|
93
|
+
this.location = null;
|
|
94
|
+
this.error = null;
|
|
95
|
+
this.loading = false;
|
|
96
|
+
console.log("Location cleared");
|
|
97
|
+
},
|
|
98
|
+
clearError() {
|
|
99
|
+
this.error = null;
|
|
26
100
|
}
|
|
27
101
|
},
|
|
28
|
-
persist:
|
|
102
|
+
persist: {
|
|
103
|
+
key: "IpLocation",
|
|
104
|
+
storage: typeof window !== "undefined" ? localStorage : void 0,
|
|
105
|
+
paths: ["location"]
|
|
106
|
+
}
|
|
29
107
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export declare const useServiceStore: import("pinia").StoreDefinition<"ServiceStore", {
|
|
2
2
|
services: never[];
|
|
3
3
|
availability: boolean;
|
|
4
|
+
loading: boolean;
|
|
5
|
+
error: string | null;
|
|
4
6
|
}, {}, {
|
|
5
7
|
getAllServices(): Promise<void>;
|
|
6
8
|
checkAvailability(data: any): Promise<any>;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { defineStore } from "pinia";
|
|
2
|
+
import { flowrixApi } from "../middleware/flowrix.js";
|
|
3
|
+
import { useRuntimeConfig } from "#imports";
|
|
2
4
|
import { useCheckoutStore } from "./Checkout.js";
|
|
3
5
|
export const useServiceStore = defineStore("ServiceStore", {
|
|
4
6
|
state: () => ({
|
|
5
7
|
services: [],
|
|
6
|
-
availability: false
|
|
8
|
+
availability: false,
|
|
9
|
+
loading: false,
|
|
10
|
+
error: null
|
|
7
11
|
}),
|
|
8
12
|
actions: {
|
|
9
13
|
async getAllServices() {
|
|
@@ -25,24 +29,36 @@ export const useServiceStore = defineStore("ServiceStore", {
|
|
|
25
29
|
}
|
|
26
30
|
},
|
|
27
31
|
async checkAvailability(data) {
|
|
32
|
+
this.loading = true;
|
|
33
|
+
this.error = null;
|
|
28
34
|
try {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
const config = useRuntimeConfig();
|
|
36
|
+
let rawCookies = "";
|
|
37
|
+
if (process.client) {
|
|
38
|
+
rawCookies = document.cookie || "";
|
|
39
|
+
}
|
|
40
|
+
const apiConfig = {
|
|
41
|
+
...config,
|
|
42
|
+
cookies: rawCookies
|
|
43
|
+
};
|
|
44
|
+
const apiUrl = "services/availability";
|
|
45
|
+
const response = await flowrixApi.post(apiUrl, apiConfig, {
|
|
35
46
|
body: data
|
|
36
47
|
});
|
|
37
|
-
if (response.status
|
|
48
|
+
if (response.status === "Success") {
|
|
38
49
|
this.availability = response.data.service;
|
|
39
50
|
useCheckoutStore().saveToCheckoutSession(data);
|
|
40
51
|
return response.data;
|
|
41
52
|
} else {
|
|
42
|
-
this.
|
|
53
|
+
this.error = response.message || "Failed to check availability";
|
|
54
|
+
return null;
|
|
43
55
|
}
|
|
44
56
|
} catch (error) {
|
|
45
|
-
console.error("Error
|
|
57
|
+
console.error("Error checking availability:", error);
|
|
58
|
+
this.error = error.message || "Failed to check availability";
|
|
59
|
+
return null;
|
|
60
|
+
} finally {
|
|
61
|
+
this.loading = false;
|
|
46
62
|
}
|
|
47
63
|
}
|
|
48
64
|
}
|