create-react-native-starter-kit 0.0.0-stage → 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 (211) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +378 -2
  3. package/bin/create-react-native-starter-kit.js +95 -0
  4. package/lib/copy-src.js +127 -0
  5. package/lib/creator.js +211 -0
  6. package/lib/deps.js +58 -0
  7. package/lib/patch-configs.js +318 -0
  8. package/lib/patch-native.js +588 -0
  9. package/package.json +47 -4
  10. package/scripts/sync-template.js +52 -0
  11. package/template/.env.example +3 -0
  12. package/template/patches/react-native-element-dropdown+2.12.4.patch +67 -0
  13. package/template/src/app/App.tsx +25 -0
  14. package/template/src/app/navigation/root.tsx +24 -0
  15. package/template/src/app/navigation/stacks/app.stack.tsx +67 -0
  16. package/template/src/app/navigation/stacks/app.tab.tsx +72 -0
  17. package/template/src/app/navigation/stacks/auth.stack.tsx +56 -0
  18. package/template/src/app/providers.tsx +43 -0
  19. package/template/src/assets/fonts/InterTight-Black.ttf +0 -0
  20. package/template/src/assets/fonts/InterTight-BlackItalic.ttf +0 -0
  21. package/template/src/assets/fonts/InterTight-Bold.ttf +0 -0
  22. package/template/src/assets/fonts/InterTight-BoldItalic.ttf +0 -0
  23. package/template/src/assets/fonts/InterTight-ExtraBold.ttf +0 -0
  24. package/template/src/assets/fonts/InterTight-ExtraBoldItalic.ttf +0 -0
  25. package/template/src/assets/fonts/InterTight-ExtraLight.ttf +0 -0
  26. package/template/src/assets/fonts/InterTight-ExtraLightItalic.ttf +0 -0
  27. package/template/src/assets/fonts/InterTight-Italic.ttf +0 -0
  28. package/template/src/assets/fonts/InterTight-Light.ttf +0 -0
  29. package/template/src/assets/fonts/InterTight-LightItalic.ttf +0 -0
  30. package/template/src/assets/fonts/InterTight-Medium.ttf +0 -0
  31. package/template/src/assets/fonts/InterTight-MediumItalic.ttf +0 -0
  32. package/template/src/assets/fonts/InterTight-Regular.ttf +0 -0
  33. package/template/src/assets/fonts/InterTight-SemiBold.ttf +0 -0
  34. package/template/src/assets/fonts/InterTight-SemiBoldItalic.ttf +0 -0
  35. package/template/src/assets/fonts/InterTight-Thin.ttf +0 -0
  36. package/template/src/assets/fonts/InterTight-ThinItalic.ttf +0 -0
  37. package/template/src/assets/icons/back.svg +3 -0
  38. package/template/src/assets/icons/calendar.svg +3 -0
  39. package/template/src/assets/icons/claim_upload.svg +3 -0
  40. package/template/src/assets/icons/clock.svg +1 -0
  41. package/template/src/assets/icons/downArrow.svg +3 -0
  42. package/template/src/assets/icons/edit.svg +3 -0
  43. package/template/src/assets/icons/eye.svg +4 -0
  44. package/template/src/assets/icons/eyeClose.svg +7 -0
  45. package/template/src/assets/icons/folder.svg +5 -0
  46. package/template/src/assets/icons/home.svg +4 -0
  47. package/template/src/assets/icons/index.ts +35 -0
  48. package/template/src/assets/icons/log_out.svg +4 -0
  49. package/template/src/assets/icons/setting.svg +4 -0
  50. package/template/src/assets/icons/tabs/home/filled_dark.svg +3 -0
  51. package/template/src/assets/icons/tabs/home/filled_light.svg +3 -0
  52. package/template/src/assets/icons/tabs/home/outline.svg +4 -0
  53. package/template/src/assets/icons/tabs/index.ts +31 -0
  54. package/template/src/assets/icons/tabs/profile/filled_dark.svg +6 -0
  55. package/template/src/assets/icons/tabs/profile/filled_light.svg +6 -0
  56. package/template/src/assets/icons/tabs/profile/outline.svg +3 -0
  57. package/template/src/assets/icons/tooltip_dark.svg +1 -0
  58. package/template/src/assets/icons/tooltip_light.svg +1 -0
  59. package/template/src/assets/icons/upArrow.svg +3 -0
  60. package/template/src/assets/images/back.png +0 -0
  61. package/template/src/assets/images/index.ts +7 -0
  62. package/template/src/assets/images/placeholder/profile.png +0 -0
  63. package/template/src/components/shared/AppImage.tsx +190 -0
  64. package/template/src/components/shared/AvatarPicker.tsx +175 -0
  65. package/template/src/components/shared/CustomButton.tsx +273 -0
  66. package/template/src/components/shared/DocumentPicker.tsx +86 -0
  67. package/template/src/components/shared/GlobalLoader.tsx +47 -0
  68. package/template/src/components/shared/Icon.tsx +28 -0
  69. package/template/src/components/shared/PressableIcon.tsx +104 -0
  70. package/template/src/components/shared/bottom-sheet/BaseBottomSheet.tsx +30 -0
  71. package/template/src/components/shared/bottom-sheet/BaseBottomSheetModal.tsx +50 -0
  72. package/template/src/components/shared/bottom-sheet/sheets/ImagePickerSheet.tsx +310 -0
  73. package/template/src/components/shared/bottom-sheet/types.ts +4 -0
  74. package/template/src/components/shared/form-input/FormInput.tsx +140 -0
  75. package/template/src/components/shared/form-input/FormInputRenderer.tsx +82 -0
  76. package/template/src/components/shared/form-input/FullNameInput.tsx +243 -0
  77. package/template/src/components/shared/form-input/formFieldStyles.ts +43 -0
  78. package/template/src/components/shared/form-input/type.ts +188 -0
  79. package/template/src/components/shared/form-input/variants/CountryPhoneVariant.tsx +289 -0
  80. package/template/src/components/shared/form-input/variants/DateTimeVariant.tsx +280 -0
  81. package/template/src/components/shared/form-input/variants/DropdownVariant.tsx +221 -0
  82. package/template/src/components/shared/form-input/variants/TextInputVariant.tsx +300 -0
  83. package/template/src/components/shared/form-input/variants/ToggleVariant.tsx +194 -0
  84. package/template/src/components/shared/modal/BaseModal.tsx +83 -0
  85. package/template/src/components/shared/modal/common/AlertModal.tsx +173 -0
  86. package/template/src/components/shared/navigation/AnimatedTabIcon.tsx +37 -0
  87. package/template/src/components/shared/navigation/HeaderActionButton.tsx +70 -0
  88. package/template/src/components/shared/navigation/HeaderBackButton.tsx +38 -0
  89. package/template/src/components/shared/navigation/StackHeader.tsx +105 -0
  90. package/template/src/components/shared/navigation/tabIconRender.tsx +14 -0
  91. package/template/src/components/shared/offline/OfflineQueryBoundary.tsx +25 -0
  92. package/template/src/components/shared/offline/OfflineState.tsx +229 -0
  93. package/template/src/components/shared/offline/index.ts +6 -0
  94. package/template/src/components/shared/offline/types.ts +15 -0
  95. package/template/src/components/shared/offline/wifi-icon-animation/BrokenWifiAnimation.tsx +116 -0
  96. package/template/src/components/shared/offline/wifi-icon-animation/WifiPiece.tsx +80 -0
  97. package/template/src/components/shared/offline/wifi-icon-animation/geometry.ts +163 -0
  98. package/template/src/components/shared/offline/wifi-icon-animation/useWifiIconAnimationActive.ts +37 -0
  99. package/template/src/components/shared/skelton/BaseSkelton.tsx +71 -0
  100. package/template/src/components/shared/skelton/ShimmerHolder.ts +6 -0
  101. package/template/src/components/shared/skelton/holders/HomeShimmer.tsx +25 -0
  102. package/template/src/components/shared/skelton/holders/index.ts +1 -0
  103. package/template/src/components/shared/skelton/layouts/avatar.layout.ts +18 -0
  104. package/template/src/components/shared/skelton/layouts/home.layout.ts +16 -0
  105. package/template/src/components/shared/skelton/layouts/index.ts +3 -0
  106. package/template/src/components/shared/skelton/layouts/text.layout.ts +18 -0
  107. package/template/src/components/shared/text/CustomText.tsx +85 -0
  108. package/template/src/components/shared/text/ShimmerText.tsx +159 -0
  109. package/template/src/components/shared/text/types.ts +40 -0
  110. package/template/src/constants/device.ts +12 -0
  111. package/template/src/constants/endpoint.ts +11 -0
  112. package/template/src/constants/keyExtractor.ts +6 -0
  113. package/template/src/constants/limits.ts +9 -0
  114. package/template/src/constants/queryKey.ts +3 -0
  115. package/template/src/constants/regex.ts +7 -0
  116. package/template/src/context/NetworkContext.tsx +102 -0
  117. package/template/src/context/ThemeContext.tsx +56 -0
  118. package/template/src/hooks/app/index.ts +2 -0
  119. package/template/src/hooks/app/useGetProfile.ts +38 -0
  120. package/template/src/hooks/app/useUpdateProfile.ts +21 -0
  121. package/template/src/hooks/useAppDispatch.ts +6 -0
  122. package/template/src/hooks/useAppSelector.ts +4 -0
  123. package/template/src/hooks/useDebounce.ts +15 -0
  124. package/template/src/hooks/useDocumentPicker.ts +102 -0
  125. package/template/src/hooks/useImagePicker.ts +83 -0
  126. package/template/src/i18n/i18next.d.ts +9 -0
  127. package/template/src/i18n/index.ts +16 -0
  128. package/template/src/i18n/locales/en.json +274 -0
  129. package/template/src/i18n/locales/ru.json +165 -0
  130. package/template/src/i18n/resources.ts +11 -0
  131. package/template/src/redux/selectors/auth.selectors.ts +4 -0
  132. package/template/src/redux/slice/auth.slice.ts +23 -0
  133. package/template/src/redux/slice/loader.slice.ts +25 -0
  134. package/template/src/redux/slice/settings.slice.ts +26 -0
  135. package/template/src/redux/slice/user.slice.ts +25 -0
  136. package/template/src/screens/app/main-tab/index.tsx +73 -0
  137. package/template/src/screens/app/main-tab/style.ts +0 -0
  138. package/template/src/screens/app/profile-setup/index.tsx +162 -0
  139. package/template/src/screens/app/profile-setup/styles.ts +3 -0
  140. package/template/src/screens/auth/forgot-password/index.tsx +59 -0
  141. package/template/src/screens/auth/forgot-password/styles.ts +0 -0
  142. package/template/src/screens/auth/index.ts +4 -0
  143. package/template/src/screens/auth/login/index.tsx +138 -0
  144. package/template/src/screens/auth/login/styles.ts +3 -0
  145. package/template/src/screens/auth/signup/index.tsx +111 -0
  146. package/template/src/screens/auth/signup/styles.ts +3 -0
  147. package/template/src/screens/auth/verify-email/index.tsx +15 -0
  148. package/template/src/screens/auth/verify-email/styles.ts +3 -0
  149. package/template/src/screens/tab/home/index.tsx +49 -0
  150. package/template/src/screens/tab/home/styles.ts +3 -0
  151. package/template/src/screens/tab/index.ts +2 -0
  152. package/template/src/screens/tab/profile/index.tsx +50 -0
  153. package/template/src/screens/tab/profile/styles.ts +3 -0
  154. package/template/src/services/api/auth/auth.api.ts +22 -0
  155. package/template/src/services/api/auth/auth.endpoints.ts +7 -0
  156. package/template/src/services/api/auth/auth.types.ts +9 -0
  157. package/template/src/services/api/auth/index.ts +3 -0
  158. package/template/src/services/api/axios/axios.instance.ts +15 -0
  159. package/template/src/services/api/axios/axios.interceptors.ts +43 -0
  160. package/template/src/services/api/axios/index.ts +2 -0
  161. package/template/src/services/api/mock/mockAdapter.ts +97 -0
  162. package/template/src/services/api/user/index.ts +2 -0
  163. package/template/src/services/api/user/user.api.ts +67 -0
  164. package/template/src/services/api/user/user.endpoints.ts +0 -0
  165. package/template/src/services/api/user/user.types.ts +0 -0
  166. package/template/src/services/errors/backendErrorCodes.ts +7 -0
  167. package/template/src/services/errors/errorHandler.ts +94 -0
  168. package/template/src/services/errors/firebaseErrorMap.ts +61 -0
  169. package/template/src/services/errors/httpStatusMap.ts +47 -0
  170. package/template/src/services/errors/index.ts +4 -0
  171. package/template/src/services/network/index.ts +1 -0
  172. package/template/src/services/network/onlineManager.ts +22 -0
  173. package/template/src/services/query/index.ts +1 -0
  174. package/template/src/services/query/queryClient.ts +23 -0
  175. package/template/src/services/toast/ToastCard.tsx +137 -0
  176. package/template/src/services/toast/ToastConfig.tsx +32 -0
  177. package/template/src/services/toast/index.ts +3 -0
  178. package/template/src/services/toast/toast.ts +68 -0
  179. package/template/src/storage/mmkvAdapter.ts +3 -0
  180. package/template/src/storage/mmkvInstance.ts +16 -0
  181. package/template/src/store/index.ts +15 -0
  182. package/template/src/store/mmkv.ts +6 -0
  183. package/template/src/store/persist.ts +10 -0
  184. package/template/src/store/rootReducer.ts +26 -0
  185. package/template/src/theme/dark.ts +40 -0
  186. package/template/src/theme/fonts.ts +20 -0
  187. package/template/src/theme/index.ts +6 -0
  188. package/template/src/theme/layout.ts +23 -0
  189. package/template/src/theme/light.ts +41 -0
  190. package/template/src/theme/palette.ts +32 -0
  191. package/template/src/theme/spacing.ts +7 -0
  192. package/template/src/theme/typography.ts +80 -0
  193. package/template/src/types/api.types.ts +34 -0
  194. package/template/src/types/env.d.ts +6 -0
  195. package/template/src/types/index.ts +2 -0
  196. package/template/src/types/navigation.types.ts +94 -0
  197. package/template/src/utils/clearAppSession.ts +10 -0
  198. package/template/src/utils/date.ts +19 -0
  199. package/template/src/utils/documentPickerHandler.ts +96 -0
  200. package/template/src/utils/getFileNameFromUri.ts +18 -0
  201. package/template/src/utils/getInitials.ts +13 -0
  202. package/template/src/utils/hindiText.ts +33 -0
  203. package/template/src/utils/imageCompress.ts +97 -0
  204. package/template/src/utils/imagePickerHandler.ts +200 -0
  205. package/template/src/utils/maskPhone.ts +17 -0
  206. package/template/src/utils/normalize.ts +12 -0
  207. package/template/src/validation/messages.ts +11 -0
  208. package/template/src/validation/rules.ts +44 -0
  209. package/template/src/validation/schema/auth.schema.ts +53 -0
  210. package/template/src/validation/schema/user.schema.ts +46 -0
  211. package/template/src/validation/tValError.ts +13 -0
@@ -0,0 +1,200 @@
1
+ import {
2
+ launchCamera,
3
+ launchImageLibrary,
4
+ CameraOptions,
5
+ ImageLibraryOptions,
6
+ } from 'react-native-image-picker';
7
+ import { getCompressedImage } from './imageCompress';
8
+ import { ErrorHandler } from '@/services/errors';
9
+
10
+ // ─────────────────────────────────────────────────────────────────────────────
11
+ // Types
12
+ // ─────────────────────────────────────────────────────────────────────────────
13
+
14
+ type ImageSourceType = 'gallery' | 'camera' | 'both';
15
+
16
+ interface CompressedImageResult {
17
+ uri: string;
18
+ width: number;
19
+ height: number;
20
+ }
21
+
22
+ interface PickImageOptions {
23
+ /**
24
+ * Source type for image selection
25
+ * - 'gallery': Pick from photo library only
26
+ * - 'camera': Capture from camera only
27
+ * - 'both': Show action sheet to choose (handled by caller)
28
+ */
29
+ source: ImageSourceType;
30
+
31
+ /**
32
+ * Allow multiple image selection (only works with 'gallery' source)
33
+ * @default false
34
+ */
35
+ multiple?: boolean;
36
+
37
+ /**
38
+ * Maximum dimension (width/height) for resizing
39
+ * @default 256
40
+ */
41
+ maxSize?: number;
42
+
43
+ /**
44
+ * Compression quality (0-100)
45
+ * @default 80
46
+ */
47
+ quality?: number;
48
+
49
+ /**
50
+ * Maximum number of images to select (0 = unlimited, only for multiple)
51
+ * @default 0
52
+ */
53
+ selectionLimit?: number;
54
+ }
55
+
56
+ interface PickImageResult {
57
+ /** Array of compressed image results */
58
+ images: CompressedImageResult[];
59
+ /** Whether the operation was cancelled */
60
+ cancelled: boolean;
61
+ }
62
+
63
+ // ─────────────────────────────────────────────────────────────────────────────
64
+ // Default Options
65
+ // ─────────────────────────────────────────────────────────────────────────────
66
+
67
+ const defaultOptions: Required<Omit<PickImageOptions, 'source'>> = {
68
+ multiple: false,
69
+ maxSize: 256,
70
+ quality: 80,
71
+ selectionLimit: 0,
72
+ };
73
+
74
+ // ─────────────────────────────────────────────────────────────────────────────
75
+ // Main Function
76
+ // ─────────────────────────────────────────────────────────────────────────────
77
+
78
+ /**
79
+ * Unified image picker with compression
80
+ *
81
+ * @example
82
+ * // Pick single image from gallery
83
+ * const result = await pickImage({ source: 'gallery' });
84
+ *
85
+ * // Capture from camera
86
+ * const result = await pickImage({ source: 'camera' });
87
+ *
88
+ * // Pick multiple images with custom compression
89
+ * const result = await pickImage({
90
+ * source: 'gallery',
91
+ * multiple: true,
92
+ * maxSize: 512,
93
+ * quality: 85,
94
+ * selectionLimit: 5,
95
+ * });
96
+ */
97
+ export const pickImage = async (
98
+ options: PickImageOptions,
99
+ ): Promise<PickImageResult> => {
100
+ const config = { ...defaultOptions, ...options };
101
+ const { source, multiple, maxSize, quality, selectionLimit } = config;
102
+
103
+ try {
104
+ // Prepare picker options
105
+ const pickerOptions: CameraOptions & ImageLibraryOptions = {
106
+ mediaType: 'photo',
107
+ includeBase64: false,
108
+ quality: 1, // Get full quality, we compress manually
109
+ selectionLimit: multiple ? selectionLimit : 1,
110
+ };
111
+
112
+ // Launch appropriate picker
113
+ const result =
114
+ source === 'camera'
115
+ ? await launchCamera(pickerOptions)
116
+ : await launchImageLibrary(pickerOptions);
117
+
118
+ // Handle cancellation
119
+ if (result.didCancel || !result.assets?.length) {
120
+ return { images: [], cancelled: true };
121
+ }
122
+
123
+ // Compress all selected images
124
+ const compressedImages: CompressedImageResult[] = [];
125
+
126
+ for (const asset of result.assets) {
127
+ if (!asset.uri) continue;
128
+
129
+ const compressed = await getCompressedImage({
130
+ path: asset.uri,
131
+ maxWidth: asset.width ?? maxSize,
132
+ maxHeight: asset.height ?? maxSize,
133
+ maxSize,
134
+ quality,
135
+ });
136
+
137
+ if (compressed?.path) {
138
+ compressedImages.push({
139
+ uri: compressed.path,
140
+ width: compressed.width,
141
+ height: compressed.height,
142
+ });
143
+ }
144
+ }
145
+
146
+ return {
147
+ images: compressedImages,
148
+ cancelled: false,
149
+ };
150
+ } catch (error) {
151
+ ErrorHandler(error);
152
+ return { images: [], cancelled: false };
153
+ }
154
+ };
155
+
156
+ // ─────────────────────────────────────────────────────────────────────────────
157
+ // Convenience Functions
158
+ // ─────────────────────────────────────────────────────────────────────────────
159
+
160
+ /**
161
+ * Pick single image from gallery
162
+ */
163
+ export const pickSingleImage = async (
164
+ options?: Partial<Omit<PickImageOptions, 'source' | 'multiple'>>,
165
+ ): Promise<CompressedImageResult | null> => {
166
+ const result = await pickImage({
167
+ source: 'gallery',
168
+ multiple: false,
169
+ ...options,
170
+ });
171
+ return result.images[0] ?? null;
172
+ };
173
+
174
+ /**
175
+ * Capture image from camera
176
+ */
177
+ export const captureImage = async (
178
+ options?: Partial<Omit<PickImageOptions, 'source' | 'multiple'>>,
179
+ ): Promise<CompressedImageResult | null> => {
180
+ const result = await pickImage({
181
+ source: 'camera',
182
+ multiple: false,
183
+ ...options,
184
+ });
185
+ return result.images[0] ?? null;
186
+ };
187
+
188
+ /**
189
+ * Pick multiple images from gallery
190
+ */
191
+ export const pickMultipleImages = async (
192
+ options?: Partial<Omit<PickImageOptions, 'source' | 'multiple'>>,
193
+ ): Promise<CompressedImageResult[] | null> => {
194
+ const result = await pickImage({
195
+ source: 'gallery',
196
+ multiple: true,
197
+ ...options,
198
+ });
199
+ return result.images.length ? result.images : null;
200
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Masks a phone number, keeping the first `visibleDigits` digits.
3
+ * Example: 9876543210 → +91 98XXXXXXX (default)
4
+ */
5
+ export const maskPhone = (
6
+ phone?: string | null,
7
+ visibleDigits = 2,
8
+ countryCode = '+91',
9
+ ): string => {
10
+ const digits = (phone ?? '').replace(/\D/g, '');
11
+ if (!digits) return '—';
12
+
13
+ const visible = digits.slice(0, visibleDigits);
14
+ const masked = 'X'.repeat(Math.max(digits.length - visibleDigits, 0));
15
+
16
+ return `${countryCode} ${visible}${masked}`;
17
+ };
@@ -0,0 +1,12 @@
1
+ import { AppDimensions } from '@/constants/device';
2
+ import { PixelRatio } from 'react-native';
3
+
4
+ const BASE_WIDTH = 375;
5
+ const MAX_SCALE = 1.2;
6
+
7
+ export const normalize = (size: number) => {
8
+ const scale = Math.min(AppDimensions.windowWidth / BASE_WIDTH, MAX_SCALE);
9
+ const newSize = size * scale;
10
+
11
+ return PixelRatio.roundToNearestPixel(newSize);
12
+ };
@@ -0,0 +1,11 @@
1
+ export const MSG = {
2
+ required: 'validation.required',
3
+ min: 'validation.minLength',
4
+ max: 'validation.maxLength',
5
+ email: 'validation.invalidEmail',
6
+ phone: 'validation.invalidPhone',
7
+ hasLetter: 'validation.hasLetter',
8
+ zip: 'validation.invalidZip',
9
+ strongPassword: 'validation.strongPassword',
10
+ selectOption: 'validation.selectOption',
11
+ };
@@ -0,0 +1,44 @@
1
+ import { REGEX } from '@/constants/regex';
2
+ import { MSG } from './messages';
3
+ import { LIMITS } from '@/constants/limits';
4
+
5
+ type Limits = { min?: number; max?: number };
6
+
7
+ const withLength = (limits?: Limits) => ({
8
+ ...(limits?.min && {
9
+ minLength: { value: limits.min, message: MSG.min },
10
+ }),
11
+ ...(limits?.max && {
12
+ maxLength: { value: limits.max, message: MSG.max },
13
+ }),
14
+ });
15
+
16
+ const base = (limits?: Limits) => ({
17
+ setValueAs: (v: string) => v?.trim(),
18
+ ...withLength(limits),
19
+ });
20
+
21
+ // NOTE:
22
+ // This file contains rule-based validation for reference and fallback use.
23
+ // If you are not using schema-based validation (Yup), you may use these
24
+ // rules directly with FormInput.
25
+
26
+ export const rules = {
27
+ name: (override?: Limits) => ({
28
+ required: MSG.required,
29
+ pattern: { value: REGEX.HAS_LETTER, message: MSG.hasLetter },
30
+ ...base({ ...LIMITS.NAME, ...override }),
31
+ }),
32
+
33
+ email: () => ({
34
+ required: MSG.required,
35
+ pattern: { value: REGEX.EMAIL, message: MSG.email },
36
+ ...base(),
37
+ }),
38
+
39
+ password: () => ({
40
+ required: MSG.required,
41
+ pattern: { value: REGEX.STRONG_PASSWORD, message: MSG.strongPassword },
42
+ setValueAs: (v: string) => v?.trim(),
43
+ }),
44
+ };
@@ -0,0 +1,53 @@
1
+ import * as yup from 'yup';
2
+ import { LIMITS } from '../../constants/limits';
3
+ import { REGEX } from '../../constants/regex';
4
+ import { MSG } from '../messages';
5
+
6
+ export const registerSchema = yup.object({
7
+ email: yup
8
+ .string()
9
+ .trim()
10
+ .required(MSG.required)
11
+ .email(MSG.email)
12
+ .min(LIMITS.EMAIL.min, MSG.min)
13
+ .max(LIMITS.EMAIL.max, MSG.max)
14
+ .matches(REGEX.EMAIL, MSG.email),
15
+
16
+ password: yup
17
+ .string()
18
+ .trim()
19
+ .required(MSG.required)
20
+ .min(LIMITS.PASSWORD.min, MSG.min)
21
+ .max(LIMITS.PASSWORD.max, MSG.max)
22
+ .matches(REGEX.STRONG_PASSWORD, MSG.strongPassword),
23
+ });
24
+
25
+ export const loginSchema = yup.object({
26
+ email: yup
27
+ .string()
28
+ .trim()
29
+ .required(MSG.required)
30
+ .email(MSG.email)
31
+ .min(LIMITS.EMAIL.min, MSG.min)
32
+ .max(LIMITS.EMAIL.max, MSG.max)
33
+ .matches(REGEX.EMAIL, MSG.email),
34
+
35
+ password: yup
36
+ .string()
37
+ .trim()
38
+ .required(MSG.required)
39
+ .min(LIMITS.PASSWORD.min, MSG.min)
40
+ .max(LIMITS.PASSWORD.max, MSG.max)
41
+ .matches(REGEX.STRONG_PASSWORD, MSG.strongPassword),
42
+ });
43
+
44
+ export const forgotPasswordSchema = yup.object({
45
+ email: yup
46
+ .string()
47
+ .trim()
48
+ .required(MSG.required)
49
+ .email(MSG.email)
50
+ .min(LIMITS.EMAIL.min, MSG.min)
51
+ .max(LIMITS.EMAIL.max, MSG.max)
52
+ .matches(REGEX.EMAIL, MSG.email),
53
+ });
@@ -0,0 +1,46 @@
1
+ import { LIMITS } from '@/constants/limits';
2
+ import { REGEX } from '@/constants/regex';
3
+ import * as yup from 'yup';
4
+ import { MSG } from '../messages';
5
+
6
+ export const profileSetupSchema = yup.object({
7
+ name: yup
8
+ .string()
9
+ .trim()
10
+ .required(MSG.required)
11
+ .min(LIMITS.NAME.min, MSG.min)
12
+ .max(LIMITS.NAME.max, MSG.max)
13
+ .matches(REGEX.HAS_LETTER, MSG.hasLetter),
14
+
15
+ phone: yup
16
+ .string()
17
+ .trim()
18
+ .required(MSG.required)
19
+ .test('phone-min', MSG.min, val => {
20
+ if (!val) return false;
21
+ const digits = val.replace(/\D/g, '');
22
+ return digits.length >= LIMITS.PHONE.min;
23
+ })
24
+ .test('phone-max', MSG.max, val => {
25
+ if (!val) return false;
26
+ const digits = val.replace(/\D/g, '');
27
+ return digits.length <= LIMITS.PHONE.max;
28
+ })
29
+ .matches(REGEX.PHONE, MSG.phone),
30
+
31
+ gender: yup.string().trim().required(MSG.selectOption),
32
+
33
+ address: yup
34
+ .string()
35
+ .trim()
36
+ .required(MSG.required)
37
+ .min(LIMITS.ADDRESS.min, MSG.min)
38
+ .max(LIMITS.ADDRESS.max, MSG.max)
39
+ .matches(REGEX.HAS_LETTER, MSG.hasLetter),
40
+
41
+ dob: yup.date().required(MSG.required),
42
+
43
+ currentTime: yup.date().required(MSG.required),
44
+
45
+ profilePicture: yup.string().nullable().notRequired(),
46
+ });
@@ -0,0 +1,13 @@
1
+ import { TFunction } from 'i18next';
2
+ import { FieldError } from 'react-hook-form';
3
+
4
+ type Params = {
5
+ min?: number;
6
+ max?: number;
7
+ field?: string;
8
+ };
9
+
10
+ export const tValError = (t: TFunction, err?: FieldError, params?: Params) => {
11
+ if (!err?.message) return undefined;
12
+ return t(err.message as any, { ...params });
13
+ };