create-gufran-expo-app 2.0.0 → 2.0.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 (266) hide show
  1. package/lib/createApp.js +7 -2
  2. package/package.json +1 -1
  3. package/template/App.tsx +118 -0
  4. package/template/ReactotronConfig.js +5 -0
  5. package/template/android/app/build.gradle +184 -0
  6. package/template/android/app/debug.keystore +0 -0
  7. package/template/android/app/google-services.json +29 -0
  8. package/template/android/app/proguard-rules.pro +14 -0
  9. package/template/android/app/src/debug/AndroidManifest.xml +7 -0
  10. package/template/android/app/src/debugOptimized/AndroidManifest.xml +7 -0
  11. package/template/android/app/src/main/AndroidManifest.xml +28 -0
  12. package/template/android/app/src/main/assets/fonts/Outfit-Bold.ttf +0 -0
  13. package/template/android/app/src/main/assets/fonts/Outfit-Light.ttf +0 -0
  14. package/template/android/app/src/main/assets/fonts/Outfit-Medium.ttf +0 -0
  15. package/template/android/app/src/main/assets/fonts/Outfit-Regular.ttf +0 -0
  16. package/template/android/app/src/main/assets/fonts/Outfit-SemiBold.ttf +0 -0
  17. package/template/android/app/src/main/java/com/club/yakka/MainActivity.kt +61 -0
  18. package/template/android/app/src/main/java/com/club/yakka/MainApplication.kt +60 -0
  19. package/template/android/app/src/main/res/drawable/ic_launcher_background.xml +6 -0
  20. package/template/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  21. package/template/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png +0 -0
  22. package/template/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png +0 -0
  23. package/template/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png +0 -0
  24. package/template/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png +0 -0
  25. package/template/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png +0 -0
  26. package/template/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  27. package/template/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  28. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  29. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp +0 -0
  30. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  31. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  32. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp +0 -0
  33. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  34. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  35. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp +0 -0
  36. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  37. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  38. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp +0 -0
  39. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  40. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  41. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp +0 -0
  42. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  43. package/template/android/app/src/main/res/values/colors.xml +6 -0
  44. package/template/android/app/src/main/res/values/strings.xml +5 -0
  45. package/template/android/app/src/main/res/values/styles.xml +14 -0
  46. package/template/android/app/src/main/res/values-night/colors.xml +1 -0
  47. package/template/android/build.gradle +33 -0
  48. package/template/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  49. package/template/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  50. package/template/android/gradle.properties +65 -0
  51. package/template/android/gradlew +251 -0
  52. package/template/android/gradlew.bat +94 -0
  53. package/template/android/settings.gradle +39 -0
  54. package/template/app.json +69 -0
  55. package/template/assets/adaptive-icon.png +0 -0
  56. package/template/assets/adaptive-icon1.png +0 -0
  57. package/template/assets/app_icon.png +0 -0
  58. package/template/assets/favicon.png +0 -0
  59. package/template/assets/icon.png +0 -0
  60. package/template/assets/splash-icon.png +0 -0
  61. package/template/babel-plugin-disable-font-scaling.js +41 -0
  62. package/template/babel.config.js +28 -0
  63. package/template/firebase.json +5 -0
  64. package/template/index.ts +24 -0
  65. package/template/ios/.xcode.env +11 -0
  66. package/template/ios/ClubYakka/AppDelegate.swift +74 -0
  67. package/template/ios/ClubYakka/ClubYakka-Bridging-Header.h +3 -0
  68. package/template/ios/ClubYakka/ClubYakka.entitlements +8 -0
  69. package/template/ios/ClubYakka/GoogleService-Info.plist +30 -0
  70. package/template/ios/ClubYakka/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png +0 -0
  71. package/template/ios/ClubYakka/Images.xcassets/AppIcon.appiconset/Contents.json +14 -0
  72. package/template/ios/ClubYakka/Images.xcassets/Contents.json +6 -0
  73. package/template/ios/ClubYakka/Images.xcassets/SplashScreenBackground.colorset/Contents.json +20 -0
  74. package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/Contents.json +23 -0
  75. package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/image.png +0 -0
  76. package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/image@2x.png +0 -0
  77. package/template/ios/ClubYakka/Images.xcassets/SplashScreenLegacy.imageset/image@3x.png +0 -0
  78. package/template/ios/ClubYakka/Info.plist +101 -0
  79. package/template/ios/ClubYakka/PrivacyInfo.xcprivacy +50 -0
  80. package/template/ios/ClubYakka/SplashScreen.storyboard +48 -0
  81. package/template/ios/ClubYakka/Supporting/Expo.plist +12 -0
  82. package/template/ios/ClubYakka.xcodeproj/project.pbxproj +669 -0
  83. package/template/ios/ClubYakka.xcodeproj/xcshareddata/xcschemes/ClubYakka.xcscheme +88 -0
  84. package/template/ios/ClubYakka.xcworkspace/contents.xcworkspacedata +10 -0
  85. package/template/ios/ClubYakka.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +5 -0
  86. package/template/ios/Podfile +84 -0
  87. package/template/ios/Podfile.lock +2698 -0
  88. package/template/ios/Podfile.properties.json +8 -0
  89. package/template/metro.config.js +17 -0
  90. package/template/package.json +70 -0
  91. package/template/patches/react-native-background-upload+6.6.0.patch +704 -0
  92. package/template/react-native.config.js +7 -0
  93. package/template/src/assets/fonts/Outfit-Bold.ttf +0 -0
  94. package/template/src/assets/fonts/Outfit-Light.ttf +0 -0
  95. package/template/src/assets/fonts/Outfit-Medium.ttf +0 -0
  96. package/template/src/assets/fonts/Outfit-Regular.ttf +0 -0
  97. package/template/src/assets/fonts/Outfit-SemiBold.ttf +0 -0
  98. package/template/src/assets/icons/BG.svg +33 -0
  99. package/template/src/assets/icons/Ic_Users.svg +6 -0
  100. package/template/src/assets/icons/arrow-left-white.svg +4 -0
  101. package/template/src/assets/icons/arrow-left.svg +4 -0
  102. package/template/src/assets/icons/bell.svg +4 -0
  103. package/template/src/assets/icons/bottomSheetIcone.svg +3 -0
  104. package/template/src/assets/icons/call.svg +4 -0
  105. package/template/src/assets/icons/camera.svg +0 -0
  106. package/template/src/assets/icons/chatAppleGreenBG.svg +22 -0
  107. package/template/src/assets/icons/check.svg +4 -0
  108. package/template/src/assets/icons/check_Radio.svg +4 -0
  109. package/template/src/assets/icons/chevron-right.svg +3 -0
  110. package/template/src/assets/icons/clubDefauldImage.png +0 -0
  111. package/template/src/assets/icons/curvBackgroundView.svg +3 -0
  112. package/template/src/assets/icons/defaultClub.png +0 -0
  113. package/template/src/assets/icons/email.svg +3 -0
  114. package/template/src/assets/icons/emptyUser.svg +5 -0
  115. package/template/src/assets/icons/eye-Hide.svg +8 -0
  116. package/template/src/assets/icons/eye.svg +4 -0
  117. package/template/src/assets/icons/gallery.svg +4 -0
  118. package/template/src/assets/icons/home.svg +4 -0
  119. package/template/src/assets/icons/ic_Calendar.svg +14 -0
  120. package/template/src/assets/icons/ic_Calendar_blue.svg +12 -0
  121. package/template/src/assets/icons/ic_Calendar_white.svg +12 -0
  122. package/template/src/assets/icons/ic_Chat.svg +14 -0
  123. package/template/src/assets/icons/ic_ChatAppleGreen.svg +21 -0
  124. package/template/src/assets/icons/ic_ChatAppleWhite.svg +21 -0
  125. package/template/src/assets/icons/ic_Download.svg +6 -0
  126. package/template/src/assets/icons/ic_Events.svg +6 -0
  127. package/template/src/assets/icons/ic_HeadCoachIcon.svg +3 -0
  128. package/template/src/assets/icons/ic_Membership.svg +10 -0
  129. package/template/src/assets/icons/ic_Notification.svg +6 -0
  130. package/template/src/assets/icons/ic_Raffles.svg +14 -0
  131. package/template/src/assets/icons/ic_Referral_Members.svg +12 -0
  132. package/template/src/assets/icons/ic_Shop.svg +13 -0
  133. package/template/src/assets/icons/ic_Teams.svg +8 -0
  134. package/template/src/assets/icons/ic_Volunteer.svg +9 -0
  135. package/template/src/assets/icons/ic_add.svg +3 -0
  136. package/template/src/assets/icons/ic_addCircle.svg +5 -0
  137. package/template/src/assets/icons/ic_chatSend.svg +4 -0
  138. package/template/src/assets/icons/ic_chat_blue_bg.svg +22 -0
  139. package/template/src/assets/icons/ic_clock_blue.svg +4 -0
  140. package/template/src/assets/icons/ic_delete.svg +8 -0
  141. package/template/src/assets/icons/ic_more.svg +5 -0
  142. package/template/src/assets/icons/ic_mute.svg +10 -0
  143. package/template/src/assets/icons/ic_pdf.svg +3 -0
  144. package/template/src/assets/icons/ic_pending_AppleGreen.svg +11 -0
  145. package/template/src/assets/icons/ic_right_appleGreen.svg +11 -0
  146. package/template/src/assets/icons/ic_unread_chat_blue_bg.svg +23 -0
  147. package/template/src/assets/icons/ic_volunteer_Member.svg +8 -0
  148. package/template/src/assets/icons/index.ts +144 -0
  149. package/template/src/assets/icons/location-blue.svg +4 -0
  150. package/template/src/assets/icons/location-white.svg +4 -0
  151. package/template/src/assets/icons/location.svg +4 -0
  152. package/template/src/assets/icons/lock.svg +5 -0
  153. package/template/src/assets/icons/log-out.svg +5 -0
  154. package/template/src/assets/icons/login_logo.svg +9 -0
  155. package/template/src/assets/icons/mail.svg +4 -0
  156. package/template/src/assets/icons/or_saprater.svg +5 -0
  157. package/template/src/assets/icons/password.svg +4 -0
  158. package/template/src/assets/icons/profile-appleGreen.svg +6 -0
  159. package/template/src/assets/icons/search.svg +3 -0
  160. package/template/src/assets/icons/settings.svg +4 -0
  161. package/template/src/assets/icons/success.svg +4 -0
  162. package/template/src/assets/icons/unCheck_Radio.svg +3 -0
  163. package/template/src/assets/icons/uncheck.svg +3 -0
  164. package/template/src/assets/icons/upload_Image.svg +6 -0
  165. package/template/src/assets/icons/upload_Image_Member.svg +6 -0
  166. package/template/src/assets/icons/user.svg +4 -0
  167. package/template/src/assets/icons/wifi.svg +1 -0
  168. package/template/src/assets/images/Splash.png +0 -0
  169. package/template/src/assets/images/SplashLogo.png +0 -0
  170. package/template/src/assets/images/background.png +0 -0
  171. package/template/src/assets/images/clubDefauldImage.png +0 -0
  172. package/template/src/assets/images/index.tsx +9 -0
  173. package/template/src/assets/index.ts +1 -0
  174. package/template/src/components/common/AddMemberModal.tsx +543 -0
  175. package/template/src/components/common/AppLoader.tsx +91 -0
  176. package/template/src/components/common/Button.tsx +173 -0
  177. package/template/src/components/common/ClubCard.tsx +248 -0
  178. package/template/src/components/common/Icon.tsx +93 -0
  179. package/template/src/components/common/IconAlt.tsx +65 -0
  180. package/template/src/components/common/IconButton.tsx +92 -0
  181. package/template/src/components/common/ImagePicker.tsx +451 -0
  182. package/template/src/components/common/LoadingSpinner.tsx +30 -0
  183. package/template/src/components/common/OTPInput.tsx +128 -0
  184. package/template/src/components/common/ReminderCalendar.tsx +129 -0
  185. package/template/src/components/common/ReminderCardItem.tsx +91 -0
  186. package/template/src/components/common/SafeAreaWrapper.tsx +27 -0
  187. package/template/src/components/common/SetReminderModal.tsx +308 -0
  188. package/template/src/components/common/SlowInternet.js +57 -0
  189. package/template/src/components/common/TeamCard.tsx +297 -0
  190. package/template/src/components/common/TextInput.tsx +227 -0
  191. package/template/src/components/common/ToastConfig.tsx +103 -0
  192. package/template/src/components/common/ToastManager.ts +86 -0
  193. package/template/src/components/common/UploadProgressModal.tsx +284 -0
  194. package/template/src/components/common/WrapperContainer.tsx +39 -0
  195. package/template/src/components/common/index.ts +19 -0
  196. package/template/src/constants/Constants.tsx +7 -0
  197. package/template/src/constants/Fonts.tsx +30 -0
  198. package/template/src/constants/index.ts +45 -0
  199. package/template/src/constants/strings.ts +211 -0
  200. package/template/src/constants/theme.ts +72 -0
  201. package/template/src/contexts/AuthContext.tsx +268 -0
  202. package/template/src/contexts/index.ts +2 -0
  203. package/template/src/hooks/index.ts +3 -0
  204. package/template/src/hooks/useImageUpload.ts +199 -0
  205. package/template/src/index.ts +8 -0
  206. package/template/src/navigation/AuthStack.tsx +67 -0
  207. package/template/src/navigation/MainStack.tsx +183 -0
  208. package/template/src/navigation/MiddleStack.tsx +35 -0
  209. package/template/src/navigation/RootNavigator.tsx +101 -0
  210. package/template/src/navigation/index.ts +5 -0
  211. package/template/src/navigation/navigationRef.ts +5 -0
  212. package/template/src/providers/QueryProvider.tsx +30 -0
  213. package/template/src/screens/DetailsScreen.tsx +271 -0
  214. package/template/src/screens/HomeScreen.tsx +736 -0
  215. package/template/src/screens/ProfileScreen.tsx +202 -0
  216. package/template/src/screens/SettingsScreen.tsx +253 -0
  217. package/template/src/screens/SportHubScreen.tsx +280 -0
  218. package/template/src/screens/auth/AddMamber.tsx +428 -0
  219. package/template/src/screens/auth/ForgotPasswordScreen.tsx +176 -0
  220. package/template/src/screens/auth/LoginScreen.tsx +286 -0
  221. package/template/src/screens/auth/OTPVerifyScreen.tsx +359 -0
  222. package/template/src/screens/auth/RegisterScreen.tsx +430 -0
  223. package/template/src/screens/auth/SuccessScreen.tsx +201 -0
  224. package/template/src/screens/auth/WalcomeScreen.tsx +274 -0
  225. package/template/src/screens/auth/index.ts +8 -0
  226. package/template/src/screens/chat/ChatScreen.tsx +1819 -0
  227. package/template/src/screens/chat/ChatThreadsScreen.tsx +360 -0
  228. package/template/src/screens/chat/ReportMessageScreen.tsx +238 -0
  229. package/template/src/screens/clubs/Announcements.tsx +426 -0
  230. package/template/src/screens/clubs/BuyRaffleTicketsScreen.tsx +568 -0
  231. package/template/src/screens/clubs/ClubDeteils.tsx +497 -0
  232. package/template/src/screens/clubs/JoinClub.tsx +841 -0
  233. package/template/src/screens/events/EventScreen.tsx +460 -0
  234. package/template/src/screens/index.ts +42 -0
  235. package/template/src/screens/raffles/MyReferralMembersScreen.tsx +758 -0
  236. package/template/src/screens/raffles/RaffleDetailsScreen.tsx +762 -0
  237. package/template/src/screens/raffles/RafflesScreen.tsx +495 -0
  238. package/template/src/screens/raffles/SetRaffleReminderScreen.tsx +390 -0
  239. package/template/src/screens/teams/JoinTeamScreen.tsx +464 -0
  240. package/template/src/screens/teams/MyTeamDetailsScreen.tsx +979 -0
  241. package/template/src/screens/teams/MyTeamScreen.tsx +568 -0
  242. package/template/src/screens/teams/PendingRequestsScreen.tsx +426 -0
  243. package/template/src/screens/volunteerOpportunities/SetReminderScreen.tsx +631 -0
  244. package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesDetailsScreen.tsx +1049 -0
  245. package/template/src/screens/volunteerOpportunities/VolunteerOpportunitiesScreen.tsx +608 -0
  246. package/template/src/services/api.ts +167 -0
  247. package/template/src/services/authService.ts +422 -0
  248. package/template/src/services/index.ts +5 -0
  249. package/template/src/services/mainServices.ts +1963 -0
  250. package/template/src/stores/authStore.ts +159 -0
  251. package/template/src/stores/index.ts +2 -0
  252. package/template/src/types/index.ts +85 -0
  253. package/template/src/types/navigation.ts +206 -0
  254. package/template/src/utils/AzureUploaderService.ts +371 -0
  255. package/template/src/utils/ClubSearchManager.ts +222 -0
  256. package/template/src/utils/NotificationManager.ts +503 -0
  257. package/template/src/utils/UploadDebugUtil.ts +107 -0
  258. package/template/src/utils/imagePicker.ts +321 -0
  259. package/template/src/utils/index.ts +111 -0
  260. package/template/src/utils/permissions.ts +277 -0
  261. package/template/src/utils/scaling.ts +14 -0
  262. package/template/src/utils/storage.ts +247 -0
  263. package/template/src/utils/usePermissions.ts +275 -0
  264. package/template/src/utils/validation.ts +340 -0
  265. package/template/tsconfig.json +50 -0
  266. package/template/types/svg.d.ts +6 -0
@@ -0,0 +1,608 @@
1
+ import React, { useMemo, useRef, useState } from 'react';
2
+ import { View, Text, StyleSheet, StatusBar, Platform, TouchableOpacity, Image, FlatList, ListRenderItem, ScrollView, RefreshControl, ActivityIndicator } from 'react-native';
3
+ import { SafeAreaView } from 'react-native-safe-area-context';
4
+ import { VolunteerOpportunitiesScreenProps } from '@navigation';
5
+ import { Strings, theme } from '@constants';
6
+ import { moderateScale } from '@utils/scaling';
7
+ import { Fonts } from '@constants/Fonts';
8
+ import SVG from '@assets/icons';
9
+ import { useVolunteerHoursDetails, useInfiniteVolunteerOpportunities, VolunteerOpportunity } from '@services/mainServices';
10
+
11
+
12
+ type VolunteerOpportunityTab = {
13
+ id: number;
14
+ tabName: string;
15
+ };
16
+
17
+ const VOLUNTEER_OPPORTUNITY_TABS: VolunteerOpportunityTab[] = [
18
+ { id: 1, tabName: "Upcoming" },
19
+ { id: 2, tabName: "Requested" },
20
+ { id: 3, tabName: "Ongoing" },
21
+ { id: 4, tabName: "Past" },
22
+ ];
23
+
24
+
25
+ export const VolunteerOpportunitiesScreen: React.FC<VolunteerOpportunitiesScreenProps> = ({ navigation, route }) => {
26
+ const { selectedClub, selectedMember: initialSelectedMember } = route?.params ?? {};
27
+ const scrollViewRef = useRef<ScrollView>(null);
28
+ const chipRefs = useRef<{ [key: string]: View | null }>({});
29
+
30
+ const memberId = initialSelectedMember?.id;
31
+ const clubId = selectedClub?.id;
32
+
33
+ const [activeTabId, setActiveTabId] = useState<number>(VOLUNTEER_OPPORTUNITY_TABS[0]?.id ?? 1);
34
+ const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
35
+
36
+ const { data: hoursData, isLoading, isRefetching, refetch: hoursDataRefetch } = useVolunteerHoursDetails(memberId, clubId);
37
+ const userHoursData = hoursData?.data?.data;
38
+
39
+ // Get opportunities with infinite query based on active tab (requestType)
40
+ const { data: opportunitiesData, isLoading: isLoadingOpportunities, isRefetching: isRefetchingOpportunities, fetchNextPage, hasNextPage, isFetchingNextPage, refetch: refetchOpportunities, } = useInfiniteVolunteerOpportunities(memberId as number, clubId as number, activeTabId);
41
+
42
+ // Flatten all pages into a single array
43
+ const opportunities = useMemo(() => {
44
+ if (!opportunitiesData?.pages) return [];
45
+
46
+ return opportunitiesData.pages.flatMap((page) => {
47
+ const pageData = page?.data?.data;
48
+ if (Array.isArray(pageData)) {
49
+ return pageData;
50
+ } else if (pageData && typeof pageData === 'object' && 'opportunities' in pageData) {
51
+ return pageData.opportunities || [];
52
+ }
53
+ return [];
54
+ });
55
+ }, [opportunitiesData]);
56
+
57
+ const renderHeader = () => (
58
+
59
+ <View style={{ marginHorizontal: theme.spacing.lg, }}>
60
+ <View style={styles.statsCard}>
61
+ <View style={styles.statsRow}>
62
+ <View style={styles.statBlock}>
63
+ <Text style={styles.statValue}>{userHoursData?.totalApprovedHours}{Strings.VolunteerOpportunities.HOURS}</Text>
64
+ <Text style={styles.statLabel}>{Strings.VolunteerOpportunities.TOTAL_HOURS}</Text>
65
+ </View>
66
+ <View style={styles.statBlock}>
67
+ <Text style={styles.statValue}>{userHoursData?.thisMonthApprovedHours}{Strings.VolunteerOpportunities.HOURS}</Text>
68
+ <Text style={styles.statLabel}>{Strings.VolunteerOpportunities.THIS_MONTH}</Text>
69
+ </View>
70
+ </View>
71
+ {userHoursData?.nextShiftDisplay !== "No upcoming shift" && (
72
+ <View style={styles.divider} />
73
+ )}
74
+ {userHoursData?.nextShiftDisplay !== "No upcoming shift" && (
75
+ <View style={{ paddingTop: theme.spacing.sm }}>
76
+ <Text style={styles.nextShift}>{userHoursData?.nextShiftDisplay}</Text>
77
+ <Text style={styles.nextShiftLabel}>{Strings.VolunteerOpportunities.NEXT_SHIFT}</Text>
78
+ </View>)}
79
+ </View>
80
+
81
+ <ScrollView bounces ref={scrollViewRef} horizontal style={styles.tabsRowContainer} contentContainerStyle={styles.tabsRow} showsHorizontalScrollIndicator={false}>
82
+ {VOLUNTEER_OPPORTUNITY_TABS.map(tab => {
83
+ const tabKey = `tab-${tab.id}`;
84
+ return (
85
+ <TouchableOpacity
86
+ key={tab.id}
87
+ activeOpacity={0.8}
88
+ ref={(ref) => {
89
+ chipRefs.current[tabKey] = ref;
90
+ }}
91
+ onPress={() => {
92
+ setActiveTabId(tab.id);
93
+ setTimeout(() => {
94
+ chipRefs.current[tabKey]?.measureLayout(
95
+ scrollViewRef.current as any,
96
+ (x) => {
97
+ scrollViewRef.current?.scrollTo({ x: Math.max(0, x - 20), animated: true });
98
+ },
99
+ () => { }
100
+ );
101
+ }, 100);
102
+ }}>
103
+ <View style={[styles.tabPill, activeTabId === tab.id && styles.tabPillActive]}>
104
+ <Text style={[styles.tabText, activeTabId === tab.id && styles.tabTextActive]}>{tab.tabName}</Text>
105
+ </View>
106
+ </TouchableOpacity>
107
+ );
108
+ })}
109
+ </ScrollView>
110
+ </View>
111
+ );
112
+
113
+ const renderOpportunity: ListRenderItem<VolunteerOpportunity> = ({ item }) => {
114
+ const location = item?.address || 'Location not available';
115
+ const dateLabel = item.scheduleDate || 'Date not available';
116
+
117
+
118
+ const createNavigationData = () => {
119
+ const navigationData = {
120
+ selectedClub: selectedClub,
121
+ selectedMember: selectedClub?.selectedMember,
122
+ item: item
123
+ };
124
+ console.log('Navigation data being passed:', navigationData);
125
+ return navigationData;
126
+ };
127
+
128
+ return (
129
+ <View style={styles.opCard}>
130
+ <View style={{ paddingHorizontal: theme.spacing.sm }}>
131
+ <Text numberOfLines={1} ellipsizeMode='tail' style={styles.opTitle}>{item.title}</Text>
132
+ </View>
133
+ {location !== 'Location not available' && (
134
+ <View style={styles.opMetaRow}>
135
+ <SVG.locationBlue width={moderateScale(20)} height={moderateScale(20)} />
136
+ <Text style={styles.opMetaText}>{location}</Text>
137
+ </View>
138
+ )}
139
+ {dateLabel !== 'Date not available' && (
140
+ <View style={styles.opMetaRow}>
141
+ <SVG.CalendarBlue width={moderateScale(20)} height={moderateScale(20)} />
142
+ <Text style={styles.opMetaText}>{dateLabel}</Text>
143
+ </View>
144
+ )}
145
+ <TouchableOpacity style={styles.detailsButton} activeOpacity={0.8}
146
+ onPress={() => {
147
+ navigation.navigate("VolunteerOpportunitiesDetailsScreen", createNavigationData())
148
+ }}>
149
+ <Text style={styles.detailsButtonText}>{Strings.VolunteerOpportunities.VIEW_MORE_DETAILS}</Text>
150
+ </TouchableOpacity>
151
+ </View>
152
+ );
153
+ };
154
+
155
+ const renderListFooter = () => {
156
+ if (!isFetchingNextPage) return null;
157
+ return (
158
+ <View style={styles.loadingFooter}>
159
+ <ActivityIndicator size="small" color={theme.colors.primary} />
160
+ </View>
161
+ );
162
+ };
163
+
164
+ const loadMoreOpportunities = () => {
165
+ if (hasNextPage && !isFetchingNextPage) {
166
+ fetchNextPage();
167
+ }
168
+ };
169
+
170
+
171
+
172
+
173
+
174
+ return (
175
+ <View style={styles.container}>
176
+ <StatusBar
177
+ barStyle="light-content"
178
+ backgroundColor={theme.colors.blue}
179
+ translucent={Platform.OS === 'android' ? true : false}
180
+ />
181
+ {Platform.OS === 'ios' && <View style={styles.statusBarBackground} />}
182
+ <SafeAreaView style={styles.header} >
183
+
184
+ <View style={{ flexDirection: 'row', paddingHorizontal: theme.spacing.lg }}>
185
+ <TouchableOpacity onPress={() => navigation.goBack()} hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}>
186
+ <SVG.arrowLeft_white width={moderateScale(25)} height={moderateScale(25)} />
187
+ </TouchableOpacity>
188
+ <View style={{ flexDirection: 'row' }}>
189
+ <View style={styles.userConSty}>
190
+ {!!selectedClub?.clubImage ? (<Image
191
+ // onLoadEnd={() => setIsLoading(false)}
192
+ // onLoad={() => setIsLoading(true)}
193
+ // onLoadStart={() => setIsLoading(true)}
194
+ source={{ uri: selectedClub?.clubImage }} style={styles.userDetailsSty}
195
+ resizeMode='cover'
196
+ />
197
+ ) : (
198
+ <View style={styles.placeholderLogoHeader}>
199
+ <SVG.UsersIcon width={moderateScale(20)} height={moderateScale(20)} />
200
+ </View>
201
+ )}
202
+ </View>
203
+ <Text style={styles.userNameSty}>{selectedClub?.clubName || 'Unknown Club'}</Text>
204
+ </View>
205
+ </View>
206
+ <View style={{ paddingHorizontal: theme.spacing.lg }}>
207
+ <Text style={styles.headerTitle}>{Strings.VolunteerOpportunities.VOLUNTEER_OPPORTUNITIES}</Text>
208
+ </View>
209
+ <View style={styles.addMemberContainer}>
210
+ </View>
211
+ <View style={styles.content}>
212
+ <FlatList
213
+ data={opportunities}
214
+ keyExtractor={(item, index) => {
215
+ const id = item?.id?.toString() || '';
216
+ const slotId = (item?.slotId || item?.slot_id || '').toString();
217
+ const date = item?.scheduleDate || '';
218
+ const type = (typeof item?.opportunityType !== 'undefined' ? String(item?.opportunityType) : '');
219
+ const parts = [id, slotId, date, type].filter(part => part && part.toString().trim() !== '');
220
+ return parts.length ? `op-${parts.join('-')}` : `op-idx-${index}`;
221
+ }}
222
+ renderItem={renderOpportunity}
223
+ showsVerticalScrollIndicator={false}
224
+ ListHeaderComponent={renderHeader}
225
+ ListFooterComponent={renderListFooter}
226
+ contentContainerStyle={{ paddingBottom: theme.spacing.xl }}
227
+ onEndReached={loadMoreOpportunities}
228
+ onEndReachedThreshold={0.5}
229
+ refreshControl={
230
+ <RefreshControl
231
+ refreshing={isRefreshing || isRefetching || isLoading || isRefetchingOpportunities}
232
+ onRefresh={async () => {
233
+ setIsRefreshing(true);
234
+ try {
235
+ await Promise.all([
236
+ hoursDataRefetch(),
237
+ refetchOpportunities(),
238
+ ]);
239
+ } finally {
240
+ setIsRefreshing(false);
241
+ }
242
+ }}
243
+ colors={[theme.colors.primary]}
244
+ tintColor={theme.colors.primary}
245
+ />
246
+ }
247
+ ListEmptyComponent={
248
+ !isLoadingOpportunities && opportunities.length === 0 ? (
249
+ <View style={styles.emptyContainer}>
250
+ <Text style={styles.emptyText}>No opportunities found</Text>
251
+ </View>
252
+ ) : null
253
+ }
254
+ />
255
+ </View>
256
+ </SafeAreaView>
257
+ </View>
258
+ );
259
+ };
260
+
261
+ const styles = StyleSheet.create({
262
+ container: {
263
+ flex: 1,
264
+ backgroundColor: theme.colors.blue,
265
+ },
266
+ statusBarBackground: {
267
+ position: 'absolute',
268
+ top: 0,
269
+ left: 0,
270
+ right: 0,
271
+ height: Platform.OS === 'ios' ? 44 : 0, // Status bar height for iOS
272
+ backgroundColor: theme.colors.blue,
273
+ zIndex: 1000,
274
+ },
275
+ header: {
276
+ flex: 1,
277
+ backgroundColor: theme.colors.blue,
278
+ },
279
+ userConSty: {
280
+ marginHorizontal: moderateScale(10),
281
+ width: moderateScale(30),
282
+ height: moderateScale(30),
283
+ borderRadius: moderateScale(15),
284
+ borderWidth: 1.5,
285
+ borderColor: theme.colors.imageBorder,
286
+ backgroundColor: theme.colors.background,
287
+ alignItems: 'center',
288
+ justifyContent: 'center'
289
+ },
290
+ headerTitle: {
291
+ marginTop: moderateScale(10),
292
+ fontFamily: Fonts.outfitMedium,
293
+ fontSize: moderateScale(22),
294
+ color: theme.colors.white,
295
+ },
296
+ skipButton: {
297
+ alignSelf: 'flex-end',
298
+ paddingHorizontal: theme.spacing.sm,
299
+ paddingVertical: theme.spacing.xs,
300
+ },
301
+ skipButtonText: {
302
+ fontFamily: Fonts.outfitSemiBold,
303
+ fontSize: theme.typography.fontSize.md,
304
+ color: theme.colors.white,
305
+ },
306
+ addMemberContainer: {
307
+ backgroundColor: theme.colors.blue,
308
+ paddingHorizontal: theme.spacing.lg,
309
+ paddingBottom: theme.spacing.md,
310
+ },
311
+ addMemberButton: {
312
+ flexDirection: 'row',
313
+ alignItems: 'center',
314
+ justifyContent: 'center',
315
+ backgroundColor: 'transparent',
316
+ borderWidth: 1.5,
317
+ borderColor: theme.colors.appleGreen,
318
+ borderRadius: theme.borderRadius.xxl,
319
+ paddingVertical: theme.spacing.sm,
320
+ paddingHorizontal: theme.spacing.lg,
321
+ gap: theme.spacing.sm,
322
+ },
323
+ addMemberButtonText: {
324
+ fontFamily: Fonts.outfitMedium,
325
+ fontSize: theme.typography.fontSize.md,
326
+ color: theme.colors.appleGreen,
327
+ },
328
+ content: {
329
+ flex: 1,
330
+ backgroundColor: theme.colors.background,
331
+ paddingTop: theme.spacing.md,
332
+ borderTopLeftRadius: moderateScale(30),
333
+ borderTopRightRadius: moderateScale(30),
334
+ },
335
+ statsCard: {
336
+ backgroundColor: '#F6F9FE',
337
+ borderRadius: moderateScale(16),
338
+ paddingHorizontal: theme.spacing.md,
339
+ paddingVertical: theme.spacing.sm,
340
+ borderWidth: 1,
341
+ borderColor: '#E6EEF9',
342
+ },
343
+ statsRow: {
344
+ flexDirection: 'row',
345
+ justifyContent: 'space-between',
346
+ marginBottom: theme.spacing.md,
347
+ },
348
+ statBlock: {
349
+ width: '48%'
350
+ },
351
+ statValue: {
352
+ fontFamily: Fonts.outfitSemiBold,
353
+ fontSize: moderateScale(16),
354
+ color: theme.colors.black,
355
+ },
356
+ statLabel: {
357
+ marginTop: moderateScale(2),
358
+ fontFamily: Fonts.outfitMedium,
359
+ fontSize: theme.typography.fontSize.sm,
360
+ color: theme.colors.DarkGray,
361
+ },
362
+ divider: {
363
+ height: StyleSheet.hairlineWidth,
364
+ backgroundColor: '#E1E8F5',
365
+ },
366
+ nextShift: {
367
+ fontFamily: Fonts.outfitMedium,
368
+ fontSize: theme.typography.fontSize.md,
369
+ color: theme.colors.appleGreen,
370
+ },
371
+ nextShiftLabel: {
372
+ marginTop: moderateScale(2),
373
+ fontFamily: Fonts.outfitMedium,
374
+ fontSize: theme.typography.fontSize.sm,
375
+ color: theme.colors.DarkGray,
376
+ },
377
+ tabsRowContainer: {
378
+ marginTop: theme.spacing.md,
379
+ marginHorizontal: -theme.spacing.sm,
380
+ // paddingHorizontal: theme.spacing.lg,
381
+ },
382
+ tabsRow: {
383
+ alignItems: 'center',
384
+ gap: theme.spacing.sm,
385
+ },
386
+ tabPill: {
387
+ paddingHorizontal: theme.spacing.md,
388
+ paddingVertical: theme.spacing.sm,
389
+ backgroundColor: theme.colors.lightLavenderGray,
390
+ borderRadius: theme.borderRadius.xxl,
391
+ },
392
+ tabPillActive: {
393
+ backgroundColor: theme.colors.blue,
394
+ borderWidth: 1,
395
+ borderColor: theme.colors.blue,
396
+ },
397
+ tabText: {
398
+ fontFamily: Fonts.outfitMedium,
399
+ fontSize: moderateScale(12),
400
+ color: theme.colors.blue,
401
+ },
402
+ tabTextActive: {
403
+ color: theme.colors.white,
404
+ },
405
+ opCard: {
406
+ backgroundColor: '#F6F9FE',
407
+ borderRadius: moderateScale(16),
408
+ padding: theme.spacing.xs,
409
+ paddingTop: theme.spacing.sm,
410
+ borderWidth: 1,
411
+ borderColor: '#E6EEF9',
412
+ marginTop: theme.spacing.lg,
413
+ marginHorizontal: theme.spacing.lg,
414
+ },
415
+ opTitle: {
416
+ fontFamily: Fonts.outfitSemiBold,
417
+ fontSize: moderateScale(16),
418
+ color: theme.colors.black,
419
+ },
420
+ opMetaRow: {
421
+ flexDirection: 'row',
422
+ alignItems: 'center',
423
+ gap: theme.spacing.sm,
424
+ paddingHorizontal: theme.spacing.sm,
425
+ marginVertical: theme.spacing.xs,
426
+ },
427
+ opMetaText: {
428
+ fontFamily: Fonts.outfitMedium,
429
+ fontSize: theme.typography.fontSize.sm,
430
+ color: '#4C5B7B',
431
+ },
432
+ opTopRight: {
433
+ right: theme.spacing.lg,
434
+ top: theme.spacing.lg,
435
+ },
436
+ ctaPill: {
437
+ paddingVertical: moderateScale(4),
438
+ paddingHorizontal: theme.spacing.md,
439
+ borderRadius: theme.borderRadius.xxl,
440
+ borderWidth: 1,
441
+
442
+ },
443
+ joinPill: {
444
+ backgroundColor: '#F1FCE6',
445
+ borderColor: '#C6E8A4',
446
+ },
447
+ joinPillText: {
448
+ color: '#6DB100',
449
+ },
450
+ requestPill: {
451
+ backgroundColor: '#F6FBEF',
452
+ borderColor: '#DDEFC6',
453
+ },
454
+ requestPillText: {
455
+ color: '#7FB800',
456
+ },
457
+ ctaPillText: {
458
+ fontFamily: Fonts.outfitSemiBold,
459
+ fontSize: theme.typography.fontSize.xs,
460
+ },
461
+ detailsButton: {
462
+ marginHorizontal: moderateScale(-2),
463
+ marginTop: theme.spacing.md,
464
+ backgroundColor: theme.colors.white,
465
+ paddingVertical: theme.spacing.sm,
466
+ alignItems: 'center',
467
+ borderWidth: 1,
468
+ borderColor: '#E6EEF9',
469
+ borderBottomRightRadius: moderateScale(12),
470
+ borderBottomLeftRadius: moderateScale(12),
471
+ },
472
+ detailsButtonText: {
473
+ fontFamily: Fonts.outfitSemiBold,
474
+ fontSize: theme.typography.fontSize.sm,
475
+ color: theme.colors.blue,
476
+ },
477
+ placeholderLogoHeader: {
478
+ width: moderateScale(20),
479
+ height: moderateScale(20),
480
+ borderRadius: moderateScale(10),
481
+ alignItems: 'center',
482
+ justifyContent: 'center'
483
+ },
484
+ userNameSty: {
485
+ marginTop: moderateScale(5),
486
+ color: theme.colors.white,
487
+ fontFamily: Fonts.outfitMedium,
488
+ fontSize: moderateScale(15)
489
+ },
490
+ userDetailsSty: {
491
+ width: moderateScale(30),
492
+ height: moderateScale(30),
493
+ borderRadius: moderateScale(15),
494
+ },
495
+ loadingFooter: {
496
+ paddingVertical: theme.spacing.md,
497
+ alignItems: 'center',
498
+ },
499
+ emptyContainer: {
500
+ paddingVertical: theme.spacing.xl,
501
+ alignItems: 'center',
502
+ marginTop: theme.spacing.xl,
503
+ },
504
+ emptyText: {
505
+ fontFamily: Fonts.outfitMedium,
506
+ fontSize: theme.typography.fontSize.md,
507
+ color: theme.colors.DarkGray,
508
+ },
509
+ slotLoadingContainer: {
510
+ flex: 1,
511
+ justifyContent: 'center',
512
+ alignItems: 'center',
513
+ paddingVertical: theme.spacing.lg,
514
+ },
515
+
516
+
517
+
518
+
519
+
520
+ modalOverlay: {
521
+ flex: 1,
522
+ justifyContent: 'flex-end',
523
+ },
524
+ modalBackdrop: {
525
+ position: 'absolute',
526
+ top: 0,
527
+ left: 0,
528
+ right: 0,
529
+ bottom: 0,
530
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
531
+ },
532
+ modalContainer: {
533
+ backgroundColor: theme.colors.white,
534
+ borderTopLeftRadius: moderateScale(20),
535
+ borderTopRightRadius: moderateScale(20),
536
+ paddingHorizontal: theme.spacing.lg,
537
+ paddingTop: theme.spacing.sm,
538
+ paddingBottom: theme.spacing.xl,
539
+ height: '45%',
540
+ maxHeight: '45%',
541
+ },
542
+ dragHandle: {
543
+ width: moderateScale(40),
544
+ height: moderateScale(4),
545
+ backgroundColor: theme.colors.border,
546
+ borderRadius: moderateScale(2),
547
+ alignSelf: 'center',
548
+ marginBottom: theme.spacing.sm,
549
+ },
550
+ membersList: {
551
+ flex: 1,
552
+ },
553
+ memberListItem: {
554
+ marginVertical: theme.spacing.xs,
555
+ height: moderateScale(55),
556
+ alignItems: 'center',
557
+ paddingVertical: theme.spacing.md,
558
+ flexDirection: 'row',
559
+ backgroundColor: theme.colors.lightLavenderGray,
560
+ borderRadius: theme.spacing.md
561
+ },
562
+ headerContainer: {
563
+ paddingVertical: theme.spacing.xs,
564
+ },
565
+ mamberHeaderTitle: {
566
+ fontFamily: Fonts.outfitBold,
567
+ fontSize: moderateScale(16),
568
+ color: theme.colors.text,
569
+ },
570
+ profileImage: {
571
+ width: moderateScale(40),
572
+ height: moderateScale(40),
573
+ borderRadius: moderateScale(20),
574
+ marginRight: moderateScale(10),
575
+ },
576
+ memberName: {
577
+ fontFamily: Fonts.outfitSemiBold,
578
+ fontSize: moderateScale(16),
579
+ color: theme.colors.text,
580
+ },
581
+ memberNameContainer: {
582
+ justifyContent: 'center',
583
+ },
584
+ memberNameSubtitle: {
585
+ fontFamily: Fonts.outfitMedium,
586
+ fontSize: moderateScale(12),
587
+ color: theme.colors.border,
588
+ },
589
+ loadingIndicator: {
590
+ position: 'absolute',
591
+ top: 0,
592
+ left: 0,
593
+ right: 0,
594
+ bottom: 0,
595
+ },
596
+ profileImageContainer: {
597
+ backgroundColor: theme.colors.white,
598
+ width: moderateScale(40),
599
+ height: moderateScale(40),
600
+ borderRadius: moderateScale(20),
601
+ alignItems: 'center',
602
+ justifyContent: 'center',
603
+ },
604
+ nextButton: {
605
+ width: '100%',
606
+ marginBottom: theme.spacing.md,
607
+ },
608
+ });