@imksh/create 1.0.23 → 1.0.25

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 (163) hide show
  1. package/package.json +1 -1
  2. package/src/utils/copyTemplate.js +6 -0
  3. package/templates/mern-react-native/client/src/vite-env.d.ts +1 -0
  4. package/templates/mern-react-native/mobile/babel.config.js +1 -0
  5. package/templates/mern-react-native/mobile/drizzle.config.ts +8 -0
  6. package/templates/mern-react-native/mobile/expo-env.d.ts +3 -0
  7. package/templates/mern-react-native/mobile/package.json +6 -0
  8. package/templates/mern-react-native/mobile/src/app/(auth)/_layout.tsx +12 -0
  9. package/templates/mern-react-native/mobile/src/app/(auth)/change-password.tsx +112 -0
  10. package/templates/mern-react-native/mobile/src/app/(auth)/edit-profile.tsx +160 -0
  11. package/templates/mern-react-native/mobile/src/app/(auth)/forgot-password.tsx +191 -0
  12. package/templates/mern-react-native/mobile/src/app/{login.tsx → (auth)/login.tsx} +69 -21
  13. package/templates/mern-react-native/mobile/src/app/(tabs)/index.tsx +18 -17
  14. package/templates/mern-react-native/mobile/src/app/(tabs)/profile.tsx +23 -60
  15. package/templates/mern-react-native/mobile/src/app/_layout.tsx +22 -4
  16. package/templates/mern-react-native/mobile/src/components/common/AnimatedHeader.tsx +328 -0
  17. package/templates/mern-react-native/mobile/src/components/common/LocationLoader.tsx +591 -0
  18. package/templates/mern-react-native/mobile/src/components/common/ScreenHeader.tsx +41 -17
  19. package/templates/mern-react-native/mobile/src/components/common/TabHeader.tsx +58 -0
  20. package/templates/mern-react-native/mobile/src/components/common/Typography.tsx +49 -30
  21. package/templates/mern-react-native/mobile/src/components/common/Welcome.tsx +2 -2
  22. package/templates/mern-react-native/mobile/src/components/ui/BottomModal.tsx +31 -20
  23. package/templates/mern-react-native/mobile/src/components/ui/ClassicTabBar.tsx +4 -3
  24. package/templates/mern-react-native/mobile/src/components/ui/CustomTabBar.tsx +108 -71
  25. package/templates/mern-react-native/mobile/src/components/ui/Sidebar.tsx +81 -56
  26. package/templates/mern-react-native/mobile/src/components/ui/buttons/Button.tsx +106 -37
  27. package/templates/mern-react-native/mobile/src/components/ui/buttons/IconButton.tsx +4 -2
  28. package/templates/mern-react-native/mobile/src/components/ui/data-display/Accordion.tsx +3 -2
  29. package/templates/mern-react-native/mobile/src/components/ui/data-display/Avatar.tsx +4 -3
  30. package/templates/mern-react-native/mobile/src/components/ui/data-display/Badge.tsx +45 -17
  31. package/templates/mern-react-native/mobile/src/components/ui/data-display/ImageViewer.tsx +19 -9
  32. package/templates/mern-react-native/mobile/src/components/ui/feedback/CircularProgress.tsx +5 -4
  33. package/templates/mern-react-native/mobile/src/components/ui/feedback/ProgressBar.tsx +4 -3
  34. package/templates/mern-react-native/mobile/src/components/ui/feedback/Skeleton.tsx +3 -0
  35. package/templates/mern-react-native/mobile/src/components/ui/feedback/Spinner.tsx +11 -3
  36. package/templates/mern-react-native/mobile/src/components/ui/inputs/Checkbox.tsx +3 -2
  37. package/templates/mern-react-native/mobile/src/components/ui/inputs/DatePicker.tsx +186 -0
  38. package/templates/mern-react-native/mobile/src/components/ui/inputs/ImagePickerModal.tsx +129 -0
  39. package/templates/mern-react-native/mobile/src/components/ui/inputs/Input.tsx +54 -14
  40. package/templates/mern-react-native/mobile/src/components/ui/inputs/LocationPicker.tsx +211 -0
  41. package/templates/mern-react-native/mobile/src/components/ui/inputs/OTPInput.tsx +32 -9
  42. package/templates/mern-react-native/mobile/src/components/ui/inputs/PasswordInput.tsx +2 -1
  43. package/templates/mern-react-native/mobile/src/components/ui/inputs/Radio.tsx +3 -2
  44. package/templates/mern-react-native/mobile/src/components/ui/inputs/SearchBar.tsx +41 -4
  45. package/templates/mern-react-native/mobile/src/components/ui/inputs/Slider.tsx +4 -3
  46. package/templates/mern-react-native/mobile/src/components/ui/inputs/Switch.tsx +3 -2
  47. package/templates/mern-react-native/mobile/src/components/ui/inputs/TextArea.tsx +2 -1
  48. package/templates/mern-react-native/mobile/src/components/ui/inputs/WebLocationPicker.tsx +321 -0
  49. package/templates/mern-react-native/mobile/src/components/ui/layout/Grid.tsx +52 -10
  50. package/templates/mern-react-native/mobile/src/components/ui/layout/Section.tsx +7 -8
  51. package/templates/mern-react-native/mobile/src/components/ui/navigation/Pagination.tsx +4 -3
  52. package/templates/mern-react-native/mobile/src/components/ui/navigation/ProgressStepper.tsx +6 -5
  53. package/templates/mern-react-native/mobile/src/components/ui/navigation/Tabs.tsx +30 -4
  54. package/templates/mern-react-native/mobile/src/config/api.ts +12 -1
  55. package/templates/mern-react-native/mobile/src/config/sidebar.config.ts +72 -0
  56. package/templates/mern-react-native/mobile/src/config/tabNavigation.ts +37 -7
  57. package/templates/mern-react-native/mobile/src/db/index.ts +9 -0
  58. package/templates/mern-react-native/mobile/src/db/schema.ts +14 -0
  59. package/templates/mern-react-native/mobile/src/hooks/useDebounce.ts +17 -0
  60. package/templates/mern-react-native/mobile/src/services/socket.service.ts +36 -0
  61. package/templates/mern-react-native/mobile/src/store/useAuthStore.ts +122 -27
  62. package/templates/mern-react-native/mobile/src/store/useNotificationStore.ts +53 -0
  63. package/templates/mern-react-native/mobile/src/types/auth.type.ts +14 -0
  64. package/templates/mern-react-native/mobile/tsconfig.json +1 -0
  65. package/templates/mern-ts/client/src/vite-env.d.ts +1 -0
  66. package/templates/react-native/babel.config.js +1 -0
  67. package/templates/react-native/drizzle.config.ts +8 -0
  68. package/templates/react-native/expo-env.d.ts +3 -0
  69. package/templates/react-native/package.json +6 -0
  70. package/templates/react-native/src/app/(auth)/_layout.tsx +12 -0
  71. package/templates/react-native/src/app/(auth)/change-password.tsx +112 -0
  72. package/templates/react-native/src/app/(auth)/edit-profile.tsx +160 -0
  73. package/templates/react-native/src/app/(auth)/forgot-password.tsx +191 -0
  74. package/templates/react-native/src/app/{login.tsx → (auth)/login.tsx} +69 -21
  75. package/templates/react-native/src/app/(tabs)/index.tsx +18 -17
  76. package/templates/react-native/src/app/(tabs)/profile.tsx +23 -60
  77. package/templates/react-native/src/app/_layout.tsx +22 -4
  78. package/templates/react-native/src/components/common/AnimatedHeader.tsx +328 -0
  79. package/templates/react-native/src/components/common/LocationLoader.tsx +591 -0
  80. package/templates/react-native/src/components/common/ScreenHeader.tsx +41 -17
  81. package/templates/react-native/src/components/common/TabHeader.tsx +58 -0
  82. package/templates/react-native/src/components/common/Typography.tsx +49 -30
  83. package/templates/react-native/src/components/common/Welcome.tsx +2 -2
  84. package/templates/react-native/src/components/ui/BottomModal.tsx +31 -20
  85. package/templates/react-native/src/components/ui/ClassicTabBar.tsx +4 -3
  86. package/templates/react-native/src/components/ui/CustomTabBar.tsx +108 -71
  87. package/templates/react-native/src/components/ui/Sidebar.tsx +81 -56
  88. package/templates/react-native/src/components/ui/buttons/Button.tsx +106 -37
  89. package/templates/react-native/src/components/ui/buttons/IconButton.tsx +4 -2
  90. package/templates/react-native/src/components/ui/data-display/Accordion.tsx +3 -2
  91. package/templates/react-native/src/components/ui/data-display/Avatar.tsx +4 -3
  92. package/templates/react-native/src/components/ui/data-display/Badge.tsx +45 -17
  93. package/templates/react-native/src/components/ui/data-display/ImageViewer.tsx +19 -9
  94. package/templates/react-native/src/components/ui/feedback/CircularProgress.tsx +5 -4
  95. package/templates/react-native/src/components/ui/feedback/ProgressBar.tsx +4 -3
  96. package/templates/react-native/src/components/ui/feedback/Skeleton.tsx +3 -0
  97. package/templates/react-native/src/components/ui/feedback/Spinner.tsx +11 -3
  98. package/templates/react-native/src/components/ui/inputs/Checkbox.tsx +3 -2
  99. package/templates/react-native/src/components/ui/inputs/DatePicker.tsx +186 -0
  100. package/templates/react-native/src/components/ui/inputs/ImagePickerModal.tsx +129 -0
  101. package/templates/react-native/src/components/ui/inputs/Input.tsx +54 -14
  102. package/templates/react-native/src/components/ui/inputs/LocationPicker.tsx +211 -0
  103. package/templates/react-native/src/components/ui/inputs/OTPInput.tsx +32 -9
  104. package/templates/react-native/src/components/ui/inputs/PasswordInput.tsx +2 -1
  105. package/templates/react-native/src/components/ui/inputs/Radio.tsx +3 -2
  106. package/templates/react-native/src/components/ui/inputs/SearchBar.tsx +41 -4
  107. package/templates/react-native/src/components/ui/inputs/Slider.tsx +4 -3
  108. package/templates/react-native/src/components/ui/inputs/Switch.tsx +3 -2
  109. package/templates/react-native/src/components/ui/inputs/TextArea.tsx +2 -1
  110. package/templates/react-native/src/components/ui/inputs/WebLocationPicker.tsx +321 -0
  111. package/templates/react-native/src/components/ui/layout/Grid.tsx +52 -10
  112. package/templates/react-native/src/components/ui/layout/Section.tsx +7 -8
  113. package/templates/react-native/src/components/ui/navigation/Pagination.tsx +4 -3
  114. package/templates/react-native/src/components/ui/navigation/ProgressStepper.tsx +6 -5
  115. package/templates/react-native/src/components/ui/navigation/Tabs.tsx +30 -4
  116. package/templates/react-native/src/config/api.ts +12 -1
  117. package/templates/react-native/src/config/sidebar.config.ts +72 -0
  118. package/templates/react-native/src/config/tabNavigation.ts +37 -7
  119. package/templates/react-native/src/db/index.ts +9 -0
  120. package/templates/react-native/src/db/schema.ts +14 -0
  121. package/templates/react-native/src/hooks/useDebounce.ts +17 -0
  122. package/templates/react-native/src/services/socket.service.ts +36 -0
  123. package/templates/react-native/src/store/useAuthStore.ts +122 -27
  124. package/templates/react-native/src/store/useNotificationStore.ts +53 -0
  125. package/templates/react-native/src/types/auth.type.ts +14 -0
  126. package/templates/react-native/tsconfig.json +1 -0
  127. package/templates/react-ts/src/vite-env.d.ts +1 -0
  128. package/templates/mern-react-native/mobile/.env +0 -2
  129. package/templates/mern-react-native/mobile/.env.example +0 -2
  130. package/templates/mern-react-native/mobile/.vscode/extensions.json +0 -1
  131. package/templates/mern-react-native/mobile/.vscode/settings.json +0 -7
  132. package/templates/mern-react-native/mobile/README.md +0 -159
  133. package/templates/mern-react-native/mobile/docs/README.md +0 -17
  134. package/templates/mern-react-native/mobile/docs/components/buttons.md +0 -40
  135. package/templates/mern-react-native/mobile/docs/components/data-display.md +0 -44
  136. package/templates/mern-react-native/mobile/docs/components/feedback.md +0 -36
  137. package/templates/mern-react-native/mobile/docs/components/gestures.md +0 -60
  138. package/templates/mern-react-native/mobile/docs/components/inputs.md +0 -41
  139. package/templates/mern-react-native/mobile/docs/components/layout.md +0 -34
  140. package/templates/mern-react-native/mobile/docs/components/navigation.md +0 -31
  141. package/templates/mern-react-native/mobile/docs/features.md +0 -85
  142. package/templates/mern-react-native/mobile/docs/getting-started.md +0 -47
  143. package/templates/mern-react-native/mobile/src/components/common/HomeHeader.tsx +0 -43
  144. package/templates/mern-react-native/mobile/src/components/settings/SettingToggle.tsx +0 -26
  145. package/templates/mern-react-native/mobile/src/components/settings/SettingsOptions.tsx +0 -47
  146. package/templates/react-native/.env +0 -2
  147. package/templates/react-native/.env.example +0 -2
  148. package/templates/react-native/.vscode/extensions.json +0 -1
  149. package/templates/react-native/.vscode/settings.json +0 -7
  150. package/templates/react-native/README.md +0 -159
  151. package/templates/react-native/docs/README.md +0 -17
  152. package/templates/react-native/docs/components/buttons.md +0 -40
  153. package/templates/react-native/docs/components/data-display.md +0 -44
  154. package/templates/react-native/docs/components/feedback.md +0 -36
  155. package/templates/react-native/docs/components/gestures.md +0 -60
  156. package/templates/react-native/docs/components/inputs.md +0 -41
  157. package/templates/react-native/docs/components/layout.md +0 -34
  158. package/templates/react-native/docs/components/navigation.md +0 -31
  159. package/templates/react-native/docs/features.md +0 -85
  160. package/templates/react-native/docs/getting-started.md +0 -47
  161. package/templates/react-native/src/components/common/HomeHeader.tsx +0 -43
  162. package/templates/react-native/src/components/settings/SettingToggle.tsx +0 -26
  163. package/templates/react-native/src/components/settings/SettingsOptions.tsx +0 -47
@@ -13,19 +13,49 @@ export const tabNavigationConfig: TabConfig[] = [
13
13
  baseIcon: "home",
14
14
  },
15
15
  {
16
- name: "search",
17
- title: "Search",
18
- baseIcon: "search",
16
+ name: "explore",
17
+ title: "Explore",
18
+ baseIcon: "compass",
19
19
  },
20
20
  {
21
- name: "dashboard",
22
- title: "Dashboard",
23
- baseIcon: "cube",
21
+ name: "payments",
22
+ title: "Payments",
23
+ baseIcon: "wallet",
24
+ },
25
+ {
26
+ name: "notifications",
27
+ title: "Notifications",
28
+ baseIcon: "notifications",
24
29
  },
25
30
  {
26
31
  name: "profile",
27
32
  title: "Profile",
28
- baseIcon: "person",
33
+ baseIcon: "person-circle",
34
+ },
35
+ {
36
+ name: "properties",
37
+ title: "Properties",
38
+ baseIcon: "business",
39
+ },
40
+ {
41
+ name: "tenant",
42
+ title: "Tenant",
43
+ baseIcon: "people",
44
+ },
45
+ {
46
+ name: "users",
47
+ title: "Users",
48
+ baseIcon: "people",
49
+ },
50
+ {
51
+ name: "requests",
52
+ title: "Requests",
53
+ baseIcon: "clipboard",
54
+ },
55
+ {
56
+ name: "more",
57
+ title: "More",
58
+ baseIcon: "ellipsis-horizontal",
29
59
  },
30
60
  ];
31
61
 
@@ -0,0 +1,9 @@
1
+ import { drizzle } from 'drizzle-orm/expo-sqlite';
2
+ import * as SQLite from 'expo-sqlite';
3
+ import * as schema from './schema';
4
+
5
+ // Open the local database. If it doesn't exist, it will be created.
6
+ const expoDb = SQLite.openDatabaseSync('imksh.db');
7
+
8
+ // Pass the database instance to Drizzle
9
+ export const db = drizzle(expoDb, { schema });
@@ -0,0 +1,14 @@
1
+ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
2
+
3
+ export const users = sqliteTable('users', {
4
+ id: integer('id').primaryKey({ autoIncrement: true }),
5
+ email: text('email').notNull().unique(),
6
+ name: text('name'),
7
+ role: text('role').default('user'),
8
+ createdAt: text('created_at').notNull().default('CURRENT_TIMESTAMP'),
9
+ });
10
+
11
+ export const preferences = sqliteTable('preferences', {
12
+ key: text('key').primaryKey(),
13
+ value: text('value').notNull(),
14
+ });
@@ -0,0 +1,17 @@
1
+ import { useState, useEffect } from "react";
2
+
3
+ export function useDebounce<T>(value: T, delay: number): T {
4
+ const [debouncedValue, setDebouncedValue] = useState<T>(value);
5
+
6
+ useEffect(() => {
7
+ const handler = setTimeout(() => {
8
+ setDebouncedValue(value);
9
+ }, delay);
10
+
11
+ return () => {
12
+ clearTimeout(handler);
13
+ };
14
+ }, [value, delay]);
15
+
16
+ return debouncedValue;
17
+ }
@@ -0,0 +1,36 @@
1
+ import { io, Socket } from 'socket.io-client';
2
+ import { get } from '../utils/storage';
3
+ import Constants from 'expo-constants';
4
+
5
+ let socket: Socket | null = null;
6
+
7
+ // Set your backend socket URL
8
+ const SOCKET_URL = Constants.expoConfig?.extra?.apiUrl || 'http://localhost:3000';
9
+
10
+ export const initializeSocket = async () => {
11
+ if (socket) return;
12
+ const token = await get('token');
13
+ if (!token) return;
14
+
15
+ socket = io(SOCKET_URL, {
16
+ auth: { token },
17
+ transports: ['websocket'],
18
+ });
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
+ };
28
+
29
+ export const getSocket = () => socket;
30
+
31
+ export const disconnectSocket = () => {
32
+ if (socket) {
33
+ socket.disconnect();
34
+ socket = null;
35
+ }
36
+ };
@@ -1,20 +1,15 @@
1
1
  import { create } from "zustand";
2
2
  import { authService } from "../services/auth.service";
3
- import { save, remove } from "../utils/storage";
4
-
5
- export interface User {
6
- _id: string;
7
- name: string;
8
- email: string;
9
- phone?: string;
10
- role: string;
11
- avatar?: {
12
- url?: string;
13
- publicId?: string;
14
- };
15
- createdAt?: string;
16
- updatedAt?: string;
17
- }
3
+ import { save, remove, get } from "../utils/storage";
4
+ import { User } from "../types/auth.type";
5
+ import { initializeSocket, disconnectSocket } from "../services/socket.service";
6
+ import { useNotificationStore } from "./useNotificationStore";
7
+
8
+ // ── Module-level prefetch ──────────────────────────────────────────────────
9
+ // Kick off the AsyncStorage read the instant this module is imported
10
+ // (before any React component mounts). By the time loadCachedUser() is
11
+ // called from a useEffect, the promise is already resolved or very close.
12
+ const _prefetchedUser: Promise<User | null> = get("user");
18
13
 
19
14
  export interface AuthState {
20
15
  user: User | null;
@@ -34,6 +29,13 @@ export interface AuthState {
34
29
  getMe: () => Promise<void>;
35
30
  updateProfile: (updates: Partial<User> | FormData) => Promise<boolean>;
36
31
  logout: () => Promise<void>;
32
+ genOtp: (email: string) => Promise<boolean>;
33
+ verifyOtp: (email: string, otp: string) => Promise<boolean>;
34
+ resetPassword: (newPassword: string) => Promise<boolean>;
35
+ changePassword: (oldPassword: string, newPassword: string) => Promise<boolean>;
36
+ setUser: (user: User) => void;
37
+ clearError: () => void;
38
+ loadCachedUser: () => Promise<void>;
37
39
  }
38
40
 
39
41
  export const useAuthStore = create<AuthState>((set) => ({
@@ -42,6 +44,27 @@ export const useAuthStore = create<AuthState>((set) => ({
42
44
  isCheckingAuth: true,
43
45
  error: null,
44
46
 
47
+ setUser: (user: User) => set({ user }),
48
+
49
+ clearError: () => set({ error: null }),
50
+
51
+ loadCachedUser: async () => {
52
+ // Await the already-in-flight prefetch — typically resolves in <1ms here
53
+ const cached = await _prefetchedUser;
54
+ if (cached) {
55
+ set({ user: cached, isCheckingAuth: false });
56
+ // Re-establish socket + push on app resume with a cached session
57
+ initializeSocket();
58
+ const notifStore = useNotificationStore.getState();
59
+ notifStore.listenForSocketEvents();
60
+ notifStore.registerForPushNotificationsAsync();
61
+ notifStore.fetchNotifications();
62
+ } else {
63
+ // No cache — mark auth check done so routing doesn't hang
64
+ set({ isCheckingAuth: false });
65
+ }
66
+ },
67
+
45
68
  sendOtp: async (email: string) => {
46
69
  set({ loading: true, error: null });
47
70
  try {
@@ -74,13 +97,21 @@ export const useAuthStore = create<AuthState>((set) => ({
74
97
  otp,
75
98
  role,
76
99
  );
77
- if (data.token) {
78
- save("token", data.token);
100
+ if (data.data?.token) {
101
+ save("token", data.data.token);
79
102
  }
103
+ const user = data.data?.user;
104
+ if (user) save("user", user);
80
105
  set({
81
- user: data.data,
106
+ user,
82
107
  loading: false,
83
108
  });
109
+ // Initialise socket + push after signup
110
+ initializeSocket();
111
+ const notifStore = useNotificationStore.getState();
112
+ notifStore.listenForSocketEvents();
113
+ notifStore.registerForPushNotificationsAsync();
114
+ notifStore.fetchNotifications();
84
115
  return true;
85
116
  } catch (err: any) {
86
117
  set({
@@ -97,13 +128,21 @@ export const useAuthStore = create<AuthState>((set) => ({
97
128
  set({ loading: true, error: null });
98
129
  try {
99
130
  const { data } = await authService.login(email, password);
100
- if (data.token) {
101
- save("token", data.token);
131
+ if (data.data?.token) {
132
+ save("token", data.data.token);
102
133
  }
134
+ const user = data.data?.user;
135
+ if (user) save("user", user);
103
136
  set({
104
- user: data.data,
137
+ user,
105
138
  loading: false,
106
139
  });
140
+ // Initialise socket + push after login
141
+ initializeSocket();
142
+ const notifStore = useNotificationStore.getState();
143
+ notifStore.listenForSocketEvents();
144
+ notifStore.registerForPushNotificationsAsync();
145
+ notifStore.fetchNotifications();
107
146
  return true;
108
147
  } catch (err: any) {
109
148
  set({
@@ -123,7 +162,7 @@ export const useAuthStore = create<AuthState>((set) => ({
123
162
  save("token", "demo-token-123");
124
163
  set({
125
164
  user: {
126
- _id: "demo_user_123",
165
+ id: "demo_user_123",
127
166
  name: "Demo User",
128
167
  email: "demo@imksh.com",
129
168
  role: "admin",
@@ -133,12 +172,13 @@ export const useAuthStore = create<AuthState>((set) => ({
133
172
  },
134
173
 
135
174
  getMe: async () => {
136
- set({ isCheckingAuth: true });
137
175
  try {
138
176
  const { data } = await authService.getMe();
139
- set({ user: data.data, isCheckingAuth: false });
140
- } catch {
141
- set({ isCheckingAuth: false });
177
+ const user = data.data;
178
+ // Always update cache with fresh server data
179
+ if (user) save("user", user);
180
+ set({ user, isCheckingAuth: false });
181
+ // Network failed — cached user (already loaded) stays in place
142
182
  } finally {
143
183
  set({ isCheckingAuth: false });
144
184
  }
@@ -167,7 +207,9 @@ export const useAuthStore = create<AuthState>((set) => ({
167
207
  } catch {
168
208
  // ignore errors — cookie may already be expired
169
209
  } finally {
170
- remove("token");
210
+ await remove("token");
211
+ await remove("user"); // clear cached user on logout
212
+ disconnectSocket();
171
213
  set({ loading: false });
172
214
  }
173
215
  set({
@@ -175,4 +217,57 @@ export const useAuthStore = create<AuthState>((set) => ({
175
217
  error: null,
176
218
  });
177
219
  },
220
+
221
+ genOtp: async (email: string) => {
222
+ set({ loading: true, error: null });
223
+ try {
224
+ await authService.genOtp(email);
225
+ return true;
226
+ } catch (err: any) {
227
+ set({ error: err.response?.data?.message || err.message, loading: false });
228
+ return false;
229
+ } finally {
230
+ set({ loading: false });
231
+ }
232
+ },
233
+
234
+ verifyOtp: async (email: string, otp: string) => {
235
+ set({ loading: true, error: null });
236
+ try {
237
+ const { data } = await authService.verifyOtp(email, otp);
238
+ if (data.token) save("token", data.token);
239
+ return true;
240
+ } catch (err: any) {
241
+ set({ error: err.response?.data?.message || err.message, loading: false });
242
+ return false;
243
+ } finally {
244
+ set({ loading: false });
245
+ }
246
+ },
247
+
248
+ resetPassword: async (newPassword: string) => {
249
+ set({ loading: true, error: null });
250
+ try {
251
+ await authService.resetPassword(newPassword);
252
+ return true;
253
+ } catch (err: any) {
254
+ set({ error: err.response?.data?.message || err.message, loading: false });
255
+ return false;
256
+ } finally {
257
+ set({ loading: false });
258
+ }
259
+ },
260
+
261
+ changePassword: async (oldPassword: string, newPassword: string) => {
262
+ set({ loading: true, error: null });
263
+ try {
264
+ await authService.changePassword(oldPassword, newPassword);
265
+ return true;
266
+ } catch (err: any) {
267
+ set({ error: err.response?.data?.message || err.message, loading: false });
268
+ return false;
269
+ } finally {
270
+ set({ loading: false });
271
+ }
272
+ },
178
273
  }));
@@ -0,0 +1,53 @@
1
+ import { create } from 'zustand';
2
+ import { getSocket } from '../services/socket.service';
3
+ import * as Notifications from 'expo-notifications';
4
+
5
+ export interface NotificationState {
6
+ notifications: any[];
7
+ pushToken: string | null;
8
+ listenForSocketEvents: () => void;
9
+ registerForPushNotificationsAsync: () => Promise<void>;
10
+ fetchNotifications: () => Promise<void>;
11
+ }
12
+
13
+ export const useNotificationStore = create<NotificationState>((set, get) => ({
14
+ notifications: [],
15
+ pushToken: null,
16
+
17
+ listenForSocketEvents: () => {
18
+ const socket = getSocket();
19
+ if (!socket) return;
20
+
21
+ socket.on('new_notification', (data) => {
22
+ set((state) => ({ notifications: [data, ...state.notifications] }));
23
+ });
24
+ },
25
+
26
+ registerForPushNotificationsAsync: async () => {
27
+ try {
28
+ const { status: existingStatus } = await Notifications.getPermissionsAsync();
29
+ let finalStatus = existingStatus;
30
+ if (existingStatus !== 'granted') {
31
+ const { status } = await Notifications.requestPermissionsAsync();
32
+ finalStatus = status;
33
+ }
34
+ if (finalStatus !== 'granted') {
35
+ console.log('Failed to get push token for push notification!');
36
+ return;
37
+ }
38
+ // Note: projectId is required for Expo Go and EAS.
39
+ const token = (await Notifications.getExpoPushTokenAsync()).data;
40
+ set({ pushToken: token });
41
+
42
+ // Here you would typically send the pushToken to your backend
43
+ } catch (e) {
44
+ console.warn("Failed to register for push notifications", e);
45
+ }
46
+ },
47
+
48
+ fetchNotifications: async () => {
49
+ // API call to fetch notifications could go here
50
+ // e.g., const data = await api.get('/notifications');
51
+ set({ notifications: [] });
52
+ }
53
+ }));
@@ -0,0 +1,14 @@
1
+ export interface User {
2
+ id: string; // Updated from _id for Prisma consistency
3
+ _id?: string; // Fallback for mongo legacy if any
4
+ name: string;
5
+ email: string;
6
+ phone?: string;
7
+ role: string;
8
+ avatar?: {
9
+ url?: string;
10
+ publicId?: string;
11
+ };
12
+ createdAt?: string;
13
+ updatedAt?: string;
14
+ }
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "extends": "expo/tsconfig.base",
3
3
  "compilerOptions": {
4
+ "jsx": "react-jsx",
4
5
  "strict": true,
5
6
  "noImplicitAny": false,
6
7
  "paths": {
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -1,2 +0,0 @@
1
- EXPO_PUBLIC_API_URL=http://10.91.141.90:5001/api
2
- EXPO_PUBLIC_APP_NAME=MyApp
@@ -1,2 +0,0 @@
1
- EXPO_PUBLIC_API_URL=your_api
2
- EXPO_PUBLIC_APP_NAME=MyApp
@@ -1 +0,0 @@
1
- { "recommendations": ["expo.vscode-expo-tools"] }
@@ -1,7 +0,0 @@
1
- {
2
- "editor.codeActionsOnSave": {
3
- "source.fixAll": "explicit",
4
- "source.organizeImports": "explicit",
5
- "source.sortMembers": "explicit"
6
- }
7
- }
@@ -1,159 +0,0 @@
1
- # 📱 Mobile App (React Native + Expo)
2
-
3
- This is the mobile application of the starter template, built entirely with **React Native** and **Expo**. It is designed to be a highly scalable boilerplate featuring a modern tech stack, a unified permissions system, and a robust custom UI component library.
4
-
5
- ## ✨ Key Features
6
-
7
- - **Expo Router**: File-based routing that makes navigation predictable and deeply linkable.
8
- - **NativeWind (Tailwind CSS)**: Styling is powered by Tailwind CSS classes, complete with dynamic Light/Dark mode support.
9
- - **Custom UI Library**: Beautiful, highly-reusable components built from scratch (e.g., `Tabs`, `Button`, `Image`, `ScrollContainer`, `Input`) designed to work perfectly across both themes.
10
- - **Zustand State Management**: Lightweight and fast global state management, used to handle the user's auth session securely.
11
- - **Unified Permission Service**: A centralized handler for iOS and Android hardware permissions using `react-native-permissions`.
12
- - **Push Notification Pipeline**: Built-in support for generating Expo Push Tokens on physical devices and syncing them to your backend.
13
-
14
- ## 🧩 Included UI Components
15
-
16
- The starter kit comes with a massive, completely customizable, and gesture-driven UI library built from scratch out of atomic elements.
17
-
18
- - **Layout**: `Container`, `Section`, `ScrollContainer`, `HStack`, `VStack`
19
- - **Buttons**: `Button` (with variants, sizes, and soft props), `IconButton`, `FloatingActionButton`
20
- - **Data Display**: `Avatar`, `Badge`, `Accordion`, `SwipeableRow`, `Card`
21
- - **Inputs**: `Input`, `Checkbox`, `Radio`, `Switch`, `Slider`, `SearchBar`
22
- - **Feedback & Loading**: `Spinner`, `ProgressBar`, `CircularProgress`, `Skeleton`, `GestureWrapper`
23
- - **Navigation**: `Tabs` (Underline & Button variants)
24
- - **Overlay & Modals**: `BottomModal` (Gesture-driven bottom sheet)
25
-
26
- ## 🔌 Included Services
27
-
28
- Pre-configured service wrappers allow you to focus on logic rather than boilerplate:
29
-
30
- - **`auth.service.ts`**: Prebuilt Axios integrations for `sendOtp`, `signup`, `login`, `updateMe`, `logout`, and `savePushToken`.
31
- - **`permission.service.ts`**: A unified permissions handler exposing `requirePermission`, `openSettingsForApp`, and `registerForPushNotificationsAsync`.
32
-
33
- ---
34
-
35
- ## 🛠 Tech Stack
36
-
37
- - **Framework**: Expo / React Native
38
- - **Styling**: NativeWind (Tailwind v3) & `react-native-reanimated`
39
- - **Routing**: `expo-router`
40
- - **State**: `zustand`
41
- - **Storage**: `@react-native-async-storage/async-storage`
42
- - **API**: `axios`
43
-
44
- ---
45
-
46
- ## 🚀 Getting Started
47
-
48
- ### Prerequisites
49
-
50
- Make sure you have the following installed on your machine:
51
-
52
- - Node.js (v18+)
53
- - [EAS CLI](https://docs.expo.dev/build/setup/) (`npm install -g eas-cli`)
54
- - iOS Simulator (macOS only) or Android Emulator
55
-
56
- ### Installation
57
-
58
- 1. Navigate to the `mobile` directory:
59
- ```bash
60
- cd mobile
61
- ```
62
- 2. Install dependencies:
63
- ```bash
64
- npm install
65
- ```
66
- 3. Start the development server:
67
- ```bash
68
- npm run dev
69
- ```
70
- 4. Press `i` to open the app on an iOS Simulator, or `a` to open it on an Android Emulator.
71
-
72
- ---
73
-
74
- ## 🏗 Project Structure
75
-
76
- ```bash
77
- src/
78
- ├── app/ # Expo Router screens (e.g., _layout.tsx, login.tsx, (tabs)/)
79
- ├── components/ # Reusable UI components
80
- │ ├── common/ # Generic components (Loading, etc.)
81
- │ └── ui/ # Custom UI library (Button, Tabs, Image, Input)
82
- ├── constants/ # Theme tokens (Colors, Typography)
83
- ├── services/ # API and business logic wrappers
84
- │ ├── auth.service.ts # Axios API calls for authentication
85
- │ └── permission.service.ts # Unified hardware permission logic
86
- ├── store/ # Zustand global stores (useAuthStore.ts)
87
- └── utils/ # Helper functions (storage.ts, api.ts)
88
- ```
89
-
90
- ---
91
-
92
- ## 📦 Build Instructions
93
-
94
- This template is configured for both cloud-based builds (via EAS) and fast local compilations using standard Gradle scripts.
95
-
96
- ### Cloud Builds (Expo Application Services)
97
-
98
- If you don't have Android Studio or Xcode configured, you can build directly in the cloud:
99
-
100
- ```bash
101
- # Build an Android APK (Uses the "preview" profile in eas.json)
102
- npm run build:eas:apk
103
-
104
- # Build an Android App Bundle (.aab) for the Play Store (Uses "production" profile)
105
- npm run build:eas:aab
106
-
107
- # Build an iOS app
108
- npm run build:ios
109
- ```
110
-
111
- ### Local Builds (Requires Android Studio/SDK)
112
-
113
- If you have your local SDK set up, you can compile extremely fast on your own machine:
114
-
115
- ```bash
116
- # Compile a local .apk
117
- npm run build:apk
118
-
119
- # Compile a local .aab
120
- npm run build:aab
121
- ```
122
-
123
- _(Note: Rebuilding natively is required whenever you add new npm packages containing native code, or modify the `app.json` plugins!)_
124
-
125
- ---
126
-
127
- ## 🔒 Handling Permissions
128
-
129
- Rather than installing different disjointed `expo-*` libraries every time you need hardware access, this template centralizes all permission requests using the `PermissionService`.
130
-
131
- To check or request access:
132
-
133
- ```typescript
134
- import { permissionService } from "@/services/permission.service";
135
-
136
- // Calling requirePermission() will automatically prompt the user if they haven't been asked yet
137
- const hasCameraAccess = await permissionService.requirePermission("camera");
138
-
139
- if (!hasCameraAccess) {
140
- // Deep link the user to the OS settings if they permanently blocked the permission
141
- permissionService.openSettingsForApp();
142
- }
143
- ```
144
-
145
- ## 🔔 Push Notifications
146
-
147
- To request push notifications, just call the utility function. It will automatically detect if it is running on a physical device, set up the required Android notification channels, fetch the token from Apple/Google, and return it:
148
-
149
- ```typescript
150
- import { permissionService } from "@/services/permission.service";
151
- import { authService } from "@/services/auth.service";
152
-
153
- const token = await permissionService.registerForPushNotificationsAsync();
154
-
155
- // Send it to the backend!
156
- if (token) {
157
- await authService.savePushToken(token);
158
- }
159
- ```
@@ -1,17 +0,0 @@
1
- # Mobile App Documentation
2
-
3
- Welcome to the documentation for the Expo React Native Mobile App. This folder contains detailed information on how to use the app, navigate its architecture, and utilize the custom UI component library.
4
-
5
- ## Table of Contents
6
-
7
- ### Core
8
- - [Getting Started](./getting-started.md)
9
-
10
- ### UI Component Library
11
- - [Buttons](./components/buttons.md)
12
- - [Data Display](./components/data-display.md) (e.g., SwipeableRow, Accordion, Avatar)
13
- - [Feedback](./components/feedback.md) (e.g., Progress, Spinners)
14
- - [Gestures & Touch](./components/gestures.md) (e.g., GestureWrapper)
15
- - [Inputs](./components/inputs.md) (e.g., TextInput, OTPInput, Switches)
16
- - [Layout](./components/layout.md) (e.g., Container, Divider, Grid)
17
- - [Navigation](./components/navigation.md) (e.g., Tabs, Pagination, ProgressStepper)
@@ -1,40 +0,0 @@
1
- # Button Components
2
-
3
- Buttons trigger actions. The custom UI library provides several highly polished button variations.
4
-
5
- ## `Button`
6
- The primary button component for the application, supporting different variants (solid, outline, ghost), colors, and loading states.
7
-
8
- ### Usage Example
9
- ```tsx
10
- import { Button } from '@/components/ui/buttons/Button';
11
-
12
- export default function Actions() {
13
- return (
14
- <>
15
- <Button
16
- title="Submit"
17
- onPress={() => console.log('Primary pressed')}
18
- />
19
- <Button
20
- title="Cancel"
21
- variant="outline"
22
- onPress={() => console.log('Outline pressed')}
23
- />
24
- <Button
25
- title="Saving..."
26
- loading={true}
27
- />
28
- </>
29
- );
30
- }
31
- ```
32
-
33
- ## `IconButton`
34
- A button that only contains an icon without text. Perfect for header buttons or tight layouts.
35
-
36
- ## `FloatingActionButton` (FAB)
37
- A persistent button that hovers over the screen, typically placed in the bottom right corner. Ideal for primary creation actions like "Compose Message".
38
-
39
- ## `ButtonGroup`
40
- A wrapper to seamlessly group multiple related buttons together horizontally.