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
|
||||||
|
---
|
||||||
730
README.md
730
README.md
@ -1,698 +1,52 @@
|
|||||||
1. RADIOLOGIST APP - DETAILED WORKFLOW
|
# NeoScan Radiologist App
|
||||||
1.1 App Launch & Authentication
|
|
||||||
App Launch → Biometric Auth → Dashboard Loading
|
|
||||||
↓
|
|
||||||
Load Case Queue → Priority Sort → Display Cases
|
|
||||||
↓
|
|
||||||
Critical (RED) → Urgent (ORANGE) → Routine (GREEN)
|
|
||||||
1.2 Critical Finding Workflow (Acute Subdural Hemorrhage)
|
|
||||||
STEP 1: Alert Reception
|
|
||||||
Push Notification → "CRITICAL - Acute Subdural Hemorrhage"
|
|
||||||
↓
|
|
||||||
Tap Notification → App Opens → Case Preview
|
|
||||||
↓
|
|
||||||
Patient: John Doe, Age 45, Head Trauma
|
|
||||||
AI Confidence: 93%
|
|
||||||
Midline Shift: 7mm
|
|
||||||
|
|
||||||
STEP 2: Full Case Review
|
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.
|
||||||
Case Preview → Open DICOM Viewer
|
|
||||||
↓
|
|
||||||
AI Overlay ON → Hemorrhage Highlighted
|
|
||||||
↓
|
|
||||||
Review Images → Confirm Finding → Add Observations
|
|
||||||
↓
|
|
||||||
"Large acute subdural hemorrhage with mass effect"
|
|
||||||
|
|
||||||
STEP 3: Report Generation
|
## Features
|
||||||
Voice-to-Text → "Acute subdural hemorrhage..."
|
- Modular structure: Auth, Dashboard, Case Review, Reporting, Notifications, Analytics, Profile
|
||||||
↓
|
- Clinical Blue Interface theme
|
||||||
Review Text → Edit if needed → Submit Report
|
- Biometric and PIN authentication
|
||||||
↓
|
- Real-time case queue with priority sorting
|
||||||
Preliminary Report Sent (< 5 minutes)
|
- DICOM viewer with AI overlays
|
||||||
|
- Voice-to-text reporting
|
||||||
|
- Push notifications for critical findings
|
||||||
|
- Redux state management
|
||||||
|
|
||||||
STEP 4: System Learning
|
## Project Structure
|
||||||
System Logs → Radiologist Confirmation
|
```
|
||||||
↓
|
app/
|
||||||
Time Metrics Recorded → Model Improvement Data
|
modules/ # Feature modules (Auth, Dashboard, CaseReview, ...)
|
||||||
1.3 Routine Scan Workflow (Negative Finding)
|
navigation/ # Navigation setup
|
||||||
STEP 1: Queue Processing
|
redux/ # Redux store setup
|
||||||
Dashboard → Routine Queue → Select Case
|
constants/ # Global constants
|
||||||
↓
|
shared/
|
||||||
Patient: Jane Smith, Age 30, Headache
|
src/
|
||||||
AI: No acute abnormalities (99% confidence)
|
components/ # Shared UI components
|
||||||
|
theme/ # Design system (colors, typography, ...)
|
||||||
STEP 2: Confirmation Review
|
utils/ # Utility functions
|
||||||
DICOM Viewer → Quick Review → Confirm Negative
|
hooks/ # Shared hooks
|
||||||
↓
|
types/ # TypeScript types
|
||||||
"No acute intracranial abnormality"
|
assets/ # App-wide assets
|
||||||
|
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Step 2: Build and run your app
|
## Theme
|
||||||
|
- Primary: #5B7CE6
|
||||||
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:
|
- Secondary: #7B94F0
|
||||||
|
- Tertiary: #E8EFFF
|
||||||
### Android
|
- Quaternary: #3B5998
|
||||||
|
- ... (see `shared/src/theme/colors.ts`)
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
1. Install dependencies:
|
||||||
```sh
|
```sh
|
||||||
# Using npm
|
npm install
|
||||||
npm run android
|
```
|
||||||
|
2. Run the app:
|
||||||
# OR using Yarn
|
```sh
|
||||||
yarn android
|
npm run android # or npm run ios
|
||||||
```
|
```
|
||||||
|
|
||||||
### iOS
|
## Copyright
|
||||||
|
Design & Developed by Tech4Biz Solutions
|
||||||
For iOS, remember to install CocoaPods dependencies (this only needs to be run on first clone or after updating native deps).
|
Copyright (c) Spurrin Innovations. All rights reserved.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
|
|||||||
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