@imksh/create 1.0.30 → 1.0.32

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 (247) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/src/commands/create.js +2 -0
  4. package/src/index.js +1 -1
  5. package/src/utils/askTemplate.js +2 -1
  6. package/templates/layers/electron/base/electron/main.js +262 -0
  7. package/templates/layers/electron/base/electron/preload.js +37 -0
  8. package/templates/layers/electron/base/electron-builder.json5 +68 -0
  9. package/templates/layers/electron/javascript/_gitignore +38 -0
  10. package/templates/layers/electron/javascript/package.json +49 -0
  11. package/templates/layers/electron/javascript/src/components/layout/SidebarLayout.jsx +43 -0
  12. package/templates/layers/electron/javascript/src/components/ui/TitleBar.jsx +130 -0
  13. package/templates/layers/electron/javascript/src/config/appConfig.js +51 -0
  14. package/templates/layers/electron/javascript/src/hooks/useElectron.js +68 -0
  15. package/templates/layers/electron/javascript/src/main.jsx +17 -0
  16. package/templates/layers/electron/javascript/src/store/useUiStore.js +59 -0
  17. package/templates/layers/electron/javascript/vite.config.js +29 -0
  18. package/templates/layers/electron/typescript/_gitignore +38 -0
  19. package/templates/layers/electron/typescript/electron/main.ts +213 -0
  20. package/templates/layers/electron/typescript/electron/preload.ts +52 -0
  21. package/templates/layers/electron/typescript/package.json +52 -0
  22. package/templates/layers/electron/typescript/src/components/layout/SidebarLayout.tsx +42 -0
  23. package/templates/layers/electron/typescript/src/components/ui/TitleBar.tsx +120 -0
  24. package/templates/layers/electron/typescript/src/config/appConfig.ts +52 -0
  25. package/templates/layers/electron/typescript/src/hooks/useElectron.ts +68 -0
  26. package/templates/layers/electron/typescript/src/main.tsx +17 -0
  27. package/templates/layers/electron/typescript/src/store/useUiStore.ts +97 -0
  28. package/templates/layers/electron/typescript/src/vite-env.d.ts +13 -0
  29. package/templates/layers/electron/typescript/tsconfig.json +25 -0
  30. package/templates/layers/electron/typescript/tsconfig.node.json +11 -0
  31. package/templates/layers/electron/typescript/vite.config.ts +29 -0
  32. package/templates/layers/next/javascript/package.json +8 -2
  33. package/templates/layers/next/javascript/src/components/common/Breadcrumbs.jsx +44 -6
  34. package/templates/layers/next/javascript/src/components/common/Loading.jsx +1 -0
  35. package/templates/layers/next/javascript/src/components/common/LottieIcon.jsx +1 -0
  36. package/templates/layers/next/javascript/src/components/common/Pagination.jsx +170 -0
  37. package/templates/layers/next/javascript/src/components/common/SidebarModal.jsx +180 -0
  38. package/templates/layers/next/javascript/src/components/common/StatsCard.jsx +168 -0
  39. package/templates/layers/next/javascript/src/components/common/Table.jsx +191 -41
  40. package/templates/layers/next/javascript/src/components/common/Tabs.jsx +187 -0
  41. package/templates/layers/next/javascript/src/components/common/ToggleSwitch.jsx +4 -3
  42. package/templates/layers/next/javascript/src/components/layout/ProtectedRoute.jsx +2 -1
  43. package/templates/layers/next/javascript/src/components/layout/PublicLayout.jsx +3 -6
  44. package/templates/layers/next/javascript/src/components/layout/SidebarLayout.jsx +4 -17
  45. package/templates/layers/next/javascript/src/components/modals/ConfirmModal.jsx +4 -12
  46. package/templates/layers/next/javascript/src/components/modals/Modal.jsx +50 -19
  47. package/templates/layers/next/javascript/src/components/ui/Input.jsx +50 -0
  48. package/templates/layers/next/{typescript/src/components/ui/sidebar.tsx → javascript/src/components/ui/Sidebar.jsx} +65 -53
  49. package/templates/layers/next/javascript/src/components/ui/SidebarHeader.jsx +11 -6
  50. package/templates/layers/next/javascript/src/config/appConfig.js +4 -4
  51. package/templates/layers/next/javascript/src/hooks/useWindowSize.js +2 -3
  52. package/templates/layers/next/javascript/src/store/useUiStore.js +9 -4
  53. package/templates/layers/next/typescript/.eslintrc.json +12 -0
  54. package/templates/layers/next/typescript/package.json +14 -3
  55. package/templates/layers/next/typescript/src/components/common/Breadcrumbs.tsx +57 -8
  56. package/templates/layers/next/typescript/src/components/common/Loading.tsx +2 -1
  57. package/templates/layers/next/typescript/src/components/common/LottieIcon.tsx +2 -2
  58. package/templates/layers/next/typescript/src/components/common/Pagination.tsx +186 -0
  59. package/templates/layers/next/typescript/src/components/common/SidebarModal.tsx +200 -0
  60. package/templates/layers/next/typescript/src/components/common/StatsCard.tsx +232 -0
  61. package/templates/layers/next/typescript/src/components/common/Table.tsx +205 -39
  62. package/templates/layers/next/typescript/src/components/common/Tabs.tsx +190 -0
  63. package/templates/layers/next/typescript/src/components/common/ToggleSwitch.tsx +12 -8
  64. package/templates/layers/next/typescript/src/components/layout/PublicLayout.tsx +8 -6
  65. package/templates/layers/next/typescript/src/components/layout/SidebarLayout.tsx +8 -17
  66. package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +3 -1
  67. package/templates/layers/next/typescript/src/components/modals/Modal.tsx +67 -21
  68. package/templates/layers/next/typescript/src/components/ui/Button.tsx +1 -0
  69. package/templates/layers/next/typescript/src/components/ui/Header.tsx +2 -2
  70. package/templates/layers/next/typescript/src/components/ui/Input.tsx +40 -60
  71. package/templates/layers/next/{javascript/src/components/ui/sidebar.jsx → typescript/src/components/ui/Sidebar.tsx} +77 -44
  72. package/templates/layers/next/typescript/src/components/ui/SidebarHeader.tsx +16 -10
  73. package/templates/layers/next/typescript/src/config/appConfig.ts +4 -4
  74. package/templates/layers/next/typescript/src/hooks/usePwaInstall.ts +10 -4
  75. package/templates/layers/next/typescript/src/hooks/useWindowSize.ts +2 -3
  76. package/templates/layers/next/typescript/src/services/authService.ts +1 -1
  77. package/templates/layers/next/typescript/src/store/useAuthStore.ts +24 -9
  78. package/templates/layers/next/typescript/src/store/useUiStore.ts +14 -4
  79. package/templates/layers/node/javascript/.env.example +3 -1
  80. package/templates/layers/node/javascript/package.json +12 -3
  81. package/templates/layers/node/javascript/src/app.js +2 -1
  82. package/templates/layers/node/javascript/src/controllers/auth.controller.js +106 -168
  83. package/templates/layers/node/javascript/src/controllers/push.controller.js +51 -58
  84. package/templates/layers/node/javascript/src/controllers/upload.controller.js +12 -19
  85. package/templates/layers/node/javascript/src/middleware/auth.middleware.js +5 -11
  86. package/templates/layers/node/javascript/src/middleware/errorHandler.js +6 -4
  87. package/templates/layers/node/javascript/src/routes/auth.route.js +2 -0
  88. package/templates/layers/node/javascript/src/services/auth.service.js +159 -164
  89. package/templates/layers/node/javascript/src/utils/ApiError.js +20 -0
  90. package/templates/layers/node/javascript/src/utils/SuccessResponse.js +15 -0
  91. package/templates/layers/node/javascript/src/utils/apiResponse.js +4 -0
  92. package/templates/layers/node/javascript/src/utils/asyncHandler.js +5 -0
  93. package/templates/layers/node/typescript/.env.example +3 -1
  94. package/templates/layers/node/typescript/.eslintrc.cjs +28 -0
  95. package/templates/layers/node/typescript/package-lock.json +2 -2
  96. package/templates/layers/node/typescript/package.json +17 -3
  97. package/templates/layers/node/typescript/src/app.ts +3 -2
  98. package/templates/layers/node/typescript/src/config/db.ts +3 -2
  99. package/templates/layers/node/typescript/src/config/socket.ts +5 -4
  100. package/templates/layers/node/typescript/src/controllers/auth.controller.ts +104 -132
  101. package/templates/layers/node/typescript/src/controllers/push.controller.ts +52 -59
  102. package/templates/layers/node/typescript/src/controllers/upload.controller.ts +12 -19
  103. package/templates/layers/node/typescript/src/middleware/auth.middleware.ts +13 -7
  104. package/templates/layers/node/typescript/src/middleware/errorHandler.ts +18 -5
  105. package/templates/layers/node/typescript/src/middleware/rateLimiter.ts +2 -2
  106. package/templates/layers/node/typescript/src/routes/auth.route.ts +2 -0
  107. package/templates/layers/node/typescript/src/services/auth.service.ts +86 -43
  108. package/templates/layers/node/typescript/src/types/express.d.ts +5 -3
  109. package/templates/layers/node/typescript/src/utils/ApiError.ts +25 -0
  110. package/templates/layers/node/typescript/src/utils/SuccessResponse.ts +17 -0
  111. package/templates/layers/node/typescript/src/utils/apiResponse.ts +4 -0
  112. package/templates/layers/node/typescript/src/utils/asyncHandler.ts +13 -0
  113. package/templates/layers/node/typescript/src/utils/cloudinaryUpload.ts +18 -7
  114. package/templates/layers/node/typescript/src/utils/expoNotification.ts +1 -1
  115. package/templates/layers/node/typescript/src/utils/genOtpToken.ts +7 -1
  116. package/templates/layers/node/typescript/src/utils/jwt.ts +8 -2
  117. package/templates/layers/node/typescript/src/utils/logger.ts +2 -1
  118. package/templates/layers/node/typescript/src/utils/pushHelper.ts +13 -9
  119. package/templates/layers/node-prisma/javascript/.env.example +3 -1
  120. package/templates/layers/node-prisma/javascript/package.json +11 -2
  121. package/templates/layers/node-prisma/javascript/src/app.js +2 -1
  122. package/templates/layers/node-prisma/javascript/src/controllers/auth.controller.js +90 -135
  123. package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +64 -76
  124. package/templates/layers/node-prisma/javascript/src/controllers/upload.controller.js +12 -19
  125. package/templates/layers/node-prisma/javascript/src/middleware/auth.middleware.js +5 -4
  126. package/templates/layers/node-prisma/javascript/src/middleware/errorHandler.js +6 -4
  127. package/templates/layers/node-prisma/javascript/src/routes/auth.route.js +2 -0
  128. package/templates/layers/node-prisma/javascript/src/services/auth.service.js +145 -122
  129. package/templates/layers/node-prisma/javascript/src/utils/ApiError.js +20 -0
  130. package/templates/layers/node-prisma/javascript/src/utils/SuccessResponse.js +15 -0
  131. package/templates/layers/node-prisma/javascript/src/utils/apiResponse.js +4 -0
  132. package/templates/layers/node-prisma/javascript/src/utils/asyncHandler.js +5 -0
  133. package/templates/layers/node-prisma/typescript/.env.example +3 -1
  134. package/templates/layers/node-prisma/typescript/.eslintrc.cjs +28 -0
  135. package/templates/layers/node-prisma/typescript/package.json +19 -5
  136. package/templates/layers/node-prisma/typescript/src/app.ts +3 -2
  137. package/templates/layers/node-prisma/typescript/src/config/prisma.ts +3 -2
  138. package/templates/layers/node-prisma/typescript/src/config/socket.ts +5 -4
  139. package/templates/layers/node-prisma/typescript/src/controllers/auth.controller.ts +89 -105
  140. package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -83
  141. package/templates/layers/node-prisma/typescript/src/controllers/upload.controller.ts +12 -19
  142. package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +9 -13
  143. package/templates/layers/node-prisma/typescript/src/middleware/errorHandler.ts +18 -5
  144. package/templates/layers/node-prisma/typescript/src/middleware/rateLimiter.ts +2 -2
  145. package/templates/layers/node-prisma/typescript/src/routes/auth.route.ts +2 -0
  146. package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +83 -31
  147. package/templates/layers/node-prisma/typescript/src/types/express.d.ts +5 -3
  148. package/templates/layers/node-prisma/typescript/src/utils/ApiError.ts +25 -0
  149. package/templates/layers/node-prisma/typescript/src/utils/SuccessResponse.ts +17 -0
  150. package/templates/layers/node-prisma/typescript/src/utils/apiResponse.ts +4 -0
  151. package/templates/layers/node-prisma/typescript/src/utils/asyncHandler.ts +13 -0
  152. package/templates/layers/node-prisma/typescript/src/utils/cloudinaryUpload.ts +18 -7
  153. package/templates/layers/node-prisma/typescript/src/utils/genOtpToken.ts +7 -1
  154. package/templates/layers/node-prisma/typescript/src/utils/jwt.ts +8 -2
  155. package/templates/layers/node-prisma/typescript/src/utils/logger.ts +2 -1
  156. package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +10 -6
  157. package/templates/layers/prisma/javascript/package.json +8 -0
  158. package/templates/layers/prisma/typescript/package.json +14 -1
  159. package/templates/layers/prisma/typescript/prisma/schema.prisma +35 -10
  160. package/templates/layers/prisma/typescript/src/config/db.ts +3 -2
  161. package/templates/layers/react/javascript/package.json +6 -0
  162. package/templates/layers/react/javascript/src/components/common/Breadcrumbs.jsx +62 -58
  163. package/templates/layers/react/javascript/src/components/common/Pagination.jsx +164 -76
  164. package/templates/layers/react/javascript/src/components/common/SidebarModal.jsx +179 -0
  165. package/templates/layers/react/javascript/src/components/common/StatsCard.jsx +167 -0
  166. package/templates/layers/react/javascript/src/components/common/Table.jsx +248 -132
  167. package/templates/layers/react/javascript/src/components/common/Tabs.jsx +164 -112
  168. package/templates/layers/react/javascript/src/components/common/ToggleSwitch.jsx +3 -3
  169. package/templates/layers/react/javascript/src/components/layout/SidebarLayout.jsx +4 -12
  170. package/templates/layers/react/javascript/src/components/modals/Modal.jsx +53 -18
  171. package/templates/layers/react/javascript/src/components/ui/Input.jsx +49 -0
  172. package/templates/layers/react/javascript/src/components/ui/Sidebar.jsx +215 -0
  173. package/templates/layers/react/javascript/src/components/ui/SidebarHeader.jsx +25 -30
  174. package/templates/layers/react/javascript/src/config/appConfig.js +3 -3
  175. package/templates/layers/react/javascript/src/hooks/useWindowSize.js +2 -2
  176. package/templates/layers/react/typescript/.eslintrc.cjs +20 -0
  177. package/templates/layers/react/typescript/package.json +11 -2
  178. package/templates/layers/react/typescript/src/components/common/Breadcrumbs.tsx +32 -6
  179. package/templates/layers/react/typescript/src/components/common/Pagination.tsx +151 -57
  180. package/templates/layers/react/typescript/src/components/common/SidebarModal.tsx +199 -0
  181. package/templates/layers/react/typescript/src/components/common/StatsCard.tsx +231 -0
  182. package/templates/layers/react/typescript/src/components/common/Table.tsx +201 -38
  183. package/templates/layers/react/typescript/src/components/common/Tabs.tsx +56 -12
  184. package/templates/layers/react/typescript/src/components/common/ToggleSwitch.tsx +15 -3
  185. package/templates/layers/react/typescript/src/components/layout/SidebarLayout.tsx +3 -6
  186. package/templates/layers/react/typescript/src/components/modals/Modal.tsx +151 -0
  187. package/templates/layers/react/typescript/src/components/ui/Input.tsx +58 -0
  188. package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +116 -53
  189. package/templates/layers/react/typescript/src/components/ui/SidebarHeader.tsx +5 -4
  190. package/templates/layers/react/typescript/src/config/appConfig.ts +3 -3
  191. package/templates/layers/react/typescript/src/hooks/useWindowSize.ts +2 -3
  192. package/templates/layers/react/typescript/src/main.tsx +11 -11
  193. package/templates/layers/react/typescript/src/services/authService.ts +1 -1
  194. package/templates/layers/react/typescript/src/store/useAuthStore.ts +24 -9
  195. package/templates/layers/react-native/base/app.json +9 -22
  196. package/templates/layers/react-native/base/eslint.config.js +5 -1
  197. package/templates/layers/react-native/base/package.json +11 -1
  198. package/templates/layers/react-native/base/src/app/(auth)/edit-profile.tsx +7 -7
  199. package/templates/layers/react-native/base/src/app/(auth)/login.tsx +31 -7
  200. package/templates/layers/react-native/base/src/app/(tabs)/_layout.tsx +3 -3
  201. package/templates/layers/react-native/base/src/app/(tabs)/index.tsx +6 -5
  202. package/templates/layers/react-native/base/src/app/_layout.tsx +3 -3
  203. package/templates/layers/react-native/base/src/app/doc-viewer.tsx +3 -3
  204. package/templates/layers/react-native/base/src/components/common/ConfirmationToast.tsx +58 -40
  205. package/templates/layers/react-native/base/src/components/common/GoogleAuthButton.tsx +144 -0
  206. package/templates/layers/react-native/base/src/components/common/LocationLoader.tsx +64 -9
  207. package/templates/layers/react-native/base/src/components/common/Typography.tsx +54 -19
  208. package/templates/layers/react-native/base/src/components/ui/ClassicTabBar.tsx +15 -12
  209. package/templates/layers/react-native/base/src/components/ui/CustomTabBar.tsx +84 -90
  210. package/templates/layers/react-native/base/src/components/ui/Drawer.tsx +344 -0
  211. package/templates/layers/react-native/base/src/components/ui/Sidebar.tsx +4 -4
  212. package/templates/layers/react-native/base/src/components/ui/buttons/Button.tsx +29 -6
  213. package/templates/layers/react-native/base/src/components/ui/data-display/Accordion.tsx +2 -2
  214. package/templates/layers/react-native/base/src/components/ui/data-display/Badge.tsx +1 -2
  215. package/templates/layers/react-native/base/src/components/ui/data-display/Image.tsx +3 -3
  216. package/templates/layers/react-native/base/src/components/ui/data-display/SwipeableRow.tsx +28 -7
  217. package/templates/layers/react-native/base/src/components/ui/feedback/GestureWrapper.tsx +11 -4
  218. package/templates/layers/react-native/base/src/components/ui/inputs/DatePicker.tsx +3 -3
  219. package/templates/layers/react-native/base/src/components/ui/inputs/ImagePickerModal.tsx +45 -19
  220. package/templates/layers/react-native/base/src/components/ui/inputs/Input.tsx +28 -9
  221. package/templates/layers/react-native/base/src/components/ui/inputs/SearchablePicker.tsx +199 -0
  222. package/templates/layers/react-native/base/src/components/ui/inputs/Switch.tsx +7 -7
  223. package/templates/layers/react-native/base/src/components/ui/inputs/WebLocationPicker.tsx +146 -72
  224. package/templates/layers/react-native/base/src/config/api.ts +5 -5
  225. package/templates/layers/react-native/base/src/config/appConfig.ts +1 -1
  226. package/templates/layers/react-native/base/src/services/auth.service.ts +3 -1
  227. package/templates/layers/react-native/base/src/services/permission.service.ts +0 -2
  228. package/templates/layers/react-native/base/src/services/socket.service.ts +0 -7
  229. package/templates/layers/react-native/base/src/store/useAuthStore.ts +61 -16
  230. package/templates/layers/react-native/base/src/store/useNotificationStore.ts +36 -42
  231. package/templates/layers/react-native/base/src/types/images.d.ts +7 -5
  232. package/templates/layers/react-native/base/src/utils/fileDownload.ts +206 -0
  233. package/templates/layers/react-native/base/src/utils/storage.ts +1 -1
  234. package/templates/presets/electron/preset.json +9 -0
  235. package/templates/presets/electron-ts/preset.json +9 -0
  236. package/templates/presets/production-fullstack/overrides/client/package.json +9 -0
  237. package/templates/presets/production-fullstack/overrides/mobile/app.json +2 -15
  238. package/templates/presets/production-fullstack/overrides/mobile/package.json +11 -1
  239. package/templates/presets/production-fullstack/overrides/mobile/src/app/_layout.tsx +3 -3
  240. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/Drawer.tsx +344 -0
  241. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/DatePicker.tsx +3 -3
  242. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/ImagePickerModal.tsx +3 -3
  243. package/templates/presets/production-fullstack/overrides/server/package.json +11 -2
  244. package/templates/layers/react/javascript/src/components/ui/sidebar.jsx +0 -169
  245. package/templates/layers/react-native/base/src/components/ui/BottomModal.tsx +0 -176
  246. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/BottomModal.tsx +0 -159
  247. /package/templates/layers/react-native/base/assets/images/logo/{logoWithBg.png → icon.png} +0 -0
@@ -0,0 +1,199 @@
1
+ import React, { useState, useMemo } from "react";
2
+ import {
3
+ View,
4
+ TouchableOpacity,
5
+ Modal,
6
+ FlatList,
7
+ ActivityIndicator,
8
+ } from "react-native";
9
+ import { Txt } from "../../common/Typography";
10
+ import { Ionicons } from "@expo/vector-icons";
11
+ import { useColorScheme } from "nativewind";
12
+ import { Colors } from "../../../constants/Colors";
13
+ import ScreenHeader from "@/components/common/ScreenHeader";
14
+ import SearchBar from "./SearchBar";
15
+
16
+ interface SearchablePickerProps<T> {
17
+ label?: string;
18
+ data: T[];
19
+ value: string;
20
+ onSelect: (item: T) => void;
21
+ labelExtractor: (item: T) => string;
22
+ valueExtractor: (item: T) => string;
23
+ placeholder?: string;
24
+ isLoading?: boolean;
25
+ size?: "xs" | "sm" | "md" | "lg";
26
+ className?: string;
27
+ weight?: "normal" | "medium" | "bold" | "semibold" | "black";
28
+ renderTrigger?: (
29
+ onPress: () => void,
30
+ selectedItem: T | undefined,
31
+ ) => React.ReactNode;
32
+ onSearchChange?: (text: string) => void;
33
+ onEndReached?: () => void;
34
+ disableLocalFilter?: boolean;
35
+ renderOption?: (item: T, isSelected: boolean) => React.ReactNode;
36
+ }
37
+ export default function SearchablePicker<T>({
38
+ label,
39
+ data,
40
+ value,
41
+ onSelect,
42
+ labelExtractor,
43
+ valueExtractor,
44
+ placeholder = "Select an option",
45
+ isLoading = false,
46
+ size = "md",
47
+ className = "mb-4",
48
+ weight = "normal",
49
+ renderTrigger,
50
+ onSearchChange,
51
+ onEndReached,
52
+ disableLocalFilter = false,
53
+ renderOption,
54
+ }: SearchablePickerProps<T>) {
55
+ const { colorScheme } = useColorScheme();
56
+ const theme = Colors[colorScheme ?? "light"];
57
+ const [modalVisible, setModalVisible] = useState(false);
58
+ const [searchQuery, setSearchQuery] = useState("");
59
+
60
+ const selectedItem = useMemo(
61
+ () => data.find((item) => valueExtractor(item) === value),
62
+ [data, value, valueExtractor],
63
+ );
64
+
65
+ const filteredData = useMemo(() => {
66
+ if (disableLocalFilter) return data;
67
+ if (!searchQuery) return data;
68
+ return data.filter((item) =>
69
+ labelExtractor(item).toLowerCase().includes(searchQuery.toLowerCase()),
70
+ );
71
+ }, [data, searchQuery, labelExtractor, disableLocalFilter]);
72
+
73
+ return (
74
+ <View className={className}>
75
+ {label && (
76
+ <Txt
77
+ color={theme.baseContent}
78
+ weight={weight}
79
+ style={{ marginBottom: 6 }}
80
+ >
81
+ {label}
82
+ </Txt>
83
+ )}
84
+ {renderTrigger ? (
85
+ renderTrigger(() => setModalVisible(true), selectedItem)
86
+ ) : (
87
+ <TouchableOpacity
88
+ onPress={() => setModalVisible(true)}
89
+ className="bg-base-100 border border-base-300 rounded-xl px-4 flex-row items-center justify-between"
90
+ style={{
91
+ height:
92
+ size === "xs" ? 30 : size === "sm" ? 40 : size === "lg" ? 64 : 52,
93
+ }}
94
+ >
95
+ <Txt
96
+ className={
97
+ selectedItem ? "text-base-content" : "text-base-content/50"
98
+ }
99
+ style={{
100
+ fontSize:
101
+ size === "xs"
102
+ ? 12
103
+ : size === "sm"
104
+ ? 14
105
+ : size === "lg"
106
+ ? 18
107
+ : 16,
108
+ }}
109
+ >
110
+ {selectedItem ? labelExtractor(selectedItem) : placeholder}
111
+ </Txt>
112
+ <Ionicons
113
+ name="chevron-down"
114
+ size={size === "xs" ? 14 : size === "sm" ? 16 : 20}
115
+ color={theme.secondary}
116
+ />
117
+ </TouchableOpacity>
118
+ )}
119
+
120
+ <Modal
121
+ visible={modalVisible}
122
+ animationType="slide"
123
+ presentationStyle="pageSheet"
124
+ onRequestClose={() => setModalVisible(false)}
125
+ >
126
+ <View className="flex-1 bg-base-100">
127
+ <ScreenHeader
128
+ title={label || "Select"}
129
+ icon="close"
130
+ onBack={() => setModalVisible(false)}
131
+ />
132
+
133
+ <View className="px-4 pt-4">
134
+ <SearchBar
135
+ value={searchQuery}
136
+ onChangeText={(text) => {
137
+ setSearchQuery(text);
138
+ if (onSearchChange) onSearchChange(text);
139
+ }}
140
+ debounceMs={0}
141
+ />
142
+ </View>
143
+
144
+ {isLoading ? (
145
+ <ActivityIndicator
146
+ style={{ marginTop: 20 }}
147
+ color={theme.primary}
148
+ />
149
+ ) : (
150
+ <FlatList
151
+ data={filteredData}
152
+ keyExtractor={(item, idx) =>
153
+ valueExtractor(item) || idx.toString()
154
+ }
155
+ contentContainerStyle={{
156
+ paddingHorizontal: 16,
157
+ paddingBottom: 40,
158
+ }}
159
+ renderItem={({ item }) => (
160
+ <TouchableOpacity
161
+ onPress={() => {
162
+ onSelect(item);
163
+ setModalVisible(false);
164
+ setSearchQuery("");
165
+ }}
166
+ className="py-4 border-b border-base-200 flex-row justify-between items-center"
167
+ >
168
+ {renderOption ? (
169
+ renderOption(item, valueExtractor(item) === value)
170
+ ) : (
171
+ <>
172
+ <Txt>{labelExtractor(item)}</Txt>
173
+ {valueExtractor(item) === value && (
174
+ <Ionicons
175
+ name="checkmark"
176
+ size={20}
177
+ color={theme.primary}
178
+ />
179
+ )}
180
+ </>
181
+ )}
182
+ </TouchableOpacity>
183
+ )}
184
+ ListEmptyComponent={() => (
185
+ <View className="items-center py-8">
186
+ <Txt className="text-base-content/50">
187
+ No data found
188
+ </Txt>
189
+ </View>
190
+ )}
191
+ onEndReached={onEndReached}
192
+ onEndReachedThreshold={0.5}
193
+ />
194
+ )}
195
+ </View>
196
+ </Modal>
197
+ </View>
198
+ );
199
+ }
@@ -11,15 +11,15 @@ import { Colors } from "../../../constants/Colors";
11
11
  import { Txt } from "../../common/Typography";
12
12
 
13
13
  export interface SwitchProps {
14
- checked: boolean;
15
- onChange?: (checked: boolean) => void;
14
+ value: boolean;
15
+ onChange?: (value: boolean) => void;
16
16
  label?: string;
17
17
  disabled?: boolean;
18
18
  style?: StyleProp<ViewStyle>;
19
19
  }
20
20
 
21
21
  export default function Switch({
22
- checked,
22
+ value,
23
23
  onChange,
24
24
  label,
25
25
  disabled,
@@ -28,11 +28,11 @@ export default function Switch({
28
28
  const { colorScheme } = useColorScheme();
29
29
  const theme = Colors[colorScheme ?? "light"];
30
30
 
31
- const progress = useSharedValue(checked ? 1 : 0);
31
+ const progress = useSharedValue(value ? 1 : 0);
32
32
 
33
33
  useEffect(() => {
34
- progress.value = withTiming(checked ? 1 : 0, { duration: 250 });
35
- }, [checked, progress]);
34
+ progress.value = withTiming(value ? 1 : 0, { duration: 250 });
35
+ }, [value, progress]);
36
36
 
37
37
  const trackStyle = useAnimatedStyle(() => {
38
38
  const backgroundColor = interpolateColor(
@@ -53,7 +53,7 @@ export default function Switch({
53
53
  <TouchableOpacity
54
54
  activeOpacity={0.8}
55
55
  disabled={disabled}
56
- onPress={() => onChange && onChange(!checked)}
56
+ onPress={() => onChange && onChange(!value)}
57
57
  style={[{ flexDirection: "row", alignItems: "center", opacity: disabled ? 0.5 : 1 }, style]}
58
58
  >
59
59
  <Animated.View
@@ -10,6 +10,7 @@ import Input from "./Input";
10
10
  import { toast } from "@/utils/toast";
11
11
  import { WebView, WebViewMessageEvent } from "react-native-webview";
12
12
  import { LocationLoader } from "../../common/LocationLoader";
13
+ import ScreenHeader from "@/components/common/ScreenHeader";
13
14
 
14
15
  export interface LocationDetails {
15
16
  addressLine1: string;
@@ -40,11 +41,12 @@ export default function WebLocationPicker({
40
41
  const [loading, setLoading] = useState(false);
41
42
  const [searchQuery, setSearchQuery] = useState("");
42
43
  const [searching, setSearching] = useState(false);
43
-
44
+
44
45
  const [loadingLocation, setLoadingLocation] = useState(false);
45
46
  const [animationDone, setAnimationDone] = useState(false);
46
47
  const [positionFetched, setPositionFetched] = useState(false);
47
-
48
+ const [showWebView, setShowWebView] = useState(true);
49
+
48
50
  const [mapCenter, setMapCenter] = useState({
49
51
  latitude: initialCoordinate?.latitude || 28.6139,
50
52
  longitude: initialCoordinate?.longitude || 77.209,
@@ -56,7 +58,6 @@ export default function WebLocationPicker({
56
58
 
57
59
  useEffect(() => {
58
60
  if (initialCoordinate?.latitude && initialCoordinate?.longitude) {
59
-
60
61
  setMapCenter(initialCoordinate);
61
62
  setCurrentCenter(initialCoordinate);
62
63
  }
@@ -69,6 +70,7 @@ export default function WebLocationPicker({
69
70
  }, [animationDone, positionFetched]);
70
71
 
71
72
  const requestLocationAndOpenMap = async () => {
73
+ setShowWebView(true);
72
74
  setModalVisible(true);
73
75
  setLoadingLocation(true);
74
76
  setAnimationDone(false);
@@ -84,7 +86,9 @@ export default function WebLocationPicker({
84
86
  }
85
87
 
86
88
  if (!initialCoordinate?.latitude) {
87
- let location = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.Balanced });
89
+ let location = await Location.getCurrentPositionAsync({
90
+ accuracy: Location.Accuracy.Balanced,
91
+ });
88
92
  setMapCenter({
89
93
  latitude: location.coords.latitude,
90
94
  longitude: location.coords.longitude,
@@ -120,8 +124,15 @@ export default function WebLocationPicker({
120
124
  latitude: currentCenter.latitude,
121
125
  longitude: currentCenter.longitude,
122
126
  };
123
- onLocationSelected(details);
124
- setModalVisible(false);
127
+
128
+ // Hide WebView first to prevent Android crash when Modal closes
129
+ setShowWebView(false);
130
+ setTimeout(() => {
131
+ onLocationSelected(details);
132
+ setModalVisible(false);
133
+ setLoading(false);
134
+ }, 150);
135
+ return;
125
136
  }
126
137
  } catch (error) {
127
138
  console.warn("Geocoding failed", error);
@@ -129,6 +140,37 @@ export default function WebLocationPicker({
129
140
  setLoading(false);
130
141
  };
131
142
 
143
+ const [fetchingCurrentLocation, setFetchingCurrentLocation] = useState(false);
144
+
145
+ const handleCurrentLocation = async () => {
146
+ setFetchingCurrentLocation(true);
147
+ try {
148
+ let { status } = await Location.requestForegroundPermissionsAsync();
149
+ if (status !== "granted") {
150
+ toast.error("Permission to access location was denied");
151
+ return;
152
+ }
153
+
154
+ let location = await Location.getCurrentPositionAsync({
155
+ accuracy: Location.Accuracy.Balanced,
156
+ });
157
+
158
+ const lat = location.coords.latitude;
159
+ const lng = location.coords.longitude;
160
+
161
+ setCurrentCenter({ latitude: lat, longitude: lng });
162
+ webViewRef.current?.injectJavaScript(`
163
+ map.setView([${lat}, ${lng}], 15);
164
+ true;
165
+ `);
166
+ } catch (error) {
167
+ console.warn("Failed to get current location", error);
168
+ toast.error("Failed to get current location");
169
+ } finally {
170
+ setFetchingCurrentLocation(false);
171
+ }
172
+ };
173
+
132
174
  const handleSearch = async () => {
133
175
  if (!searchQuery.trim()) return;
134
176
  setSearching(true);
@@ -142,11 +184,10 @@ export default function WebLocationPicker({
142
184
  true;
143
185
  `);
144
186
  } else {
145
- toast.success("Location not found");
187
+ toast.error("Location not found");
146
188
  }
147
- } catch (error) {
148
- console.warn("Search failed", error);
149
- toast.error("Failed to search location");
189
+ } catch {
190
+ toast.error("Failed to search");
150
191
  }
151
192
  setSearching(false);
152
193
  };
@@ -163,7 +204,8 @@ export default function WebLocationPicker({
163
204
  } catch {}
164
205
  };
165
206
 
166
- const htmlContent = useMemo(() => `
207
+ const htmlContent = useMemo(
208
+ () => `
167
209
  <!DOCTYPE html>
168
210
  <html>
169
211
  <head>
@@ -188,7 +230,7 @@ export default function WebLocationPicker({
188
230
  <div class="center-marker">📍</div>
189
231
  <script>
190
232
  var map = L.map('map', { zoomControl: false, attributionControl: false }).setView([${mapCenter.latitude}, ${mapCenter.longitude}], 15);
191
- L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png', {
233
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
192
234
  maxZoom: 19
193
235
  }).addTo(map);
194
236
 
@@ -203,7 +245,9 @@ export default function WebLocationPicker({
203
245
  </script>
204
246
  </body>
205
247
  </html>
206
- `, [mapCenter.latitude, mapCenter.longitude]);
248
+ `,
249
+ [mapCenter.latitude, mapCenter.longitude],
250
+ );
207
251
 
208
252
  return (
209
253
  <>
@@ -223,14 +267,18 @@ export default function WebLocationPicker({
223
267
  />
224
268
  <Txt color={theme.baseContent} weight="semibold">
225
269
  {initialCoordinate?.latitude
226
- ? "Update Location on Map"
227
- : "Pick from Map"}
270
+ ? "Update Location"
271
+ : "Pick from map"}
228
272
  </Txt>
229
273
  </View>
230
274
  {loading && !modalVisible ? (
231
275
  <ActivityIndicator color={theme.primary} />
232
276
  ) : (
233
- <Ionicons name="chevron-forward" size={20} color={theme.secondary} />
277
+ <Ionicons
278
+ name="chevron-forward"
279
+ size={20}
280
+ color={theme.secondary}
281
+ />
234
282
  )}
235
283
  </TouchableOpacity>
236
284
  )}
@@ -251,68 +299,94 @@ export default function WebLocationPicker({
251
299
  </View>
252
300
  ) : (
253
301
  <View className="flex-1 bg-base-100">
254
- <View
255
- className="h-16 flex-row items-center justify-between px-4 border-b border-base-300 pt-4 bg-base-100"
256
- style={{ zIndex: 10 }}
257
- >
258
- <TouchableOpacity onPress={() => setModalVisible(false)}>
259
- <Ionicons name="close" size={28} color={theme.baseContent} />
260
- </TouchableOpacity>
261
- <Txt weight="bold" style={{ fontSize: 18 }}>
262
- Select Location
263
- </Txt>
264
- <View style={{ width: 28 }} />
265
- </View>
302
+ <ScreenHeader
303
+ icon="close"
304
+ onBack={() => setModalVisible(false)}
305
+ title="Select Location"
306
+ />
266
307
 
267
- <View className="px-4 py-3 bg-base-100 flex-row items-center border-b border-base-200 gap-4">
268
- <View className="flex-1" style={{ marginBottom: -16 }}>
269
- <Input
270
- placeholder="Search city, area, etc."
271
- value={searchQuery}
272
- onChangeText={setSearchQuery}
273
- onSubmitEditing={handleSearch}
274
- returnKeyType="search"
275
- containerStyle={{ marginBottom: 0 }}
276
- leftIcon="search"
308
+ <View className="px-4 py-3 bg-base-100 flex-row items-center border-b border-base-200 gap-4">
309
+ <View className="flex-1" style={{ marginBottom: -8 }}>
310
+ <Input
311
+ placeholder="Search Location"
312
+ value={searchQuery}
313
+ onChangeText={setSearchQuery}
314
+ onSubmitEditing={handleSearch}
315
+ returnKeyType="search"
316
+ containerStyle={{ marginBottom: 0 }}
317
+ leftIcon="search"
318
+ />
319
+ </View>
320
+ <Button
321
+ onPress={handleSearch}
322
+ isLoading={searching}
323
+ label="Go"
324
+ variant="primary"
325
+ soft
326
+ size="md"
327
+ className=""
277
328
  />
278
329
  </View>
279
- <Button
280
- onPress={handleSearch}
281
- isLoading={searching}
282
- label="Go"
283
- variant="primary"
284
- soft
285
- size="md"
286
- className="my-auto"
287
- />
288
- </View>
289
330
 
290
- <View className="flex-1 relative">
291
- <WebView
292
- ref={webViewRef}
293
- source={{ html: htmlContent }}
294
- onMessage={handleMessage}
295
- style={{ flex: 1 }}
296
- javaScriptEnabled={true}
297
- scrollEnabled={false}
298
- bounces={false}
299
- startInLoadingState={true}
300
- renderLoading={() => (
301
- <View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, justifyContent: 'center', alignItems: 'center', backgroundColor: '#f0f0f0' }}>
302
- <ActivityIndicator size="large" color={theme.primary} />
303
- </View>
331
+ <View className="flex-1 relative">
332
+ {showWebView && (
333
+ <WebView
334
+ ref={webViewRef}
335
+ source={{ html: htmlContent }}
336
+ onMessage={handleMessage}
337
+ style={{ flex: 1 }}
338
+ javaScriptEnabled={true}
339
+ scrollEnabled={false}
340
+ bounces={false}
341
+ startInLoadingState={true}
342
+ renderLoading={() => (
343
+ <View
344
+ style={{
345
+ position: "absolute",
346
+ top: 0,
347
+ left: 0,
348
+ right: 0,
349
+ bottom: 0,
350
+ justifyContent: "center",
351
+ alignItems: "center",
352
+ backgroundColor: "#f0f0f0",
353
+ }}
354
+ >
355
+ <ActivityIndicator size="large" color={theme.primary} />
356
+ </View>
357
+ )}
358
+ />
304
359
  )}
305
- />
306
- </View>
307
360
 
308
- <View className="p-4 bg-base-100 border-t border-base-300 pb-8">
309
- <Button
310
- label="Confirm Location"
311
- onPress={handleConfirmLocation}
312
- isLoading={loading}
313
- variant="primary"
314
- />
315
- </View>
361
+ <TouchableOpacity
362
+ onPress={handleCurrentLocation}
363
+ disabled={fetchingCurrentLocation}
364
+ className="absolute bottom-6 right-4 w-12 h-12 rounded-full items-center justify-center shadow-lg border border-base-200"
365
+ style={{
366
+ backgroundColor: theme.base100,
367
+ elevation: 5,
368
+ shadowColor: "#000",
369
+ shadowOffset: { width: 0, height: 2 },
370
+ shadowOpacity: 0.25,
371
+ shadowRadius: 3.84,
372
+ }}
373
+ >
374
+ {fetchingCurrentLocation ? (
375
+ <ActivityIndicator size="small" color={theme.primary} />
376
+ ) : (
377
+ <Ionicons name="locate" size={24} color={theme.primary} />
378
+ )}
379
+ </TouchableOpacity>
380
+ </View>
381
+
382
+ <View className="p-4 bg-base-100 border-t border-base-300 pb-8">
383
+ <Button
384
+ label="Confirm Location"
385
+ onPress={handleConfirmLocation}
386
+ isLoading={loading}
387
+ variant="primary"
388
+ />
389
+ </View>
316
390
  </View>
317
391
  )}
318
392
  </Modal>
@@ -4,11 +4,11 @@ import { ApiResponse } from "../types/common.type";
4
4
 
5
5
  declare module 'axios' {
6
6
  export interface AxiosInstance {
7
- get<T = any, R = AxiosResponse<ApiResponse<T>>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
8
- delete<T = any, R = AxiosResponse<ApiResponse<T>>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
9
- post<T = any, R = AxiosResponse<ApiResponse<T>>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
10
- put<T = any, R = AxiosResponse<ApiResponse<T>>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
11
- patch<T = any, R = AxiosResponse<ApiResponse<T>>, D = any>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
7
+ get<T = unknown, R = AxiosResponse<ApiResponse<T>>, D = unknown>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
8
+ delete<T = unknown, R = AxiosResponse<ApiResponse<T>>, D = unknown>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
9
+ post<T = unknown, R = AxiosResponse<ApiResponse<T>>, D = unknown>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
10
+ put<T = unknown, R = AxiosResponse<ApiResponse<T>>, D = unknown>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
11
+ patch<T = unknown, R = AxiosResponse<ApiResponse<T>>, D = unknown>(url: string, data?: D, config?: AxiosRequestConfig<D>): Promise<R>;
12
12
  }
13
13
  }
14
14
 
@@ -5,7 +5,7 @@ const appConfig = {
5
5
  shortName: "Mobile App",
6
6
  description: "A mobile application built with @imksh/create",
7
7
  version: "1.0.0",
8
- author: "Karan Sharma",
8
+ author: "",
9
9
  },
10
10
 
11
11
  api: {
@@ -14,9 +14,11 @@ export const authService = {
14
14
  login: (email: string, password: string) =>
15
15
  api.post("/auth/login", { email, password }),
16
16
 
17
+ googleLogin: (idToken: string) => api.post("/auth/google", { idToken }),
18
+
17
19
  getMe: () => api.get("/auth/me"),
18
20
 
19
- updateMe: (data: any) => api.put("/auth/me", data),
21
+ updateMe: (data: FormData | Record<string, unknown>) => api.put("/auth/me", data),
20
22
 
21
23
  logout: () => api.post("/auth/logout"),
22
24
 
@@ -175,13 +175,11 @@ class PermissionService {
175
175
  const Notifications = require("expo-notifications");
176
176
 
177
177
  if (!Device.isDevice) {
178
- console.log("Must use physical device for Push Notifications");
179
178
  return null;
180
179
  }
181
180
 
182
181
  const hasPermission = await this.requirePermission("notification");
183
182
  if (!hasPermission) {
184
- console.log("User denied push notification permissions");
185
183
  return null;
186
184
  }
187
185
 
@@ -17,13 +17,6 @@ export const initializeSocket = async () => {
17
17
  transports: ['websocket'],
18
18
  });
19
19
 
20
- socket.on('connect', () => {
21
- console.log('Connected to socket server');
22
- });
23
-
24
- socket.on('disconnect', () => {
25
- console.log('Disconnected from socket server');
26
- });
27
20
  };
28
21
 
29
22
  export const getSocket = () => socket;