code generated for dashboard case view ,profile and auth section

This commit is contained in:
yashwin-foxy 2025-07-18 18:40:19 +05:30
parent c7288f54a8
commit 20ab5d9d21
142 changed files with 7392 additions and 754 deletions

View File

@ -0,0 +1,3 @@
---
alwaysApply: true
---

734
README.md
View File

@ -1,698 +1,52 @@
1. RADIOLOGIST APP - DETAILED WORKFLOW
1.1 App Launch & Authentication
App Launch → Biometric Auth → Dashboard Loading
Load Case Queue → Priority Sort → Display Cases
Critical (RED) → Urgent (ORANGE) → Routine (GREEN)
1.2 Critical Finding Workflow (Acute Subdural Hemorrhage)
STEP 1: Alert Reception
Push Notification → "CRITICAL - Acute Subdural Hemorrhage"
Tap Notification → App Opens → Case Preview
Patient: John Doe, Age 45, Head Trauma
AI Confidence: 93%
Midline Shift: 7mm
# NeoScan Radiologist App
STEP 2: Full Case Review
Case Preview → Open DICOM Viewer
AI Overlay ON → Hemorrhage Highlighted
Review Images → Confirm Finding → Add Observations
"Large acute subdural hemorrhage with mass effect"
A next-generation radiology workflow app designed for rapid, AI-assisted case review and reporting. Built with a modular architecture and a Clinical Blue Interface theme.
STEP 3: Report Generation
Voice-to-Text → "Acute subdural hemorrhage..."
Review Text → Edit if needed → Submit Report
Preliminary Report Sent (< 5 minutes)
## Features
- Modular structure: Auth, Dashboard, Case Review, Reporting, Notifications, Analytics, Profile
- Clinical Blue Interface theme
- Biometric and PIN authentication
- Real-time case queue with priority sorting
- DICOM viewer with AI overlays
- Voice-to-text reporting
- Push notifications for critical findings
- Redux state management
STEP 4: System Learning
System Logs → Radiologist Confirmation
Time Metrics Recorded → Model Improvement Data
1.3 Routine Scan Workflow (Negative Finding)
STEP 1: Queue Processing
Dashboard → Routine Queue → Select Case
Patient: Jane Smith, Age 30, Headache
AI: No acute abnormalities (99% confidence)
STEP 2: Confirmation Review
DICOM Viewer → Quick Review → Confirm Negative
"No acute intracranial abnormality"
STEP 3: Report & Documentation
Submit Report → System Logs Concordance
Next Case in Queue
1.4 System Uncertainty Protocol
AI Confidence 70-85% → "REVIEW RECOMMENDED"
Special Yellow Flag → Enhanced Attention Required
Detailed Review → Feedback to System
3. RADIOLOGIST APP WIREFRAMES
3.1 Login Screen
+------------------------+
| [HOSPITAL LOGO] |
| |
| Radiologist Portal |
| |
| [👤 Dr. Smith] |
| [🔒 Biometric Login] |
| |
| [Face ID Icon] |
| Touch to Login |
| |
| OR |
| |
| PIN: [****] |
| [LOGIN] |
| |
| Emergency Access |
+------------------------+
3.2 Dashboard - Case Queue
+------------------------+
| Dr. Smith [🔔3][⚙️] |
| On-Call Status: ACTIVE |
| |
| CRITICAL CASES |
| 🔴 Bed 3 - Hemorrhage |
| 📱 2 min ago |
| AI: 93% confidence |
| [REVIEW NOW] |
| |
| 🔴 Bed 7 - Stroke |
| 📱 5 min ago |
| AI: 87% confidence |
| [REVIEW NOW] |
| |
| URGENT CASES (4) |
| 🟡 Show All |
| |
| ROUTINE CASES (12) |
| 🟢 Show All |
+------------------------+
3.3 Critical Case Details
+------------------------+
| ← CRITICAL CASE |
| |
| Patient: John Doe, 45M |
| Bed: 3 | Time: 14:35 |
| Clinical: Head trauma |
| |
| AI ANALYSIS: |
| 🔴 Acute Subdural |
| Confidence: 93% |
| Midline Shift: 7mm |
| Mass Effect: Present |
| |
| [VIEW DICOM IMAGES] |
| [VOICE REPORT] |
| [QUICK REPORT] |
| |
| Status: PENDING REVIEW |
+------------------------+
3.4 DICOM Viewer
+------------------------+
| ← John Doe - CT Brain |
| |
| [ CT SCAN IMAGE ]|
| [ WITH AI OVERLAY ]|
| [ HEMORRHAGE ]|
| [ HIGHLIGHTED ]|
| |
| Tools: [🔍][📏][✏️] |
| AI: [ON] Conf: 93% |
| |
| Measurements: |
| • Hemorrhage: 3.2cm |
| • Midline: 7mm shift |
| |
| [🎤 START REPORT] |
+------------------------+
3.5 Voice Report Interface
+------------------------+
| ← Voice Report |
| |
| [🎤 RECORDING 01:23] |
| |
| "There is a large |
| acute subdural |
| hemorrhage in the |
| right frontoparietal |
| region..." |
| |
| [PAUSE] [STOP] [PLAY] |
| |
| [SAVE DRAFT] |
| [SUBMIT REPORT] |
| |
| Estimated: 2 min left |
+------------------------+
5.1 React Native Project Structure
🏥 RADIOLOGIST APP STRUCTURE
NeoScan_Radiologist/
├── app/
│ ├── modules/ # 🌐 Feature-wise modular architecture
│ │ ├── Auth/ # 🔐 Authentication Module
│ │ │ ├── components/
│ │ │ │ ├── BiometricLogin.tsx
│ │ │ │ ├── PinInput.tsx
│ │ │ │ ├── EmergencyAccess.tsx
│ │ │ │ └── index.ts
│ │ │ ├── screens/
│ │ │ │ ├── LoginScreen.tsx
│ │ │ │ ├── SetupBiometricScreen.tsx
│ │ │ │ └── index.ts
│ │ │ ├── hooks/
│ │ │ │ ├── useAuth.ts
│ │ │ │ ├── useBiometric.ts
│ │ │ │ └── index.ts
│ │ │ ├── redux/
│ │ │ │ ├── authSlice.ts
│ │ │ │ ├── authActions.ts
│ │ │ │ ├── authSelectors.ts
│ │ │ │ └── index.ts
│ │ │ ├── services/
│ │ │ │ ├── authAPI.ts
│ │ │ │ ├── biometricService.ts
│ │ │ │ └── index.ts
│ │ │ ├── __tests__/
│ │ │ │ ├── AuthService.test.ts
│ │ │ │ ├── LoginScreen.test.tsx
│ │ │ │ └── authSlice.test.ts
│ │ │ └── index.ts
│ │ │
│ │ ├── Dashboard/ # 📊 Dashboard Module
│ │ │ ├── components/
│ │ │ │ ├── CaseQueue.tsx
│ │ │ │ ├── CaseCard.tsx
│ │ │ │ ├── PriorityIndicator.tsx
│ │ │ │ ├── StatsPanel.tsx
│ │ │ │ ├── FilterBar.tsx
│ │ │ │ └── index.ts
│ │ │ ├── screens/
│ │ │ │ ├── DashboardScreen.tsx
│ │ │ │ ├── CaseListScreen.tsx
│ │ │ │ └── index.ts
│ │ │ ├── hooks/
│ │ │ │ ├── useCaseQueue.ts
│ │ │ │ ├── useRealTimeUpdates.ts
│ │ │ │ ├── useFilterCases.ts
│ │ │ │ └── index.ts
│ │ │ ├── redux/
│ │ │ │ ├── dashboardSlice.ts
│ │ │ │ ├── caseQueueSlice.ts
│ │ │ │ ├── dashboardActions.ts
│ │ │ │ ├── dashboardSelectors.ts
│ │ │ │ └── index.ts
│ │ │ ├── services/
│ │ │ │ ├── caseAPI.ts
│ │ │ │ ├── websocketService.ts
│ │ │ │ ├── notificationService.ts
│ │ │ │ └── index.ts
│ │ │ ├── __tests__/
│ │ │ │ ├── CaseQueue.test.tsx
│ │ │ │ ├── dashboardSlice.test.ts
│ │ │ │ └── caseAPI.test.ts
│ │ │ └── index.ts
│ │ │
│ │ ├── CaseReview/ # 🔍 Case Review Module
│ │ │ ├── components/
│ │ │ │ ├── DICOMViewer.tsx
│ │ │ │ ├── AIOverlay.tsx
│ │ │ │ ├── MeasurementTools.tsx
│ │ │ │ ├── AnnotationTools.tsx
│ │ │ │ ├── CaseMetadata.tsx
│ │ │ │ ├── PriorStudyComparison.tsx
│ │ │ │ └── index.ts
│ │ │ ├── screens/
│ │ │ │ ├── CaseDetailsScreen.tsx
│ │ │ │ ├── DICOMViewerScreen.tsx
│ │ │ │ ├── ComparisonScreen.tsx
│ │ │ │ └── index.ts
│ │ │ ├── hooks/
│ │ │ │ ├── useDICOMViewer.ts
│ │ │ │ ├── useAIOverlay.ts
│ │ │ │ ├── useMeasurements.ts
│ │ │ │ ├── useAnnotations.ts
│ │ │ │ └── index.ts
│ │ │ ├── redux/
│ │ │ │ ├── caseReviewSlice.ts
│ │ │ │ ├── dicomSlice.ts
│ │ │ │ ├── aiAnalysisSlice.ts
│ │ │ │ ├── caseReviewActions.ts
│ │ │ │ ├── caseReviewSelectors.ts
│ │ │ │ └── index.ts
│ │ │ ├── services/
│ │ │ │ ├── dicomAPI.ts
│ │ │ │ ├── aiAnalysisAPI.ts
│ │ │ │ ├── dicomParser.ts
│ │ │ │ ├── imageProcessor.ts
│ │ │ │ └── index.ts
│ │ │ ├── __tests__/
│ │ │ │ ├── DICOMViewer.test.tsx
│ │ │ │ ├── AIOverlay.test.tsx
│ │ │ │ ├── dicomParser.test.ts
│ │ │ │ └── caseReviewSlice.test.ts
│ │ │ └── index.ts
│ │ │
│ │ ├── Reporting/ # 📝 Reporting Module
│ │ │ ├── components/
│ │ │ │ ├── VoiceRecorder.tsx
│ │ │ │ ├── ReportEditor.tsx
│ │ │ │ ├── ReportTemplate.tsx
│ │ │ │ ├── QuickReportButtons.tsx
│ │ │ │ ├── ReportPreview.tsx
│ │ │ │ └── index.ts
│ │ │ ├── screens/
│ │ │ │ ├── ReportingScreen.tsx
│ │ │ │ ├── VoiceReportScreen.tsx
│ │ │ │ ├── ReportHistoryScreen.tsx
│ │ │ │ └── index.ts
│ │ │ ├── hooks/
│ │ │ │ ├── useVoiceRecording.ts
│ │ │ │ ├── useReportGeneration.ts
│ │ │ │ ├── useSpeechToText.ts
│ │ │ │ └── index.ts
│ │ │ ├── redux/
│ │ │ │ ├── reportingSlice.ts
│ │ │ │ ├── voiceRecordingSlice.ts
│ │ │ │ ├── reportingActions.ts
│ │ │ │ ├── reportingSelectors.ts
│ │ │ │ └── index.ts
│ │ │ ├── services/
│ │ │ │ ├── reportAPI.ts
│ │ │ │ ├── voiceToTextAPI.ts
│ │ │ │ ├── reportTemplateService.ts
│ │ │ │ └── index.ts
│ │ │ ├── __tests__/
│ │ │ │ ├── VoiceRecorder.test.tsx
│ │ │ │ ├── reportingSlice.test.ts
│ │ │ │ └── voiceToTextAPI.test.ts
│ │ │ └── index.ts
│ │ │
│ │ ├── Notifications/ # 🔔 Notifications Module
│ │ │ ├── components/
│ │ │ │ ├── NotificationPanel.tsx
│ │ │ │ ├── AlertBanner.tsx
│ │ │ │ ├── CriticalAlert.tsx
│ │ │ │ ├── NotificationSettings.tsx
│ │ │ │ └── index.ts
│ │ │ ├── screens/
│ │ │ │ ├── NotificationsScreen.tsx
│ │ │ │ ├── AlertDetailsScreen.tsx
│ │ │ │ └── index.ts
│ │ │ ├── hooks/
│ │ │ │ ├── useNotifications.ts
│ │ │ │ ├── usePushNotifications.ts
│ │ │ │ ├── useAlertHandling.ts
│ │ │ │ └── index.ts
│ │ │ ├── redux/
│ │ │ │ ├── notificationsSlice.ts
│ │ │ │ ├── alertsSlice.ts
│ │ │ │ ├── notificationActions.ts
│ │ │ │ ├── notificationSelectors.ts
│ │ │ │ └── index.ts
│ │ │ ├── services/
│ │ │ │ ├── pushNotificationService.ts
│ │ │ │ ├── alertService.ts
│ │ │ │ ├── notificationAPI.ts
│ │ │ │ └── index.ts
│ │ │ ├── __tests__/
│ │ │ │ ├── NotificationPanel.test.tsx
│ │ │ │ ├── alertService.test.ts
│ │ │ │ └── notificationsSlice.test.ts
│ │ │ └── index.ts
│ │ │
│ │ ├── Analytics/ # 📈 Analytics Module
│ │ │ ├── components/
│ │ │ │ ├── PerformanceMetrics.tsx
│ │ │ │ ├── ResponseTimeChart.tsx
│ │ │ │ ├── ConcordanceStats.tsx
│ │ │ │ ├── WorkloadAnalysis.tsx
│ │ │ │ └── index.ts
│ │ │ ├── screens/
│ │ │ │ ├── AnalyticsScreen.tsx
│ │ │ │ ├── PerformanceScreen.tsx
│ │ │ │ └── index.ts
│ │ │ ├── hooks/
│ │ │ │ ├── useAnalytics.ts
│ │ │ │ ├── usePerformanceMetrics.ts
│ │ │ │ └── index.ts
│ │ │ ├── redux/
│ │ │ │ ├── analyticsSlice.ts
│ │ │ │ ├── analyticsActions.ts
│ │ │ │ ├── analyticsSelectors.ts
│ │ │ │ └── index.ts
│ │ │ ├── services/
│ │ │ │ ├── analyticsAPI.ts
│ │ │ │ ├── metricsCollector.ts
│ │ │ │ └── index.ts
│ │ │ ├── __tests__/
│ │ │ │ ├── PerformanceMetrics.test.tsx
│ │ │ │ ├── analyticsSlice.test.ts
│ │ │ │ └── metricsCollector.test.ts
│ │ │ └── index.ts
│ │ │
│ │ └── Profile/ # 👤 User Profile Module
│ │ ├── components/
│ │ │ ├── ProfileHeader.tsx
│ │ │ ├── SettingsPanel.tsx
│ │ │ ├── PreferencesForm.tsx
│ │ │ ├── SecuritySettings.tsx
│ │ │ └── index.ts
│ │ ├── screens/
│ │ │ ├── ProfileScreen.tsx
│ │ │ ├── SettingsScreen.tsx
│ │ │ ├── PreferencesScreen.tsx
│ │ │ └── index.ts
│ │ ├── hooks/
│ │ │ ├── useProfile.ts
│ │ │ ├── useSettings.ts
│ │ │ └── index.ts
│ │ ├── redux/
│ │ │ ├── profileSlice.ts
│ │ │ ├── settingsSlice.ts
│ │ │ ├── profileActions.ts
│ │ │ ├── profileSelectors.ts
│ │ │ └── index.ts
│ │ ├── services/
│ │ │ ├── profileAPI.ts
│ │ │ ├── settingsAPI.ts
│ │ │ └── index.ts
│ │ ├── __tests__/
│ │ │ ├── ProfileHeader.test.tsx
│ │ │ ├── profileSlice.test.ts
│ │ │ └── profileAPI.test.ts
│ │ └── index.ts
│ │
│ ├── navigation/ # 🧭 Navigation Setup
│ │ ├── AppNavigator.tsx
│ │ ├── AuthNavigator.tsx
│ │ ├── MainNavigator.tsx
│ │ ├── TabNavigator.tsx
│ │ ├── ModalNavigator.tsx
│ │ ├── navigationTypes.ts
│ │ └── index.ts
│ │
│ ├── redux/ # 🗃️ Redux Store Setup
│ │ ├── store.ts
│ │ ├── rootReducer.ts
│ │ ├── middleware.ts
│ │ ├── persistConfig.ts
│ │ └── index.ts
│ │
│ ├── constants/ # 📋 Global Constants
│ │ ├── apiEndpoints.ts
│ │ ├── appConstants.ts
│ │ ├── errorMessages.ts
│ │ ├── notificationTypes.ts
│ │ ├── priorityLevels.ts
│ │ └── index.ts
│ │
│ ├── App.tsx # 🎯 Root Component
│ └── index.ts # 🚀 App Bootstrap
├── shared/ # ✅ Shared Components & Utilities
│ ├── src/
│ │ ├── components/ # 🎨 Global UI Components
│ │ │ ├── Button/
│ │ │ │ ├── Button.tsx
│ │ │ │ ├── Button.styles.ts
│ │ │ │ ├── Button.types.ts
│ │ │ │ └── index.ts
│ │ │ ├── Input/
│ │ │ │ ├── TextInput.tsx
│ │ │ │ ├── SearchInput.tsx
│ │ │ │ ├── Input.styles.ts
│ │ │ │ ├── Input.types.ts
│ │ │ │ └── index.ts
│ │ │ ├── Modal/
│ │ │ │ ├── Modal.tsx
│ │ │ │ ├── ConfirmModal.tsx
│ │ │ │ ├── AlertModal.tsx
│ │ │ │ ├── Modal.styles.ts
│ │ │ │ ├── Modal.types.ts
│ │ │ │ └── index.ts
│ │ │ ├── Card/
│ │ │ │ ├── Card.tsx
│ │ │ │ ├── InfoCard.tsx
│ │ │ │ ├── Card.styles.ts
│ │ │ │ ├── Card.types.ts
│ │ │ │ └── index.ts
│ │ │ ├── Loading/
│ │ │ │ ├── Spinner.tsx
│ │ │ │ ├── LoadingOverlay.tsx
│ │ │ │ ├── Loading.styles.ts
│ │ │ │ └── index.ts
│ │ │ ├── Icons/
│ │ │ │ ├── CustomIcon.tsx
│ │ │ │ ├── IconButton.tsx
│ │ │ │ ├── Icons.types.ts
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ │
│ │ ├── theme/ # 🎨 Design System
│ │ │ ├── colors.ts
│ │ │ ├── spacing.ts
│ │ │ ├── typography.ts
│ │ │ ├── shadows.ts
│ │ │ ├── borderRadius.ts
│ │ │ ├── animations.ts
│ │ │ └── index.ts
│ │ │
│ │ ├── assets/ # 📁 Static Assets
│ │ │ ├── icons/
│ │ │ │ ├── critical-alert.svg
│ │ │ │ ├── urgent-alert.svg
│ │ │ │ ├── routine-alert.svg
│ │ │ │ └── index.ts
│ │ │ ├── images/
│ │ │ │ ├── logo.png
│ │ │ │ ├── placeholder.png
│ │ │ │ └── index.ts
│ │ │ ├── fonts/
│ │ │ │ ├── Roboto-Regular.ttf
│ │ │ │ ├── Roboto-Bold.ttf
│ │ │ │ └── index.ts
│ │ │ └── sounds/
│ │ │ ├── critical-alert.wav
│ │ │ ├── urgent-alert.wav
│ │ │ └── routine-alert.wav
│ │ │
│ │ ├── utils/ # 🛠️ Utility Functions
│ │ │ ├── dateTime/
│ │ │ │ ├── formatDate.ts
│ │ │ │ ├── timeAgo.ts
│ │ │ │ └── index.ts
│ │ │ ├── validation/
│ │ │ │ ├── validators.ts
│ │ │ │ ├── schemas.ts
│ │ │ │ └── index.ts
│ │ │ ├── helpers/
│ │ │ │ ├── debounce.ts
│ │ │ │ ├── throttle.ts
│ │ │ │ ├── formatters.ts
│ │ │ │ └── index.ts
│ │ │ ├── constants/
│ │ │ │ ├── regex.ts
│ │ │ │ ├── formats.ts
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ │
│ │ ├── hooks/ # 🎣 Shared Custom Hooks
│ │ │ ├── useTheme.ts
│ │ │ ├── useDebounce.ts
│ │ │ ├── useKeyboard.ts
│ │ │ ├── useNetworkStatus.ts
│ │ │ ├── useOrientation.ts
│ │ │ └── index.ts
│ │ │
│ │ ├── types/ # 📝 TypeScript Types
│ │ │ ├── common.ts
│ │ │ ├── api.ts
│ │ │ ├── navigation.ts
│ │ │ ├── theme.ts
│ │ │ └── index.ts
│ │ │
│ │ └── index.ts # 📦 Barrel Export
│ │
│ ├── package.json # 📦 Shared Package Config
│ └── tsconfig.json # ⚙️ TypeScript Config
├── __tests__/ # 🧪 Global Tests
│ ├── App.test.tsx
│ ├── Navigation.test.tsx
│ ├── Redux.test.tsx
│ └── Integration.test.tsx
├── assets/ # 🖼️ App-wide Assets
│ ├── images/
│ ├── videos/
│ └── audio/
├── android/ # 🤖 Android Native
├── ios/ # 🍎 iOS Native
├── index.js # 🎯 RN Entry Point
├── package.json # 📦 Root Dependencies
├── tsconfig.json # ⚙️ Base TypeScript Config
├── metro.config.js # 📦 Metro Bundler Config
├── babel.config.js # 🔄 Babel Config
└── .eslintrc.js # 📏 ESLint Config
Key Features:
Real-time WebSocket connections
Offline caching for critical cases
Biometric authentication for radiologists
Performance monitoring with timing metrics
Radiologist App Options
Option 1: "Clinical Gray Spectrum"
Primary: #2C3E50 (Dark Blue-Gray)
Secondary: #34495E (Medium Blue-Gray)
Tertiary: #95A5A6 (Light Gray)
Text Primary: #2C3E50 (Dark Blue-Gray)
Text Secondary: #7F8C8D (Medium Gray)
Text Muted: #BDC3C7 (Light Gray)
Background: #F8F9FA (Light Gray)
Background Alt: #E9ECEF (Darker Light Gray)
Success: #27AE60 (Green)
Warning: #F39C12 (Orange)
Error: #E74C3C (Red)
Info: #3498DB (Blue)
Option 2: "Deep Tech Purple"
Primary: #6C5CE7 (Modern Purple)
Secondary: #A29BFE (Light Purple)
Tertiary: #DDD6FE (Very Light Purple)
Text Primary: #2D3436 (Charcoal)
Text Secondary: #636E72 (Gray)
Text Muted: #B2BEC3 (Light Gray)
Background: #FFFFFF (White)
Background Alt: #F8F7FF (Very Light Purple)
Success: #00B894 (Teal)
Warning: #FDCB6E (Yellow)
Error: #FD79A8 (Pink)
Info: #74B9FF (Light Blue)
Option 3: "Dark Professional"
Primary: #1A365D (Navy Blue)
Secondary: #2D3748 (Dark Gray)
Tertiary: #4A5568 (Medium Gray)
Text Primary: #1A202C (Very Dark Gray)
Text Secondary: #4A5568 (Medium Gray)
Text Muted: #718096 (Light Gray)
Background: #FFFFFF (White)
Background Alt: #F7FAFC (Off White)
Success: #38A169 (Green)
Warning: #DD6B20 (Orange)
Error: #E53E3E (Red)
Info: #3182CE (Blue)
Option 4: "Sophisticated Slate"
Primary: #475569 (Slate Gray)
Secondary: #64748B (Medium Slate)
Tertiary: #CBD5E1 (Light Slate)
Text Primary: #0F172A (Almost Black)
Text Secondary: #475569 (Slate Gray)
Text Muted: #94A3B8 (Light Slate)
Background: #FFFFFF (White)
Background Alt: #F8FAFC (Very Light Slate)
Success: #059669 (Emerald)
Warning: #D97706 (Amber)
Error: #DC2626 (Red)
Info: #0284C7 (Sky Blue)
Radiologist App - "Clinical Blue Interface"
Primary: #5B7CE6 (Blue Purple - from the selected buttons)
Secondary: #7B94F0 (Light Blue Purple)
Tertiary: #E8EFFF (Very Light Blue)
Quaternary: #3B5998 (Deep Blue)
Text Primary: #1A1D29 (Almost Black)
Text Secondary: #4A5568 (Medium Gray)
Text Muted: #9CA3AF (Light Gray)
Background: #FFFFFF (White)
Background Alt: #F8FAFF (Very Light Blue Tint)
Background Accent: #F1F5FF (Soft Blue)
Card Background: #FFFFFF (White with shadow)
Success: #10B981 (Green - from the security icon)
Warning: #F59E0B (Amber)
Error: #EF4444 (Red)
Info: #3B82F6 (Blue)
Border: #E5E7EB (Light Gray)
Selected State: #5B7CE6 (Same as Primary)
Inactive State: #F3F4F6 (Light Gray)
Shadow: rgba(91, 124, 230, 0.1) (Blue Shadow)
Gradient Background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)
Dark professional Prefered
```sh
# Using npm
npm start
# OR using Yarn
yarn start
## Project Structure
```
app/
modules/ # Feature modules (Auth, Dashboard, CaseReview, ...)
navigation/ # Navigation setup
redux/ # Redux store setup
constants/ # Global constants
shared/
src/
components/ # Shared UI components
theme/ # Design system (colors, typography, ...)
utils/ # Utility functions
hooks/ # Shared hooks
types/ # TypeScript types
assets/ # App-wide assets
```
## Step 2: Build and run your app
## Theme
- Primary: #5B7CE6
- Secondary: #7B94F0
- Tertiary: #E8EFFF
- Quaternary: #3B5998
- ... (see `shared/src/theme/colors.ts`)
With Metro running, open a new terminal window/pane from the root of your React Native project, and use one of the following commands to build and run your Android or iOS app:
## Setup
1. Install dependencies:
```sh
npm install
```
2. Run the app:
```sh
npm run android # or npm run ios
```
### Android
```sh
# Using npm
npm run android
# OR using Yarn
yarn android
```
### iOS
For iOS, remember to install CocoaPods dependencies (this only needs to be run on first clone or after updating native deps).
The first time you create a new project, run the Ruby bundler to install CocoaPods itself:
```sh
bundle install
```
Then, and every time you update your native dependencies, run:
```sh
bundle exec pod install
```
For more information, please visit [CocoaPods Getting Started guide](https://guides.cocoapods.org/using/getting-started.html).
```sh
# Using npm
npm run ios
# OR using Yarn
yarn ios
```
If everything is set up correctly, you should see your new app running in the Android Emulator, iOS Simulator, or your connected device.
This is one way to run your app — you can also build it directly from Android Studio or Xcode.
## Copyright
Design & Developed by Tech4Biz Solutions
Copyright (c) Spurrin Innovations. All rights reserved.

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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.
*/

View 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