@propel-nsl/propel-react-native-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/assets/fonts/Lexend-Black.ttf +0 -0
  2. package/assets/fonts/Lexend-Bold.ttf +0 -0
  3. package/assets/fonts/Lexend-ExtraBold.ttf +0 -0
  4. package/assets/fonts/Lexend-ExtraLight.ttf +0 -0
  5. package/assets/fonts/Lexend-Light.ttf +0 -0
  6. package/assets/fonts/Lexend-Medium.ttf +0 -0
  7. package/assets/fonts/Lexend-Regular.ttf +0 -0
  8. package/assets/fonts/Lexend-SemiBold.ttf +0 -0
  9. package/assets/fonts/Lexend-Thin.ttf +0 -0
  10. package/assets/images/HomeGoods.png +0 -0
  11. package/assets/images/accessories.png +0 -0
  12. package/assets/images/amazon.png +0 -0
  13. package/assets/images/apnaClubLogo.png +0 -0
  14. package/assets/images/apparel.png +0 -0
  15. package/assets/images/backgroundblue.png +0 -0
  16. package/assets/images/bannerCard.png +0 -0
  17. package/assets/images/bottomWave.png +0 -0
  18. package/assets/images/coin_1_1.png +0 -0
  19. package/assets/images/flipkart.png +0 -0
  20. package/assets/images/frame_1171278967.png +0 -0
  21. package/assets/images/gift.png +0 -0
  22. package/assets/images/herobanner.png +0 -0
  23. package/assets/images/hinduPencilsLogo.png +0 -0
  24. package/assets/images/icons/addwhiteicon.png +0 -0
  25. package/assets/images/icons/arrow-back.png +0 -0
  26. package/assets/images/icons/arrowgreen.png +0 -0
  27. package/assets/images/icons/arrowred.png +0 -0
  28. package/assets/images/icons/arrowright.png +0 -0
  29. package/assets/images/icons/arrowup.png +0 -0
  30. package/assets/images/icons/blackarrowdown.png +0 -0
  31. package/assets/images/icons/blackarrowup.png +0 -0
  32. package/assets/images/icons/blackcross.png +0 -0
  33. package/assets/images/icons/call.png +0 -0
  34. package/assets/images/icons/camera.png +0 -0
  35. package/assets/images/icons/cart.png +0 -0
  36. package/assets/images/icons/chat.png +0 -0
  37. package/assets/images/icons/circleblack.png +0 -0
  38. package/assets/images/icons/copy.png +0 -0
  39. package/assets/images/icons/cross.png +0 -0
  40. package/assets/images/icons/delete.png +0 -0
  41. package/assets/images/icons/delivery.png +0 -0
  42. package/assets/images/icons/eVoucher.png +0 -0
  43. package/assets/images/icons/editIcon.png +0 -0
  44. package/assets/images/icons/email.png +0 -0
  45. package/assets/images/icons/eye.png +0 -0
  46. package/assets/images/icons/faq.png +0 -0
  47. package/assets/images/icons/filtericon.png +0 -0
  48. package/assets/images/icons/greyDownArrow.png +0 -0
  49. package/assets/images/icons/help.png +0 -0
  50. package/assets/images/icons/home.png +0 -0
  51. package/assets/images/icons/homeinactive.png +0 -0
  52. package/assets/images/icons/i_blackicon.png +0 -0
  53. package/assets/images/icons/i_icon.png +0 -0
  54. package/assets/images/icons/location.png +0 -0
  55. package/assets/images/icons/logout.png +0 -0
  56. package/assets/images/icons/minus.png +0 -0
  57. package/assets/images/icons/myOrders.png +0 -0
  58. package/assets/images/icons/orders.png +0 -0
  59. package/assets/images/icons/pencillogo.png +0 -0
  60. package/assets/images/icons/pending.png +0 -0
  61. package/assets/images/icons/plus.png +0 -0
  62. package/assets/images/icons/redCross.png +0 -0
  63. package/assets/images/icons/redCrossicon.png +0 -0
  64. package/assets/images/icons/redWarningicon.png +0 -0
  65. package/assets/images/icons/redeem.png +0 -0
  66. package/assets/images/icons/redeemactive.png +0 -0
  67. package/assets/images/icons/redemptionHistory.png +0 -0
  68. package/assets/images/icons/redhelpicon.png +0 -0
  69. package/assets/images/icons/search.png +0 -0
  70. package/assets/images/icons/stopwatch.png +0 -0
  71. package/assets/images/icons/successTick.gif +0 -0
  72. package/assets/images/icons/tick.png +0 -0
  73. package/assets/images/icons/tnc.png +0 -0
  74. package/assets/images/icons/user.png +0 -0
  75. package/assets/images/icons/userredicon.png +0 -0
  76. package/assets/images/icons/wavecorner.png +0 -0
  77. package/assets/images/logo.png +0 -0
  78. package/assets/images/myntra_copy.png +0 -0
  79. package/assets/images/nike.png +0 -0
  80. package/assets/images/oq3p0u0_1.png +0 -0
  81. package/assets/images/profileicon.png +0 -0
  82. package/assets/images/topWave.png +0 -0
  83. package/assets/images/zagglePropelLogo.png +0 -0
  84. package/index.ts +23 -0
  85. package/lib/index.ts +25 -0
  86. package/package.json +70 -0
  87. package/src/PropelSDKScreen.tsx +98 -0
  88. package/src/SDKApp.tsx +540 -0
  89. package/src/bridge/SDKBridge.ts +104 -0
  90. package/src/bridge/index.ts +3 -0
  91. package/src/components/SDKBackHandler.tsx +187 -0
  92. package/src/components/SDKProfile.tsx +87 -0
  93. package/src/components/SDKProfileWrapper.tsx +51 -0
  94. package/src/constants.ts +10 -0
  95. package/src/contexts/SDKContext.tsx +72 -0
  96. package/src/hooks/useSDKBackNavigation.ts +61 -0
  97. package/src/navigation/SDKMainTabNavigator.tsx +315 -0
  98. package/src/navigation/SDKNavigator.tsx +41 -0
  99. package/src/redux/store.ts +32 -0
  100. package/src/screens/Dashboard/index.tsx +531 -0
  101. package/src/screens/Dashboard/styles.ts +512 -0
  102. package/src/screens/DeliveryAddress/index.tsx +221 -0
  103. package/src/screens/DeliveryAddress/styles.ts +122 -0
  104. package/src/screens/E-Vouchers/index.tsx +157 -0
  105. package/src/screens/E-Vouchers/styles.ts +106 -0
  106. package/src/screens/Faq/Faq.constants.ts +164 -0
  107. package/src/screens/Faq/index.tsx +114 -0
  108. package/src/screens/Faq/styles.ts +131 -0
  109. package/src/screens/Help/index.tsx +128 -0
  110. package/src/screens/Help/styles.ts +121 -0
  111. package/src/screens/Login/index.tsx +215 -0
  112. package/src/screens/Login/styles.ts +134 -0
  113. package/src/screens/MyCart/MyCart.constants.ts +13 -0
  114. package/src/screens/MyCart/index.tsx +318 -0
  115. package/src/screens/MyCart/styles.ts +249 -0
  116. package/src/screens/MyOrders/index.tsx +87 -0
  117. package/src/screens/MyOrders/styles.ts +281 -0
  118. package/src/screens/MyProfile/index.tsx +72 -0
  119. package/src/screens/MyProfile/styles.ts +47 -0
  120. package/src/screens/NewDeliveryAddress/index.tsx +360 -0
  121. package/src/screens/NewDeliveryAddress/styles.ts +68 -0
  122. package/src/screens/Onboarding/index.tsx +57 -0
  123. package/src/screens/Onboarding/styles.ts +60 -0
  124. package/src/screens/OrdersDetails/index.tsx +333 -0
  125. package/src/screens/OrdersDetails/styles.ts +262 -0
  126. package/src/screens/OtpVerification/index.tsx +283 -0
  127. package/src/screens/OtpVerification/styles.ts +197 -0
  128. package/src/screens/PaymentMethod/index.tsx +389 -0
  129. package/src/screens/PaymentMethod/styles.ts +246 -0
  130. package/src/screens/PointsLog/index.tsx +286 -0
  131. package/src/screens/PointsLog/styles.ts +156 -0
  132. package/src/screens/ProductDetails/index.tsx +682 -0
  133. package/src/screens/ProductDetails/styles.ts +372 -0
  134. package/src/screens/Profile/index.tsx +368 -0
  135. package/src/screens/Profile/styles.ts +158 -0
  136. package/src/screens/RedemptionHistory/RedemptionHistory.constants.ts +4 -0
  137. package/src/screens/RedemptionHistory/index.tsx +304 -0
  138. package/src/screens/RedemptionHistory/styles.ts +84 -0
  139. package/src/screens/Reedem/index.tsx +345 -0
  140. package/src/screens/Reedem/styles.ts +269 -0
  141. package/src/screens/TnC/TnC.constants.ts +169 -0
  142. package/src/screens/TnC/index.tsx +83 -0
  143. package/src/screens/TnC/styles.ts +88 -0
  144. package/src/screens/TransactionSuccessful/index.tsx +77 -0
  145. package/src/screens/TransactionSuccessful/styles.ts +77 -0
  146. package/src/screens/Verification/index.tsx +58 -0
  147. package/src/screens/Verification/styles.ts +74 -0
  148. package/src/screens/index.ts +23 -0
  149. package/src/types/index.ts +46 -0
  150. package/src-app/components/AmountBreakDownModal/index.tsx +86 -0
  151. package/src-app/components/AmountBreakDownModal/styles.ts +110 -0
  152. package/src-app/components/BottomNavIcons.tsx +125 -0
  153. package/src-app/components/Button.tsx +120 -0
  154. package/src-app/components/Card.tsx +47 -0
  155. package/src-app/components/ConfirmPopup/ConfirmPopup.constants.ts +25 -0
  156. package/src-app/components/ConfirmPopup/index.tsx +48 -0
  157. package/src-app/components/ConfirmPopup/styles.ts +167 -0
  158. package/src-app/components/CustomButton/index.tsx +67 -0
  159. package/src-app/components/CustomButton/styles.ts +44 -0
  160. package/src-app/components/CustomCard/index.tsx +221 -0
  161. package/src-app/components/CustomCard/styles.ts +184 -0
  162. package/src-app/components/CustomError/index.tsx +54 -0
  163. package/src-app/components/CustomError/styles.ts +41 -0
  164. package/src-app/components/CustomImage/index.tsx +37 -0
  165. package/src-app/components/CustomImage/styles.ts +5 -0
  166. package/src-app/components/CustomLoader/index.tsx +45 -0
  167. package/src-app/components/CustomLoader/styles.ts +35 -0
  168. package/src-app/components/CustomMessagePopUp/index.tsx +51 -0
  169. package/src-app/components/CustomMessagePopUp/styles.ts +74 -0
  170. package/src-app/components/CustomProductCard/index.tsx +13 -0
  171. package/src-app/components/CustomProductCard/styles.ts +5 -0
  172. package/src-app/components/FilterModal.tsx +372 -0
  173. package/src-app/components/Footer/index.tsx +23 -0
  174. package/src-app/components/Footer/styles.ts +37 -0
  175. package/src-app/components/Icon.tsx +80 -0
  176. package/src-app/components/Logout/index.tsx +82 -0
  177. package/src-app/components/Logout/styles.ts +116 -0
  178. package/src-app/components/MobileHeader.tsx +141 -0
  179. package/src-app/components/NoDataFound/index.tsx +18 -0
  180. package/src-app/components/NoDataFound/styles.ts +26 -0
  181. package/src-app/components/OTPModal.tsx +747 -0
  182. package/src-app/components/ProfileField.tsx +47 -0
  183. package/src-app/components/QuantityModal/index.tsx +113 -0
  184. package/src-app/components/QuantityModal/styles.ts +84 -0
  185. package/src-app/components/TabBarIcons.tsx +110 -0
  186. package/src-app/components/TextInput.tsx +79 -0
  187. package/src-app/components/ToastConfig.tsx +60 -0
  188. package/src-app/components/index.ts +18 -0
  189. package/src-app/config/env.ts +22 -0
  190. package/src-app/constants/Fonts.ts +12 -0
  191. package/src-app/constants/Formatter.ts +39 -0
  192. package/src-app/constants/HtmlSanitization.ts +46 -0
  193. package/src-app/constants/Images.ts +81 -0
  194. package/src-app/constants/Labels.ts +8 -0
  195. package/src-app/constants/Messages.ts +108 -0
  196. package/src-app/constants/Routes.ts +17 -0
  197. package/src-app/constants/Scaling.ts +5 -0
  198. package/src-app/constants/Text.ts +8 -0
  199. package/src-app/constants/offSets.ts +18 -0
  200. package/src-app/hooks/useAppDispatch.ts +4 -0
  201. package/src-app/hooks/useAppSelector.ts +4 -0
  202. package/src-app/hooks/useBackHandler.ts +47 -0
  203. package/src-app/hooks/useScreenBackHandler.ts +91 -0
  204. package/src-app/navigation/AppNavigator.tsx +34 -0
  205. package/src-app/navigation/MainTabNavigator.tsx +294 -0
  206. package/src-app/redux/authSaga.ts +605 -0
  207. package/src-app/redux/authSlice.ts +754 -0
  208. package/src-app/redux/rootSaga.ts +6 -0
  209. package/src-app/redux/store.ts +25 -0
  210. package/src-app/services/api.ts +14 -0
  211. package/src-app/services/endpoints.ts +33 -0
  212. package/src-app/services/index.ts +574 -0
  213. package/src-app/services/sdkCredentials.ts +44 -0
  214. package/src-app/styles/colors.ts +85 -0
  215. package/src-app/styles/shared.ts +112 -0
  216. package/src-app/types/authTypes.ts +155 -0
  217. package/src-app/types/navigation.ts +99 -0
  218. package/src-app/utils/Validation.ts +48 -0
  219. package/src-app/utils/filterPins.ts +29 -0
  220. package/src-app/utils/navigationUtils.ts +43 -0
  221. package/src-app/utils/useHardwareBack.ts +21 -0
@@ -0,0 +1,682 @@
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ ScrollView,
6
+ TouchableOpacity,
7
+ Image,
8
+ SafeAreaView,
9
+ useWindowDimensions,
10
+ BackHandler,
11
+ } from 'react-native';
12
+ import Svg, { Path } from 'react-native-svg';
13
+ import { useNavigation, useRoute, RouteProp, useFocusEffect } from '@react-navigation/native';
14
+ import { StackNavigationProp } from '@react-navigation/stack';
15
+ import { AppStackParamList } from '../../../src-app/types/navigation';
16
+ import styles from './styles';
17
+ import {
18
+ AmountModal,
19
+ CustomImage,
20
+ CustomLoader,
21
+ CustomMessagePopUp,
22
+ } from '../../../src-app/components';
23
+ import Images from '../../../src-app/constants/Images';
24
+ import { useAppDispatch } from '../../../src-app/hooks/useAppDispatch';
25
+ import {
26
+ addToCartRequest,
27
+ productDetailsByIdRequest,
28
+ dashboardInfoRequest,
29
+ resetAuthState,
30
+ } from '../../../src-app/redux/authSlice';
31
+ import { useAppSelector } from '../../../src-app/hooks/useAppSelector';
32
+ import { RootState } from '../../../src-app/redux/store';
33
+ import RenderHTML from 'react-native-render-html';
34
+ import Toast from 'react-native-toast-message';
35
+ import { FALLBACK_TEXT, handleLinkPress, IGNORED_DOM_TAGS, RENDER_KEYS } from '../../../src-app/utils/filterPins';
36
+ import {
37
+ DAYS,
38
+ DELIVERY_WITHIN,
39
+ DESCRIPTION,
40
+ ERROR,
41
+ NO_VARIATION_SELECTED,
42
+ QUANTITY,
43
+ SELECT_VARIANT,
44
+ REDEMPTION_TERMS,
45
+ SHOPPING_HANDLING_TERMS,
46
+ PRODUCT_DESCRIPTION_NOT_AVAILABLE,
47
+ REDEMPTION_TERMS_NOT_AVAILABLE,
48
+ SHOPPING_HANDLING_TERMS_NOT_AVAILABLE,
49
+ VIEW_DETAILS,
50
+ ADD_TO_CART,
51
+ ORDER_AMOUNT,
52
+ DELIVERY_CHARGES,
53
+ ERROR_TITLE,
54
+ ITEM_ADDED_TO_CART_SUCCESSFULLY,
55
+ SUCCESS
56
+ } from '../../../src-app/constants/Messages';
57
+ import { HTML_SANITIZATION_PATTERNS, CUSTOM_RENDERER_STYLES } from '../../../src-app/constants/HtmlSanitization';
58
+ import { colors } from '../../../src-app/styles/colors';
59
+ import { useHardwareBack } from '../../../src-app/utils/useHardwareBack';
60
+ import { ROUTES } from '../../../src-app/constants/Routes';
61
+ import { handleCustomGoBack } from '../../../src-app/utils/navigationUtils';
62
+ import { isPureText } from '../../../src-app/constants/Text';
63
+
64
+
65
+
66
+ type ProductDetailRouteProp = RouteProp<AppStackParamList, 'ProductDetail'>;
67
+
68
+ const BackIcon: React.FC = () => (
69
+ <Svg width="24" height="24" viewBox="0 0 24 24" fill="none">
70
+ <Path
71
+ d="M18.9987 11.0007H7.13972L10.7694 6.64111C10.9391 6.43691 11.0208 6.17365 10.9964 5.90925C10.972 5.64484 10.8436 5.40095 10.6394 5.23123C10.4352 5.06151 10.1719 4.97985 9.90755 5.00423C9.64314 5.02861 9.39925 5.15702 9.22953 5.36122L4.22998 11.3607C4.19634 11.4084 4.16626 11.4585 4.13999 11.5107C4.13999 11.5607 4.13999 11.5907 4.06999 11.6407C4.02467 11.7553 4.00094 11.8773 4 12.0006C4.00094 12.1239 4.02467 12.2459 4.06999 12.3606C4.06999 12.4106 4.06999 12.4406 4.13999 12.4906C4.16626 12.5427 4.19634 12.5928 4.22998 12.6406L9.22953 18.64C9.32355 18.7529 9.44128 18.8437 9.57435 18.9059C9.70742 18.9681 9.85257 19.0002 9.99946 19C10.2331 19.0005 10.4595 18.9191 10.6394 18.77C10.7407 18.6861 10.8243 18.583 10.8857 18.4666C10.947 18.3503 10.9848 18.223 10.9969 18.0921C11.009 17.9611 10.9951 17.829 10.9561 17.7034C10.917 17.5778 10.8536 17.4612 10.7694 17.3601L7.13972 13.0005H18.9987C19.2639 13.0005 19.5182 12.8952 19.7057 12.7077C19.8932 12.5201 19.9986 12.2658 19.9986 12.0006C19.9986 11.7354 19.8932 11.4811 19.7057 11.2936C19.5182 11.1061 19.2639 11.0007 18.9987 11.0007Z"
72
+ fill={colors.black}
73
+ />
74
+ </Svg>
75
+ );
76
+
77
+ const customRenderers = {
78
+ [RENDER_KEYS.IMG]: ({ TDefaultRenderer, ...props }: any) => (
79
+ <View style={CUSTOM_RENDERER_STYLES.IMAGE_CONTAINER}>
80
+ <TDefaultRenderer {...props} />
81
+ </View>
82
+ ),
83
+
84
+ [RENDER_KEYS.LINK]: ({ TDefaultRenderer, tnode, ...props }: any) => {
85
+ const href = tnode?.attributes?.href;
86
+ const text = tnode?.init?.children?.[0]?.data || href || FALLBACK_TEXT.link;
87
+
88
+ return (
89
+ <Text style={styles.link} onPress={() => handleLinkPress(href)}>
90
+ {text}
91
+ </Text>
92
+ );
93
+ },
94
+ };
95
+
96
+ const MinusIcon: React.FC = () => (
97
+ <Svg width="12" height="2" viewBox="0 0 12 2" fill="none">
98
+ <Path
99
+ d="M10.8462 1.19976H1.15385C0.515152 1.19976 0 0.663955 0 -0.000244141C0 -0.664443 0.515152 -1.20025 1.15385 -1.20025H10.8462C11.4848 -1.20025 12 -0.664443 12 -0.000244141C12 0.663955 11.4848 1.19976 10.8462 1.19976Z"
100
+ fill={colors.onSurfaceVariant}
101
+ />
102
+ </Svg>
103
+ );
104
+
105
+ const PlusIcon: React.FC = () => (
106
+ <Svg width="12" height="12" viewBox="0 0 12 12" fill="none">
107
+ <Path
108
+ d="M6 12C5.549 12 5.184 11.6351 5.184 11.1847V0.815305C5.184 0.364898 5.549 0 6 0C6.45 0 6.815 0.364898 6.815 0.815305V11.1847C6.815 11.6351 6.45 12 6 12Z"
109
+ fill={colors.onSurfaceVariant}
110
+ stroke={colors.onSurfaceVariant}
111
+ strokeWidth="0.48"
112
+ />
113
+ <Path
114
+ d="M11.185 6.81522H0.815305C0.364898 6.81522 0 6.45032 0 5.99991C0 5.54951 0.364898 5.18461 0.815305 5.18461H11.185C11.635 5.18461 12 5.54951 12 5.99991C12 6.45032 11.635 6.81522 11.185 6.81522Z"
115
+ fill={colors.onSurfaceVariant}
116
+ stroke={colors.onSurfaceVariant}
117
+ strokeWidth="0.48"
118
+ />
119
+ </Svg>
120
+ );
121
+
122
+ const ChevronUpIcon: React.FC = () => (
123
+ <Svg width="11" height="7" viewBox="0 0 11 7" fill="none">
124
+ <Path
125
+ fillRule="evenodd"
126
+ clipRule="evenodd"
127
+ d="M10.7803 6.28033C10.4874 6.57322 10.0126 6.57322 9.71967 6.28033L5.75 2.31066L1.78033 6.28033C1.48744 6.57322 1.01256 6.57322 0.719669 6.28033C0.426776 5.98744 0.426776 5.51256 0.719669 5.21967L5.21967 0.71967C5.51256 0.426776 5.98744 0.426776 6.28033 0.71967L10.7803 5.21967C11.0732 5.51256 11.0732 5.98744 10.7803 6.28033Z"
128
+ fill={colors.onSurfaceVariant}
129
+ stroke={colors.onSurfaceVariant}
130
+ strokeWidth="0.2"
131
+ />
132
+ </Svg>
133
+ );
134
+
135
+ const ChevronDownIcon: React.FC = () => (
136
+ <Svg width="11" height="7" viewBox="0 0 11 7" fill="none">
137
+ <Path
138
+ fillRule="evenodd"
139
+ clipRule="evenodd"
140
+ d="M0.71967 0.71967C1.01256 0.426777 1.48744 0.426777 1.78033 0.71967L5.75 4.68934L9.71967 0.71967C10.0126 0.426777 10.4874 0.426777 10.7803 0.71967C11.0732 1.01256 11.0732 1.48744 10.7803 1.78033L6.28033 6.28033C5.98744 6.57322 5.51256 6.57322 5.21967 6.28033L0.71967 1.78033C0.426777 1.48744 0.426777 1.01256 0.71967 0.71967Z"
141
+ fill={colors.onSurfaceVariant}
142
+ stroke={colors.onSurfaceVariant}
143
+ strokeWidth="0.2"
144
+ />
145
+ </Svg>
146
+ );
147
+
148
+ const AccordionItem: React.FC<{
149
+ title: string;
150
+ content: string;
151
+ isExpanded: boolean;
152
+ onToggle: () => void;
153
+ width: number;
154
+ renderers: any;
155
+ sanitizeHtmlContent: (html: string) => string;
156
+ }> = ({
157
+ title,
158
+ content,
159
+ isExpanded,
160
+ onToggle,
161
+ width,
162
+ renderers,
163
+ sanitizeHtmlContent,
164
+ }) => (
165
+ <View style={styles.accordionContainer}>
166
+ <View style={styles.divider} />
167
+ <TouchableOpacity
168
+ activeOpacity={0.7}
169
+ style={styles.accordionHeader}
170
+ onPress={onToggle}
171
+ >
172
+ <Text style={styles.accordionTitle}>{title}</Text>
173
+ {isExpanded ? <ChevronUpIcon /> : <ChevronDownIcon />}
174
+ </TouchableOpacity>
175
+ {isExpanded && (
176
+ <View style={styles.accordionContent}>
177
+ <RenderHTML
178
+ contentWidth={width}
179
+ source={{
180
+ html: sanitizeHtmlContent(content ?? ''),
181
+ }}
182
+ tagsStyles={{
183
+ ol: styles.ol,
184
+ ul: styles.ul,
185
+ li: styles.li,
186
+ p: styles.p,
187
+ div: styles.div,
188
+ }}
189
+ renderers={renderers}
190
+ ignoredDomTags={IGNORED_DOM_TAGS}
191
+ />
192
+ </View>
193
+ )}
194
+ </View>
195
+ );
196
+
197
+ const ProductDetail: React.FC = () => {
198
+ const navigation = useNavigation<StackNavigationProp<AppStackParamList>>();
199
+ const route = useRoute<ProductDetailRouteProp>();
200
+ const from = route.params?.from;
201
+ const id = route.params?.id;
202
+ const dispatch = useAppDispatch();
203
+ const [messagepopUpVisible, setMessagepopUpVisible] = useState(false);
204
+ const {
205
+ productDetailsByIdData,
206
+ productDetailsByIdLoading,
207
+ addToCartError,
208
+ addToCartData,
209
+ viewMyCartData,
210
+ } = useAppSelector((state: RootState) => state.auth);
211
+ const { dashboardInfoData } = useAppSelector(
212
+ (state: RootState) => state.auth,
213
+ );
214
+ const { width } = useWindowDimensions();
215
+ const [selectedVariant, setSelectedVariant] = useState('');
216
+ const [quantity, setQuantity] = useState(1);
217
+ const [expandedSections, setExpandedSections] = useState({
218
+ description: true,
219
+ redemption: true,
220
+ shopping: false,
221
+ });
222
+ const [amountModalVisible, setAmountModalVisible] = useState(false);
223
+ const [isAddingToCart, setIsAddingToCart] = useState(false);
224
+
225
+ const variants = productDetailsByIdData?.variation_types || [];
226
+
227
+ const toggleSection = (section: keyof typeof expandedSections) => {
228
+ setExpandedSections(prev => ({
229
+ ...prev,
230
+ [section]: !prev[section],
231
+ }));
232
+ };
233
+
234
+ useEffect(() => {
235
+ if (isAddingToCart && addToCartData) {
236
+ console.log('🛒 Add to cart SUCCESS:', addToCartData);
237
+ Toast.show({
238
+ type: SUCCESS,
239
+ text1: ITEM_ADDED_TO_CART_SUCCESSFULLY,
240
+ });
241
+ navigateToCart();
242
+ setIsAddingToCart(false);
243
+ dispatch(resetAuthState('addToCartData'));
244
+ }
245
+ }, [addToCartData, isAddingToCart, dispatch]);
246
+
247
+ const increaseQuantity = () => setQuantity(prev => prev + 1);
248
+ const decreaseQuantity = () => setQuantity(prev => Math.max(1, prev - 1));
249
+
250
+ useEffect(() => {
251
+ if (addToCartError) {
252
+ console.log('🛒 Add to cart ERROR:', addToCartError);
253
+ setMessagepopUpVisible(true);
254
+ setIsAddingToCart(false);
255
+ }
256
+ }, [addToCartError]);
257
+
258
+ useEffect(() => {
259
+ if (id) {
260
+ console.log('🛒 Loading product details for ID:', id);
261
+ dispatch(productDetailsByIdRequest({ id: id }));
262
+ } else {
263
+ console.log('🛒 No product ID provided');
264
+ setSelectedVariant('');
265
+ setQuantity(1);
266
+ }
267
+ }, [id, dispatch]);
268
+
269
+ useEffect(() => {
270
+ dispatch(dashboardInfoRequest({}));
271
+ }, [dispatch]);
272
+
273
+ const sanitizeHtmlContent = (html: string): string => {
274
+ if (!html) return '';
275
+
276
+ const cleaned = html
277
+ .replace(HTML_SANITIZATION_PATTERNS.STYLE_TAGS, '')
278
+ .replace(HTML_SANITIZATION_PATTERNS.SPAN_OPEN, '')
279
+ .replace(HTML_SANITIZATION_PATTERNS.SPAN_CLOSE, '')
280
+ .replace(HTML_SANITIZATION_PATTERNS.MSO_NORMAL_CLASS, '')
281
+ .replace(HTML_SANITIZATION_PATTERNS.O_P_TAG, '')
282
+ .replace(HTML_SANITIZATION_PATTERNS.SUP_TAG, '')
283
+ .replace(HTML_SANITIZATION_PATTERNS.SUB_TAG, '')
284
+ .replace(HTML_SANITIZATION_PATTERNS.EMPTY_P, '')
285
+ .replace(HTML_SANITIZATION_PATTERNS.FONT_OPEN, '')
286
+ .replace(HTML_SANITIZATION_PATTERNS.FONT_CLOSE, '')
287
+
288
+ .replace(HTML_SANITIZATION_PATTERNS.TABLE_OPEN, '<div>')
289
+ .replace(HTML_SANITIZATION_PATTERNS.TABLE_CLOSE, '</div>')
290
+ .replace(HTML_SANITIZATION_PATTERNS.TR_OPEN, '<div>')
291
+ .replace(HTML_SANITIZATION_PATTERNS.TR_CLOSE, '</div>')
292
+ .replace(HTML_SANITIZATION_PATTERNS.TD_OPEN, '<div>')
293
+ .replace(HTML_SANITIZATION_PATTERNS.TD_CLOSE, '</div>')
294
+ .replace(HTML_SANITIZATION_PATTERNS.TBODY_OPEN, '<div>')
295
+ .replace(HTML_SANITIZATION_PATTERNS.TBODY_CLOSE, '</div>')
296
+ .replace(HTML_SANITIZATION_PATTERNS.P_OPEN, '<div>')
297
+ .replace(HTML_SANITIZATION_PATTERNS.P_CLOSE, '</div>')
298
+
299
+ .replace(HTML_SANITIZATION_PATTERNS.DIV_BR, '</div><div>')
300
+ .replace(HTML_SANITIZATION_PATTERNS.BR, '</div><div>')
301
+ .replace(HTML_SANITIZATION_PATTERNS.EMPTY_DIV, '')
302
+ .replace(HTML_SANITIZATION_PATTERNS.SUP_INLINE, '')
303
+
304
+ .replace(HTML_SANITIZATION_PATTERNS.STYLE_ATTRIBUTES, '')
305
+ .replace(HTML_SANITIZATION_PATTERNS.ID_ATTRIBUTES, '')
306
+ .replace(HTML_SANITIZATION_PATTERNS.CLASS_ATTRIBUTES, '')
307
+ .replace(HTML_SANITIZATION_PATTERNS.TITLE_ATTRIBUTES, '')
308
+
309
+ .replace(HTML_SANITIZATION_PATTERNS.NBSP, ' ')
310
+ .replace(HTML_SANITIZATION_PATTERNS.MULTI_SPACE, ' ')
311
+
312
+ .replace(HTML_SANITIZATION_PATTERNS.DIV_OPEN, '<p>')
313
+ .replace(HTML_SANITIZATION_PATTERNS.DIV_CLOSE, '</p>')
314
+
315
+ .trim();
316
+
317
+ return `<div>${cleaned}</div>`;
318
+ };
319
+
320
+ const normalize = (v: any) => String(v ?? '').trim().toLowerCase();
321
+
322
+ const getSelectedVariation = () => {
323
+ const allVariations = productDetailsByIdData?.variations || [];
324
+ const selectNormalized = normalize(selectedVariant);
325
+
326
+ if (!allVariations.length) return undefined;
327
+
328
+ const match = allVariations.find((v: any) => {
329
+ const candidateLabels = new Set<string>();
330
+ candidateLabels.add(normalize(v?.variations_key));
331
+ candidateLabels.add(normalize(v?.name));
332
+
333
+ if (Array.isArray(v?.variation_types)) {
334
+ v?.variation_types?.forEach?.((t: any) => candidateLabels.add(normalize(t?.value)));
335
+ }
336
+ return candidateLabels.has(selectNormalized);
337
+ });
338
+
339
+ return match || allVariations[0];
340
+ };
341
+
342
+ useFocusEffect(
343
+ useCallback(() => {
344
+ return () => {
345
+ setAmountModalVisible(false);
346
+ };
347
+ }, [])
348
+ );
349
+
350
+
351
+ useEffect(() => {
352
+ console.log('🛒 Product data loaded:', {
353
+ hasData: !!productDetailsByIdData,
354
+ variations: productDetailsByIdData?.variations?.length || 0,
355
+ variationTypes: productDetailsByIdData?.variation_types?.length || 0,
356
+ });
357
+
358
+ const firstValue = productDetailsByIdData?.variation_types?.[0]?.values?.[0];
359
+ if (firstValue !== undefined && firstValue !== null) {
360
+ console.log('🛒 Setting selected variant from first value:', firstValue);
361
+ setSelectedVariant(String(firstValue));
362
+ return;
363
+ }
364
+ const firstVar = productDetailsByIdData?.variations?.[0];
365
+ if (firstVar) {
366
+ const fallback = String(firstVar?.variations_key ?? firstVar?.name ?? '');
367
+ console.log('🛒 Setting selected variant from first variation:', fallback);
368
+ setSelectedVariant(fallback);
369
+ return;
370
+ }
371
+ console.log('🛒 No variations found, clearing selected variant');
372
+ setSelectedVariant('');
373
+ }, [productDetailsByIdData, id]);
374
+
375
+ const handleAddToCart = () => {
376
+ console.log('🛒 Add to Cart button pressed');
377
+ console.log('🛒 Selected variant:', selectedVariant);
378
+ console.log('🛒 Quantity:', quantity);
379
+
380
+ const chosen = getSelectedVariation();
381
+ console.log('🛒 Chosen variation:', chosen);
382
+
383
+ if (!chosen?.id) {
384
+ console.log('🛒 No variation selected, showing toast');
385
+ Toast.show({ type: ERROR, text1: NO_VARIATION_SELECTED });
386
+ return;
387
+ }
388
+
389
+ console.log('🛒 Adding to cart with variation ID:', chosen.id);
390
+ setIsAddingToCart(true);
391
+
392
+ const cartPayload = {
393
+ order: {
394
+ items: [
395
+ {
396
+ free_field_value: '',
397
+ product_variation_id: chosen.id,
398
+ quantity: quantity,
399
+ shipping_address: {
400
+ email: dashboardInfoData?.email,
401
+ phone_numbers: dashboardInfoData?.phone,
402
+ },
403
+ },
404
+ ],
405
+ zaggle_card_client_id: dashboardInfoData?.user_client_id,
406
+ },
407
+ skip_payable_validation: true,
408
+ };
409
+
410
+ console.log('🛒 Cart payload:', JSON.stringify(cartPayload, null, 2));
411
+
412
+ dispatch(addToCartRequest(cartPayload));
413
+ };
414
+
415
+ const navigateToCart = () => {
416
+ console.log('🚀 ProductDetail navigating to MyCart');
417
+ // In SDK, MyCart is a hidden tab, not nested inside DashboardTab
418
+ navigation.navigate(ROUTES.MyCart as any);
419
+ };
420
+
421
+ const handleGoBack = () =>
422
+ handleCustomGoBack(navigation, route, {
423
+ map: {
424
+ [ROUTES.REDEEM]: ROUTES.REDEEM,
425
+ },
426
+ fallback: ROUTES.DASHBOARD,
427
+ });
428
+
429
+ useHardwareBack(handleGoBack)
430
+
431
+
432
+
433
+ const selectedVariationForModal = getSelectedVariation();
434
+
435
+ const variantLabel = String(
436
+ selectedVariant ||
437
+ selectedVariationForModal?.variations_key ||
438
+ selectedVariationForModal?.name ||
439
+ ''
440
+ ).trim();
441
+
442
+ const shrinkLabel = isPureText(variantLabel);
443
+
444
+ return (
445
+ <SafeAreaView style={styles.container}>
446
+ <View style={styles.header}>
447
+ <View style={styles.headerContent}>
448
+ <TouchableOpacity
449
+ onPress={() => handleGoBack()}
450
+ >
451
+ <BackIcon />
452
+ </TouchableOpacity>
453
+ <TouchableOpacity
454
+ activeOpacity={0.7}
455
+ style={styles.cartBadge}
456
+ onPress={() => navigateToCart()}
457
+ >
458
+ <View style={styles.cartCountContainer}>
459
+ <Text style={styles.cartCountText}>
460
+ {viewMyCartData?.items?.length}
461
+ </Text>
462
+ </View>
463
+ <CustomImage source={Images.cart} imgStyle={styles.cartIcon} />
464
+ </TouchableOpacity>
465
+ </View>
466
+ </View>
467
+
468
+ {productDetailsByIdLoading ? (
469
+ <CustomLoader visible={productDetailsByIdLoading} />
470
+ ) : (
471
+ <>
472
+ <ScrollView
473
+ style={styles.scrollContainer}
474
+ showsVerticalScrollIndicator={false}
475
+ contentContainerStyle={{ paddingBottom: 50 }}
476
+ >
477
+ <View style={[styles.productImageContainer]}>
478
+ <Image
479
+ source={{
480
+ uri: productDetailsByIdData?.image?.url,
481
+ }}
482
+ style={styles.productImage}
483
+ />
484
+ </View>
485
+
486
+ <View style={styles.productInfoSection}>
487
+ <View style={styles.productHeader}>
488
+ <View style={styles.productTitleContainer}>
489
+ <Text style={styles.productTitle}>
490
+ {productDetailsByIdData?.name}
491
+ </Text>
492
+ {productDetailsByIdData?.subtitle && (
493
+ <Text style={styles.productSubtitle}>
494
+ {productDetailsByIdData?.subtitle}
495
+ </Text>
496
+ )}
497
+ </View>
498
+ </View>
499
+
500
+ {variants.length > 0 && (
501
+ <View style={styles.variantSection}>
502
+ <Text style={styles.sectionTitle}>{SELECT_VARIANT}</Text>
503
+ <View style={styles.variantGrid}>
504
+ <ScrollView
505
+ horizontal
506
+ showsHorizontalScrollIndicator={false}
507
+ contentContainerStyle={styles.variantScrollContainer}
508
+ >
509
+ {variants?.flatMap((type: any) =>
510
+ type?.values?.map((value: string) => (
511
+ <TouchableOpacity
512
+ key={value}
513
+ style={[
514
+ styles.variantButton,
515
+ selectedVariant === value &&
516
+ styles.variantButtonSelected,
517
+ ]}
518
+ onPress={() => setSelectedVariant(value)}
519
+ >
520
+ <Text
521
+ style={[
522
+ styles.variantText,
523
+ selectedVariant === value &&
524
+ styles.variantTextSelected,
525
+ ]}
526
+ >
527
+ {value}
528
+ </Text>
529
+ </TouchableOpacity>
530
+ ))
531
+ )}
532
+ </ScrollView>
533
+ </View>
534
+ </View>
535
+ )}
536
+
537
+ <View style={styles.quantitySection}>
538
+ <Text style={styles.sectionTitle}>{QUANTITY}</Text>
539
+ <View style={styles.quantityRow}>
540
+ <View style={styles.quantitySelector}>
541
+ <TouchableOpacity
542
+ style={styles.quantityButton}
543
+ onPress={decreaseQuantity}
544
+ >
545
+ <MinusIcon />
546
+ </TouchableOpacity>
547
+ <Text style={styles.quantityText}>{quantity}</Text>
548
+ <TouchableOpacity
549
+ style={styles.quantityButton}
550
+ onPress={increaseQuantity}
551
+ >
552
+ <PlusIcon />
553
+ </TouchableOpacity>
554
+ </View>
555
+ {productDetailsByIdData?.delivery_time && (
556
+ <View style={styles.deliveryInfo}>
557
+ <CustomImage
558
+ source={Images.delivery}
559
+ imgStyle={styles.deliveryIcon}
560
+ />
561
+ <Text style={styles.delivery}>
562
+ {DELIVERY_WITHIN} {productDetailsByIdData?.delivery_time}{' '}
563
+ {DAYS}
564
+ </Text>
565
+ </View>
566
+ )}
567
+ </View>
568
+ </View>
569
+
570
+ <AccordionItem
571
+ title={DESCRIPTION}
572
+ content={
573
+ productDetailsByIdData?.description ||
574
+ PRODUCT_DESCRIPTION_NOT_AVAILABLE
575
+ }
576
+ isExpanded={expandedSections.description}
577
+ onToggle={() => toggleSection('description')}
578
+ width={width}
579
+ renderers={customRenderers}
580
+ sanitizeHtmlContent={sanitizeHtmlContent}
581
+ />
582
+
583
+ <AccordionItem
584
+ title={REDEMPTION_TERMS}
585
+ content={
586
+ productDetailsByIdData?.how_to_redeem ||
587
+ REDEMPTION_TERMS_NOT_AVAILABLE
588
+ }
589
+ isExpanded={expandedSections.redemption}
590
+ onToggle={() => toggleSection('redemption')}
591
+ width={width}
592
+ renderers={customRenderers}
593
+ sanitizeHtmlContent={sanitizeHtmlContent}
594
+ />
595
+
596
+ <AccordionItem
597
+ title={SHOPPING_HANDLING_TERMS}
598
+ content={
599
+ productDetailsByIdData?.how_it_works ||
600
+ SHOPPING_HANDLING_TERMS_NOT_AVAILABLE
601
+ }
602
+ isExpanded={expandedSections.shopping}
603
+ onToggle={() => toggleSection('shopping')}
604
+ width={width}
605
+ renderers={customRenderers}
606
+ sanitizeHtmlContent={sanitizeHtmlContent}
607
+ />
608
+ </View>
609
+ </ScrollView>
610
+
611
+ <View style={styles.bottomCTA}>
612
+ <View style={styles.priceSection}>
613
+ <Text
614
+ style={[styles.priceText, shrinkLabel && styles.priceTextSmall]}
615
+ numberOfLines={1}
616
+ ellipsizeMode="tail"
617
+ adjustsFontSizeToFit={shrinkLabel}
618
+ minimumFontScale={0.8}
619
+ >
620
+ {variantLabel}
621
+ </Text>
622
+ <TouchableOpacity
623
+ activeOpacity={0.7}
624
+ style={styles.viewDetailsButton}
625
+ onPress={() => setAmountModalVisible(true)}
626
+ >
627
+ <Text style={styles.viewDetailsText}>{VIEW_DETAILS}</Text>
628
+ <Svg width="10" height="6" viewBox="0 0 10 6" fill="none">
629
+ <Path
630
+ d="M9 5L5 1L1 5"
631
+ stroke={colors.primary}
632
+ strokeWidth="1.6"
633
+ strokeLinecap="round"
634
+ strokeLinejoin="round"
635
+ />
636
+ </Svg>
637
+ </TouchableOpacity>
638
+ </View>
639
+ <TouchableOpacity
640
+ activeOpacity={0.7}
641
+ style={styles.addToCartButton}
642
+ onPress={() => {
643
+ console.log('🛒 Add to Cart button PRESSED - onPress triggered');
644
+ handleAddToCart();
645
+ }}
646
+ >
647
+ <Text style={styles.addToCartText}>{ADD_TO_CART}</Text>
648
+ </TouchableOpacity>
649
+ </View>
650
+ </>
651
+ )}
652
+
653
+ {amountModalVisible && (
654
+ <AmountModal
655
+ visible={amountModalVisible}
656
+ onClose={() => setAmountModalVisible(false)}
657
+ item={{
658
+ discount_cents:
659
+ selectedVariationForModal?.points_required_to_redeem,
660
+ grand_total: selectedVariationForModal?.price_cents,
661
+ total_amount_cents: selectedVariationForModal?.price_cents,
662
+ }}
663
+ label_1={ORDER_AMOUNT}
664
+ label_2={DELIVERY_CHARGES}
665
+ />
666
+ )}
667
+
668
+ <CustomMessagePopUp
669
+ title={ERROR_TITLE}
670
+ message={addToCartError || ''}
671
+ visible={messagepopUpVisible}
672
+ onClose={() => {
673
+ setMessagepopUpVisible(false);
674
+ dispatch(resetAuthState('addToCartError'));
675
+ }}
676
+ icon={Images?.pendingIcon}
677
+ />
678
+ </SafeAreaView>
679
+ );
680
+ };
681
+
682
+ export default ProductDetail;