@imksh/create 1.0.24 → 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.
- package/package.json +1 -1
- package/src/commands/create.js +2 -1
- package/src/index.js +1 -1
- package/src/utils/askTemplate.js +1 -0
- package/templates/mern-react-native/client/src/vite-env.d.ts +1 -0
- package/templates/mern-react-native/mobile/_gitignore +43 -0
- package/templates/mern-react-native/mobile/babel.config.js +1 -0
- package/templates/mern-react-native/mobile/drizzle.config.ts +8 -0
- package/templates/mern-react-native/mobile/expo-env.d.ts +3 -0
- package/templates/mern-react-native/mobile/package.json +8 -1
- package/templates/mern-react-native/mobile/src/app/(auth)/_layout.tsx +12 -0
- package/templates/mern-react-native/mobile/src/app/(auth)/change-password.tsx +112 -0
- package/templates/mern-react-native/mobile/src/app/(auth)/edit-profile.tsx +156 -0
- package/templates/mern-react-native/mobile/src/app/(auth)/forgot-password.tsx +191 -0
- package/templates/mern-react-native/mobile/src/app/{login.tsx → (auth)/login.tsx} +69 -21
- package/templates/mern-react-native/mobile/src/app/(tabs)/dashboard.tsx +2 -2
- package/templates/mern-react-native/mobile/src/app/(tabs)/index.tsx +18 -17
- package/templates/mern-react-native/mobile/src/app/(tabs)/profile.tsx +23 -60
- package/templates/mern-react-native/mobile/src/app/(tabs)/search.tsx +2 -2
- package/templates/mern-react-native/mobile/src/app/_layout.tsx +22 -4
- package/templates/mern-react-native/mobile/src/components/common/AnimatedHeader.tsx +328 -0
- package/templates/mern-react-native/mobile/src/components/common/LocationLoader.tsx +591 -0
- package/templates/mern-react-native/mobile/src/components/common/ScreenHeader.tsx +41 -17
- package/templates/mern-react-native/mobile/src/components/common/TabHeader.tsx +58 -0
- package/templates/mern-react-native/mobile/src/components/common/Typography.tsx +49 -30
- package/templates/mern-react-native/mobile/src/components/common/Welcome.tsx +2 -2
- package/templates/mern-react-native/mobile/src/components/ui/BottomModal.tsx +31 -20
- package/templates/mern-react-native/mobile/src/components/ui/ClassicTabBar.tsx +4 -3
- package/templates/mern-react-native/mobile/src/components/ui/CustomTabBar.tsx +108 -71
- package/templates/mern-react-native/mobile/src/components/ui/Sidebar.tsx +81 -56
- package/templates/mern-react-native/mobile/src/components/ui/buttons/Button.tsx +106 -37
- package/templates/mern-react-native/mobile/src/components/ui/buttons/IconButton.tsx +4 -2
- package/templates/mern-react-native/mobile/src/components/ui/data-display/Accordion.tsx +3 -2
- package/templates/mern-react-native/mobile/src/components/ui/data-display/Avatar.tsx +4 -3
- package/templates/mern-react-native/mobile/src/components/ui/data-display/Badge.tsx +45 -17
- package/templates/mern-react-native/mobile/src/components/ui/data-display/ImageViewer.tsx +19 -9
- package/templates/mern-react-native/mobile/src/components/ui/feedback/CircularProgress.tsx +5 -4
- package/templates/mern-react-native/mobile/src/components/ui/feedback/ProgressBar.tsx +4 -3
- package/templates/mern-react-native/mobile/src/components/ui/feedback/Skeleton.tsx +3 -0
- package/templates/mern-react-native/mobile/src/components/ui/feedback/Spinner.tsx +11 -3
- package/templates/mern-react-native/mobile/src/components/ui/inputs/Checkbox.tsx +3 -2
- package/templates/mern-react-native/mobile/src/components/ui/inputs/DatePicker.tsx +186 -0
- package/templates/mern-react-native/mobile/src/components/ui/inputs/ImagePickerModal.tsx +129 -0
- package/templates/mern-react-native/mobile/src/components/ui/inputs/Input.tsx +54 -14
- package/templates/mern-react-native/mobile/src/components/ui/inputs/LocationPicker.tsx +211 -0
- package/templates/mern-react-native/mobile/src/components/ui/inputs/OTPInput.tsx +32 -9
- package/templates/mern-react-native/mobile/src/components/ui/inputs/PasswordInput.tsx +2 -1
- package/templates/mern-react-native/mobile/src/components/ui/inputs/Radio.tsx +3 -2
- package/templates/mern-react-native/mobile/src/components/ui/inputs/SearchBar.tsx +41 -4
- package/templates/mern-react-native/mobile/src/components/ui/inputs/Slider.tsx +4 -3
- package/templates/mern-react-native/mobile/src/components/ui/inputs/Switch.tsx +3 -2
- package/templates/mern-react-native/mobile/src/components/ui/inputs/TextArea.tsx +2 -1
- package/templates/mern-react-native/mobile/src/components/ui/inputs/WebLocationPicker.tsx +321 -0
- package/templates/mern-react-native/mobile/src/components/ui/layout/Grid.tsx +52 -10
- package/templates/mern-react-native/mobile/src/components/ui/layout/Section.tsx +7 -8
- package/templates/mern-react-native/mobile/src/components/ui/navigation/Pagination.tsx +4 -3
- package/templates/mern-react-native/mobile/src/components/ui/navigation/ProgressStepper.tsx +6 -5
- package/templates/mern-react-native/mobile/src/components/ui/navigation/Tabs.tsx +30 -4
- package/templates/mern-react-native/mobile/src/config/api.ts +12 -1
- package/templates/mern-react-native/mobile/src/config/sidebar.config.ts +12 -0
- package/templates/mern-react-native/mobile/src/config/tabNavigation.ts +1 -1
- package/templates/mern-react-native/mobile/src/db/index.ts +9 -0
- package/templates/mern-react-native/mobile/src/db/schema.ts +14 -0
- package/templates/mern-react-native/mobile/src/hooks/useDebounce.ts +17 -0
- package/templates/mern-react-native/mobile/src/services/notification.service.ts +22 -0
- package/templates/mern-react-native/mobile/src/services/socket.service.ts +36 -0
- package/templates/mern-react-native/mobile/src/store/useAuthStore.ts +122 -27
- package/templates/mern-react-native/mobile/src/store/useNotificationStore.ts +178 -0
- package/templates/mern-react-native/mobile/src/types/auth.type.ts +14 -0
- package/templates/mern-react-native/mobile/tsconfig.json +1 -0
- package/templates/mern-ts/client/src/vite-env.d.ts +1 -0
- package/templates/next/next.config.mjs +1 -0
- package/templates/next/package.json +10 -9
- package/templates/next/postcss.config.js +5 -0
- package/templates/next/src/app/layout.js +1 -0
- package/templates/next/src/app/page.js +1 -1
- package/templates/next/src/components/layout/ProtectedRoute.jsx +3 -3
- package/templates/next/src/index.css +1 -0
- package/templates/next/src/store/useUiStore.js +13 -7
- package/templates/next-ts/.env.example +0 -0
- package/templates/next-ts/AGENTS.md +37 -0
- package/templates/next-ts/CLAUDE.md +1 -0
- package/templates/next-ts/_gitignore +0 -24
- package/templates/next-ts/next-env.d.ts +3 -1
- package/templates/next-ts/next.config.mjs +1 -0
- package/templates/next-ts/package.json +13 -12
- package/templates/next-ts/postcss.config.js +5 -0
- package/templates/next-ts/public/logo.png +0 -0
- package/templates/next-ts/src/app/error.tsx +41 -0
- package/templates/next-ts/src/app/layout.tsx +2 -1
- package/templates/next-ts/src/app/not-found.tsx +5 -0
- package/templates/next-ts/src/app/page.tsx +1 -1
- package/templates/next-ts/src/components/layout/ProtectedRoute.tsx +3 -3
- package/templates/next-ts/src/components/ui/Button.tsx +63 -0
- package/templates/next-ts/src/components/ui/Input.tsx +79 -0
- package/templates/next-ts/src/index.css +1 -0
- package/templates/next-ts/src/store/useUiStore.ts +13 -7
- package/templates/next-ts/tsconfig.json +19 -5
- package/templates/production-fullstack/client/index.html +14 -0
- package/templates/production-fullstack/client/package.json +47 -0
- package/templates/production-fullstack/client/public/_redirects +1 -0
- package/templates/production-fullstack/client/public/favicon.ico +0 -0
- package/templates/production-fullstack/client/public/favicon.svg +1 -0
- package/templates/production-fullstack/client/public/logo.png +0 -0
- package/templates/production-fullstack/client/public/manifest.json +24 -0
- package/templates/production-fullstack/client/public/sw.js +56 -0
- package/templates/production-fullstack/client/src/App.tsx +68 -0
- package/templates/production-fullstack/client/src/assets/animations/loading.json +1 -0
- package/templates/production-fullstack/client/src/assets/data/navigation.ts +29 -0
- package/templates/production-fullstack/client/src/assets/images/logo.png +0 -0
- package/templates/production-fullstack/client/src/components/layout/ProtectedRoute.tsx +23 -0
- package/templates/production-fullstack/client/src/components/layout/PublicLayout.tsx +13 -0
- package/templates/production-fullstack/client/src/components/layout/SidebarLayout.tsx +37 -0
- package/templates/production-fullstack/client/src/components/shared/Breadcrumbs.tsx +27 -0
- package/templates/production-fullstack/client/src/components/shared/LottieIcon.tsx +36 -0
- package/templates/production-fullstack/client/src/components/shared/NotFound.tsx +78 -0
- package/templates/production-fullstack/client/src/components/shared/PwaInstallPrompt.tsx +72 -0
- package/templates/production-fullstack/client/src/components/shared/SEO.tsx +67 -0
- package/templates/production-fullstack/client/src/components/shared/Scroll.tsx +25 -0
- package/templates/production-fullstack/client/src/components/shared/Unauthorized.tsx +76 -0
- package/templates/production-fullstack/client/src/components/shared/skeletons/common/CardSkeleton.tsx +23 -0
- package/templates/production-fullstack/client/src/components/shared/skeletons/common/DashboardSkeleton.tsx +19 -0
- package/templates/production-fullstack/client/src/components/shared/skeletons/common/ListSkeleton.tsx +24 -0
- package/templates/production-fullstack/client/src/components/shared/skeletons/common/PageSkeleton.tsx +26 -0
- package/templates/production-fullstack/client/src/components/shared/skeletons/common/ProfileSkeleton.tsx +23 -0
- package/templates/production-fullstack/client/src/components/shared/skeletons/common/Skeleton.tsx +18 -0
- package/templates/production-fullstack/client/src/components/shared/skeletons/common/TableSkeleton.tsx +33 -0
- package/templates/production-fullstack/client/src/components/ui/ConfirmModal.tsx +152 -0
- package/templates/production-fullstack/client/src/components/ui/Header.tsx +230 -0
- package/templates/production-fullstack/client/src/components/ui/Loading.tsx +19 -0
- package/templates/production-fullstack/client/src/components/ui/Modal.tsx +106 -0
- package/templates/production-fullstack/client/src/components/ui/SidebarHeader.tsx +34 -0
- package/templates/production-fullstack/client/src/components/ui/Table.tsx +118 -0
- package/templates/production-fullstack/client/src/components/ui/ToggleSwitch.tsx +38 -0
- package/templates/production-fullstack/client/src/components/ui/sidebar.tsx +152 -0
- package/templates/production-fullstack/client/src/config/index.ts +44 -0
- package/templates/production-fullstack/client/src/constants/navigation.ts +14 -0
- package/templates/production-fullstack/client/src/features/auth/api/authService.ts +19 -0
- package/templates/production-fullstack/client/src/features/auth/pages/Login.tsx +233 -0
- package/templates/production-fullstack/client/src/features/auth/store/useAuthStore.ts +143 -0
- package/templates/production-fullstack/client/src/features/core/pages/Home.tsx +150 -0
- package/templates/production-fullstack/client/src/features/core/pages/Profile.tsx +9 -0
- package/templates/production-fullstack/client/src/hooks/useLenis.ts +28 -0
- package/templates/production-fullstack/client/src/hooks/usePwaInstall.ts +45 -0
- package/templates/production-fullstack/client/src/hooks/useWindowSize.ts +25 -0
- package/templates/production-fullstack/client/src/index.css +158 -0
- package/templates/production-fullstack/client/src/lib/axios.ts +22 -0
- package/templates/production-fullstack/client/src/lib/dayjs.ts +12 -0
- package/templates/production-fullstack/client/src/lib/query-client.ts +11 -0
- package/templates/production-fullstack/client/src/lib/socket.ts +6 -0
- package/templates/production-fullstack/client/src/main.tsx +28 -0
- package/templates/production-fullstack/client/src/store/useUiStore.ts +74 -0
- package/templates/production-fullstack/client/src/utils/formatDate.ts +19 -0
- package/templates/production-fullstack/client/src/utils/getInitials.ts +10 -0
- package/templates/production-fullstack/client/src/vite-env.d.ts +1 -0
- package/templates/production-fullstack/client/tsconfig.json +25 -0
- package/templates/production-fullstack/client/tsconfig.node.json +10 -0
- package/templates/production-fullstack/client/vite.config.ts +8 -0
- package/templates/production-fullstack/mobile/LICENSE +21 -0
- package/templates/production-fullstack/mobile/app.json +84 -0
- package/templates/production-fullstack/mobile/assets/animations/celebrate.json +1 -0
- package/templates/production-fullstack/mobile/assets/animations/infinity.json +1 -0
- package/templates/production-fullstack/mobile/assets/animations/loading.json +1 -0
- package/templates/production-fullstack/mobile/assets/fonts/Inter_Bold.ttf +0 -0
- package/templates/production-fullstack/mobile/assets/fonts/Inter_ExtraBold.ttf +0 -0
- package/templates/production-fullstack/mobile/assets/fonts/Inter_Medium.ttf +0 -0
- package/templates/production-fullstack/mobile/assets/fonts/Inter_Regular.ttf +0 -0
- package/templates/production-fullstack/mobile/assets/fonts/Inter_SemiBold.ttf +0 -0
- package/templates/production-fullstack/mobile/assets/images/logo/logo.png +0 -0
- package/templates/production-fullstack/mobile/assets/images/logo/logo.svg +1 -0
- package/templates/production-fullstack/mobile/assets/images/logo/logoWithBg.png +0 -0
- package/templates/production-fullstack/mobile/babel.config.js +13 -0
- package/templates/production-fullstack/mobile/drizzle.config.ts +8 -0
- package/templates/production-fullstack/mobile/eas.json +24 -0
- package/templates/production-fullstack/mobile/eslint.config.js +10 -0
- package/templates/production-fullstack/mobile/expo-env.d.ts +3 -0
- package/templates/production-fullstack/mobile/metro.config.js +6 -0
- package/templates/production-fullstack/mobile/nativewind-env.d.ts +3 -0
- package/templates/production-fullstack/mobile/package.json +89 -0
- package/templates/production-fullstack/mobile/src/app/(auth)/_layout.tsx +12 -0
- package/templates/production-fullstack/mobile/src/app/(auth)/change-password.tsx +112 -0
- package/templates/production-fullstack/mobile/src/app/(auth)/edit-profile.tsx +160 -0
- package/templates/production-fullstack/mobile/src/app/(auth)/forgot-password.tsx +191 -0
- package/templates/production-fullstack/mobile/src/app/(auth)/login.tsx +253 -0
- package/templates/production-fullstack/mobile/src/app/_layout.tsx +102 -0
- package/templates/production-fullstack/mobile/src/app/global.css +82 -0
- package/templates/production-fullstack/mobile/src/components/common/AnimatedHeader.tsx +328 -0
- package/templates/production-fullstack/mobile/src/components/common/ConfirmationToast.tsx +67 -0
- package/templates/production-fullstack/mobile/src/components/common/Loading.tsx +26 -0
- package/templates/production-fullstack/mobile/src/components/common/LocationLoader.tsx +591 -0
- package/templates/production-fullstack/mobile/src/components/common/ScreenHeader.tsx +68 -0
- package/templates/production-fullstack/mobile/src/components/common/TabHeader.tsx +58 -0
- package/templates/production-fullstack/mobile/src/components/common/Typography.tsx +113 -0
- package/templates/production-fullstack/mobile/src/components/common/Welcome.tsx +55 -0
- package/templates/production-fullstack/mobile/src/components/ui/BottomModal.tsx +159 -0
- package/templates/production-fullstack/mobile/src/components/ui/ClassicTabBar.tsx +129 -0
- package/templates/production-fullstack/mobile/src/components/ui/CustomTabBar.tsx +238 -0
- package/templates/production-fullstack/mobile/src/components/ui/Sidebar.tsx +258 -0
- package/templates/production-fullstack/mobile/src/components/ui/buttons/Button.tsx +180 -0
- package/templates/production-fullstack/mobile/src/components/ui/buttons/ButtonGroup.tsx +32 -0
- package/templates/production-fullstack/mobile/src/components/ui/buttons/FloatingActionButton.tsx +72 -0
- package/templates/production-fullstack/mobile/src/components/ui/buttons/IconButton.tsx +84 -0
- package/templates/production-fullstack/mobile/src/components/ui/data-display/Accordion.tsx +82 -0
- package/templates/production-fullstack/mobile/src/components/ui/data-display/Avatar.tsx +47 -0
- package/templates/production-fullstack/mobile/src/components/ui/data-display/Badge.tsx +104 -0
- package/templates/production-fullstack/mobile/src/components/ui/data-display/Card.tsx +36 -0
- package/templates/production-fullstack/mobile/src/components/ui/data-display/Image.tsx +43 -0
- package/templates/production-fullstack/mobile/src/components/ui/data-display/ImageViewer.tsx +33 -0
- package/templates/production-fullstack/mobile/src/components/ui/data-display/SwipeableRow.tsx +94 -0
- package/templates/production-fullstack/mobile/src/components/ui/feedback/CircularProgress.tsx +38 -0
- package/templates/production-fullstack/mobile/src/components/ui/feedback/GestureWrapper.tsx +114 -0
- package/templates/production-fullstack/mobile/src/components/ui/feedback/ProgressBar.tsx +51 -0
- package/templates/production-fullstack/mobile/src/components/ui/feedback/Skeleton.tsx +84 -0
- package/templates/production-fullstack/mobile/src/components/ui/feedback/Spinner.tsx +50 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/Checkbox.tsx +54 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/DatePicker.tsx +186 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/ImagePickerModal.tsx +129 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/Input.tsx +158 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/LocationPicker.tsx +211 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/OTPInput.tsx +83 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/PasswordInput.tsx +16 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/Radio.tsx +54 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/SearchBar.tsx +53 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/Slider.tsx +49 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/Switch.tsx +75 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/TextArea.tsx +15 -0
- package/templates/production-fullstack/mobile/src/components/ui/inputs/WebLocationPicker.tsx +321 -0
- package/templates/production-fullstack/mobile/src/components/ui/layout/Container.tsx +23 -0
- package/templates/production-fullstack/mobile/src/components/ui/layout/Divider.tsx +20 -0
- package/templates/production-fullstack/mobile/src/components/ui/layout/Grid.tsx +62 -0
- package/templates/production-fullstack/mobile/src/components/ui/layout/KeyboardAvoidingViewWrapper.tsx +16 -0
- package/templates/production-fullstack/mobile/src/components/ui/layout/ScrollContainer.tsx +21 -0
- package/templates/production-fullstack/mobile/src/components/ui/layout/Section.tsx +51 -0
- package/templates/production-fullstack/mobile/src/components/ui/layout/Spacer.tsx +6 -0
- package/templates/production-fullstack/mobile/src/components/ui/layout/Stack.tsx +34 -0
- package/templates/production-fullstack/mobile/src/components/ui/navigation/Pagination.tsx +61 -0
- package/templates/production-fullstack/mobile/src/components/ui/navigation/ProgressStepper.tsx +66 -0
- package/templates/production-fullstack/mobile/src/components/ui/navigation/Tabs.tsx +173 -0
- package/templates/production-fullstack/mobile/src/config/sidebar.config.ts +72 -0
- package/templates/production-fullstack/mobile/src/config/tabNavigation.ts +64 -0
- package/templates/production-fullstack/mobile/src/constants/Colors.ts +71 -0
- package/templates/production-fullstack/mobile/src/db/index.ts +9 -0
- package/templates/production-fullstack/mobile/src/db/schema.ts +14 -0
- package/templates/production-fullstack/mobile/src/features/auth/api/auth.service.ts +35 -0
- package/templates/production-fullstack/mobile/src/features/auth/store/useAuthStore.ts +273 -0
- package/templates/production-fullstack/mobile/src/features/auth/types/auth.type.ts +14 -0
- package/templates/production-fullstack/mobile/src/hooks/useDebounce.ts +17 -0
- package/templates/production-fullstack/mobile/src/lib/axios.ts +27 -0
- package/templates/production-fullstack/mobile/src/lib/query-client.ts +11 -0
- package/templates/production-fullstack/mobile/src/lib/socket.ts +36 -0
- package/templates/production-fullstack/mobile/src/lib/storage.ts +55 -0
- package/templates/production-fullstack/mobile/src/services/permission.service.ts +214 -0
- package/templates/production-fullstack/mobile/src/store/localStore.ts +7 -0
- package/templates/production-fullstack/mobile/src/store/useNotificationStore.ts +53 -0
- package/templates/production-fullstack/mobile/src/types/common.type.ts +6 -0
- package/templates/production-fullstack/mobile/src/types/images.d.ts +24 -0
- package/templates/production-fullstack/mobile/src/utils/toast.ts +26 -0
- package/templates/production-fullstack/mobile/tailwind.config.js +71 -0
- package/templates/production-fullstack/mobile/tsconfig.json +23 -0
- package/templates/production-fullstack/server/.env.example +63 -0
- package/templates/production-fullstack/server/README.md +75 -0
- package/templates/production-fullstack/server/eslint.config.js +13 -0
- package/templates/production-fullstack/server/package.json +58 -0
- package/templates/production-fullstack/server/prisma/schema/core/attachment.prisma +14 -0
- package/templates/production-fullstack/server/prisma/schema/core/notification.prisma +18 -0
- package/templates/production-fullstack/server/prisma/schema/core/permission.prisma +26 -0
- package/templates/production-fullstack/server/prisma/schema/core/role.prisma +25 -0
- package/templates/production-fullstack/server/prisma/schema/core/user.prisma +56 -0
- package/templates/production-fullstack/server/prisma/schema/enums/common.prisma +13 -0
- package/templates/production-fullstack/server/prisma/schema/enums/notification.prisma +6 -0
- package/templates/production-fullstack/server/prisma/schema/enums/user.prisma +11 -0
- package/templates/production-fullstack/server/prisma/schema/schema.prisma +7 -0
- package/templates/production-fullstack/server/prisma.config.ts +14 -0
- package/templates/production-fullstack/server/src/app.ts +36 -0
- package/templates/production-fullstack/server/src/common/middlewares/auth.middleware.ts +45 -0
- package/templates/production-fullstack/server/src/common/middlewares/errorHandler.ts +16 -0
- package/templates/production-fullstack/server/src/common/middlewares/multer.ts +20 -0
- package/templates/production-fullstack/server/src/common/middlewares/rateLimiter.ts +53 -0
- package/templates/production-fullstack/server/src/common/services/email.service.ts +28 -0
- package/templates/production-fullstack/server/src/common/services/push.service.ts +65 -0
- package/templates/production-fullstack/server/src/common/services/storage.service.ts +9 -0
- package/templates/production-fullstack/server/src/common/types/express.d.ts +10 -0
- package/templates/production-fullstack/server/src/common/utils/cloudinaryUpload.ts +44 -0
- package/templates/production-fullstack/server/src/common/utils/emailOtp.ts +30 -0
- package/templates/production-fullstack/server/src/common/utils/genOtpToken.ts +22 -0
- package/templates/production-fullstack/server/src/common/utils/generateVapidKeys.ts +12 -0
- package/templates/production-fullstack/server/src/common/utils/jwt.ts +27 -0
- package/templates/production-fullstack/server/src/common/utils/pushHelper.ts +22 -0
- package/templates/production-fullstack/server/src/config/env.ts +2 -0
- package/templates/production-fullstack/server/src/config/logger.ts +58 -0
- package/templates/production-fullstack/server/src/config/prisma.ts +26 -0
- package/templates/production-fullstack/server/src/features/attachments/upload.controller.ts +22 -0
- package/templates/production-fullstack/server/src/features/attachments/upload.routes.ts +11 -0
- package/templates/production-fullstack/server/src/features/auth/auth.controller.ts +124 -0
- package/templates/production-fullstack/server/src/features/auth/auth.routes.ts +22 -0
- package/templates/production-fullstack/server/src/features/auth/auth.service.ts +155 -0
- package/templates/production-fullstack/server/src/features/notifications/push.controller.ts +55 -0
- package/templates/production-fullstack/server/src/features/notifications/push.routes.ts +11 -0
- package/templates/production-fullstack/server/src/index.ts +17 -0
- package/templates/production-fullstack/server/src/routes/index.ts +12 -0
- package/templates/production-fullstack/server/src/sockets/auth.ts +0 -0
- package/templates/production-fullstack/server/src/sockets/connection.ts +31 -0
- package/templates/production-fullstack/server/src/sockets/emitters/assignment.emitter.ts +0 -0
- package/templates/production-fullstack/server/src/sockets/emitters/attendance.emitter.ts +0 -0
- package/templates/production-fullstack/server/src/sockets/emitters/exam.emitter.ts +0 -0
- package/templates/production-fullstack/server/src/sockets/emitters/notification.emitter.ts +17 -0
- package/templates/production-fullstack/server/src/sockets/events.ts +27 -0
- package/templates/production-fullstack/server/src/sockets/handlers/attendance.handler.ts +0 -0
- package/templates/production-fullstack/server/src/sockets/handlers/chat.handler.ts +0 -0
- package/templates/production-fullstack/server/src/sockets/handlers/index.ts +15 -0
- package/templates/production-fullstack/server/src/sockets/handlers/lms.handler.ts +0 -0
- package/templates/production-fullstack/server/src/sockets/handlers/notification.handler.ts +39 -0
- package/templates/production-fullstack/server/src/sockets/index.ts +39 -0
- package/templates/production-fullstack/server/src/sockets/middleware.ts +54 -0
- package/templates/production-fullstack/server/src/sockets/rooms.ts +11 -0
- package/templates/production-fullstack/server/src/sockets/types.ts +32 -0
- package/templates/production-fullstack/server/tsconfig.json +14 -0
- package/templates/react-native/babel.config.js +1 -0
- package/templates/react-native/drizzle.config.ts +8 -0
- package/templates/react-native/package.json +8 -1
- package/templates/react-native/src/app/(auth)/_layout.tsx +12 -0
- package/templates/react-native/src/app/(auth)/change-password.tsx +112 -0
- package/templates/react-native/src/app/(auth)/edit-profile.tsx +156 -0
- package/templates/react-native/src/app/(auth)/forgot-password.tsx +191 -0
- package/templates/react-native/src/app/{login.tsx → (auth)/login.tsx} +69 -21
- package/templates/react-native/src/app/(tabs)/dashboard.tsx +2 -2
- package/templates/react-native/src/app/(tabs)/index.tsx +18 -17
- package/templates/react-native/src/app/(tabs)/profile.tsx +23 -60
- package/templates/react-native/src/app/(tabs)/search.tsx +2 -2
- package/templates/react-native/src/app/_layout.tsx +22 -4
- package/templates/react-native/src/components/common/AnimatedHeader.tsx +328 -0
- package/templates/react-native/src/components/common/LocationLoader.tsx +591 -0
- package/templates/react-native/src/components/common/ScreenHeader.tsx +41 -17
- package/templates/react-native/src/components/common/TabHeader.tsx +58 -0
- package/templates/react-native/src/components/common/Typography.tsx +49 -30
- package/templates/react-native/src/components/common/Welcome.tsx +2 -2
- package/templates/react-native/src/components/ui/BottomModal.tsx +31 -20
- package/templates/react-native/src/components/ui/ClassicTabBar.tsx +4 -3
- package/templates/react-native/src/components/ui/CustomTabBar.tsx +108 -71
- package/templates/react-native/src/components/ui/Sidebar.tsx +81 -56
- package/templates/react-native/src/components/ui/buttons/Button.tsx +106 -37
- package/templates/react-native/src/components/ui/buttons/IconButton.tsx +4 -2
- package/templates/react-native/src/components/ui/data-display/Accordion.tsx +3 -2
- package/templates/react-native/src/components/ui/data-display/Avatar.tsx +4 -3
- package/templates/react-native/src/components/ui/data-display/Badge.tsx +45 -17
- package/templates/react-native/src/components/ui/data-display/ImageViewer.tsx +19 -9
- package/templates/react-native/src/components/ui/feedback/CircularProgress.tsx +5 -4
- package/templates/react-native/src/components/ui/feedback/ProgressBar.tsx +4 -3
- package/templates/react-native/src/components/ui/feedback/Skeleton.tsx +3 -0
- package/templates/react-native/src/components/ui/feedback/Spinner.tsx +11 -3
- package/templates/react-native/src/components/ui/inputs/Checkbox.tsx +3 -2
- package/templates/react-native/src/components/ui/inputs/DatePicker.tsx +186 -0
- package/templates/react-native/src/components/ui/inputs/ImagePickerModal.tsx +129 -0
- package/templates/react-native/src/components/ui/inputs/Input.tsx +54 -14
- package/templates/react-native/src/components/ui/inputs/LocationPicker.tsx +211 -0
- package/templates/react-native/src/components/ui/inputs/OTPInput.tsx +32 -9
- package/templates/react-native/src/components/ui/inputs/PasswordInput.tsx +2 -1
- package/templates/react-native/src/components/ui/inputs/Radio.tsx +3 -2
- package/templates/react-native/src/components/ui/inputs/SearchBar.tsx +41 -4
- package/templates/react-native/src/components/ui/inputs/Slider.tsx +4 -3
- package/templates/react-native/src/components/ui/inputs/Switch.tsx +3 -2
- package/templates/react-native/src/components/ui/inputs/TextArea.tsx +2 -1
- package/templates/react-native/src/components/ui/inputs/WebLocationPicker.tsx +321 -0
- package/templates/react-native/src/components/ui/layout/Grid.tsx +52 -10
- package/templates/react-native/src/components/ui/layout/Section.tsx +7 -8
- package/templates/react-native/src/components/ui/navigation/Pagination.tsx +4 -3
- package/templates/react-native/src/components/ui/navigation/ProgressStepper.tsx +6 -5
- package/templates/react-native/src/components/ui/navigation/Tabs.tsx +30 -4
- package/templates/react-native/src/config/api.ts +12 -1
- package/templates/react-native/src/config/sidebar.config.ts +12 -0
- package/templates/react-native/src/config/tabNavigation.ts +1 -1
- package/templates/react-native/src/db/index.ts +9 -0
- package/templates/react-native/src/db/schema.ts +14 -0
- package/templates/react-native/src/hooks/useDebounce.ts +17 -0
- package/templates/react-native/src/services/notification.service.ts +22 -0
- package/templates/react-native/src/services/socket.service.ts +36 -0
- package/templates/react-native/src/store/useAuthStore.ts +122 -27
- package/templates/react-native/src/store/useNotificationStore.ts +178 -0
- package/templates/react-native/src/types/auth.type.ts +14 -0
- package/templates/react-native/tsconfig.json +1 -0
- package/templates/react-ts/src/vite-env.d.ts +1 -0
- package/templates/mern-react-native/mobile/.env.example +0 -2
- package/templates/mern-react-native/mobile/.vscode/extensions.json +0 -1
- package/templates/mern-react-native/mobile/.vscode/settings.json +0 -7
- package/templates/mern-react-native/mobile/README.md +0 -159
- package/templates/mern-react-native/mobile/docs/README.md +0 -17
- package/templates/mern-react-native/mobile/docs/components/buttons.md +0 -40
- package/templates/mern-react-native/mobile/docs/components/data-display.md +0 -44
- package/templates/mern-react-native/mobile/docs/components/feedback.md +0 -36
- package/templates/mern-react-native/mobile/docs/components/gestures.md +0 -60
- package/templates/mern-react-native/mobile/docs/components/inputs.md +0 -41
- package/templates/mern-react-native/mobile/docs/components/layout.md +0 -34
- package/templates/mern-react-native/mobile/docs/components/navigation.md +0 -31
- package/templates/mern-react-native/mobile/docs/features.md +0 -85
- package/templates/mern-react-native/mobile/docs/getting-started.md +0 -47
- package/templates/mern-react-native/mobile/src/components/common/HomeHeader.tsx +0 -43
- package/templates/mern-react-native/mobile/src/components/settings/SettingToggle.tsx +0 -26
- package/templates/mern-react-native/mobile/src/components/settings/SettingsOptions.tsx +0 -47
- package/templates/react-native/.env.example +0 -2
- package/templates/react-native/.vscode/extensions.json +0 -1
- package/templates/react-native/.vscode/settings.json +0 -7
- package/templates/react-native/README.md +0 -159
- package/templates/react-native/docs/README.md +0 -17
- package/templates/react-native/docs/components/buttons.md +0 -40
- package/templates/react-native/docs/components/data-display.md +0 -44
- package/templates/react-native/docs/components/feedback.md +0 -36
- package/templates/react-native/docs/components/gestures.md +0 -60
- package/templates/react-native/docs/components/inputs.md +0 -41
- package/templates/react-native/docs/components/layout.md +0 -34
- package/templates/react-native/docs/components/navigation.md +0 -31
- package/templates/react-native/docs/features.md +0 -85
- package/templates/react-native/docs/getting-started.md +0 -47
- package/templates/react-native/src/components/common/HomeHeader.tsx +0 -43
- package/templates/react-native/src/components/settings/SettingToggle.tsx +0 -26
- package/templates/react-native/src/components/settings/SettingsOptions.tsx +0 -47
|
@@ -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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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,178 @@
|
|
|
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";
|
|
7
|
+
|
|
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
|
+
|
|
25
|
+
fetchNotifications: () => Promise<void>;
|
|
26
|
+
markAsRead: (id: string) => Promise<void>;
|
|
27
|
+
markAllAsRead: () => Promise<void>;
|
|
28
|
+
registerForPushNotificationsAsync: () => Promise<string | undefined>;
|
|
29
|
+
listenForSocketEvents: () => void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const useNotificationStore = create<NotificationState>((set, get) => ({
|
|
33
|
+
notifications: [],
|
|
34
|
+
unreadCount: 0,
|
|
35
|
+
loading: false,
|
|
36
|
+
error: null,
|
|
37
|
+
|
|
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
|
+
},
|
|
57
|
+
|
|
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
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
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
|
+
|
|
103
|
+
try {
|
|
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
|
+
});
|
|
116
|
+
}
|
|
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');
|
|
147
|
+
}
|
|
148
|
+
} catch (e) {
|
|
149
|
+
console.warn("Failed to setup push notifications dynamically:", e);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return token;
|
|
153
|
+
},
|
|
154
|
+
|
|
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
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}));
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{ "recommendations": ["expo.vscode-expo-tools"] }
|
|
@@ -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.
|