react-native-salespanda 0.6.0 → 0.7.1

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 (130) hide show
  1. package/README.md +50 -102
  2. package/package.json +2 -3
  3. package/lib/module/NativeSalespanda.js.map +0 -1
  4. package/lib/module/SalespandaApp.js.map +0 -1
  5. package/lib/module/assets/images/index.js.map +0 -1
  6. package/lib/module/components/BottomSheet.js.map +0 -1
  7. package/lib/module/components/ContactViaModal.js.map +0 -1
  8. package/lib/module/components/Loader.js.map +0 -1
  9. package/lib/module/components/ScreenHeader.js.map +0 -1
  10. package/lib/module/components/SearchBar.js.map +0 -1
  11. package/lib/module/components/TabsHeader.js.map +0 -1
  12. package/lib/module/components/index.js.map +0 -1
  13. package/lib/module/config/FlavorConfig.js.map +0 -1
  14. package/lib/module/config/SalespandaConfig.js.map +0 -1
  15. package/lib/module/constants/Colors.js.map +0 -1
  16. package/lib/module/constants/GetPlatorm.js.map +0 -1
  17. package/lib/module/index.js.map +0 -1
  18. package/lib/module/navigation/AppNavigator.js.map +0 -1
  19. package/lib/module/navigation/BottomTabNavigator.js.map +0 -1
  20. package/lib/module/navigation/DrawerNavigator.js.map +0 -1
  21. package/lib/module/navigation/StackNavigator.js.map +0 -1
  22. package/lib/module/screens/CRM/AddContactModal.js.map +0 -1
  23. package/lib/module/screens/CRM/CategoryTabs.js.map +0 -1
  24. package/lib/module/screens/CRM/ContactItem.js.map +0 -1
  25. package/lib/module/screens/CRM/FilterDropdown.js.map +0 -1
  26. package/lib/module/screens/CRM/FunnelChart.js.map +0 -1
  27. package/lib/module/screens/CRM/InfoCard.js.map +0 -1
  28. package/lib/module/screens/CRM/LeadCard.js.map +0 -1
  29. package/lib/module/screens/CRM/LogCallScreen.js.map +0 -1
  30. package/lib/module/screens/CRM/TopTabs.js.map +0 -1
  31. package/lib/module/screens/CRM/index.js.map +0 -1
  32. package/lib/module/screens/ReportsScreen.js.map +0 -1
  33. package/lib/module/screens/Tabs/ActivityAnalytics.js.map +0 -1
  34. package/lib/module/screens/Tabs/CRMScreen.js.map +0 -1
  35. package/lib/module/screens/Tabs/DigitalDiary.js.map +0 -1
  36. package/lib/module/screens/Tabs/HomeScreen.js.map +0 -1
  37. package/lib/module/screens/Tabs/NotificationsScreen.js.map +0 -1
  38. package/lib/module/screens/contentliberary/contentliberary.js.map +0 -1
  39. package/lib/module/services/api.js.map +0 -1
  40. package/lib/module/services/authService.js.map +0 -1
  41. package/lib/module/store/index.js.map +0 -1
  42. package/lib/typescript/src/NativeSalespanda.d.ts.map +0 -1
  43. package/lib/typescript/src/SalespandaApp.d.ts.map +0 -1
  44. package/lib/typescript/src/assets/images/index.d.ts.map +0 -1
  45. package/lib/typescript/src/components/BottomSheet.d.ts.map +0 -1
  46. package/lib/typescript/src/components/ContactViaModal.d.ts.map +0 -1
  47. package/lib/typescript/src/components/Loader.d.ts.map +0 -1
  48. package/lib/typescript/src/components/ScreenHeader.d.ts.map +0 -1
  49. package/lib/typescript/src/components/SearchBar.d.ts.map +0 -1
  50. package/lib/typescript/src/components/TabsHeader.d.ts.map +0 -1
  51. package/lib/typescript/src/components/index.d.ts.map +0 -1
  52. package/lib/typescript/src/config/FlavorConfig.d.ts.map +0 -1
  53. package/lib/typescript/src/config/SalespandaConfig.d.ts.map +0 -1
  54. package/lib/typescript/src/constants/Colors.d.ts.map +0 -1
  55. package/lib/typescript/src/constants/GetPlatorm.d.ts.map +0 -1
  56. package/lib/typescript/src/index.d.ts.map +0 -1
  57. package/lib/typescript/src/navigation/AppNavigator.d.ts.map +0 -1
  58. package/lib/typescript/src/navigation/BottomTabNavigator.d.ts.map +0 -1
  59. package/lib/typescript/src/navigation/DrawerNavigator.d.ts.map +0 -1
  60. package/lib/typescript/src/navigation/StackNavigator.d.ts.map +0 -1
  61. package/lib/typescript/src/screens/CRM/AddContactModal.d.ts.map +0 -1
  62. package/lib/typescript/src/screens/CRM/CategoryTabs.d.ts.map +0 -1
  63. package/lib/typescript/src/screens/CRM/ContactItem.d.ts.map +0 -1
  64. package/lib/typescript/src/screens/CRM/FilterDropdown.d.ts.map +0 -1
  65. package/lib/typescript/src/screens/CRM/FunnelChart.d.ts.map +0 -1
  66. package/lib/typescript/src/screens/CRM/InfoCard.d.ts.map +0 -1
  67. package/lib/typescript/src/screens/CRM/LeadCard.d.ts.map +0 -1
  68. package/lib/typescript/src/screens/CRM/LogCallScreen.d.ts.map +0 -1
  69. package/lib/typescript/src/screens/CRM/TopTabs.d.ts.map +0 -1
  70. package/lib/typescript/src/screens/CRM/index.d.ts.map +0 -1
  71. package/lib/typescript/src/screens/ReportsScreen.d.ts.map +0 -1
  72. package/lib/typescript/src/screens/Tabs/ActivityAnalytics.d.ts.map +0 -1
  73. package/lib/typescript/src/screens/Tabs/CRMScreen.d.ts.map +0 -1
  74. package/lib/typescript/src/screens/Tabs/DigitalDiary.d.ts.map +0 -1
  75. package/lib/typescript/src/screens/Tabs/HomeScreen.d.ts.map +0 -1
  76. package/lib/typescript/src/screens/Tabs/NotificationsScreen.d.ts.map +0 -1
  77. package/lib/typescript/src/screens/contentliberary/contentliberary.d.ts.map +0 -1
  78. package/lib/typescript/src/services/api.d.ts.map +0 -1
  79. package/lib/typescript/src/services/authService.d.ts.map +0 -1
  80. package/lib/typescript/src/store/index.d.ts.map +0 -1
  81. package/src/NativeSalespanda.ts +0 -7
  82. package/src/SalespandaApp.tsx +0 -141
  83. package/src/assets/images/bottomtabs/analytics.png +0 -0
  84. package/src/assets/images/bottomtabs/analyticsactive.png +0 -0
  85. package/src/assets/images/bottomtabs/crm.png +0 -0
  86. package/src/assets/images/bottomtabs/crmactive.png +0 -0
  87. package/src/assets/images/bottomtabs/diary.png +0 -0
  88. package/src/assets/images/bottomtabs/diaryactive.png +0 -0
  89. package/src/assets/images/bottomtabs/home.png +0 -0
  90. package/src/assets/images/bottomtabs/homeactive.png +0 -0
  91. package/src/assets/images/bottomtabs/notification.png +0 -0
  92. package/src/assets/images/bottomtabs/notificationactive.png +0 -0
  93. package/src/assets/images/index.js +0 -11
  94. package/src/assets/images/index.ts +0 -40
  95. package/src/components/BottomSheet.tsx +0 -146
  96. package/src/components/ContactViaModal.tsx +0 -80
  97. package/src/components/Loader.tsx +0 -48
  98. package/src/components/ScreenHeader.tsx +0 -57
  99. package/src/components/SearchBar.tsx +0 -59
  100. package/src/components/TabsHeader.tsx +0 -72
  101. package/src/components/index.ts +0 -5
  102. package/src/config/FlavorConfig.ts +0 -55
  103. package/src/config/SalespandaConfig.ts +0 -142
  104. package/src/constants/Colors.ts +0 -17
  105. package/src/constants/GetPlatorm.ts +0 -29
  106. package/src/index.tsx +0 -31
  107. package/src/navigation/AppNavigator.tsx +0 -24
  108. package/src/navigation/BottomTabNavigator.tsx +0 -181
  109. package/src/navigation/DrawerNavigator.tsx +0 -306
  110. package/src/navigation/StackNavigator.tsx +0 -32
  111. package/src/screens/CRM/AddContactModal.tsx +0 -57
  112. package/src/screens/CRM/CategoryTabs.tsx +0 -109
  113. package/src/screens/CRM/ContactItem.tsx +0 -168
  114. package/src/screens/CRM/FilterDropdown.tsx +0 -34
  115. package/src/screens/CRM/FunnelChart.tsx +0 -103
  116. package/src/screens/CRM/InfoCard.tsx +0 -51
  117. package/src/screens/CRM/LeadCard.tsx +0 -69
  118. package/src/screens/CRM/LogCallScreen.tsx +0 -318
  119. package/src/screens/CRM/TopTabs.tsx +0 -95
  120. package/src/screens/CRM/index.ts +0 -10
  121. package/src/screens/ReportsScreen.tsx +0 -37
  122. package/src/screens/Tabs/ActivityAnalytics.tsx +0 -25
  123. package/src/screens/Tabs/CRMScreen.tsx +0 -381
  124. package/src/screens/Tabs/DigitalDiary.tsx +0 -35
  125. package/src/screens/Tabs/HomeScreen.tsx +0 -379
  126. package/src/screens/Tabs/NotificationsScreen.tsx +0 -25
  127. package/src/screens/contentliberary/contentliberary.tsx +0 -268
  128. package/src/services/api.ts +0 -173
  129. package/src/services/authService.ts +0 -75
  130. package/src/store/index.ts +0 -16
@@ -1,379 +0,0 @@
1
- import React from 'react';
2
- import {
3
- View,
4
- Text,
5
- StyleSheet,
6
- TouchableOpacity,
7
- ScrollView,
8
- Image,
9
- FlatList,
10
- Dimensions,
11
- } from 'react-native';
12
- import {
13
- scale,
14
- verticalScale,
15
- moderateScale,
16
- moderateVerticalScale,
17
- } from 'react-native-size-matters';
18
- import { SafeAreaView } from 'react-native-safe-area-context';
19
- import Loader from '../../components/Loader';
20
- import TabsHeader from '../../components/TabsHeader';
21
- import { Colors } from '../../constants/Colors';
22
- import {
23
- useAuthenticateMutation,
24
- useLazyGetHomeQuery,
25
- useSpssoLoginMutation,
26
- loadPersistedTokens,
27
- persistTokens,
28
- setRuntimeTokens,
29
- clearPersistedTokens,
30
- } from '../../services/api';
31
- import type { HomeResponse } from '../../services/api';
32
-
33
- interface MenuItemProps {
34
- title: string;
35
- imageUrl?: string | null;
36
- onPress?: () => void;
37
- }
38
-
39
- const MenuItem: React.FC<MenuItemProps> = ({ imageUrl, onPress }) => {
40
- return (
41
- <TouchableOpacity
42
- style={styles.menuItem}
43
- onPress={onPress}
44
- activeOpacity={0.8}
45
- >
46
- {imageUrl ? (
47
- <Image source={{ uri: imageUrl }} style={styles.menuItemImage} />
48
- ) : (
49
- <Loader message="" />
50
- )}
51
- </TouchableOpacity>
52
- );
53
- };
54
-
55
- const HomeScreen: React.FC = () => {
56
- const [loading, setLoading] = React.useState<boolean>(false);
57
- const [error, setError] = React.useState<string | null>(null);
58
- const [homeData, setHomeData] = React.useState<
59
- HomeResponse['response'] | null
60
- >(null);
61
-
62
- type BannerItem = {
63
- image: string;
64
- title?: string;
65
- link?: string | null;
66
- };
67
-
68
- const [authenticate] = useAuthenticateMutation();
69
- const [spssoLogin] = useSpssoLoginMutation();
70
- const [triggerHome, { isFetching: isHomeFetching }] = useLazyGetHomeQuery();
71
-
72
- const bannerItems: BannerItem[] = React.useMemo(
73
- () =>
74
- homeData?.home_banner && homeData.home_banner.length > 0
75
- ? homeData.home_banner.map((item) => ({
76
- image: item.image,
77
- title: item.title ?? undefined,
78
- link: item.link ?? null,
79
- }))
80
- : [],
81
- [homeData?.home_banner]
82
- );
83
-
84
- const menuItems = React.useMemo(() => homeData?.menu ?? [], [homeData?.menu]);
85
-
86
- const screenWidth = Dimensions.get('window').width;
87
- const [activeIndex, setActiveIndex] = React.useState(0);
88
- const flatListRef = React.useRef<any>(null);
89
-
90
- const handleMomentumScrollEnd = (e: any) => {
91
- const offsetX = e.nativeEvent.contentOffset.x;
92
- const index = Math.round(offsetX / screenWidth);
93
- setActiveIndex(index);
94
- };
95
-
96
- const loadHomeData = React.useCallback(
97
- async (isRetry?: boolean) => {
98
- try {
99
- setLoading(true);
100
- setError(null);
101
-
102
- const storedTokens = await loadPersistedTokens();
103
- let accessToken = storedTokens.accessToken;
104
- let token = storedTokens.token;
105
-
106
- if (!accessToken) {
107
- const authResp = await authenticate({}).unwrap();
108
- accessToken = authResp.access_token;
109
- setRuntimeTokens({ accessToken });
110
- await persistTokens({ accessToken });
111
- }
112
-
113
- if (!token && accessToken) {
114
- const ssoResp = await spssoLogin({
115
- access_token: accessToken,
116
- }).unwrap();
117
- token = ssoResp.token;
118
- setRuntimeTokens({ token });
119
- await persistTokens({ token });
120
- }
121
-
122
- if (!token) {
123
- throw new Error('Missing auth token, please try again.');
124
- }
125
-
126
- const tokenHeader: string | undefined = token ?? undefined;
127
- const homeResp = await triggerHome({
128
- tokenOverride: tokenHeader,
129
- }).unwrap();
130
-
131
- const isSuccess =
132
- homeResp.statusCode === '200' &&
133
- homeResp.status?.toLowerCase() === 'success';
134
- if (!isSuccess) {
135
- const code = homeResp.statusCode;
136
-
137
- if (
138
- !isRetry &&
139
- code &&
140
- (code === '1008' || code === '401' || code === '403')
141
- ) {
142
- await clearPersistedTokens();
143
- setRuntimeTokens({ token: null, accessToken: null });
144
- await loadHomeData(true);
145
- return;
146
- }
147
-
148
- throw new Error(homeResp?.message || 'Failed to load home data');
149
- }
150
-
151
- setHomeData(homeResp.response);
152
- setLoading(false);
153
- } catch (e: any) {
154
- const statusCode = e?.status || e?.data?.statusCode;
155
- if (statusCode === 401 || statusCode === 403) {
156
- setError(null);
157
- return;
158
- }
159
- setError(
160
- e?.data?.message ||
161
- e?.message ||
162
- 'Failed to load home data. Please try again.'
163
- );
164
- setLoading(false);
165
- }
166
- },
167
- [authenticate, spssoLogin, triggerHome]
168
- );
169
-
170
- React.useEffect(() => {
171
- loadHomeData();
172
- }, [loadHomeData]);
173
-
174
- React.useEffect(() => {
175
- const id = setInterval(() => {
176
- setActiveIndex((prev) => {
177
- const next =
178
- bannerItems.length > 0 ? (prev + 1) % bannerItems.length : 0;
179
- if (flatListRef.current) {
180
- try {
181
- flatListRef.current.scrollToIndex({ index: next, animated: true });
182
- } catch {
183
- // ignore scroll failures
184
- }
185
- }
186
- return next;
187
- });
188
- }, 2500);
189
- return () => {
190
- clearInterval(id);
191
- };
192
- }, [bannerItems.length]);
193
-
194
- return (
195
- <SafeAreaView style={styles.safeArea} edges={['top', 'bottom']}>
196
- <View style={styles.container}>
197
- <TabsHeader title="Home" />
198
-
199
- <View style={styles.contentWrapper}>
200
- {loading || isHomeFetching || !homeData ? (
201
- <Loader overlay message="Loading home data..." />
202
- ) : null}
203
-
204
- <View style={styles.carouselContainer}>
205
- <FlatList
206
- ref={flatListRef}
207
- data={bannerItems}
208
- keyExtractor={(item, idx) => `${item.title || 'banner'}-${idx}`}
209
- renderItem={({ item }) => (
210
- <TouchableOpacity activeOpacity={0.9} onPress={() => {}}>
211
- <Image
212
- source={{ uri: item.image }}
213
- style={[styles.carouselImage, { width: screenWidth }]}
214
- />
215
- </TouchableOpacity>
216
- )}
217
- horizontal
218
- pagingEnabled
219
- showsHorizontalScrollIndicator={false}
220
- onMomentumScrollEnd={handleMomentumScrollEnd}
221
- getItemLayout={(_, index) => ({
222
- length: screenWidth,
223
- offset: screenWidth * index,
224
- index,
225
- })}
226
- />
227
- <View style={styles.carouselIndicatorsOverlay}>
228
- {bannerItems.map((_, idx) => (
229
- <View
230
- key={idx}
231
- style={[
232
- styles.indicator,
233
- idx === activeIndex ? styles.activeIndicator : null,
234
- ]}
235
- />
236
- ))}
237
- </View>
238
- </View>
239
-
240
- <ScrollView
241
- style={styles.scrollView}
242
- contentContainerStyle={styles.scrollContent}
243
- >
244
- {error ? (
245
- <>
246
- <Text style={styles.errorText}>{error}</Text>
247
- <TouchableOpacity onPress={() => loadHomeData()}>
248
- <Text style={styles.retryText}>Tap to retry</Text>
249
- </TouchableOpacity>
250
- </>
251
- ) : null}
252
-
253
- {menuItems.length > 0 ? (
254
- <View style={styles.menuGrid}>
255
- {menuItems.map((item) => (
256
- <MenuItem
257
- key={item.title}
258
- title={item.title}
259
- imageUrl={item.image}
260
- />
261
- ))}
262
- </View>
263
- ) : null}
264
- </ScrollView>
265
- </View>
266
- </View>
267
- </SafeAreaView>
268
- );
269
- };
270
-
271
- export default HomeScreen;
272
-
273
- const styles = StyleSheet.create({
274
- safeArea: {
275
- flex: 1,
276
- backgroundColor: Colors.white,
277
- },
278
- container: {
279
- flex: 1,
280
- backgroundColor: Colors.white,
281
- },
282
- scrollView: {
283
- flex: 1,
284
- backgroundColor: Colors.white,
285
- },
286
- scrollContent: {
287
- paddingBottom: verticalScale(24),
288
- },
289
- carouselContainer: {
290
- width: '100%',
291
- backgroundColor: Colors.white,
292
- position: 'relative',
293
- },
294
- carouselImage: {
295
- height: verticalScale(155),
296
- resizeMode: 'contain',
297
- backgroundColor: Colors.white,
298
- },
299
- carouselIndicatorsOverlay: {
300
- position: 'absolute',
301
- left: 0,
302
- right: 0,
303
- bottom: verticalScale(20),
304
- flexDirection: 'row',
305
- justifyContent: 'center',
306
- alignItems: 'center',
307
- },
308
- indicator: {
309
- width: scale(8),
310
- height: scale(8),
311
- borderRadius: moderateScale(4),
312
- backgroundColor: Colors.divider,
313
- marginHorizontal: scale(4),
314
- },
315
- activeIndicator: {
316
- backgroundColor: Colors.white,
317
- width: scale(24),
318
- },
319
- menuGrid: {
320
- flexDirection: 'row',
321
- flexWrap: 'wrap',
322
- paddingTop: scale(8),
323
- paddingBottom: verticalScale(16),
324
- justifyContent: 'flex-start',
325
- alignSelf: 'center',
326
- width: '100%',
327
- },
328
- menuItem: {
329
- width: '29.5%',
330
- aspectRatio: 1,
331
- borderRadius: moderateScale(16),
332
- marginBottom: moderateVerticalScale(8),
333
- marginHorizontal: scale(6),
334
- overflow: 'hidden',
335
- },
336
- iconContainer: {
337
- width: scale(50),
338
- height: scale(50),
339
- justifyContent: 'center',
340
- alignItems: 'center',
341
- marginBottom: verticalScale(8),
342
- },
343
- iconText: {
344
- fontSize: moderateScale(24),
345
- },
346
- menuItemImage: {
347
- width: '100%',
348
- height: '100%',
349
- resizeMode: 'cover',
350
- },
351
- menuItemText: {
352
- fontSize: moderateScale(11),
353
- textAlign: 'center',
354
- color: Colors.black,
355
- fontWeight: '500',
356
- },
357
- statusText: {
358
- fontSize: moderateScale(12),
359
- color: Colors.black,
360
- paddingHorizontal: scale(12),
361
- paddingTop: verticalScale(8),
362
- },
363
- errorText: {
364
- fontSize: moderateScale(12),
365
- color: 'red',
366
- paddingHorizontal: scale(12),
367
- paddingTop: verticalScale(4),
368
- },
369
- retryText: {
370
- fontSize: moderateScale(12),
371
- color: Colors.primary,
372
- paddingHorizontal: scale(12),
373
- paddingTop: verticalScale(6),
374
- },
375
- contentWrapper: {
376
- flex: 1,
377
- position: 'relative',
378
- },
379
- });
@@ -1,25 +0,0 @@
1
- import { StyleSheet, ScrollView } from 'react-native';
2
- import { SafeAreaView } from 'react-native-safe-area-context';
3
- import { Colors } from '../../constants/Colors';
4
- import TabsHeader from '../../components/TabsHeader';
5
-
6
- const NotificationsScreen: React.FC = () => {
7
- return (
8
- <SafeAreaView style={styles.container} edges={['top', 'bottom']}>
9
- <TabsHeader title="Notifications" />
10
- <ScrollView style={styles.scrollView} />
11
- </SafeAreaView>
12
- );
13
- };
14
-
15
- export default NotificationsScreen;
16
-
17
- const styles = StyleSheet.create({
18
- container: {
19
- flex: 1,
20
- backgroundColor: Colors.background,
21
- },
22
- scrollView: {
23
- flex: 1,
24
- },
25
- });
@@ -1,268 +0,0 @@
1
- import { useState } from 'react';
2
- import {
3
- View,
4
- Text,
5
- TouchableOpacity,
6
- StyleSheet,
7
- TextInput,
8
- FlatList,
9
- Image,
10
- Dimensions,
11
- ScrollView,
12
- } from 'react-native';
13
- import FeatherIcon from 'react-native-vector-icons/Feather';
14
- import { SafeAreaView } from 'react-native-safe-area-context';
15
-
16
- const { width } = Dimensions.get('window');
17
- const categories = [
18
- 'All',
19
- 'Co-Branded',
20
- 'Videos',
21
- 'Reel',
22
- 'Social',
23
- 'Brochure',
24
- 'Leaflet',
25
- ];
26
- const greetingsData = [
27
- {
28
- id: '1',
29
- title: 'Happy Diwali!',
30
- tags: ['Video', 'Co-branded'],
31
- image:
32
- 'https://img.freepik.com/premium-vector/poster-festival-lights-diwali_1287372-756.jpg?w=1060',
33
- },
34
- {
35
- id: '2',
36
- title: 'Happy Diwali!',
37
- tags: ['Video', 'Co-branded'],
38
- image:
39
- 'https://img.freepik.com/premium-vector/poster-festival-lights-diwali_1287372-756.jpg?w=1060',
40
- },
41
- ];
42
- export default function DashboardScreen() {
43
- const [selectedCat, setSelectedCat] = useState('All');
44
- return (
45
- <ScrollView style={styles.container} showsVerticalScrollIndicator={false}>
46
- <SafeAreaView>
47
- {/* Header */}
48
- <View style={styles.headerRow}>
49
- <TouchableOpacity>
50
- <FeatherIcon name="arrow-left" size={24} color="#000" />
51
- </TouchableOpacity>
52
- <Text style={styles.headerTitle}>Content Library</Text>
53
- <TouchableOpacity>
54
- <FeatherIcon name="heart" size={24} color="#000" />
55
- </TouchableOpacity>
56
- </View>
57
- {/* Search Bar */}
58
- <View style={styles.searchBox}>
59
- <FeatherIcon name="search" size={18} color="#999" />
60
- <TextInput
61
- placeholder="Search by content name, tags"
62
- placeholderTextColor="#999"
63
- style={styles.input}
64
- />
65
- </View>
66
- {/* Category Tabs */}
67
- <View style={styles.categoryContainer}>
68
- <ScrollView horizontal showsHorizontalScrollIndicator={false}>
69
- {categories.map((cat, index) => {
70
- const isActive = selectedCat === cat;
71
- return (
72
- <TouchableOpacity
73
- key={index}
74
- onPress={() => setSelectedCat(cat)}
75
- style={styles.tabItem}
76
- >
77
- <Text
78
- style={[styles.tabText, isActive && styles.activeTabText]}
79
- >
80
- {cat}
81
- </Text>
82
- {/* Yellow bottom line */}
83
- {isActive ? (
84
- <View style={styles.activeLine} />
85
- ) : (
86
- <View style={{ height: 3 }} />
87
- )}
88
- </TouchableOpacity>
89
- );
90
- })}
91
- </ScrollView>
92
- </View>
93
- {/* Greetings Section */}
94
- <View style={styles.sectionHeader}>
95
- <Text style={styles.sectionTitle}>Greetings (9)</Text>
96
- <TouchableOpacity>
97
- <Text style={styles.seeAll}>See all</Text>
98
- </TouchableOpacity>
99
- </View>
100
- <FlatList
101
- data={greetingsData}
102
- keyExtractor={(item) => item.id}
103
- horizontal
104
- showsHorizontalScrollIndicator={false}
105
- renderItem={({ item }) => <Card item={item} />}
106
- />
107
- </SafeAreaView>
108
- </ScrollView>
109
- );
110
- }
111
- function Card({ item }: any) {
112
- return (
113
- <View style={styles.card}>
114
- <Image source={{ uri: item.image }} style={styles.cardImage} />
115
- {/* Heart button */}
116
- <TouchableOpacity style={styles.cardHeart}>
117
- <FeatherIcon name="heart" size={18} color="#fff" />
118
- </TouchableOpacity>
119
- {/* Tags */}
120
- <View style={styles.tagRow}>
121
- {item.tags.map((t: string, i: number) => (
122
- <View key={i} style={styles.tag}>
123
- <Text style={styles.tagText}>{t}</Text>
124
- </View>
125
- ))}
126
- </View>
127
- {/* Title + Share */}
128
- <View style={styles.cardFooter}>
129
- <Text numberOfLines={1} style={styles.cardTitle}>
130
- {item.title}
131
- </Text>
132
- <TouchableOpacity
133
- style={{ flexDirection: 'row', alignItems: 'center' }}
134
- >
135
- <Text style={styles.shareText}>Share</Text>
136
- <FeatherIcon name="chevron-right" size={16} color="#ff9900" />
137
- </TouchableOpacity>
138
- </View>
139
- </View>
140
- );
141
- }
142
- const styles = StyleSheet.create({
143
- container: {
144
- padding: 16,
145
- backgroundColor: '#fff',
146
- },
147
- headerRow: {
148
- flexDirection: 'row',
149
- alignItems: 'center',
150
- justifyContent: 'space-between',
151
- marginBottom: 12,
152
- },
153
- headerTitle: {
154
- fontSize: 20,
155
- fontWeight: '600',
156
- marginLeft: -120, // shifts title closer to back icon like screenshot
157
- },
158
- searchBox: {
159
- flexDirection: 'row',
160
- alignItems: 'center',
161
- backgroundColor: '#fff',
162
- borderRadius: 12,
163
- paddingHorizontal: 12,
164
- paddingVertical: 10,
165
- borderWidth: 1,
166
- borderColor: '#ddd',
167
- marginTop: 10,
168
- },
169
- input: {
170
- marginLeft: 8,
171
- flex: 1,
172
- fontSize: 14,
173
- },
174
- categoryContainer: {
175
- marginTop: 18,
176
- paddingBottom: 6,
177
- borderBottomWidth: 1,
178
- borderColor: '#e4e4e4',
179
- },
180
- tabItem: {
181
- marginRight: 24,
182
- alignItems: 'center',
183
- },
184
- tabText: {
185
- fontSize: 15,
186
- color: '#666',
187
- marginBottom: 6,
188
- },
189
- activeTabText: {
190
- color: '#000',
191
- fontWeight: '600',
192
- },
193
- activeLine: {
194
- height: 3,
195
- width: 25,
196
- backgroundColor: '#fbbf24', // exact yellow underline
197
- borderRadius: 2,
198
- },
199
- sectionHeader: {
200
- marginTop: 20,
201
- marginBottom: 10,
202
- flexDirection: 'row',
203
- justifyContent: 'space-between',
204
- alignItems: 'center',
205
- },
206
- sectionTitle: {
207
- fontSize: 18,
208
- fontWeight: '600',
209
- },
210
- seeAll: {
211
- color: '#ff9900',
212
- fontWeight: '600',
213
- },
214
- card: {
215
- width: width * 0.7,
216
- marginRight: 16,
217
- borderRadius: 14,
218
- backgroundColor: '#fff',
219
- overflow: 'hidden',
220
- elevation: 2,
221
- },
222
- cardImage: {
223
- width: '100%',
224
- height: 160,
225
- },
226
- cardHeart: {
227
- position: 'absolute',
228
- top: 10,
229
- right: 10,
230
- backgroundColor: 'rgba(0,0,0,0.4)',
231
- padding: 6,
232
- borderRadius: 20,
233
- },
234
- tagRow: {
235
- flexDirection: 'row',
236
- marginTop: 6,
237
- marginLeft: 8,
238
- },
239
- tag: {
240
- backgroundColor: '#fff',
241
- paddingHorizontal: 6,
242
- paddingVertical: 2,
243
- borderRadius: 6,
244
- marginRight: 6,
245
- borderWidth: 1,
246
- borderColor: '#ddd',
247
- },
248
- tagText: {
249
- fontSize: 10,
250
- color: '#444',
251
- },
252
- cardFooter: {
253
- padding: 10,
254
- flexDirection: 'row',
255
- justifyContent: 'space-between',
256
- alignItems: 'center',
257
- },
258
- cardTitle: {
259
- width: '60%',
260
- fontSize: 14,
261
- fontWeight: '600',
262
- },
263
- shareText: {
264
- color: '#ff9900',
265
- fontWeight: '600',
266
- marginRight: 4,
267
- },
268
- });