@imksh/create 1.0.31 → 1.0.33

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 (158) 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/templates/layers/next/javascript/package.json +8 -2
  6. package/templates/layers/next/typescript/.eslintrc.json +12 -0
  7. package/templates/layers/next/typescript/package.json +14 -3
  8. package/templates/layers/next/typescript/src/components/common/Loading.tsx +1 -1
  9. package/templates/layers/next/typescript/src/components/common/LottieIcon.tsx +2 -2
  10. package/templates/layers/next/typescript/src/components/common/Table.tsx +4 -4
  11. package/templates/layers/next/typescript/src/components/modals/ConfirmModal.tsx +2 -1
  12. package/templates/layers/next/typescript/src/components/ui/Header.tsx +2 -2
  13. package/templates/layers/next/typescript/src/hooks/usePwaInstall.ts +10 -4
  14. package/templates/layers/next/typescript/src/services/authService.ts +1 -1
  15. package/templates/layers/next/typescript/src/store/useAuthStore.ts +24 -9
  16. package/templates/layers/node/javascript/.env.example +3 -1
  17. package/templates/layers/node/javascript/package.json +12 -3
  18. package/templates/layers/node/javascript/src/app.js +2 -1
  19. package/templates/layers/node/javascript/src/controllers/auth.controller.js +106 -168
  20. package/templates/layers/node/javascript/src/controllers/push.controller.js +51 -58
  21. package/templates/layers/node/javascript/src/controllers/upload.controller.js +12 -19
  22. package/templates/layers/node/javascript/src/middleware/auth.middleware.js +5 -11
  23. package/templates/layers/node/javascript/src/middleware/errorHandler.js +6 -4
  24. package/templates/layers/node/javascript/src/routes/auth.route.js +2 -0
  25. package/templates/layers/node/javascript/src/services/auth.service.js +159 -164
  26. package/templates/layers/node/javascript/src/utils/ApiError.js +20 -0
  27. package/templates/layers/node/javascript/src/utils/SuccessResponse.js +15 -0
  28. package/templates/layers/node/javascript/src/utils/apiResponse.js +4 -0
  29. package/templates/layers/node/javascript/src/utils/asyncHandler.js +5 -0
  30. package/templates/layers/node/typescript/.env.example +3 -1
  31. package/templates/layers/node/typescript/.eslintrc.cjs +28 -0
  32. package/templates/layers/node/typescript/package.json +16 -2
  33. package/templates/layers/node/typescript/src/app.ts +3 -2
  34. package/templates/layers/node/typescript/src/config/db.ts +3 -2
  35. package/templates/layers/node/typescript/src/config/socket.ts +5 -4
  36. package/templates/layers/node/typescript/src/controllers/auth.controller.ts +104 -132
  37. package/templates/layers/node/typescript/src/controllers/push.controller.ts +52 -59
  38. package/templates/layers/node/typescript/src/controllers/upload.controller.ts +12 -19
  39. package/templates/layers/node/typescript/src/middleware/auth.middleware.ts +13 -7
  40. package/templates/layers/node/typescript/src/middleware/errorHandler.ts +18 -5
  41. package/templates/layers/node/typescript/src/middleware/rateLimiter.ts +2 -2
  42. package/templates/layers/node/typescript/src/routes/auth.route.ts +2 -0
  43. package/templates/layers/node/typescript/src/services/auth.service.ts +86 -43
  44. package/templates/layers/node/typescript/src/types/express.d.ts +5 -3
  45. package/templates/layers/node/typescript/src/utils/ApiError.ts +25 -0
  46. package/templates/layers/node/typescript/src/utils/SuccessResponse.ts +17 -0
  47. package/templates/layers/node/typescript/src/utils/apiResponse.ts +4 -0
  48. package/templates/layers/node/typescript/src/utils/asyncHandler.ts +13 -0
  49. package/templates/layers/node/typescript/src/utils/cloudinaryUpload.ts +18 -7
  50. package/templates/layers/node/typescript/src/utils/expoNotification.ts +1 -1
  51. package/templates/layers/node/typescript/src/utils/genOtpToken.ts +7 -1
  52. package/templates/layers/node/typescript/src/utils/jwt.ts +8 -2
  53. package/templates/layers/node/typescript/src/utils/logger.ts +2 -1
  54. package/templates/layers/node/typescript/src/utils/pushHelper.ts +13 -9
  55. package/templates/layers/node-prisma/javascript/.env.example +3 -1
  56. package/templates/layers/node-prisma/javascript/package.json +11 -2
  57. package/templates/layers/node-prisma/javascript/src/app.js +2 -1
  58. package/templates/layers/node-prisma/javascript/src/controllers/auth.controller.js +90 -135
  59. package/templates/layers/node-prisma/javascript/src/controllers/push.controller.js +64 -76
  60. package/templates/layers/node-prisma/javascript/src/controllers/upload.controller.js +12 -19
  61. package/templates/layers/node-prisma/javascript/src/middleware/auth.middleware.js +5 -4
  62. package/templates/layers/node-prisma/javascript/src/middleware/errorHandler.js +6 -4
  63. package/templates/layers/node-prisma/javascript/src/routes/auth.route.js +2 -0
  64. package/templates/layers/node-prisma/javascript/src/services/auth.service.js +145 -122
  65. package/templates/layers/node-prisma/javascript/src/utils/ApiError.js +20 -0
  66. package/templates/layers/node-prisma/javascript/src/utils/SuccessResponse.js +15 -0
  67. package/templates/layers/node-prisma/javascript/src/utils/apiResponse.js +4 -0
  68. package/templates/layers/node-prisma/javascript/src/utils/asyncHandler.js +5 -0
  69. package/templates/layers/node-prisma/typescript/.env.example +3 -1
  70. package/templates/layers/node-prisma/typescript/.eslintrc.cjs +28 -0
  71. package/templates/layers/node-prisma/typescript/package.json +19 -5
  72. package/templates/layers/node-prisma/typescript/src/app.ts +3 -2
  73. package/templates/layers/node-prisma/typescript/src/config/prisma.ts +3 -2
  74. package/templates/layers/node-prisma/typescript/src/config/socket.ts +5 -4
  75. package/templates/layers/node-prisma/typescript/src/controllers/auth.controller.ts +89 -105
  76. package/templates/layers/node-prisma/typescript/src/controllers/push.controller.ts +67 -83
  77. package/templates/layers/node-prisma/typescript/src/controllers/upload.controller.ts +12 -19
  78. package/templates/layers/node-prisma/typescript/src/middleware/auth.middleware.ts +9 -13
  79. package/templates/layers/node-prisma/typescript/src/middleware/errorHandler.ts +18 -5
  80. package/templates/layers/node-prisma/typescript/src/middleware/rateLimiter.ts +2 -2
  81. package/templates/layers/node-prisma/typescript/src/routes/auth.route.ts +2 -0
  82. package/templates/layers/node-prisma/typescript/src/services/auth.service.ts +83 -31
  83. package/templates/layers/node-prisma/typescript/src/types/express.d.ts +5 -3
  84. package/templates/layers/node-prisma/typescript/src/utils/ApiError.ts +25 -0
  85. package/templates/layers/node-prisma/typescript/src/utils/SuccessResponse.ts +17 -0
  86. package/templates/layers/node-prisma/typescript/src/utils/apiResponse.ts +4 -0
  87. package/templates/layers/node-prisma/typescript/src/utils/asyncHandler.ts +13 -0
  88. package/templates/layers/node-prisma/typescript/src/utils/cloudinaryUpload.ts +18 -7
  89. package/templates/layers/node-prisma/typescript/src/utils/genOtpToken.ts +7 -1
  90. package/templates/layers/node-prisma/typescript/src/utils/jwt.ts +8 -2
  91. package/templates/layers/node-prisma/typescript/src/utils/logger.ts +2 -1
  92. package/templates/layers/node-prisma/typescript/src/utils/pushHelper.ts +10 -6
  93. package/templates/layers/prisma/javascript/package.json +8 -0
  94. package/templates/layers/prisma/typescript/package.json +14 -1
  95. package/templates/layers/prisma/typescript/src/config/db.ts +3 -2
  96. package/templates/layers/react/javascript/package.json +6 -0
  97. package/templates/layers/react/typescript/.eslintrc.cjs +20 -0
  98. package/templates/layers/react/typescript/package.json +11 -2
  99. package/templates/layers/react/typescript/src/components/common/Table.tsx +4 -4
  100. package/templates/layers/react/typescript/src/components/ui/Sidebar.tsx +1 -1
  101. package/templates/layers/react/typescript/src/main.tsx +11 -11
  102. package/templates/layers/react/typescript/src/services/authService.ts +1 -1
  103. package/templates/layers/react/typescript/src/store/useAuthStore.ts +24 -9
  104. package/templates/layers/react-native/base/app.json +9 -22
  105. package/templates/layers/react-native/base/eslint.config.js +5 -1
  106. package/templates/layers/react-native/base/package.json +40 -33
  107. package/templates/layers/react-native/base/src/app/(auth)/edit-profile.tsx +7 -7
  108. package/templates/layers/react-native/base/src/app/(auth)/login.tsx +31 -7
  109. package/templates/layers/react-native/base/src/app/(tabs)/_layout.tsx +3 -3
  110. package/templates/layers/react-native/base/src/app/(tabs)/index.tsx +6 -5
  111. package/templates/layers/react-native/base/src/app/_layout.tsx +4 -8
  112. package/templates/layers/react-native/base/src/app/screens/doc-viewer.tsx +378 -0
  113. package/templates/layers/react-native/base/src/components/common/ConfirmationToast.tsx +58 -40
  114. package/templates/layers/react-native/base/src/components/common/GoogleAuthButton.tsx +123 -0
  115. package/templates/layers/react-native/base/src/components/common/LocationLoader.tsx +64 -9
  116. package/templates/layers/react-native/base/src/components/common/Typography.tsx +54 -19
  117. package/templates/layers/react-native/base/src/components/ui/BiometricLock.tsx +3 -3
  118. package/templates/layers/react-native/base/src/components/ui/ClassicTabBar.tsx +15 -12
  119. package/templates/layers/react-native/base/src/components/ui/CustomTabBar.tsx +90 -91
  120. package/templates/layers/react-native/base/src/components/ui/Drawer.tsx +344 -0
  121. package/templates/layers/react-native/base/src/components/ui/Sidebar.tsx +4 -4
  122. package/templates/layers/react-native/base/src/components/ui/buttons/Button.tsx +29 -6
  123. package/templates/layers/react-native/base/src/components/ui/data-display/Accordion.tsx +2 -2
  124. package/templates/layers/react-native/base/src/components/ui/data-display/Badge.tsx +1 -2
  125. package/templates/layers/react-native/base/src/components/ui/data-display/Image.tsx +3 -3
  126. package/templates/layers/react-native/base/src/components/ui/data-display/SwipeableRow.tsx +28 -7
  127. package/templates/layers/react-native/base/src/components/ui/feedback/GestureWrapper.tsx +11 -4
  128. package/templates/layers/react-native/base/src/components/ui/inputs/DatePicker.tsx +3 -3
  129. package/templates/layers/react-native/base/src/components/ui/inputs/ImagePickerModal.tsx +45 -19
  130. package/templates/layers/react-native/base/src/components/ui/inputs/Input.tsx +28 -9
  131. package/templates/layers/react-native/base/src/components/ui/inputs/SearchablePicker.tsx +199 -0
  132. package/templates/layers/react-native/base/src/components/ui/inputs/Switch.tsx +7 -7
  133. package/templates/layers/react-native/base/src/components/ui/inputs/WebLocationPicker.tsx +146 -72
  134. package/templates/layers/react-native/base/src/config/api.ts +5 -5
  135. package/templates/layers/react-native/base/src/services/auth.service.ts +3 -1
  136. package/templates/layers/react-native/base/src/services/permission.service.ts +168 -44
  137. package/templates/layers/react-native/base/src/services/socket.service.ts +0 -7
  138. package/templates/layers/react-native/base/src/store/useAuthStore.ts +61 -16
  139. package/templates/layers/react-native/base/src/store/useNotificationStore.ts +36 -42
  140. package/templates/layers/react-native/base/src/types/images.d.ts +7 -5
  141. package/templates/layers/react-native/base/src/utils/env.ts +47 -0
  142. package/templates/layers/react-native/base/src/utils/fileDownload.ts +206 -0
  143. package/templates/layers/react-native/base/src/utils/storage.ts +1 -1
  144. package/templates/presets/production-fullstack/overrides/client/package.json +9 -0
  145. package/templates/presets/production-fullstack/overrides/mobile/app.json +2 -15
  146. package/templates/presets/production-fullstack/overrides/mobile/package.json +39 -29
  147. package/templates/presets/production-fullstack/overrides/mobile/src/app/_layout.tsx +3 -3
  148. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/Drawer.tsx +344 -0
  149. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/DatePicker.tsx +3 -3
  150. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/inputs/ImagePickerModal.tsx +3 -3
  151. package/templates/presets/production-fullstack/overrides/mobile/src/services/permission.service.ts +168 -44
  152. package/templates/presets/production-fullstack/overrides/mobile/src/store/useNotificationStore.ts +16 -0
  153. package/templates/presets/production-fullstack/overrides/mobile/src/utils/env.ts +47 -0
  154. package/templates/presets/production-fullstack/overrides/server/package.json +11 -2
  155. package/templates/layers/react-native/base/src/app/doc-viewer.tsx +0 -193
  156. package/templates/layers/react-native/base/src/components/ui/BottomModal.tsx +0 -176
  157. package/templates/presets/production-fullstack/overrides/mobile/src/components/ui/BottomModal.tsx +0 -159
  158. /package/templates/layers/react-native/base/assets/images/logo/{logoWithBg.png → icon.png} +0 -0
@@ -0,0 +1,344 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import {
3
+ View,
4
+ TouchableWithoutFeedback,
5
+ Dimensions,
6
+ StyleSheet,
7
+ KeyboardAvoidingView,
8
+ Platform,
9
+ BackHandler,
10
+ ViewStyle,
11
+ } from "react-native";
12
+ import Animated, {
13
+ useSharedValue,
14
+ useAnimatedStyle,
15
+ withTiming,
16
+ Easing,
17
+ runOnJS,
18
+ } from "react-native-reanimated";
19
+ import { Gesture, GestureDetector } from "react-native-gesture-handler";
20
+ import { Portal } from "react-native-paper";
21
+ import { useColorScheme } from "nativewind";
22
+ import { Colors } from "../../constants/Colors";
23
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
24
+
25
+ const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get("window");
26
+
27
+ export type DrawerPosition = "top" | "bottom" | "left" | "right";
28
+
29
+ export interface DrawerProps {
30
+ isOpen: boolean;
31
+ onClose: () => void;
32
+ children: React.ReactNode;
33
+ position?: DrawerPosition;
34
+ sizePercent?: number; // e.g. 0.5 for 50% of the screen height (top/bottom) or width (left/right)
35
+ heightPercent?: number; // backwards compatibility alias for top/bottom
36
+ widthPercent?: number; // backwards compatibility alias for left/right
37
+ showHandle?: boolean;
38
+ zIndex?: number;
39
+ }
40
+
41
+ export default function Drawer({
42
+ isOpen,
43
+ onClose,
44
+ children,
45
+ position = "bottom",
46
+ sizePercent,
47
+ heightPercent,
48
+ widthPercent,
49
+ showHandle = true,
50
+ zIndex = 50,
51
+ }: DrawerProps) {
52
+ const { colorScheme } = useColorScheme();
53
+ const colors = Colors[colorScheme ?? "light"];
54
+ const insets = useSafeAreaInsets();
55
+
56
+ const isVertical = position === "top" || position === "bottom";
57
+ const defaultPercent = isVertical ? 0.5 : 0.75;
58
+ const resolvedPercent =
59
+ sizePercent ??
60
+ (isVertical ? heightPercent : widthPercent) ??
61
+ defaultPercent;
62
+
63
+ const DRAWER_SIZE = isVertical
64
+ ? SCREEN_HEIGHT * resolvedPercent
65
+ : SCREEN_WIDTH * resolvedPercent;
66
+
67
+ const getClosedOffset = () => {
68
+ switch (position) {
69
+ case "top":
70
+ case "left":
71
+ return -DRAWER_SIZE;
72
+ case "bottom":
73
+ case "right":
74
+ return DRAWER_SIZE;
75
+ }
76
+ };
77
+
78
+ const translate = useSharedValue(getClosedOffset());
79
+ const backdropOpacity = useSharedValue(0);
80
+ const [isRendered, setIsRendered] = useState(false);
81
+
82
+ if (isOpen && !isRendered) {
83
+ setIsRendered(true);
84
+ }
85
+
86
+ useEffect(() => {
87
+ const closedOffset = getClosedOffset();
88
+ if (isOpen) {
89
+ translate.value = withTiming(0, {
90
+ duration: 300,
91
+ easing: Easing.out(Easing.ease),
92
+ });
93
+ backdropOpacity.value = withTiming(0.4, { duration: 300 });
94
+ } else {
95
+ translate.value = withTiming(closedOffset, {
96
+ duration: 300,
97
+ easing: Easing.in(Easing.ease),
98
+ });
99
+ backdropOpacity.value = withTiming(0, { duration: 300 }, (finished) => {
100
+ if (finished) {
101
+ runOnJS(setIsRendered)(false);
102
+ }
103
+ });
104
+ }
105
+ }, [isOpen, DRAWER_SIZE, position]);
106
+
107
+ useEffect(() => {
108
+ if (!isOpen) return;
109
+
110
+ const backAction = () => {
111
+ onClose();
112
+ return true;
113
+ };
114
+
115
+ const backHandler = BackHandler.addEventListener(
116
+ "hardwareBackPress",
117
+ backAction
118
+ );
119
+
120
+ return () => backHandler.remove();
121
+ }, [isOpen, onClose]);
122
+
123
+ const panGesture = Gesture.Pan()
124
+ .onUpdate((event) => {
125
+ if (position === "bottom") {
126
+ if (event.translationY > 0) {
127
+ translate.value = event.translationY;
128
+ }
129
+ } else if (position === "top") {
130
+ if (event.translationY < 0) {
131
+ translate.value = event.translationY;
132
+ }
133
+ } else if (position === "left") {
134
+ if (event.translationX < 0) {
135
+ translate.value = event.translationX;
136
+ }
137
+ } else if (position === "right") {
138
+ if (event.translationX > 0) {
139
+ translate.value = event.translationX;
140
+ }
141
+ }
142
+ })
143
+ .onEnd((event) => {
144
+ let shouldClose = false;
145
+ if (position === "bottom") {
146
+ shouldClose =
147
+ event.translationY > DRAWER_SIZE * 0.3 || event.velocityY > 500;
148
+ } else if (position === "top") {
149
+ shouldClose =
150
+ event.translationY < -DRAWER_SIZE * 0.3 || event.velocityY < -500;
151
+ } else if (position === "left") {
152
+ shouldClose =
153
+ event.translationX < -DRAWER_SIZE * 0.3 || event.velocityX < -500;
154
+ } else if (position === "right") {
155
+ shouldClose =
156
+ event.translationX > DRAWER_SIZE * 0.3 || event.velocityX > 500;
157
+ }
158
+
159
+ if (shouldClose) {
160
+ runOnJS(onClose)();
161
+ } else {
162
+ translate.value = withTiming(0, {
163
+ duration: 300,
164
+ easing: Easing.out(Easing.ease),
165
+ });
166
+ }
167
+ });
168
+
169
+ const drawerAnimatedStyle = useAnimatedStyle(() => {
170
+ if (position === "left" || position === "right") {
171
+ return {
172
+ transform: [{ translateX: translate.value }],
173
+ };
174
+ }
175
+ return {
176
+ transform: [{ translateY: translate.value }],
177
+ };
178
+ });
179
+
180
+ const backdropAnimatedStyle = useAnimatedStyle(() => ({
181
+ opacity: backdropOpacity.value,
182
+ }));
183
+
184
+ const getContainerStyle = (): ViewStyle => {
185
+ const baseShadow: ViewStyle = {
186
+ backgroundColor: colors.base100,
187
+ elevation: 24,
188
+ shadowColor: "#000",
189
+ shadowOpacity: 0.15,
190
+ shadowRadius: 15,
191
+ };
192
+
193
+ switch (position) {
194
+ case "top":
195
+ return {
196
+ ...baseShadow,
197
+ position: "absolute",
198
+ top: 0,
199
+ left: 0,
200
+ right: 0,
201
+ height: DRAWER_SIZE,
202
+ borderBottomLeftRadius: 30,
203
+ borderBottomRightRadius: 30,
204
+ shadowOffset: { width: 0, height: 5 },
205
+ paddingTop: insets.top > 0 ? insets.top : 20,
206
+ };
207
+ case "bottom":
208
+ return {
209
+ ...baseShadow,
210
+ position: "absolute",
211
+ bottom: 0,
212
+ left: 0,
213
+ right: 0,
214
+ height: DRAWER_SIZE,
215
+ borderTopLeftRadius: 30,
216
+ borderTopRightRadius: 30,
217
+ shadowOffset: { width: 0, height: -5 },
218
+ paddingBottom: insets.bottom > 0 ? insets.bottom : 20,
219
+ };
220
+ case "left":
221
+ return {
222
+ ...baseShadow,
223
+ position: "absolute",
224
+ top: 0,
225
+ bottom: 0,
226
+ left: 0,
227
+ width: DRAWER_SIZE,
228
+ borderTopRightRadius: 30,
229
+ borderBottomRightRadius: 30,
230
+ shadowOffset: { width: 5, height: 0 },
231
+ paddingTop: insets.top > 0 ? insets.top : 20,
232
+ paddingBottom: insets.bottom > 0 ? insets.bottom : 20,
233
+ paddingLeft: insets.left > 0 ? insets.left : 0,
234
+ };
235
+ case "right":
236
+ return {
237
+ ...baseShadow,
238
+ position: "absolute",
239
+ top: 0,
240
+ bottom: 0,
241
+ right: 0,
242
+ width: DRAWER_SIZE,
243
+ borderTopLeftRadius: 30,
244
+ borderBottomLeftRadius: 30,
245
+ shadowOffset: { width: -5, height: 0 },
246
+ paddingTop: insets.top > 0 ? insets.top : 20,
247
+ paddingBottom: insets.bottom > 0 ? insets.bottom : 20,
248
+ paddingRight: insets.right > 0 ? insets.right : 0,
249
+ };
250
+ }
251
+ };
252
+
253
+ if (!isRendered && !isOpen) return null;
254
+
255
+ return (
256
+ <Portal>
257
+ <KeyboardAvoidingView
258
+ behavior={Platform.OS === "ios" ? "padding" : undefined}
259
+ style={StyleSheet.absoluteFill}
260
+ pointerEvents="box-none"
261
+ >
262
+ <View style={[StyleSheet.absoluteFill, { zIndex }]}>
263
+ {/* Backdrop Overlay */}
264
+ <TouchableWithoutFeedback onPress={onClose}>
265
+ <Animated.View
266
+ style={[
267
+ StyleSheet.absoluteFill,
268
+ { backgroundColor: "#000" },
269
+ backdropAnimatedStyle,
270
+ ]}
271
+ />
272
+ </TouchableWithoutFeedback>
273
+
274
+ {/* Sliding Drawer Panel */}
275
+ <Animated.View style={[getContainerStyle(), drawerAnimatedStyle]}>
276
+ {position === "bottom" && (
277
+ <View className="flex-1">
278
+ {showHandle && (
279
+ <GestureDetector gesture={panGesture}>
280
+ <View
281
+ className="items-center pt-4 pb-6 bg-base-100 rounded-t-[30px]"
282
+ style={{ zIndex: 10 }}
283
+ >
284
+ <View className="w-16 h-1.5 rounded-full bg-base-300" />
285
+ </View>
286
+ </GestureDetector>
287
+ )}
288
+ <View className="flex-1 px-6">{children}</View>
289
+ </View>
290
+ )}
291
+
292
+ {position === "top" && (
293
+ <View className="flex-1">
294
+ <View className="flex-1 px-6">{children}</View>
295
+ {showHandle && (
296
+ <GestureDetector gesture={panGesture}>
297
+ <View
298
+ className="items-center pt-4 pb-6 bg-base-100 rounded-b-[30px]"
299
+ style={{ zIndex: 10 }}
300
+ >
301
+ <View className="w-16 h-1.5 rounded-full bg-base-300" />
302
+ </View>
303
+ </GestureDetector>
304
+ )}
305
+ </View>
306
+ )}
307
+
308
+ {position === "left" && (
309
+ <View className="flex-1 flex-row">
310
+ <View className="flex-1 px-6">{children}</View>
311
+ {showHandle && (
312
+ <GestureDetector gesture={panGesture}>
313
+ <View
314
+ className="justify-center items-center px-3 bg-base-100 rounded-r-[30px]"
315
+ style={{ zIndex: 10 }}
316
+ >
317
+ <View className="w-1.5 h-16 rounded-full bg-base-300" />
318
+ </View>
319
+ </GestureDetector>
320
+ )}
321
+ </View>
322
+ )}
323
+
324
+ {position === "right" && (
325
+ <View className="flex-1 flex-row">
326
+ {showHandle && (
327
+ <GestureDetector gesture={panGesture}>
328
+ <View
329
+ className="justify-center items-center px-3 bg-base-100 rounded-l-[30px]"
330
+ style={{ zIndex: 10 }}
331
+ >
332
+ <View className="w-1.5 h-16 rounded-full bg-base-300" />
333
+ </View>
334
+ </GestureDetector>
335
+ )}
336
+ <View className="flex-1 px-6">{children}</View>
337
+ </View>
338
+ )}
339
+ </Animated.View>
340
+ </View>
341
+ </KeyboardAvoidingView>
342
+ </Portal>
343
+ );
344
+ }
@@ -21,7 +21,7 @@ import { Ionicons } from "@expo/vector-icons";
21
21
  import { useSafeAreaInsets } from "react-native-safe-area-context";
22
22
  import { useAuthStore } from "../../store/useAuthStore";
23
23
  import { sidebarConfig } from "../../config/sidebar.config";
24
- import { router } from "expo-router";
24
+ import { router, type Href } from "expo-router";
25
25
  import Image from "./data-display/Image";
26
26
 
27
27
  const { width } = Dimensions.get("window");
@@ -40,10 +40,10 @@ const MenuItem = ({
40
40
  onPress,
41
41
  colors,
42
42
  }: {
43
- icon: any;
43
+ icon: keyof typeof Ionicons.glyphMap;
44
44
  label: string;
45
45
  onPress?: () => void;
46
- colors: any;
46
+ colors: (typeof Colors)[keyof typeof Colors];
47
47
  }) => (
48
48
  <TouchableOpacity
49
49
  activeOpacity={0.7}
@@ -217,7 +217,7 @@ export default function Sidebar({
217
217
  label={item.label}
218
218
  onPress={() => {
219
219
  onClose();
220
- router.push(item.route as any);
220
+ router.push(item.route as Href);
221
221
  }}
222
222
  colors={colors}
223
223
  />
@@ -1,4 +1,3 @@
1
- import React from "react";
2
1
  import {
3
2
  TouchableOpacity,
4
3
  ActivityIndicator,
@@ -32,6 +31,7 @@ export interface ButtonProps extends TouchableOpacityProps {
32
31
  isFullWidth?: boolean;
33
32
  soft?: boolean;
34
33
  outline?: boolean;
34
+ className?: string;
35
35
  }
36
36
 
37
37
  export default function Button({
@@ -46,6 +46,7 @@ export default function Button({
46
46
  outline = false,
47
47
  style,
48
48
  disabled,
49
+ className = "",
49
50
  ...props
50
51
  }: ButtonProps) {
51
52
  const { colorScheme } = useColorScheme();
@@ -109,7 +110,8 @@ export default function Button({
109
110
  const vStyles = getVariantStyles();
110
111
  if (outline) {
111
112
  vStyles.border = vStyles.bg === "transparent" ? theme.base300 : vStyles.bg;
112
- vStyles.text = vStyles.bg === "transparent" ? theme.baseContent : vStyles.bg;
113
+ vStyles.text =
114
+ vStyles.bg === "transparent" ? theme.baseContent : vStyles.bg;
113
115
  vStyles.bg = "transparent";
114
116
  } else if (soft && variant !== "ghost" && variant !== "outline") {
115
117
  vStyles.text = vStyles.bg;
@@ -124,9 +126,10 @@ export default function Button({
124
126
  <TouchableOpacity
125
127
  activeOpacity={0.7}
126
128
  disabled={isDisabled}
129
+ className={className}
127
130
  style={[
128
131
  {
129
- backgroundColor: vStyles.bg as any,
132
+ backgroundColor: vStyles.bg,
130
133
  height: sStyles.height,
131
134
  paddingHorizontal: sStyles.px,
132
135
  borderRadius: 12,
@@ -134,7 +137,7 @@ export default function Button({
134
137
  alignItems: "center",
135
138
  justifyContent: "center",
136
139
  opacity: isDisabled ? 0.6 : 1,
137
- borderWidth: (variant === "outline" || outline) ? 1 : 0,
140
+ borderWidth: variant === "outline" || outline ? 1 : 0,
138
141
  borderColor: vStyles.border,
139
142
  width: isFullWidth ? "100%" : undefined,
140
143
  },
@@ -144,13 +147,29 @@ export default function Button({
144
147
  >
145
148
  {/* Loading overlay */}
146
149
  {isLoading && (
147
- <View style={{ position: "absolute", top: 0, left: 0, right: 0, bottom: 0, justifyContent: "center", alignItems: "center" }}>
150
+ <View
151
+ style={{
152
+ position: "absolute",
153
+ top: 0,
154
+ left: 0,
155
+ right: 0,
156
+ bottom: 0,
157
+ justifyContent: "center",
158
+ alignItems: "center",
159
+ }}
160
+ >
148
161
  <ActivityIndicator color={vStyles.text} size="small" />
149
162
  </View>
150
163
  )}
151
164
 
152
165
  {/* Button content (hidden while loading to maintain dimensions) */}
153
- <View style={{ flexDirection: "row", alignItems: "center", opacity: isLoading ? 0 : 1 }}>
166
+ <View
167
+ style={{
168
+ flexDirection: "row",
169
+ alignItems: "center",
170
+ opacity: isLoading ? 0 : 1,
171
+ }}
172
+ >
154
173
  {leftIcon && (
155
174
  <Ionicons
156
175
  name={leftIcon}
@@ -163,6 +182,10 @@ export default function Button({
163
182
  color={vStyles.text}
164
183
  weight="semibold"
165
184
  style={{ fontSize: sStyles.fontSize }}
185
+ allowFontScaling={true}
186
+ numberOfLines={1}
187
+ ellipsizeMode="tail"
188
+ adjustsFontSizeToFit
166
189
  >
167
190
  {label}
168
191
  </Txt>
@@ -72,9 +72,9 @@ export default function Accordion({ title, children }: AccordionProps) {
72
72
  style={{ position: "absolute", width: "100%", paddingHorizontal: 16, paddingBottom: 16 }}
73
73
  >
74
74
  {/* This inner view is measured */}
75
- <View ref={aref as any}>
75
+ <Animated.View ref={aref}>
76
76
  {children}
77
- </View>
77
+ </Animated.View>
78
78
  </View>
79
79
  </Animated.View>
80
80
  </View>
@@ -1,4 +1,3 @@
1
- import React from "react";
2
1
  import { View, ViewProps } from "react-native";
3
2
  import { useColorScheme } from "nativewind";
4
3
  import { Colors } from "../../../constants/Colors";
@@ -80,7 +79,7 @@ export default function Badge({
80
79
  <View
81
80
  style={[
82
81
  {
83
- backgroundColor: vStyles.bg as any,
82
+ backgroundColor: vStyles.bg,
84
83
  height: sStyles.height,
85
84
  paddingHorizontal: sStyles.px,
86
85
  borderRadius: 9999,
@@ -1,13 +1,13 @@
1
1
  import React from "react";
2
- import { Image as ExpoImage, ImageProps as ExpoImageProps } from "expo-image";
2
+ import { Image as ExpoImage, ImageProps as ExpoImageProps, ImageSource } from "expo-image";
3
3
  import { View, ActivityIndicator, DimensionValue } from "react-native";
4
4
  import { useColorScheme } from "nativewind";
5
5
  import { Colors } from "../../../constants/Colors";
6
6
 
7
7
  export interface ImageProps extends Omit<ExpoImageProps, "source"> {
8
8
  fallbackIcon?: React.ReactNode;
9
- src?: string | number | any;
10
- source?: any;
9
+ src?: string | number | ImageSource;
10
+ source?: ImageSource | string | number;
11
11
  width?: DimensionValue;
12
12
  height?: DimensionValue;
13
13
  }
@@ -1,5 +1,11 @@
1
1
  import React from "react";
2
- import { View, TouchableOpacity, StyleSheet, StyleProp, ViewStyle } from "react-native";
2
+ import {
3
+ View,
4
+ TouchableOpacity,
5
+ StyleSheet,
6
+ StyleProp,
7
+ ViewStyle,
8
+ } from "react-native";
3
9
  import Swipeable from "react-native-gesture-handler/Swipeable";
4
10
  import { useColorScheme } from "nativewind";
5
11
  import { Colors } from "../../../constants/Colors";
@@ -30,13 +36,24 @@ export const SwipeableRow: React.FC<SwipeableRowProps> = ({
30
36
  }) => {
31
37
  const { colorScheme } = useColorScheme();
32
38
  const isDark = colorScheme === "dark";
33
- const colors = Colors[isDark ? "dark" : "light"];
39
+ const _colors = Colors[isDark ? "dark" : "light"];
34
40
 
35
- const renderActions = (actions: SwipeAction[], progress: any, dragX: any, isRight: boolean) => {
41
+ const renderActions = (
42
+ actions: SwipeAction[],
43
+ _progress: unknown,
44
+ _dragX: unknown,
45
+ isRight: boolean,
46
+ ) => {
36
47
  if (actions.length === 0) return null;
37
48
 
38
49
  return (
39
- <View style={[styles.actionsContainer, isRight ? styles.rightActions : styles.leftActions]}>
50
+ <View
51
+ style={[
52
+ styles.actionsContainer,
53
+ isRight ? styles.rightActions : styles.leftActions,
54
+ { borderRadius: 16, overflow: "hidden", marginVertical: 1 },
55
+ ]}
56
+ >
40
57
  {actions.map((action, index) => {
41
58
  const actionWidth = action.width || 75;
42
59
  return (
@@ -62,13 +79,17 @@ export const SwipeableRow: React.FC<SwipeableRowProps> = ({
62
79
  friction={2}
63
80
  leftThreshold={40}
64
81
  rightThreshold={40}
65
- renderLeftActions={(progress, dragX) => renderActions(leftActions, progress, dragX, false)}
66
- renderRightActions={(progress, dragX) => renderActions(rightActions, progress, dragX, true)}
82
+ renderLeftActions={(progress, dragX) =>
83
+ renderActions(leftActions, progress, dragX, false)
84
+ }
85
+ renderRightActions={(progress, dragX) =>
86
+ renderActions(rightActions, progress, dragX, true)
87
+ }
67
88
  onSwipeableOpen={onSwipeOpen}
68
89
  onSwipeableClose={onSwipeClose}
69
90
  containerStyle={containerStyle}
70
91
  >
71
- <View style={{ backgroundColor: colors.base100 }}>
92
+ <View style={{ flex: 1, backgroundColor: "transparent" }}>
72
93
  {children}
73
94
  </View>
74
95
  </Swipeable>
@@ -1,6 +1,13 @@
1
1
  import React from "react";
2
2
  import { StyleProp, ViewStyle } from "react-native";
3
- import { Gesture, GestureDetector, GestureType } from "react-native-gesture-handler";
3
+ import {
4
+ Gesture,
5
+ GestureDetector,
6
+ GestureType,
7
+ GestureStateChangeEvent,
8
+ GestureUpdateEvent,
9
+ PanGestureHandlerEventPayload,
10
+ } from "react-native-gesture-handler";
4
11
  import Animated, { runOnJS } from "react-native-reanimated";
5
12
 
6
13
  interface GestureWrapperProps {
@@ -12,9 +19,9 @@ interface GestureWrapperProps {
12
19
  onDoubleTap?: () => void;
13
20
 
14
21
  // Pan/Drag Gestures
15
- onPanStart?: (e: any) => void;
16
- onPanUpdate?: (e: any) => void;
17
- onPanEnd?: (e: any) => void;
22
+ onPanStart?: (e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => void;
23
+ onPanUpdate?: (e: GestureUpdateEvent<PanGestureHandlerEventPayload>) => void;
24
+ onPanEnd?: (e: GestureStateChangeEvent<PanGestureHandlerEventPayload>) => void;
18
25
 
19
26
  // Swipe Gestures
20
27
  onSwipeLeft?: () => void;
@@ -4,7 +4,7 @@ import { Txt } from "../../common/Typography";
4
4
  import { Ionicons } from "@expo/vector-icons";
5
5
  import { useColorScheme } from "nativewind";
6
6
  import { Colors } from "../../../constants/Colors";
7
- import BottomModal from "../BottomModal";
7
+ import Drawer from "../Drawer";
8
8
  import Button from "../buttons/Button";
9
9
  import dayjs from "dayjs";
10
10
 
@@ -104,7 +104,7 @@ export default function DatePicker({
104
104
  <Ionicons name="calendar-outline" size={18} color={colors.primary} />
105
105
  </TouchableOpacity>
106
106
 
107
- <BottomModal isOpen={isOpen} onClose={() => setIsOpen(false)} heightPercent={0.65}>
107
+ <Drawer isOpen={isOpen} onClose={() => setIsOpen(false)} position="bottom" sizePercent={0.65}>
108
108
  <View className="p-4 flex-1">
109
109
  <Txt variant="lg" className="font-bold text-center mb-4">
110
110
  {label}
@@ -180,7 +180,7 @@ export default function DatePicker({
180
180
  <Button label="Close" variant="outline" onPress={() => setIsOpen(false)} className="w-full" />
181
181
  </View>
182
182
  </View>
183
- </BottomModal>
183
+ </Drawer>
184
184
  </View>
185
185
  );
186
186
  }