@imksh/create 1.0.25 → 1.0.26

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 (267) hide show
  1. package/package.json +1 -1
  2. package/src/commands/create.js +2 -1
  3. package/src/index.js +1 -1
  4. package/src/utils/askTemplate.js +1 -0
  5. package/templates/mern-react-native/mobile/_gitignore +43 -0
  6. package/templates/mern-react-native/mobile/package.json +2 -1
  7. package/templates/mern-react-native/mobile/src/app/(auth)/edit-profile.tsx +11 -15
  8. package/templates/mern-react-native/mobile/src/app/(tabs)/dashboard.tsx +2 -2
  9. package/templates/mern-react-native/mobile/src/app/(tabs)/search.tsx +2 -2
  10. package/templates/mern-react-native/mobile/src/config/sidebar.config.ts +0 -60
  11. package/templates/mern-react-native/mobile/src/config/tabNavigation.ts +6 -36
  12. package/templates/mern-react-native/mobile/src/services/notification.service.ts +22 -0
  13. package/templates/mern-react-native/mobile/src/store/useNotificationStore.ts +158 -33
  14. package/templates/next/next.config.mjs +1 -0
  15. package/templates/next/package.json +10 -9
  16. package/templates/next/postcss.config.js +5 -0
  17. package/templates/next/src/app/layout.js +1 -0
  18. package/templates/next/src/app/page.js +1 -1
  19. package/templates/next/src/components/layout/ProtectedRoute.jsx +3 -3
  20. package/templates/next/src/index.css +1 -0
  21. package/templates/next/src/store/useUiStore.js +13 -7
  22. package/templates/next-ts/.env.example +0 -0
  23. package/templates/next-ts/AGENTS.md +37 -0
  24. package/templates/next-ts/CLAUDE.md +1 -0
  25. package/templates/next-ts/_gitignore +0 -24
  26. package/templates/next-ts/next-env.d.ts +3 -1
  27. package/templates/next-ts/next.config.mjs +1 -0
  28. package/templates/next-ts/package.json +13 -12
  29. package/templates/next-ts/postcss.config.js +5 -0
  30. package/templates/next-ts/public/logo.png +0 -0
  31. package/templates/next-ts/src/app/error.tsx +41 -0
  32. package/templates/next-ts/src/app/layout.tsx +2 -1
  33. package/templates/next-ts/src/app/not-found.tsx +5 -0
  34. package/templates/next-ts/src/app/page.tsx +1 -1
  35. package/templates/next-ts/src/components/layout/ProtectedRoute.tsx +3 -3
  36. package/templates/next-ts/src/components/ui/Button.tsx +63 -0
  37. package/templates/next-ts/src/components/ui/Input.tsx +79 -0
  38. package/templates/next-ts/src/index.css +1 -0
  39. package/templates/next-ts/src/store/useUiStore.ts +13 -7
  40. package/templates/next-ts/tsconfig.json +19 -5
  41. package/templates/production-fullstack/client/index.html +14 -0
  42. package/templates/production-fullstack/client/package.json +47 -0
  43. package/templates/production-fullstack/client/public/_redirects +1 -0
  44. package/templates/production-fullstack/client/public/favicon.ico +0 -0
  45. package/templates/production-fullstack/client/public/favicon.svg +1 -0
  46. package/templates/production-fullstack/client/public/logo.png +0 -0
  47. package/templates/production-fullstack/client/public/manifest.json +24 -0
  48. package/templates/production-fullstack/client/public/sw.js +56 -0
  49. package/templates/production-fullstack/client/src/App.tsx +68 -0
  50. package/templates/production-fullstack/client/src/assets/animations/loading.json +1 -0
  51. package/templates/production-fullstack/client/src/assets/data/navigation.ts +29 -0
  52. package/templates/production-fullstack/client/src/assets/images/logo.png +0 -0
  53. package/templates/production-fullstack/client/src/components/layout/ProtectedRoute.tsx +23 -0
  54. package/templates/production-fullstack/client/src/components/layout/PublicLayout.tsx +13 -0
  55. package/templates/production-fullstack/client/src/components/layout/SidebarLayout.tsx +37 -0
  56. package/templates/production-fullstack/client/src/components/shared/Breadcrumbs.tsx +27 -0
  57. package/templates/production-fullstack/client/src/components/shared/LottieIcon.tsx +36 -0
  58. package/templates/production-fullstack/client/src/components/shared/NotFound.tsx +78 -0
  59. package/templates/production-fullstack/client/src/components/shared/PwaInstallPrompt.tsx +72 -0
  60. package/templates/production-fullstack/client/src/components/shared/SEO.tsx +67 -0
  61. package/templates/production-fullstack/client/src/components/shared/Scroll.tsx +25 -0
  62. package/templates/production-fullstack/client/src/components/shared/Unauthorized.tsx +76 -0
  63. package/templates/production-fullstack/client/src/components/shared/skeletons/common/CardSkeleton.tsx +23 -0
  64. package/templates/production-fullstack/client/src/components/shared/skeletons/common/DashboardSkeleton.tsx +19 -0
  65. package/templates/production-fullstack/client/src/components/shared/skeletons/common/ListSkeleton.tsx +24 -0
  66. package/templates/production-fullstack/client/src/components/shared/skeletons/common/PageSkeleton.tsx +26 -0
  67. package/templates/production-fullstack/client/src/components/shared/skeletons/common/ProfileSkeleton.tsx +23 -0
  68. package/templates/production-fullstack/client/src/components/shared/skeletons/common/Skeleton.tsx +18 -0
  69. package/templates/production-fullstack/client/src/components/shared/skeletons/common/TableSkeleton.tsx +33 -0
  70. package/templates/production-fullstack/client/src/components/ui/ConfirmModal.tsx +152 -0
  71. package/templates/production-fullstack/client/src/components/ui/Header.tsx +230 -0
  72. package/templates/production-fullstack/client/src/components/ui/Loading.tsx +19 -0
  73. package/templates/production-fullstack/client/src/components/ui/Modal.tsx +106 -0
  74. package/templates/production-fullstack/client/src/components/ui/SidebarHeader.tsx +34 -0
  75. package/templates/production-fullstack/client/src/components/ui/Table.tsx +118 -0
  76. package/templates/production-fullstack/client/src/components/ui/ToggleSwitch.tsx +38 -0
  77. package/templates/production-fullstack/client/src/components/ui/sidebar.tsx +152 -0
  78. package/templates/production-fullstack/client/src/config/index.ts +44 -0
  79. package/templates/production-fullstack/client/src/constants/navigation.ts +14 -0
  80. package/templates/production-fullstack/client/src/features/auth/api/authService.ts +19 -0
  81. package/templates/production-fullstack/client/src/features/auth/pages/Login.tsx +233 -0
  82. package/templates/production-fullstack/client/src/features/auth/store/useAuthStore.ts +143 -0
  83. package/templates/production-fullstack/client/src/features/core/pages/Home.tsx +150 -0
  84. package/templates/production-fullstack/client/src/features/core/pages/Profile.tsx +9 -0
  85. package/templates/production-fullstack/client/src/hooks/useLenis.ts +28 -0
  86. package/templates/production-fullstack/client/src/hooks/usePwaInstall.ts +45 -0
  87. package/templates/production-fullstack/client/src/hooks/useWindowSize.ts +25 -0
  88. package/templates/production-fullstack/client/src/index.css +158 -0
  89. package/templates/production-fullstack/client/src/lib/axios.ts +22 -0
  90. package/templates/production-fullstack/client/src/lib/dayjs.ts +12 -0
  91. package/templates/production-fullstack/client/src/lib/query-client.ts +11 -0
  92. package/templates/production-fullstack/client/src/lib/socket.ts +6 -0
  93. package/templates/production-fullstack/client/src/main.tsx +28 -0
  94. package/templates/production-fullstack/client/src/store/useUiStore.ts +74 -0
  95. package/templates/production-fullstack/client/src/utils/formatDate.ts +19 -0
  96. package/templates/production-fullstack/client/src/utils/getInitials.ts +10 -0
  97. package/templates/production-fullstack/client/src/vite-env.d.ts +1 -0
  98. package/templates/production-fullstack/client/tsconfig.json +25 -0
  99. package/templates/production-fullstack/client/tsconfig.node.json +10 -0
  100. package/templates/production-fullstack/client/vite.config.ts +8 -0
  101. package/templates/production-fullstack/mobile/LICENSE +21 -0
  102. package/templates/production-fullstack/mobile/app.json +84 -0
  103. package/templates/production-fullstack/mobile/assets/animations/celebrate.json +1 -0
  104. package/templates/production-fullstack/mobile/assets/animations/infinity.json +1 -0
  105. package/templates/production-fullstack/mobile/assets/animations/loading.json +1 -0
  106. package/templates/production-fullstack/mobile/assets/fonts/Inter_Bold.ttf +0 -0
  107. package/templates/production-fullstack/mobile/assets/fonts/Inter_ExtraBold.ttf +0 -0
  108. package/templates/production-fullstack/mobile/assets/fonts/Inter_Medium.ttf +0 -0
  109. package/templates/production-fullstack/mobile/assets/fonts/Inter_Regular.ttf +0 -0
  110. package/templates/production-fullstack/mobile/assets/fonts/Inter_SemiBold.ttf +0 -0
  111. package/templates/production-fullstack/mobile/assets/images/logo/logo.png +0 -0
  112. package/templates/production-fullstack/mobile/assets/images/logo/logo.svg +1 -0
  113. package/templates/production-fullstack/mobile/assets/images/logo/logoWithBg.png +0 -0
  114. package/templates/production-fullstack/mobile/babel.config.js +13 -0
  115. package/templates/production-fullstack/mobile/drizzle.config.ts +8 -0
  116. package/templates/production-fullstack/mobile/eas.json +24 -0
  117. package/templates/production-fullstack/mobile/eslint.config.js +10 -0
  118. package/templates/production-fullstack/mobile/metro.config.js +6 -0
  119. package/templates/production-fullstack/mobile/nativewind-env.d.ts +3 -0
  120. package/templates/production-fullstack/mobile/package.json +89 -0
  121. package/templates/production-fullstack/mobile/src/app/(auth)/_layout.tsx +12 -0
  122. package/templates/production-fullstack/mobile/src/app/(auth)/change-password.tsx +112 -0
  123. package/templates/production-fullstack/mobile/src/app/(auth)/edit-profile.tsx +160 -0
  124. package/templates/production-fullstack/mobile/src/app/(auth)/forgot-password.tsx +191 -0
  125. package/templates/production-fullstack/mobile/src/app/(auth)/login.tsx +253 -0
  126. package/templates/production-fullstack/mobile/src/app/_layout.tsx +102 -0
  127. package/templates/production-fullstack/mobile/src/app/global.css +82 -0
  128. package/templates/production-fullstack/mobile/src/components/common/AnimatedHeader.tsx +328 -0
  129. package/templates/production-fullstack/mobile/src/components/common/ConfirmationToast.tsx +67 -0
  130. package/templates/production-fullstack/mobile/src/components/common/Loading.tsx +26 -0
  131. package/templates/production-fullstack/mobile/src/components/common/LocationLoader.tsx +591 -0
  132. package/templates/production-fullstack/mobile/src/components/common/ScreenHeader.tsx +68 -0
  133. package/templates/production-fullstack/mobile/src/components/common/TabHeader.tsx +58 -0
  134. package/templates/production-fullstack/mobile/src/components/common/Typography.tsx +113 -0
  135. package/templates/production-fullstack/mobile/src/components/common/Welcome.tsx +55 -0
  136. package/templates/production-fullstack/mobile/src/components/ui/BottomModal.tsx +159 -0
  137. package/templates/production-fullstack/mobile/src/components/ui/ClassicTabBar.tsx +129 -0
  138. package/templates/production-fullstack/mobile/src/components/ui/CustomTabBar.tsx +238 -0
  139. package/templates/production-fullstack/mobile/src/components/ui/Sidebar.tsx +258 -0
  140. package/templates/production-fullstack/mobile/src/components/ui/buttons/Button.tsx +180 -0
  141. package/templates/production-fullstack/mobile/src/components/ui/buttons/ButtonGroup.tsx +32 -0
  142. package/templates/production-fullstack/mobile/src/components/ui/buttons/FloatingActionButton.tsx +72 -0
  143. package/templates/production-fullstack/mobile/src/components/ui/buttons/IconButton.tsx +84 -0
  144. package/templates/production-fullstack/mobile/src/components/ui/data-display/Accordion.tsx +82 -0
  145. package/templates/production-fullstack/mobile/src/components/ui/data-display/Avatar.tsx +47 -0
  146. package/templates/production-fullstack/mobile/src/components/ui/data-display/Badge.tsx +104 -0
  147. package/templates/production-fullstack/mobile/src/components/ui/data-display/Card.tsx +36 -0
  148. package/templates/production-fullstack/mobile/src/components/ui/data-display/Image.tsx +43 -0
  149. package/templates/production-fullstack/mobile/src/components/ui/data-display/ImageViewer.tsx +33 -0
  150. package/templates/production-fullstack/mobile/src/components/ui/data-display/SwipeableRow.tsx +94 -0
  151. package/templates/production-fullstack/mobile/src/components/ui/feedback/CircularProgress.tsx +38 -0
  152. package/templates/production-fullstack/mobile/src/components/ui/feedback/GestureWrapper.tsx +114 -0
  153. package/templates/production-fullstack/mobile/src/components/ui/feedback/ProgressBar.tsx +51 -0
  154. package/templates/production-fullstack/mobile/src/components/ui/feedback/Skeleton.tsx +84 -0
  155. package/templates/production-fullstack/mobile/src/components/ui/feedback/Spinner.tsx +50 -0
  156. package/templates/production-fullstack/mobile/src/components/ui/inputs/Checkbox.tsx +54 -0
  157. package/templates/production-fullstack/mobile/src/components/ui/inputs/DatePicker.tsx +186 -0
  158. package/templates/production-fullstack/mobile/src/components/ui/inputs/ImagePickerModal.tsx +129 -0
  159. package/templates/production-fullstack/mobile/src/components/ui/inputs/Input.tsx +158 -0
  160. package/templates/production-fullstack/mobile/src/components/ui/inputs/LocationPicker.tsx +211 -0
  161. package/templates/production-fullstack/mobile/src/components/ui/inputs/OTPInput.tsx +83 -0
  162. package/templates/production-fullstack/mobile/src/components/ui/inputs/PasswordInput.tsx +16 -0
  163. package/templates/production-fullstack/mobile/src/components/ui/inputs/Radio.tsx +54 -0
  164. package/templates/production-fullstack/mobile/src/components/ui/inputs/SearchBar.tsx +53 -0
  165. package/templates/production-fullstack/mobile/src/components/ui/inputs/Slider.tsx +49 -0
  166. package/templates/production-fullstack/mobile/src/components/ui/inputs/Switch.tsx +75 -0
  167. package/templates/production-fullstack/mobile/src/components/ui/inputs/TextArea.tsx +15 -0
  168. package/templates/production-fullstack/mobile/src/components/ui/inputs/WebLocationPicker.tsx +321 -0
  169. package/templates/production-fullstack/mobile/src/components/ui/layout/Container.tsx +23 -0
  170. package/templates/production-fullstack/mobile/src/components/ui/layout/Divider.tsx +20 -0
  171. package/templates/production-fullstack/mobile/src/components/ui/layout/Grid.tsx +62 -0
  172. package/templates/production-fullstack/mobile/src/components/ui/layout/KeyboardAvoidingViewWrapper.tsx +16 -0
  173. package/templates/production-fullstack/mobile/src/components/ui/layout/ScrollContainer.tsx +21 -0
  174. package/templates/production-fullstack/mobile/src/components/ui/layout/Section.tsx +51 -0
  175. package/templates/production-fullstack/mobile/src/components/ui/layout/Spacer.tsx +6 -0
  176. package/templates/production-fullstack/mobile/src/components/ui/layout/Stack.tsx +34 -0
  177. package/templates/production-fullstack/mobile/src/components/ui/navigation/Pagination.tsx +61 -0
  178. package/templates/production-fullstack/mobile/src/components/ui/navigation/ProgressStepper.tsx +66 -0
  179. package/templates/production-fullstack/mobile/src/components/ui/navigation/Tabs.tsx +173 -0
  180. package/templates/production-fullstack/mobile/src/config/sidebar.config.ts +72 -0
  181. package/templates/production-fullstack/mobile/src/config/tabNavigation.ts +64 -0
  182. package/templates/production-fullstack/mobile/src/constants/Colors.ts +71 -0
  183. package/templates/production-fullstack/mobile/src/db/index.ts +9 -0
  184. package/templates/production-fullstack/mobile/src/db/schema.ts +14 -0
  185. package/templates/production-fullstack/mobile/src/features/auth/api/auth.service.ts +35 -0
  186. package/templates/production-fullstack/mobile/src/features/auth/store/useAuthStore.ts +273 -0
  187. package/templates/production-fullstack/mobile/src/features/auth/types/auth.type.ts +14 -0
  188. package/templates/production-fullstack/mobile/src/hooks/useDebounce.ts +17 -0
  189. package/templates/production-fullstack/mobile/src/lib/axios.ts +27 -0
  190. package/templates/production-fullstack/mobile/src/lib/query-client.ts +11 -0
  191. package/templates/production-fullstack/mobile/src/lib/socket.ts +36 -0
  192. package/templates/production-fullstack/mobile/src/lib/storage.ts +55 -0
  193. package/templates/production-fullstack/mobile/src/services/permission.service.ts +214 -0
  194. package/templates/production-fullstack/mobile/src/store/localStore.ts +7 -0
  195. package/templates/production-fullstack/mobile/src/store/useNotificationStore.ts +53 -0
  196. package/templates/production-fullstack/mobile/src/types/common.type.ts +6 -0
  197. package/templates/production-fullstack/mobile/src/types/images.d.ts +24 -0
  198. package/templates/production-fullstack/mobile/src/utils/toast.ts +26 -0
  199. package/templates/production-fullstack/mobile/tailwind.config.js +71 -0
  200. package/templates/production-fullstack/mobile/tsconfig.json +23 -0
  201. package/templates/production-fullstack/server/.env.example +63 -0
  202. package/templates/production-fullstack/server/README.md +75 -0
  203. package/templates/production-fullstack/server/eslint.config.js +13 -0
  204. package/templates/production-fullstack/server/package.json +58 -0
  205. package/templates/production-fullstack/server/prisma/schema/core/attachment.prisma +14 -0
  206. package/templates/production-fullstack/server/prisma/schema/core/notification.prisma +18 -0
  207. package/templates/production-fullstack/server/prisma/schema/core/permission.prisma +26 -0
  208. package/templates/production-fullstack/server/prisma/schema/core/role.prisma +25 -0
  209. package/templates/production-fullstack/server/prisma/schema/core/user.prisma +56 -0
  210. package/templates/production-fullstack/server/prisma/schema/enums/common.prisma +13 -0
  211. package/templates/production-fullstack/server/prisma/schema/enums/notification.prisma +6 -0
  212. package/templates/production-fullstack/server/prisma/schema/enums/user.prisma +11 -0
  213. package/templates/production-fullstack/server/prisma/schema/schema.prisma +7 -0
  214. package/templates/production-fullstack/server/prisma.config.ts +14 -0
  215. package/templates/production-fullstack/server/src/app.ts +36 -0
  216. package/templates/production-fullstack/server/src/common/middlewares/auth.middleware.ts +45 -0
  217. package/templates/production-fullstack/server/src/common/middlewares/errorHandler.ts +16 -0
  218. package/templates/production-fullstack/server/src/common/middlewares/multer.ts +20 -0
  219. package/templates/production-fullstack/server/src/common/middlewares/rateLimiter.ts +53 -0
  220. package/templates/production-fullstack/server/src/common/services/email.service.ts +28 -0
  221. package/templates/production-fullstack/server/src/common/services/push.service.ts +65 -0
  222. package/templates/production-fullstack/server/src/common/services/storage.service.ts +9 -0
  223. package/templates/production-fullstack/server/src/common/types/express.d.ts +10 -0
  224. package/templates/production-fullstack/server/src/common/utils/cloudinaryUpload.ts +44 -0
  225. package/templates/production-fullstack/server/src/common/utils/emailOtp.ts +30 -0
  226. package/templates/production-fullstack/server/src/common/utils/genOtpToken.ts +22 -0
  227. package/templates/production-fullstack/server/src/common/utils/generateVapidKeys.ts +12 -0
  228. package/templates/production-fullstack/server/src/common/utils/jwt.ts +27 -0
  229. package/templates/production-fullstack/server/src/common/utils/pushHelper.ts +22 -0
  230. package/templates/production-fullstack/server/src/config/env.ts +2 -0
  231. package/templates/production-fullstack/server/src/config/logger.ts +58 -0
  232. package/templates/production-fullstack/server/src/config/prisma.ts +26 -0
  233. package/templates/production-fullstack/server/src/features/attachments/upload.controller.ts +22 -0
  234. package/templates/production-fullstack/server/src/features/attachments/upload.routes.ts +11 -0
  235. package/templates/production-fullstack/server/src/features/auth/auth.controller.ts +124 -0
  236. package/templates/production-fullstack/server/src/features/auth/auth.routes.ts +22 -0
  237. package/templates/production-fullstack/server/src/features/auth/auth.service.ts +155 -0
  238. package/templates/production-fullstack/server/src/features/notifications/push.controller.ts +55 -0
  239. package/templates/production-fullstack/server/src/features/notifications/push.routes.ts +11 -0
  240. package/templates/production-fullstack/server/src/index.ts +17 -0
  241. package/templates/production-fullstack/server/src/routes/index.ts +12 -0
  242. package/templates/production-fullstack/server/src/sockets/auth.ts +0 -0
  243. package/templates/production-fullstack/server/src/sockets/connection.ts +31 -0
  244. package/templates/production-fullstack/server/src/sockets/emitters/assignment.emitter.ts +0 -0
  245. package/templates/production-fullstack/server/src/sockets/emitters/attendance.emitter.ts +0 -0
  246. package/templates/production-fullstack/server/src/sockets/emitters/exam.emitter.ts +0 -0
  247. package/templates/production-fullstack/server/src/sockets/emitters/notification.emitter.ts +17 -0
  248. package/templates/production-fullstack/server/src/sockets/events.ts +27 -0
  249. package/templates/production-fullstack/server/src/sockets/handlers/attendance.handler.ts +0 -0
  250. package/templates/production-fullstack/server/src/sockets/handlers/chat.handler.ts +0 -0
  251. package/templates/production-fullstack/server/src/sockets/handlers/index.ts +15 -0
  252. package/templates/production-fullstack/server/src/sockets/handlers/lms.handler.ts +0 -0
  253. package/templates/production-fullstack/server/src/sockets/handlers/notification.handler.ts +39 -0
  254. package/templates/production-fullstack/server/src/sockets/index.ts +39 -0
  255. package/templates/production-fullstack/server/src/sockets/middleware.ts +54 -0
  256. package/templates/production-fullstack/server/src/sockets/rooms.ts +11 -0
  257. package/templates/production-fullstack/server/src/sockets/types.ts +32 -0
  258. package/templates/production-fullstack/server/tsconfig.json +14 -0
  259. package/templates/react-native/package.json +2 -1
  260. package/templates/react-native/src/app/(auth)/edit-profile.tsx +11 -15
  261. package/templates/react-native/src/app/(tabs)/dashboard.tsx +2 -2
  262. package/templates/react-native/src/app/(tabs)/search.tsx +2 -2
  263. package/templates/react-native/src/config/sidebar.config.ts +0 -60
  264. package/templates/react-native/src/config/tabNavigation.ts +6 -36
  265. package/templates/react-native/src/services/notification.service.ts +22 -0
  266. package/templates/react-native/src/store/useNotificationStore.ts +158 -33
  267. /package/templates/{react-native → production-fullstack/mobile}/expo-env.d.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imksh/create",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "description": "A production-ready create for scaffolding modern applications.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,7 +38,8 @@ export default async function createCommand(projectName, options) {
38
38
  'next',
39
39
  'next-ts',
40
40
  'react-native',
41
- 'mern-react-native'
41
+ 'mern-react-native',
42
+ 'production-fullstack'
42
43
  ];
43
44
  if (!validTemplates.includes(template)) {
44
45
  throw new Error(`Invalid template "${template}". Valid templates are: ${validTemplates.join(', ')}`);
package/src/index.js CHANGED
@@ -24,7 +24,7 @@ program
24
24
  .argument("<project-name>", "Name of the project")
25
25
  .option(
26
26
  "-t, --template <template>",
27
- "Template to use (react, react-ts, node, node-ts, node-prisma, node-prisma-ts, mern, mern-ts, next, next-ts, react-native, mern-react-native)",
27
+ "Template to use (react, react-ts, node, node-ts, node-prisma, node-prisma-ts, mern, mern-ts, next, next-ts, react-native, mern-react-native, production-fullstack)",
28
28
  )
29
29
  .option("--skip-install", "Skip installing dependencies")
30
30
  .option("--no-git", "Skip initializing a git repository")
@@ -13,6 +13,7 @@ export async function askTemplate() {
13
13
  { name: 'React Native', value: 'react-native' },
14
14
  { name: 'MERN + React Native', value: 'mern-react-native' },
15
15
  { name: 'Next.js', value: 'next' },
16
+ { name: 'Production Fullstack', value: 'production-fullstack' },
16
17
  ],
17
18
  },
18
19
  ]);
@@ -0,0 +1,43 @@
1
+ # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2
+
3
+ # dependencies
4
+ node_modules/
5
+
6
+ # Expo
7
+ .expo/
8
+ dist/
9
+ web-build/
10
+ expo-env.d.ts
11
+
12
+ # Native
13
+ .kotlin/
14
+ *.orig.*
15
+ *.jks
16
+ *.p8
17
+ *.p12
18
+ *.key
19
+ *.mobileprovision
20
+
21
+ # Metro
22
+ .metro-health-check*
23
+
24
+ # debug
25
+ npm-debug.*
26
+ yarn-debug.*
27
+ yarn-error.*
28
+
29
+ # macOS
30
+ .DS_Store
31
+ *.pem
32
+
33
+ # local env files
34
+ .env*.local
35
+
36
+ # typescript
37
+ *.tsbuildinfo
38
+
39
+ example
40
+
41
+ # generated native folders
42
+ /ios
43
+ /android
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "mobile",
2
+ "name": "shivAi",
3
3
  "main": "expo-router/entry",
4
4
  "version": "1.0.0",
5
5
  "dependencies": {
@@ -19,6 +19,7 @@
19
19
  "expo-font": "~57.0.1",
20
20
  "expo-glass-effect": "~57.0.1",
21
21
  "expo-image": "~57.0.1",
22
+ "expo-image-picker": "^57.0.6",
22
23
  "expo-linear-gradient": "^57.0.1",
23
24
  "expo-linking": "~57.0.3",
24
25
  "expo-notifications": "~57.0.6",
@@ -3,8 +3,8 @@ import { View, TouchableOpacity } from "react-native";
3
3
  import { useAuthStore } from "../../store/useAuthStore";
4
4
  import { Txt } from "../../components/common/Typography";
5
5
  import { router, useFocusEffect } from "expo-router";
6
- import * as ImagePicker from "expo-image-picker";
7
6
  import { Ionicons } from "@expo/vector-icons";
7
+ import ImagePickerModal from "../../components/ui/inputs/ImagePickerModal";
8
8
  import Avatar from "../../components/ui/data-display/Avatar";
9
9
 
10
10
  import ScrollContainer from "../../components/ui/layout/ScrollContainer";
@@ -27,19 +27,7 @@ export default function EditProfileScreen() {
27
27
  const [name, setName] = useState(user?.name || "");
28
28
  const [phone, setPhone] = useState(user?.phone || "");
29
29
  const [avatarUri, setAvatarUri] = useState<string | null>(null);
30
-
31
- const pickImage = async () => {
32
- const result = await ImagePicker.launchImageLibraryAsync({
33
- mediaTypes: ImagePicker.MediaTypeOptions.Images,
34
- allowsEditing: true,
35
- aspect: [1, 1],
36
- quality: 0.8,
37
- });
38
-
39
- if (!result.canceled && result.assets[0]) {
40
- setAvatarUri(result.assets[0].uri);
41
- }
42
- };
30
+ const [isImagePickerOpen, setIsImagePickerOpen] = useState(false);
43
31
 
44
32
  const handleSave = async () => {
45
33
  const formData = new FormData();
@@ -112,7 +100,7 @@ export default function EditProfileScreen() {
112
100
  )}
113
101
  </View>
114
102
  <TouchableOpacity
115
- onPress={pickImage}
103
+ onPress={() => setIsImagePickerOpen(true)}
116
104
  className="absolute bottom-0 right-0 bg-primary p-2 rounded-full border-2 border-base-100"
117
105
  >
118
106
  <Ionicons name="camera" size={18} color="#fff" />
@@ -155,6 +143,14 @@ export default function EditProfileScreen() {
155
143
  </View>
156
144
  </View>
157
145
  </ScrollContainer>
146
+ <ImagePickerModal
147
+ isOpen={isImagePickerOpen}
148
+ onClose={() => setIsImagePickerOpen(false)}
149
+ onImageSelected={(image) => setAvatarUri(image.uri)}
150
+ allowsEditing
151
+ title="Profile Picture"
152
+ subtitle="Choose a new profile picture"
153
+ />
158
154
  </>
159
155
  );
160
156
  }
@@ -1,11 +1,11 @@
1
1
  import { View } from "react-native";
2
2
  import React from "react";
3
- import HomeHeader from "@/components/common/HomeHeader";
3
+ import TabHeader from "@/components/common/TabHeader";
4
4
 
5
5
  const dashboard = () => {
6
6
  return (
7
7
  <View>
8
- <HomeHeader title="Dashboard" icon="apps-outline" />
8
+ <TabHeader title="Dashboard" showMenuButton={false} />
9
9
  </View>
10
10
  );
11
11
  };
@@ -1,10 +1,10 @@
1
1
  import { View } from "react-native";
2
- import HomeHeader from "@/components/common/HomeHeader";
2
+ import TabHeader from "@/components/common/TabHeader";
3
3
 
4
4
  const search = () => {
5
5
  return (
6
6
  <View>
7
- <HomeHeader title="My App" />
7
+ <TabHeader title="Search" showMenuButton={false} />
8
8
  </View>
9
9
  );
10
10
  };
@@ -5,66 +5,6 @@ export interface SidebarMenuItem {
5
5
  }
6
6
 
7
7
  export const sidebarConfig: Record<string, SidebarMenuItem[]> = {
8
- owner: [
9
- { icon: "home-outline", label: "Dashboard", route: "/(owner)" },
10
- {
11
- icon: "business-outline",
12
- label: "Properties",
13
- route: "/(owner)/properties",
14
- },
15
- { icon: "card-outline", label: "Payments", route: "/(owner)/payments" },
16
- { icon: "people-outline", label: "Tenants", route: "/(owner)/tenant" },
17
- {
18
- icon: "clipboard-outline",
19
- label: "Requests",
20
- route: "/(owner)/requests",
21
- },
22
- {
23
- icon: "construct-outline",
24
- label: "Maintenance",
25
- route: "/(owner)/maintenance",
26
- },
27
- {
28
- icon: "flask-outline",
29
- label: "Test Notifications",
30
- route: "/(owner)/test-notifications",
31
- },
32
- ],
33
- tenant: [
34
-
35
- {
36
- icon: "business-outline",
37
- label: "My Units",
38
- route: "/(tenant)/my-units",
39
- },
40
- { icon: "search-outline", label: "Explore", route: "/(tenant)/explore" },
41
- { icon: "card-outline", label: "Payments", route: "/(tenant)/payments" },
42
- {
43
- icon: "construct-outline",
44
- label: "Maintenance",
45
- route: "/(tenant)/maintenance",
46
- },
47
- {
48
- icon: "document-text-outline",
49
- label: "Documents",
50
- route: "/(tenant)/documents",
51
- },
52
- {
53
- icon: "chatbubble-outline",
54
- label: "Messages",
55
- route: "/(tenant)/messages",
56
- },
57
- {
58
- icon: "notifications-outline",
59
- label: "Notifications",
60
- route: "/(tenant)/notifications",
61
- },
62
- {
63
- icon: "flask-outline",
64
- label: "Test Notifications",
65
- route: "/(tenant)/test-notifications",
66
- },
67
- ],
68
8
  default: [
69
9
  { icon: "home-outline", label: "Dashboard", route: "/" },
70
10
  { icon: "person-outline", label: "Profile", route: "/profile" },
@@ -13,50 +13,20 @@ export const tabNavigationConfig: TabConfig[] = [
13
13
  baseIcon: "home",
14
14
  },
15
15
  {
16
- name: "explore",
17
- title: "Explore",
18
- baseIcon: "compass",
16
+ name: "search",
17
+ title: "Search",
18
+ baseIcon: "search",
19
19
  },
20
20
  {
21
- name: "payments",
22
- title: "Payments",
23
- baseIcon: "wallet",
24
- },
25
- {
26
- name: "notifications",
27
- title: "Notifications",
28
- baseIcon: "notifications",
21
+ name: "dashboard",
22
+ title: "Dashboard",
23
+ baseIcon: "cube",
29
24
  },
30
25
  {
31
26
  name: "profile",
32
27
  title: "Profile",
33
28
  baseIcon: "person-circle",
34
29
  },
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",
59
- },
60
30
  ];
61
31
 
62
32
  export const getTabConfig = (routeName: string) => {
@@ -0,0 +1,22 @@
1
+ import {api} from "../config/api";
2
+
3
+ export const notificationService = {
4
+ getNotifications: () => {
5
+ return api.get("/notifications");
6
+ },
7
+
8
+ markAsRead: (id: string) => {
9
+ return api.put(`/notifications/${id}/read`);
10
+ },
11
+
12
+ markAllAsRead: () => {
13
+ return api.put("/notifications/read-all");
14
+ },
15
+
16
+ registerPushToken: (token: string) => {
17
+ return api.post("/notifications/push-token", { token });
18
+ },
19
+ sendCustomNotification: (tenantId: string, title: string, message: string) => {
20
+ return api.post("/notifications/send-custom", { tenantId, title, message });
21
+ },
22
+ };
@@ -1,53 +1,178 @@
1
- import { create } from 'zustand';
2
- import { getSocket } from '../services/socket.service';
3
- import * as Notifications from 'expo-notifications';
1
+ import { create } from "zustand";
2
+ import { notificationService } from "../services/notification.service";
3
+ import Constants from "expo-constants";
4
+ import { Platform } from "react-native";
5
+ import { getSocket } from "../services/socket.service";
6
+ import { toast } from "../utils/toast";
4
7
 
5
- export interface NotificationState {
6
- notifications: any[];
7
- pushToken: string | null;
8
- listenForSocketEvents: () => void;
9
- registerForPushNotificationsAsync: () => Promise<void>;
8
+ export interface AppNotification {
9
+ id: string;
10
+ title: string;
11
+ message: string;
12
+ type: string;
13
+ url: string | null;
14
+ isRead: boolean;
15
+ metadata: any | null;
16
+ createdAt: string;
17
+ }
18
+
19
+ interface NotificationState {
20
+ notifications: AppNotification[];
21
+ unreadCount: number;
22
+ loading: boolean;
23
+ error: string | null;
24
+
10
25
  fetchNotifications: () => Promise<void>;
26
+ markAsRead: (id: string) => Promise<void>;
27
+ markAllAsRead: () => Promise<void>;
28
+ registerForPushNotificationsAsync: () => Promise<string | undefined>;
29
+ listenForSocketEvents: () => void;
11
30
  }
12
31
 
13
32
  export const useNotificationStore = create<NotificationState>((set, get) => ({
14
33
  notifications: [],
15
- pushToken: null,
34
+ unreadCount: 0,
35
+ loading: false,
36
+ error: null,
16
37
 
17
- listenForSocketEvents: () => {
18
- const socket = getSocket();
19
- if (!socket) return;
38
+ fetchNotifications: async () => {
39
+ set({ loading: true, error: null });
40
+ try {
41
+ const response = await notificationService.getNotifications();
42
+ const notifications = response.data.data;
43
+ const unreadCount = notifications.filter((n: AppNotification) => !n.isRead).length;
44
+
45
+ set({
46
+ notifications,
47
+ unreadCount,
48
+ loading: false,
49
+ });
50
+ } catch (error: any) {
51
+ set({
52
+ error: error.response?.data?.message || "Failed to fetch notifications",
53
+ loading: false,
54
+ });
55
+ }
56
+ },
20
57
 
21
- socket.on('new_notification', (data) => {
22
- set((state) => ({ notifications: [data, ...state.notifications] }));
23
- });
58
+ markAsRead: async (id: string) => {
59
+ try {
60
+ // Optimistic update
61
+ const { notifications, unreadCount } = get();
62
+ const updated = notifications.map(n =>
63
+ n.id === id ? { ...n, isRead: true } : n
64
+ );
65
+
66
+ set({
67
+ notifications: updated,
68
+ unreadCount: Math.max(0, unreadCount - 1)
69
+ });
70
+
71
+ await notificationService.markAsRead(id);
72
+ } catch{
73
+ // Revert if failed
74
+ get().fetchNotifications();
75
+ }
76
+ },
77
+
78
+ markAllAsRead: async () => {
79
+ try {
80
+ const { notifications } = get();
81
+ const updated = notifications.map(n => ({ ...n, isRead: true }));
82
+
83
+ set({
84
+ notifications: updated,
85
+ unreadCount: 0
86
+ });
87
+
88
+ await notificationService.markAllAsRead();
89
+ } catch{
90
+ get().fetchNotifications();
91
+ }
24
92
  },
25
93
 
26
94
  registerForPushNotificationsAsync: async () => {
95
+ let token;
96
+
97
+ const isExpoGo = Constants.appOwnership === 'expo';
98
+ if (isExpoGo && Platform.OS === 'android') {
99
+ console.log('Skipping push notification registration: not supported in Expo Go on Android (SDK 53+).');
100
+ return;
101
+ }
102
+
27
103
  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;
104
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
105
+ const Notifications = require("expo-notifications");
106
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
107
+ const Device = require("expo-device");
108
+
109
+ if (Platform.OS === 'android') {
110
+ await Notifications.setNotificationChannelAsync('default', {
111
+ name: 'default',
112
+ importance: Notifications.AndroidImportance.MAX,
113
+ vibrationPattern: [0, 250, 250, 250],
114
+ lightColor: '#FF231F7C',
115
+ });
33
116
  }
34
- if (finalStatus !== 'granted') {
35
- console.log('Failed to get push token for push notification!');
36
- return;
117
+
118
+ if (Device.isDevice) {
119
+ const { status: existingStatus } = await Notifications.getPermissionsAsync();
120
+ let finalStatus = existingStatus;
121
+
122
+ if (existingStatus !== 'granted') {
123
+ const { status } = await Notifications.requestPermissionsAsync();
124
+ finalStatus = status;
125
+ }
126
+
127
+ if (finalStatus !== 'granted') {
128
+ console.log('Failed to get push token for push notification!');
129
+ return;
130
+ }
131
+
132
+ const projectId =
133
+ Constants.expoConfig?.extra?.eas?.projectId ??
134
+ Constants.easConfig?.projectId ??
135
+ "f9a7053c-bc16-4717-805b-560c5a35e974";
136
+
137
+ token = (await Notifications.getExpoPushTokenAsync({ projectId })).data;
138
+
139
+ // Send token to backend
140
+ try {
141
+ await notificationService.registerPushToken(token);
142
+ } catch (e) {
143
+ console.error("Failed to save push token to server", e);
144
+ }
145
+ } else {
146
+ console.log('Must use physical device for Push Notifications');
37
147
  }
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
148
  } catch (e) {
44
- console.warn("Failed to register for push notifications", e);
149
+ console.warn("Failed to setup push notifications dynamically:", e);
45
150
  }
151
+
152
+ return token;
46
153
  },
47
154
 
48
- fetchNotifications: async () => {
49
- // API call to fetch notifications could go here
50
- // e.g., const data = await api.get('/notifications');
51
- set({ notifications: [] });
155
+ listenForSocketEvents: () => {
156
+ const socket = getSocket();
157
+ if (!socket) return;
158
+
159
+ // Remove any existing listener to prevent duplicates
160
+ socket.off("new_notification");
161
+
162
+ socket.on("new_notification", (notification: AppNotification) => {
163
+ const { notifications, unreadCount } = get();
164
+
165
+ // Ensure we don't duplicate notifications if they were fetched via API around the same time
166
+ const exists = notifications.find((n) => n.id === notification.id);
167
+ if (!exists) {
168
+ set({
169
+ notifications: [notification, ...notifications],
170
+ unreadCount: unreadCount + 1,
171
+ });
172
+
173
+ // Alert the user visually!
174
+ toast.info(notification.title, notification.message);
175
+ }
176
+ });
52
177
  }
53
178
  }));
@@ -17,6 +17,7 @@ const nextConfig = {
17
17
  },
18
18
  ];
19
19
  },
20
+ turbopack: {},
20
21
  };
21
22
 
22
23
  export default withPWA(nextConfig);
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "next-template",
2
+ "name": "client",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
@@ -9,24 +9,25 @@
9
9
  "lint": "next lint"
10
10
  },
11
11
  "dependencies": {
12
- "react": "^18",
13
- "react-dom": "^18",
14
- "next": "14.0.3",
15
12
  "@ducanh2912/next-pwa": "latest",
13
+ "@tailwindcss/postcss": "^4.3.3",
16
14
  "axios": "^1.6.0",
17
15
  "flyonui": "^2.4.1",
18
16
  "framer-motion": "^11.0.0",
19
17
  "lenis": "^1.1.3",
20
18
  "lottie-react": "^2.4.0",
21
- "lucide-react": "^0.292.0",
19
+ "lucide-react": "^0.470.0",
20
+ "next": "^16.3.0",
21
+ "postcss": "^8.5.26",
22
+ "react": "^19.2.8",
23
+ "react-dom": "^19.2.8",
22
24
  "react-hot-toast": "^2.4.1",
23
25
  "react-icons": "^4.11.0",
24
- "tailwindcss": "^4.3.2",
25
- "@tailwindcss/postcss": "^4.3.2",
26
+ "tailwindcss": "^4.3.3",
26
27
  "zustand": "^4.5.2"
27
28
  },
28
29
  "devDependencies": {
29
- "eslint": "^8",
30
- "eslint-config-next": "14.0.3"
30
+ "eslint": "^10.8.1",
31
+ "eslint-config-next": "^16.3.0"
31
32
  }
32
33
  }
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
@@ -28,6 +28,7 @@ export default function RootLayout({ children }) {
28
28
  <div className="min-h-screen bg-base-100 text-base-content font-sans">
29
29
  <Toaster position="top-right" />
30
30
  <Scroll />
31
+
31
32
  {children}
32
33
  </div>
33
34
  </body>
@@ -2,7 +2,7 @@
2
2
  import React from "react";
3
3
  import Link from "next/link";
4
4
  import { motion } from "framer-motion";
5
- import { FiZap, FiLayers, FiCpu, FiShield, FiArrowRight } from "react-icons/fi";
5
+ import { FiZap, FiLayers, FiCpu, FiShield } from "react-icons/fi";
6
6
  import appConfig from "../config/appConfig";
7
7
  import toast from "react-hot-toast";
8
8
  import { useState } from "react";
@@ -9,16 +9,16 @@ const ProtectedRoute = ({ children, role }) => {
9
9
  const router = useRouter();
10
10
 
11
11
  useEffect(() => {
12
- if (!loading) {
12
+ if (!isCheckingAuth) {
13
13
  if (!user) {
14
14
  router.replace("/login");
15
15
  } else if (role && user.role !== role) {
16
16
  router.replace("/");
17
17
  }
18
18
  }
19
- }, [user, loading, role, router]);
19
+ }, [user, isCheckingAuth, role, router]);
20
20
 
21
- if (loading || !user || (role && user.role !== role)) return <Loading />;
21
+ if (isCheckingAuth || !user || (role && user.role !== role)) return <Loading />;
22
22
 
23
23
  return <>{children}</>;
24
24
  };
@@ -1,5 +1,6 @@
1
1
  @import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800;900&display=swap');
2
2
  @import "tailwindcss";
3
+ @source "./";
3
4
  @plugin "flyonui";
4
5
 
5
6