@varunindiit/create-rn-starter 1.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 (243) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -0
  3. package/bin/index.js +270 -0
  4. package/lib/prompt.js +63 -0
  5. package/lib/rename.js +239 -0
  6. package/lib/scaffold.js +110 -0
  7. package/lib/utils.js +122 -0
  8. package/package.json +38 -0
  9. package/template/.eslintrc.js +4 -0
  10. package/template/.prettierrc.js +5 -0
  11. package/template/.watchmanconfig +1 -0
  12. package/template/App.tsx +100 -0
  13. package/template/Gemfile +17 -0
  14. package/template/README.md +97 -0
  15. package/template/__tests__/App.test.tsx +13 -0
  16. package/template/_gitignore +75 -0
  17. package/template/android/app/build.gradle +119 -0
  18. package/template/android/app/debug.keystore +0 -0
  19. package/template/android/app/proguard-rules.pro +10 -0
  20. package/template/android/app/src/main/AndroidManifest.xml +45 -0
  21. package/template/android/app/src/main/assets/fonts/MonaSans-Black.ttf +0 -0
  22. package/template/android/app/src/main/assets/fonts/MonaSans-BlackItalic.ttf +0 -0
  23. package/template/android/app/src/main/assets/fonts/MonaSans-Bold.ttf +0 -0
  24. package/template/android/app/src/main/assets/fonts/MonaSans-BoldItalic.ttf +0 -0
  25. package/template/android/app/src/main/assets/fonts/MonaSans-ExtraBold.ttf +0 -0
  26. package/template/android/app/src/main/assets/fonts/MonaSans-ExtraBoldItalic.ttf +0 -0
  27. package/template/android/app/src/main/assets/fonts/MonaSans-ExtraLight.ttf +0 -0
  28. package/template/android/app/src/main/assets/fonts/MonaSans-ExtraLightItalic.ttf +0 -0
  29. package/template/android/app/src/main/assets/fonts/MonaSans-Italic.ttf +0 -0
  30. package/template/android/app/src/main/assets/fonts/MonaSans-Light.ttf +0 -0
  31. package/template/android/app/src/main/assets/fonts/MonaSans-LightItalic.ttf +0 -0
  32. package/template/android/app/src/main/assets/fonts/MonaSans-Medium.ttf +0 -0
  33. package/template/android/app/src/main/assets/fonts/MonaSans-MediumItalic.ttf +0 -0
  34. package/template/android/app/src/main/assets/fonts/MonaSans-Regular.ttf +0 -0
  35. package/template/android/app/src/main/assets/fonts/MonaSans-SemiBold.ttf +0 -0
  36. package/template/android/app/src/main/assets/fonts/MonaSans-SemiBoldItalic.ttf +0 -0
  37. package/template/android/app/src/main/java/com/awesomeproject/MainActivity.kt +22 -0
  38. package/template/android/app/src/main/java/com/awesomeproject/MainApplication.kt +27 -0
  39. package/template/android/app/src/main/res/drawable/launch_screen.png +0 -0
  40. package/template/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
  41. package/template/android/app/src/main/res/layout/launch_screen.xml +12 -0
  42. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  43. package/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  44. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  45. package/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  46. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  47. package/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  48. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  49. package/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  50. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  51. package/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  52. package/template/android/app/src/main/res/values/colors.xml +3 -0
  53. package/template/android/app/src/main/res/values/strings.xml +3 -0
  54. package/template/android/app/src/main/res/values/styles.xml +11 -0
  55. package/template/android/build.gradle +21 -0
  56. package/template/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  57. package/template/android/gradle/wrapper/gradle-wrapper.properties +7 -0
  58. package/template/android/gradle.properties +44 -0
  59. package/template/android/gradlew +248 -0
  60. package/template/android/gradlew.bat +98 -0
  61. package/template/android/link-assets-manifest.json +69 -0
  62. package/template/android/settings.gradle +6 -0
  63. package/template/app.json +4 -0
  64. package/template/babel.config.js +4 -0
  65. package/template/declarations.d.ts +6 -0
  66. package/template/env.example +20 -0
  67. package/template/index.js +10 -0
  68. package/template/ios/.xcode.env +11 -0
  69. package/template/ios/.xcode.env.local +1 -0
  70. package/template/ios/AwesomeProject/AppDelegate.swift +60 -0
  71. package/template/ios/AwesomeProject/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
  72. package/template/ios/AwesomeProject/Images.xcassets/Contents.json +6 -0
  73. package/template/ios/AwesomeProject/Images.xcassets/Splash.imageset/Contents.json +23 -0
  74. package/template/ios/AwesomeProject/Images.xcassets/Splash.imageset/Splash@1x.png +0 -0
  75. package/template/ios/AwesomeProject/Images.xcassets/Splash.imageset/Splash@2x.png +0 -0
  76. package/template/ios/AwesomeProject/Images.xcassets/Splash.imageset/Splash@3x.png +0 -0
  77. package/template/ios/AwesomeProject/Info.plist +89 -0
  78. package/template/ios/AwesomeProject/LaunchScreen.storyboard +40 -0
  79. package/template/ios/AwesomeProject/PrivacyInfo.xcprivacy +38 -0
  80. package/template/ios/AwesomeProject.xcodeproj/project.pbxproj +576 -0
  81. package/template/ios/AwesomeProject.xcodeproj/xcshareddata/xcschemes/AwesomeProject.xcscheme +88 -0
  82. package/template/ios/AwesomeProject.xcworkspace/contents.xcworkspacedata +10 -0
  83. package/template/ios/Podfile +68 -0
  84. package/template/ios/link-assets-manifest.json +69 -0
  85. package/template/jest.config.js +3 -0
  86. package/template/metro.config.js +24 -0
  87. package/template/package.json +68 -0
  88. package/template/react-native.config.js +7 -0
  89. package/template/src/assets/fonts/MonaSans-Black.ttf +0 -0
  90. package/template/src/assets/fonts/MonaSans-BlackItalic.ttf +0 -0
  91. package/template/src/assets/fonts/MonaSans-Bold.ttf +0 -0
  92. package/template/src/assets/fonts/MonaSans-BoldItalic.ttf +0 -0
  93. package/template/src/assets/fonts/MonaSans-ExtraBold.ttf +0 -0
  94. package/template/src/assets/fonts/MonaSans-ExtraBoldItalic.ttf +0 -0
  95. package/template/src/assets/fonts/MonaSans-ExtraLight.ttf +0 -0
  96. package/template/src/assets/fonts/MonaSans-ExtraLightItalic.ttf +0 -0
  97. package/template/src/assets/fonts/MonaSans-Italic.ttf +0 -0
  98. package/template/src/assets/fonts/MonaSans-Light.ttf +0 -0
  99. package/template/src/assets/fonts/MonaSans-LightItalic.ttf +0 -0
  100. package/template/src/assets/fonts/MonaSans-Medium.ttf +0 -0
  101. package/template/src/assets/fonts/MonaSans-MediumItalic.ttf +0 -0
  102. package/template/src/assets/fonts/MonaSans-Regular.ttf +0 -0
  103. package/template/src/assets/fonts/MonaSans-SemiBold.ttf +0 -0
  104. package/template/src/assets/fonts/MonaSans-SemiBoldItalic.ttf +0 -0
  105. package/template/src/assets/image/BackGroundAuth.png +0 -0
  106. package/template/src/assets/image/BackgroundVerification.png +0 -0
  107. package/template/src/assets/image/logo.png +0 -0
  108. package/template/src/assets/svg/add-circle.svg +5 -0
  109. package/template/src/assets/svg/airConditioning.svg +12 -0
  110. package/template/src/assets/svg/apple.svg +3 -0
  111. package/template/src/assets/svg/arrowDown.svg +3 -0
  112. package/template/src/assets/svg/back.svg +10 -0
  113. package/template/src/assets/svg/bag.svg +11 -0
  114. package/template/src/assets/svg/calender.svg +5 -0
  115. package/template/src/assets/svg/car.svg +10 -0
  116. package/template/src/assets/svg/carConfirm.svg +60 -0
  117. package/template/src/assets/svg/chatActive.svg +3 -0
  118. package/template/src/assets/svg/chatUnActive.svg +3 -0
  119. package/template/src/assets/svg/document-text.svg +6 -0
  120. package/template/src/assets/svg/gender.svg +11 -0
  121. package/template/src/assets/svg/google.svg +6 -0
  122. package/template/src/assets/svg/headphone.svg +3 -0
  123. package/template/src/assets/svg/homeActive.svg +3 -0
  124. package/template/src/assets/svg/homeUnActive.svg +3 -0
  125. package/template/src/assets/svg/logo.svg +18 -0
  126. package/template/src/assets/svg/logout.svg +5 -0
  127. package/template/src/assets/svg/maxBack.svg +4 -0
  128. package/template/src/assets/svg/message-text.svg +7 -0
  129. package/template/src/assets/svg/music.svg +5 -0
  130. package/template/src/assets/svg/noSmoking.svg +10 -0
  131. package/template/src/assets/svg/notification.svg +5 -0
  132. package/template/src/assets/svg/passenger.svg +4 -0
  133. package/template/src/assets/svg/phone.svg +3 -0
  134. package/template/src/assets/svg/rightArrow.svg +3 -0
  135. package/template/src/assets/svg/security-user.svg +5 -0
  136. package/template/src/assets/svg/star.svg +3 -0
  137. package/template/src/assets/svg/tick-circle.svg +4 -0
  138. package/template/src/assets/svg/trafficLight.svg +41 -0
  139. package/template/src/assets/svg/tripActive.svg +10 -0
  140. package/template/src/assets/svg/tripUnActive.svg +10 -0
  141. package/template/src/assets/svg/usbChargers.svg +3 -0
  142. package/template/src/assets/svg/user.svg +4 -0
  143. package/template/src/assets/svg/userActive.svg +3 -0
  144. package/template/src/assets/svg/userPlaceholder.svg +3 -0
  145. package/template/src/assets/svg/userUnActive.svg +3 -0
  146. package/template/src/components/AuthLayout/AuthLayout.tsx +170 -0
  147. package/template/src/components/AuthLayout/index.ts +1 -0
  148. package/template/src/components/BottomSheet/BottomSheet.tsx +73 -0
  149. package/template/src/components/BottomSheet/BottomSheetAlert.tsx +100 -0
  150. package/template/src/components/BottomSheet/CenterAlert.tsx +153 -0
  151. package/template/src/components/BottomSheet/index.ts +2 -0
  152. package/template/src/components/BottomTabBar/index.tsx +145 -0
  153. package/template/src/components/Button/RNButton.tsx +152 -0
  154. package/template/src/components/Button/index.ts +2 -0
  155. package/template/src/components/Common/Avatar.tsx +80 -0
  156. package/template/src/components/Common/Card.tsx +49 -0
  157. package/template/src/components/Common/CardBrandLogo.tsx +66 -0
  158. package/template/src/components/Common/Checkbox.tsx +65 -0
  159. package/template/src/components/Common/Chip.tsx +79 -0
  160. package/template/src/components/Common/CommonStyles.tsx +594 -0
  161. package/template/src/components/Common/Divider.tsx +33 -0
  162. package/template/src/components/Common/DriverTripCard.tsx +308 -0
  163. package/template/src/components/Common/Dropdown.tsx +161 -0
  164. package/template/src/components/Common/EmptyState.tsx +52 -0
  165. package/template/src/components/Common/FAB.tsx +68 -0
  166. package/template/src/components/Common/HeaderLocation.tsx +108 -0
  167. package/template/src/components/Common/Loader.tsx +23 -0
  168. package/template/src/components/Common/RatingStars.tsx +103 -0
  169. package/template/src/components/Common/RouteDots.tsx +98 -0
  170. package/template/src/components/Common/SegmentedControl.tsx +126 -0
  171. package/template/src/components/Common/SosButton.tsx +80 -0
  172. package/template/src/components/Common/SosSheet.tsx +344 -0
  173. package/template/src/components/Common/StarRating.tsx +58 -0
  174. package/template/src/components/Common/StatusBadge.tsx +56 -0
  175. package/template/src/components/Common/Toggle.tsx +66 -0
  176. package/template/src/components/Common/TripCard.tsx +247 -0
  177. package/template/src/components/Common/UploadBox.tsx +106 -0
  178. package/template/src/components/Container/MainContainer.tsx +76 -0
  179. package/template/src/components/Container/index.ts +1 -0
  180. package/template/src/components/Header/index.tsx +143 -0
  181. package/template/src/components/Icon/SvgIcons.tsx +1991 -0
  182. package/template/src/components/ImagePickerSheet/ImagePickerSheet.tsx +233 -0
  183. package/template/src/components/ImagePickerSheet/index.ts +2 -0
  184. package/template/src/components/Input/CountryDropdown.tsx +71 -0
  185. package/template/src/components/Input/OtpInput.tsx +117 -0
  186. package/template/src/components/Input/RNInput.tsx +138 -0
  187. package/template/src/components/Input/index.ts +4 -0
  188. package/template/src/components/Picker/DatePickerSheet.tsx +393 -0
  189. package/template/src/components/Picker/PassengerPickerSheet.tsx +237 -0
  190. package/template/src/components/Text/RNText.tsx +62 -0
  191. package/template/src/components/Text/index.ts +1 -0
  192. package/template/src/components/index.ts +44 -0
  193. package/template/src/hooks/useCurrentLocation.ts +72 -0
  194. package/template/src/localization/i18n.ts +29 -0
  195. package/template/src/localization/i18next.d.ts +11 -0
  196. package/template/src/localization/index.ts +4 -0
  197. package/template/src/localization/languageStorage.ts +27 -0
  198. package/template/src/localization/languages.ts +62 -0
  199. package/template/src/localization/resources/en.ts +703 -0
  200. package/template/src/localization/resources/fr.ts +703 -0
  201. package/template/src/localization/useLanguage.ts +42 -0
  202. package/template/src/navigation/AuthNavigation.tsx +23 -0
  203. package/template/src/navigation/BottomTabs.tsx +24 -0
  204. package/template/src/navigation/RootNavigation.tsx +27 -0
  205. package/template/src/navigation/RouteKey.ts +22 -0
  206. package/template/src/navigation/StackNavigation.tsx +52 -0
  207. package/template/src/navigation/paramLists.ts +25 -0
  208. package/template/src/redux/slice/app.ts +66 -0
  209. package/template/src/redux/slice/auth.ts +40 -0
  210. package/template/src/redux/slice/userProfile.ts +124 -0
  211. package/template/src/redux/store.ts +17 -0
  212. package/template/src/screen/auth/Login.tsx +69 -0
  213. package/template/src/screen/onboarding/LanguageSelection.tsx +231 -0
  214. package/template/src/screen/root/home/index.tsx +36 -0
  215. package/template/src/screen/root/profile/index.tsx +69 -0
  216. package/template/src/screen/shared/Chat.tsx +308 -0
  217. package/template/src/screen/shared/EditProfile.tsx +407 -0
  218. package/template/src/screen/shared/HelpSupport.tsx +678 -0
  219. package/template/src/screen/shared/LocationSearch.tsx +362 -0
  220. package/template/src/screen/shared/Messages.tsx +115 -0
  221. package/template/src/screen/shared/Notifications.tsx +86 -0
  222. package/template/src/screen/shared/PrivacyPolicy.tsx +297 -0
  223. package/template/src/screen/shared/Profile.tsx +118 -0
  224. package/template/src/screen/shared/Ratings.tsx +170 -0
  225. package/template/src/screen/shared/TermsConditions.tsx +315 -0
  226. package/template/src/screen/shared/profile/DriverProfile.tsx +262 -0
  227. package/template/src/screen/shared/profile/PassengerProfile.tsx +123 -0
  228. package/template/src/screen/shared/profile/ProfileParts.tsx +219 -0
  229. package/template/src/services/Config.ts +37 -0
  230. package/template/src/services/api.ts +37 -0
  231. package/template/src/services/index.ts +4 -0
  232. package/template/src/services/places.ts +320 -0
  233. package/template/src/services/storage.ts +33 -0
  234. package/template/src/theme/fonts.ts +30 -0
  235. package/template/src/theme/index.ts +3 -0
  236. package/template/src/theme/spacing.ts +66 -0
  237. package/template/src/theme/theme.ts +58 -0
  238. package/template/src/types/env.d.ts +8 -0
  239. package/template/src/types/index.ts +3 -0
  240. package/template/src/utils/card.ts +101 -0
  241. package/template/src/utils/constants.ts +39 -0
  242. package/template/src/utils/functions.ts +24 -0
  243. package/template/tsconfig.json +8 -0
@@ -0,0 +1,308 @@
1
+ import React from "react";
2
+ import type { TFunction } from "i18next";
3
+ import {
4
+ StyleProp,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ View,
8
+ ViewStyle,
9
+ } from "react-native";
10
+ import Svg, { Circle, Path } from "react-native-svg";
11
+ import { moderateScale } from "react-native-size-matters";
12
+ import Card from "./Card";
13
+ import StatusBadge from "./StatusBadge";
14
+ import RNText from "../Text/RNText";
15
+ import { CheckCircleIcon } from "../Icon/SvgIcons";
16
+ import { THEME } from "../../theme";
17
+ import { useLanguage } from "../../localization";
18
+ import { Trip, TripStatus } from "../../redux/slice/trip";
19
+
20
+ interface DriverTripCardProps {
21
+ trip: Trip;
22
+ onPress?: () => void;
23
+ style?: StyleProp<ViewStyle>;
24
+ }
25
+
26
+ /** Filled circle with a white ✕ — used for the Cancelled badge. */
27
+ const FilledXCircle: React.FC<{ size: number; color: string }> = ({
28
+ size,
29
+ color,
30
+ }) => (
31
+ <Svg width={size} height={size} viewBox="0 0 24 24" fill="none">
32
+ <Circle cx={12} cy={12} r={10} fill={color} />
33
+ <Path
34
+ d="M8.5 8.5l7 7M15.5 8.5l-7 7"
35
+ stroke="#FFFFFF"
36
+ strokeWidth={1.8}
37
+ strokeLinecap="round"
38
+ strokeLinejoin="round"
39
+ />
40
+ </Svg>
41
+ );
42
+
43
+ type Variant = {
44
+ /** Color of the timeline dots. */
45
+ dotColor: string;
46
+ /** Whether the route/header text is dimmed. */
47
+ muted: boolean;
48
+ /** Inline status icon + label (renders instead of the pill badge). */
49
+ badge?: { icon: React.ReactNode; label: string; color: string };
50
+ /** Footer (divider + label); omitted means no footer. */
51
+ footer?: { label: string; color: string };
52
+ };
53
+
54
+ const buildVariant = (trip: Trip, t: TFunction): Variant => {
55
+ const iconSize = moderateScale(16);
56
+ const seatsLeft = Math.max(trip.totalSeats - trip.bookedSeats, 0);
57
+ const seatLabel = t("common.seatsLeftCount", { count: seatsLeft });
58
+
59
+ switch (trip.status) {
60
+ case "completed":
61
+ return {
62
+ dotColor: THEME.success,
63
+ muted: false,
64
+ badge: {
65
+ icon: <CheckCircleIcon size={iconSize} color={THEME.primary} />,
66
+ label: t("tripStatus.completed"),
67
+ color: THEME.primary,
68
+ },
69
+ };
70
+ case "cancelled":
71
+ return {
72
+ dotColor: THEME.primaryLight,
73
+ muted: true,
74
+ badge: {
75
+ icon: <FilledXCircle size={iconSize} color={THEME.text} />,
76
+ label: t("tripStatus.cancelled"),
77
+ color: THEME.text,
78
+ },
79
+ };
80
+ case "confirmed":
81
+ return {
82
+ dotColor: THEME.primary,
83
+ muted: false,
84
+ badge: {
85
+ icon: <CheckCircleIcon size={iconSize} color={THEME.success} />,
86
+ label: t("tripStatus.confirmed"),
87
+ color: THEME.success,
88
+ },
89
+ footer: { label: seatLabel, color: THEME.primary },
90
+ };
91
+ default:
92
+ // available / fully_booked / in_progress
93
+ return {
94
+ dotColor: THEME.primary,
95
+ muted: false,
96
+ footer: { label: seatLabel, color: THEME.primary },
97
+ };
98
+ }
99
+ };
100
+
101
+ const STATUS_BADGE_TONE: Record<
102
+ TripStatus,
103
+ "success" | "warning" | "danger" | "info" | "primary"
104
+ > = {
105
+ available: "info",
106
+ fully_booked: "warning",
107
+ confirmed: "success",
108
+ in_progress: "primary",
109
+ completed: "success",
110
+ cancelled: "danger",
111
+ };
112
+
113
+ /** Localized status label, falling back to the English literal where no key exists. */
114
+ const statusLabel = (t: TFunction, status: TripStatus): string => {
115
+ switch (status) {
116
+ case "confirmed":
117
+ return t("tripStatus.confirmed");
118
+ case "completed":
119
+ return t("tripStatus.completed");
120
+ case "cancelled":
121
+ return t("tripStatus.cancelled");
122
+ case "available":
123
+ return t("tripStatus.available");
124
+ case "fully_booked":
125
+ return t("tripStatus.fullyBooked");
126
+ case "in_progress":
127
+ return t("tripStatus.inProgress");
128
+ default:
129
+ return status;
130
+ }
131
+ };
132
+
133
+ const DriverTripCard: React.FC<DriverTripCardProps> = ({
134
+ trip,
135
+ onPress,
136
+ style,
137
+ }) => {
138
+ const { t } = useLanguage();
139
+ const variant = buildVariant(trip, t);
140
+ const textColor = variant.muted ? THEME.textMuted : THEME.text;
141
+ const timeColor = variant.muted ? THEME.textMuted : THEME.textSecondary;
142
+
143
+ return (
144
+ <TouchableOpacity onPress={onPress} activeOpacity={0.9} disabled={!onPress}>
145
+ <Card shadow padding={moderateScale(16)} style={[styles.card, style]}>
146
+ {/* Header: date + status */}
147
+ <View style={styles.headerRow}>
148
+ <RNText font="bold" size={17} color={textColor}>
149
+ {trip.date}, {trip.weekday}
150
+ </RNText>
151
+ {variant.badge ? (
152
+ <View style={styles.statusRow}>
153
+ {variant.badge.icon}
154
+ <RNText
155
+ font="semibold"
156
+ size={14}
157
+ color={variant.badge.color}
158
+ style={styles.statusLabel}
159
+ >
160
+ {variant.badge.label}
161
+ </RNText>
162
+ </View>
163
+ ) : (
164
+ <StatusBadge
165
+ label={statusLabel(t, trip.status)}
166
+ tone={STATUS_BADGE_TONE[trip.status]}
167
+ />
168
+ )}
169
+ </View>
170
+
171
+ <View style={styles.divider} />
172
+
173
+ {/* Route timeline */}
174
+ <View style={styles.timelineWrap}>
175
+ <View style={styles.timelineRow}>
176
+ <RNText
177
+ font="medium"
178
+ size={14}
179
+ color={timeColor}
180
+ style={styles.timelineTime}
181
+ >
182
+ {trip.from.time || "--:--"}
183
+ </RNText>
184
+ <View
185
+ style={[styles.timelineDot, { backgroundColor: variant.dotColor }]}
186
+ />
187
+ <RNText
188
+ font="medium"
189
+ size={15}
190
+ color={textColor}
191
+ style={styles.timelinePlace}
192
+ >
193
+ {trip.from.city}
194
+ {trip.from.area ? `, ${trip.from.area}` : ""}
195
+ </RNText>
196
+ </View>
197
+
198
+ <View style={styles.timelineConnector}>
199
+ <View style={styles.timeColSpacer} />
200
+ <View style={styles.timelineDashCol}>
201
+ {Array.from({ length: 4 }).map((_, i) => (
202
+ <View
203
+ key={i}
204
+ style={[
205
+ styles.timelineDashSegment,
206
+ { backgroundColor: variant.dotColor },
207
+ ]}
208
+ />
209
+ ))}
210
+ </View>
211
+ </View>
212
+
213
+ <View style={styles.timelineRow}>
214
+ <RNText
215
+ font="medium"
216
+ size={14}
217
+ color={timeColor}
218
+ style={styles.timelineTime}
219
+ >
220
+ {trip.to.time || "--:--"}
221
+ </RNText>
222
+ <View
223
+ style={[styles.timelineDot, { backgroundColor: variant.dotColor }]}
224
+ />
225
+ <RNText
226
+ font="medium"
227
+ size={15}
228
+ color={textColor}
229
+ style={styles.timelinePlace}
230
+ >
231
+ {trip.to.city}
232
+ {trip.to.area ? `, ${trip.to.area}` : ""}
233
+ </RNText>
234
+ </View>
235
+ </View>
236
+
237
+ {/* Footer (only when there are seats to show) */}
238
+ {variant.footer ? (
239
+ <>
240
+ <View style={styles.divider} />
241
+ <RNText font="semibold" size={15} color={variant.footer.color}>
242
+ {variant.footer.label}
243
+ </RNText>
244
+ </>
245
+ ) : null}
246
+ </Card>
247
+ </TouchableOpacity>
248
+ );
249
+ };
250
+
251
+ export default DriverTripCard;
252
+
253
+ const styles = StyleSheet.create({
254
+ card: {
255
+ borderRadius: moderateScale(20),
256
+ },
257
+ headerRow: {
258
+ flexDirection: "row",
259
+ alignItems: "center",
260
+ justifyContent: "space-between",
261
+ },
262
+ statusRow: {
263
+ flexDirection: "row",
264
+ alignItems: "center",
265
+ },
266
+ statusLabel: {
267
+ marginLeft: moderateScale(5),
268
+ },
269
+ divider: {
270
+ height: 1,
271
+ backgroundColor: THEME.divider,
272
+ marginVertical: moderateScale(14),
273
+ },
274
+ timelineWrap: {},
275
+ timelineRow: {
276
+ flexDirection: "row",
277
+ alignItems: "center",
278
+ },
279
+ timelineTime: {
280
+ width: moderateScale(48),
281
+ },
282
+ timelinePlace: {
283
+ flex: 1,
284
+ marginLeft: moderateScale(14),
285
+ },
286
+ timelineDot: {
287
+ width: moderateScale(10),
288
+ height: moderateScale(10),
289
+ borderRadius: moderateScale(5),
290
+ },
291
+ timelineConnector: {
292
+ flexDirection: "row",
293
+ },
294
+ timeColSpacer: {
295
+ width: moderateScale(48),
296
+ },
297
+ timelineDashCol: {
298
+ width: moderateScale(10),
299
+ alignItems: "center",
300
+ paddingVertical: moderateScale(3),
301
+ },
302
+ timelineDashSegment: {
303
+ width: moderateScale(1.5),
304
+ height: moderateScale(3),
305
+ marginVertical: moderateScale(1.5),
306
+ opacity: 0.6,
307
+ },
308
+ });
@@ -0,0 +1,161 @@
1
+ import React, { useCallback, useState } from "react";
2
+ import {
3
+ Pressable,
4
+ StyleProp,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ View,
8
+ ViewStyle,
9
+ } from "react-native";
10
+ import { moderateScale } from "react-native-size-matters";
11
+ import { SPACING, THEME } from "../../theme";
12
+ import RNText from "../Text/RNText";
13
+ import BottomSheet from "../BottomSheet/BottomSheet";
14
+ import ArrowDownSvg from "../../assets/svg/arrowDown.svg";
15
+
16
+ export interface DropdownOption {
17
+ label: string;
18
+ value: string;
19
+ }
20
+
21
+ interface DropdownProps {
22
+ value?: string | null;
23
+ options: DropdownOption[];
24
+ onChange: (value: string) => void;
25
+ placeholder?: string;
26
+ title?: string;
27
+ leftIcon?: React.ReactNode;
28
+ rightIcon?: React.ReactNode;
29
+ disabled?: boolean;
30
+ error?: string;
31
+ containerStyle?: StyleProp<ViewStyle>;
32
+ triggerStyle?: StyleProp<ViewStyle>;
33
+ }
34
+
35
+ const Dropdown: React.FC<DropdownProps> = ({
36
+ value,
37
+ options,
38
+ onChange,
39
+ placeholder = "Select",
40
+ title,
41
+ leftIcon,
42
+ rightIcon,
43
+ disabled,
44
+ error,
45
+ containerStyle,
46
+ triggerStyle,
47
+ }) => {
48
+ const [visible, setVisible] = useState(false);
49
+
50
+ const selected = options.find((o) => o.value === value);
51
+
52
+ const open = useCallback(() => {
53
+ if (!disabled) setVisible(true);
54
+ }, [disabled]);
55
+ const close = useCallback(() => setVisible(false), []);
56
+
57
+ const handleSelect = useCallback(
58
+ (val: string) => {
59
+ onChange(val);
60
+ setVisible(false);
61
+ },
62
+ [onChange],
63
+ );
64
+
65
+ return (
66
+ <View style={containerStyle}>
67
+ <TouchableOpacity
68
+ activeOpacity={0.85}
69
+ onPress={open}
70
+ disabled={disabled}
71
+ style={[
72
+ styles.trigger,
73
+ { borderColor: error ? THEME.danger : THEME.inputBorder },
74
+ triggerStyle,
75
+ ]}
76
+ >
77
+ {leftIcon ? <View style={styles.left}>{leftIcon}</View> : null}
78
+ <RNText
79
+ size={14}
80
+ font="regular"
81
+ color={selected ? THEME.text : THEME.textPlaceholder}
82
+ style={styles.triggerText}
83
+ >
84
+ {selected ? selected.label : placeholder}
85
+ </RNText>
86
+ {rightIcon ?? (
87
+ <ArrowDownSvg
88
+ width={moderateScale(18)}
89
+ height={moderateScale(18)}
90
+ />
91
+ )}
92
+ </TouchableOpacity>
93
+
94
+ {error ? (
95
+ <RNText size={11} color={THEME.danger} style={styles.errorText}>
96
+ {error}
97
+ </RNText>
98
+ ) : null}
99
+
100
+ <BottomSheet visible={visible} onClose={close}>
101
+ {title ? (
102
+ <RNText
103
+ font="semibold"
104
+ size={18}
105
+ color={THEME.text}
106
+ style={styles.sheetTitle}
107
+ >
108
+ {title}
109
+ </RNText>
110
+ ) : null}
111
+
112
+ {options.map((opt) => {
113
+ const active = opt.value === value;
114
+ return (
115
+ <Pressable
116
+ key={opt.value}
117
+ onPress={() => handleSelect(opt.value)}
118
+ style={({ pressed }) => [
119
+ styles.option,
120
+ pressed && { backgroundColor: THEME.primaryFaint },
121
+ ]}
122
+ >
123
+ <RNText
124
+ size={15}
125
+ font={active ? "semibold" : "regular"}
126
+ color={active ? THEME.primary : THEME.text}
127
+ >
128
+ {opt.label}
129
+ </RNText>
130
+ </Pressable>
131
+ );
132
+ })}
133
+ </BottomSheet>
134
+ </View>
135
+ );
136
+ };
137
+
138
+ export default Dropdown;
139
+
140
+ const styles = StyleSheet.create({
141
+ trigger: {
142
+ height: moderateScale(50),
143
+ borderRadius: SPACING.radiusPill,
144
+ borderWidth: 1,
145
+ backgroundColor: THEME.surface,
146
+ flexDirection: "row",
147
+ alignItems: "center",
148
+ paddingHorizontal: moderateScale(18),
149
+ },
150
+ left: { marginRight: moderateScale(10) },
151
+ triggerText: { flex: 1 },
152
+ errorText: { marginTop: moderateScale(6) },
153
+ sheetTitle: {
154
+ marginBottom: moderateScale(12),
155
+ },
156
+ option: {
157
+ paddingVertical: moderateScale(14),
158
+ borderBottomWidth: 1,
159
+ borderBottomColor: THEME.divider,
160
+ },
161
+ });
@@ -0,0 +1,52 @@
1
+ import React from "react";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { moderateScale } from "react-native-size-matters";
4
+ import { SPACING, THEME } from "../../theme";
5
+ import RNText from "../Text/RNText";
6
+
7
+ interface EmptyStateProps {
8
+ title?: string;
9
+ description?: string;
10
+ icon?: React.ReactNode;
11
+ }
12
+
13
+ const EmptyState: React.FC<EmptyStateProps> = ({ title, description, icon }) => (
14
+ <View style={styles.wrap}>
15
+ {icon ? <View style={styles.icon}>{icon}</View> : null}
16
+ {title ? (
17
+ <RNText
18
+ font="semibold"
19
+ size={16}
20
+ color={THEME.text}
21
+ textAlign="center"
22
+ style={styles.title}
23
+ >
24
+ {title}
25
+ </RNText>
26
+ ) : null}
27
+ {description ? (
28
+ <RNText
29
+ size={13}
30
+ color={THEME.textMuted}
31
+ textAlign="center"
32
+ style={styles.desc}
33
+ >
34
+ {description}
35
+ </RNText>
36
+ ) : null}
37
+ </View>
38
+ );
39
+
40
+ export default EmptyState;
41
+
42
+ const styles = StyleSheet.create({
43
+ wrap: {
44
+ flex: 1,
45
+ alignItems: "center",
46
+ justifyContent: "center",
47
+ padding: SPACING.xxl,
48
+ },
49
+ icon: { marginBottom: moderateScale(12) },
50
+ title: { marginBottom: moderateScale(6) },
51
+ desc: { maxWidth: "80%" },
52
+ });
@@ -0,0 +1,68 @@
1
+ import React from "react";
2
+ import { Pressable, StyleProp, StyleSheet, View, ViewStyle } from "react-native";
3
+ import { moderateScale } from "react-native-size-matters";
4
+ import { THEME } from "../../theme";
5
+ import { PlusIcon } from "../Icon/SvgIcons";
6
+
7
+ interface FABProps {
8
+ onPress?: () => void;
9
+ style?: StyleProp<ViewStyle>;
10
+ size?: number;
11
+ children?: React.ReactNode;
12
+ }
13
+
14
+ const FAB: React.FC<FABProps> = ({ onPress, style, size = 56, children }) => {
15
+ const dim = moderateScale(size);
16
+ const ringPad = moderateScale(6);
17
+ const outerDim = dim + ringPad * 2;
18
+ return (
19
+ <View
20
+ style={[
21
+ styles.halo,
22
+ {
23
+ width: outerDim,
24
+ height: outerDim,
25
+ borderRadius: outerDim / 2,
26
+ padding: ringPad,
27
+ },
28
+ style,
29
+ ]}
30
+ >
31
+ <Pressable
32
+ onPress={onPress}
33
+ style={[
34
+ styles.fab,
35
+ {
36
+ width: dim,
37
+ height: dim,
38
+ borderRadius: dim / 2,
39
+ backgroundColor: THEME.primary,
40
+ },
41
+ ]}
42
+ >
43
+ {children ?? (
44
+ <PlusIcon size={moderateScale(24)} color={THEME.textOnPrimary} />
45
+ )}
46
+ </Pressable>
47
+ </View>
48
+ );
49
+ };
50
+
51
+ export default FAB;
52
+
53
+ const styles = StyleSheet.create({
54
+ halo: {
55
+ alignItems: "center",
56
+ justifyContent: "center",
57
+ backgroundColor: "rgba(242, 107, 42, 0.18)",
58
+ },
59
+ fab: {
60
+ alignItems: "center",
61
+ justifyContent: "center",
62
+ shadowColor: "#F26B2A",
63
+ shadowOpacity: 0.35,
64
+ shadowRadius: 14,
65
+ shadowOffset: { width: 0, height: 8 },
66
+ elevation: 8,
67
+ },
68
+ });
@@ -0,0 +1,108 @@
1
+ import React from "react";
2
+ import {
3
+ ActivityIndicator,
4
+ Linking,
5
+ StyleSheet,
6
+ TouchableOpacity,
7
+ View,
8
+ } from "react-native";
9
+ import { moderateScale } from "react-native-size-matters";
10
+ import { FONT_SIZE, THEME } from "../../theme";
11
+ import { PinIcon } from "../Icon/SvgIcons";
12
+ import RNText from "../Text/RNText";
13
+ import { useLanguage } from "../../localization";
14
+ import type { LocationStatus } from "../../redux/slice/location";
15
+
16
+ interface HeaderLocationProps {
17
+ status: LocationStatus;
18
+ label?: string;
19
+ /** Retry the location fetch (used for the error state). */
20
+ onRetry?: () => void;
21
+ /** Text colour — lets each Home match its header palette. */
22
+ textColor?: string;
23
+ }
24
+
25
+ /**
26
+ * Compact, reusable header element that surfaces the device's current
27
+ * location. Reacts to the global location slice status: shows a spinner while
28
+ * resolving, the resolved city once granted, and tappable affordances for the
29
+ * denied / error states.
30
+ */
31
+ const HeaderLocation: React.FC<HeaderLocationProps> = ({
32
+ status,
33
+ label,
34
+ onRetry,
35
+ textColor = THEME.textSecondary,
36
+ }) => {
37
+ const { t } = useLanguage();
38
+ if (status === "loading" || status === "idle") {
39
+ return (
40
+ <View style={styles.row}>
41
+ <ActivityIndicator size="small" color={THEME.primary} />
42
+ <RNText size={FONT_SIZE.sm} color={textColor} style={styles.text}>
43
+ {t("location.gettingLocation")}
44
+ </RNText>
45
+ </View>
46
+ );
47
+ }
48
+
49
+ if (status === "denied") {
50
+ return (
51
+ <TouchableOpacity
52
+ style={styles.row}
53
+ activeOpacity={0.7}
54
+ hitSlop={moderateScale(8)}
55
+ onPress={() => Linking.openSettings()}
56
+ >
57
+ <PinIcon size={moderateScale(13)} color={THEME.danger} />
58
+ <RNText size={FONT_SIZE.sm} color={THEME.danger} style={styles.text}>
59
+ {t("location.enableAccess")}
60
+ </RNText>
61
+ </TouchableOpacity>
62
+ );
63
+ }
64
+
65
+ if (status === "error") {
66
+ return (
67
+ <TouchableOpacity
68
+ style={styles.row}
69
+ activeOpacity={0.7}
70
+ hitSlop={moderateScale(8)}
71
+ onPress={onRetry}
72
+ >
73
+ <PinIcon size={moderateScale(13)} color={THEME.warning} />
74
+ <RNText size={FONT_SIZE.sm} color={THEME.warning} style={styles.text}>
75
+ {t("location.unavailableRetry")}
76
+ </RNText>
77
+ </TouchableOpacity>
78
+ );
79
+ }
80
+
81
+ return (
82
+ <View style={styles.row}>
83
+ <PinIcon size={moderateScale(13)} color={THEME.primary} />
84
+ <RNText
85
+ size={FONT_SIZE.sm}
86
+ color={textColor}
87
+ style={styles.text}
88
+ numberOfLines={1}
89
+ >
90
+ {label || t("location.current")}
91
+ </RNText>
92
+ </View>
93
+ );
94
+ };
95
+
96
+ export default HeaderLocation;
97
+
98
+ const styles = StyleSheet.create({
99
+ row: {
100
+ flexDirection: "row",
101
+ alignItems: "center",
102
+ marginTop: moderateScale(4),
103
+ },
104
+ text: {
105
+ marginLeft: moderateScale(5),
106
+ flexShrink: 1,
107
+ },
108
+ });