diff --git a/.cursor/rules/appflow.mdc b/.cursor/rules/appflow.mdc
new file mode 100644
index 0000000..a1d35a7
--- /dev/null
+++ b/.cursor/rules/appflow.mdc
@@ -0,0 +1,165 @@
+---
+description:
+globs:
+alwaysApply: true
+---
+Radiologist_Rule1
+1. RADIOLOGIST APP - DETAILED WORKFLOW
+1.1 App Launch & Authentication
+App Launch → Biometric Auth → Dashboard Loading
+ ↓
+Load Case Queue → Priority Sort → Display Cases
+ ↓
+Critical (RED) → Urgent (ORANGE) → Routine (GREEN)
+1.2 Critical Finding Workflow (Acute Subdural Hemorrhage)
+STEP 1: Alert Reception
+Push Notification → "CRITICAL - Acute Subdural Hemorrhage"
+ ↓
+Tap Notification → App Opens → Case Preview
+ ↓
+Patient: John Doe, Age 45, Head Trauma
+AI Confidence: 93%
+Midline Shift: 7mm
+
+STEP 2: Full Case Review
+Case Preview → Open DICOM Viewer
+ ↓
+AI Overlay ON → Hemorrhage Highlighted
+ ↓
+Review Images → Confirm Finding → Add Observations
+ ↓
+"Large acute subdural hemorrhage with mass effect"
+
+STEP 3: Report Generation
+Voice-to-Text → "Acute subdural hemorrhage..."
+ ↓
+Review Text → Edit if needed → Submit Report
+ ↓
+Preliminary Report Sent (< 5 minutes)
+
+STEP 4: System Learning
+System Logs → Radiologist Confirmation
+ ↓
+Time Metrics Recorded → Model Improvement Data
+1.3 Routine Scan Workflow (Negative Finding)
+STEP 1: Queue Processing
+Dashboard → Routine Queue → Select Case
+ ↓
+Patient: Jane Smith, Age 30, Headache
+AI: No acute abnormalities (99% confidence)
+
+STEP 2: Confirmation Review
+DICOM Viewer → Quick Review → Confirm Negative
+ ↓
+"No acute intracranial abnormality"
+
+STEP 3: Report & Documentation
+Submit Report → System Logs Concordance
+ ↓
+Next Case in Queue
+1.4 System Uncertainty Protocol
+AI Confidence 70-85% → "REVIEW RECOMMENDED"
+ ↓
+Special Yellow Flag → Enhanced Attention Required
+ ↓
+Detailed Review → Feedback to System
+
+3. RADIOLOGIST APP WIREFRAMES
+3.1 Login Screen
++------------------------+
+| [HOSPITAL LOGO] |
+| |
+| Radiologist Portal |
+| |
+| [👤 Dr. Smith] |
+| [🔒 Biometric Login] |
+| |
+| [Face ID Icon] |
+| Touch to Login |
+| |
+| OR |
+| |
+| PIN: [****] |
+| [LOGIN] |
+| |
+| Emergency Access |
++------------------------+
+3.2 Dashboard - Case Queue
++------------------------+
+| Dr. Smith [🔔3][⚙️] |
+| On-Call Status: ACTIVE |
+| |
+| CRITICAL CASES |
+| 🔴 Bed 3 - Hemorrhage |
+| 📱 2 min ago |
+| AI: 93% confidence |
+| [REVIEW NOW] |
+| |
+| 🔴 Bed 7 - Stroke |
+| 📱 5 min ago |
+| AI: 87% confidence |
+| [REVIEW NOW] |
+| |
+| URGENT CASES (4) |
+| 🟡 Show All |
+| |
+| ROUTINE CASES (12) |
+| 🟢 Show All |
++------------------------+
+3.3 Critical Case Details
++------------------------+
+| ← CRITICAL CASE |
+| |
+| Patient: John Doe, 45M |
+| Bed: 3 | Time: 14:35 |
+| Clinical: Head trauma |
+| |
+| AI ANALYSIS: |
+| 🔴 Acute Subdural |
+| Confidence: 93% |
+| Midline Shift: 7mm |
+| Mass Effect: Present |
+| |
+| [VIEW DICOM IMAGES] |
+| [VOICE REPORT] |
+| [QUICK REPORT] |
+| |
+| Status: PENDING REVIEW |
++------------------------+
+3.4 DICOM Viewer
++------------------------+
+| ← John Doe - CT Brain |
+| |
+| [ CT SCAN IMAGE ]|
+| [ WITH AI OVERLAY ]|
+| [ HEMORRHAGE ]|
+| [ HIGHLIGHTED ]|
+| |
+| Tools: [🔍][📏][✏️] |
+| AI: [ON] Conf: 93% |
+| |
+| Measurements: |
+| • Hemorrhage: 3.2cm |
+| • Midline: 7mm shift |
+| |
+| [🎤 START REPORT] |
++------------------------+
+3.5 Voice Report Interface
++------------------------+
+| ← Voice Report |
+| |
+| [🎤 RECORDING 01:23] |
+| |
+| "There is a large |
+| acute subdural |
+| hemorrhage in the |
+| right frontoparietal |
+| region..." |
+| |
+| [PAUSE] [STOP] [PLAY] |
+| |
+| [SAVE DRAFT] |
+| [SUBMIT REPORT] |
+| |
+| Estimated: 2 min left |
++------------------------+
\ No newline at end of file
diff --git a/.cursor/rules/filestructure.mdc b/.cursor/rules/filestructure.mdc
new file mode 100644
index 0000000..8bb80be
--- /dev/null
+++ b/.cursor/rules/filestructure.mdc
@@ -0,0 +1,34 @@
+---
+alwaysApply: true
+---
+Each file should contain this as header
+/*
+ * File: FILE_NAME.tsx
+ * Description: Main chat screen component
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+
+file footer
+/*
+ * End of File: ChatScreen.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+and it should add proper comments in the file for better understanding the flow.
+should follow rule file while generating code.Each file should contain this as header
+/*
+ * File: FILE_NAME.tsx
+ * Description: Main chat screen component
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+
+file footer
+/*
+ * End of File: ChatScreen.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+and it should add proper comments in the file for better understanding the flow.
+should follow rule file while generating code.
\ No newline at end of file
diff --git a/.cursor/rules/folderstructure.mdc b/.cursor/rules/folderstructure.mdc
deleted file mode 100644
index 3dca909..0000000
--- a/.cursor/rules/folderstructure.mdc
+++ /dev/null
@@ -1,3 +0,0 @@
----
-alwaysApply: true
----
diff --git a/.cursor/rules/projectstructure.mdc b/.cursor/rules/projectstructure.mdc
new file mode 100644
index 0000000..3b32ae0
--- /dev/null
+++ b/.cursor/rules/projectstructure.mdc
@@ -0,0 +1,397 @@
+---
+description:
+globs:
+alwaysApply: true
+---
+Project Structure
+🏥 RADIOLOGIST APP STRUCTURE
+NeoScan_Radiologist/
+│
+├── app/
+│ ├── modules/ # 🌐 Feature-wise modular architecture
+│ │ ├── Auth/ # 🔐 Authentication Module
+│ │ │ ├── components/
+│ │ │ │ ├── BiometricLogin.tsx
+│ │ │ │ ├── PinInput.tsx
+│ │ │ │ ├── EmergencyAccess.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── screens/
+│ │ │ │ ├── LoginScreen.tsx
+│ │ │ │ ├── SetupBiometricScreen.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── hooks/
+│ │ │ │ ├── useAuth.ts
+│ │ │ │ ├── useBiometric.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── redux/
+│ │ │ │ ├── authSlice.ts
+│ │ │ │ ├── authActions.ts
+│ │ │ │ ├── authSelectors.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── services/
+│ │ │ │ ├── authAPI.ts
+│ │ │ │ ├── biometricService.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── __tests__/
+│ │ │ │ ├── AuthService.test.ts
+│ │ │ │ ├── LoginScreen.test.tsx
+│ │ │ │ └── authSlice.test.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ ├── Dashboard/ # 📊 Dashboard Module
+│ │ │ ├── components/
+│ │ │ │ ├── CaseQueue.tsx
+│ │ │ │ ├── CaseCard.tsx
+│ │ │ │ ├── PriorityIndicator.tsx
+│ │ │ │ ├── StatsPanel.tsx
+│ │ │ │ ├── FilterBar.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── screens/
+│ │ │ │ ├── DashboardScreen.tsx
+│ │ │ │ ├── CaseListScreen.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── hooks/
+│ │ │ │ ├── useCaseQueue.ts
+│ │ │ │ ├── useRealTimeUpdates.ts
+│ │ │ │ ├── useFilterCases.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── redux/
+│ │ │ │ ├── dashboardSlice.ts
+│ │ │ │ ├── caseQueueSlice.ts
+│ │ │ │ ├── dashboardActions.ts
+│ │ │ │ ├── dashboardSelectors.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── services/
+│ │ │ │ ├── caseAPI.ts
+│ │ │ │ ├── websocketService.ts
+│ │ │ │ ├── notificationService.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── __tests__/
+│ │ │ │ ├── CaseQueue.test.tsx
+│ │ │ │ ├── dashboardSlice.test.ts
+│ │ │ │ └── caseAPI.test.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ ├── CaseReview/ # 🔍 Case Review Module
+│ │ │ ├── components/
+│ │ │ │ ├── DICOMViewer.tsx
+│ │ │ │ ├── AIOverlay.tsx
+│ │ │ │ ├── MeasurementTools.tsx
+│ │ │ │ ├── AnnotationTools.tsx
+│ │ │ │ ├── CaseMetadata.tsx
+│ │ │ │ ├── PriorStudyComparison.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── screens/
+│ │ │ │ ├── CaseDetailsScreen.tsx
+│ │ │ │ ├── DICOMViewerScreen.tsx
+│ │ │ │ ├── ComparisonScreen.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── hooks/
+│ │ │ │ ├── useDICOMViewer.ts
+│ │ │ │ ├── useAIOverlay.ts
+│ │ │ │ ├── useMeasurements.ts
+│ │ │ │ ├── useAnnotations.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── redux/
+│ │ │ │ ├── caseReviewSlice.ts
+│ │ │ │ ├── dicomSlice.ts
+│ │ │ │ ├── aiAnalysisSlice.ts
+│ │ │ │ ├── caseReviewActions.ts
+│ │ │ │ ├── caseReviewSelectors.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── services/
+│ │ │ │ ├── dicomAPI.ts
+│ │ │ │ ├── aiAnalysisAPI.ts
+│ │ │ │ ├── dicomParser.ts
+│ │ │ │ ├── imageProcessor.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── __tests__/
+│ │ │ │ ├── DICOMViewer.test.tsx
+│ │ │ │ ├── AIOverlay.test.tsx
+│ │ │ │ ├── dicomParser.test.ts
+│ │ │ │ └── caseReviewSlice.test.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ ├── Reporting/ # 📝 Reporting Module
+│ │ │ ├── components/
+│ │ │ │ ├── VoiceRecorder.tsx
+│ │ │ │ ├── ReportEditor.tsx
+│ │ │ │ ├── ReportTemplate.tsx
+│ │ │ │ ├── QuickReportButtons.tsx
+│ │ │ │ ├── ReportPreview.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── screens/
+│ │ │ │ ├── ReportingScreen.tsx
+│ │ │ │ ├── VoiceReportScreen.tsx
+│ │ │ │ ├── ReportHistoryScreen.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── hooks/
+│ │ │ │ ├── useVoiceRecording.ts
+│ │ │ │ ├── useReportGeneration.ts
+│ │ │ │ ├── useSpeechToText.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── redux/
+│ │ │ │ ├── reportingSlice.ts
+│ │ │ │ ├── voiceRecordingSlice.ts
+│ │ │ │ ├── reportingActions.ts
+│ │ │ │ ├── reportingSelectors.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── services/
+│ │ │ │ ├── reportAPI.ts
+│ │ │ │ ├── voiceToTextAPI.ts
+│ │ │ │ ├── reportTemplateService.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── __tests__/
+│ │ │ │ ├── VoiceRecorder.test.tsx
+│ │ │ │ ├── reportingSlice.test.ts
+│ │ │ │ └── voiceToTextAPI.test.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ ├── Notifications/ # 🔔 Notifications Module
+│ │ │ ├── components/
+│ │ │ │ ├── NotificationPanel.tsx
+│ │ │ │ ├── AlertBanner.tsx
+│ │ │ │ ├── CriticalAlert.tsx
+│ │ │ │ ├── NotificationSettings.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── screens/
+│ │ │ │ ├── NotificationsScreen.tsx
+│ │ │ │ ├── AlertDetailsScreen.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── hooks/
+│ │ │ │ ├── useNotifications.ts
+│ │ │ │ ├── usePushNotifications.ts
+│ │ │ │ ├── useAlertHandling.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── redux/
+│ │ │ │ ├── notificationsSlice.ts
+│ │ │ │ ├── alertsSlice.ts
+│ │ │ │ ├── notificationActions.ts
+│ │ │ │ ├── notificationSelectors.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── services/
+│ │ │ │ ├── pushNotificationService.ts
+│ │ │ │ ├── alertService.ts
+│ │ │ │ ├── notificationAPI.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── __tests__/
+│ │ │ │ ├── NotificationPanel.test.tsx
+│ │ │ │ ├── alertService.test.ts
+│ │ │ │ └── notificationsSlice.test.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ ├── Analytics/ # 📈 Analytics Module
+│ │ │ ├── components/
+│ │ │ │ ├── PerformanceMetrics.tsx
+│ │ │ │ ├── ResponseTimeChart.tsx
+│ │ │ │ ├── ConcordanceStats.tsx
+│ │ │ │ ├── WorkloadAnalysis.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── screens/
+│ │ │ │ ├── AnalyticsScreen.tsx
+│ │ │ │ ├── PerformanceScreen.tsx
+│ │ │ │ └── index.ts
+│ │ │ ├── hooks/
+│ │ │ │ ├── useAnalytics.ts
+│ │ │ │ ├── usePerformanceMetrics.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── redux/
+│ │ │ │ ├── analyticsSlice.ts
+│ │ │ │ ├── analyticsActions.ts
+│ │ │ │ ├── analyticsSelectors.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── services/
+│ │ │ │ ├── analyticsAPI.ts
+│ │ │ │ ├── metricsCollector.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── __tests__/
+│ │ │ │ ├── PerformanceMetrics.test.tsx
+│ │ │ │ ├── analyticsSlice.test.ts
+│ │ │ │ └── metricsCollector.test.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ └── Profile/ # 👤 User Profile Module
+│ │ ├── components/
+│ │ │ ├── ProfileHeader.tsx
+│ │ │ ├── SettingsPanel.tsx
+│ │ │ ├── PreferencesForm.tsx
+│ │ │ ├── SecuritySettings.tsx
+│ │ │ └── index.ts
+│ │ ├── screens/
+│ │ │ ├── ProfileScreen.tsx
+│ │ │ ├── SettingsScreen.tsx
+│ │ │ ├── PreferencesScreen.tsx
+│ │ │ └── index.ts
+│ │ ├── hooks/
+│ │ │ ├── useProfile.ts
+│ │ │ ├── useSettings.ts
+│ │ │ └── index.ts
+│ │ ├── redux/
+│ │ │ ├── profileSlice.ts
+│ │ │ ├── settingsSlice.ts
+│ │ │ ├── profileActions.ts
+│ │ │ ├── profileSelectors.ts
+│ │ │ └── index.ts
+│ │ ├── services/
+│ │ │ ├── profileAPI.ts
+│ │ │ ├── settingsAPI.ts
+│ │ │ └── index.ts
+│ │ ├── __tests__/
+│ │ │ ├── ProfileHeader.test.tsx
+│ │ │ ├── profileSlice.test.ts
+│ │ │ └── profileAPI.test.ts
+│ │ └── index.ts
+│ │
+│ ├── navigation/ # 🧭 Navigation Setup
+│ │ ├── AppNavigator.tsx
+│ │ ├── AuthNavigator.tsx
+│ │ ├── MainNavigator.tsx
+│ │ ├── TabNavigator.tsx
+│ │ ├── ModalNavigator.tsx
+│ │ ├── navigationTypes.ts
+│ │ └── index.ts
+│ │
+│ ├── redux/ # 🗃️ Redux Store Setup
+│ │ ├── store.ts
+│ │ ├── rootReducer.ts
+│ │ ├── middleware.ts
+│ │ ├── persistConfig.ts
+│ │ └── index.ts
+│ │
+│ ├── constants/ # 📋 Global Constants
+│ │ ├── apiEndpoints.ts
+│ │ ├── appConstants.ts
+│ │ ├── errorMessages.ts
+│ │ ├── notificationTypes.ts
+│ │ ├── priorityLevels.ts
+│ │ └── index.ts
+│ │
+│ ├── App.tsx # 🎯 Root Component
+│ └── index.ts # 🚀 App Bootstrap
+│
+├── shared/ # ✅ Shared Components & Utilities
+│ ├── src/
+│ │ ├── components/ # 🎨 Global UI Components
+│ │ │ ├── Button/
+│ │ │ │ ├── Button.tsx
+│ │ │ │ ├── Button.styles.ts
+│ │ │ │ ├── Button.types.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── Input/
+│ │ │ │ ├── TextInput.tsx
+│ │ │ │ ├── SearchInput.tsx
+│ │ │ │ ├── Input.styles.ts
+│ │ │ │ ├── Input.types.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── Modal/
+│ │ │ │ ├── Modal.tsx
+│ │ │ │ ├── ConfirmModal.tsx
+│ │ │ │ ├── AlertModal.tsx
+│ │ │ │ ├── Modal.styles.ts
+│ │ │ │ ├── Modal.types.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── Card/
+│ │ │ │ ├── Card.tsx
+│ │ │ │ ├── InfoCard.tsx
+│ │ │ │ ├── Card.styles.ts
+│ │ │ │ ├── Card.types.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── Loading/
+│ │ │ │ ├── Spinner.tsx
+│ │ │ │ ├── LoadingOverlay.tsx
+│ │ │ │ ├── Loading.styles.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── Icons/
+│ │ │ │ ├── CustomIcon.tsx
+│ │ │ │ ├── IconButton.tsx
+│ │ │ │ ├── Icons.types.ts
+│ │ │ │ └── index.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ ├── theme/ # 🎨 Design System
+│ │ │ ├── colors.ts
+│ │ │ ├── spacing.ts
+│ │ │ ├── typography.ts
+│ │ │ ├── shadows.ts
+│ │ │ ├── borderRadius.ts
+│ │ │ ├── animations.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ ├── assets/ # 📁 Static Assets
+│ │ │ ├── icons/
+│ │ │ │ ├── critical-alert.svg
+│ │ │ │ ├── urgent-alert.svg
+│ │ │ │ ├── routine-alert.svg
+│ │ │ │ └── index.ts
+│ │ │ ├── images/
+│ │ │ │ ├── logo.png
+│ │ │ │ ├── placeholder.png
+│ │ │ │ └── index.ts
+│ │ │ ├── fonts/
+│ │ │ │ ├── Roboto-Regular.ttf
+│ │ │ │ ├── Roboto-Bold.ttf
+│ │ │ │ └── index.ts
+│ │ │ └── sounds/
+│ │ │ ├── critical-alert.wav
+│ │ │ ├── urgent-alert.wav
+│ │ │ └── routine-alert.wav
+│ │ │
+│ │ ├── utils/ # 🛠️ Utility Functions
+│ │ │ ├── dateTime/
+│ │ │ │ ├── formatDate.ts
+│ │ │ │ ├── timeAgo.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── validation/
+│ │ │ │ ├── validators.ts
+│ │ │ │ ├── schemas.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── helpers/
+│ │ │ │ ├── debounce.ts
+│ │ │ │ ├── throttle.ts
+│ │ │ │ ├── formatters.ts
+│ │ │ │ └── index.ts
+│ │ │ ├── constants/
+│ │ │ │ ├── regex.ts
+│ │ │ │ ├── formats.ts
+│ │ │ │ └── index.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ ├── hooks/ # 🎣 Shared Custom Hooks
+│ │ │ ├── useTheme.ts
+│ │ │ ├── useDebounce.ts
+│ │ │ ├── useKeyboard.ts
+│ │ │ ├── useNetworkStatus.ts
+│ │ │ ├── useOrientation.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ ├── types/ # 📝 TypeScript Types
+│ │ │ ├── common.ts
+│ │ │ ├── api.ts
+│ │ │ ├── navigation.ts
+│ │ │ ├── theme.ts
+│ │ │ └── index.ts
+│ │ │
+│ │ └── index.ts # 📦 Barrel Export
+│ │
+│ ├── package.json # 📦 Shared Package Config
+│ └── tsconfig.json # ⚙️ TypeScript Config
+│
+├── __tests__/ # 🧪 Global Tests
+│ ├── App.test.tsx
+│ ├── Navigation.test.tsx
+│ ├── Redux.test.tsx
+│ └── Integration.test.tsx
+│
+├── assets/ # 🖼️ App-wide Assets
+│ ├── images/
+│ ├── videos/
+│ └── audio/
+│
+├── android/ # 🤖 Android Native
+├── ios/ # 🍎 iOS Native
+├── index.js # 🎯 RN Entry Point
+├── package.json # 📦 Root Dependencies
+├── tsconfig.json # ⚙️ Base TypeScript Config
+├── metro.config.js # 📦 Metro Bundler Config
+├── babel.config.js # 🔄 Babel Config
+└── .eslintrc.js # 📏 ESLint Config
\ No newline at end of file
diff --git a/.cursor/rules/themestructure.mdc b/.cursor/rules/themestructure.mdc
new file mode 100644
index 0000000..f673c3f
--- /dev/null
+++ b/.cursor/rules/themestructure.mdc
@@ -0,0 +1,66 @@
+---
+description:
+globs:
+alwaysApply: true
+---
+Radiologist_Rule3
+Theme and commonly used node_module packages
+
+Theme
+Radiologist App - "Clinical Blue Interface"
+Primary: #5B7CE6 (Blue Purple - from the selected buttons)
+Secondary: #7B94F0 (Light Blue Purple)
+Tertiary: #E8EFFF (Very Light Blue)
+Quaternary: #3B5998 (Deep Blue)
+Text Primary: #1A1D29 (Almost Black)
+Text Secondary: #4A5568 (Medium Gray)
+Text Muted: #9CA3AF (Light Gray)
+Background: #FFFFFF (White)
+Background Alt: #F8FAFF (Very Light Blue Tint)
+Background Accent: #F1F5FF (Soft Blue)
+Card Background: #FFFFFF (White with shadow)
+Success: #10B981 (Green - from the security icon)
+Warning: #F59E0B (Amber)
+Error: #EF4444 (Red)
+Info: #3B82F6 (Blue)
+Border: #E5E7EB (Light Gray)
+Selected State: #5B7CE6 (Same as Primary)
+Inactive State: #F3F4F6 (Light Gray)
+Shadow: rgba(91, 124, 230, 0.1) (Blue Shadow)
+Gradient Background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)
+
+Packages Used
+
+
+ "@gorhom/bottom-sheet": "^5.1.2",
+ "@react-native-async-storage/async-storage": "^2.1.2",
+ "@react-native-community/masked-view": "^0.1.11",
+ "@react-native-community/netinfo": "^11.4.1",
+ "@react-navigation/bottom-tabs": "^7.3.10",
+ "@react-navigation/native": "^7.1.6",
+ "@react-navigation/native-stack": "^7.3.9",
+ "@reduxjs/toolkit": "^2.6.1",
+ "@testing-library/react-native": "^13.2.0",
+ "apisauce": "^2.1.6",
+ "axios": "^1.8.4",
+ "react": "19.0.0",
+ "react-native": "0.79.0",
+ "react-native-chart-kit": "^6.12.0",
+ "react-native-config": "^1.5.5",
+ "react-native-device-info": "^14.0.4",
+ "react-native-gesture-handler": "^2.25.0",
+ "react-native-linear-gradient": "^2.8.3",
+ "react-native-paper": "^5.13.1",
+ "react-native-permissions": "^5.4.0",
+ "react-native-raw-bottom-sheet": "^3.0.0",
+ "react-native-reanimated": "^3.17.4",
+ "react-native-safe-area-context": "^5.3.0",
+ "react-native-screens": "^4.10.0",
+ "react-native-skeleton-content": "^1.0.28",
+ "react-native-sound": "^0.11.2",
+ "react-native-svg": "^15.11.2",
+ "react-native-tab-view": "^4.0.10",
+ "react-native-toast-message": "^2.2.1",
+ "react-native-vector-icons": "^10.2.0",
+ "react-redux": "^9.2.0",
+ "redux-persist": "^6.0.0"
diff --git a/.env b/.env
index aa3551d..d568298 100644
--- a/.env
+++ b/.env
@@ -1,2 +1,2 @@
-BASE_URL='https://neoscan-backend.tech4bizsolutions.com'
-# BASE_URL='http://192.168.1.87:3000'
\ No newline at end of file
+# BASE_URL='https://neoscan-backend.tech4bizsolutions.com'
+BASE_URL='http://192.168.1.87:3000'
\ No newline at end of file
diff --git a/App.tsx b/App.tsx
index c3b4b7b..fbd0bae 100644
--- a/App.tsx
+++ b/App.tsx
@@ -16,13 +16,14 @@ import {
View,
} from 'react-native';
import { Provider } from 'react-redux'; // Redux Provider
-import store from './app/redux/store'; // Redux store
+import {store,persistor} from './app/redux/store'; // Redux store
import {
Colors,
} from 'react-native/Libraries/NewAppScreen';
import TabNavigator from './app/navigation/TabNavigator';
import AppNavigator from './app/navigation/AppNavigator';
import Toast from 'react-native-toast-message'; // Import Toast
+import { PersistGate } from 'redux-persist/integration/react';
type SectionProps = PropsWithChildren<{
title: string;
@@ -77,6 +78,7 @@ function App(): React.JSX.Element {
// Wrap the app with Redux Provider to enable global state management
return (
+
-
+
+
);
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 92b52fb..0ef6108 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -3,6 +3,8 @@
+
+
void
+ style?: any
+}
+
+const BackButton: React.FC = ({ onPress, style }) => {
+ return (
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ backButton: {
+ width: 40,
+ height: 40,
+ borderRadius: 20,
+ backgroundColor: Colors.backButtonBackground,
+ justifyContent: "center",
+ alignItems: "center",
+ shadowColor: Colors.backButtonShadow,
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 3,
+ },
+})
+
+export default BackButton
+
+/*
+ * End of File: BackButton.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
diff --git a/app/modules/Auth/components/DocumentUploadScreen.tsx b/app/modules/Auth/components/DocumentUploadScreen.tsx
new file mode 100644
index 0000000..502be43
--- /dev/null
+++ b/app/modules/Auth/components/DocumentUploadScreen.tsx
@@ -0,0 +1,401 @@
+"use client"
+
+/*
+ * File: DocumentUploadScreen.tsx
+ * Description: Image upload screen for onboarding flow with react-native-image-picker.
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+import type React from "react"
+import { useState } from "react"
+import {
+ View,
+ Text,
+ StyleSheet,
+ TouchableWithoutFeedback,
+ Keyboard,
+ TouchableOpacity,
+ Alert,
+ PermissionsAndroid,
+ Platform,
+ Image
+} from "react-native"
+import {
+ launchImageLibrary,
+ launchCamera,
+ ImagePickerResponse,
+ MediaType,
+ ImagePickerOptions
+} from 'react-native-image-picker'
+import { Button } from "../../../../shared/src/components/Button"
+import { Colors, Spacing, Typography } from "../../../../shared/src/theme"
+import { showError, showSuccess } from "../../../../shared/src/utils/helpers/Toast"
+import Icon from "react-native-vector-icons/Feather"
+import OnboardingContainer from "./OnboardingContainer"
+
+interface DocumentUploadScreenProps {
+ onContinue: (imageData: {
+ uri: string
+ name: string
+ type: string
+ size?: number
+ }) => void
+ onBack: () => void
+}
+
+interface ImageData {
+ uri: string
+ name: string
+ type: string
+ size?: number
+}
+
+const DocumentUploadScreen: React.FC = ({ onContinue, onBack }) => {
+ const [selectedImage, setSelectedImage] = useState(null)
+ const [loading, setLoading] = useState(false)
+
+ // Request camera permission for Android
+ const requestCameraPermission = async (): Promise => {
+ if (Platform.OS === 'android') {
+ try {
+ const granted = await PermissionsAndroid.request(
+ PermissionsAndroid.PERMISSIONS.CAMERA,
+ {
+ title: 'Camera Permission',
+ message: 'This app needs camera permission to capture images.',
+ buttonNeutral: 'Ask Me Later',
+ buttonNegative: 'Cancel',
+ buttonPositive: 'OK',
+ }
+ )
+ return granted === PermissionsAndroid.RESULTS.GRANTED
+ } catch (err) {
+ console.warn(err)
+ return false
+ }
+ }
+ return true
+ }
+
+ const handleImagePicker = () => {
+ Alert.alert("Select Image", "Choose how you want to upload your image", [
+ {
+ text: "Camera",
+ onPress: () => handleCameraCapture(),
+ },
+ {
+ text: "Gallery",
+ onPress: () => handleGalleryPicker(),
+ },
+ {
+ text: "Cancel",
+ style: "cancel",
+ },
+ ])
+ }
+
+ const handleCameraCapture = async () => {
+ const hasPermission = await requestCameraPermission()
+
+ if (!hasPermission) {
+ showError("Permission Error", "Camera permission is required to capture images.")
+ return
+ }
+
+ const options: ImagePickerOptions = {
+ mediaType: 'photo' as MediaType,
+ quality: 0.8,
+ maxWidth: 2000,
+ maxHeight: 2000,
+ includeBase64: false,
+ }
+
+ launchCamera(options, (response: ImagePickerResponse) => {
+ if (response.didCancel) {
+ return
+ }
+
+ if (response.errorMessage) {
+ showError("Camera Error", response.errorMessage)
+ return
+ }
+
+ if (response.assets && response.assets[0]) {
+ const asset = response.assets[0]
+ const imageData: ImageData = {
+ uri: asset.uri!,
+ name: asset.fileName || `image_${Date.now()}.jpg`,
+ type: asset.type || 'image/jpeg',
+ size: asset.fileSize,
+ }
+
+ setSelectedImage(imageData)
+ showSuccess("Success", "Image captured successfully!")
+ }
+ })
+ }
+
+ const handleGalleryPicker = () => {
+ const options: ImagePickerOptions = {
+ mediaType: 'photo' as MediaType,
+ quality: 0.8,
+ maxWidth: 2000,
+ maxHeight: 2000,
+ includeBase64: false,
+ }
+
+ launchImageLibrary(options, (response: ImagePickerResponse) => {
+ if (response.didCancel) {
+ return
+ }
+
+ if (response.errorMessage) {
+ showError("Gallery Error", response.errorMessage)
+ return
+ }
+
+ if (response.assets && response.assets[0]) {
+ const asset = response.assets[0]
+ const imageData: ImageData = {
+ uri: asset.uri!,
+ name: asset.fileName || `image_${Date.now()}.jpg`,
+ type: asset.type || 'image/jpeg',
+ size: asset.fileSize,
+ }
+
+ setSelectedImage(imageData)
+ showSuccess("Success", "Image selected from gallery!")
+ }
+ })
+ }
+
+ const formatFileSize = (bytes?: number): string => {
+ if (!bytes) return ''
+
+ const sizes = ['Bytes', 'KB', 'MB', 'GB']
+ if (bytes === 0) return '0 Bytes'
+
+ const i = Math.floor(Math.log(bytes) / Math.log(1024))
+ return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]
+ }
+
+ const getFileTypeDisplay = (type: string): string => {
+ if (type.includes('jpeg') || type.includes('jpg')) return 'JPEG'
+ if (type.includes('png')) return 'PNG'
+ if (type.includes('gif')) return 'GIF'
+ if (type.includes('webp')) return 'WebP'
+ return 'Image'
+ }
+
+ const handleContinue = () => {
+ if (!selectedImage) {
+ showError("Validation Error", "Please upload an image to continue.")
+ return
+ }
+
+ setLoading(true)
+ setTimeout(() => {
+ setLoading(false)
+ onContinue(selectedImage)
+ }, 1000)
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ Upload Image
+ Please upload your profile picture or identification image.
+
+
+
+ {selectedImage ? (
+
+
+ setSelectedImage(null)}>
+
+
+ Image Uploaded
+ {selectedImage.name}
+
+ {getFileTypeDisplay(selectedImage.type)}
+ {selectedImage.size && (
+ • {formatFileSize(selectedImage.size)}
+ )}
+
+
+ ) : (
+ <>
+
+ Tap to upload image
+ JPG, PNG supported
+ >
+ )}
+
+
+
+ {selectedImage && (
+
+ Change Image
+
+ )}
+
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ content: {
+ flex: 1,
+ justifyContent: "center",
+ },
+ iconContainer: {
+ alignItems: "center",
+ marginBottom: Spacing.xl,
+ },
+ iconWrapper: {
+ width: 80,
+ height: 80,
+ borderRadius: 20,
+ backgroundColor: Colors.backgroundAlt,
+ justifyContent: "center",
+ alignItems: "center",
+ borderWidth: 1,
+ borderColor: Colors.border,
+ },
+ title: {
+ fontFamily: Typography.fontFamily.bold,
+ fontSize: Typography.fontSize.title,
+ color: Colors.textPrimary,
+ textAlign: "center",
+ marginBottom: Spacing.sm,
+ },
+ subtitle: {
+ fontFamily: Typography.fontFamily.regular,
+ fontSize: Typography.fontSize.md,
+ color: Colors.textSecondary,
+ textAlign: "center",
+ marginBottom: Spacing.xl,
+ },
+ uploadContainer: {
+ backgroundColor: Colors.backgroundAlt,
+ borderWidth: 2,
+ borderColor: Colors.border,
+ borderStyle: "dashed",
+ borderRadius: 12,
+ padding: Spacing.xl,
+ marginBottom: Spacing.md,
+ minHeight: 200,
+ },
+ uploadContent: {
+ alignItems: "center",
+ },
+ imagePreviewContainer: {
+ alignItems: "center",
+ width: '100%',
+ },
+ imagePreview: {
+ width: '100%',
+ height: 150,
+ borderRadius: 12,
+ marginBottom: Spacing.sm,
+ resizeMode: 'contain',
+ },
+ imageOverlay: {
+ position: 'absolute',
+ top: 0,
+ right: 20,
+ marginRight: -20, // Half of image width minus half of overlay
+ backgroundColor: 'rgba(0, 0, 0, 0.4)',
+ borderRadius: 17,
+ width: 34,
+ height: 34,
+ justifyContent: 'center',
+ alignItems: 'center',
+ padding:2
+ },
+ uploadText: {
+ fontFamily: Typography.fontFamily.medium,
+ fontSize: Typography.fontSize.md,
+ color: Colors.textPrimary,
+ marginTop: Spacing.sm,
+ },
+ uploadSubtext: {
+ fontFamily: Typography.fontFamily.regular,
+ fontSize: Typography.fontSize.sm,
+ color: Colors.textSecondary,
+ marginTop: Spacing.xs,
+ },
+ uploadedText: {
+ fontFamily: Typography.fontFamily.medium,
+ fontSize: Typography.fontSize.md,
+ color: Colors.success,
+ marginTop: Spacing.sm,
+ },
+ fileName: {
+ fontFamily: Typography.fontFamily.regular,
+ fontSize: Typography.fontSize.sm,
+ color: Colors.textSecondary,
+ marginTop: Spacing.xs,
+ textAlign: "center",
+ maxWidth: '80%',
+ },
+ fileDetails: {
+ flexDirection: "row",
+ alignItems: "center",
+ marginTop: Spacing.xs,
+ },
+ fileType: {
+ fontFamily: Typography.fontFamily.regular,
+ fontSize: Typography.fontSize.xs,
+ color: Colors.textMuted,
+ },
+ fileSize: {
+ fontFamily: Typography.fontFamily.regular,
+ fontSize: Typography.fontSize.xs,
+ color: Colors.textMuted,
+ },
+ changeButton: {
+ alignSelf: "center",
+ marginBottom: Spacing.xl,
+ },
+ changeButtonText: {
+ fontFamily: Typography.fontFamily.medium,
+ fontSize: Typography.fontSize.md,
+ color: Colors.primary,
+ },
+ button: {
+ marginTop: Spacing.md,
+ },
+ buttonDisabled: {
+ opacity: 0.5,
+ },
+})
+
+export default DocumentUploadScreen
+
+/*
+ * End of File: DocumentUploadScreen.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
\ No newline at end of file
diff --git a/app/modules/Auth/components/EmailScreen.tsx b/app/modules/Auth/components/EmailScreen.tsx
new file mode 100644
index 0000000..0ce5b28
--- /dev/null
+++ b/app/modules/Auth/components/EmailScreen.tsx
@@ -0,0 +1,127 @@
+"use client"
+
+/*
+ * File: EmailScreen.tsx
+ * Description: Email input screen for onboarding flow with gradient background.
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+import type React from "react"
+import { useState } from "react"
+import { View, Text, StyleSheet, TextInput } from "react-native"
+import { Button } from "../../../../shared/src/components/Button"
+import OnboardingContainer from "./OnboardingContainer"
+import IconContainer from "./IconContainer"
+import { Colors, Spacing, Typography } from "../../../../shared/src/theme"
+import { showError } from "../../../../shared/src/utils/helpers/Toast"
+import { validateEmail } from "../../../../shared/src/utils/validation/validators"
+import Icon from "react-native-vector-icons/Feather"
+
+interface EmailScreenProps {
+ onContinue: (email: string) => void
+ onBack: () => void
+}
+
+const EmailScreen: React.FC = ({ onContinue, onBack }) => {
+ const [email, setEmail] = useState("")
+ const [loading, setLoading] = useState(false)
+
+ const handleContinue = () => {
+ if (!email.trim()) {
+ showError("Validation Error", "Email address is required.")
+ return
+ }
+ if (!validateEmail(email)) {
+ showError("Validation Error", "Please enter a valid email address.")
+ return
+ }
+
+ setLoading(true)
+ setTimeout(() => {
+ setLoading(false)
+ onContinue(email)
+ }, 1000)
+ }
+
+ return (
+
+
+
+ What's your email?
+ Please enter your email address.
+
+
+
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ title: {
+ fontFamily: Typography.fontFamily.bold,
+ fontSize: Typography.fontSize.title,
+ color: Colors.textPrimary,
+ textAlign: "center",
+ marginBottom: Spacing.sm,
+ },
+ subtitle: {
+ fontFamily: Typography.fontFamily.regular,
+ fontSize: Typography.fontSize.md,
+ color: Colors.textSecondary,
+ textAlign: "center",
+ marginBottom: Spacing.xl,
+ },
+ inputContainer: {
+ flexDirection: "row",
+ alignItems: "center",
+ backgroundColor: Colors.inputBackground,
+ borderWidth: 1,
+ borderColor: Colors.border,
+ borderRadius: 12,
+ marginBottom: Spacing.xl,
+ paddingHorizontal: Spacing.md,
+ paddingVertical: 2,
+ shadowColor: Colors.backButtonShadow,
+ shadowOffset: {
+ width: 0,
+ height: 1,
+ },
+ shadowOpacity: 0.05,
+ shadowRadius: 2,
+ elevation: 1,
+ },
+ inputIcon: {
+ marginRight: Spacing.sm,
+ },
+ inputField: {
+ flex: 1,
+ paddingVertical: Spacing.md,
+ fontSize: Typography.fontSize.md,
+ color: Colors.textPrimary,
+ },
+ button: {
+ marginTop: Spacing.md,
+ paddingVertical:Spacing.lg,
+ },
+})
+
+export default EmailScreen
+
+/*
+ * End of File: EmailScreen.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
diff --git a/app/modules/Auth/components/HospitalSelectionScreen.tsx b/app/modules/Auth/components/HospitalSelectionScreen.tsx
new file mode 100644
index 0000000..b5fa7c6
--- /dev/null
+++ b/app/modules/Auth/components/HospitalSelectionScreen.tsx
@@ -0,0 +1,231 @@
+"use client"
+
+/*
+ * File: HospitalSelectionScreen.tsx
+ * Description: Hospital selection screen with dropdown using react-native-element-dropdown.
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+import type React from "react"
+import { useState } from "react"
+import { View, Text, StyleSheet, TouchableWithoutFeedback, Keyboard } from "react-native"
+import { Dropdown } from "react-native-element-dropdown"
+import { Button } from "../../../../shared/src/components/Button"
+import { Colors, Spacing, Typography } from "../../../../shared/src/theme"
+import { showError } from "../../../../shared/src/utils/helpers/Toast"
+import Icon from "react-native-vector-icons/Feather"
+import OnboardingContainer from "./OnboardingContainer"
+import { selectHospitals } from "../redux"
+import { useSelector } from "react-redux"
+
+interface HospitalSelectionScreenProps {
+ onContinue: (hospitalId: string) => void
+ onBack: () => void
+}
+
+const hospitalData = [
+ { label: "City General Hospital", value: "city_general" },
+ { label: "St. Mary's Medical Center", value: "st_marys" },
+ { label: "University Hospital", value: "university" },
+ { label: "Regional Medical Center", value: "regional" },
+ { label: "Metropolitan Hospital", value: "metropolitan" },
+ { label: "Community Health Center", value: "community" },
+ { label: "Children's Hospital", value: "childrens" },
+ { label: "Veterans Medical Center", value: "veterans" },
+]
+
+const HospitalSelectionScreen: React.FC = ({ onContinue, onBack }) => {
+ const [selectedHospital, setSelectedHospital] = useState(null)
+ const [loading, setLoading] = useState(false)
+ const [isFocus, setIsFocus] = useState(false)
+ const hospitals :any=useSelector(selectHospitals)
+
+ const handleContinue = () => {
+ if (!selectedHospital) {
+ showError("Validation Error", "Please select a hospital to continue.")
+ return
+ }
+
+ setLoading(true)
+ setTimeout(() => {
+ setLoading(false)
+ onContinue(selectedHospital)
+ }, 1000)
+ }
+
+ const renderLabel = () => {
+ if (selectedHospital || isFocus) {
+ return Select Hospital
+ }
+ return null
+ }
+
+ return (
+
+
+
+ {/*
+
+ */}
+
+
+
+
+
+
+
+
+ Select Hospital
+ Choose the hospital you're affiliated with.
+
+
+ {renderLabel()}
+ setIsFocus(true)}
+ onBlur={() => setIsFocus(false)}
+ onChange={(item) => {
+ console.log('item',item)
+ setSelectedHospital(item.hospital_id)
+ setIsFocus(false)
+ }}
+ renderLeftIcon={() => (
+
+ )}
+ />
+
+
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ // backgroundColor: Colors.background,
+ },
+ header: {
+ paddingTop: Spacing.xl,
+ paddingHorizontal: Spacing.lg,
+ paddingBottom: Spacing.md,
+ },
+ content: {
+ flex: 1,
+ // paddingHorizontal: Spacing.lg,
+ justifyContent: "center",
+ },
+ iconContainer: {
+ alignItems: "center",
+ marginBottom: Spacing.xl,
+ },
+ iconWrapper: {
+ width: 80,
+ height: 80,
+ borderRadius: 20,
+ backgroundColor: Colors.backgroundAlt,
+ justifyContent: "center",
+ alignItems: "center",
+ borderWidth: 1,
+ borderColor: Colors.border,
+ },
+ title: {
+ fontFamily: Typography.fontFamily.bold,
+ fontSize: Typography.fontSize.title,
+ color: Colors.textPrimary,
+ textAlign: "center",
+ marginBottom: Spacing.sm,
+ },
+ subtitle: {
+ fontFamily: Typography.fontFamily.regular,
+ fontSize: Typography.fontSize.md,
+ color: Colors.textSecondary,
+ textAlign: "center",
+ marginBottom: Spacing.xl,
+ },
+ dropdownContainer: {
+ marginBottom: Spacing.xl,
+ },
+ label: {
+ position: "absolute",
+ // backgroundColor: Colors.background,
+ left: 0,
+ top: -20,
+ zIndex: 999,
+ paddingHorizontal: 8,
+ fontSize: Typography.fontSize.sm,
+ color: Colors.textSecondary,
+ fontFamily: Typography.fontFamily.bold,
+ },
+ dropdown: {
+ height: 50,
+ borderColor: Colors.border,
+ borderWidth: 1,
+ borderRadius: 12,
+ paddingHorizontal: Spacing.md,
+ backgroundColor: Colors.backgroundAlt,
+ },
+ icon: {
+ marginRight: Spacing.sm,
+ },
+ placeholderStyle: {
+ fontSize: Typography.fontSize.md,
+ color: Colors.textMuted,
+ fontFamily: Typography.fontFamily.regular,
+ },
+ selectedTextStyle: {
+ fontSize: Typography.fontSize.md,
+ color: Colors.textPrimary,
+ fontFamily: Typography.fontFamily.regular,
+ },
+ iconStyle: {
+ width: 20,
+ height: 20,
+ },
+ inputSearchStyle: {
+ height: 40,
+ fontSize: Typography.fontSize.md,
+ color: Colors.textPrimary,
+ fontFamily: Typography.fontFamily.regular,
+ },
+ button: {
+ marginTop: Spacing.md,
+ },
+ buttonDisabled: {
+ opacity: 0.5,
+ },
+})
+
+export default HospitalSelectionScreen
+
+/*
+ * End of File: HospitalSelectionScreen.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
diff --git a/app/modules/Auth/components/IconContainer.tsx b/app/modules/Auth/components/IconContainer.tsx
new file mode 100644
index 0000000..02f886b
--- /dev/null
+++ b/app/modules/Auth/components/IconContainer.tsx
@@ -0,0 +1,76 @@
+"use client"
+
+/*
+ * File: IconContainer.tsx
+ * Description: Reusable icon container component for onboarding screens.
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+import type React from "react"
+import { View, StyleSheet } from "react-native"
+import Icon from "react-native-vector-icons/Feather"
+import { Colors, Spacing } from "../../../../shared/src/theme"
+
+interface IconContainerProps {
+ iconName: string
+ iconSize?: number
+ containerSize?: number
+ backgroundColor?: string
+ iconColor?: string
+}
+
+const IconContainer: React.FC = ({
+ iconName,
+ iconSize = 32,
+ containerSize = 80,
+ backgroundColor = Colors.inputBackground,
+ iconColor = Colors.primary,
+}) => {
+ return (
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: "center",
+ marginBottom: Spacing.xl,
+ },
+ iconWrapper: {
+ justifyContent: "center",
+ alignItems: "center",
+ borderWidth: 1,
+ borderColor: Colors.border,
+ shadowColor: Colors.backButtonShadow,
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 2,
+ },
+})
+
+export default IconContainer
+
+/*
+ * End of File: IconContainer.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
diff --git a/app/modules/Auth/components/NameScreen.tsx b/app/modules/Auth/components/NameScreen.tsx
new file mode 100644
index 0000000..2856a00
--- /dev/null
+++ b/app/modules/Auth/components/NameScreen.tsx
@@ -0,0 +1,164 @@
+"use client"
+
+/*
+ * File: NameScreen.tsx
+ * Description: Name input screen with gradient background and shared components.
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+import type React from "react"
+import { useState } from "react"
+import { View, Text, StyleSheet, TextInput } from "react-native"
+import { Button } from "../../../../shared/src/components/Button"
+import OnboardingContainer from "./OnboardingContainer"
+import IconContainer from "./IconContainer"
+import { Colors, Spacing, Typography } from "../../../../shared/src/theme"
+import { showError } from "../../../../shared/src/utils/helpers/Toast"
+import Icon from "react-native-vector-icons/Feather"
+
+interface NameScreenProps {
+ onContinue: (firstName: string, lastName: string, username: string) => void
+ onBack: () => void
+}
+
+const NameScreen: React.FC = ({ onContinue, onBack }) => {
+ const [firstName, setFirstName] = useState("")
+ const [lastName, setLastName] = useState("")
+ const [username, setUsername] = useState("")
+ const [loading, setLoading] = useState(false)
+
+ const handleContinue = () => {
+ if (!firstName.trim() || !lastName.trim() || !username.trim()) {
+ showError("Validation Error", "First name, last name, and username are required.")
+ return
+ }
+
+ if (firstName.trim().length < 2 ) {
+ showError("Validation Error", "Names must be at least 2 characters long.")
+ return
+ }
+
+ if (username.trim().length < 3) {
+ showError("Validation Error", "Username must be at least 3 characters long.")
+ return
+ }
+
+ // Basic username validation (alphanumeric and underscores only)
+ const usernameRegex = /^[a-zA-Z0-9_]+$/
+ if (!usernameRegex.test(username.trim())) {
+ showError("Validation Error", "Username can only contain letters, numbers, and underscores.")
+ return
+ }
+
+ setLoading(true)
+ setTimeout(() => {
+ setLoading(false)
+ onContinue(firstName.trim(), lastName.trim(), username.trim())
+ }, 1000)
+ }
+
+ return (
+
+
+
+ What's your name?
+ Please enter your details to get started.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ title: {
+ fontFamily: Typography.fontFamily.bold,
+ fontSize: Typography.fontSize.title,
+ color: Colors.textPrimary,
+ textAlign: "center",
+ marginBottom: Spacing.sm,
+ },
+ subtitle: {
+ fontFamily: Typography.fontFamily.regular,
+ fontSize: Typography.fontSize.md,
+ color: Colors.textSecondary,
+ textAlign: "center",
+ marginBottom: Spacing.xl,
+ },
+ inputContainer: {
+ flexDirection: "row",
+ alignItems: "center",
+ backgroundColor: Colors.inputBackground,
+ borderWidth: 1,
+ borderColor: Colors.border,
+ borderRadius: 12,
+ marginBottom: Spacing.md,
+ paddingHorizontal: Spacing.md,
+ paddingVertical: 2,
+ shadowColor: Colors.backButtonShadow,
+ shadowOffset: {
+ width: 0,
+ height: 1,
+ },
+ shadowOpacity: 0.05,
+ shadowRadius: 2,
+ elevation: 1,
+ },
+ inputIcon: {
+ marginRight: Spacing.sm,
+ },
+ inputField: {
+ flex: 1,
+ paddingVertical: Spacing.md,
+ fontSize: Typography.fontSize.md,
+ color: Colors.textPrimary,
+ },
+ button: {
+ marginTop: Spacing.xl,
+ },
+})
+
+export default NameScreen
+
+/*
+ * End of File: NameScreen.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
\ No newline at end of file
diff --git a/app/modules/Auth/components/OnboardingContainer.tsx b/app/modules/Auth/components/OnboardingContainer.tsx
new file mode 100644
index 0000000..2e15b91
--- /dev/null
+++ b/app/modules/Auth/components/OnboardingContainer.tsx
@@ -0,0 +1,64 @@
+"use client"
+
+/*
+ * File: OnboardingContainer.tsx
+ * Description: Shared container component for onboarding screens with gradient background.
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+import type React from "react"
+import { View, StyleSheet, TouchableWithoutFeedback, Keyboard } from "react-native"
+import LinearGradient from "react-native-linear-gradient"
+import { Colors, Spacing } from "../../../../shared/src/theme"
+import BackButton from "./BackButton"
+
+interface OnboardingContainerProps {
+ children: React.ReactNode
+ onBack: () => void
+ showBackButton?: boolean
+}
+
+const OnboardingContainer: React.FC = ({ children, onBack, showBackButton = true }) => {
+ return (
+
+
+ {showBackButton && (
+
+
+
+ )}
+ {children}
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ header: {
+ paddingTop: Spacing.md,
+ paddingHorizontal: Spacing.lg,
+ paddingBottom: Spacing.md,
+ },
+ content: {
+ flex: 1,
+ paddingHorizontal: Spacing.lg,
+ justifyContent: "center",
+ },
+})
+
+export default OnboardingContainer
+
+/*
+ * End of File: OnboardingContainer.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
diff --git a/app/modules/Auth/components/PasswordScreen.tsx b/app/modules/Auth/components/PasswordScreen.tsx
new file mode 100644
index 0000000..f74af0e
--- /dev/null
+++ b/app/modules/Auth/components/PasswordScreen.tsx
@@ -0,0 +1,158 @@
+"use client"
+
+/*
+ * File: PasswordScreen.tsx
+ * Description: Password creation screen with gradient background and shared components.
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+import type React from "react"
+import { useState } from "react"
+import { View, Text, StyleSheet, TextInput, TouchableOpacity } from "react-native"
+import { Button } from "../../../../shared/src/components/Button"
+import OnboardingContainer from "./OnboardingContainer"
+import IconContainer from "./IconContainer"
+import { Colors, Spacing, Typography } from "../../../../shared/src/theme"
+import { showError } from "../../../../shared/src/utils/helpers/Toast"
+import Icon from "react-native-vector-icons/Feather"
+
+interface PasswordScreenProps {
+ onContinue: (password: string) => void
+ onBack: () => void
+}
+
+const PasswordScreen: React.FC = ({ onContinue, onBack }) => {
+ const [password, setPassword] = useState("")
+ const [confirmPassword, setConfirmPassword] = useState("")
+ const [isPasswordVisible, setPasswordVisible] = useState(false)
+ const [isConfirmPasswordVisible, setConfirmPasswordVisible] = useState(false)
+ const [loading, setLoading] = useState(false)
+
+ const validatePassword = (pwd: string): boolean => {
+ return pwd.length >= 8
+ }
+
+ const handleContinue = () => {
+ if (!password.trim() || !confirmPassword.trim()) {
+ showError("Validation Error", "Both password fields are required.")
+ return
+ }
+
+ if (!validatePassword(password)) {
+ showError("Validation Error", "Password must be at least 8 characters long.")
+ return
+ }
+
+ if (password !== confirmPassword) {
+ showError("Validation Error", "Passwords do not match.")
+ return
+ }
+
+ setLoading(true)
+ setTimeout(() => {
+ setLoading(false)
+ onContinue(password)
+ }, 1000)
+ }
+
+ return (
+
+
+
+ Create a password
+ Password must be at least 8 characters
+
+
+
+
+ setPasswordVisible(!isPasswordVisible)} style={styles.eyeIcon}>
+
+
+
+
+
+
+
+ setConfirmPasswordVisible(!isConfirmPasswordVisible)} style={styles.eyeIcon}>
+
+
+
+
+
+
+ )
+}
+
+const styles = StyleSheet.create({
+ title: {
+ fontFamily: Typography.fontFamily.bold,
+ fontSize: Typography.fontSize.title,
+ color: Colors.textPrimary,
+ textAlign: "center",
+ marginBottom: Spacing.sm,
+ },
+ subtitle: {
+ fontFamily: Typography.fontFamily.regular,
+ fontSize: Typography.fontSize.md,
+ color: Colors.textSecondary,
+ textAlign: "center",
+ marginBottom: Spacing.xl,
+ },
+ inputContainer: {
+ flexDirection: "row",
+ alignItems: "center",
+ backgroundColor: Colors.inputBackground,
+ borderWidth: 1,
+ borderColor: Colors.border,
+ borderRadius: 12,
+ marginBottom: Spacing.md,
+ paddingHorizontal: Spacing.md,
+ paddingVertical: 2,
+ shadowColor: Colors.backButtonShadow,
+ shadowOffset: {
+ width: 0,
+ height: 1,
+ },
+ shadowOpacity: 0.05,
+ shadowRadius: 2,
+ elevation: 1,
+ },
+ inputIcon: {
+ marginRight: Spacing.sm,
+ },
+ inputField: {
+ flex: 1,
+ paddingVertical: Spacing.md,
+ fontSize: Typography.fontSize.md,
+ color: Colors.textPrimary,
+ },
+ eyeIcon: {
+ paddingLeft: Spacing.sm,
+ },
+ button: {
+ marginTop: Spacing.xl,
+ },
+})
+
+export default PasswordScreen
+
+/*
+ * End of File: PasswordScreen.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
diff --git a/app/modules/Auth/navigation/AuthNavigator.tsx b/app/modules/Auth/navigation/AuthNavigator.tsx
index 3882bb7..0758958 100644
--- a/app/modules/Auth/navigation/AuthNavigator.tsx
+++ b/app/modules/Auth/navigation/AuthNavigator.tsx
@@ -9,10 +9,12 @@ import React from 'react';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { LoginScreen, SetupBiometricScreen } from '../screens';
import { Colors } from '../../../../shared/src/theme';
+import SignUpScreen from '../screens/SignUpScreen';
export type AuthStackParamList = {
Login: undefined;
SetupBiometric: undefined;
+ SignUpScreen:undefined;
};
const Stack = createNativeStackNavigator();
@@ -37,7 +39,12 @@ const AuthNavigator: React.FC = () => (
+
);
diff --git a/app/modules/Auth/redux/authActions.ts b/app/modules/Auth/redux/authActions.ts
index d0576b1..f8268f1 100644
--- a/app/modules/Auth/redux/authActions.ts
+++ b/app/modules/Auth/redux/authActions.ts
@@ -8,6 +8,7 @@
import { createAsyncThunk } from '@reduxjs/toolkit';
import { loginStart, loginSuccess, loginFailure } from './authSlice';
import { authAPI } from '../services/authAPI';
+import { showError, showSuccess } from '../../../../shared/src/utils/helpers/Toast';
/**
* Thunk to login user
@@ -19,6 +20,12 @@ export const login = createAsyncThunk(
dispatch(loginStart());
const response:any = await authAPI.login(credentials.email, credentials.password,'web');
console.log('user response',response)
+ if(response.data.message&& !response.data.success){
+ showError(response.data.message)
+ }
+ if(response.data.message&& response.data.success){
+ showSuccess(response.data.message)
+ }
if (response.ok && response.data &&response.data.data) {
//@ts-ignore
dispatch(loginSuccess({...response.data.data.user,access_token:response.data.data.access_token}));
@@ -33,6 +40,7 @@ export const login = createAsyncThunk(
}
);
+
/*
* End of File: authActions.ts
* Design & Developed by Tech4Biz Solutions
diff --git a/app/modules/Auth/redux/authSelectors.ts b/app/modules/Auth/redux/authSelectors.ts
index 9fd5547..792c196 100644
--- a/app/modules/Auth/redux/authSelectors.ts
+++ b/app/modules/Auth/redux/authSelectors.ts
@@ -11,6 +11,7 @@ export const selectUser = (state: RootState) => state.auth.user;
export const selectAuthLoading = (state: RootState) => state.auth.loading;
export const selectAuthError = (state: RootState) => state.auth.error;
export const selectIsAuthenticated = (state: RootState) => state.auth.isAuthenticated;
+export const selectHospitals = (state: RootState) => state.hospital.hospitals;
/*
* End of File: authSelectors.ts
diff --git a/app/modules/Auth/redux/hospitalSlice.ts b/app/modules/Auth/redux/hospitalSlice.ts
new file mode 100644
index 0000000..832295e
--- /dev/null
+++ b/app/modules/Auth/redux/hospitalSlice.ts
@@ -0,0 +1,55 @@
+/*
+ * File: authSlice.ts
+ * Description: Redux slice for Auth state management
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+
+
+
+interface Hospital {
+ hospital_id: string | null;
+ hospital_name: string | null;
+}
+
+// Auth state type
+interface HospitalState {
+ hospitals: Hospital[] | null;
+ loading: boolean;
+ error: string | null;
+}
+
+const initialState: HospitalState = {
+ hospitals: null,
+ loading: false,
+ error: null,
+};
+
+/**
+ * Auth slice for managing authentication state
+ */
+const hospitalSlice = createSlice({
+ name: 'hospital',
+ initialState,
+ reducers: {
+
+ setHospitals(state, action: PayloadAction) {
+ state.hospitals = action.payload;
+ state.loading = false;
+ },
+ },
+});
+
+export const {
+ setHospitals
+} = hospitalSlice.actions;
+
+export default hospitalSlice.reducer;
+
+/*
+ * End of File: authSlice.ts
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
\ No newline at end of file
diff --git a/app/modules/Auth/screens/LoginScreen.tsx b/app/modules/Auth/screens/LoginScreen.tsx
index 7ed0651..e5f8abb 100644
--- a/app/modules/Auth/screens/LoginScreen.tsx
+++ b/app/modules/Auth/screens/LoginScreen.tsx
@@ -22,6 +22,7 @@ import { login } from '../redux/authActions';
import { showError } from '../../../../shared/src/utils/helpers/Toast';
import { validateEmail } from '../../../../shared/src/utils/validation/validators';
import Icon from 'react-native-vector-icons/Feather';
+import { useNavigation } from '@react-navigation/native';
const LoginScreen: React.FC = () => {
const [email, setEmail] = useState('');
@@ -29,6 +30,7 @@ const LoginScreen: React.FC = () => {
const [isPasswordVisible, setPasswordVisible] = useState(false);
const [loading, setLoading] = useState(false); // Add loading state
const dispatch = useAppDispatch();
+ const navigation=useNavigation();
const handleLogin = () => {
if (!email.trim() || !password.trim()) {
@@ -52,7 +54,7 @@ const LoginScreen: React.FC = () => {
return (
- Radiologist Portal
+ Radiologist
{/* Email Input */}
@@ -92,6 +94,14 @@ const LoginScreen: React.FC = () => {
style={styles.button}
loading={loading} // Pass loading state to button
/>
+ -----OR------
+
+
);
diff --git a/app/modules/Auth/screens/SignUpScreen.tsx b/app/modules/Auth/screens/SignUpScreen.tsx
new file mode 100644
index 0000000..569b823
--- /dev/null
+++ b/app/modules/Auth/screens/SignUpScreen.tsx
@@ -0,0 +1,192 @@
+"use client"
+
+/*
+ * File: SignUpScreen.tsx
+ * Description: Main signup screen that manages the onboarding flow through multiple steps.
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
+
+import type React from "react"
+import { useEffect, useState } from "react"
+import { View, StyleSheet, StatusBar, Platform } from "react-native"
+import { Colors } from "../../../../shared/src/theme"
+import { useAppDispatch } from "../../../redux/hooks"
+import { showError, showSuccess } from "../../../../shared/src/utils/helpers/Toast"
+
+// Import all onboarding screens
+import EmailScreen from "../components/EmailScreen"
+import PasswordScreen from "../components/PasswordScreen"
+import NameScreen from "../components/NameScreen"
+import DocumentUploadScreen from "../components/DocumentUploadScreen"
+import HospitalSelectionScreen from "../components/HospitalSelectionScreen"
+import { authAPI } from "../services/authAPI"
+import { setHospitals } from "../redux/hospitalSlice"
+
+interface SignUpData {
+ email: string
+ password: string
+ first_name: string
+ last_name: string
+ username:string
+ id_photo_url: {}
+ hospital_id: string
+}
+
+interface SignUpScreenProps {
+ navigation: any
+}
+
+const SignUpScreen: React.FC = ({ navigation }) => {
+ const [currentStep, setCurrentStep] = useState(0)
+ const [signUpData, setSignUpData] = useState>({})
+ const dispatch = useAppDispatch()
+
+ const steps = ["email", "password", "name", "document", "hospital"]
+
+ const handleEmailContinue = (email: string) => {
+ setSignUpData((prev) => ({ ...prev, email }))
+ setCurrentStep(1)
+ }
+ useEffect(()=>{
+ fetchHospitals();
+ },[])
+ console.log('signup called')
+ const fetchHospitals= async ()=>{
+
+ try {
+
+ const response:any = await authAPI.gethospitals();
+ console.log('hospital response',response)
+ if(response.ok && response.data &&response.data.data) {
+ //@ts-ignore
+ dispatch(setHospitals(response.data.data))
+ } else {
+ showError('error while fetching hospital list')
+ }
+ } catch (error: any) {
+ console.log('error',error)
+ }
+
+ }
+ const onSignUpComplete=async (payload:SignUpData)=>{
+ console.log('final payload',payload);
+ try {
+ const formData = new FormData();
+ let role='radiologist'
+
+ formData.append('email',payload.email);
+ formData.append('password', payload.password);
+ formData.append('first_name', payload.first_name);
+ formData.append('last_name', payload.last_name);
+ formData.append('username', payload.username);
+ formData.append('dashboard_role',role);
+ formData.append('hospital_id', payload.hospital_id);
+
+ // Attach file (Node.js or React Native: use File or Blob API accordingly)
+ const filePath = payload.id_photo_url.uri;
+ const file = {
+ uri: Platform.OS === 'android' ? filePath! : filePath!.replace('file://', ''), // for React Native
+ name: 'id_photo',
+ type: 'image/jpg',
+ };
+ formData.append('id_photo_url', file);
+ console.log('payload prepared',formData);
+ const response:any = await authAPI.signup(formData);
+ console.log('signup response',response)
+ if(response.ok && response.data && response.data.success ) {
+ //@ts-ignore
+ showSuccess('Sign Up Successfully')
+ navigation.navigate('Login');
+ // dispatch(setHospitals(response.data.data))
+ } else {
+ showError('error while signup')
+ }
+ } catch (error: any) {
+ console.log('error',error)
+ }
+ }
+ const onBack=()=>{
+ navigation.goBack()
+ }
+ const handlePasswordContinue = (password: string) => {
+ setSignUpData((prev) => ({ ...prev, password }))
+ setCurrentStep(2)
+ }
+
+ const handleNameContinue = (firstName: string, lastName: string,userName:string) => {
+ setSignUpData((prev) => ({ ...prev, first_name : firstName, last_name : lastName ,username: userName}))
+ setCurrentStep(3)
+ }
+
+ const handleDocumentContinue = (documentUri: string) => {
+ setSignUpData((prev) => ({ ...prev, id_photo_url:documentUri }))
+ setCurrentStep(4)
+ }
+
+ const handleHospitalContinue = (hospitalId: string) => {
+ const finalData: SignUpData = {
+ ...signUpData,
+ hospital_id:hospitalId,
+ } as SignUpData
+
+ setSignUpData(finalData)
+
+ // Show success message
+
+ // Call completion handler
+ if (onSignUpComplete) {
+ onSignUpComplete(finalData)
+ }
+
+ // Here you would typically dispatch a signup action
+ // dispatch(signUp(finalData))
+ }
+
+ const handleBack = () => {
+ if (currentStep > 0) {
+ setCurrentStep(currentStep - 1)
+ } else if (onBack) {
+ onBack()
+ }
+ }
+
+ const renderCurrentStep = () => {
+ switch (currentStep) {
+ case 0:
+ return
+ case 1:
+ return
+ case 2:
+ return
+ case 3:
+ return
+ case 4:
+ return
+ default:
+ return
+ }
+ }
+
+ return (
+
+
+ {renderCurrentStep()}
+
+ )
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: Colors.background,
+ },
+})
+
+export default SignUpScreen
+
+/*
+ * End of File: SignUpScreen.tsx
+ * Design & Developed by Tech4Biz Solutions
+ * Copyright (c) Spurrin Innovations. All rights reserved.
+ */
diff --git a/app/modules/Auth/services/authAPI.ts b/app/modules/Auth/services/authAPI.ts
index 7afe6a8..b18ef2b 100644
--- a/app/modules/Auth/services/authAPI.ts
+++ b/app/modules/Auth/services/authAPI.ts
@@ -11,7 +11,6 @@ import { buildHeaders } from '../../../../shared/src/utils/helpers/Api';
const api = create({
baseURL: BASE_URL, // TODO: Replace with actual endpoint
- timeout: 3000,
});
/**
@@ -19,6 +18,10 @@ const api = create({
*/
export const authAPI = {
login: (email: string, password: string,platform:string) => api.post('/api/auth/auth/login', { email, password,platform },buildHeaders()),
+ //fetch hospital list
+ gethospitals: () => api.get('/api/hospitals/hospitals/app_user/hospitals', {},buildHeaders()),
+ //user signup
+ signup: (formData:any) => api.post('/api/auth/auth/admin/create-user-fromapp', formData,buildHeaders({ contentType: 'multipart/form-data' })),
// Add more endpoints as needed
};
diff --git a/app/modules/Dashboard/redux/dashboardActions.ts b/app/modules/Dashboard/redux/dashboardActions.ts
index 759c0ca..cd9bd5b 100644
--- a/app/modules/Dashboard/redux/dashboardActions.ts
+++ b/app/modules/Dashboard/redux/dashboardActions.ts
@@ -37,7 +37,7 @@ export const fetchPatients = createAsyncThunk(
try {
dispatch(fetchCasesStart());
const response :any = await caseAPI.getPatients(payload);
- console.log('response i got',response)
+ // console.log('response i got',response)
if (response.ok && response.data && response.data.data) {
dispatch(fetchPatientsSuccess(response.data.data));
}
diff --git a/app/modules/Dashboard/screens/DashBoardDetail.tsx b/app/modules/Dashboard/screens/DashBoardDetail.tsx
index a64c858..603070c 100644
--- a/app/modules/Dashboard/screens/DashBoardDetail.tsx
+++ b/app/modules/Dashboard/screens/DashBoardDetail.tsx
@@ -364,7 +364,7 @@ const styles = StyleSheet.create({
backgroundColor: Colors.background,
},
content: {
- padding: Spacing.lg,
+ padding: Spacing.md,
},
summaryCard: {
marginBottom: Spacing.lg,
diff --git a/app/modules/Dashboard/screens/DashboardScreen.tsx b/app/modules/Dashboard/screens/DashboardScreen.tsx
index d8ac4cb..1eb90c0 100644
--- a/app/modules/Dashboard/screens/DashboardScreen.tsx
+++ b/app/modules/Dashboard/screens/DashboardScreen.tsx
@@ -101,7 +101,7 @@ const DashboardScreen: React.FC<{ navigation: any }> = ({ navigation }) => {
filteredCases.forEach(case_ => {
const type = case_.type;
- const modality = case_.patientdetails.Modality;
+ const modality = case_.patientdetails?.Modality;
stats[type].total += 1;
stats[type].modalityCounts[modality?.substring(1)] += 1;
@@ -151,7 +151,6 @@ const DashboardScreen: React.FC<{ navigation: any }> = ({ navigation }) => {
const color = getCaseColor(type);
if (stats.total === 0) return null; // Don't render card if no cases of this type
-console.log('counts data',stats.modalityCounts)
return (
@@ -187,7 +186,6 @@ console.log('counts data',stats.modalityCounts)
);
};
- console.log('patients data', patientData);
return (
diff --git a/app/modules/Dashboard/services/caseAPI.ts b/app/modules/Dashboard/services/caseAPI.ts
index 9f64edf..0dd3e96 100644
--- a/app/modules/Dashboard/services/caseAPI.ts
+++ b/app/modules/Dashboard/services/caseAPI.ts
@@ -23,7 +23,6 @@ export const caseAPI = {
getCases: (token:string) => api.get('/api/dicom/medpacks-sync/get-synced-medpacks-data',{},buildHeaders({token})),
// get patients data
getPatients: (token:string) =>{
- console.log('token',token)
return api.get('/api/dicom/medpacks-sync/get-synced-medpacks-data',{},buildHeaders({token}))
}
// Add more endpoints as needed
diff --git a/app/redux/rootReducer.ts b/app/redux/rootReducer.ts
index 081a795..9dec4b7 100644
--- a/app/redux/rootReducer.ts
+++ b/app/redux/rootReducer.ts
@@ -12,6 +12,7 @@ import settingsReducer from '../modules/Profile/redux/settingsSlice';
// Add other slices as needed, e.g.:
import dashboardReducer from '../modules/Dashboard/redux/dashboardSlice';
import authReducer from '../modules/Auth/redux/authSlice';
+import hospitalReducer from '../modules/Auth/redux/hospitalSlice';
// RootState type for use throughout the app
export type RootState = ReturnType;
@@ -21,7 +22,8 @@ const rootReducer = combineReducers({
profile: profileReducer,
settings: settingsReducer,
dashboard:dashboardReducer,
- auth:authReducer
+ auth:authReducer,
+ hospital:hospitalReducer
// Add other reducers here
});
diff --git a/app/redux/store.ts b/app/redux/store.ts
index 0296dcc..1d8c19e 100644
--- a/app/redux/store.ts
+++ b/app/redux/store.ts
@@ -7,16 +7,34 @@
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './rootReducer';
+import { persistStore, persistReducer, PersistConfig } from 'redux-persist';
+import storage from '@react-native-async-storage/async-storage';
+
+
+const persistConfig: PersistConfig = {
+ key: 'root',
+ storage,
+ version: 1,
+ whitelist: ['auth']
+};
// Configure the Redux store
-const store = configureStore({
- reducer: rootReducer,
- // You can add middleware, devTools, and other options here if needed
+const persistedReducer = persistReducer(persistConfig, rootReducer);
+
+export const store = configureStore({
+ reducer: persistedReducer,
+ middleware: (getDefaultMiddleware) =>
+ getDefaultMiddleware({
+ serializableCheck: {
+ ignoredActions: ['persist/PERSIST'], // Ignore persist actions to avoid the non-serializable warning
+ },
+ }),
});
+
// Export store and types for use throughout the app
export type AppDispatch = typeof store.dispatch;
-export default store;
+export const persistor = persistStore(store);
/*
* End of File: store.ts
diff --git a/package-lock.json b/package-lock.json
index b969a6d..cc2ad13 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,7 +24,9 @@
"react-native": "0.79.0",
"react-native-biometrics": "^3.0.1",
"react-native-blob-util": "^0.22.2",
+ "react-native-chart-kit": "^6.12.0",
"react-native-config": "^1.5.5",
+ "react-native-element-dropdown": "^2.12.4",
"react-native-gesture-handler": "^2.22.1",
"react-native-image-picker": "^7.2.3",
"react-native-keychain": "^10.0.0",
@@ -9781,7 +9783,6 @@
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
- "dev": true,
"license": "MIT"
},
"node_modules/lodash.debounce": {
@@ -11154,6 +11155,15 @@
"node": ">=8"
}
},
+ "node_modules/paths-js": {
+ "version": "0.4.11",
+ "resolved": "https://registry.npmjs.org/paths-js/-/paths-js-0.4.11.tgz",
+ "integrity": "sha512-3mqcLomDBXOo7Fo+UlaenG6f71bk1ZezPQy2JCmYHy2W2k5VKpP+Jbin9H0bjXynelTbglCqdFhSEkeIkKTYUA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.11.0"
+ }
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -11250,6 +11260,12 @@
"node": ">=8"
}
},
+ "node_modules/point-in-polygon": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
+ "integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw==",
+ "license": "MIT"
+ },
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@@ -11664,6 +11680,22 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/react-native-chart-kit": {
+ "version": "6.12.0",
+ "resolved": "https://registry.npmjs.org/react-native-chart-kit/-/react-native-chart-kit-6.12.0.tgz",
+ "integrity": "sha512-nZLGyCFzZ7zmX0KjYeeSV1HKuPhl1wOMlTAqa0JhlyW62qV/1ZPXHgT8o9s8mkFaGxdqbspOeuaa6I9jUQDgnA==",
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.17.13",
+ "paths-js": "^0.4.10",
+ "point-in-polygon": "^1.0.1"
+ },
+ "peerDependencies": {
+ "react": "> 16.7.0",
+ "react-native": ">= 0.50.0",
+ "react-native-svg": "> 6.4.1"
+ }
+ },
"node_modules/react-native-config": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/react-native-config/-/react-native-config-1.5.5.tgz",
@@ -11678,6 +11710,22 @@
}
}
},
+ "node_modules/react-native-element-dropdown": {
+ "version": "2.12.4",
+ "resolved": "https://registry.npmjs.org/react-native-element-dropdown/-/react-native-element-dropdown-2.12.4.tgz",
+ "integrity": "sha512-abZc5SVji9FIt7fjojRYrbuvp03CoeZJrgvezQoDoSOrpiTqkX69ix5m+j06W2AVncA0VWvbT+vCMam8SoVadw==",
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.17.21"
+ },
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/react-native-gesture-handler": {
"version": "2.27.1",
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.27.1.tgz",
diff --git a/package.json b/package.json
index 5c3d723..548ada8 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,9 @@
"react-native": "0.79.0",
"react-native-biometrics": "^3.0.1",
"react-native-blob-util": "^0.22.2",
+ "react-native-chart-kit": "^6.12.0",
"react-native-config": "^1.5.5",
+ "react-native-element-dropdown": "^2.12.4",
"react-native-gesture-handler": "^2.22.1",
"react-native-image-picker": "^7.2.3",
"react-native-keychain": "^10.0.0",
diff --git a/shared/src/components/Button/Button.tsx b/shared/src/components/Button/Button.tsx
index dac1cd3..54ae88c 100644
--- a/shared/src/components/Button/Button.tsx
+++ b/shared/src/components/Button/Button.tsx
@@ -43,9 +43,9 @@ const Button: React.FC = ({
const styles = StyleSheet.create({
button: {
backgroundColor: Colors.primary,
- paddingVertical: Spacing.md,
+ paddingVertical: Spacing.lg,
paddingHorizontal: Spacing.lg,
- borderRadius: BorderRadius.md,
+ borderRadius: BorderRadius.xl,
alignItems: 'center',
justifyContent: 'center',
},
diff --git a/shared/src/theme/colors.ts b/shared/src/theme/colors.ts
index 6554061..17d5947 100644
--- a/shared/src/theme/colors.ts
+++ b/shared/src/theme/colors.ts
@@ -27,6 +27,33 @@ export const Colors = {
inactiveState: '#F3F4F6', // Light Gray
shadow: 'rgba(91, 124, 230, 0.1)', // Blue Shadow
gradient: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', // Gradient Background
+
+ // Primary colors
+
+ primaryDark: "#7C3AED",
+ primaryLight: "#A78BFA",
+
+ // Gradient colors
+ gradientStart: "#E0E7FF",
+ gradientMiddle: "#DDD6FE",
+ gradientEnd: "#E9D5FF",
+
+ // Background colors
+ backgroundGradient: ["#E0E7FF", "#DDD6FE", "#E9D5FF"],
+
+ // Text colors
+
+ textOnPrimary: "#FFFFFF",
+
+ // Border and input colors
+ borderFocus: "#8B5CF6",
+ inputBackground: "#FFFFFF",
+
+
+
+ // Back button
+ backButtonBackground: "#FFFFFF",
+ backButtonShadow: "rgba(0, 0, 0, 0.1)",
};
// Usage: import { Colors } from './colors';