ordering-ui-react-native 0.22.65 → 0.22.66-release

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/package.json +5 -7
  2. package/src/DeliveryApp.tsx +1 -1
  3. package/src/components/BusinessesListing/index.tsx +1 -1
  4. package/src/components/Checkout/index.tsx +40 -39
  5. package/src/components/VerifyPhone/styles.tsx +1 -2
  6. package/src/context/OfflineActions/index.tsx +236 -0
  7. package/src/providers/AlertProvider.tsx +3 -1
  8. package/themes/business/src/components/AcceptOrRejectOrder/index.tsx +5 -3
  9. package/themes/business/src/components/AcceptOrRejectOrder/styles.tsx +1 -0
  10. package/themes/business/src/components/BusinessController/index.tsx +8 -3
  11. package/themes/business/src/components/BusinessProductList/index.tsx +3 -2
  12. package/themes/business/src/components/Chat/index.tsx +15 -3
  13. package/themes/business/src/components/DriverMap/index.tsx +44 -33
  14. package/themes/business/src/components/FloatingButton/index.tsx +3 -2
  15. package/themes/business/src/components/LanguageSelector/index.tsx +1 -1
  16. package/themes/business/src/components/LoginForm/index.tsx +123 -98
  17. package/themes/business/src/components/LogoutButton/index.tsx +13 -4
  18. package/themes/business/src/components/MapView/RenderMarker.tsx +146 -0
  19. package/themes/business/src/components/MapView/index.tsx +68 -142
  20. package/themes/business/src/components/NewOrderNotification/index.tsx +38 -54
  21. package/themes/business/src/components/OrderDetails/Business.tsx +56 -20
  22. package/themes/business/src/components/OrderDetails/Delivery.tsx +123 -54
  23. package/themes/business/src/components/OrderDetails/OrderContentComponent.tsx +146 -36
  24. package/themes/business/src/components/OrderDetails/OrderHeaderComponent.tsx +51 -28
  25. package/themes/business/src/components/OrderDetails/styles.tsx +39 -3
  26. package/themes/business/src/components/OrderDetails/usePrinterCommands.tsx +17 -16
  27. package/themes/business/src/components/OrderDetailsLogistic/index.tsx +3 -2
  28. package/themes/business/src/components/OrderSummary/index.tsx +271 -176
  29. package/themes/business/src/components/OrdersListManager/index.tsx +13 -1
  30. package/themes/business/src/components/OrdersOption/index.tsx +342 -228
  31. package/themes/business/src/components/OrdersOption/styles.tsx +14 -0
  32. package/themes/business/src/components/PreviousMessages/index.tsx +26 -3
  33. package/themes/business/src/components/PreviousOrders/OrderItem.tsx +30 -18
  34. package/themes/business/src/components/PreviousOrders/index.tsx +74 -66
  35. package/themes/business/src/components/PreviousOrders/styles.tsx +2 -1
  36. package/themes/business/src/components/PrinterEdition/MessageAlert.tsx +33 -0
  37. package/themes/business/src/components/PrinterEdition/index.tsx +143 -75
  38. package/themes/business/src/components/PrinterEdition/printerList.tsx +23 -0
  39. package/themes/business/src/components/PrinterSettings/index.tsx +1 -1
  40. package/themes/business/src/components/ProductItemAccordion/index.tsx +3 -2
  41. package/themes/business/src/components/ReviewCustomer/index.tsx +2 -0
  42. package/themes/business/src/components/StoresList/index.tsx +2 -2
  43. package/themes/business/src/components/UserProfileForm/index.tsx +48 -10
  44. package/themes/business/src/components/UserProfileForm/styles.tsx +7 -0
  45. package/themes/business/src/components/WebsocketStatus/index.tsx +2 -2
  46. package/themes/business/src/config/currency.tsx +1010 -0
  47. package/themes/business/src/hooks/useLocation.tsx +16 -12
  48. package/themes/business/src/layouts/SafeAreaContainer.tsx +35 -19
  49. package/themes/business/src/types/index.tsx +33 -7
  50. package/themes/business/src/utils/index.tsx +28 -3
  51. package/themes/doordash/src/components/BusinessesListing/index.tsx +1 -1
  52. package/themes/doordash/src/components/LoginForm/index.tsx +1 -2
  53. package/themes/instacart/src/components/BusinessesListing/index.tsx +1 -1
  54. package/themes/kiosk/src/components/Checkout/index.tsx +9 -5
  55. package/themes/kiosk/src/components/CustomerName/index.tsx +1 -1
  56. package/themes/kiosk/src/components/NavBar/index.tsx +14 -14
  57. package/themes/kiosk/src/components/OptionCard/index.tsx +1 -1
  58. package/themes/kiosk/src/components/OrderTypeCardSelector/index.tsx +8 -10
  59. package/themes/kiosk/src/components/PaymentOptions/index.tsx +121 -57
  60. package/themes/kiosk/src/components/shared/OButton.tsx +5 -18
  61. package/themes/original/index.tsx +223 -219
  62. package/themes/original/src/components/AddressForm/index.tsx +922 -888
  63. package/themes/original/src/components/AppleLogin/index.tsx +4 -4
  64. package/themes/original/src/components/BusinessBasicInformation/index.tsx +1 -1
  65. package/themes/original/src/components/BusinessController/index.tsx +4 -2
  66. package/themes/original/src/components/BusinessItemAccordion/index.tsx +10 -4
  67. package/themes/original/src/components/BusinessListingSearch/BusinessSearchFooter.tsx +103 -92
  68. package/themes/original/src/components/BusinessListingSearch/BusinessSearchHeader.tsx +7 -3
  69. package/themes/original/src/components/BusinessListingSearch/index.tsx +8 -13
  70. package/themes/original/src/components/BusinessPreorder/index.tsx +30 -17
  71. package/themes/original/src/components/BusinessProductsList/SubcategoriesComponent/index.tsx +73 -70
  72. package/themes/original/src/components/BusinessProductsList/index.tsx +6 -7
  73. package/themes/original/src/components/BusinessProductsList/styles.tsx +0 -3
  74. package/themes/original/src/components/BusinessProductsListing/index.tsx +5 -4
  75. package/themes/original/src/components/BusinessTypeFilter/index.tsx +9 -8
  76. package/themes/original/src/components/BusinessesListing/Layout/Appointment/index.tsx +2 -1
  77. package/themes/original/src/components/BusinessesListing/Layout/Original/styles.tsx +1 -1
  78. package/themes/original/src/components/BusinessesListing/index.tsx +0 -1
  79. package/themes/original/src/components/Cart/index.tsx +44 -12
  80. package/themes/original/src/components/CartContent/index.tsx +0 -2
  81. package/themes/original/src/components/Checkout/index.tsx +125 -97
  82. package/themes/original/src/components/Favorite/index.tsx +1 -5
  83. package/themes/original/src/components/ForgotPasswordForm/index.tsx +1 -2
  84. package/themes/original/src/components/GPSButton/index.tsx +2 -1
  85. package/themes/original/src/components/GoogleMap/index.tsx +5 -4
  86. package/themes/original/src/components/Help/functions.tsx +76 -0
  87. package/themes/original/src/components/Help/index.tsx +74 -29
  88. package/themes/original/src/components/Help/styles.tsx +4 -1
  89. package/themes/original/src/components/HelpOptions/index.tsx +53 -0
  90. package/themes/original/src/components/HighestRatedBusinesses/index.tsx +1 -1
  91. package/themes/original/src/components/Home/index.tsx +37 -12
  92. package/themes/original/src/components/LastOrder/index.tsx +1 -1
  93. package/themes/original/src/components/LoginForm/index.tsx +11 -5
  94. package/themes/original/src/components/MessageListing/index.tsx +1 -1
  95. package/themes/original/src/components/Messages/index.tsx +20 -13
  96. package/themes/original/src/components/MomentOption/TimeListItem.tsx +56 -0
  97. package/themes/original/src/components/MomentOption/index.tsx +67 -49
  98. package/themes/original/src/components/MomentSelector/index.tsx +5 -2
  99. package/themes/original/src/components/MultiCheckout/index.tsx +78 -36
  100. package/themes/original/src/components/MultiOrdersDetails/SingleOrderCard.tsx +2 -2
  101. package/themes/original/src/components/MultiOrdersDetails/index.tsx +2 -2
  102. package/themes/original/src/components/MyOrders/index.tsx +2 -2
  103. package/themes/original/src/components/NavBar/index.tsx +6 -2
  104. package/themes/original/src/components/NotFoundSource/index.tsx +40 -39
  105. package/themes/original/src/components/NotFoundSource/styles.tsx +18 -9
  106. package/themes/original/src/components/OrderDetails/OrderEta.tsx +4 -3
  107. package/themes/original/src/components/OrderDetails/OrderHistory.tsx +11 -4
  108. package/themes/original/src/components/OrderDetails/index.tsx +43 -19
  109. package/themes/original/src/components/OrderDetails/styles.tsx +0 -1
  110. package/themes/original/src/components/OrderProgress/index.tsx +4 -3
  111. package/themes/original/src/components/OrderSummary/index.tsx +32 -11
  112. package/themes/original/src/components/OrderTypeSelector/index.tsx +4 -3
  113. package/themes/original/src/components/OrdersOption/index.tsx +3 -6
  114. package/themes/original/src/components/PaymentOptionWallet/index.tsx +56 -56
  115. package/themes/original/src/components/PaymentOptions/index.tsx +471 -459
  116. package/themes/original/src/components/PhoneInputNumber/index.tsx +92 -7
  117. package/themes/original/src/components/ProductForm/ActionButton.tsx +6 -10
  118. package/themes/original/src/components/ProductItemAccordion/index.tsx +28 -37
  119. package/themes/original/src/components/ProductOptionSubOption/index.tsx +15 -14
  120. package/themes/original/src/components/ServiceForm/index.tsx +1 -1
  121. package/themes/original/src/components/SignupForm/index.tsx +40 -24
  122. package/themes/original/src/components/SingleOrderCard/index.tsx +8 -5
  123. package/themes/original/src/components/SingleProductCard/index.tsx +3 -4
  124. package/themes/original/src/components/SingleProductCard/styles.tsx +0 -3
  125. package/themes/original/src/components/StripeCardsList/index.tsx +9 -4
  126. package/themes/original/src/components/StripeElementsForm/index.tsx +2 -2
  127. package/themes/original/src/components/StripeElementsForm/naked.tsx +1 -1
  128. package/themes/original/src/components/TaxInformation/index.tsx +3 -2
  129. package/themes/original/src/components/UpsellingProducts/UpsellingContent.tsx +7 -2
  130. package/themes/original/src/components/UserDetails/index.tsx +17 -16
  131. package/themes/original/src/components/UserFormDetails/index.tsx +109 -67
  132. package/themes/original/src/components/UserVerification/index.tsx +18 -5
  133. package/themes/original/src/components/shared/OButton.tsx +2 -2
  134. package/themes/original/src/components/shared/OInput.tsx +4 -8
  135. package/themes/original/src/components/shared/OModal.tsx +7 -2
  136. package/themes/original/src/types/index.tsx +700 -693
  137. package/themes/original/src/utils/index.tsx +29 -0
  138. package/themes/uber-eats/src/components/BusinessesListing/index.tsx +1 -1
  139. package/themes/original/src/components/HelpAccountAndPayment/index.tsx +0 -62
  140. package/themes/original/src/components/HelpAccountAndPayment/styles.tsx +0 -12
  141. package/themes/original/src/components/HelpGuide/index.tsx +0 -68
  142. package/themes/original/src/components/HelpGuide/styles.tsx +0 -12
  143. package/themes/original/src/components/HelpOrder/index.tsx +0 -71
  144. package/themes/original/src/components/HelpOrder/styles.tsx +0 -13
@@ -1,20 +1,20 @@
1
1
  import React, { useEffect, useState, useRef } from 'react';
2
2
  import {
3
- StyleSheet,
4
- View,
5
- TouchableOpacity,
6
- Keyboard,
7
- TouchableWithoutFeedback,
8
- Platform,
3
+ StyleSheet,
4
+ View,
5
+ TouchableOpacity,
6
+ Keyboard,
7
+ TouchableWithoutFeedback,
8
+ Platform,
9
9
  } from 'react-native';
10
10
  import {
11
- AddressForm as AddressFormController,
12
- useLanguage,
13
- useConfig,
14
- useSession,
15
- useOrder,
16
- useToast,
17
- ToastType
11
+ AddressForm as AddressFormController,
12
+ useLanguage,
13
+ useConfig,
14
+ useSession,
15
+ useOrder,
16
+ useToast,
17
+ ToastType
18
18
  } from 'ordering-components/native';
19
19
  import { DeviceOrientationMethods } from '../../../../../src/hooks/DeviceOrientation'
20
20
 
@@ -31,11 +31,11 @@ import { GoogleMap } from '../GoogleMap';
31
31
  import NavBar from '../NavBar';
32
32
 
33
33
  import {
34
- AddressFormContainer,
35
- AutocompleteInput,
36
- IconsContainer,
37
- GoogleMapContainer,
38
- FormInput,
34
+ AddressFormContainer,
35
+ AutocompleteInput,
36
+ IconsContainer,
37
+ GoogleMapContainer,
38
+ FormInput,
39
39
  } from './styles';
40
40
  import { GPSButton } from '../GPSButton';
41
41
  import { ScrollView } from 'react-native-gesture-handler';
@@ -43,880 +43,914 @@ import { ScrollView } from 'react-native-gesture-handler';
43
43
  const { useDeviceOrientation } = DeviceOrientationMethods
44
44
 
45
45
  const inputNames = [
46
- { name: 'address', code: 'Address' },
47
- { name: 'internal_number', code: 'Internal number' },
48
- { name: 'zipcode', code: 'Zipcode' },
49
- { name: 'address_notes', code: 'Address notes' },
46
+ { name: 'address', code: 'Address' },
47
+ { name: 'internal_number', code: 'Internal number' },
48
+ { name: 'zipcode', code: 'Zipcode' },
49
+ { name: 'address_notes', code: 'Address notes' },
50
50
  ];
51
51
 
52
52
  const AddressFormUI = (props: AddressFormParams) => {
53
- const {
54
- navigation,
55
- updateChanges,
56
- address,
57
- formState,
58
- isEditing,
59
- handleChangeInput,
60
- addressState,
61
- addressesList,
62
- saveAddress,
63
- isGuestUser,
64
- isRequiredField,
65
- showField,
66
- isFromProductsList,
67
- hasAddressDefault,
68
- afterSignup,
69
- businessSlug,
70
- isFromCheckout,
71
- onNavigationRedirect
72
- } = props;
73
-
74
- const theme = useTheme();
75
- const [orientationState] = useDeviceOrientation();
76
-
77
- const [autoCompleteInputFocused, setAutoCompleteInputFocused] = useState(false)
78
-
79
- const tagsName = [
80
- { icon: theme.images.general.tag_home, value: 'home' },
81
- { icon: theme.images.general.tag_building, value: 'office' },
82
- { icon: theme.images.general.tag_heart, value: 'favorite' },
83
- { icon: theme.images.general.tag_plus, value: 'other' },
84
- ];
85
-
86
- const HEIGHT_SCREEN = orientationState?.dimensions?.height
87
-
88
- const styles = StyleSheet.create({
89
- iconContainer: {
90
- display: 'flex',
91
- justifyContent: 'center',
92
- alignItems: 'center',
93
- paddingHorizontal: 18,
94
- paddingVertical: 5,
95
- backgroundColor: theme.colors.clear
96
- },
97
- inputsStyle: {
98
- borderRadius: 10,
99
- marginBottom: 20,
100
- height: 50,
101
- maxHeight: 50,
102
- minHeight: 50,
103
- flex: 1,
104
- },
105
- textAreaStyles: {
106
- borderRadius: 10,
107
- marginBottom: 20,
108
- height: 104,
109
- maxHeight: 104,
110
- minHeight: 104,
111
- textAlignVertical: 'top',
112
- alignItems: 'flex-start',
113
- },
114
- pinIcon: {
115
- position: 'absolute',
116
- end: 0,
117
- top: 12,
118
- zIndex: 1002,
119
- right: autoCompleteInputFocused && (
120
- !!address?.address ||
121
- !!formState.changes?.address ||
122
- !!addressState.address.address
123
- ) ? 30 : 15,
124
- width: 16
125
- },
126
- wrapperNavbar: Platform.OS === 'ios'
127
- ? { paddingVertical: 0, paddingLeft: 20, paddingRight: 20 }
128
- : { paddingVertical: 10, paddingLeft: 20, paddingRight: 20 }
129
- });
130
-
131
- const [, t] = useLanguage();
132
- const [{ auth }] = useSession();
133
- const [, { showToast }] = useToast();
134
- const [configState] = useConfig();
135
- const [orderState] = useOrder();
136
- const { handleSubmit, errors, control, setValue } = useForm();
137
- const [autoCompleteAddress, setAutoCompleteAddress] = useState(false)
138
-
139
- const [toggleMap, setToggleMap] = useState(false);
140
- const [alertState, setAlertState] = useState<{
141
- open: boolean;
142
- content: Array<string>;
143
- key?: string | null;
144
- }>({ open: false, content: [], key: null });
145
- const [addressTag, setAddressTag] = useState(addressState?.address?.tag);
146
- const [firstLocationNoEdit, setFirstLocationNoEdit] = useState({
147
- value: { lat: null, lng: null },
148
- address: null,
149
- });
150
- const [isFirstTime, setIsFirstTime] = useState(true);
151
- const [locationChange, setLocationChange] = useState(
152
- isEditing
153
- ? addressState?.address?.location
154
- : formState.changes?.location ?? null,
155
- );
156
- const [saveMapLocation, setSaveMapLocation] = useState(false);
157
- const [isKeyboardShow, setIsKeyboardShow] = useState(false);
158
- const [isSignUpEffect, setIsSignUpEffect] = useState(false);
159
- const [hasEditing, setAddressEditing] = useState(false);
160
-
161
- const googleInput: any = useRef(null);
162
- const internalNumberRef: any = useRef(null);
163
- const zipCodeRef: any = useRef(null);
164
- const addressNotesRef: any = useRef(null);
165
-
166
- const googleMapsApiKey = configState?.configs?.google_maps_api_key?.value;
167
- const isLocationRequired =
168
- configState.configs?.google_autocomplete_selection_required?.value ===
169
- '1' ||
170
- configState.configs?.google_autocomplete_selection_required?.value ===
171
- 'true';
172
- const maxLimitLocation =
173
- configState?.configs?.meters_to_change_address?.value;
174
- const countryCode = configState?.configs?.country_autocomplete?.value
175
- const isHideMap = theme?.address?.components?.map?.hidden
176
- const isHideIcons = theme?.address?.components?.icons?.hidden
177
- const continueAsGuest = () => navigation.navigate(!!businessSlug ? 'Business' : 'BusinessList', { isGuestUser: true });
178
- const unaddressedTypes = configState?.configs?.unaddressed_order_types_allowed?.value.split('|').map((value: any) => Number(value)) || []
179
- const isAllowUnaddressOrderType = unaddressedTypes.includes(orderState?.options?.type)
180
- const goToBack = () => navigation?.canGoBack() && navigation.goBack();
181
-
182
- const getAddressFormatted = (address: any) => {
183
- const data: any = { address: null, error: null };
184
- Geocoder.from(address)
185
- .then((json) => {
186
- if (json.results && json.results?.length > 0) {
187
- let postalCode = null;
188
- for (const component of json.results?.[0].address_components) {
189
- const addressType = component.types?.[0];
190
- if (addressType === 'postal_code') {
191
- postalCode = component.short_name;
192
- break;
193
- }
194
- }
195
- data.address = {
196
- address,
197
- location: json.results[0].geometry.location,
198
- map_data: {
199
- library: 'google',
200
- place_id: json.results?.[0].place_id,
201
- },
202
- };
203
- if (postalCode) {
204
- data.address.zipcode = postalCode;
205
- }
206
-
207
- const arrayList = isEditing
208
- ? addressesList?.addresses?.filter(
209
- (address: any) => address.id !== addressState?.address?.id,
210
- ) || []
211
- : addressesList || [];
212
- const addressToCompare = isEditing
213
- ? { ...addressState.address, ...data.address, ...formState.changes }
214
- : { ...data.address, ...formState?.changes };
215
-
216
- const isAddressAlreadyExist =
217
- arrayList
218
- .map((address: any) => checkAddress(address, addressToCompare))
219
- .some((value: any) => value) ?? false;
220
-
221
- if (!isAddressAlreadyExist) {
222
- saveAddress(data.address);
223
- if (isGuestUser) {
224
- continueAsGuest();
225
- return;
226
- }
227
- if (!isGuestUser && !auth) {
228
- !isFromProductsList
229
- ? navigation.navigate('Business')
230
- : navigation?.canGoBack() && navigation.goBack();
231
- }
232
- return;
233
- }
234
-
235
- setAlertState({
236
- open: true,
237
- content: [t('ADDRESS_ALREADY_EXIST', 'The address already exists')],
238
- });
239
- } else {
240
- setAlertState({
241
- open: true,
242
- content: [t('ERROR_NOT_FOUND_ADDRESS', 'Error, address not found')],
243
- });
244
- }
245
- })
246
- .catch((error) => {
247
- setAlertState({
248
- open: true,
249
- content: [error?.message || error?.toString()],
250
- });
251
- });
252
- };
253
-
254
- const onSubmit = () => {
255
- if (
256
- !auth &&
257
- formState?.changes?.address === '' &&
258
- addressState?.address?.address
259
- ) {
260
- setAlertState({
261
- open: true,
262
- content: [
263
- t(
264
- 'VALIDATION_ERROR_ADDRESS_REQUIRED',
265
- 'The field Address is required',
266
- ),
267
- ],
268
- });
269
- return;
270
- }
271
- if (formState?.changes?.address && !formState?.changes?.location) {
272
- if (isLocationRequired) {
273
- setAlertState({
274
- open: true,
275
- content: [
276
- t(
277
- 'ADDRESS_REQUIRE_LOCATION',
278
- 'The address needs a location, please select one of the suggested',
279
- ),
280
- ],
281
- });
282
- return;
283
- }
284
- getAddressFormatted(formState?.changes?.address);
285
- return;
286
- }
287
-
288
- const arrayList = isEditing
289
- ? addressesList?.addresses?.filter(
290
- (address: any) => address.id !== addressState?.address?.id,
291
- ) || []
292
- : addressesList || [];
293
- const addressToCompare = isEditing
294
- ? { ...addressState.address, ...formState.changes }
295
- : formState?.changes;
296
-
297
- const isAddressAlreadyExist =
298
- arrayList
299
- .map((address: any) => checkAddress(address, addressToCompare))
300
- .some((value: any) => value) ?? false;
301
-
302
- if (!isAddressAlreadyExist) {
303
- saveAddress();
304
- if (isGuestUser) {
305
- continueAsGuest();
306
- }
307
- if (!isGuestUser && !auth && !afterSignup) {
308
- !isFromProductsList
309
- ? navigation.navigate('Business')
310
- : navigation?.canGoBack() && navigation.goBack();
311
- }
312
- return;
313
- }
314
-
315
- setAlertState({
316
- open: true,
317
- content: [t('ADDRESS_ALREADY_EXIST', 'The address already exists')],
318
- });
319
- };
320
-
321
- /**
322
- * Returns true when the user made no changes
323
- * @param {object} address
324
- */
325
- const checkAddress = (address: any, addressToCompare: any) => {
326
- const props = [
327
- 'address',
328
- 'address_notes',
329
- 'zipcode',
330
- 'location',
331
- 'internal_number',
332
- ];
333
- const values: any = [];
334
- props.forEach((prop) => {
335
- if (addressToCompare[prop]) {
336
- if (prop === 'location') {
337
- values.push(
338
- address[prop]?.lat === addressToCompare[prop]?.lat &&
339
- address[prop]?.lng === addressToCompare[prop]?.lng,
340
- );
341
- } else {
342
- values.push(address[prop] === addressToCompare[prop]);
343
- }
344
- } else {
345
- values.push(!address[prop]);
346
- }
347
- });
348
- return values.every((value: any) => value);
349
- };
350
-
351
- const handleChangeAddress = (data: any, details: any) => {
352
- const addressSelected = {
353
- address: data?.description || data?.address,
354
- location: details?.geometry?.location,
355
- utc_offset: details?.utc_offset || null,
356
- map_data: { library: 'google', place_id: data.place_id },
357
- zip_code: data?.zip_code || null,
358
- };
359
- if (googleInput?.current) {
360
- googleInput?.current?.setAddressText(addressSelected.address);
361
- }
362
- updateChanges(addressSelected);
363
- };
364
-
365
- const handleAddressTag = (tag: string) => {
366
- setAddressTag(tag);
367
- handleChangeInput({
368
- target: {
369
- name: 'tag',
370
- value: tag,
371
- },
372
- });
373
- };
374
-
375
- const handleToggleMap = () => {
376
- setToggleMap(!toggleMap);
377
- };
378
-
379
- const showFieldWithTheme = (name) => {
380
- return !theme?.address?.components?.[name]?.hidden
381
- }
382
-
383
- useEffect(() => {
384
- if (
385
- orderState.loading &&
386
- !addressesList &&
387
- orderState?.options?.address &&
388
- auth &&
389
- !afterSignup
390
- ) {
391
- isFromCheckout
392
- ? navigation.goBack()
393
- : !isFromProductsList
394
- ? navigation.navigate('BottomTab')
395
- : navigation.navigate('Business');
396
- }
397
- }, [orderState?.options?.address]);
398
-
399
- useEffect(() => {
400
- if (alertState.open && alertState?.key !== 'ERROR_MAX_LIMIT_LOCATION') {
401
- alertState.content && showToast(ToastType.Error, alertState.content);
402
- }
403
- }, [alertState.content]);
404
-
405
- useEffect(() => {
406
- if (!auth) {
407
- inputNames.forEach((field) =>
408
- setValue(
409
- field.name,
410
- formState?.changes[field.name] ||
411
- (orderState?.options?.address &&
412
- orderState?.options?.address[field.name]) ||
413
- '',
414
- ),
415
- );
416
- return;
417
- }
418
-
419
- if (!formState.loading && formState.result?.error) {
420
- setAlertState({
421
- open: true,
422
- content: formState.result?.result || [t('ERROR', 'Error')],
423
- });
424
- }
425
-
426
- setValue(
427
- 'address',
428
- formState?.changes?.address ?? addressState.address?.address ?? '',
429
- );
430
- if (!isEditing) {
431
- formState?.changes?.address &&
432
- setLocationChange(formState?.changes?.location);
433
- if (
434
- formState?.changes?.address &&
435
- formState?.changes?.address !== firstLocationNoEdit?.address &&
436
- formState?.changes?.location &&
437
- formState?.changes?.location?.lat !== firstLocationNoEdit.value?.lat &&
438
- formState?.changes?.location?.lng !== firstLocationNoEdit.value?.lng
439
- ) {
440
- setFirstLocationNoEdit({
441
- value: formState?.changes?.location,
442
- address: formState?.changes?.address,
443
- });
444
- }
445
- }
446
-
447
- if (isEditing) {
448
- if (formState?.changes?.location) {
449
- const prevLocation = {
450
- lat: locationChange?.lat?.toFixed(5),
451
- lng: locationChange?.lng?.toFixed(5),
452
- };
453
- const newLocation = {
454
- lat: formState?.changes?.location?.lat?.toFixed(5),
455
- lng: formState?.changes?.location?.lng?.toFixed(5),
456
- };
457
- if (
458
- prevLocation?.lat !== newLocation?.lat &&
459
- prevLocation?.lng !== newLocation?.lng
460
- ) {
461
- setLocationChange(formState?.changes?.location);
462
- }
463
- }
464
- }
465
- }, [formState]);
466
-
467
- useEffect(() => {
468
- if (formState?.result?.result && !formState?.loading) {
469
- if (formState.result?.error) {
470
- showToast(ToastType.Error, formState.result.result);
471
- } else {
472
- showToast(
473
- ToastType.Success,
474
- t('UPDATE_SUCCESSFULLY', 'Update successfully'),
475
- );
476
- }
477
- }
478
- }, [formState.result]);
479
-
480
- useEffect(() => {
481
- if (Object.keys(errors).length > 0) {
482
- // Convert all errors in one string to show in toast provider
483
- const list = Object.values(errors);
484
- let stringError = '';
485
- list.map((item: any, i: number) => {
486
- stringError +=
487
- i + 1 === list.length ? `- ${item.message}` : `- ${item.message}\n`;
488
- });
489
- showToast(ToastType.Error, stringError);
490
- }
491
- }, [errors]);
492
-
493
- useEffect(() => {
494
- if (googleInput?.current) {
495
- googleInput?.current?.setAddressText(
496
- address?.address ||
497
- formState.changes?.address ||
498
- addressState.address.address ||
499
- '',
500
- );
501
- }
502
- }, []);
503
-
504
- useEffect(() => {
505
- Geocoder.init(googleMapsApiKey);
506
- }, [googleMapsApiKey]);
507
-
508
- useEffect(() => {
509
- const keyboardDidShowListener = Keyboard.addListener(
510
- 'keyboardDidShow',
511
- () => {
512
- setIsKeyboardShow(true);
513
- },
514
- );
515
- const keyboardDidHideListener = Keyboard.addListener(
516
- 'keyboardDidHide',
517
- () => {
518
- setIsKeyboardShow(false);
519
- },
520
- );
521
- return () => {
522
- keyboardDidShowListener.remove();
523
- keyboardDidHideListener.remove();
524
- };
525
- }, []);
526
-
527
- useEffect(() => {
528
- if (!orderState.loading && auth && !hasAddressDefault && isSignUpEffect) {
529
- navigation.navigate('BottomTab');
530
- }
531
- setIsSignUpEffect(true);
532
- }, [orderState.loading]);
533
-
534
- useEffect(() => {
535
- if (isAllowUnaddressOrderType) {
536
- onNavigationRedirect && onNavigationRedirect(!!businessSlug ? 'Business' : 'BusinessList')
537
- }
538
- }, [isAllowUnaddressOrderType])
539
-
540
- return (
541
- <ScrollView
542
- keyboardShouldPersistTaps='always'
543
- listViewDisplayed={false}
544
- >
545
- <View style={styles.wrapperNavbar}>
546
- <NavBar
547
- title={t('WHERE_DO_WE_DELIVERY', 'Where do we delivery?')}
548
- titleAlign={'center'}
549
- onActionLeft={goToBack}
550
- showCall={false}
551
- btnStyle={{ paddingLeft: 0, paddingRight: 5 }}
552
- style={{ marginTop: Platform.OS === 'ios' ? 0 : 10 }}
553
- titleWrapStyle={{ paddingHorizontal: 0, width: '100%' }}
554
- titleStyle={{ marginRight: 0, marginLeft: 0, paddingRight: 5 }}
555
- />
556
- </View>
557
- <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
558
- <AddressFormContainer style={{ overflow: 'scroll' }}>
559
- <View>
560
- <FormInput>
561
- <AutocompleteInput>
562
- <Controller
563
- control={control}
564
- name="address"
565
- defaultValue={
566
- address?.address ||
567
- formState.changes?.address ||
568
- addressState.address.address ||
569
- ''
570
- }
571
- rules={{
572
- required:
573
- isRequiredField && isRequiredField('address')
574
- ? t(
575
- `VALIDATION_ERROR_ADDRESS_REQUIRED`,
576
- `The field Address is required`,
577
- )
578
- : null,
579
- }}
580
- render={() => (
581
- <GooglePlacesAutocomplete
582
- placeholder={t('ADD_ADDRESS', 'Add a address')}
583
- onPress={(data, details: any) => {
584
- handleChangeAddress(data, details);
585
- setAutoCompleteAddress(true);
586
- }}
587
- query={{
588
- key: googleMapsApiKey,
589
- components: countryCode && countryCode !== '*' ? `country:${countryCode}` : ''
590
- }}
591
- fetchDetails
592
- ref={googleInput}
593
- textInputProps={{
594
- onChangeText: (text) => {
595
- if (!isFirstTime) {
596
- handleChangeInput({
597
- target: { name: 'address', value: text },
598
- });
599
- setValue('address', text);
600
- setAutoCompleteAddress(true)
601
- }
602
- setIsFirstTime(false);
603
- setAddressEditing(text.length == 0);
604
- },
605
- onSubmitEditing: () =>
606
- internalNumberRef?.current?.focus?.(),
607
- autoCorrect: false,
608
- blurOnSubmit: false,
609
- returnKeyType: 'next',
610
- onFocus: () => setAutoCompleteInputFocused(true),
611
- onBlur: () => setAutoCompleteInputFocused(false)
612
- }}
613
- onFail={(error) =>
614
- setAlertState({
615
- open: true,
616
- content: getTraduction(error),
617
- })
618
- }
619
- styles={{
620
- listView: {
621
- position: 'relative',
622
- borderBottomStartRadius: 15,
623
- borderBottomEndRadius: 15,
624
- elevation: 2,
625
- borderWidth: 1,
626
- borderColor: '#ddd',
627
- bottom: 20,
628
- },
629
- container: {
630
- zIndex: 100,
631
- },
632
- textInput: {
633
- borderWidth: 1,
634
- borderRadius: 7.6,
635
- borderColor: autoCompleteInputFocused ? theme.colors.primary : theme.colors.border,
636
- flexGrow: 1,
637
- fontSize: 15,
638
- paddingLeft: 16,
639
- paddingRight: 32,
640
- minHeight: 50,
641
- fontFamily: 'Poppins-Regular',
642
- marginBottom: 24
643
- },
644
- }}
645
- />
646
- )}
647
- />
648
-
649
- {(
650
- ((!isEditing && !isGuestUser) ||
651
- (isEditing && !isGuestUser)) ||
652
- (isGuestUser)) &&
653
- (
654
- <View style={styles.pinIcon}>
655
- <GPSButton
656
- apiKey={googleMapsApiKey}
657
- handleGPS={(data: any, detail: any) => {
658
- handleChangeAddress(data, detail);
659
- setValue('address', data.address);
660
- if (googleInput?.current) {
661
- googleInput?.current?.setAddressText(data.address);
662
- }
663
- }}
664
- IconButton={<OIcon src={theme.images.general.pin} width={16} />}
665
- isIntGeoCoder
666
- />
667
- </View>
668
- )}
669
- </AutocompleteInput>
670
-
671
- {!isHideMap && (locationChange || formState.changes?.location) && (
672
- <View
673
- style={{
674
- height: 189,
675
- borderRadius: 7.6,
676
- overflow: 'hidden',
677
- marginBottom: 24,
678
- }}>
679
- <GoogleMapContainer>
680
- <GoogleMap
681
- location={locationChange || formState.changes?.location}
682
- handleChangeAddressMap={handleChangeAddress}
683
- maxLimitLocation={maxLimitLocation}
684
- saveLocation={saveMapLocation}
685
- setSaveLocation={setSaveMapLocation}
686
- handleToggleMap={handleToggleMap}
687
- isIntGeoCoder
688
- setAutoCompleteAddress={setAutoCompleteAddress}
689
- autoCompleteAddress={autoCompleteAddress}
690
- />
691
- </GoogleMapContainer>
692
- </View>
693
- )}
694
-
695
- <View style={{ flexDirection: 'row', flexBasis: '50%' }}>
696
- {((isRequiredField && isRequiredField('internal_number')) || showFieldWithTheme('internal_number')) && (
697
- <Controller
698
- control={control}
699
- name="internal_number"
700
- rules={{
701
- required:
702
- isRequiredField && isRequiredField('internal_number')
703
- ? t(
704
- `VALIDATION_ERROR_INTERNAL_NUMBER_REQUIRED`,
705
- `The field internal number is required`,
706
- )
707
- : null,
708
- }}
709
- defaultValue={
710
- address?.internal_number ||
711
- formState.changes?.internal_number ||
712
- addressState?.address?.internal_number ||
713
- ''
714
- }
715
- render={() => (
716
- <OInput
717
- name="internal_number"
718
- placeholder={t('INTERNAL_NUMBER', 'Internal number')}
719
- onChange={(text: string) => {
720
- handleChangeInput(text);
721
- setValue('internal_number', text);
722
- }}
723
- value={
724
- address?.internal_number ||
725
- formState.changes?.internal_number ||
726
- addressState?.address?.internal_number ||
727
- ''
728
- }
729
- isFocusHighlight
730
- style={{
731
- ...styles.inputsStyle,
732
- marginRight: showField?.('internal_number') && showField?.('zipcode') ? 24 : 0
733
- }}
734
- forwardRef={internalNumberRef}
735
- returnKeyType="next"
736
- onSubmitEditing={() => zipCodeRef?.current?.focus?.()}
737
- blurOnSubmit={false}
738
- />
739
- )}
740
- />
741
- )}
742
-
743
- {((isRequiredField && isRequiredField('zipcode')) || showFieldWithTheme('zipcode')) && (
744
- <Controller
745
- control={control}
746
- name="zipcode"
747
- rules={{
748
- required:
749
- isRequiredField && isRequiredField('zipcode')
750
- ? t(
751
- `VALIDATION_ERROR_ZIP_CODE_REQUIRED`,
752
- `The field Zip Code is required`,
753
- )
754
- : null,
755
- }}
756
- defaultValue={
757
- address?.zipcode ||
758
- formState.changes?.zipcode ||
759
- addressState.address.zipcode ||
760
- ''
761
- }
762
- render={() => (
763
- <OInput
764
- name="zipcode"
765
- placeholder={t('ZIP_CODE', 'Zip code')}
766
- onChange={(text: string) => {
767
- handleChangeInput(text);
768
- setValue('zipcode', text);
769
- }}
770
- value={
771
- address?.zipcode ||
772
- formState.changes?.zipcode ||
773
- addressState.address.zipcode ||
774
- ''
775
- }
776
- isFocusHighlight
777
- style={styles.inputsStyle}
778
- forwardRef={zipCodeRef}
779
- returnKeyType="next"
780
- onSubmitEditing={() => addressNotesRef?.current?.focus?.()}
781
- blurOnSubmit={false}
782
- />
783
- )}
784
- />
785
- )}
786
- </View>
787
-
788
- {((isRequiredField && isRequiredField('address_notes')) || showFieldWithTheme('address_notes')) && (
789
- <Controller
790
- control={control}
791
- name="address_notes"
792
- rules={{
793
- required:
794
- isRequiredField && isRequiredField('address_notes')
795
- ? t(
796
- `VALIDATION_ERROR_ADDRESS_NOTES_REQUIRED`,
797
- `The field address notes is required`,
798
- )
799
- : null,
800
- }}
801
- defaultValue={
802
- address?.address_notes ||
803
- formState.changes?.address_notes ||
804
- addressState.address.address_notes ||
805
- ''
806
- }
807
- render={() => (
808
- <OInput
809
- name="address_notes"
810
- placeholder={t('ADDRESS_NOTES', 'Address notes')}
811
- onChange={(text: any) => {
812
- handleChangeInput(text);
813
- setValue('address_notes', text);
814
- }}
815
- value={
816
- address?.address_notes ||
817
- formState.changes?.address_notes ||
818
- addressState.address.address_notes ||
819
- ''
820
- }
821
- multiline
822
- isFocusHighlight
823
- style={styles.textAreaStyles}
824
- returnKeyType="done"
825
- forwardRef={addressNotesRef}
826
- blurOnSubmit
827
- />
828
- )}
829
- />
830
- )}
831
- </FormInput>
832
- {!isHideIcons && (
833
- <IconsContainer>
834
- {tagsName.map((tag) => (
835
- <TouchableOpacity
836
- key={tag.value}
837
- onPress={() => handleAddressTag(tag.value)}>
838
- <View
839
- style={{
840
- ...styles.iconContainer,
841
- }}>
842
- <OIcon
843
- src={tag.icon}
844
- width={24}
845
- color={
846
- addressTag === tag.value ?
847
- theme.colors.primary : theme.colors.disabled
848
- }
849
- />
850
- </View>
851
- </TouchableOpacity>
852
- ))}
853
- </IconsContainer>
854
- )}
855
- </View>
856
-
857
- <View>
858
- {Object.keys(formState?.changes).length > 0 ? (
859
- <OButton
860
- text={
861
- !formState.loading
862
- ? isEditing ||
863
- (!auth && orderState.options?.address?.address)
864
- ? t('UPDATE', 'Update')
865
- : t('CONTINUE', 'Continue')
866
- : t('LOADING', 'Loading')
867
- }
868
- onClick={handleSubmit(onSubmit)}
869
- isDisabled={formState.loading}
870
- style={{ borderRadius: 7.6, shadowOpacity: 0 }}
871
- showNextIcon
872
- />
873
- ) : (
874
- <OButton
875
- text={t('CANCEL', 'Cancel')}
876
- bgColor={theme.colors.secundary}
877
- borderColor={theme.colors.secundary}
878
- textStyle={{ color: theme.colors.black }}
879
- style={{ borderRadius: 7.6, borderWidth: 1, shadowOpacity: 0 }}
880
- onClick={() => navigation?.canGoBack() && navigation.goBack()}
881
- />
882
- )}
883
- </View>
884
- <OModal
885
- open={toggleMap}
886
- onClose={() => handleToggleMap()}
887
- entireModal
888
- customClose>
889
- {(locationChange || formState.changes?.location) && (
890
- <GoogleMapContainer>
891
- <GoogleMap
892
- location={locationChange || formState.changes?.location}
893
- handleChangeAddressMap={handleChangeAddress}
894
- maxLimitLocation={maxLimitLocation}
895
- saveLocation={saveMapLocation}
896
- setSaveLocation={setSaveMapLocation}
897
- handleToggleMap={handleToggleMap}
898
- isIntGeoCoder
899
- />
900
- </GoogleMapContainer>
901
- )}
902
- <OButton
903
- text={t('SAVE', 'Save')}
904
- imgRightSrc={null}
905
- style={{ marginHorizontal: 30, marginBottom: 10 }}
906
- onClick={() => setSaveMapLocation(true)}
907
- />
908
- </OModal>
909
- <Spinner visible={saveMapLocation} />
910
- </AddressFormContainer>
911
- </TouchableWithoutFeedback>
912
- </ScrollView>
913
- );
53
+ const {
54
+ navigation,
55
+ updateChanges,
56
+ address,
57
+ formState,
58
+ isEditing,
59
+ handleChangeInput,
60
+ addressState,
61
+ addressesList,
62
+ saveAddress,
63
+ isGuestUser,
64
+ isRequiredField,
65
+ showField,
66
+ isFromProductsList,
67
+ hasAddressDefault,
68
+ afterSignup,
69
+ businessSlug,
70
+ isFromCheckout,
71
+ onNavigationRedirect
72
+ } = props;
73
+
74
+ const theme = useTheme();
75
+ const [orientationState] = useDeviceOrientation();
76
+
77
+ const [autoCompleteInputFocused, setAutoCompleteInputFocused] = useState(false)
78
+
79
+ const isEmptyField = (key: any, str: any) => isRequiredField && isRequiredField(key) && (!str || str.trim().length === 0)
80
+
81
+ const tagsName = [
82
+ { icon: theme.images.general.tag_home, value: 'home' },
83
+ { icon: theme.images.general.tag_building, value: 'office' },
84
+ { icon: theme.images.general.tag_heart, value: 'favorite' },
85
+ { icon: theme.images.general.tag_plus, value: 'other' },
86
+ ];
87
+
88
+ const HEIGHT_SCREEN = orientationState?.dimensions?.height
89
+
90
+ const styles = StyleSheet.create({
91
+ iconContainer: {
92
+ display: 'flex',
93
+ justifyContent: 'center',
94
+ alignItems: 'center',
95
+ paddingHorizontal: 18,
96
+ paddingVertical: 5,
97
+ backgroundColor: theme.colors.clear
98
+ },
99
+ inputsStyle: {
100
+ borderRadius: 10,
101
+ marginBottom: 20,
102
+ height: 50,
103
+ maxHeight: 50,
104
+ minHeight: 50,
105
+ flex: 1,
106
+ },
107
+ textAreaStyles: {
108
+ borderRadius: 10,
109
+ marginBottom: 20,
110
+ height: 104,
111
+ maxHeight: 104,
112
+ minHeight: 104,
113
+ textAlignVertical: 'top',
114
+ alignItems: 'flex-start',
115
+ },
116
+ pinIcon: {
117
+ position: 'absolute',
118
+ end: 0,
119
+ top: 12,
120
+ zIndex: 1002,
121
+ right: autoCompleteInputFocused && (
122
+ !!address?.address ||
123
+ !!formState.changes?.address ||
124
+ !!addressState.address.address
125
+ ) ? 30 : 15,
126
+ width: 16
127
+ },
128
+ wrapperNavbar: Platform.OS === 'ios'
129
+ ? { paddingVertical: 0, paddingLeft: 20, paddingRight: 20 }
130
+ : { paddingVertical: 10, paddingLeft: 20, paddingRight: 20 }
131
+ });
132
+
133
+ const [, t] = useLanguage();
134
+ const [{ auth }] = useSession();
135
+ const [, { showToast }] = useToast();
136
+ const [configState] = useConfig();
137
+ const [orderState] = useOrder();
138
+ const { handleSubmit, errors, control, setValue } = useForm();
139
+ const [autoCompleteAddress, setAutoCompleteAddress] = useState(false)
140
+
141
+ const [toggleMap, setToggleMap] = useState(false);
142
+ const [alertState, setAlertState] = useState<{
143
+ open: boolean;
144
+ content: Array<string>;
145
+ key?: string | null;
146
+ }>({ open: false, content: [], key: null });
147
+ const [addressTag, setAddressTag] = useState(addressState?.address?.tag);
148
+ const [firstLocationNoEdit, setFirstLocationNoEdit] = useState({
149
+ value: { lat: null, lng: null },
150
+ address: null,
151
+ });
152
+ const [isFirstTime, setIsFirstTime] = useState(true);
153
+ const [locationChange, setLocationChange] = useState(
154
+ isEditing
155
+ ? addressState?.address?.location
156
+ : formState.changes?.location ?? null,
157
+ );
158
+ const [saveMapLocation, setSaveMapLocation] = useState(false);
159
+ const [isKeyboardShow, setIsKeyboardShow] = useState(false);
160
+ const [isSignUpEffect, setIsSignUpEffect] = useState(false);
161
+ const [hasEditing, setAddressEditing] = useState(false);
162
+
163
+ const googleInput: any = useRef(null);
164
+ const internalNumberRef: any = useRef(null);
165
+ const zipCodeRef: any = useRef(null);
166
+ const addressNotesRef: any = useRef(null);
167
+
168
+ const googleMapsApiKey = configState?.configs?.google_maps_api_key?.value;
169
+ const isLocationRequired =
170
+ configState.configs?.google_autocomplete_selection_required?.value ===
171
+ '1' ||
172
+ configState.configs?.google_autocomplete_selection_required?.value ===
173
+ 'true';
174
+ const maxLimitLocation =
175
+ configState?.configs?.meters_to_change_address?.value;
176
+ const countryCode = configState?.configs?.country_autocomplete?.value
177
+ const isHideMap = theme?.address?.components?.map?.hidden
178
+ const isHideIcons = theme?.address?.components?.icons?.hidden
179
+ const continueAsGuest = () => navigation.navigate(!!businessSlug ? 'Business' : 'BusinessList', { isGuestUser: true });
180
+ const unaddressedTypes = configState?.configs?.unaddressed_order_types_allowed?.value.split('|').map((value: any) => Number(value)) || []
181
+ const isAllowUnaddressOrderType = unaddressedTypes.includes(orderState?.options?.type)
182
+ const goToBack = () => navigation?.canGoBack() && navigation.goBack();
183
+
184
+ const getAddressFormatted = (address: any) => {
185
+ const data: any = { address: null, error: null };
186
+ Geocoder.from(address)
187
+ .then((json) => {
188
+ if (json.results && json.results?.length > 0) {
189
+ let postalCode = null;
190
+ for (const component of json.results?.[0].address_components) {
191
+ const addressType = component.types?.[0];
192
+ if (addressType === 'postal_code') {
193
+ postalCode = component.short_name;
194
+ break;
195
+ }
196
+ }
197
+ data.address = {
198
+ address,
199
+ location: json.results[0].geometry.location,
200
+ map_data: {
201
+ library: 'google',
202
+ place_id: json.results?.[0].place_id,
203
+ },
204
+ };
205
+ if (postalCode) {
206
+ data.address.zipcode = postalCode;
207
+ }
208
+
209
+ const arrayList = isEditing
210
+ ? addressesList?.addresses?.filter(
211
+ (address: any) => address.id !== addressState?.address?.id,
212
+ ) || []
213
+ : addressesList || [];
214
+ const addressToCompare = isEditing
215
+ ? { ...addressState.address, ...data.address, ...formState.changes }
216
+ : { ...data.address, ...formState?.changes };
217
+
218
+ const isAddressAlreadyExist =
219
+ arrayList
220
+ .map((address: any) => checkAddress(address, addressToCompare))
221
+ .some((value: any) => value) ?? false;
222
+
223
+ if (!isAddressAlreadyExist) {
224
+ saveAddress(data.address);
225
+ if (isGuestUser) {
226
+ continueAsGuest();
227
+ return;
228
+ }
229
+ if (!isGuestUser && !auth) {
230
+ !isFromProductsList
231
+ ? navigation.navigate('Business')
232
+ : navigation?.canGoBack() && navigation.goBack();
233
+ }
234
+ return;
235
+ }
236
+
237
+ setAlertState({
238
+ open: true,
239
+ content: [t('ADDRESS_ALREADY_EXIST', 'The address already exists')],
240
+ });
241
+ } else {
242
+ setAlertState({
243
+ open: true,
244
+ content: [t('ERROR_NOT_FOUND_ADDRESS', 'Error, address not found')],
245
+ });
246
+ }
247
+ })
248
+ .catch((error) => {
249
+ setAlertState({
250
+ open: true,
251
+ content: [error?.message || error?.toString()],
252
+ });
253
+ });
254
+ };
255
+
256
+ const onSubmit = () => {
257
+ if (
258
+ !auth &&
259
+ formState?.changes?.address === '' &&
260
+ addressState?.address?.address
261
+ ) {
262
+ setAlertState({
263
+ open: true,
264
+ content: [
265
+ t(
266
+ 'VALIDATION_ERROR_ADDRESS_REQUIRED',
267
+ 'The field Address is required',
268
+ ),
269
+ ],
270
+ });
271
+ return;
272
+ }
273
+ if (formState?.changes?.address && !formState?.changes?.location) {
274
+ if (isLocationRequired) {
275
+ setAlertState({
276
+ open: true,
277
+ content: [
278
+ t(
279
+ 'ADDRESS_REQUIRE_LOCATION',
280
+ 'The address needs a location, please select one of the suggested',
281
+ ),
282
+ ],
283
+ });
284
+ return;
285
+ }
286
+ getAddressFormatted(formState?.changes?.address);
287
+ return;
288
+ }
289
+
290
+ const arrayList = isEditing
291
+ ? addressesList?.addresses?.filter(
292
+ (address: any) => address.id !== addressState?.address?.id,
293
+ ) || []
294
+ : addressesList || [];
295
+ const addressToCompare = isEditing
296
+ ? { ...addressState.address, ...formState.changes }
297
+ : formState?.changes;
298
+
299
+ const isAddressAlreadyExist =
300
+ arrayList
301
+ .map((address: any) => checkAddress(address, addressToCompare))
302
+ .some((value: any) => value) ?? false;
303
+
304
+ if (!isAddressAlreadyExist) {
305
+ saveAddress();
306
+ if (isGuestUser) {
307
+ continueAsGuest();
308
+ }
309
+ if (!isGuestUser && !auth && !afterSignup) {
310
+ !isFromProductsList
311
+ ? navigation.navigate('Business')
312
+ : navigation?.canGoBack() && navigation.goBack();
313
+ }
314
+ return;
315
+ }
316
+
317
+ setAlertState({
318
+ open: true,
319
+ content: [t('ADDRESS_ALREADY_EXIST', 'The address already exists')],
320
+ });
321
+ };
322
+
323
+ /**
324
+ * Returns true when the user made no changes
325
+ * @param {object} address
326
+ */
327
+ const checkAddress = (address: any, addressToCompare: any) => {
328
+ const props = [
329
+ 'address',
330
+ 'address_notes',
331
+ 'zipcode',
332
+ 'location',
333
+ 'internal_number',
334
+ ];
335
+ const values: any = [];
336
+ props.forEach((prop) => {
337
+ if (addressToCompare[prop]) {
338
+ if (prop === 'location') {
339
+ values.push(
340
+ address[prop]?.lat === addressToCompare[prop]?.lat &&
341
+ address[prop]?.lng === addressToCompare[prop]?.lng,
342
+ );
343
+ } else {
344
+ values.push(address[prop] === addressToCompare[prop]);
345
+ }
346
+ } else {
347
+ values.push(!address[prop]);
348
+ }
349
+ });
350
+ return values.every((value: any) => value);
351
+ };
352
+
353
+ const handleChangeAddress = (data: any, details: any) => {
354
+ const addressObj: any = {}
355
+ if (details?.address_components) {
356
+ details.address_components.map((component: any) => {
357
+ const addressType = component.types[0]
358
+ if (addressType === 'postal_code') {
359
+ addressObj.zipcode = component.short_name
360
+ }
361
+ if (addressType === 'street_number') {
362
+ addressObj.street_number = component.long_name
363
+ }
364
+ if (addressType === 'neighborhood') {
365
+ addressObj.neighborhood = component.long_name
366
+ }
367
+ if (addressType === 'route') {
368
+ addressObj.route = component.short_name
369
+ }
370
+ if (addressType === 'locality') {
371
+ addressObj.locality = component.long_name
372
+ }
373
+ if (component.types?.includes('sublocality')) {
374
+ addressObj.sublocality = component.long_name
375
+ }
376
+ if (addressType === 'country') {
377
+ addressObj.country = component.long_name
378
+ addressObj.country_code = component.short_name
379
+ }
380
+ if (addressType === 'administrative_area_level_1') {
381
+ addressObj.state = component.long_name
382
+ }
383
+ if (addressType === 'administrative_area_level_2') {
384
+ addressObj.city = component.long_name
385
+ }
386
+ })
387
+ }
388
+ const addressSelected = {
389
+ address: data?.description || data?.address,
390
+ location: details?.geometry?.location,
391
+ utc_offset: details?.utc_offset || null,
392
+ map_data: { library: 'google', place_id: data.place_id },
393
+ zipcode: data?.zipcode || null,
394
+ ...addressObj
395
+ };
396
+ updateChanges(addressSelected);
397
+ };
398
+
399
+ const handleAddressTag = (tag: string) => {
400
+ setAddressTag(tag);
401
+ handleChangeInput({
402
+ target: {
403
+ name: 'tag',
404
+ value: tag,
405
+ },
406
+ });
407
+ };
408
+
409
+ const handleToggleMap = () => {
410
+ setToggleMap(!toggleMap);
411
+ };
412
+
413
+ const showFieldWithTheme = (name) => {
414
+ return !theme?.address?.components?.[name]?.hidden
415
+ }
416
+
417
+ useEffect(() => {
418
+ if (
419
+ orderState.loading &&
420
+ !addressesList &&
421
+ orderState?.options?.address &&
422
+ auth &&
423
+ !afterSignup
424
+ ) {
425
+ isFromCheckout
426
+ ? navigation.goBack()
427
+ : !isFromProductsList
428
+ ? navigation.navigate('BottomTab')
429
+ : navigation.navigate('Business');
430
+ }
431
+ }, [orderState?.options?.address]);
432
+
433
+ useEffect(() => {
434
+ if (alertState.open && alertState?.key !== 'ERROR_MAX_LIMIT_LOCATION') {
435
+ alertState.content && showToast(ToastType.Error, alertState.content);
436
+ }
437
+ }, [alertState.content]);
438
+
439
+ useEffect(() => {
440
+ if (!auth) {
441
+ inputNames.forEach((field) =>
442
+ setValue(
443
+ field.name,
444
+ formState?.changes[field.name] ||
445
+ (orderState?.options?.address &&
446
+ orderState?.options?.address[field.name]) ||
447
+ '',
448
+ ),
449
+ );
450
+ return;
451
+ }
452
+
453
+ if (!formState.loading && formState.result?.error) {
454
+ setAlertState({
455
+ open: true,
456
+ content: formState.result?.result || [t('ERROR', 'Error')],
457
+ });
458
+ }
459
+
460
+ setValue(
461
+ 'address',
462
+ formState?.changes?.address ?? addressState.address?.address ?? '',
463
+ );
464
+ if (!isEditing) {
465
+ formState?.changes?.address &&
466
+ setLocationChange(formState?.changes?.location);
467
+ if (
468
+ formState?.changes?.address &&
469
+ formState?.changes?.address !== firstLocationNoEdit?.address &&
470
+ formState?.changes?.location &&
471
+ formState?.changes?.location?.lat !== firstLocationNoEdit.value?.lat &&
472
+ formState?.changes?.location?.lng !== firstLocationNoEdit.value?.lng
473
+ ) {
474
+ setFirstLocationNoEdit({
475
+ value: formState?.changes?.location,
476
+ address: formState?.changes?.address,
477
+ });
478
+ }
479
+ }
480
+
481
+ if (isEditing) {
482
+ if (formState?.changes?.location) {
483
+ const prevLocation = {
484
+ lat: locationChange?.lat?.toFixed(5),
485
+ lng: locationChange?.lng?.toFixed(5),
486
+ };
487
+ const newLocation = {
488
+ lat: formState?.changes?.location?.lat?.toFixed(5),
489
+ lng: formState?.changes?.location?.lng?.toFixed(5),
490
+ };
491
+ if (
492
+ prevLocation?.lat !== newLocation?.lat &&
493
+ prevLocation?.lng !== newLocation?.lng
494
+ ) {
495
+ setLocationChange(formState?.changes?.location);
496
+ }
497
+ }
498
+ }
499
+ }, [formState]);
500
+
501
+ useEffect(() => {
502
+ if (formState?.result && !formState?.loading) {
503
+ if (formState?.error) {
504
+ showToast(ToastType.Error, formState.error[0]);
505
+ } else {
506
+ showToast(
507
+ ToastType.Success,
508
+ t('UPDATE_SUCCESSFULLY', 'Update successfully'),
509
+ );
510
+ }
511
+ }
512
+ }, [formState.result]);
513
+
514
+ useEffect(() => {
515
+ if (Object.keys(errors).length > 0) {
516
+ // Convert all errors in one string to show in toast provider
517
+ const list = Object.values(errors);
518
+ let stringError = '';
519
+ list.map((item: any, i: number) => {
520
+ stringError +=
521
+ i + 1 === list.length ? `- ${item.message}` : `- ${item.message}\n`;
522
+ });
523
+ showToast(ToastType.Error, stringError);
524
+ }
525
+ }, [errors]);
526
+
527
+ useEffect(() => {
528
+ if (googleInput?.current) {
529
+ googleInput?.current?.setAddressText(
530
+ address?.address ||
531
+ formState.changes?.address ||
532
+ addressState.address.address ||
533
+ '',
534
+ );
535
+ }
536
+ }, []);
537
+
538
+ useEffect(() => {
539
+ Geocoder.init(googleMapsApiKey);
540
+ }, [googleMapsApiKey]);
541
+
542
+ useEffect(() => {
543
+ const keyboardDidShowListener = Keyboard.addListener(
544
+ 'keyboardDidShow',
545
+ () => {
546
+ setIsKeyboardShow(true);
547
+ },
548
+ );
549
+ const keyboardDidHideListener = Keyboard.addListener(
550
+ 'keyboardDidHide',
551
+ () => {
552
+ setIsKeyboardShow(false);
553
+ },
554
+ );
555
+ return () => {
556
+ keyboardDidShowListener.remove();
557
+ keyboardDidHideListener.remove();
558
+ };
559
+ }, []);
560
+
561
+ useEffect(() => {
562
+ if (!orderState.loading && auth && !hasAddressDefault && isSignUpEffect) {
563
+ navigation.navigate('BottomTab');
564
+ }
565
+ setIsSignUpEffect(true);
566
+ }, [orderState.loading]);
567
+
568
+ useEffect(() => {
569
+ if (isAllowUnaddressOrderType) {
570
+ onNavigationRedirect && onNavigationRedirect(!!businessSlug ? 'Business' : 'BusinessList')
571
+ }
572
+ }, [isAllowUnaddressOrderType])
573
+
574
+ return (
575
+ <ScrollView
576
+ keyboardShouldPersistTaps='always'
577
+ listViewDisplayed={false}
578
+ >
579
+ <View style={styles.wrapperNavbar}>
580
+ <NavBar
581
+ title={t('WHERE_DO_WE_DELIVERY', 'Where do we delivery?')}
582
+ titleAlign={'center'}
583
+ onActionLeft={goToBack}
584
+ showCall={false}
585
+ btnStyle={{ paddingLeft: 0, paddingRight: 5 }}
586
+ style={{ marginTop: Platform.OS === 'ios' ? 0 : 10 }}
587
+ titleWrapStyle={{ paddingHorizontal: 0, width: '100%' }}
588
+ titleStyle={{ marginRight: 0, marginLeft: 0, paddingRight: 5 }}
589
+ />
590
+ </View>
591
+ <TouchableWithoutFeedback onPress={Keyboard.dismiss}>
592
+ <AddressFormContainer style={{ overflow: 'scroll' }}>
593
+ <View>
594
+ <FormInput>
595
+ <AutocompleteInput>
596
+ <Controller
597
+ control={control}
598
+ name="address"
599
+ defaultValue={
600
+ address?.address ||
601
+ formState.changes?.address ||
602
+ addressState.address.address ||
603
+ ''
604
+ }
605
+ rules={{
606
+ required:
607
+ isRequiredField && isRequiredField('address')
608
+ ? t(
609
+ `VALIDATION_ERROR_ADDRESS_REQUIRED`,
610
+ `The field Address is required`,
611
+ )
612
+ : null,
613
+ }}
614
+ render={() => (
615
+ <GooglePlacesAutocomplete
616
+ placeholder={t('ADD_ADDRESS', 'Add a address')}
617
+ onPress={(data, details: any) => {
618
+ handleChangeAddress(data, details);
619
+ setAutoCompleteAddress(true);
620
+ }}
621
+ query={{
622
+ key: googleMapsApiKey,
623
+ components: countryCode && countryCode !== '*' ? `country:${countryCode}` : ''
624
+ }}
625
+ fetchDetails
626
+ ref={googleInput}
627
+ textInputProps={{
628
+ onChangeText: (text) => {
629
+ if (!isFirstTime) {
630
+ handleChangeInput({
631
+ target: { name: 'address', value: text },
632
+ });
633
+ setValue('address', text);
634
+ setAutoCompleteAddress(true)
635
+ }
636
+ setIsFirstTime(false);
637
+ setAddressEditing(text.length == 0);
638
+ },
639
+ onSubmitEditing: () =>
640
+ internalNumberRef?.current?.focus?.(),
641
+ autoCorrect: false,
642
+ blurOnSubmit: false,
643
+ returnKeyType: 'next',
644
+ onFocus: () => setAutoCompleteInputFocused(true),
645
+ onBlur: () => setAutoCompleteInputFocused(false)
646
+ }}
647
+ onFail={(error) =>
648
+ setAlertState({
649
+ open: true,
650
+ content: getTraduction(error, t),
651
+ })
652
+ }
653
+ styles={{
654
+ listView: {
655
+ position: 'relative',
656
+ borderBottomStartRadius: 15,
657
+ borderBottomEndRadius: 15,
658
+ elevation: 2,
659
+ borderWidth: 1,
660
+ borderColor: '#ddd',
661
+ bottom: 20,
662
+ },
663
+ container: {
664
+ zIndex: 100,
665
+ },
666
+ textInput: {
667
+ borderWidth: 1,
668
+ borderRadius: 7.6,
669
+ borderColor: autoCompleteInputFocused ? theme.colors.primary : theme.colors.border,
670
+ flexGrow: 1,
671
+ fontSize: 15,
672
+ paddingLeft: 16,
673
+ paddingRight: 32,
674
+ minHeight: 50,
675
+ fontFamily: 'Poppins-Regular',
676
+ marginBottom: 24
677
+ },
678
+ }}
679
+ />
680
+ )}
681
+ />
682
+
683
+ {(
684
+ ((!isEditing && !isGuestUser) ||
685
+ (isEditing && !isGuestUser)) ||
686
+ (isGuestUser)) &&
687
+ (
688
+ <View style={styles.pinIcon}>
689
+ <GPSButton
690
+ apiKey={googleMapsApiKey}
691
+ handleGPS={(data: any, detail: any) => {
692
+ handleChangeAddress(data, detail);
693
+ setValue('address', data.address);
694
+ if (googleInput?.current) {
695
+ googleInput?.current?.setAddressText(data.address);
696
+ }
697
+ }}
698
+ IconButton={<OIcon src={theme.images.general.pin} width={16} />}
699
+ isIntGeoCoder
700
+ />
701
+ </View>
702
+ )}
703
+ </AutocompleteInput>
704
+
705
+ {!isHideMap && (locationChange || formState.changes?.location) && (
706
+ <View
707
+ style={{
708
+ height: 189,
709
+ borderRadius: 7.6,
710
+ overflow: 'hidden',
711
+ marginBottom: 24,
712
+ }}>
713
+ <GoogleMapContainer>
714
+ <GoogleMap
715
+ location={locationChange || formState.changes?.location}
716
+ handleChangeAddressMap={handleChangeAddress}
717
+ maxLimitLocation={maxLimitLocation}
718
+ saveLocation={saveMapLocation}
719
+ setSaveLocation={setSaveMapLocation}
720
+ handleToggleMap={handleToggleMap}
721
+ isIntGeoCoder
722
+ setAutoCompleteAddress={setAutoCompleteAddress}
723
+ autoCompleteAddress={autoCompleteAddress}
724
+ />
725
+ </GoogleMapContainer>
726
+ </View>
727
+ )}
728
+
729
+ <View style={{ flexDirection: 'row', flexBasis: '50%' }}>
730
+ {showField?.('internal_number') && ((isRequiredField && isRequiredField('internal_number')) || showFieldWithTheme('internal_number')) && (
731
+ <Controller
732
+ control={control}
733
+ name="internal_number"
734
+ rules={{
735
+ required:
736
+ isRequiredField && isRequiredField('internal_number')
737
+ ? t(
738
+ `VALIDATION_ERROR_INTERNAL_NUMBER_REQUIRED`,
739
+ `The field internal number is required`,
740
+ )
741
+ : null,
742
+ }}
743
+ defaultValue={
744
+ address?.internal_number ||
745
+ formState.changes?.internal_number ||
746
+ addressState?.address?.internal_number ||
747
+ ''
748
+ }
749
+ render={() => (
750
+ <OInput
751
+ name="internal_number"
752
+ placeholder={t('INTERNAL_NUMBER', 'Internal number')}
753
+ onChange={(data: any) => {
754
+ handleChangeInput(data);
755
+ setValue('internal_number', isEmptyField('internal_number', data?.target?.value) ? null : data);
756
+ }}
757
+ value={
758
+ address?.internal_number ||
759
+ formState.changes?.internal_number ||
760
+ addressState?.address?.internal_number ||
761
+ ''
762
+ }
763
+ isFocusHighlight
764
+ style={{
765
+ ...styles.inputsStyle,
766
+ marginRight: showField?.('internal_number') && showField?.('zipcode') ? 24 : 0
767
+ }}
768
+ forwardRef={internalNumberRef}
769
+ returnKeyType="next"
770
+ onSubmitEditing={() => zipCodeRef?.current?.focus?.()}
771
+ blurOnSubmit={false}
772
+ />
773
+ )}
774
+ />
775
+ )}
776
+
777
+ {showField?.('zipcode') && ((isRequiredField && isRequiredField('zipcode')) || showFieldWithTheme('zipcode')) && (
778
+ <Controller
779
+ control={control}
780
+ name="zipcode"
781
+ rules={{
782
+ required:
783
+ isRequiredField && isRequiredField('zipcode')
784
+ ? t(
785
+ `VALIDATION_ERROR_ZIP_CODE_REQUIRED`,
786
+ `The field Zip Code is required`,
787
+ )
788
+ : null,
789
+ }}
790
+ defaultValue={
791
+ address?.zipcode ||
792
+ formState.changes?.zipcode ||
793
+ addressState.address.zipcode ||
794
+ ''
795
+ }
796
+ render={() => (
797
+ <OInput
798
+ name="zipcode"
799
+ placeholder={t('ZIP_CODE', 'Zip code')}
800
+ onChange={(data: any) => {
801
+ handleChangeInput(data);
802
+ setValue('zipcode', isEmptyField('zipcode', data?.target?.value) ? null : data);
803
+ }}
804
+ value={
805
+ address?.zipcode ||
806
+ formState.changes?.zipcode ||
807
+ addressState.address.zipcode ||
808
+ ''
809
+ }
810
+ isFocusHighlight
811
+ style={styles.inputsStyle}
812
+ forwardRef={zipCodeRef}
813
+ returnKeyType="next"
814
+ onSubmitEditing={() => addressNotesRef?.current?.focus?.()}
815
+ blurOnSubmit={false}
816
+ />
817
+ )}
818
+ />
819
+ )}
820
+ </View>
821
+
822
+ {showField?.('address_notes') && ((isRequiredField && isRequiredField('address_notes')) || showFieldWithTheme('address_notes')) && (
823
+ <Controller
824
+ control={control}
825
+ name="address_notes"
826
+ rules={{
827
+ required:
828
+ isRequiredField && isRequiredField('address_notes')
829
+ ? t(
830
+ `VALIDATION_ERROR_ADDRESS_NOTES_REQUIRED`,
831
+ `The field address notes is required`,
832
+ )
833
+ : null,
834
+ }}
835
+ defaultValue={
836
+ address?.address_notes ||
837
+ formState.changes?.address_notes ||
838
+ addressState.address.address_notes ||
839
+ ''
840
+ }
841
+ render={() => (
842
+ <OInput
843
+ name="address_notes"
844
+ placeholder={t('ADDRESS_NOTES', 'Address notes')}
845
+ onChange={(text: any) => {
846
+ handleChangeInput(text);
847
+ setValue('address_notes', text);
848
+ }}
849
+ value={
850
+ address?.address_notes ||
851
+ formState.changes?.address_notes ||
852
+ addressState.address.address_notes ||
853
+ ''
854
+ }
855
+ multiline
856
+ isFocusHighlight
857
+ style={styles.textAreaStyles}
858
+ returnKeyType="done"
859
+ forwardRef={addressNotesRef}
860
+ blurOnSubmit
861
+ />
862
+ )}
863
+ />
864
+ )}
865
+ </FormInput>
866
+ {!isHideIcons && (
867
+ <IconsContainer>
868
+ {tagsName.map((tag) => (
869
+ <TouchableOpacity
870
+ key={tag.value}
871
+ onPress={() => handleAddressTag(tag.value)}>
872
+ <View
873
+ style={{
874
+ ...styles.iconContainer,
875
+ }}>
876
+ <OIcon
877
+ src={tag.icon}
878
+ width={24}
879
+ color={
880
+ addressTag === tag.value ?
881
+ theme.colors.primary : theme.colors.disabled
882
+ }
883
+ />
884
+ </View>
885
+ </TouchableOpacity>
886
+ ))}
887
+ </IconsContainer>
888
+ )}
889
+ </View>
890
+
891
+ <View>
892
+ {Object.keys(formState?.changes).length > 0 ? (
893
+ <OButton
894
+ text={
895
+ !formState.loading
896
+ ? isEditing ||
897
+ (!auth && orderState.options?.address?.address)
898
+ ? t('UPDATE', 'Update')
899
+ : t('CONTINUE', 'Continue')
900
+ : t('LOADING', 'Loading')
901
+ }
902
+ onClick={handleSubmit(onSubmit)}
903
+ isDisabled={formState.loading}
904
+ style={{ borderRadius: 7.6, shadowOpacity: 0 }}
905
+ showNextIcon
906
+ />
907
+ ) : (
908
+ <OButton
909
+ text={t('CANCEL', 'Cancel')}
910
+ bgColor={theme.colors.secundary}
911
+ borderColor={theme.colors.secundary}
912
+ textStyle={{ color: theme.colors.textNormal }}
913
+ style={{ borderRadius: 7.6, borderWidth: 1, shadowOpacity: 0 }}
914
+ onClick={() => navigation?.canGoBack() && navigation.goBack()}
915
+ />
916
+ )}
917
+ </View>
918
+ <OModal
919
+ open={toggleMap}
920
+ onClose={() => handleToggleMap()}
921
+ entireModal
922
+ customClose>
923
+ {(locationChange || formState.changes?.location) && (
924
+ <GoogleMapContainer>
925
+ <GoogleMap
926
+ location={locationChange || formState.changes?.location}
927
+ handleChangeAddressMap={handleChangeAddress}
928
+ maxLimitLocation={maxLimitLocation}
929
+ saveLocation={saveMapLocation}
930
+ setSaveLocation={setSaveMapLocation}
931
+ handleToggleMap={handleToggleMap}
932
+ isIntGeoCoder
933
+ />
934
+ </GoogleMapContainer>
935
+ )}
936
+ <OButton
937
+ text={t('SAVE', 'Save')}
938
+ imgRightSrc={null}
939
+ style={{ marginHorizontal: 30, marginBottom: 10 }}
940
+ onClick={() => setSaveMapLocation(true)}
941
+ />
942
+ </OModal>
943
+ <Spinner visible={saveMapLocation} />
944
+ </AddressFormContainer>
945
+ </TouchableWithoutFeedback>
946
+ </ScrollView>
947
+ );
914
948
  };
915
949
 
916
950
  export const AddressForm = (props: AddressFormParams) => {
917
- const addressFormProps = {
918
- ...props,
919
- UIComponent: AddressFormUI,
920
- };
921
- return <AddressFormController {...addressFormProps} />;
951
+ const addressFormProps = {
952
+ ...props,
953
+ UIComponent: AddressFormUI,
954
+ };
955
+ return <AddressFormController {...addressFormProps} />;
922
956
  };