@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
@@ -0,0 +1,23 @@
1
+ import Skeleton from "./Skeleton";
2
+
3
+ const CardSkeleton = () => {
4
+ return (
5
+ <div className="card bg-base-100 shadow border border-base-300">
6
+ <div className="card-body space-y-4">
7
+
8
+ <Skeleton className="h-40 w-full rounded-xl" />
9
+
10
+ <Skeleton className="h-6 w-2/3" />
11
+
12
+ <Skeleton className="h-4 w-full" />
13
+
14
+ <Skeleton className="h-4 w-5/6" />
15
+
16
+ <Skeleton className="h-10 w-28" />
17
+
18
+ </div>
19
+ </div>
20
+ );
21
+ };
22
+
23
+ export default CardSkeleton;
@@ -0,0 +1,19 @@
1
+ import CardSkeleton from "./CardSkeleton";
2
+
3
+ const DashboardSkeleton = () => {
4
+ return (
5
+ <div className="space-y-8">
6
+
7
+ <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
8
+ {[...Array(4)].map((_, i) => (
9
+ <CardSkeleton key={i} />
10
+ ))}
11
+ </div>
12
+
13
+ <CardSkeleton />
14
+
15
+ </div>
16
+ );
17
+ };
18
+
19
+ export default DashboardSkeleton;
@@ -0,0 +1,24 @@
1
+ import Skeleton from "./Skeleton";
2
+
3
+ const ListSkeleton = ({ items = 6 }) => {
4
+ return (
5
+ <div className="space-y-4">
6
+ {Array.from({ length: items }).map((_, i) => (
7
+ <div
8
+ key={i}
9
+ className="flex items-center gap-4"
10
+ >
11
+ <Skeleton className="w-12 h-12 rounded-full" />
12
+
13
+ <div className="flex-1 space-y-2">
14
+ <Skeleton className="h-5 w-1/3" />
15
+
16
+ <Skeleton className="h-4 w-2/3" />
17
+ </div>
18
+ </div>
19
+ ))}
20
+ </div>
21
+ );
22
+ };
23
+
24
+ export default ListSkeleton;
@@ -0,0 +1,26 @@
1
+ import Skeleton from "./Skeleton";
2
+
3
+ const PageSkeleton = () => {
4
+ return (
5
+ <div className="space-y-8">
6
+
7
+ <Skeleton className="h-10 w-56" />
8
+
9
+ <Skeleton className="h-64 w-full rounded-2xl" />
10
+
11
+ <div className="grid md:grid-cols-3 gap-6">
12
+
13
+ {[1, 2, 3].map((i) => (
14
+ <Skeleton
15
+ key={i}
16
+ className="h-40 rounded-xl"
17
+ />
18
+ ))}
19
+
20
+ </div>
21
+
22
+ </div>
23
+ );
24
+ };
25
+
26
+ export default PageSkeleton;
@@ -0,0 +1,23 @@
1
+ import Skeleton from "./Skeleton";
2
+
3
+ const ProfileSkeleton = () => {
4
+ return (
5
+ <div className="flex gap-6 items-center">
6
+
7
+ <Skeleton className="w-24 h-24 rounded-full" />
8
+
9
+ <div className="flex-1 space-y-3">
10
+
11
+ <Skeleton className="h-6 w-48" />
12
+
13
+ <Skeleton className="h-4 w-64" />
14
+
15
+ <Skeleton className="h-10 w-32" />
16
+
17
+ </div>
18
+
19
+ </div>
20
+ );
21
+ };
22
+
23
+ export default ProfileSkeleton;
@@ -0,0 +1,18 @@
1
+ const Skeleton = ({
2
+ className = "",
3
+ rounded = "rounded-lg",
4
+ animate = true,
5
+ }) => {
6
+ return (
7
+ <div
8
+ className={`
9
+ bg-base-300
10
+ ${rounded}
11
+ ${animate ? "animate-pulse" : ""}
12
+ ${className}
13
+ `}
14
+ />
15
+ );
16
+ };
17
+
18
+ export default Skeleton;
@@ -0,0 +1,33 @@
1
+ import Skeleton from "./Skeleton";
2
+
3
+ const TableSkeleton = ({ rows = 6, columns = 5 }) => {
4
+ return (
5
+ <div className="overflow-x-auto border border-base-300 rounded-xl">
6
+ <table className="table">
7
+ <thead>
8
+ <tr>
9
+ {Array.from({ length: columns }).map((_, i) => (
10
+ <th key={i}>
11
+ <Skeleton className="h-4 w-20" />
12
+ </th>
13
+ ))}
14
+ </tr>
15
+ </thead>
16
+
17
+ <tbody>
18
+ {Array.from({ length: rows }).map((_, row) => (
19
+ <tr key={row}>
20
+ {Array.from({ length: columns }).map((_, col) => (
21
+ <td key={col}>
22
+ <Skeleton className="h-4 w-full" />
23
+ </td>
24
+ ))}
25
+ </tr>
26
+ ))}
27
+ </tbody>
28
+ </table>
29
+ </div>
30
+ );
31
+ };
32
+
33
+ export default TableSkeleton;
@@ -0,0 +1,152 @@
1
+ import { useEffect } from "react";
2
+ import { createPortal } from "react-dom";
3
+ import {
4
+ FaTriangleExclamation,
5
+ FaCircleInfo,
6
+ FaCircleCheck,
7
+ FaCircleXmark,
8
+ } from "react-icons/fa6";
9
+
10
+ const variants: Record<
11
+ "warning" | "error" | "success" | "info",
12
+ { icon: any; iconBg: string; iconColor: string; button: string }
13
+ > = {
14
+ warning: {
15
+ icon: FaTriangleExclamation,
16
+ iconBg: "bg-warning/15",
17
+ iconColor: "text-warning",
18
+ button: "btn-warning",
19
+ },
20
+ error: {
21
+ icon: FaCircleXmark,
22
+ iconBg: "bg-error/15",
23
+ iconColor: "text-error",
24
+ button: "btn-error",
25
+ },
26
+ success: {
27
+ icon: FaCircleCheck,
28
+ iconBg: "bg-success/15",
29
+ iconColor: "text-success",
30
+ button: "btn-success",
31
+ },
32
+ info: {
33
+ icon: FaCircleInfo,
34
+ iconBg: "bg-info/15",
35
+ iconColor: "text-info",
36
+ button: "btn-info",
37
+ },
38
+ };
39
+
40
+ export interface ConfirmModalProps {
41
+ open: boolean;
42
+ variant?: "warning" | "error" | "success" | "info";
43
+ title?: string;
44
+ message?: string;
45
+ confirmText?: string;
46
+ cancelText?: string;
47
+ loading?: boolean;
48
+ closeOnBackdrop?: boolean;
49
+ closeOnEsc?: boolean;
50
+ onConfirm: () => void | Promise<void>;
51
+ onCancel: () => void;
52
+ }
53
+
54
+ const ConfirmModal = ({
55
+ open,
56
+ variant = "warning",
57
+ title = "Confirm Action",
58
+ message = "Are you sure you want to continue?",
59
+ confirmText = "Confirm",
60
+ cancelText = "Cancel",
61
+ loading = false,
62
+ closeOnBackdrop = true,
63
+ closeOnEsc = true,
64
+ onConfirm,
65
+ onCancel,
66
+ }: ConfirmModalProps) => {
67
+ useEffect(() => {
68
+ if (!open || !closeOnEsc) return;
69
+
70
+ const handleKeyDown = (e: KeyboardEvent) => {
71
+ if (e.key === "Escape") {
72
+ onCancel?.();
73
+ }
74
+ };
75
+
76
+ window.addEventListener("keydown", handleKeyDown);
77
+
78
+ return () => window.removeEventListener("keydown", handleKeyDown);
79
+ }, [open, closeOnEsc, onCancel]);
80
+
81
+ if (!open) return null;
82
+
83
+ const style = variants[variant || "warning"];
84
+ const Icon = style.icon;
85
+
86
+ return createPortal(
87
+ <div className="fixed inset-0 z-[9999] flex items-center justify-center">
88
+ {/* Backdrop */}
89
+
90
+ <div
91
+ className="absolute inset-0 bg-black/60 backdrop-blur-sm"
92
+ onClick={closeOnBackdrop ? onCancel : undefined}
93
+ />
94
+
95
+ {/* Modal */}
96
+
97
+ <div className="relative w-full max-w-md mx-4 rounded-2xl bg-base-100 shadow-2xl animate-in fade-in zoom-in duration-200">
98
+ <div className="p-6">
99
+
100
+ <div className="flex gap-4">
101
+
102
+ <div
103
+ className={`w-14 h-14 rounded-full flex items-center justify-center shrink-0 ${style.iconBg}`}
104
+ >
105
+ <Icon className={`text-2xl ${style.iconColor}`} />
106
+ </div>
107
+
108
+ <div className="flex-1">
109
+
110
+ <h2 className="text-xl font-bold">
111
+ {title}
112
+ </h2>
113
+
114
+ <p className="mt-2 text-sm text-base-content/70 leading-relaxed">
115
+ {message}
116
+ </p>
117
+
118
+ </div>
119
+
120
+ </div>
121
+
122
+ <div className="mt-8 flex justify-end gap-3">
123
+
124
+ <button
125
+ className="btn btn-ghost"
126
+ disabled={loading}
127
+ onClick={onCancel}
128
+ >
129
+ {cancelText}
130
+ </button>
131
+
132
+ <button
133
+ className={`btn ${style.button}`}
134
+ disabled={loading}
135
+ onClick={onConfirm}
136
+ >
137
+ {loading && (
138
+ <span className="loading loading-spinner loading-xs" />
139
+ )}
140
+
141
+ {confirmText}
142
+ </button>
143
+
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>,
148
+ document.body,
149
+ );
150
+ };
151
+
152
+ export default ConfirmModal;
@@ -0,0 +1,230 @@
1
+ import React, { useEffect, useRef } from "react";
2
+ import { Link, useNavigate, useLocation } from "react-router-dom";
3
+ import { FiMenu, FiBookOpen } from "react-icons/fi";
4
+ import { Moon, Sun, X, Globe } from "lucide-react";
5
+ import { useUiStore } from "../../store/useUiStore";
6
+ import appConfig from "../../config/index";
7
+ import { useAuthStore } from "../../features/auth/store/useAuthStore";
8
+ import { getInitials } from "../../utils/getInitials";
9
+ import { motion, AnimatePresence } from "framer-motion";
10
+ import { navigation } from "../../constants/navigation";
11
+
12
+ const Header = () => {
13
+ const {
14
+ theme,
15
+ toggleTheme,
16
+ isMobileMenuOpened: isMobileMenuOpen,
17
+ setMobileMenuOpened: setIsMobilemenuOpen
18
+ } = useUiStore();
19
+ const { user } = useAuthStore();
20
+ const navigate = useNavigate();
21
+ const location = useLocation().pathname;
22
+ const headerRef = useRef(null);
23
+
24
+ // Close mobile menu on scroll
25
+ useEffect(() => {
26
+ const handleScroll = () => {
27
+ setIsMobilemenuOpen(false);
28
+ };
29
+ window.addEventListener("scroll", handleScroll, { passive: true });
30
+ return () => window.removeEventListener("scroll", handleScroll);
31
+ }, [setIsMobilemenuOpen]);
32
+
33
+ // Close mobile menu on click outside
34
+ useEffect(() => {
35
+ const handleClickOutside = (event) => {
36
+ if (headerRef.current && !headerRef.current.contains(event.target)) {
37
+ setIsMobilemenuOpen(false);
38
+ }
39
+ };
40
+ document.addEventListener("mousedown", handleClickOutside);
41
+ return () => document.removeEventListener("mousedown", handleClickOutside);
42
+ }, [setIsMobilemenuOpen]);
43
+
44
+ return (
45
+ <header ref={headerRef} className="sticky top-0 z-40 p-4">
46
+ <div className="max-w-7xl mx-auto relative">
47
+ {/* Main Navbar Bar */}
48
+ <div className="bg-primary text-primary-content rounded-full shadow-lg px-5 py-2.5 flex items-center justify-between relative">
49
+ {/* Left */}
50
+ <div className="flex items-center gap-3">
51
+ <Link to="/" className="flex items-center gap-2">
52
+ {appConfig.app.logo ? (
53
+ <img src={appConfig.logo} alt="logo" className="h-10 w-10" />
54
+ ) : (
55
+ <div className="flex items-center">
56
+ <div className="w-8 h-8 rounded-full bg-base-100/25 flex items-center justify-center text-primary-content shadow-inner">
57
+ <FiBookOpen size={16} />
58
+ </div>
59
+ </div>
60
+ )}
61
+
62
+ <span className="font-bold text-lg hidden sm:block">
63
+ {appConfig.app.name}
64
+ </span>
65
+ </Link>
66
+ </div>
67
+
68
+ {/* Center: Dynamic Navigation */}
69
+ <nav className="hidden lg:flex items-center gap-6 text-sm font-medium absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
70
+ {navigation.map((item) => (
71
+ <Link
72
+ key={item.path}
73
+ to={item.path}
74
+ className={`hover:text-primary transition-colors ${
75
+ location === item.path ? "text-white font-bold" : "text-white/80"
76
+ }`}
77
+ >
78
+ {item.title}
79
+ </Link>
80
+ ))}
81
+ </nav>
82
+
83
+ {/* Right */}
84
+ <div className="flex items-center gap-2">
85
+ {/* Desktop Theme Switcher */}
86
+ <button
87
+ onClick={toggleTheme}
88
+ className="hidden md:flex btn btn-outline btn-soft border-none btn-circle btn-xs"
89
+ title="Toggle Theme"
90
+ >
91
+ {theme === "dark" ? (
92
+ <Sun size={16} className="text-primary-content" />
93
+ ) : (
94
+ <Moon size={16} className="text-primary-content" />
95
+ )}
96
+ </button>
97
+
98
+ {/* Desktop Auth */}
99
+ {user ? (
100
+ <button
101
+ onClick={() => navigate("/profile")}
102
+ className="hidden md:flex btn btn-sm btn-circle overflow-hidden shadow-none hover:ring-1 hover:ring-offset-2 hover:ring-base-300 cursor-pointer btn-primary"
103
+ >
104
+ <img
105
+ src={`https://placehold.co/600x400?text=${getInitials(user?.name) || "KS"}`}
106
+ alt="avatar"
107
+ className="w-full h-full object-cover object-center"
108
+ />
109
+ </button>
110
+ ) : (
111
+ <button
112
+ onClick={() => navigate("/login")}
113
+ className="hidden md:flex btn btn-primary btn-sm rounded-full px-4"
114
+ >
115
+ Login
116
+ </button>
117
+ )}
118
+
119
+ {/* Mobile Dropdown Menu Button */}
120
+ <button
121
+ onClick={() => setIsMobilemenuOpen(!isMobileMenuOpen)}
122
+ className="lg:hidden btn btn-outline btn-soft border-none btn-circle btn-xs text-primary-content transition-transform duration-200"
123
+ title="Toggle Menu"
124
+ >
125
+ {isMobileMenuOpen ? <X size={18} /> : <FiMenu size={18} />}
126
+ </button>
127
+ </div>
128
+ </div>
129
+
130
+ {/* Mobile Dropdown Menu style from user's request */}
131
+ <AnimatePresence>
132
+ {isMobileMenuOpen && (
133
+ <motion.div
134
+ initial={{ opacity: 0, y: -40 }}
135
+ animate={{ opacity: 1, y: 0 }}
136
+ exit={{ opacity: 0, y: -40 }}
137
+ transition={{ duration: 0.3 }}
138
+ className="lg:hidden mt-3 bg-primary backdrop-blur-lg rounded-2xl shadow-xl border border-white/20 overflow-hidden text-primary-content absolute w-full"
139
+ >
140
+ <div className="p-4 space-y-2">
141
+ {/* Dynamic Navigation Links */}
142
+ {navigation.map((item) => (
143
+ <Link
144
+ key={item.path}
145
+ to={item.path}
146
+ onClick={() => setIsMobilemenuOpen(false)}
147
+ className={`block px-4 py-3 rounded-xl hover:bg-white/10 transition-colors font-semibold ${
148
+ location === item.path ? "bg-white/15 text-white" : "text-white/80"
149
+ }`}
150
+ >
151
+ {item.title}
152
+ </Link>
153
+ ))}
154
+
155
+ {/* Theme Toggle */}
156
+ <button
157
+ onClick={toggleTheme}
158
+ className="w-full px-4 py-3 rounded-xl hover:bg-white/10 transition-colors flex items-center gap-3 font-semibold text-white/90"
159
+ >
160
+ <span className="p-2 rounded-lg bg-white/10">
161
+ {theme === "dark" ? <Sun size={16} /> : <Moon size={16} />}
162
+ </span>
163
+ <span>{theme === "dark" ? "Light Mode" : "Dark Mode"}</span>
164
+ </button>
165
+
166
+ {/* Language (Mock Selector matching user design layout) */}
167
+ <button className="w-full px-4 py-3 rounded-xl hover:bg-white/10 transition-colors flex items-center gap-3 font-semibold text-white/90">
168
+ <span className="p-2 rounded-lg bg-white/10">
169
+ <Globe size={16} />
170
+ </span>
171
+ <span>English</span>
172
+ </button>
173
+
174
+ {/* Auth Buttons */}
175
+ {!user ? (
176
+ <div className="pt-4 mt-4 border-t border-white/10 space-y-2">
177
+ <button
178
+ onClick={() => {
179
+ navigate("/login");
180
+ setIsMobilemenuOpen(false);
181
+ }}
182
+ className="w-full px-4 py-3 bg-white/10 border border-white/20 text-white font-semibold rounded-xl hover:bg-white/20 transition-all"
183
+ >
184
+ Login
185
+ </button>
186
+
187
+ <button
188
+ onClick={() => {
189
+ navigate("/login");
190
+ setIsMobilemenuOpen(false);
191
+ }}
192
+ className="w-full px-4 py-3 bg-white text-primary font-semibold rounded-xl hover:bg-white/90 transition-all"
193
+ >
194
+ Register
195
+ </button>
196
+ </div>
197
+ ) : (
198
+ <div className="pt-4 mt-4 border-t border-white/10 space-y-2">
199
+ <div className="flex items-center gap-3 bg-white/10 p-3 rounded-xl border border-white/15">
200
+ <img
201
+ src={`https://placehold.co/100x100?text=${getInitials(user.name)}`}
202
+ className="w-10 h-10 rounded-full border border-white/20 shrink-0"
203
+ alt="avatar"
204
+ />
205
+ <div className="text-left">
206
+ <div className="font-bold text-sm text-white">{user.name}</div>
207
+ <div className="text-xs text-white/70 truncate max-w-[180px]">{user.email}</div>
208
+ </div>
209
+ </div>
210
+ <button
211
+ onClick={() => {
212
+ setIsMobilemenuOpen(false);
213
+ navigate("/profile");
214
+ }}
215
+ className="w-full px-4 py-3 bg-white text-primary font-semibold rounded-xl hover:bg-white/90 transition-all"
216
+ >
217
+ Profile
218
+ </button>
219
+ </div>
220
+ )}
221
+ </div>
222
+ </motion.div>
223
+ )}
224
+ </AnimatePresence>
225
+ </div>
226
+ </header>
227
+ );
228
+ };
229
+
230
+ export default Header;
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import LottieAnimation from "../shared/LottieIcon";
3
+ import loader from "../../assets/animations/loading.json";
4
+
5
+ const Loading = ({
6
+ className = "w-60 h-60",
7
+ bgClass = "bg-base-100",
8
+ animationData = loader,
9
+ }) => {
10
+ return (
11
+ <div
12
+ className={`absolute inset-0 flex items-center justify-center z-50 ${bgClass} max-h-dvh`}
13
+ >
14
+ <LottieAnimation animation={animationData} className={className} />
15
+ </div>
16
+ );
17
+ };
18
+
19
+ export default Loading;
@@ -0,0 +1,106 @@
1
+ import { useEffect, ReactNode } from "react";
2
+ import { createPortal } from "react-dom";
3
+ import { IoClose } from "react-icons/io5";
4
+
5
+ export interface ModalProps {
6
+ open: boolean;
7
+ onClose: () => void;
8
+ title: string;
9
+ children: ReactNode;
10
+ size?: "sm" | "md" | "lg" | "xl" | "2xl" | "full";
11
+ closeOnBackdrop?: boolean;
12
+ closeOnEsc?: boolean;
13
+ showCloseButton?: boolean;
14
+ footer?: ReactNode;
15
+ }
16
+
17
+ const sizes = {
18
+ sm: "max-w-sm",
19
+ md: "max-w-md",
20
+ lg: "max-w-lg",
21
+ xl: "max-w-2xl",
22
+ "2xl": "max-w-4xl",
23
+ full: "max-w-7xl",
24
+ };
25
+
26
+ const Modal = ({
27
+ open,
28
+ onClose,
29
+ title,
30
+ children,
31
+ size = "md",
32
+ closeOnBackdrop = true,
33
+ closeOnEsc = true,
34
+ showCloseButton = true,
35
+ footer,
36
+ }: ModalProps) => {
37
+ useEffect(() => {
38
+ if (!open || !closeOnEsc) return;
39
+
40
+ const handleKeyDown = (e) => {
41
+ if (e.key === "Escape") {
42
+ onClose?.();
43
+ }
44
+ };
45
+
46
+ document.addEventListener("keydown", handleKeyDown);
47
+
48
+ return () =>
49
+ document.removeEventListener("keydown", handleKeyDown);
50
+ }, [open, closeOnEsc, onClose]);
51
+
52
+ if (!open) return null;
53
+
54
+ return createPortal(
55
+ <div className="fixed inset-0 z-[9999] flex items-center justify-center p-4">
56
+ {/* Backdrop */}
57
+ <div
58
+ className="absolute inset-0 bg-black/50 backdrop-blur-sm"
59
+ onClick={closeOnBackdrop ? onClose : undefined}
60
+ />
61
+
62
+ {/* Modal */}
63
+ <div
64
+ className={`
65
+ relative
66
+ w-full
67
+ ${sizes[size]}
68
+ rounded-2xl
69
+ bg-base-100
70
+ shadow-2xl
71
+ border border-base-300
72
+ animate-in zoom-in-95 fade-in duration-200
73
+ `}
74
+ >
75
+ {/* Header */}
76
+ <div className="flex items-center justify-between border-b border-base-300 px-6 py-4">
77
+ <h2 className="text-lg font-bold">{title}</h2>
78
+
79
+ {showCloseButton && (
80
+ <button
81
+ className="btn btn-circle btn-sm btn-ghost"
82
+ onClick={onClose}
83
+ >
84
+ <IoClose size={20} />
85
+ </button>
86
+ )}
87
+ </div>
88
+
89
+ {/* Body */}
90
+ <div className="max-h-[70vh] overflow-y-auto p-6">
91
+ {children}
92
+ </div>
93
+
94
+ {/* Footer */}
95
+ {footer && (
96
+ <div className="border-t border-base-300 px-6 py-4">
97
+ {footer}
98
+ </div>
99
+ )}
100
+ </div>
101
+ </div>,
102
+ document.body,
103
+ );
104
+ };
105
+
106
+ export default Modal;