code generated for dashboard case view ,profile and auth section
This commit is contained in:
parent
c7288f54a8
commit
20ab5d9d21
3
.cursor/rules/folderstructure.mdc
Normal file
3
.cursor/rules/folderstructure.mdc
Normal file
@ -0,0 +1,3 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
734
README.md
734
README.md
@ -1,698 +1,52 @@
|
||||
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
|
||||
# NeoScan Radiologist App
|
||||
|
||||
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"
|
||||
A next-generation radiology workflow app designed for rapid, AI-assisted case review and reporting. Built with a modular architecture and a Clinical Blue Interface theme.
|
||||
|
||||
STEP 3: Report Generation
|
||||
Voice-to-Text → "Acute subdural hemorrhage..."
|
||||
↓
|
||||
Review Text → Edit if needed → Submit Report
|
||||
↓
|
||||
Preliminary Report Sent (< 5 minutes)
|
||||
## Features
|
||||
- Modular structure: Auth, Dashboard, Case Review, Reporting, Notifications, Analytics, Profile
|
||||
- Clinical Blue Interface theme
|
||||
- Biometric and PIN authentication
|
||||
- Real-time case queue with priority sorting
|
||||
- DICOM viewer with AI overlays
|
||||
- Voice-to-text reporting
|
||||
- Push notifications for critical findings
|
||||
- Redux state management
|
||||
|
||||
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 |
|
||||
+------------------------+
|
||||
|
||||
5.1 React Native 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
|
||||
|
||||
|
||||
Key Features:
|
||||
|
||||
Real-time WebSocket connections
|
||||
Offline caching for critical cases
|
||||
Biometric authentication for radiologists
|
||||
Performance monitoring with timing metrics
|
||||
|
||||
Radiologist App Options
|
||||
Option 1: "Clinical Gray Spectrum"
|
||||
|
||||
Primary: #2C3E50 (Dark Blue-Gray)
|
||||
Secondary: #34495E (Medium Blue-Gray)
|
||||
Tertiary: #95A5A6 (Light Gray)
|
||||
Text Primary: #2C3E50 (Dark Blue-Gray)
|
||||
Text Secondary: #7F8C8D (Medium Gray)
|
||||
Text Muted: #BDC3C7 (Light Gray)
|
||||
Background: #F8F9FA (Light Gray)
|
||||
Background Alt: #E9ECEF (Darker Light Gray)
|
||||
Success: #27AE60 (Green)
|
||||
Warning: #F39C12 (Orange)
|
||||
Error: #E74C3C (Red)
|
||||
Info: #3498DB (Blue)
|
||||
|
||||
Option 2: "Deep Tech Purple"
|
||||
|
||||
Primary: #6C5CE7 (Modern Purple)
|
||||
Secondary: #A29BFE (Light Purple)
|
||||
Tertiary: #DDD6FE (Very Light Purple)
|
||||
Text Primary: #2D3436 (Charcoal)
|
||||
Text Secondary: #636E72 (Gray)
|
||||
Text Muted: #B2BEC3 (Light Gray)
|
||||
Background: #FFFFFF (White)
|
||||
Background Alt: #F8F7FF (Very Light Purple)
|
||||
Success: #00B894 (Teal)
|
||||
Warning: #FDCB6E (Yellow)
|
||||
Error: #FD79A8 (Pink)
|
||||
Info: #74B9FF (Light Blue)
|
||||
|
||||
Option 3: "Dark Professional"
|
||||
|
||||
Primary: #1A365D (Navy Blue)
|
||||
Secondary: #2D3748 (Dark Gray)
|
||||
Tertiary: #4A5568 (Medium Gray)
|
||||
Text Primary: #1A202C (Very Dark Gray)
|
||||
Text Secondary: #4A5568 (Medium Gray)
|
||||
Text Muted: #718096 (Light Gray)
|
||||
Background: #FFFFFF (White)
|
||||
Background Alt: #F7FAFC (Off White)
|
||||
Success: #38A169 (Green)
|
||||
Warning: #DD6B20 (Orange)
|
||||
Error: #E53E3E (Red)
|
||||
Info: #3182CE (Blue)
|
||||
|
||||
Option 4: "Sophisticated Slate"
|
||||
|
||||
Primary: #475569 (Slate Gray)
|
||||
Secondary: #64748B (Medium Slate)
|
||||
Tertiary: #CBD5E1 (Light Slate)
|
||||
Text Primary: #0F172A (Almost Black)
|
||||
Text Secondary: #475569 (Slate Gray)
|
||||
Text Muted: #94A3B8 (Light Slate)
|
||||
Background: #FFFFFF (White)
|
||||
Background Alt: #F8FAFC (Very Light Slate)
|
||||
Success: #059669 (Emerald)
|
||||
Warning: #D97706 (Amber)
|
||||
Error: #DC2626 (Red)
|
||||
Info: #0284C7 (Sky Blue)
|
||||
|
||||
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%)
|
||||
|
||||
Dark professional Prefered
|
||||
|
||||
```sh
|
||||
# Using npm
|
||||
npm start
|
||||
|
||||
# OR using Yarn
|
||||
yarn start
|
||||
## Project Structure
|
||||
```
|
||||
app/
|
||||
modules/ # Feature modules (Auth, Dashboard, CaseReview, ...)
|
||||
navigation/ # Navigation setup
|
||||
redux/ # Redux store setup
|
||||
constants/ # Global constants
|
||||
shared/
|
||||
src/
|
||||
components/ # Shared UI components
|
||||
theme/ # Design system (colors, typography, ...)
|
||||
utils/ # Utility functions
|
||||
hooks/ # Shared hooks
|
||||
types/ # TypeScript types
|
||||
assets/ # App-wide assets
|
||||
```
|
||||
|
||||
## Step 2: Build and run your app
|
||||
## Theme
|
||||
- Primary: #5B7CE6
|
||||
- Secondary: #7B94F0
|
||||
- Tertiary: #E8EFFF
|
||||
- Quaternary: #3B5998
|
||||
- ... (see `shared/src/theme/colors.ts`)
|
||||
|
||||
With Metro running, open a new terminal window/pane from the root of your React Native project, and use one of the following commands to build and run your Android or iOS app:
|
||||
## Setup
|
||||
1. Install dependencies:
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
2. Run the app:
|
||||
```sh
|
||||
npm run android # or npm run ios
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
```sh
|
||||
# Using npm
|
||||
npm run android
|
||||
|
||||
# OR using Yarn
|
||||
yarn android
|
||||
```
|
||||
|
||||
### iOS
|
||||
|
||||
For iOS, remember to install CocoaPods dependencies (this only needs to be run on first clone or after updating native deps).
|
||||
|
||||
The first time you create a new project, run the Ruby bundler to install CocoaPods itself:
|
||||
|
||||
```sh
|
||||
bundle install
|
||||
```
|
||||
|
||||
Then, and every time you update your native dependencies, run:
|
||||
|
||||
```sh
|
||||
bundle exec pod install
|
||||
```
|
||||
|
||||
For more information, please visit [CocoaPods Getting Started guide](https://guides.cocoapods.org/using/getting-started.html).
|
||||
|
||||
```sh
|
||||
# Using npm
|
||||
npm run ios
|
||||
|
||||
# OR using Yarn
|
||||
yarn ios
|
||||
```
|
||||
|
||||
If everything is set up correctly, you should see your new app running in the Android Emulator, iOS Simulator, or your connected device.
|
||||
|
||||
This is one way to run your app — you can also build it directly from Android Studio or Xcode.
|
||||
## Copyright
|
||||
Design & Developed by Tech4Biz Solutions
|
||||
Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
|
||||
|
||||
30
app/modules/Auth/__tests__/BiometricLogin.test.tsx
Normal file
30
app/modules/Auth/__tests__/BiometricLogin.test.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* File: BiometricLogin.test.tsx
|
||||
* Description: Test for BiometricLogin component
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react-native';
|
||||
import BiometricLogin from '../components/BiometricLogin';
|
||||
|
||||
jest.mock('../hooks/useBiometric', () => ({
|
||||
useBiometric: () => ({ authenticate: jest.fn() })
|
||||
}));
|
||||
|
||||
describe('BiometricLogin', () => {
|
||||
it('renders button and triggers handler', () => {
|
||||
const { getByText } = render(<BiometricLogin />);
|
||||
const button = getByText('Login with Biometric');
|
||||
expect(button).toBeTruthy();
|
||||
fireEvent.press(button);
|
||||
// No error means handler is called
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: BiometricLogin.test.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
26
app/modules/Auth/__tests__/authAPI.test.ts
Normal file
26
app/modules/Auth/__tests__/authAPI.test.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* File: authAPI.test.ts
|
||||
* Description: Test for authAPI service
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { authAPI } from '../services/authAPI';
|
||||
|
||||
jest.mock('apisauce', () => ({
|
||||
create: () => ({ post: jest.fn(() => Promise.resolve({ ok: true, data: { id: '1', name: 'Dr. Smith', email: 'dr@hospital.com' } })) })
|
||||
}));
|
||||
|
||||
describe('authAPI', () => {
|
||||
it('calls login and returns data', async () => {
|
||||
const response = await authAPI.login('dr@hospital.com', 'password');
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.data).toHaveProperty('email', 'dr@hospital.com');
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: authAPI.test.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
40
app/modules/Auth/__tests__/authSlice.test.ts
Normal file
40
app/modules/Auth/__tests__/authSlice.test.ts
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* File: authSlice.test.ts
|
||||
* Description: Test for authSlice reducer
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import reducer, { loginSuccess, loginFailure } from '../redux/authSlice';
|
||||
|
||||
const initialState = {
|
||||
user: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
isAuthenticated: false,
|
||||
};
|
||||
|
||||
describe('authSlice', () => {
|
||||
it('handles loginSuccess', () => {
|
||||
const user = { id: '1', name: 'Dr. Smith', email: 'dr@hospital.com' };
|
||||
const state = reducer(initialState, loginSuccess(user));
|
||||
expect(state.user).toEqual(user);
|
||||
expect(state.isAuthenticated).toBe(true);
|
||||
expect(state.loading).toBe(false);
|
||||
expect(state.error).toBeNull();
|
||||
});
|
||||
|
||||
it('handles loginFailure', () => {
|
||||
const error = 'Invalid credentials';
|
||||
const state = reducer(initialState, loginFailure(error));
|
||||
expect(state.loading).toBe(false);
|
||||
expect(state.error).toBe(error);
|
||||
expect(state.isAuthenticated).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: authSlice.test.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
32
app/modules/Auth/components/BiometricLogin.tsx
Normal file
32
app/modules/Auth/components/BiometricLogin.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* File: BiometricLogin.tsx
|
||||
* Description: Component for biometric login button
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button } from 'shared/src/components/Button';
|
||||
import { useBiometric } from '../hooks/useBiometric';
|
||||
|
||||
/**
|
||||
* BiometricLogin - button to trigger biometric authentication
|
||||
*/
|
||||
const BiometricLogin: React.FC = () => {
|
||||
const { authenticate } = useBiometric();
|
||||
|
||||
const handleBiometric = async () => {
|
||||
await authenticate();
|
||||
// TODO: Handle result and login
|
||||
};
|
||||
|
||||
return <Button title="Login with Biometric" onPress={handleBiometric} />;
|
||||
};
|
||||
|
||||
export default BiometricLogin;
|
||||
|
||||
/*
|
||||
* End of File: BiometricLogin.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
28
app/modules/Auth/components/EmergencyAccess.tsx
Normal file
28
app/modules/Auth/components/EmergencyAccess.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* File: EmergencyAccess.tsx
|
||||
* Description: Component for emergency access button
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Button } from 'shared/src/components/Button';
|
||||
|
||||
/**
|
||||
* EmergencyAccess - button to trigger emergency access
|
||||
*/
|
||||
const EmergencyAccess: React.FC = () => {
|
||||
const handleEmergency = () => {
|
||||
// TODO: Handle emergency access
|
||||
};
|
||||
|
||||
return <Button title="Emergency Access" onPress={handleEmergency} />;
|
||||
};
|
||||
|
||||
export default EmergencyAccess;
|
||||
|
||||
/*
|
||||
* End of File: EmergencyAccess.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
69
app/modules/Auth/components/PinInput.tsx
Normal file
69
app/modules/Auth/components/PinInput.tsx
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* File: PinInput.tsx
|
||||
* Description: Component for PIN entry
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, StyleSheet, TextInput as RNTextInput } from 'react-native';
|
||||
import { Colors, Spacing, BorderRadius, Typography } from 'shared/src/theme';
|
||||
|
||||
interface PinInputProps {
|
||||
value: string;
|
||||
onChange: (val: string) => void;
|
||||
length?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* PinInput - input for entering PIN code
|
||||
*/
|
||||
const PinInput: React.FC<PinInputProps> = ({ value, onChange, length = 4 }) => {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{Array.from({ length }).map((_, idx) => (
|
||||
<RNTextInput
|
||||
key={idx}
|
||||
style={styles.input}
|
||||
value={value[idx] || ''}
|
||||
onChangeText={text => {
|
||||
const newValue = value.substring(0, idx) + text + value.substring(idx + 1);
|
||||
onChange(newValue);
|
||||
}}
|
||||
maxLength={1}
|
||||
keyboardType="number-pad"
|
||||
secureTextEntry
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
marginVertical: Spacing.md,
|
||||
},
|
||||
input: {
|
||||
width: 40,
|
||||
height: 48,
|
||||
marginHorizontal: Spacing.xs,
|
||||
backgroundColor: Colors.backgroundAlt,
|
||||
borderRadius: BorderRadius.md,
|
||||
textAlign: 'center',
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.lg,
|
||||
color: Colors.textPrimary,
|
||||
borderWidth: 1,
|
||||
borderColor: Colors.border,
|
||||
},
|
||||
});
|
||||
|
||||
export default PinInput;
|
||||
|
||||
/*
|
||||
* End of File: PinInput.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
16
app/modules/Auth/components/index.ts
Normal file
16
app/modules/Auth/components/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Auth components
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as BiometricLogin } from './BiometricLogin';
|
||||
export { default as PinInput } from './PinInput';
|
||||
export { default as EmergencyAccess } from './EmergencyAccess';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
15
app/modules/Auth/hooks/index.ts
Normal file
15
app/modules/Auth/hooks/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Auth hooks
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export * from './useAuth';
|
||||
export * from './useBiometric';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
24
app/modules/Auth/hooks/useAuth.ts
Normal file
24
app/modules/Auth/hooks/useAuth.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* File: useAuth.ts
|
||||
* Description: Custom hook for accessing auth state from redux
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectUser, selectIsAuthenticated } from '../redux/authSelectors';
|
||||
|
||||
/**
|
||||
* useAuth - returns the current user and authentication status from redux state
|
||||
*/
|
||||
export const useAuth = () => {
|
||||
const user = useSelector(selectUser);
|
||||
const isAuthenticated = useSelector(selectIsAuthenticated);
|
||||
return { user, isAuthenticated };
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useAuth.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
29
app/modules/Auth/hooks/useBiometric.ts
Normal file
29
app/modules/Auth/hooks/useBiometric.ts
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* File: useBiometric.ts
|
||||
* Description: Custom hook for biometric authentication (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* useBiometric - stub for biometric authentication
|
||||
*/
|
||||
export const useBiometric = () => {
|
||||
const authenticate = async () => {
|
||||
// TODO: Implement biometric authentication
|
||||
// Example: return await biometricService.authenticate();
|
||||
return true;
|
||||
};
|
||||
const isAvailable = async () => {
|
||||
// TODO: Check if biometric is available
|
||||
// Example: return await biometricService.isAvailable();
|
||||
return true;
|
||||
};
|
||||
return { authenticate, isAvailable };
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useBiometric.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
51
app/modules/Auth/navigation/AuthNavigator.tsx
Normal file
51
app/modules/Auth/navigation/AuthNavigator.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* File: AuthNavigator.tsx
|
||||
* Description: Stack navigator for Auth module
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import { LoginScreen, SetupBiometricScreen } from '../screens';
|
||||
import { Colors } from 'shared/src/theme';
|
||||
|
||||
export type AuthStackParamList = {
|
||||
Login: undefined;
|
||||
SetupBiometric: undefined;
|
||||
};
|
||||
|
||||
const Stack = createStackNavigator<AuthStackParamList>();
|
||||
|
||||
/**
|
||||
* AuthNavigator sets up stack navigation for Auth module
|
||||
*/
|
||||
const AuthNavigator: React.FC = () => (
|
||||
<Stack.Navigator
|
||||
initialRouteName="Login"
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: Colors.primary },
|
||||
headerTintColor: Colors.background,
|
||||
headerTitleStyle: { fontWeight: 'bold' },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
name="Login"
|
||||
component={LoginScreen}
|
||||
options={{ title: 'Login' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="SetupBiometric"
|
||||
component={SetupBiometricScreen}
|
||||
options={{ title: 'Setup Biometric' }}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
||||
export default AuthNavigator;
|
||||
|
||||
/*
|
||||
* End of File: AuthNavigator.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
14
app/modules/Auth/navigation/index.ts
Normal file
14
app/modules/Auth/navigation/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Auth navigation
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as AuthNavigator } from './AuthNavigator';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
38
app/modules/Auth/redux/authActions.ts
Normal file
38
app/modules/Auth/redux/authActions.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* File: authActions.ts
|
||||
* Description: Async actions (thunks) for Auth state
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { loginStart, loginSuccess, loginFailure } from './authSlice';
|
||||
import { authAPI } from '../services/authAPI';
|
||||
|
||||
/**
|
||||
* Thunk to login user
|
||||
*/
|
||||
export const login = createAsyncThunk(
|
||||
'auth/login',
|
||||
async (credentials: { email: string; password: string }, { dispatch, rejectWithValue }) => {
|
||||
try {
|
||||
dispatch(loginStart());
|
||||
const response = await authAPI.login(credentials.email, credentials.password);
|
||||
if (response.ok && response.data) {
|
||||
dispatch(loginSuccess(response.data));
|
||||
} else {
|
||||
dispatch(loginFailure(response.problem || 'Unknown error'));
|
||||
return rejectWithValue(response.problem || 'Unknown error');
|
||||
}
|
||||
} catch (error: any) {
|
||||
dispatch(loginFailure(error.message));
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/*
|
||||
* End of File: authActions.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
19
app/modules/Auth/redux/authSelectors.ts
Normal file
19
app/modules/Auth/redux/authSelectors.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* File: authSelectors.ts
|
||||
* Description: Selectors for Auth redux state
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { RootState } from 'app/redux/rootReducer';
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* End of File: authSelectors.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
73
app/modules/Auth/redux/authSlice.ts
Normal file
73
app/modules/Auth/redux/authSlice.ts
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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';
|
||||
|
||||
// Sample user type
|
||||
export interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
// Auth state type
|
||||
interface AuthState {
|
||||
user: User | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
isAuthenticated: boolean;
|
||||
}
|
||||
|
||||
const initialState: AuthState = {
|
||||
user: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
isAuthenticated: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Auth slice for managing authentication state
|
||||
*/
|
||||
const authSlice = createSlice({
|
||||
name: 'auth',
|
||||
initialState,
|
||||
reducers: {
|
||||
loginStart(state) {
|
||||
state.loading = true;
|
||||
state.error = null;
|
||||
},
|
||||
loginSuccess(state, action: PayloadAction<User>) {
|
||||
state.user = action.payload;
|
||||
state.isAuthenticated = true;
|
||||
state.loading = false;
|
||||
},
|
||||
loginFailure(state, action: PayloadAction<string>) {
|
||||
state.loading = false;
|
||||
state.error = action.payload;
|
||||
state.isAuthenticated = false;
|
||||
},
|
||||
logout(state) {
|
||||
state.user = null;
|
||||
state.isAuthenticated = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
loginStart,
|
||||
loginSuccess,
|
||||
loginFailure,
|
||||
logout,
|
||||
} = authSlice.actions;
|
||||
|
||||
export default authSlice.reducer;
|
||||
|
||||
/*
|
||||
* End of File: authSlice.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
16
app/modules/Auth/redux/index.ts
Normal file
16
app/modules/Auth/redux/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Auth redux
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as authReducer } from './authSlice';
|
||||
export * from './authActions';
|
||||
export * from './authSelectors';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
79
app/modules/Auth/screens/LoginScreen.tsx
Normal file
79
app/modules/Auth/screens/LoginScreen.tsx
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* File: LoginScreen.tsx
|
||||
* Description: Login screen for user authentication
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { TextInput } from 'shared/src/components/Input';
|
||||
import { Button } from 'shared/src/components/Button';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { login } from '../redux/authActions';
|
||||
|
||||
/**
|
||||
* LoginScreen - allows user to login with email and password
|
||||
*/
|
||||
const LoginScreen: React.FC = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleLogin = () => {
|
||||
dispatch(login({ email, password }));
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Radiologist Portal</Text>
|
||||
<TextInput
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChangeText={setEmail}
|
||||
style={styles.input}
|
||||
autoCapitalize="none"
|
||||
keyboardType="email-address"
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChangeText={setPassword}
|
||||
style={styles.input}
|
||||
secureTextEntry
|
||||
/>
|
||||
<Button title="Login" onPress={handleLogin} style={styles.button} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.background,
|
||||
justifyContent: 'center',
|
||||
padding: Spacing.lg,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.title,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.xl,
|
||||
textAlign: 'center',
|
||||
},
|
||||
input: {
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
button: {
|
||||
marginTop: Spacing.md,
|
||||
},
|
||||
});
|
||||
|
||||
export default LoginScreen;
|
||||
|
||||
/*
|
||||
* End of File: LoginScreen.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
66
app/modules/Auth/screens/SetupBiometricScreen.tsx
Normal file
66
app/modules/Auth/screens/SetupBiometricScreen.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* File: SetupBiometricScreen.tsx
|
||||
* Description: Screen for setting up biometric authentication
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { Button } from 'shared/src/components/Button';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
import { useBiometric } from '../hooks/useBiometric';
|
||||
|
||||
/**
|
||||
* SetupBiometricScreen - allows user to setup biometric authentication
|
||||
*/
|
||||
const SetupBiometricScreen: React.FC = () => {
|
||||
const { authenticate } = useBiometric();
|
||||
|
||||
const handleSetup = async () => {
|
||||
await authenticate();
|
||||
// TODO: Handle result and navigate
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Setup Biometric Login</Text>
|
||||
<Text style={styles.info}>Enable Face ID or Touch ID for quick and secure login.</Text>
|
||||
<Button title="Enable Biometric" onPress={handleSetup} style={styles.button} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.background,
|
||||
justifyContent: 'center',
|
||||
padding: Spacing.lg,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.title,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.lg,
|
||||
textAlign: 'center',
|
||||
},
|
||||
info: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textSecondary,
|
||||
marginBottom: Spacing.xl,
|
||||
textAlign: 'center',
|
||||
},
|
||||
button: {
|
||||
marginTop: Spacing.md,
|
||||
},
|
||||
});
|
||||
|
||||
export default SetupBiometricScreen;
|
||||
|
||||
/*
|
||||
* End of File: SetupBiometricScreen.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
15
app/modules/Auth/screens/index.ts
Normal file
15
app/modules/Auth/screens/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Auth screens
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as LoginScreen } from './LoginScreen';
|
||||
export { default as SetupBiometricScreen } from './SetupBiometricScreen';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
27
app/modules/Auth/services/authAPI.ts
Normal file
27
app/modules/Auth/services/authAPI.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* File: authAPI.ts
|
||||
* Description: API service for authentication using apisauce
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { create } from 'apisauce';
|
||||
|
||||
const api = create({
|
||||
baseURL: 'https://api.example.com', // TODO: Replace with actual endpoint
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
/**
|
||||
* login - authenticates user with email and password
|
||||
*/
|
||||
export const authAPI = {
|
||||
login: (email: string, password: string) => api.post('/login', { email, password }),
|
||||
// Add more endpoints as needed
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: authAPI.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
23
app/modules/Auth/services/biometricService.ts
Normal file
23
app/modules/Auth/services/biometricService.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* File: biometricService.ts
|
||||
* Description: Service for biometric authentication (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export const biometricService = {
|
||||
authenticate: async () => {
|
||||
// TODO: Implement biometric authentication
|
||||
return true;
|
||||
},
|
||||
isAvailable: async () => {
|
||||
// TODO: Check if biometric is available
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: biometricService.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
15
app/modules/Auth/services/index.ts
Normal file
15
app/modules/Auth/services/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Auth services
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export * from './authAPI';
|
||||
export * from './biometricService';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
26
app/modules/CaseReview/__tests__/DICOMViewer.test.tsx
Normal file
26
app/modules/CaseReview/__tests__/DICOMViewer.test.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* File: DICOMViewer.test.tsx
|
||||
* Description: Test for DICOMViewer component
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react-native';
|
||||
import DICOMViewer from '../components/DICOMViewer';
|
||||
|
||||
describe('DICOMViewer', () => {
|
||||
it('renders image and findings', () => {
|
||||
const { getByText } = render(
|
||||
<DICOMViewer image="https://example.com/image.png" findings="Hemorrhage" confidence={93} />
|
||||
);
|
||||
expect(getByText(/AI: Hemorrhage/)).toBeTruthy();
|
||||
expect(getByText(/93%/)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: DICOMViewer.test.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
34
app/modules/CaseReview/__tests__/caseReviewSlice.test.ts
Normal file
34
app/modules/CaseReview/__tests__/caseReviewSlice.test.ts
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* File: caseReviewSlice.test.ts
|
||||
* Description: Test for caseReviewSlice reducer
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import reducer, { selectCase, setError, ReviewCase } from '../redux/caseReviewSlice';
|
||||
|
||||
const initialState = {
|
||||
selectedCase: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
describe('caseReviewSlice', () => {
|
||||
it('handles selectCase', () => {
|
||||
const reviewCase: ReviewCase = { id: '1', patientName: 'John Doe', images: [], aiFindings: 'None' };
|
||||
const state = reducer(initialState, selectCase(reviewCase));
|
||||
expect(state.selectedCase).toEqual(reviewCase);
|
||||
});
|
||||
|
||||
it('handles setError', () => {
|
||||
const error = 'Failed to load';
|
||||
const state = reducer(initialState, setError(error));
|
||||
expect(state.error).toBe(error);
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: caseReviewSlice.test.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
26
app/modules/CaseReview/__tests__/dicomAPI.test.ts
Normal file
26
app/modules/CaseReview/__tests__/dicomAPI.test.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* File: dicomAPI.test.ts
|
||||
* Description: Test for dicomAPI service
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { dicomAPI } from '../services/dicomAPI';
|
||||
|
||||
jest.mock('apisauce', () => ({
|
||||
create: () => ({ get: jest.fn(() => Promise.resolve({ ok: true, data: ['image1.png', 'image2.png'] })) })
|
||||
}));
|
||||
|
||||
describe('dicomAPI', () => {
|
||||
it('calls getImages and returns data', async () => {
|
||||
const response = await dicomAPI.getImages('1');
|
||||
expect(response.ok).toBe(true);
|
||||
expect(Array.isArray(response.data)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: dicomAPI.test.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
54
app/modules/CaseReview/components/AIOverlay.tsx
Normal file
54
app/modules/CaseReview/components/AIOverlay.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* File: AIOverlay.tsx
|
||||
* Description: Component for displaying AI overlay graphics (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface AIOverlayProps {
|
||||
findings: string;
|
||||
confidence: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* AIOverlay - displays AI overlay graphics (stub)
|
||||
*/
|
||||
const AIOverlay: React.FC<AIOverlayProps> = ({ findings, confidence }) => (
|
||||
<View style={styles.overlay}>
|
||||
{/* TODO: Render overlay graphics */}
|
||||
<Text style={styles.text}>AI: {findings} ({confidence}%)</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
overlay: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'rgba(91, 124, 230, 0.1)',
|
||||
},
|
||||
text: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.error,
|
||||
backgroundColor: Colors.background,
|
||||
padding: Spacing.xs,
|
||||
borderRadius: 4,
|
||||
},
|
||||
});
|
||||
|
||||
export default AIOverlay;
|
||||
|
||||
/*
|
||||
* End of File: AIOverlay.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
56
app/modules/CaseReview/components/AnnotationTools.tsx
Normal file
56
app/modules/CaseReview/components/AnnotationTools.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* File: AnnotationTools.tsx
|
||||
* Description: Component for annotation tools UI (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet, Button as RNButton } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface AnnotationToolsProps {
|
||||
annotations: any[];
|
||||
addAnnotation: (a: any) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* AnnotationTools - UI for adding/viewing annotations (stub)
|
||||
*/
|
||||
const AnnotationTools: React.FC<AnnotationToolsProps> = ({ annotations, addAnnotation }) => (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Annotations</Text>
|
||||
{annotations.map((a, idx) => (
|
||||
<Text key={idx} style={styles.annotation}>{JSON.stringify(a)}</Text>
|
||||
))}
|
||||
<RNButton title="Add Annotation" onPress={() => addAnnotation({ note: 'Sample annotation' })} />
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginVertical: Spacing.md,
|
||||
padding: Spacing.md,
|
||||
backgroundColor: Colors.backgroundAlt,
|
||||
borderRadius: 8,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
annotation: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
color: Colors.textSecondary,
|
||||
},
|
||||
});
|
||||
|
||||
export default AnnotationTools;
|
||||
|
||||
/*
|
||||
* End of File: AnnotationTools.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
52
app/modules/CaseReview/components/CaseMetadata.tsx
Normal file
52
app/modules/CaseReview/components/CaseMetadata.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* File: CaseMetadata.tsx
|
||||
* Description: Component for displaying case metadata (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface CaseMetadataProps {
|
||||
caseData: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* CaseMetadata - displays metadata for a case (stub)
|
||||
*/
|
||||
const CaseMetadata: React.FC<CaseMetadataProps> = ({ caseData }) => (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Case Metadata</Text>
|
||||
<Text style={styles.meta}>{JSON.stringify(caseData)}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginVertical: Spacing.md,
|
||||
padding: Spacing.md,
|
||||
backgroundColor: Colors.backgroundAlt,
|
||||
borderRadius: 8,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
meta: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
color: Colors.textSecondary,
|
||||
},
|
||||
});
|
||||
|
||||
export default CaseMetadata;
|
||||
|
||||
/*
|
||||
* End of File: CaseMetadata.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
53
app/modules/CaseReview/components/DICOMViewer.tsx
Normal file
53
app/modules/CaseReview/components/DICOMViewer.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* File: DICOMViewer.tsx
|
||||
* Description: Component for displaying a DICOM image with AI overlay (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Image, Text, StyleSheet } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface DICOMViewerProps {
|
||||
image: string;
|
||||
findings: string;
|
||||
confidence: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* DICOMViewer - displays a DICOM image with AI overlay (stub)
|
||||
*/
|
||||
const DICOMViewer: React.FC<DICOMViewerProps> = ({ image, findings, confidence }) => (
|
||||
<View style={styles.container}>
|
||||
<Image source={{ uri: image }} style={styles.image} resizeMode="contain" />
|
||||
<Text style={styles.findings}>AI: {findings} ({confidence}%)</Text>
|
||||
{/* TODO: Render overlay graphics */}
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
image: {
|
||||
width: '100%',
|
||||
height: 300,
|
||||
backgroundColor: Colors.backgroundAlt,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
findings: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.info,
|
||||
},
|
||||
});
|
||||
|
||||
export default DICOMViewer;
|
||||
|
||||
/*
|
||||
* End of File: DICOMViewer.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
56
app/modules/CaseReview/components/MeasurementTools.tsx
Normal file
56
app/modules/CaseReview/components/MeasurementTools.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* File: MeasurementTools.tsx
|
||||
* Description: Component for measurement tools UI (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet, Button as RNButton } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface MeasurementToolsProps {
|
||||
measurements: any[];
|
||||
addMeasurement: (m: any) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* MeasurementTools - UI for adding/viewing measurements (stub)
|
||||
*/
|
||||
const MeasurementTools: React.FC<MeasurementToolsProps> = ({ measurements, addMeasurement }) => (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Measurements</Text>
|
||||
{measurements.map((m, idx) => (
|
||||
<Text key={idx} style={styles.measure}>{JSON.stringify(m)}</Text>
|
||||
))}
|
||||
<RNButton title="Add Measurement" onPress={() => addMeasurement({ length: Math.random() * 10 })} />
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginVertical: Spacing.md,
|
||||
padding: Spacing.md,
|
||||
backgroundColor: Colors.backgroundAlt,
|
||||
borderRadius: 8,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
measure: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
color: Colors.textSecondary,
|
||||
},
|
||||
});
|
||||
|
||||
export default MeasurementTools;
|
||||
|
||||
/*
|
||||
* End of File: MeasurementTools.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
56
app/modules/CaseReview/components/PriorStudyComparison.tsx
Normal file
56
app/modules/CaseReview/components/PriorStudyComparison.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* File: PriorStudyComparison.tsx
|
||||
* Description: Component for comparing prior studies (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface PriorStudyComparisonProps {
|
||||
prior: any;
|
||||
current: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* PriorStudyComparison - compares prior and current studies (stub)
|
||||
*/
|
||||
const PriorStudyComparison: React.FC<PriorStudyComparisonProps> = ({ prior, current }) => (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Prior Study</Text>
|
||||
<Text style={styles.data}>{JSON.stringify(prior)}</Text>
|
||||
<Text style={styles.title}>Current Study</Text>
|
||||
<Text style={styles.data}>{JSON.stringify(current)}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginVertical: Spacing.md,
|
||||
padding: Spacing.md,
|
||||
backgroundColor: Colors.backgroundAlt,
|
||||
borderRadius: 8,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
data: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
color: Colors.textSecondary,
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
});
|
||||
|
||||
export default PriorStudyComparison;
|
||||
|
||||
/*
|
||||
* End of File: PriorStudyComparison.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
19
app/modules/CaseReview/components/index.ts
Normal file
19
app/modules/CaseReview/components/index.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for CaseReview components
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as DICOMViewer } from './DICOMViewer';
|
||||
export { default as AIOverlay } from './AIOverlay';
|
||||
export { default as MeasurementTools } from './MeasurementTools';
|
||||
export { default as AnnotationTools } from './AnnotationTools';
|
||||
export { default as CaseMetadata } from './CaseMetadata';
|
||||
export { default as PriorStudyComparison } from './PriorStudyComparison';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
17
app/modules/CaseReview/hooks/index.ts
Normal file
17
app/modules/CaseReview/hooks/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for CaseReview hooks
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export * from './useDICOMViewer';
|
||||
export * from './useAIOverlay';
|
||||
export * from './useMeasurements';
|
||||
export * from './useAnnotations';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
24
app/modules/CaseReview/hooks/useAIOverlay.ts
Normal file
24
app/modules/CaseReview/hooks/useAIOverlay.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* File: useAIOverlay.ts
|
||||
* Description: Custom hook for AI overlay logic (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectAIFindings, selectAIConfidence } from '../redux/caseReviewSelectors';
|
||||
|
||||
/**
|
||||
* useAIOverlay - returns AI findings and confidence (stub)
|
||||
*/
|
||||
export const useAIOverlay = () => {
|
||||
const findings = useSelector(selectAIFindings);
|
||||
const confidence = useSelector(selectAIConfidence);
|
||||
return { findings, confidence };
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useAIOverlay.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
23
app/modules/CaseReview/hooks/useAnnotations.ts
Normal file
23
app/modules/CaseReview/hooks/useAnnotations.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* File: useAnnotations.ts
|
||||
* Description: Custom hook for annotation tools (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
/**
|
||||
* useAnnotations - manages annotations (stub)
|
||||
*/
|
||||
export const useAnnotations = () => {
|
||||
const [annotations, setAnnotations] = useState<any[]>([]);
|
||||
const addAnnotation = (a: any) => setAnnotations(prev => [...prev, a]);
|
||||
return { annotations, addAnnotation };
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useAnnotations.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
24
app/modules/CaseReview/hooks/useDICOMViewer.ts
Normal file
24
app/modules/CaseReview/hooks/useDICOMViewer.ts
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* File: useDICOMViewer.ts
|
||||
* Description: Custom hook for DICOM viewer logic (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectDICOMImages } from '../redux/caseReviewSelectors';
|
||||
|
||||
/**
|
||||
* useDICOMViewer - returns current DICOM image and navigation helpers (stub)
|
||||
*/
|
||||
export const useDICOMViewer = () => {
|
||||
const images = useSelector(selectDICOMImages);
|
||||
// TODO: Add navigation logic (next/prev image)
|
||||
return { images };
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useDICOMViewer.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
23
app/modules/CaseReview/hooks/useMeasurements.ts
Normal file
23
app/modules/CaseReview/hooks/useMeasurements.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* File: useMeasurements.ts
|
||||
* Description: Custom hook for measurement tools (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
/**
|
||||
* useMeasurements - manages measurements (stub)
|
||||
*/
|
||||
export const useMeasurements = () => {
|
||||
const [measurements, setMeasurements] = useState<any[]>([]);
|
||||
const addMeasurement = (m: any) => setMeasurements(prev => [...prev, m]);
|
||||
return { measurements, addMeasurement };
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useMeasurements.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
57
app/modules/CaseReview/navigation/CaseReviewNavigator.tsx
Normal file
57
app/modules/CaseReview/navigation/CaseReviewNavigator.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* File: CaseReviewNavigator.tsx
|
||||
* Description: Stack navigator for CaseReview module
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import { CaseDetailsScreen, DICOMViewerScreen, ComparisonScreen } from '../screens';
|
||||
import { Colors } from 'shared/src/theme';
|
||||
|
||||
export type CaseReviewStackParamList = {
|
||||
CaseDetails: undefined;
|
||||
DICOMViewer: undefined;
|
||||
Comparison: undefined;
|
||||
};
|
||||
|
||||
const Stack = createStackNavigator<CaseReviewStackParamList>();
|
||||
|
||||
/**
|
||||
* CaseReviewNavigator sets up stack navigation for CaseReview module
|
||||
*/
|
||||
const CaseReviewNavigator: React.FC = () => (
|
||||
<Stack.Navigator
|
||||
initialRouteName="CaseDetails"
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: Colors.primary },
|
||||
headerTintColor: Colors.background,
|
||||
headerTitleStyle: { fontWeight: 'bold' },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
name="CaseDetails"
|
||||
component={CaseDetailsScreen}
|
||||
options={{ title: 'Case Details' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="DICOMViewer"
|
||||
component={DICOMViewerScreen}
|
||||
options={{ title: 'DICOM Viewer' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Comparison"
|
||||
component={ComparisonScreen}
|
||||
options={{ title: 'Comparison' }}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
||||
export default CaseReviewNavigator;
|
||||
|
||||
/*
|
||||
* End of File: CaseReviewNavigator.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
14
app/modules/CaseReview/navigation/index.ts
Normal file
14
app/modules/CaseReview/navigation/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for CaseReview navigation
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as CaseReviewNavigator } from './CaseReviewNavigator';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
58
app/modules/CaseReview/redux/aiAnalysisSlice.ts
Normal file
58
app/modules/CaseReview/redux/aiAnalysisSlice.ts
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* File: aiAnalysisSlice.ts
|
||||
* Description: Redux slice for AI analysis state management
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
interface AIAnalysisState {
|
||||
findings: string;
|
||||
confidence: number;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const initialState: AIAnalysisState = {
|
||||
findings: '',
|
||||
confidence: 0,
|
||||
loading: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* aiAnalysisSlice for managing AI findings and confidence
|
||||
*/
|
||||
const aiAnalysisSlice = createSlice({
|
||||
name: 'aiAnalysis',
|
||||
initialState,
|
||||
reducers: {
|
||||
setFindings(state, action: PayloadAction<string>) {
|
||||
state.findings = action.payload;
|
||||
},
|
||||
setConfidence(state, action: PayloadAction<number>) {
|
||||
state.confidence = action.payload;
|
||||
},
|
||||
setLoading(state, action: PayloadAction<boolean>) {
|
||||
state.loading = action.payload;
|
||||
},
|
||||
clearAnalysis(state) {
|
||||
state.findings = '';
|
||||
state.confidence = 0;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
setFindings,
|
||||
setConfidence,
|
||||
setLoading,
|
||||
clearAnalysis,
|
||||
} = aiAnalysisSlice.actions;
|
||||
|
||||
export default aiAnalysisSlice.reducer;
|
||||
|
||||
/*
|
||||
* End of File: aiAnalysisSlice.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
53
app/modules/CaseReview/redux/caseReviewActions.ts
Normal file
53
app/modules/CaseReview/redux/caseReviewActions.ts
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* File: caseReviewActions.ts
|
||||
* Description: Async actions (thunks) for CaseReview state
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { setLoading, selectCase, setError } from './caseReviewSlice';
|
||||
import { setImages } from './dicomSlice';
|
||||
import { setFindings, setConfidence } from './aiAnalysisSlice';
|
||||
import { dicomAPI } from '../services/dicomAPI';
|
||||
import { aiAnalysisAPI } from '../services/aiAnalysisAPI';
|
||||
|
||||
/**
|
||||
* Thunk to fetch case details, DICOM images, and AI analysis
|
||||
*/
|
||||
export const fetchCaseDetails = createAsyncThunk(
|
||||
'caseReview/fetchCaseDetails',
|
||||
async (caseId: string, { dispatch, rejectWithValue }) => {
|
||||
try {
|
||||
dispatch(setLoading(true));
|
||||
// Fetch DICOM images
|
||||
const dicomRes = await dicomAPI.getImages(caseId);
|
||||
if (dicomRes.ok && dicomRes.data) {
|
||||
dispatch(setImages(dicomRes.data));
|
||||
} else {
|
||||
dispatch(setError('Failed to load DICOM images'));
|
||||
return rejectWithValue('Failed to load DICOM images');
|
||||
}
|
||||
// Fetch AI analysis
|
||||
const aiRes = await aiAnalysisAPI.getAnalysis(caseId);
|
||||
if (aiRes.ok && aiRes.data) {
|
||||
dispatch(setFindings(aiRes.data.findings));
|
||||
dispatch(setConfidence(aiRes.data.confidence));
|
||||
} else {
|
||||
dispatch(setError('Failed to load AI analysis'));
|
||||
return rejectWithValue('Failed to load AI analysis');
|
||||
}
|
||||
dispatch(setLoading(false));
|
||||
} catch (error: any) {
|
||||
dispatch(setError(error.message));
|
||||
dispatch(setLoading(false));
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/*
|
||||
* End of File: caseReviewActions.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
21
app/modules/CaseReview/redux/caseReviewSelectors.ts
Normal file
21
app/modules/CaseReview/redux/caseReviewSelectors.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* File: caseReviewSelectors.ts
|
||||
* Description: Selectors for CaseReview redux state
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { RootState } from 'app/redux/rootReducer';
|
||||
|
||||
export const selectSelectedCase = (state: RootState) => state.caseReview.selectedCase;
|
||||
export const selectCaseReviewLoading = (state: RootState) => state.caseReview.loading;
|
||||
export const selectCaseReviewError = (state: RootState) => state.caseReview.error;
|
||||
export const selectDICOMImages = (state: RootState) => state.dicom.images;
|
||||
export const selectAIFindings = (state: RootState) => state.aiAnalysis.findings;
|
||||
export const selectAIConfidence = (state: RootState) => state.aiAnalysis.confidence;
|
||||
|
||||
/*
|
||||
* End of File: caseReviewSelectors.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
66
app/modules/CaseReview/redux/caseReviewSlice.ts
Normal file
66
app/modules/CaseReview/redux/caseReviewSlice.ts
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* File: caseReviewSlice.ts
|
||||
* Description: Redux slice for CaseReview state management
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
// Sample case type
|
||||
export interface ReviewCase {
|
||||
id: string;
|
||||
patientName: string;
|
||||
images: string[];
|
||||
aiFindings: string;
|
||||
}
|
||||
|
||||
// CaseReview state type
|
||||
interface CaseReviewState {
|
||||
selectedCase: ReviewCase | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const initialState: CaseReviewState = {
|
||||
selectedCase: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* CaseReview slice for managing selected case and review state
|
||||
*/
|
||||
const caseReviewSlice = createSlice({
|
||||
name: 'caseReview',
|
||||
initialState,
|
||||
reducers: {
|
||||
selectCase(state, action: PayloadAction<ReviewCase>) {
|
||||
state.selectedCase = action.payload;
|
||||
},
|
||||
clearCase(state) {
|
||||
state.selectedCase = null;
|
||||
},
|
||||
setLoading(state, action: PayloadAction<boolean>) {
|
||||
state.loading = action.payload;
|
||||
},
|
||||
setError(state, action: PayloadAction<string | null>) {
|
||||
state.error = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
selectCase,
|
||||
clearCase,
|
||||
setLoading,
|
||||
setError,
|
||||
} = caseReviewSlice.actions;
|
||||
|
||||
export default caseReviewSlice.reducer;
|
||||
|
||||
/*
|
||||
* End of File: caseReviewSlice.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
59
app/modules/CaseReview/redux/dicomSlice.ts
Normal file
59
app/modules/CaseReview/redux/dicomSlice.ts
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* File: dicomSlice.ts
|
||||
* Description: Redux slice for DICOM image state management
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
interface DICOMState {
|
||||
images: string[];
|
||||
currentIndex: number;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const initialState: DICOMState = {
|
||||
images: [],
|
||||
currentIndex: 0,
|
||||
loading: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* dicomSlice for managing DICOM images and navigation
|
||||
*/
|
||||
const dicomSlice = createSlice({
|
||||
name: 'dicom',
|
||||
initialState,
|
||||
reducers: {
|
||||
setImages(state, action: PayloadAction<string[]>) {
|
||||
state.images = action.payload;
|
||||
state.currentIndex = 0;
|
||||
},
|
||||
setCurrentIndex(state, action: PayloadAction<number>) {
|
||||
state.currentIndex = action.payload;
|
||||
},
|
||||
setLoading(state, action: PayloadAction<boolean>) {
|
||||
state.loading = action.payload;
|
||||
},
|
||||
clearImages(state) {
|
||||
state.images = [];
|
||||
state.currentIndex = 0;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
setImages,
|
||||
setCurrentIndex,
|
||||
setLoading,
|
||||
clearImages,
|
||||
} = dicomSlice.actions;
|
||||
|
||||
export default dicomSlice.reducer;
|
||||
|
||||
/*
|
||||
* End of File: dicomSlice.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
18
app/modules/CaseReview/redux/index.ts
Normal file
18
app/modules/CaseReview/redux/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for CaseReview redux
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as caseReviewReducer } from './caseReviewSlice';
|
||||
export { default as dicomReducer } from './dicomSlice';
|
||||
export { default as aiAnalysisReducer } from './aiAnalysisSlice';
|
||||
export * from './caseReviewActions';
|
||||
export * from './caseReviewSelectors';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
83
app/modules/CaseReview/screens/CaseDetailsScreen.tsx
Normal file
83
app/modules/CaseReview/screens/CaseDetailsScreen.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* File: CaseDetailsScreen.tsx
|
||||
* Description: Screen for displaying case details
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { Button } from 'shared/src/components/Button';
|
||||
import { Card } from 'shared/src/components/Card';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectSelectedCase } from '../redux/caseReviewSelectors';
|
||||
import { useAIOverlay } from '../hooks/useAIOverlay';
|
||||
|
||||
/**
|
||||
* CaseDetailsScreen - shows patient info, AI findings, and navigation to DICOM viewer
|
||||
*/
|
||||
const CaseDetailsScreen: React.FC<any> = ({ navigation }) => {
|
||||
const caseData = useSelector(selectSelectedCase);
|
||||
const { findings, confidence } = useAIOverlay();
|
||||
|
||||
if (!caseData) {
|
||||
return <View style={styles.container}><Text>No case selected.</Text></View>;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Card>
|
||||
<Text style={styles.title}>{caseData.patientName}</Text>
|
||||
<Text style={styles.label}>AI Findings:</Text>
|
||||
<Text style={styles.findings}>{findings}</Text>
|
||||
<Text style={styles.confidence}>Confidence: {confidence}%</Text>
|
||||
<Button title="View DICOM Images" onPress={() => navigation.navigate('DICOMViewer')} style={styles.button} />
|
||||
</Card>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.background,
|
||||
justifyContent: 'center',
|
||||
padding: Spacing.lg,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.lg,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
label: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textPrimary,
|
||||
marginTop: Spacing.sm,
|
||||
},
|
||||
findings: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textSecondary,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
confidence: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
color: Colors.info,
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
button: {
|
||||
marginTop: Spacing.md,
|
||||
},
|
||||
});
|
||||
|
||||
export default CaseDetailsScreen;
|
||||
|
||||
/*
|
||||
* End of File: CaseDetailsScreen.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
63
app/modules/CaseReview/screens/ComparisonScreen.tsx
Normal file
63
app/modules/CaseReview/screens/ComparisonScreen.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* File: ComparisonScreen.tsx
|
||||
* Description: Screen for comparing prior studies
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { Card } from 'shared/src/components/Card';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
/**
|
||||
* ComparisonScreen - shows prior/current images and findings (stub)
|
||||
*/
|
||||
const ComparisonScreen: React.FC = () => {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Card>
|
||||
<Text style={styles.title}>Comparison</Text>
|
||||
<Text style={styles.label}>Prior Study:</Text>
|
||||
<Text style={styles.stub}>[Prior Image/Findings Here]</Text>
|
||||
<Text style={styles.label}>Current Study:</Text>
|
||||
<Text style={styles.stub}>[Current Image/Findings Here]</Text>
|
||||
</Card>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.background,
|
||||
justifyContent: 'center',
|
||||
padding: Spacing.lg,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.lg,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
label: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textPrimary,
|
||||
marginTop: Spacing.sm,
|
||||
},
|
||||
stub: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textSecondary,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
});
|
||||
|
||||
export default ComparisonScreen;
|
||||
|
||||
/*
|
||||
* End of File: ComparisonScreen.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
66
app/modules/CaseReview/screens/DICOMViewerScreen.tsx
Normal file
66
app/modules/CaseReview/screens/DICOMViewerScreen.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* File: DICOMViewerScreen.tsx
|
||||
* Description: Screen for viewing DICOM images
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet, Image } from 'react-native';
|
||||
import { Button } from 'shared/src/components/Button';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
import { useDICOMViewer } from '../hooks/useDICOMViewer';
|
||||
|
||||
/**
|
||||
* DICOMViewerScreen - shows current DICOM image and navigation controls (stub)
|
||||
*/
|
||||
const DICOMViewerScreen: React.FC<any> = ({ navigation }) => {
|
||||
const { images } = useDICOMViewer();
|
||||
// TODO: Add navigation logic for images
|
||||
const currentImage = images[0];
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>DICOM Viewer</Text>
|
||||
{currentImage ? (
|
||||
<Image source={{ uri: currentImage }} style={styles.image} resizeMode="contain" />
|
||||
) : (
|
||||
<Text>No images available.</Text>
|
||||
)}
|
||||
<Button title="Back to Details" onPress={() => navigation.goBack()} style={styles.button} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.background,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
padding: Spacing.lg,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.lg,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
image: {
|
||||
width: '100%',
|
||||
height: 300,
|
||||
backgroundColor: Colors.backgroundAlt,
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
button: {
|
||||
marginTop: Spacing.md,
|
||||
},
|
||||
});
|
||||
|
||||
export default DICOMViewerScreen;
|
||||
|
||||
/*
|
||||
* End of File: DICOMViewerScreen.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
16
app/modules/CaseReview/screens/index.ts
Normal file
16
app/modules/CaseReview/screens/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for CaseReview screens
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as CaseDetailsScreen } from './CaseDetailsScreen';
|
||||
export { default as DICOMViewerScreen } from './DICOMViewerScreen';
|
||||
export { default as ComparisonScreen } from './ComparisonScreen';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
27
app/modules/CaseReview/services/aiAnalysisAPI.ts
Normal file
27
app/modules/CaseReview/services/aiAnalysisAPI.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* File: aiAnalysisAPI.ts
|
||||
* Description: API service for fetching AI analysis using apisauce
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { create } from 'apisauce';
|
||||
|
||||
const api = create({
|
||||
baseURL: 'https://api.example.com', // TODO: Replace with actual endpoint
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
/**
|
||||
* getAnalysis - fetches AI analysis for a case
|
||||
*/
|
||||
export const aiAnalysisAPI = {
|
||||
getAnalysis: (caseId: string) => api.get(`/cases/${caseId}/ai-analysis`),
|
||||
// Add more endpoints as needed
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: aiAnalysisAPI.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
27
app/modules/CaseReview/services/dicomAPI.ts
Normal file
27
app/modules/CaseReview/services/dicomAPI.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* File: dicomAPI.ts
|
||||
* Description: API service for fetching DICOM images using apisauce
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { create } from 'apisauce';
|
||||
|
||||
const api = create({
|
||||
baseURL: 'https://api.example.com', // TODO: Replace with actual endpoint
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
/**
|
||||
* getImages - fetches DICOM images for a case
|
||||
*/
|
||||
export const dicomAPI = {
|
||||
getImages: (caseId: string) => api.get(`/cases/${caseId}/dicom`),
|
||||
// Add more endpoints as needed
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: dicomAPI.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
22
app/modules/CaseReview/services/dicomParser.ts
Normal file
22
app/modules/CaseReview/services/dicomParser.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* File: dicomParser.ts
|
||||
* Description: Utility for parsing DICOM files (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* dicomParser - stub for parsing DICOM files
|
||||
*/
|
||||
export const dicomParser = {
|
||||
parse: (file: any) => {
|
||||
// TODO: Implement DICOM parsing logic
|
||||
return {};
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: dicomParser.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
22
app/modules/CaseReview/services/imageProcessor.ts
Normal file
22
app/modules/CaseReview/services/imageProcessor.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* File: imageProcessor.ts
|
||||
* Description: Utility for image processing (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* imageProcessor - stub for image processing utilities
|
||||
*/
|
||||
export const imageProcessor = {
|
||||
process: (image: any) => {
|
||||
// TODO: Implement image processing logic
|
||||
return image;
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: imageProcessor.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
17
app/modules/CaseReview/services/index.ts
Normal file
17
app/modules/CaseReview/services/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for CaseReview services
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export * from './dicomAPI';
|
||||
export * from './aiAnalysisAPI';
|
||||
export * from './dicomParser';
|
||||
export * from './imageProcessor';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
34
app/modules/Dashboard/__tests__/CaseQueue.test.tsx
Normal file
34
app/modules/Dashboard/__tests__/CaseQueue.test.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* File: CaseQueue.test.tsx
|
||||
* Description: Test for CaseQueue component
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, fireEvent } from '@testing-library/react-native';
|
||||
import CaseQueue from '../components/CaseQueue';
|
||||
|
||||
const mockCases = [
|
||||
{ id: '1', patientName: 'John Doe', status: 'critical', aiConfidence: 93, description: 'Head trauma' },
|
||||
{ id: '2', patientName: 'Jane Smith', status: 'routine', aiConfidence: 99, description: 'Headache' },
|
||||
];
|
||||
|
||||
describe('CaseQueue', () => {
|
||||
it('renders cases', () => {
|
||||
const { getByText } = render(<CaseQueue cases={mockCases} onSelect={() => {}} />);
|
||||
expect(getByText('John Doe')).toBeTruthy();
|
||||
expect(getByText('Jane Smith')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders empty state', () => {
|
||||
const { getByText } = render(<CaseQueue cases={[]} onSelect={() => {}} />);
|
||||
expect(getByText('No cases in queue.')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: CaseQueue.test.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
26
app/modules/Dashboard/__tests__/caseAPI.test.ts
Normal file
26
app/modules/Dashboard/__tests__/caseAPI.test.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* File: caseAPI.test.ts
|
||||
* Description: Test for caseAPI service
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { caseAPI } from '../services/caseAPI';
|
||||
|
||||
jest.mock('apisauce', () => ({
|
||||
create: () => ({ get: jest.fn(() => Promise.resolve({ ok: true, data: [] })) })
|
||||
}));
|
||||
|
||||
describe('caseAPI', () => {
|
||||
it('calls getCases and returns data', async () => {
|
||||
const response = await caseAPI.getCases();
|
||||
expect(response.ok).toBe(true);
|
||||
expect(Array.isArray(response.data)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: caseAPI.test.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
37
app/modules/Dashboard/__tests__/dashboardSlice.test.ts
Normal file
37
app/modules/Dashboard/__tests__/dashboardSlice.test.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* File: dashboardSlice.test.ts
|
||||
* Description: Test for dashboardSlice reducer
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import reducer, { fetchCasesSuccess, fetchCasesFailure } from '../redux/dashboardSlice';
|
||||
|
||||
const initialState = {
|
||||
caseQueue: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
describe('dashboardSlice', () => {
|
||||
it('handles fetchCasesSuccess', () => {
|
||||
const cases = [{ id: '1', patientName: 'John Doe', status: 'critical', aiConfidence: 93, description: 'Head trauma' }];
|
||||
const state = reducer(initialState, fetchCasesSuccess(cases));
|
||||
expect(state.caseQueue).toEqual(cases);
|
||||
expect(state.loading).toBe(false);
|
||||
expect(state.error).toBeNull();
|
||||
});
|
||||
|
||||
it('handles fetchCasesFailure', () => {
|
||||
const error = 'Failed to fetch';
|
||||
const state = reducer(initialState, fetchCasesFailure(error));
|
||||
expect(state.loading).toBe(false);
|
||||
expect(state.error).toBe(error);
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: dashboardSlice.test.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
78
app/modules/Dashboard/components/CaseCard.tsx
Normal file
78
app/modules/Dashboard/components/CaseCard.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* File: CaseCard.tsx
|
||||
* Description: Component to display a single case card
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
||||
import { Card } from 'shared/src/components/Card';
|
||||
import { CustomIcon } from 'shared/src/components/Icons';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
import { Case } from '../redux/dashboardSlice';
|
||||
|
||||
interface CaseCardProps {
|
||||
item: Case;
|
||||
onPress: (item: Case) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* CaseCard - displays a single case card
|
||||
*/
|
||||
const CaseCard: React.FC<CaseCardProps> = ({ item, onPress }) => (
|
||||
<TouchableOpacity onPress={() => onPress(item)}>
|
||||
<Card style={styles.card}>
|
||||
<View style={styles.row}>
|
||||
<CustomIcon
|
||||
name={item.status === 'critical' ? 'alert' : item.status === 'urgent' ? 'alert-outline' : 'check-circle'}
|
||||
color={item.status === 'critical' ? Colors.error : item.status === 'urgent' ? Colors.warning : Colors.success}
|
||||
size={24}
|
||||
/>
|
||||
<View style={styles.info}>
|
||||
<Text style={styles.patient}>{item.patientName}</Text>
|
||||
<Text style={styles.desc}>{item.description}</Text>
|
||||
</View>
|
||||
<Text style={styles.confidence}>{item.aiConfidence}%</Text>
|
||||
</View>
|
||||
</Card>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
info: {
|
||||
flex: 1,
|
||||
marginLeft: Spacing.sm,
|
||||
},
|
||||
patient: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textPrimary,
|
||||
},
|
||||
desc: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
color: Colors.textSecondary,
|
||||
},
|
||||
confidence: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.info,
|
||||
marginLeft: Spacing.sm,
|
||||
},
|
||||
});
|
||||
|
||||
export default CaseCard;
|
||||
|
||||
/*
|
||||
* End of File: CaseCard.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
95
app/modules/Dashboard/components/CaseQueue.tsx
Normal file
95
app/modules/Dashboard/components/CaseQueue.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* File: CaseQueue.tsx
|
||||
* Description: Component to display a list of cases in the queue
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, FlatList, StyleSheet, TouchableOpacity } from 'react-native';
|
||||
import { Card, InfoCard } from 'shared/src/components/Card';
|
||||
import { CustomIcon } from 'shared/src/components/Icons';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
import { Case } from '../redux/dashboardSlice';
|
||||
|
||||
interface CaseQueueProps {
|
||||
cases: Case[];
|
||||
onSelect: (item: Case) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* CaseQueue - displays a list of cases in the queue
|
||||
*/
|
||||
const CaseQueue: React.FC<CaseQueueProps> = ({ cases, onSelect }) => {
|
||||
if (!cases.length) {
|
||||
return <InfoCard><Text style={styles.empty}>No cases in queue.</Text></InfoCard>;
|
||||
}
|
||||
return (
|
||||
<FlatList
|
||||
data={cases}
|
||||
keyExtractor={item => item.id}
|
||||
renderItem={({ item }) => (
|
||||
<TouchableOpacity onPress={() => onSelect(item)}>
|
||||
<Card style={styles.card}>
|
||||
<View style={styles.row}>
|
||||
<CustomIcon
|
||||
name={item.status === 'critical' ? 'alert' : item.status === 'urgent' ? 'alert-outline' : 'check-circle'}
|
||||
color={item.status === 'critical' ? Colors.error : item.status === 'urgent' ? Colors.warning : Colors.success}
|
||||
size={24}
|
||||
/>
|
||||
<View style={styles.info}>
|
||||
<Text style={styles.patient}>{item.patientName}</Text>
|
||||
<Text style={styles.desc}>{item.description}</Text>
|
||||
</View>
|
||||
<Text style={styles.confidence}>{item.aiConfidence}%</Text>
|
||||
</View>
|
||||
</Card>
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
info: {
|
||||
flex: 1,
|
||||
marginLeft: Spacing.sm,
|
||||
},
|
||||
patient: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textPrimary,
|
||||
},
|
||||
desc: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
color: Colors.textSecondary,
|
||||
},
|
||||
confidence: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.info,
|
||||
marginLeft: Spacing.sm,
|
||||
},
|
||||
empty: {
|
||||
textAlign: 'center',
|
||||
color: Colors.textMuted,
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.md,
|
||||
},
|
||||
});
|
||||
|
||||
export default CaseQueue;
|
||||
|
||||
/*
|
||||
* End of File: CaseQueue.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
70
app/modules/Dashboard/components/FilterBar.tsx
Normal file
70
app/modules/Dashboard/components/FilterBar.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* File: FilterBar.tsx
|
||||
* Description: Component to filter cases by status
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
import { Button } from 'shared/src/components/Button';
|
||||
import { Colors, Spacing } from 'shared/src/theme';
|
||||
|
||||
interface FilterBarProps {
|
||||
filter: 'critical' | 'urgent' | 'routine';
|
||||
onChange: (filter: 'critical' | 'urgent' | 'routine') => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* FilterBar - allows filtering cases by status
|
||||
*/
|
||||
const FilterBar: React.FC<FilterBarProps> = ({ filter, onChange }) => (
|
||||
<View style={styles.container}>
|
||||
<Button
|
||||
title="Critical"
|
||||
onPress={() => onChange('critical')}
|
||||
style={[styles.button, filter === 'critical' && styles.selected]}
|
||||
textStyle={filter === 'critical' ? styles.selectedText : undefined}
|
||||
/>
|
||||
<Button
|
||||
title="Urgent"
|
||||
onPress={() => onChange('urgent')}
|
||||
style={[styles.button, filter === 'urgent' && styles.selected]}
|
||||
textStyle={filter === 'urgent' ? styles.selectedText : undefined}
|
||||
/>
|
||||
<Button
|
||||
title="Routine"
|
||||
onPress={() => onChange('routine')}
|
||||
style={[styles.button, filter === 'routine' && styles.selected]}
|
||||
textStyle={filter === 'routine' ? styles.selectedText : undefined}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
button: {
|
||||
flex: 1,
|
||||
marginHorizontal: Spacing.xs,
|
||||
backgroundColor: Colors.inactiveState,
|
||||
},
|
||||
selected: {
|
||||
backgroundColor: Colors.selectedState,
|
||||
},
|
||||
selectedText: {
|
||||
color: Colors.background,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
});
|
||||
|
||||
export default FilterBar;
|
||||
|
||||
/*
|
||||
* End of File: FilterBar.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
61
app/modules/Dashboard/components/PriorityIndicator.tsx
Normal file
61
app/modules/Dashboard/components/PriorityIndicator.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* File: PriorityIndicator.tsx
|
||||
* Description: Component to display a colored dot and label for case priority
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface PriorityIndicatorProps {
|
||||
status: 'critical' | 'urgent' | 'routine';
|
||||
}
|
||||
|
||||
/**
|
||||
* PriorityIndicator - displays a colored dot and label for case priority
|
||||
*/
|
||||
export const PriorityIndicator: React.FC<PriorityIndicatorProps> = ({ status }) => {
|
||||
const color =
|
||||
status === 'critical'
|
||||
? Colors.error
|
||||
: status === 'urgent'
|
||||
? Colors.warning
|
||||
: Colors.success;
|
||||
const label =
|
||||
status === 'critical'
|
||||
? 'Critical'
|
||||
: status === 'urgent'
|
||||
? 'Urgent'
|
||||
: 'Routine';
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.dot, { backgroundColor: color }]} />
|
||||
<Text style={[styles.label, { color }]}>{label}</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
},
|
||||
dot: {
|
||||
width: 10,
|
||||
height: 10,
|
||||
borderRadius: 5,
|
||||
marginRight: Spacing.xs,
|
||||
},
|
||||
label: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
},
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: PriorityIndicator.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
70
app/modules/Dashboard/components/StatsPanel.tsx
Normal file
70
app/modules/Dashboard/components/StatsPanel.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* File: StatsPanel.tsx
|
||||
* Description: Component to display dashboard statistics (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { Card } from 'shared/src/components/Card';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface StatsPanelProps {
|
||||
stats: { label: string; value: string | number }[];
|
||||
}
|
||||
|
||||
/**
|
||||
* StatsPanel - displays dashboard statistics (stub)
|
||||
*/
|
||||
const StatsPanel: React.FC<StatsPanelProps> = ({ stats }) => (
|
||||
<Card style={styles.card}>
|
||||
<Text style={styles.title}>Statistics</Text>
|
||||
<View style={styles.statsRow}>
|
||||
{stats.map((stat, idx) => (
|
||||
<View key={idx} style={styles.statItem}>
|
||||
<Text style={styles.statValue}>{stat.value}</Text>
|
||||
<Text style={styles.statLabel}>{stat.label}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</Card>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textPrimary,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
statsRow: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
statItem: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
},
|
||||
statValue: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.lg,
|
||||
color: Colors.primary,
|
||||
},
|
||||
statLabel: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.sm,
|
||||
color: Colors.textSecondary,
|
||||
},
|
||||
});
|
||||
|
||||
export default StatsPanel;
|
||||
|
||||
/*
|
||||
* End of File: StatsPanel.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
18
app/modules/Dashboard/components/index.ts
Normal file
18
app/modules/Dashboard/components/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Dashboard components
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as CaseQueue } from './CaseQueue';
|
||||
export { default as CaseCard } from './CaseCard';
|
||||
export { PriorityIndicator } from './PriorityIndicator';
|
||||
export { default as StatsPanel } from './StatsPanel';
|
||||
export { default as FilterBar } from './FilterBar';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
16
app/modules/Dashboard/hooks/index.ts
Normal file
16
app/modules/Dashboard/hooks/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Dashboard hooks
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export * from './useCaseQueue';
|
||||
export * from './useRealTimeUpdates';
|
||||
export * from './useFilterCases';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
22
app/modules/Dashboard/hooks/useCaseQueue.ts
Normal file
22
app/modules/Dashboard/hooks/useCaseQueue.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* File: useCaseQueue.ts
|
||||
* Description: Custom hook for accessing the case queue from redux
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectCaseQueue } from '../redux/dashboardSelectors';
|
||||
|
||||
/**
|
||||
* useCaseQueue - returns the current case queue from redux state
|
||||
*/
|
||||
export const useCaseQueue = () => {
|
||||
return useSelector(selectCaseQueue);
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useCaseQueue.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
22
app/modules/Dashboard/hooks/useFilterCases.ts
Normal file
22
app/modules/Dashboard/hooks/useFilterCases.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* File: useFilterCases.ts
|
||||
* Description: Custom hook for filtering cases by status
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { Case } from '../redux/dashboardSlice';
|
||||
|
||||
/**
|
||||
* useFilterCases - returns filtered cases by status
|
||||
*/
|
||||
export const useFilterCases = (cases: Case[], status: 'critical' | 'urgent' | 'routine') => {
|
||||
return useMemo(() => cases.filter(c => c.status === status), [cases, status]);
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useFilterCases.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
27
app/modules/Dashboard/hooks/useRealTimeUpdates.ts
Normal file
27
app/modules/Dashboard/hooks/useRealTimeUpdates.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* File: useRealTimeUpdates.ts
|
||||
* Description: Custom hook for subscribing to real-time updates (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
/**
|
||||
* useRealTimeUpdates - subscribes to real-time updates (stub)
|
||||
*/
|
||||
export const useRealTimeUpdates = () => {
|
||||
useEffect(() => {
|
||||
// TODO: Connect to websocket and handle updates
|
||||
// Example: websocketService.subscribe(...)
|
||||
return () => {
|
||||
// Cleanup subscription
|
||||
};
|
||||
}, []);
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useRealTimeUpdates.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
47
app/modules/Dashboard/navigation/DashboardNavigator.tsx
Normal file
47
app/modules/Dashboard/navigation/DashboardNavigator.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* File: DashboardNavigator.tsx
|
||||
* Description: Stack navigator for Dashboard module
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import { DashboardScreen } from '../screens';
|
||||
import { Colors } from 'shared/src/theme';
|
||||
|
||||
export type DashboardStackParamList = {
|
||||
Dashboard: undefined;
|
||||
// Add more screens here as needed
|
||||
};
|
||||
|
||||
const Stack = createStackNavigator<DashboardStackParamList>();
|
||||
|
||||
/**
|
||||
* DashboardNavigator sets up stack navigation for Dashboard module
|
||||
*/
|
||||
const DashboardNavigator: React.FC = () => (
|
||||
<Stack.Navigator
|
||||
initialRouteName="Dashboard"
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: Colors.primary },
|
||||
headerTintColor: Colors.background,
|
||||
headerTitleStyle: { fontWeight: 'bold' },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
name="Dashboard"
|
||||
component={DashboardScreen}
|
||||
options={{ title: 'Dashboard' }}
|
||||
/>
|
||||
{/* Add more screens here */}
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
||||
export default DashboardNavigator;
|
||||
|
||||
/*
|
||||
* End of File: DashboardNavigator.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
14
app/modules/Dashboard/navigation/index.ts
Normal file
14
app/modules/Dashboard/navigation/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Dashboard navigation
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as DashboardNavigator } from './DashboardNavigator';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
61
app/modules/Dashboard/redux/caseQueueSlice.ts
Normal file
61
app/modules/Dashboard/redux/caseQueueSlice.ts
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* File: caseQueueSlice.ts
|
||||
* Description: Redux slice for case queue state management
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { Case } from './dashboardSlice';
|
||||
|
||||
interface CaseQueueState {
|
||||
queue: Case[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const initialState: CaseQueueState = {
|
||||
queue: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Slice for managing the case queue
|
||||
*/
|
||||
const caseQueueSlice = createSlice({
|
||||
name: 'caseQueue',
|
||||
initialState,
|
||||
reducers: {
|
||||
fetchQueueStart(state) {
|
||||
state.loading = true;
|
||||
state.error = null;
|
||||
},
|
||||
fetchQueueSuccess(state, action: PayloadAction<Case[]>) {
|
||||
state.queue = action.payload;
|
||||
state.loading = false;
|
||||
},
|
||||
fetchQueueFailure(state, action: PayloadAction<string>) {
|
||||
state.loading = false;
|
||||
state.error = action.payload;
|
||||
},
|
||||
clearQueue(state) {
|
||||
state.queue = [];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
fetchQueueStart,
|
||||
fetchQueueSuccess,
|
||||
fetchQueueFailure,
|
||||
clearQueue,
|
||||
} = caseQueueSlice.actions;
|
||||
|
||||
export default caseQueueSlice.reducer;
|
||||
|
||||
/*
|
||||
* End of File: caseQueueSlice.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
38
app/modules/Dashboard/redux/dashboardActions.ts
Normal file
38
app/modules/Dashboard/redux/dashboardActions.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* File: dashboardActions.ts
|
||||
* Description: Async actions (thunks) for Dashboard state
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { fetchCasesStart, fetchCasesSuccess, fetchCasesFailure } from './dashboardSlice';
|
||||
import { caseAPI } from '../services/caseAPI';
|
||||
|
||||
/**
|
||||
* Thunk to fetch case queue from API
|
||||
*/
|
||||
export const fetchCases = createAsyncThunk(
|
||||
'dashboard/fetchCases',
|
||||
async (_, { dispatch, rejectWithValue }) => {
|
||||
try {
|
||||
dispatch(fetchCasesStart());
|
||||
const response = await caseAPI.getCases();
|
||||
if (response.ok && response.data) {
|
||||
dispatch(fetchCasesSuccess(response.data));
|
||||
} else {
|
||||
dispatch(fetchCasesFailure(response.problem || 'Unknown error'));
|
||||
return rejectWithValue(response.problem || 'Unknown error');
|
||||
}
|
||||
} catch (error: any) {
|
||||
dispatch(fetchCasesFailure(error.message));
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/*
|
||||
* End of File: dashboardActions.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
18
app/modules/Dashboard/redux/dashboardSelectors.ts
Normal file
18
app/modules/Dashboard/redux/dashboardSelectors.ts
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* File: dashboardSelectors.ts
|
||||
* Description: Selectors for Dashboard redux state
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { RootState } from 'app/redux/rootReducer';
|
||||
|
||||
export const selectCaseQueue = (state: RootState) => state.dashboard.caseQueue;
|
||||
export const selectDashboardLoading = (state: RootState) => state.dashboard.loading;
|
||||
export const selectDashboardError = (state: RootState) => state.dashboard.error;
|
||||
|
||||
/*
|
||||
* End of File: dashboardSelectors.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
70
app/modules/Dashboard/redux/dashboardSlice.ts
Normal file
70
app/modules/Dashboard/redux/dashboardSlice.ts
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* File: dashboardSlice.ts
|
||||
* Description: Redux slice for Dashboard state management
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
// Sample case type
|
||||
export interface Case {
|
||||
id: string;
|
||||
patientName: string;
|
||||
status: 'critical' | 'urgent' | 'routine';
|
||||
aiConfidence: number;
|
||||
description: string;
|
||||
}
|
||||
|
||||
// Dashboard state type
|
||||
interface DashboardState {
|
||||
caseQueue: Case[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const initialState: DashboardState = {
|
||||
caseQueue: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Dashboard slice for managing case queue and dashboard state
|
||||
*/
|
||||
const dashboardSlice = createSlice({
|
||||
name: 'dashboard',
|
||||
initialState,
|
||||
reducers: {
|
||||
fetchCasesStart(state) {
|
||||
state.loading = true;
|
||||
state.error = null;
|
||||
},
|
||||
fetchCasesSuccess(state, action: PayloadAction<Case[]>) {
|
||||
state.caseQueue = action.payload;
|
||||
state.loading = false;
|
||||
},
|
||||
fetchCasesFailure(state, action: PayloadAction<string>) {
|
||||
state.loading = false;
|
||||
state.error = action.payload;
|
||||
},
|
||||
clearCases(state) {
|
||||
state.caseQueue = [];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
fetchCasesStart,
|
||||
fetchCasesSuccess,
|
||||
fetchCasesFailure,
|
||||
clearCases,
|
||||
} = dashboardSlice.actions;
|
||||
|
||||
export default dashboardSlice.reducer;
|
||||
|
||||
/*
|
||||
* End of File: dashboardSlice.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
16
app/modules/Dashboard/redux/index.ts
Normal file
16
app/modules/Dashboard/redux/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Dashboard redux
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as dashboardReducer } from './dashboardSlice';
|
||||
export * from './dashboardActions';
|
||||
export * from './dashboardSelectors';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
175
app/modules/Dashboard/screens/DashboardScreen.tsx
Normal file
175
app/modules/Dashboard/screens/DashboardScreen.tsx
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* File: DashboardScreen.tsx
|
||||
* Description: Sample dashboard screen using Clinical Blue Interface and shared components
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { View, Text, StyleSheet, ScrollView } from 'react-native';
|
||||
import { Button } from 'shared/src/components/Button';
|
||||
import { Card, InfoCard } from 'shared/src/components/Card';
|
||||
import { TextInput, SearchInput } from 'shared/src/components/Input';
|
||||
import { Modal, ConfirmModal, AlertModal } from 'shared/src/components/Modal';
|
||||
import { Spinner, LoadingOverlay } from 'shared/src/components/Loading';
|
||||
import { CustomIcon, IconButton } from 'shared/src/components/Icons';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
/**
|
||||
* DashboardScreen demonstrates usage of all shared, themed components.
|
||||
* This is a reference implementation for other modules.
|
||||
*/
|
||||
const DashboardScreen: React.FC = () => {
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [confirmVisible, setConfirmVisible] = useState(false);
|
||||
const [alertVisible, setAlertVisible] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [search, setSearch] = useState('');
|
||||
const [input, setInput] = useState('');
|
||||
|
||||
return (
|
||||
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
|
||||
<Text style={styles.header}>Dashboard</Text>
|
||||
<SearchInput
|
||||
placeholder="Search cases..."
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
containerStyle={styles.search}
|
||||
/>
|
||||
<InfoCard>
|
||||
<Text style={styles.infoTitle}>Welcome, Dr. Smith</Text>
|
||||
<Text style={styles.infoText}>On-Call Status: <Text style={styles.active}>ACTIVE</Text></Text>
|
||||
</InfoCard>
|
||||
<Card>
|
||||
<Text style={styles.cardTitle}>Critical Cases</Text>
|
||||
<View style={styles.row}>
|
||||
<CustomIcon name="alert" color={Colors.error} size={24} />
|
||||
<Text style={styles.criticalText}>Bed 3 - Hemorrhage (93% AI)</Text>
|
||||
<Button title="Review Now" onPress={() => setModalVisible(true)} style={styles.button} />
|
||||
</View>
|
||||
</Card>
|
||||
<Card>
|
||||
<Text style={styles.cardTitle}>Routine Cases</Text>
|
||||
<View style={styles.row}>
|
||||
<CustomIcon name="check-circle" color={Colors.success} size={24} />
|
||||
<Text style={styles.routineText}>Bed 12 - Headache (99% AI)</Text>
|
||||
<IconButton name="chevron-right" onPress={() => setConfirmVisible(true)} />
|
||||
</View>
|
||||
</Card>
|
||||
<TextInput
|
||||
placeholder="Add note..."
|
||||
value={input}
|
||||
onChangeText={setInput}
|
||||
style={styles.input}
|
||||
/>
|
||||
<Button title="Show Alert" onPress={() => setAlertVisible(true)} style={styles.button} />
|
||||
<Button title={loading ? "Loading..." : "Show Loading Overlay"} onPress={() => setLoading(true)} style={styles.button} />
|
||||
<Spinner style={styles.spinner} />
|
||||
{/* Modals */}
|
||||
<Modal visible={modalVisible} onRequestClose={() => setModalVisible(false)}>
|
||||
<Text style={styles.modalTitle}>Critical Case Details</Text>
|
||||
<Text>Patient: John Doe, 45M</Text>
|
||||
<Button title="Close" onPress={() => setModalVisible(false)} style={styles.button} />
|
||||
</Modal>
|
||||
<ConfirmModal
|
||||
visible={confirmVisible}
|
||||
title="Confirm Action"
|
||||
message="Are you sure you want to proceed?"
|
||||
onConfirm={() => { setConfirmVisible(false); }}
|
||||
onCancel={() => setConfirmVisible(false)}
|
||||
/>
|
||||
<AlertModal
|
||||
visible={alertVisible}
|
||||
title="Critical Alert"
|
||||
message="Acute Subdural Hemorrhage detected!"
|
||||
iconName="alert-circle"
|
||||
iconColor={Colors.error}
|
||||
onDismiss={() => setAlertVisible(false)}
|
||||
/>
|
||||
<LoadingOverlay visible={loading} />
|
||||
</ScrollView>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.background,
|
||||
},
|
||||
content: {
|
||||
padding: Spacing.lg,
|
||||
},
|
||||
header: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.title,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.lg,
|
||||
textAlign: 'center',
|
||||
},
|
||||
search: {
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
infoTitle: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.lg,
|
||||
color: Colors.textPrimary,
|
||||
marginBottom: Spacing.xs,
|
||||
},
|
||||
infoText: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textSecondary,
|
||||
},
|
||||
active: {
|
||||
color: Colors.success,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
cardTitle: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textPrimary,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
criticalText: {
|
||||
color: Colors.error,
|
||||
flex: 1,
|
||||
marginLeft: Spacing.sm,
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
},
|
||||
routineText: {
|
||||
color: Colors.success,
|
||||
flex: 1,
|
||||
marginLeft: Spacing.sm,
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
},
|
||||
button: {
|
||||
marginLeft: Spacing.sm,
|
||||
},
|
||||
input: {
|
||||
marginVertical: Spacing.md,
|
||||
},
|
||||
spinner: {
|
||||
marginVertical: Spacing.md,
|
||||
},
|
||||
modalTitle: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.lg,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.md,
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default DashboardScreen;
|
||||
|
||||
/*
|
||||
* End of File: DashboardScreen.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
14
app/modules/Dashboard/screens/index.ts
Normal file
14
app/modules/Dashboard/screens/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Dashboard screens
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as DashboardScreen } from './DashboardScreen';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
27
app/modules/Dashboard/services/caseAPI.ts
Normal file
27
app/modules/Dashboard/services/caseAPI.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* File: caseAPI.ts
|
||||
* Description: API service for fetching cases using apisauce
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { create } from 'apisauce';
|
||||
|
||||
const api = create({
|
||||
baseURL: 'https://api.example.com', // TODO: Replace with actual endpoint
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
/**
|
||||
* getCases - fetches the list of cases from the server
|
||||
*/
|
||||
export const caseAPI = {
|
||||
getCases: () => api.get('/cases'),
|
||||
// Add more endpoints as needed
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: caseAPI.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
16
app/modules/Dashboard/services/index.ts
Normal file
16
app/modules/Dashboard/services/index.ts
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Dashboard services
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export * from './caseAPI';
|
||||
export * from './websocketService';
|
||||
export * from './notificationService';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
20
app/modules/Dashboard/services/notificationService.ts
Normal file
20
app/modules/Dashboard/services/notificationService.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* File: notificationService.ts
|
||||
* Description: Service for sending notifications (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
// Stub for notification service
|
||||
export const notificationService = {
|
||||
sendNotification: (title: string, message: string) => {
|
||||
// TODO: Implement notification logic
|
||||
// Example: PushNotification.localNotification({ title, message })
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: notificationService.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
25
app/modules/Dashboard/services/websocketService.ts
Normal file
25
app/modules/Dashboard/services/websocketService.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* File: websocketService.ts
|
||||
* Description: Service for real-time websocket updates (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
// Stub for websocket connection
|
||||
class WebsocketService {
|
||||
subscribe(callback: (data: any) => void) {
|
||||
// TODO: Implement websocket subscription
|
||||
// Example: ws.onmessage = (event) => callback(event.data)
|
||||
}
|
||||
unsubscribe() {
|
||||
// TODO: Implement unsubscribe logic
|
||||
}
|
||||
}
|
||||
|
||||
export const websocketService = new WebsocketService();
|
||||
|
||||
/*
|
||||
* End of File: websocketService.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
25
app/modules/Profile/__tests__/ProfileHeader.test.tsx
Normal file
25
app/modules/Profile/__tests__/ProfileHeader.test.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* File: ProfileHeader.test.tsx
|
||||
* Description: Test for ProfileHeader component
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react-native';
|
||||
import ProfileHeader from '../components/ProfileHeader';
|
||||
|
||||
describe('ProfileHeader', () => {
|
||||
it('renders avatar and name', () => {
|
||||
const { getByText } = render(
|
||||
<ProfileHeader user={{ name: 'Dr. Smith', avatar: 'https://example.com/avatar.png' }} />
|
||||
);
|
||||
expect(getByText('Dr. Smith')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: ProfileHeader.test.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
26
app/modules/Profile/__tests__/profileAPI.test.ts
Normal file
26
app/modules/Profile/__tests__/profileAPI.test.ts
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* File: profileAPI.test.ts
|
||||
* Description: Test for profileAPI service
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { profileAPI } from '../services/profileAPI';
|
||||
|
||||
jest.mock('apisauce', () => ({
|
||||
create: () => ({ get: jest.fn(() => Promise.resolve({ ok: true, data: { id: '1', name: 'Dr. Smith', email: 'dr@hospital.com' } })) })
|
||||
}));
|
||||
|
||||
describe('profileAPI', () => {
|
||||
it('calls getProfile and returns data', async () => {
|
||||
const response = await profileAPI.getProfile('1');
|
||||
expect(response.ok).toBe(true);
|
||||
expect(response.data).toHaveProperty('email', 'dr@hospital.com');
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: profileAPI.test.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
37
app/modules/Profile/__tests__/profileSlice.test.ts
Normal file
37
app/modules/Profile/__tests__/profileSlice.test.ts
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* File: profileSlice.test.ts
|
||||
* Description: Test for profileSlice reducer
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import reducer, { fetchProfileSuccess, fetchProfileFailure, UserProfile } from '../redux/profileSlice';
|
||||
|
||||
const initialState = {
|
||||
userProfile: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
describe('profileSlice', () => {
|
||||
it('handles fetchProfileSuccess', () => {
|
||||
const user: UserProfile = { id: '1', name: 'Dr. Smith', email: 'dr@hospital.com' };
|
||||
const state = reducer(initialState, fetchProfileSuccess(user));
|
||||
expect(state.userProfile).toEqual(user);
|
||||
expect(state.loading).toBe(false);
|
||||
expect(state.error).toBeNull();
|
||||
});
|
||||
|
||||
it('handles fetchProfileFailure', () => {
|
||||
const error = 'Failed to fetch';
|
||||
const state = reducer(initialState, fetchProfileFailure(error));
|
||||
expect(state.loading).toBe(false);
|
||||
expect(state.error).toBe(error);
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* End of File: profileSlice.test.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
55
app/modules/Profile/components/PreferencesForm.tsx
Normal file
55
app/modules/Profile/components/PreferencesForm.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* File: PreferencesForm.tsx
|
||||
* Description: Component for user preferences form (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface PreferencesFormProps {
|
||||
preferences: any;
|
||||
onChange: (changes: any) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* PreferencesForm - displays user preferences form (stub)
|
||||
*/
|
||||
const PreferencesForm: React.FC<PreferencesFormProps> = ({ preferences, onChange }) => (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Preferences</Text>
|
||||
<Text style={styles.stub}>[Preferences Form Fields Here]</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
padding: Spacing.md,
|
||||
backgroundColor: Colors.backgroundAlt,
|
||||
borderRadius: 8,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.md,
|
||||
textAlign: 'center',
|
||||
},
|
||||
stub: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textSecondary,
|
||||
textAlign: 'center',
|
||||
marginTop: Spacing.lg,
|
||||
},
|
||||
});
|
||||
|
||||
export default PreferencesForm;
|
||||
|
||||
/*
|
||||
* End of File: PreferencesForm.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
50
app/modules/Profile/components/ProfileHeader.tsx
Normal file
50
app/modules/Profile/components/ProfileHeader.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* File: ProfileHeader.tsx
|
||||
* Description: Component for displaying user avatar and name
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, Image, StyleSheet } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface ProfileHeaderProps {
|
||||
user: { name: string; avatar?: string };
|
||||
}
|
||||
|
||||
/**
|
||||
* ProfileHeader - displays user avatar and name
|
||||
*/
|
||||
const ProfileHeader: React.FC<ProfileHeaderProps> = ({ user }) => (
|
||||
<View style={styles.container}>
|
||||
{user.avatar && <Image source={{ uri: user.avatar }} style={styles.avatar} />}
|
||||
<Text style={styles.name}>{user.name}</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
avatar: {
|
||||
width: 64,
|
||||
height: 64,
|
||||
borderRadius: 32,
|
||||
marginBottom: Spacing.sm,
|
||||
},
|
||||
name: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.lg,
|
||||
color: Colors.primary,
|
||||
},
|
||||
});
|
||||
|
||||
export default ProfileHeader;
|
||||
|
||||
/*
|
||||
* End of File: ProfileHeader.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
55
app/modules/Profile/components/SecuritySettings.tsx
Normal file
55
app/modules/Profile/components/SecuritySettings.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* File: SecuritySettings.tsx
|
||||
* Description: Component for user security settings (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface SecuritySettingsProps {
|
||||
security: any;
|
||||
onChange: (changes: any) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* SecuritySettings - displays user security settings (stub)
|
||||
*/
|
||||
const SecuritySettings: React.FC<SecuritySettingsProps> = ({ security, onChange }) => (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Security Settings</Text>
|
||||
<Text style={styles.stub}>[Security Options Here]</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
padding: Spacing.md,
|
||||
backgroundColor: Colors.backgroundAlt,
|
||||
borderRadius: 8,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.md,
|
||||
textAlign: 'center',
|
||||
},
|
||||
stub: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textSecondary,
|
||||
textAlign: 'center',
|
||||
marginTop: Spacing.lg,
|
||||
},
|
||||
});
|
||||
|
||||
export default SecuritySettings;
|
||||
|
||||
/*
|
||||
* End of File: SecuritySettings.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
75
app/modules/Profile/components/SettingsPanel.tsx
Normal file
75
app/modules/Profile/components/SettingsPanel.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* File: SettingsPanel.tsx
|
||||
* Description: Component for displaying and toggling user settings (stub)
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet, Switch } from 'react-native';
|
||||
import { Colors, Spacing, Typography } from 'shared/src/theme';
|
||||
|
||||
interface SettingsPanelProps {
|
||||
settings: { notificationsEnabled: boolean; darkMode: boolean; language: string };
|
||||
onChange: (changes: Partial<SettingsPanelProps['settings']>) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* SettingsPanel - displays and toggles user settings (stub)
|
||||
*/
|
||||
const SettingsPanel: React.FC<SettingsPanelProps> = ({ settings, onChange }) => (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Settings</Text>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.label}>Notifications</Text>
|
||||
<Switch value={settings.notificationsEnabled} onValueChange={v => onChange({ notificationsEnabled: v })} />
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.label}>Dark Mode</Text>
|
||||
<Switch value={settings.darkMode} onValueChange={v => onChange({ darkMode: v })} />
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.label}>Language</Text>
|
||||
<Text style={styles.value}>{settings.language}</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
padding: Spacing.md,
|
||||
backgroundColor: Colors.backgroundAlt,
|
||||
borderRadius: 8,
|
||||
},
|
||||
title: {
|
||||
fontFamily: Typography.fontFamily.bold,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.primary,
|
||||
marginBottom: Spacing.md,
|
||||
textAlign: 'center',
|
||||
},
|
||||
row: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: Spacing.md,
|
||||
},
|
||||
label: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textPrimary,
|
||||
},
|
||||
value: {
|
||||
fontFamily: Typography.fontFamily.regular,
|
||||
fontSize: Typography.fontSize.md,
|
||||
color: Colors.textSecondary,
|
||||
},
|
||||
});
|
||||
|
||||
export default SettingsPanel;
|
||||
|
||||
/*
|
||||
* End of File: SettingsPanel.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
17
app/modules/Profile/components/index.ts
Normal file
17
app/modules/Profile/components/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Profile components
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as ProfileHeader } from './ProfileHeader';
|
||||
export { default as SettingsPanel } from './SettingsPanel';
|
||||
export { default as PreferencesForm } from './PreferencesForm';
|
||||
export { default as SecuritySettings } from './SecuritySettings';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
15
app/modules/Profile/hooks/index.ts
Normal file
15
app/modules/Profile/hooks/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Profile hooks
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export * from './useProfile';
|
||||
export * from './useSettings';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
22
app/modules/Profile/hooks/useProfile.ts
Normal file
22
app/modules/Profile/hooks/useProfile.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* File: useProfile.ts
|
||||
* Description: Custom hook for accessing user profile from redux
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectUserProfile } from '../redux/profileSelectors';
|
||||
|
||||
/**
|
||||
* useProfile - returns the current user profile from redux state
|
||||
*/
|
||||
export const useProfile = () => {
|
||||
return useSelector(selectUserProfile);
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useProfile.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
22
app/modules/Profile/hooks/useSettings.ts
Normal file
22
app/modules/Profile/hooks/useSettings.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* File: useSettings.ts
|
||||
* Description: Custom hook for accessing user settings from redux
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectUserSettings } from '../redux/profileSelectors';
|
||||
|
||||
/**
|
||||
* useSettings - returns the current user settings from redux state
|
||||
*/
|
||||
export const useSettings = () => {
|
||||
return useSelector(selectUserSettings);
|
||||
};
|
||||
|
||||
/*
|
||||
* End of File: useSettings.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
57
app/modules/Profile/navigation/ProfileNavigator.tsx
Normal file
57
app/modules/Profile/navigation/ProfileNavigator.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* File: ProfileNavigator.tsx
|
||||
* Description: Stack navigator for Profile module
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import { ProfileScreen, SettingsScreen, PreferencesScreen } from '../screens';
|
||||
import { Colors } from 'shared/src/theme';
|
||||
|
||||
export type ProfileStackParamList = {
|
||||
Profile: undefined;
|
||||
Settings: undefined;
|
||||
Preferences: undefined;
|
||||
};
|
||||
|
||||
const Stack = createStackNavigator<ProfileStackParamList>();
|
||||
|
||||
/**
|
||||
* ProfileNavigator sets up stack navigation for Profile module
|
||||
*/
|
||||
const ProfileNavigator: React.FC = () => (
|
||||
<Stack.Navigator
|
||||
initialRouteName="Profile"
|
||||
screenOptions={{
|
||||
headerStyle: { backgroundColor: Colors.primary },
|
||||
headerTintColor: Colors.background,
|
||||
headerTitleStyle: { fontWeight: 'bold' },
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
name="Profile"
|
||||
component={ProfileScreen}
|
||||
options={{ title: 'Profile' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Settings"
|
||||
component={SettingsScreen}
|
||||
options={{ title: 'Settings' }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="Preferences"
|
||||
component={PreferencesScreen}
|
||||
options={{ title: 'Preferences' }}
|
||||
/>
|
||||
</Stack.Navigator>
|
||||
);
|
||||
|
||||
export default ProfileNavigator;
|
||||
|
||||
/*
|
||||
* End of File: ProfileNavigator.tsx
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
14
app/modules/Profile/navigation/index.ts
Normal file
14
app/modules/Profile/navigation/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Profile navigation
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as ProfileNavigator } from './ProfileNavigator';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
17
app/modules/Profile/redux/index.ts
Normal file
17
app/modules/Profile/redux/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* File: index.ts
|
||||
* Description: Barrel export for Profile redux
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
export { default as profileReducer } from './profileSlice';
|
||||
export { default as settingsReducer } from './settingsSlice';
|
||||
export * from './profileActions';
|
||||
export * from './profileSelectors';
|
||||
|
||||
/*
|
||||
* End of File: index.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
38
app/modules/Profile/redux/profileActions.ts
Normal file
38
app/modules/Profile/redux/profileActions.ts
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* File: profileActions.ts
|
||||
* Description: Async actions (thunks) for Profile state
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { fetchProfileStart, fetchProfileSuccess, fetchProfileFailure } from './profileSlice';
|
||||
import { profileAPI } from '../services/profileAPI';
|
||||
|
||||
/**
|
||||
* Thunk to fetch user profile from API
|
||||
*/
|
||||
export const fetchProfile = createAsyncThunk(
|
||||
'profile/fetchProfile',
|
||||
async (userId: string, { dispatch, rejectWithValue }) => {
|
||||
try {
|
||||
dispatch(fetchProfileStart());
|
||||
const response = await profileAPI.getProfile(userId);
|
||||
if (response.ok && response.data) {
|
||||
dispatch(fetchProfileSuccess(response.data));
|
||||
} else {
|
||||
dispatch(fetchProfileFailure(response.problem || 'Unknown error'));
|
||||
return rejectWithValue(response.problem || 'Unknown error');
|
||||
}
|
||||
} catch (error: any) {
|
||||
dispatch(fetchProfileFailure(error.message));
|
||||
return rejectWithValue(error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/*
|
||||
* End of File: profileActions.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
19
app/modules/Profile/redux/profileSelectors.ts
Normal file
19
app/modules/Profile/redux/profileSelectors.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* File: profileSelectors.ts
|
||||
* Description: Selectors for Profile redux state
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { RootState } from 'app/redux/rootReducer';
|
||||
|
||||
export const selectUserProfile = (state: RootState) => state.profile.userProfile;
|
||||
export const selectProfileLoading = (state: RootState) => state.profile.loading;
|
||||
export const selectProfileError = (state: RootState) => state.profile.error;
|
||||
export const selectUserSettings = (state: RootState) => state.settings.settings;
|
||||
|
||||
/*
|
||||
* End of File: profileSelectors.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
76
app/modules/Profile/redux/profileSlice.ts
Normal file
76
app/modules/Profile/redux/profileSlice.ts
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* File: profileSlice.ts
|
||||
* Description: Redux slice for Profile state management
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
// Sample user profile type
|
||||
export interface UserProfile {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
avatar?: string;
|
||||
role?: string;
|
||||
}
|
||||
|
||||
// Profile state type
|
||||
interface ProfileState {
|
||||
userProfile: UserProfile | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const initialState: ProfileState = {
|
||||
userProfile: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Profile slice for managing user profile state
|
||||
*/
|
||||
const profileSlice = createSlice({
|
||||
name: 'profile',
|
||||
initialState,
|
||||
reducers: {
|
||||
fetchProfileStart(state) {
|
||||
state.loading = true;
|
||||
state.error = null;
|
||||
},
|
||||
fetchProfileSuccess(state, action: PayloadAction<UserProfile>) {
|
||||
state.userProfile = action.payload;
|
||||
state.loading = false;
|
||||
},
|
||||
fetchProfileFailure(state, action: PayloadAction<string>) {
|
||||
state.loading = false;
|
||||
state.error = action.payload;
|
||||
},
|
||||
updateProfile(state, action: PayloadAction<Partial<UserProfile>>) {
|
||||
if (state.userProfile) {
|
||||
state.userProfile = { ...state.userProfile, ...action.payload };
|
||||
}
|
||||
},
|
||||
clearProfile(state) {
|
||||
state.userProfile = null;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
fetchProfileStart,
|
||||
fetchProfileSuccess,
|
||||
fetchProfileFailure,
|
||||
updateProfile,
|
||||
clearProfile,
|
||||
} = profileSlice.actions;
|
||||
|
||||
export default profileSlice.reducer;
|
||||
|
||||
/*
|
||||
* End of File: profileSlice.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
63
app/modules/Profile/redux/settingsSlice.ts
Normal file
63
app/modules/Profile/redux/settingsSlice.ts
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* File: settingsSlice.ts
|
||||
* Description: Redux slice for user settings state management
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
|
||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
|
||||
export interface UserSettings {
|
||||
notificationsEnabled: boolean;
|
||||
darkMode: boolean;
|
||||
language: string;
|
||||
}
|
||||
|
||||
interface SettingsState {
|
||||
settings: UserSettings;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const initialState: SettingsState = {
|
||||
settings: {
|
||||
notificationsEnabled: true,
|
||||
darkMode: false,
|
||||
language: 'en',
|
||||
},
|
||||
loading: false,
|
||||
error: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* settingsSlice for managing user settings
|
||||
*/
|
||||
const settingsSlice = createSlice({
|
||||
name: 'settings',
|
||||
initialState,
|
||||
reducers: {
|
||||
updateSettings(state, action: PayloadAction<Partial<UserSettings>>) {
|
||||
state.settings = { ...state.settings, ...action.payload };
|
||||
},
|
||||
setSettingsLoading(state, action: PayloadAction<boolean>) {
|
||||
state.loading = action.payload;
|
||||
},
|
||||
setSettingsError(state, action: PayloadAction<string | null>) {
|
||||
state.error = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
updateSettings,
|
||||
setSettingsLoading,
|
||||
setSettingsError,
|
||||
} = settingsSlice.actions;
|
||||
|
||||
export default settingsSlice.reducer;
|
||||
|
||||
/*
|
||||
* End of File: settingsSlice.ts
|
||||
* Design & Developed by Tech4Biz Solutions
|
||||
* Copyright (c) Spurrin Innovations. All rights reserved.
|
||||
*/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user