diff --git a/.cursor/rules/appflow.mdc b/.cursor/rules/appflow.mdc index 4747c45..4cf0a5c 100644 --- a/.cursor/rules/appflow.mdc +++ b/.cursor/rules/appflow.mdc @@ -1,351 +1,193 @@ --- alwaysApply: true --- -# Physician App - Application Flow Rules +Radiologist App Flow Guide +1. Onboarding Flow +There are two ways for a radiologist to join the platform: +A. Self-Registration (via App) -## ๐Ÿš€ App Launch & Authentication Flow +Start App Signup โ†’ Radiologist enters email address +Email Check -### 1. Initial App Launch -``` -App Launch โ†’ Splash Screen โ†’ Authentication Check +If email already exists โ†’ Show message "Already Registered" โ†’ Redirect to Login +If new email โ†’ Continue to next step + + +Set Password โ†’ Create secure password +Enter Personal Info โ†’ Complete profile information +Upload Hospital ID โ†’ for verification +Select Hospital/Institution โ†’ Choose affiliated organization โ†’ Wait for admin approval + +Note: Radiologist registration requires additional verification of Uploaded hospital-ID. Status remains 'Inactive' until admin approval. +After Review โ†’ Login โ†’ Reset Password โ†’ Radiologist Dashboard +B. Admin-Created Radiologist + +Admin creates radiologist account in the system +Login credentials sent to radiologist's registered email address +Radiologist logs in using received credentials +Upload Hospital ID โ†’ Upload the ID-Card issued by the hospital +Reset Password โ†’ Access Radiologist Dashboard + +2. Dashboard Overview +When the radiologist logs in, they access a specialized Dashboard displaying: + +Cases Prediction Review (AI predictions over all review) +Total Cases Reviewed (lifetime review count like feedback given by radiologist) +Feedback Statistics (Agreed/Disagreed with AI predictions) +Priority Cases (High-severity cases requiring immediate attention) + +3. Patient List & Review Queue +Accessible from the Patient List Tab and Review Queue Tab. +Patient List View +Displays patient information in a structured table/card layout with the following details: + +Patient ID & Demographics +Clinical History (if available) +Scan Details (date, type, protocol) +Previous Radiology Reports (if any) +Modality +Report Status (available/not available) +Series Information + +Filtering Options: + +All Cases +Processed (Data already proceessed by AI) +Pending (Data need to proceesss) + +4. Case Review Interface +Clicking a case opens the Case Review Interface with: +Patient Information Panel: + +Patient ID & Demographics +Clinical History (if available) +Scan Details (date, type, protocol) +Previous Radiology Reports (if any) + +AI Analysis Panel: + +AI Predictions Summary +Confidence Scores for each finding +Severity Classifications +Location Mappings +Processing Time and Algorithm Version + +DICOM Viewer Panel: + +Full-screen DICOM image viewer with professional tools +Advanced viewing controls: + +Zoom functionality +Frame navigation with slide bar control + +Local DICOM Upload - Option to choose and load DICOM files from local system + +Feedback Panel: + +Overall Assessment + +โœ… Agree with AI - AI prediction is accurate +๐Ÿ”ถ Partially Agree - Some findings correct, others missed/incorrect +โŒ Disagree with AI - AI prediction is inaccurate + +Detailed Feedback by Finding + +True Positive โœ“ (AI correctly identified finding) +False Positive โœ— (AI incorrectly identified finding) +False Negative โœ— (AI missed actual finding) +Additional Findings (Findings not detected by AI) + +5. AI Predictions Analysis & DICOM Viewer +Series Navigation: + +Each Series contains multiple images +Thumbnail navigation strip +Scroll through series (e.g., Image 15 of 120) + +Detailed AI Analysis: + +Hemorrhage Detection + +Epidural +Subdural +Subarachnoid +Intraparenchymal +Intraventricular + +Stroke Analysis +Midline shift measurement + +Confidence Scoring: + +Overall Confidence: 85.6% +Per-finding Confidence: + +Subdural hematoma: 94.2% +Midline shift: 78.9% +No acute stroke: 91.5% + +6. Completed Reviews & History +Review History Tab: + +List of all previously reviewed cases +Review statistics and performance metrics + +Case Features: + +AI System Performance (Radiologist Perspective): + +Algorithm Accuracy based on radiologist feedback +Common AI Errors and patterns +Suggested Improvements submission portal + +8. Profile & Account Management +Profile Card: + +Display Name +Role (Radiologist) +Email Address +Profile Picture (Optional) + +Account Management Options: + +Edit Profile: + +Update personal details +Update dispaly name + +Change Password: + +Reset login password + + +Navigation Flow Summary +App Launch โ†“ -[Authenticated] โ†’ ER Dashboard -[Not Authenticated] โ†’ Login Screen -``` - -### 2. Authentication Flow Rules -``` -Login Screen Options: -โ”œโ”€โ”€ Hospital SSO Login (Primary) -โ”œโ”€โ”€ Credential Login (Fallback) -โ”œโ”€โ”€ Emergency Access (Quick) -โ””โ”€โ”€ Temporary Login (Limited) -``` - -### 3. SSO Integration Rules -- **Primary**: Hospital SSO integration -- **Fallback**: Username/password authentication -- **Emergency**: Quick access codes for urgent situations -- **Remember Device**: 30-day device authentication -- **Session Timeout**: 8 hours for security - -## ๐Ÿ“Š ER Dashboard Flow - -### 4. Dashboard Entry Points -``` -ER Dashboard โ†’ Load Patient List โ†’ Real-time Updates +Registration/Login โ†“ -โ”œโ”€โ”€ Critical Alerts (Priority 1) -โ”œโ”€โ”€ Pending Scans (Priority 2) -โ”œโ”€โ”€ Recent Reports (Priority 3) -โ””โ”€โ”€ All Patients (Complete View) -``` +Radiologist Dashboard (Main Hub) + โ”œโ”€โ”€ Review Queue + โ”‚ โ””โ”€โ”€ Case Review Interface + โ”‚ โ”œโ”€โ”€ Patient Info + โ”‚ โ”œโ”€โ”€ AI Analysis + โ”‚ โ”œโ”€โ”€ DICOM Viewer + โ”‚ โ””โ”€โ”€ Feedback Panel + โ”œโ”€โ”€ Completed Reviews + โ”‚ โ””โ”€โ”€ Review History & Analytics + โ”œโ”€โ”€ Performance Dashboard + โ”‚ โ””โ”€โ”€ Personal & AI Metrics + โ”œโ”€โ”€ Profile Management + โ”‚ โ”œโ”€โ”€ Edit Profile + โ”‚ โ””โ”€โ”€ Change Password + โ””โ”€โ”€ Logout +Key Features -### 5. Real-time Data Flow -``` -WebSocket Connection โ†’ Live Updates - โ†“ -โ”œโ”€โ”€ Patient Status Changes -โ”œโ”€โ”€ New Scan Results -โ”œโ”€โ”€ Critical Findings -โ”œโ”€โ”€ Bed Assignments -โ””โ”€โ”€ Shift Changes -``` +Comprehensive Case Review with advanced DICOM viewing capabilities +Detailed AI Feedback System for continuous algorithm improvement +Performance Analytics for personal and system-wide metrics +Professional-grade Tools for accurate radiological assessment +Streamlined Workflow optimized for radiology practice +Quality Assurance features for maintaining diagnostic standards +Educational Components for continuous learning and improvement -### 6. Alert Priority System -- **๐Ÿ”ด Critical**: Immediate action required (0-2 minutes) -- **๐ŸŸก Warning**: Attention needed (2-10 minutes) -- **๐ŸŸข Info**: Routine updates (10+ minutes) -- **๐Ÿ”ต Status**: General information - -## ๐Ÿšจ Critical Finding Response Workflow - -### 7. Critical Alert Reception -``` -Push Notification โ†’ Alert Screen โ†’ Patient Details - โ†“ -AI Summary โ†’ Image Review โ†’ Treatment Protocol - โ†“ -Consultation โ†’ Action โ†’ Documentation -``` - -### 8. Alert Response Timeline -- **0-30 seconds**: Alert received & acknowledged -- **30-60 seconds**: Patient details reviewed -- **1-2 minutes**: AI findings assessed -- **2-5 minutes**: Treatment decision made -- **5+ minutes**: Action initiated & documented - -### 9. Critical Alert Components -``` -Alert Screen Elements: -โ”œโ”€โ”€ Patient Identification -โ”œโ”€โ”€ Bed Location -โ”œโ”€โ”€ Critical Finding Type -โ”œโ”€โ”€ AI-Generated Summary -โ”œโ”€โ”€ Urgency Level -โ”œโ”€โ”€ Time Stamp -โ””โ”€โ”€ Action Buttons -``` - -## ๐Ÿฅ Patient Care Flow - -### 10. Patient Selection Flow -``` -Patient List โ†’ Patient Card โ†’ Patient Details - โ†“ -โ”œโ”€โ”€ Vital Signs -โ”œโ”€โ”€ Medical History -โ”œโ”€โ”€ Current Medications -โ”œโ”€โ”€ Allergy Information -โ”œโ”€โ”€ Bed Information -โ””โ”€โ”€ Admission Details -``` - -### 11. Patient Detail Navigation -``` -Patient Details Screen: -โ”œโ”€โ”€ Demographics (Top) -โ”œโ”€โ”€ Vital Signs (Real-time) -โ”œโ”€โ”€ Medical History (Expandable) -โ”œโ”€โ”€ Current Medications (List) -โ”œโ”€โ”€ Allergy Information (Alert) -โ”œโ”€โ”€ Bed Information (Status) -โ””โ”€โ”€ Action Buttons (Bottom) -``` - -### 12. Medical Record Integration -- **EMR Sync**: Real-time patient data -- **Vital Signs**: Live monitoring integration -- **Medication History**: Pharmacy system sync -- **Lab Results**: Laboratory system integration -- **Imaging**: PACS system connection - -## ๐Ÿ“ฑ Screen Navigation Patterns - -### 13. Primary Navigation Structure -``` -Bottom Tab Navigation: -โ”œโ”€โ”€ Dashboard (Home) -โ”œโ”€โ”€ Patients -โ”œโ”€โ”€ Alerts -โ”œโ”€โ”€ Reports -โ””โ”€โ”€ Settings -``` - -### 14. Secondary Navigation -``` -Stack Navigation per Tab: -โ”œโ”€โ”€ List View โ†’ Detail View -โ”œโ”€โ”€ Detail View โ†’ Action View -โ”œโ”€โ”€ Action View โ†’ Confirmation -โ””โ”€โ”€ Confirmation โ†’ Return to List -``` - -### 15. Modal Navigation Rules -- **Quick Actions**: Modal overlays -- **Critical Actions**: Full-screen modals -- **Confirmation**: Alert modals -- **Settings**: Sheet modals - -## ๐Ÿ”„ State Management Flow - -### 16. Redux State Structure -``` -Root State: -โ”œโ”€โ”€ Auth (Authentication state) -โ”œโ”€โ”€ Dashboard (ER dashboard data) -โ”œโ”€โ”€ PatientCare (Patient information) -โ”œโ”€โ”€ Alerts (Notification system) -โ”œโ”€โ”€ Settings (User preferences) -โ””โ”€โ”€ UI (Interface state) -``` - -### 17. Data Flow Patterns -``` -API Call โ†’ Redux Action โ†’ State Update โ†’ UI Re-render - โ†“ -WebSocket โ†’ Real-time Update โ†’ Immediate UI Change - โ†“ -User Action โ†’ Local State โ†’ API Call โ†’ Server Sync -``` - -### 18. Caching Strategy -- **Patient Data**: 15-minute cache -- **Critical Alerts**: No cache (real-time) -- **User Settings**: Persistent storage -- **Medical Records**: 5-minute cache - -## ๐Ÿ“‹ Workflow Rules - -### 19. Critical Finding Workflow -``` -Step 1: Alert Reception -โ”œโ”€โ”€ Push notification received -โ”œโ”€โ”€ Alert screen displayed -โ”œโ”€โ”€ Patient context loaded -โ””โ”€โ”€ AI summary generated - -Step 2: Assessment -โ”œโ”€โ”€ Patient details reviewed -โ”œโ”€โ”€ Medical history checked -โ”œโ”€โ”€ Current status assessed -โ””โ”€โ”€ Urgency level determined - -Step 3: Action Planning -โ”œโ”€โ”€ Treatment protocol loaded -โ”œโ”€โ”€ Specialist consultation initiated -โ”œโ”€โ”€ Emergency procedures prepared -โ””โ”€โ”€ Documentation started - -Step 4: Implementation -โ”œโ”€โ”€ Actions executed -โ”œโ”€โ”€ Status updated -โ”œโ”€โ”€ Team notified -โ””โ”€โ”€ Record documented -``` - -### 20. Routine Scan Processing -``` -Step 1: Report Notification -โ”œโ”€โ”€ Scan completion notification -โ”œโ”€โ”€ Report status update -โ”œโ”€โ”€ AI findings summary -โ””โ”€โ”€ Priority assignment - -Step 2: Review Process -โ”œโ”€โ”€ Report details loaded -โ”œโ”€โ”€ Images reviewed -โ”œโ”€โ”€ Findings assessed -โ””โ”€โ”€ Action plan created - -Step 3: Documentation -โ”œโ”€โ”€ Patient record updated -โ”œโ”€โ”€ Treatment plan documented -โ”œโ”€โ”€ Follow-up scheduled -โ””โ”€โ”€ Discharge planning initiated -``` - -## ๐Ÿ” Security & Access Control - -### 21. Authentication Rules -- **Session Management**: 8-hour timeout -- **Auto-logout**: Inactivity after 30 minutes -- **Device Remembering**: 30-day trusted devices -- **Emergency Access**: Limited functionality -- **Audit Trail**: All actions logged - -### 22. Permission Levels -``` -User Roles: -โ”œโ”€โ”€ ER Physician (Full Access) -โ”œโ”€โ”€ Resident (Limited Access) -โ”œโ”€โ”€ Medical Student (Read-only) -โ”œโ”€โ”€ Emergency Access (Critical Only) -โ””โ”€โ”€ Temporary Access (Time-limited) -``` - -### 23. Data Access Rules -- **Patient Data**: Role-based access -- **Critical Alerts**: All ER staff -- **Medical Records**: Authorized personnel only -- **Settings**: User-specific -- **Audit Logs**: Admin only - -## ๐Ÿ“Š Performance & Optimization - -### 24. Loading States -``` -Loading Hierarchy: -โ”œโ”€โ”€ Critical Alerts (Immediate) -โ”œโ”€โ”€ Patient List (Fast) -โ”œโ”€โ”€ Patient Details (Medium) -โ”œโ”€โ”€ Medical History (Medium) -โ””โ”€โ”€ Full Reports (Slow) -``` - -### 25. Offline Capabilities -- **Critical Alerts**: Always available -- **Patient List**: Cached data -- **Recent Reports**: Offline access -- **Settings**: Local storage -- **Sync**: Automatic when online - -### 26. Error Handling -``` -Error Recovery: -โ”œโ”€โ”€ Network Errors โ†’ Retry with backoff -โ”œโ”€โ”€ Authentication Errors โ†’ Re-login -โ”œโ”€โ”€ Data Errors โ†’ Fallback to cache -โ”œโ”€โ”€ Critical Errors โ†’ Emergency mode -โ””โ”€โ”€ UI Errors โ†’ Graceful degradation -``` - -## ๐ŸŽฏ User Experience Rules - -### 27. Interaction Patterns -- **Critical Actions**: Confirmation required -- **Quick Actions**: One-tap execution -- **Navigation**: Intuitive flow -- **Feedback**: Immediate response -- **Accessibility**: WCAG 2.1 compliance - -### 28. Visual Hierarchy -``` -Priority Order: -โ”œโ”€โ”€ Critical Alerts (Red, Large) -โ”œโ”€โ”€ Active Patients (Blue, Medium) -โ”œโ”€โ”€ Pending Items (Yellow, Medium) -โ”œโ”€โ”€ Completed Items (Green, Small) -โ””โ”€โ”€ Background Info (Gray, Small) -``` - -### 29. Responsive Design -- **Mobile First**: Optimized for phones -- **Tablet Support**: Enhanced layouts -- **Landscape Mode**: Alternative views -- **Accessibility**: Voice commands support - -## ๐Ÿ”„ Data Synchronization - -### 30. Real-time Updates -``` -Update Types: -โ”œโ”€โ”€ Patient Status (Immediate) -โ”œโ”€โ”€ Vital Signs (30-second intervals) -โ”œโ”€โ”€ Alert Status (Real-time) -โ”œโ”€โ”€ Bed Assignments (Real-time) -โ””โ”€โ”€ Report Status (5-minute intervals) -``` - -### 31. Conflict Resolution -- **Server Priority**: Server data overrides local -- **Timestamp Comparison**: Latest data wins -- **User Confirmation**: Manual resolution for conflicts -- **Audit Trail**: All changes tracked - -## ๐Ÿ“ฑ Device Integration - -### 32. Hardware Integration -- **Camera**: Document scanning -- **Microphone**: Voice notes -- **Biometrics**: Secure access -- **NFC**: Patient identification -- **Bluetooth**: Medical device connection - -### 33. Platform-Specific Features -``` -iOS Features: -โ”œโ”€โ”€ Face ID authentication -โ”œโ”€โ”€ Apple Health integration -โ”œโ”€โ”€ Siri shortcuts -โ””โ”€โ”€ iOS notifications - -Android Features: -โ”œโ”€โ”€ Fingerprint authentication -โ”œโ”€โ”€ Google Fit integration -โ”œโ”€โ”€ Android Auto -โ””โ”€โ”€ Android notifications -``` - -This comprehensive flow ensures efficient, secure, and user-friendly operation of the Physician App in emergency medical scenarios. \ No newline at end of file +This flow guide ensures radiologists can efficiently review AI predictions, provide meaningful feedback, and maintain the highest standards of diagnostic accuracy while contributing to AI system improvement. \ No newline at end of file diff --git a/.cursor/rules/projectstructurerule.mdc b/.cursor/rules/projectstructurerule.mdc index 3604da8..66eca95 100644 --- a/.cursor/rules/projectstructurerule.mdc +++ b/.cursor/rules/projectstructurerule.mdc @@ -208,13 +208,7 @@ docs/ โ””โ”€โ”€ wireframes/ # UI wireframes ``` -## ๐Ÿงช Testing Rules -### 17. Test Organization -- **Unit tests** alongside source files in `__tests__/` folders -- **Integration tests** in module-level `__tests__/` folders -- **E2E tests** in root-level `__tests__/` folder -- **Test utilities** in `shared/__tests__/` ## ๐Ÿ“ฆ Package Management diff --git a/.env b/.env index aa3551d..0d0407f 100644 --- a/.env +++ b/.env @@ -1,2 +1,3 @@ BASE_URL='https://neoscan-backend.tech4bizsolutions.com' -# BASE_URL='http://192.168.1.87:3000' \ No newline at end of file +# BASE_URL='http://192.168.1.87:3000' +# BASE_URL='https://neoscanbackend.tech4biz.io' \ No newline at end of file diff --git a/PROJECT_STRUCTURE.md b/PROJECT_STRUCTURE.md index 9ea9fe6..7dcc76e 100644 --- a/PROJECT_STRUCTURE.md +++ b/PROJECT_STRUCTURE.md @@ -24,10 +24,7 @@ NeoScan_Radiologist/ โ”‚ โ”‚ โ”œโ”€โ”€ Dashboard/ # ER Dashboard module โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ components/ # Dashboard components โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ PatientCard.tsx # Patient information card -โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ CriticalAlerts.tsx # Critical alerts display โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ DashboardHeader.tsx # Dashboard statistics header -โ”‚ โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ QuickActions.tsx # Emergency quick actions -โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ DepartmentStats.tsx # Department statistics โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ screens/ # Dashboard screens โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ DashboardScreen.tsx # Main ER dashboard โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ hooks/ # Dashboard custom hooks @@ -216,32 +213,23 @@ NeoScan_Radiologist/ ### Auth Module **Purpose**: Handles all authentication and authorization functionality -- **LoginScreen**: Hospital SSO, credential login, emergency access +- **LoginScreen**: credential login (email,password) - **Components**: Login forms, authentication modals - **Services**: Authentication API, token management - **Redux**: Auth state, user session management ### Dashboard Module -**Purpose**: Main ER dashboard with patient monitoring and alerts +**Purpose**: Main Dashboard with patient monitoring. - **DashboardScreen**: Main dashboard with patient list and statistics - **PatientCard**: Individual patient information display -- **CriticalAlerts**: High-priority alert notifications -- **QuickActions**: Emergency procedure shortcuts -- **DepartmentStats**: Real-time department overview ### PatientCare Module **Purpose**: Comprehensive patient management and medical records - **PatientDetailsScreen**: Complete patient information -- **VitalSigns**: Real-time vital signs monitoring -- **MedicalHistory**: Patient medical records -- **Medications**: Current medication management -- **Allergies**: Allergy information and alerts ### Settings Module **Purpose**: App configuration and user preferences - **SettingsScreen**: User preferences and app settings -- **NotificationSettings**: Alert and notification configuration -- **SecuritySettings**: Authentication and security options - **AboutScreen**: App information and version details ## ๐Ÿ“ File Naming Conventions @@ -262,7 +250,7 @@ NeoScan_Radiologist/ ### Redux - **camelCase** with descriptive suffixes -- **Examples**: `authSlice.ts`, `erDashboardSlice.ts`, `patientCareActions.ts` +- **Examples**: `authSlice.ts`, `DashboardSlice.ts`, `patientCareActions.ts` ### Tests - **Same name as source file** + `.test.ts` or `.test.tsx` @@ -282,7 +270,6 @@ NeoScan_Radiologist/ - **auth.ts**: Authentication types and interfaces - **patient.ts**: Patient data and medical record types - **dashboard.ts**: Dashboard and ER management types -- **alerts.ts**: Alert and notification types - **common.ts**: Common utility types and interfaces ### Utilities @@ -307,10 +294,6 @@ NeoScan_Radiologist/ - **Icon Components**: Medical and UI icons - **Layout Components**: Container and layout components -### Responsive Design -- **Mobile First**: Optimized for mobile devices -- **Tablet Support**: Enhanced layouts for tablets -- **Breakpoints**: Mobile (375px), Tablet (768px), Desktop (1024px) ## ๐Ÿš€ Getting Started diff --git a/README.md b/README.md index cf3ff71..7ec74a5 100644 --- a/README.md +++ b/README.md @@ -1,317 +1,682 @@ -/* - * File: README.md - * Description: Project documentation and setup instructions - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ +# NeoScan Radiologist App -# NeoScan Physician App +> **A comprehensive React Native application designed for radiologists to review AI-powered medical imaging predictions, validate diagnostic accuracy, and provide clinical feedback to improve AI performance.** -A comprehensive React Native application designed for emergency department physicians to manage patient care, critical alerts, and medical workflows in real-time. +*Design & Developed by Tech4Biz Solutions* +*Copyright (c) Spurrin Innovations. All rights reserved.* -## ๐Ÿš€ Features +--- -### Authentication & Security -- **Hospital SSO Integration** - Seamless login with hospital credentials -- **Multi-factor Authentication** - Enhanced security for medical data -- **Emergency Access** - Quick access codes for urgent situations -- **Session Management** - 8-hour sessions with 30-minute inactivity timeout -- **Device Remembering** - 30-day trusted device authentication +## ๐Ÿ“‹ Table of Contents -### ER Dashboard -- **Real-time Patient Monitoring** - Live updates of patient status and vital signs -- **Critical Alert System** - Immediate notifications for life-threatening conditions -- **Department Statistics** - Overview of emergency, trauma, cardiac, neurology, pediatrics, and ICU -- **Quick Actions** - Emergency procedures, scan orders, medication, lab work, consultations -- **Patient Filtering** - Filter by status (All, Critical, Active) +- [๐Ÿš€ Overview](#-overview) +- [๐Ÿ—๏ธ Project Architecture](#๏ธ-project-architecture) +- [๐Ÿ”„ Project Flow & User Journey](#-project-flow--user-journey) +- [๐Ÿ› ๏ธ Technology Stack](#๏ธ-technology-stack) +- [๐Ÿ“ฑ Core Features](#-core-features) +- [๐Ÿ”ง Development Setup](#-development-setup) +- [๐Ÿ“ Project Structure](#-project-structure) +- [๐ŸŽจ Design System](#-design-system) +- [๐Ÿ” Security & Authentication](#-security--authentication) +- [๐Ÿ“Š Data Flow & State Management](#-data-flow--state-management) +- [๐Ÿ“ฆ Building & Deployment](#-building--deployment) +- [๐Ÿšจ Troubleshooting](#-troubleshooting) +- [๐Ÿ“ž Support & Documentation](#-support--documentation) -### Patient Management -- **Comprehensive Patient Cards** - Vital signs, allergies, medications, diagnosis -- **Medical History** - Complete patient medical records -- **Real-time Vital Signs** - Blood pressure, heart rate, temperature, respiratory rate, oxygen saturation -- **Allergy Alerts** - Prominent display of patient allergies -- **Medication Tracking** - Current medications with dosages and schedules +--- -### Critical Finding Response -- **AI-Powered Detection** - Automated critical finding identification -- **Immediate Alert System** - Push notifications for urgent cases -- **Response Timeline** - 0-30 seconds acknowledgment, 2-5 minutes action -- **Treatment Protocols** - Quick access to emergency procedures +## ๐Ÿš€ Overview -## ๐Ÿ—๏ธ Architecture +NeoScan Radiologist App is a specialized mobile application that bridges the gap between AI-powered medical imaging analysis and expert radiologist validation. The app enables radiologists to: + +- **Review AI Predictions**: Analyze automated diagnostic findings with confidence scores +- **Validate Clinical Accuracy**: Provide expert feedback on AI-generated results +- **Access DICOM Images**: View high-resolution medical imaging files +- **Track Performance**: Monitor AI improvement through feedback analytics +- **Manage Cases**: Organize and prioritize patient cases for review + +--- + +## ๐Ÿ—๏ธ Project Architecture + +### High-Level Architecture +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ React Native โ”‚ โ”‚ Redux Store โ”‚ โ”‚ API Services โ”‚ +โ”‚ App Layer โ”‚โ—„โ”€โ”€โ–บโ”‚ (State Mgmt) โ”‚โ—„โ”€โ”€โ–บโ”‚ (Backend) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ โ”‚ โ”‚ + โ–ผ โ–ผ โ–ผ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Native Modules โ”‚ โ”‚ Local Storage โ”‚ โ”‚ DICOM Viewer โ”‚ +โ”‚ (iOS/Android) โ”‚ โ”‚ (Redux Persist)โ”‚ โ”‚ (WebView) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### Module-Based Architecture +The app follows a **feature-based modular architecture** where each major feature is self-contained: + +- **Auth Module**: Authentication, registration, and user management +- **Dashboard Module**: Analytics, statistics, and overview +- **PatientCare Module**: Patient data, case management, and DICOM viewing +- **Settings Module**: User preferences and app configuration + +--- + +## ๐Ÿ”„ Project Flow & User Journey + +### 1. **Authentication Flow** +``` +App Launch โ†’ Check Authentication โ†’ Login/Register โ†’ Dashboard + โ”‚ โ”‚ โ”‚ โ”‚ + โ–ผ โ–ผ โ–ผ โ–ผ + Splash Screen โ†’ Auth Check โ†’ Login Screen โ†’ Main App +``` + +**Registration Process:** +1. **Self-Registration**: User enters email โ†’ Password creation โ†’ Personal info โ†’ Hospital ID upload โ†’ Hospital selection โ†’ Wait for admin approval +2. **Admin-Created**: Admin creates account โ†’ Credentials sent via email โ†’ User logs in โ†’ Hospital ID upload โ†’ Password reset โ†’ Access granted + +### 2. **Main Application Flow** +``` +Dashboard โ†’ Patient List โ†’ Case Selection โ†’ DICOM Review โ†’ AI Analysis โ†’ Feedback Submission + โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ + โ–ผ โ–ผ โ–ผ โ–ผ โ–ผ โ–ผ +Overview โ†’ Filter Cases โ†’ Open Case โ†’ View Images โ†’ Review AI โ†’ Submit Validation +``` + +**Case Review Workflow:** +1. **Select Case**: Choose from patient queue or search +2. **View DICOM**: Access medical imaging files +3. **Review AI**: Analyze AI predictions and confidence scores +4. **Provide Feedback**: Submit validation (Agree/Partially Agree/Disagree) +5. **Add Notes**: Optional clinical observations +6. **Complete Review**: Case marked as reviewed + +### 3. **Data Flow** +``` +API Request โ†’ Redux Action โ†’ State Update โ†’ UI Re-render โ†’ User Interaction โ†’ Feedback โ†’ API Update +``` + +--- + +## ๐Ÿ› ๏ธ Technology Stack + +### **Frontend Framework** +- **React Native 0.79.0**: Cross-platform mobile development +- **TypeScript 5.x**: Type-safe development with strict typing +- **React Navigation 6**: Navigation management and routing + +### **State Management** +- **Redux Toolkit**: Modern Redux with simplified patterns +- **Redux Persist**: Local storage for offline capability +- **React Redux Hooks**: Typed hooks for state access + +### **UI & Styling** +- **React Native Vector Icons**: Comprehensive icon library +- **Custom Theme System**: Consistent design tokens and components +- **Responsive Design**: Adaptive layouts for different screen sizes + +### **Native Integration** +- **WebView**: DICOM image viewing with Cornerstone.js +- **AsyncStorage**: Local data persistence +- **Push Notifications**: Real-time alerts and updates + +### **Development Tools** +- **Metro Bundler**: React Native bundler +- **ESLint + Prettier**: Code quality and formatting +- **Jest**: Unit testing framework +- **TypeScript Compiler**: Static type checking + +--- + +## ๐Ÿ“ฑ Core Features + +### **๐Ÿ” Authentication & Security** +- **Dual Registration Paths**: Self-registration or admin-created accounts +- **Hospital Integration**: Institution-based access control +- **ID Verification**: Secure credential upload and verification +- **Admin Approval System**: Account activation requires approval +- **Secure Storage**: Encrypted credential management + +### **๐Ÿ“Š Dashboard & Analytics** +- **Validation Metrics**: Total predictions reviewed and accuracy rates +- **Performance Tracking**: AI confidence score analysis +- **Quality Assurance**: Monitor AI performance trends +- **Real-time Updates**: Live data synchronization + +### **๐Ÿ” AI Prediction Review** +- **Comprehensive Analysis**: Review automated diagnostic predictions +- **Confidence Assessment**: Evaluate AI confidence scores (e.g., 98.6%) +- **Finding Categories**: Stroke detection, hemorrhage analysis, anatomical findings +- **Severity Classification**: High/Medium/Low risk stratification + +### **๐Ÿ–ผ๏ธ DICOM Image Viewer** +- **Series Navigation**: Browse through multiple image series +- **AI Overlay Integration**: Compare predictions with actual scans +- **Finding Localization**: Precise visualization of pathological findings +- **Image Controls**: Zoom, pan, and navigation tools + +### **๐Ÿ“ Advanced Feedback System** +- **Binary Validation**: Positive (โœ…) or Negative (โŒ) feedback +- **Clinical Notes**: Detailed observations and suggestions +- **Performance Improvement**: Feedback data enhances AI accuracy +- **Quality Metrics**: Continuous validation performance tracking + +--- + +## ๐Ÿ”ง Development Setup + +### **Prerequisites** + +#### **System Requirements** +- **Operating System**: macOS (for iOS development) or Windows/Linux (for Android) +- **Node.js**: Version 18.0.0 or higher +- **RAM**: Minimum 8GB, recommended 16GB +- **Storage**: At least 10GB free space + +#### **Required Software** +- **Node.js & npm**: [Download from nodejs.org](https://nodejs.org/) +- **Git**: [Download from git-scm.com](https://git-scm.com/) +- **React Native CLI**: Install globally with `npm install -g @react-native-community/cli` + +#### **Platform-Specific Requirements** + +**For Android Development:** +- **Android Studio**: [Download from developer.android.com](https://developer.android.com/studio) +- **Android SDK**: API level 21 (Android 5.0) or higher +- **Java Development Kit (JDK)**: Version 11 or 17 +- **Android Emulator**: For testing without physical device + +**For iOS Development (macOS only):** +- **Xcode**: Latest version from Mac App Store +- **iOS Simulator**: For testing without physical device +- **CocoaPods**: Install with `sudo gem install cocoapods` + +### **Installation Steps** + +#### **1. Clone the Repository** +```bash +# Clone the project +git clone +cd NeoScan_Radiologist + +# Verify the structure +ls -la +``` + +#### **2. Install Dependencies** +```bash +# Install Node.js dependencies +npm install + +# Install iOS dependencies (macOS only) +cd ios && pod install && cd .. +``` + +#### **3. Environment Configuration** +```bash +# Copy environment template +cp .env.example .env + +# Edit environment variables +nano .env +``` + +**Required Environment Variables:** +```env +# API Configuration +API_BASE_URL=https://api.neoscan.com +API_TIMEOUT=30000 + +# Authentication +AUTH_TOKEN_KEY=neoscan_auth_token +REFRESH_TOKEN_KEY=neoscan_refresh_token + +# DICOM Viewer +DICOM_VIEWER_URL=https://viewer.neoscan.com +``` + +#### **4. Start Development Server** +```bash +# Start Metro bundler +npm start + +# In a new terminal, run on device/simulator +npm run android # For Android +npm run ios # For iOS (macOS only) +``` + +### **First-Time Setup Checklist** + +- [ ] Node.js and npm installed +- [ ] React Native CLI installed globally +- [ ] Repository cloned successfully +- [ ] Dependencies installed (`npm install`) +- [ ] Environment file configured (`.env`) +- [ ] Platform-specific tools installed (Android Studio/Xcode) +- [ ] Development server starts without errors +- [ ] App runs on device/simulator + +--- + +## ๐Ÿ“ Project Structure -### Project Structure ``` NeoScan_Radiologist/ -โ”œโ”€โ”€ app/ # Main application code -โ”‚ โ”œโ”€โ”€ modules/ # Feature-based modules -โ”‚ โ”‚ โ”œโ”€โ”€ Auth/ # Authentication module -โ”‚ โ”‚ โ”œโ”€โ”€ Dashboard/ # ER Dashboard module -โ”‚ โ”‚ โ”œโ”€โ”€ PatientCare/ # Patient management module -โ”‚ โ”‚ โ””โ”€โ”€ Settings/ # App settings module -โ”‚ โ”œโ”€โ”€ shared/ # Shared utilities & components -โ”‚ โ”‚ โ”œโ”€โ”€ components/ # Reusable UI components -โ”‚ โ”‚ โ”œโ”€โ”€ utils/ # Utility functions -โ”‚ โ”‚ โ””โ”€โ”€ types/ # TypeScript type definitions -โ”‚ โ”œโ”€โ”€ theme/ # Design system & theming -โ”‚ โ”œโ”€โ”€ navigation/ # Navigation setup -โ”‚ โ”œโ”€โ”€ store/ # Redux state management -โ”‚ โ”œโ”€โ”€ config/ # Configuration files -โ”‚ โ””โ”€โ”€ assets/ # Static assets -โ”œโ”€โ”€ android/ # Android native code -โ”œโ”€โ”€ ios/ # iOS native code -โ””โ”€โ”€ docs/ # Documentation +โ”œโ”€โ”€ ๐Ÿ“ฑ app/ # Main application code +โ”‚ โ”œโ”€โ”€ ๐ŸŽฏ modules/ # Feature-based modules +โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ” Auth/ # Authentication & user management +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ components/ # UI components +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ screens/ # Screen components +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ redux/ # State management +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ services/ # API services +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ types/ # Type definitions +โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ“Š Dashboard/ # Analytics & overview +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ components/ # Dashboard components +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ screens/ # Dashboard screens +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ redux/ # Dashboard state +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ hooks/ # Custom hooks +โ”‚ โ”‚ โ”œโ”€โ”€ ๐Ÿ‘ฅ PatientCare/ # Patient & case management +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ components/ # Patient components +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ screens/ # Patient screens +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ redux/ # Patient state +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ services/ # Patient API services +โ”‚ โ”‚ โ””โ”€โ”€ โš™๏ธ Settings/ # App configuration +โ”‚ โ”‚ โ”œโ”€โ”€ components/ # Settings components +โ”‚ โ”‚ โ”œโ”€โ”€ screens/ # Settings screens +โ”‚ โ”‚ โ””โ”€โ”€ redux/ # Settings state +โ”‚ โ”œโ”€โ”€ ๐Ÿ”ง shared/ # Shared utilities & components +โ”‚ โ”‚ โ”œโ”€โ”€ components/ # Reusable UI components +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ CustomModal.tsx # Modal component +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ ComingSoonScreen.tsx # Placeholder screen +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ DicomViewer.tsx # DICOM image viewer +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ DicomViewerModal.tsx # DICOM viewer modal +โ”‚ โ”‚ โ”œโ”€โ”€ utils/ # Utility functions +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ api.ts # API utilities +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ constants.ts # App constants +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ helpers.ts # Helper functions +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ validators.ts # Validation functions +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ toast.ts # Toast notifications +โ”‚ โ”‚ โ””โ”€โ”€ types/ # TypeScript type definitions +โ”‚ โ”‚ โ”œโ”€โ”€ auth.ts # Authentication types +โ”‚ โ”‚ โ”œโ”€โ”€ patient.ts # Patient data types +โ”‚ โ”‚ โ”œโ”€โ”€ dashboard.ts # Dashboard types +โ”‚ โ”‚ โ””โ”€โ”€ common.ts # Common types +โ”‚ โ”œโ”€โ”€ ๐ŸŽจ theme/ # Design system & theming +โ”‚ โ”‚ โ”œโ”€โ”€ colors.ts # Color palette +โ”‚ โ”‚ โ”œโ”€โ”€ typography.ts # Font definitions +โ”‚ โ”‚ โ”œโ”€โ”€ spacing.ts # Spacing scale +โ”‚ โ”‚ โ”œโ”€โ”€ shadows.ts # Shadow definitions +โ”‚ โ”‚ โ”œโ”€โ”€ animations.ts # Animation configurations +โ”‚ โ”‚ โ””โ”€โ”€ theme.ts # Main theme object +โ”‚ โ”œโ”€โ”€ ๐Ÿงญ navigation/ # Navigation setup +โ”‚ โ”‚ โ”œโ”€โ”€ RootStackNavigator.tsx # Root navigation +โ”‚ โ”‚ โ”œโ”€โ”€ MainTabNavigator.tsx # Tab navigation +โ”‚ โ”‚ โ”œโ”€โ”€ navigationTypes.ts # Navigation types +โ”‚ โ”‚ โ””โ”€โ”€ navigationUtils.ts # Navigation utilities +โ”‚ โ”œโ”€โ”€ ๐Ÿ“ฆ store/ # Redux state management +โ”‚ โ”‚ โ”œโ”€โ”€ index.ts # Store configuration +โ”‚ โ”‚ โ”œโ”€โ”€ Provider.tsx # Redux provider +โ”‚ โ”‚ โ””โ”€โ”€ hooks.ts # Custom Redux hooks +โ”‚ โ”œโ”€โ”€ ๐Ÿ“ assets/ # Static assets +โ”‚ โ”‚ โ”œโ”€โ”€ images/ # Image assets +โ”‚ โ”‚ โ”œโ”€โ”€ fonts/ # Custom fonts +โ”‚ โ”‚ โ””โ”€โ”€ dicom/ # DICOM viewer HTML +โ”‚ โ”œโ”€โ”€ โš™๏ธ config/ # Configuration files +โ”‚ โ””โ”€โ”€ ๐ŸŒ localization/ # Internationalization +โ”œโ”€โ”€ ๐Ÿค– android/ # Android native code +โ”œโ”€โ”€ ๐ŸŽ ios/ # iOS native code +โ”œโ”€โ”€ ๐Ÿ“š docs/ # Documentation +โ”œโ”€โ”€ ๐Ÿ“„ package.json # Dependencies & scripts +โ”œโ”€โ”€ ๐Ÿ”ง metro.config.js # Metro bundler config +โ”œโ”€โ”€ ๐Ÿ“ tsconfig.json # TypeScript configuration +โ””โ”€โ”€ ๐Ÿ“– README.md # This file ``` -### Technology Stack -- **React Native 0.79.0** - Cross-platform mobile development -- **TypeScript** - Type-safe development -- **React Navigation 6** - Navigation management -- **Redux Toolkit** - State management -- **React Native Vector Icons** - Icon library -- **React Native Push Notification** - Real-time notifications -- **React Native Keychain** - Secure credential storage +### **Key File Explanations** + +#### **Entry Points** +- **`app/App.tsx`**: Main application component with navigation setup +- **`index.js`**: React Native entry point +- **`metro.config.js`**: Bundler configuration + +#### **Module Structure** +Each module follows the same pattern: +- **`components/`**: Reusable UI components +- **`screens/`**: Full-screen components +- **`redux/`**: State management (actions, reducers, selectors) +- **`services/`**: API and external service integrations +- **`types/`**: TypeScript type definitions + +#### **Shared Resources** +- **`shared/components/`**: Cross-module reusable components +- **`shared/utils/`**: Common utility functions +- **`shared/types/`**: Shared type definitions +- **`theme/`**: Centralized design system + +--- ## ๐ŸŽจ Design System -### Color Palette - "Modern Healthcare Blue" -- **Primary**: #2196F3 (Material Blue) -- **Secondary**: #1976D2 (Darker Blue) -- **Critical**: #F44336 (Material Red) -- **Warning**: #FF9800 (Material Orange) -- **Success**: #4CAF50 (Material Green) +### **Color Palette - "Modern Healthcare Blue"** -### Typography -- **Primary Font**: Roboto -- **Font Weights**: Light (300), Regular (400), Medium (500), Bold (700) -- **Font Sizes**: Display (32px, 24px, 20px), Body (16px, 14px, 12px), Caption (10px) +```typescript +// Primary Colors +Primary: '#2196F3' // Material Blue - Main brand color +Secondary: '#1976D2' // Darker Blue - Secondary actions +Tertiary: '#E3F2FD' // Very Light Blue - Backgrounds -### Components -- **Patient Cards** - Comprehensive patient information display -- **Critical Alerts** - High-priority notification system -- **Quick Actions** - Emergency procedure shortcuts -- **Department Stats** - Real-time department overview -- **Dashboard Header** - ER statistics and shift information +// Status Colors +Success: '#4CAF50' // Green - Success states +Warning: '#FF9800' // Orange - Warning states +Error: '#F44336' // Red - Error states +Info: '#2196F3' // Blue - Information states -## ๐Ÿ“ฑ Screens - -### Authentication Flow -1. **Splash Screen** - App initialization and authentication check -2. **Login Screen** - Hospital SSO, credential login, emergency access -3. **Main Dashboard** - ER overview with patient list and alerts - -### Main Application -1. **ER Dashboard** - Real-time patient monitoring and critical alerts -2. **Patient Details** - Comprehensive patient information and medical history -3. **Alerts Center** - Critical finding notifications and response -4. **Reports** - Medical reports and scan results -5. **Settings** - User preferences and app configuration - -## ๐Ÿ”ง Setup & Installation - -### Prerequisites -- Node.js >= 18 -- React Native CLI -- Android Studio (for Android development) -- Xcode (for iOS development) - -### Installation Steps - -1. **Clone the repository** - ```bash - git clone - cd NeoScan_Radiologist - ``` - -2. **Install dependencies** - ```bash - npm install - ``` - -3. **iOS Setup** (macOS only) - ```bash - cd ios - pod install - cd .. - ``` - -4. **Start the development server** - ```bash - npm start - ``` - -5. **Run on device/simulator** - ```bash - # Android - npm run android - - # iOS - npm run ios - ``` - -## ๐Ÿšจ Critical Alert Workflow - -### Alert Reception (0-30 seconds) -- Push notification received -- Alert screen displayed -- Patient context loaded -- AI summary generated - -### Assessment (30-60 seconds) -- Patient details reviewed -- Medical history checked -- Current status assessed -- Urgency level determined - -### Action Planning (1-2 minutes) -- Treatment protocol loaded -- Specialist consultation initiated -- Emergency procedures prepared -- Documentation started - -### Implementation (2-5 minutes) -- Actions executed -- Status updated -- Team notified -- Record documented - -## ๐Ÿ” Security Features - -### Authentication -- **Session Management**: 8-hour timeout -- **Auto-logout**: Inactivity after 30 minutes -- **Device Remembering**: 30-day trusted devices -- **Emergency Access**: Limited functionality -- **Audit Trail**: All actions logged - -### Data Protection -- **Encryption**: End-to-end data encryption -- **HIPAA Compliance**: Healthcare data protection -- **Secure Storage**: Encrypted local storage -- **Network Security**: HTTPS/TLS communication - -## ๐Ÿ“Š Performance Optimization - -### Loading States -- **Critical Alerts**: Immediate loading -- **Patient List**: Fast loading with caching -- **Patient Details**: Medium loading -- **Medical History**: Optimized loading -- **Full Reports**: Background loading - -### Offline Capabilities -- **Critical Alerts**: Always available -- **Patient List**: Cached data access -- **Recent Reports**: Offline viewing -- **Settings**: Local storage -- **Sync**: Automatic when online - -## ๐Ÿงช Testing - -### Test Structure -- **Unit Tests**: Component and utility testing -- **Integration Tests**: Module interaction testing -- **E2E Tests**: Complete workflow testing -- **Performance Tests**: Load and stress testing - -### Running Tests -```bash -# Unit tests -npm test - -# E2E tests -npm run test:e2e - -# Performance tests -npm run test:performance +// Text Colors +TextPrimary: '#212121' // Dark Gray - Main text +TextSecondary: '#757575' // Medium Gray - Secondary text +TextMuted: '#9E9E9E' // Light Gray - Muted text ``` -## ๐Ÿ“ฑ Platform Support +### **Typography System** -### iOS Features -- Face ID authentication -- Apple Health integration -- Siri shortcuts -- iOS notifications +```typescript +// Font Family +PrimaryFont: 'Roboto' // Main font family -### Android Features -- Fingerprint authentication -- Google Fit integration -- Android Auto -- Android notifications +// Font Weights +Light: 300, Regular: 400, Medium: 500, Bold: 700 -## ๐Ÿ”„ Real-time Updates +// Font Sizes +DisplayLarge: 32px // Main headings +DisplayMedium: 24px // Section headings +BodyLarge: 16px // Body text +BodyMedium: 14px // Secondary text +Caption: 10px // Small labels +``` -### WebSocket Integration -- **Patient Status**: Real-time updates -- **Vital Signs**: 30-second intervals -- **Alert Status**: Immediate updates -- **Bed Assignments**: Real-time changes -- **Report Status**: 5-minute intervals +### **Component Design Rules** -### Data Synchronization -- **Server Priority**: Server data overrides local -- **Timestamp Comparison**: Latest data wins -- **User Confirmation**: Manual resolution for conflicts -- **Audit Trail**: All changes tracked +#### **Button Variants** +- **Primary**: Blue background with shadow +- **Secondary**: Transparent with blue border +- **Success**: Green background for positive actions +- **Critical**: Red background for urgent actions -## ๐Ÿ“‹ Development Guidelines +#### **Card Design** +- **Default**: White background with subtle shadow +- **Elevated**: Enhanced shadow for important content +- **Critical**: Red accent for urgent information -### Code Style -- **TypeScript**: Strict type checking -- **ESLint**: Code quality enforcement +--- + +## ๐Ÿ” Security & Authentication + +### **Authentication Flow** + +``` +1. User Registration โ†’ Email Validation โ†’ Hospital ID Upload โ†’ Admin Approval +2. User Login โ†’ Token Generation โ†’ Session Management โ†’ Access Control +3. Token Refresh โ†’ Automatic Renewal โ†’ Secure Storage โ†’ Session Persistence +``` + +### **Security Features** + +- **Multi-Factor Authentication**: Email + password + hospital verification +- **Token-Based Security**: JWT tokens with automatic refresh +- **Secure Storage**: Encrypted credential storage +- **Admin Approval System**: Account activation requires approval +- **Hospital Integration**: Institution-based access control + +### **Data Protection** + +- **DICOM Encryption**: Medical imaging file security +- **API Security**: HTTPS with token authentication +- **Local Storage**: Encrypted sensitive data +- **Session Management**: Secure session handling + +--- + +## ๐Ÿ“Š Data Flow & State Management + +### **Redux Store Structure** + +```typescript +interface RootState { + auth: AuthState; // Authentication & user data + dashboard: DashboardState; // Dashboard analytics + patientCare: PatientCareState; // Patient & case data + settings: SettingsState; // User preferences + hospital: HospitalState; // Hospital information +} +``` + +### **Data Flow Pattern** + +``` +1. API Request โ†’ Redux Action โ†’ State Update โ†’ UI Re-render +2. User Interaction โ†’ Redux Action โ†’ API Call โ†’ State Update +3. Local Storage โ†’ Redux Persist โ†’ State Hydration โ†’ App Initialization +``` + +### **State Persistence** + +- **Redux Persist**: Automatic state persistence +- **Selective Persistence**: Only critical data persisted +- **Migration Support**: Version-based state updates +- **Offline Capability**: Local data access + +--- + + + +### **Testing Strategy** + +#### **Unit Testing** +```bash +# Run unit tests +npm test + +# Run tests with coverage +npm run test:coverage + +# Run specific test file +npm test -- PatientCard.test.tsx +``` + +#### **Component Testing** +- **React Native Testing Library**: Component behavior testing +- **Jest**: Test runner and assertion library +- **Mock Services**: API service mocking + +### **Code Quality Tools** + +- **ESLint**: Code linting and style enforcement - **Prettier**: Code formatting -- **Conventional Commits**: Git commit messages +- **TypeScript**: Static type checking +- **Pre-commit Hooks**: Automated quality checks -### Component Guidelines -- **Single Responsibility**: One component, one purpose -- **Reusability**: Shared components in shared/ -- **Type Safety**: Full TypeScript coverage -- **Accessibility**: WCAG 2.1 compliance +--- -### Performance Guidelines -- **Lazy Loading**: Components loaded on demand -- **Memoization**: React.memo for expensive components -- **Image Optimization**: Compressed and cached images -- **Bundle Size**: Minimal dependencies +## ๐Ÿ“ฆ Building & Deployment -## ๐Ÿš€ Deployment +### **Development Build** -### Build Configuration -- **Environment Variables**: Separate configs for dev/staging/prod -- **Code Signing**: Proper certificate management -- **Bundle Optimization**: Minified and optimized builds -- **Asset Management**: Optimized images and fonts +```bash +# Start development server +npm start -### Release Process -1. **Development**: Feature development and testing -2. **Staging**: Integration testing and QA -3. **Production**: Final testing and deployment -4. **Monitoring**: Performance and error tracking +# Run on Android +npm run android -## ๐Ÿ“ž Support +# Run on iOS +npm run ios +``` -### Documentation -- **API Documentation**: Complete API reference -- **User Guide**: End-user documentation -- **Developer Guide**: Technical documentation +### **Production Build** + +#### **Android Build** +```bash +# Generate APK +cd android +./gradlew assembleRelease + +# Generate AAB (for Play Store) +./gradlew bundleRelease +``` + +#### **iOS Build** +```bash +# Open Xcode project +cd ios +open NeoScan_Physician.xcworkspace + +# Archive and distribute through Xcode +``` + +### **Environment Configuration** + +```bash +# Development +npm run start:dev + +# Staging +npm run start:staging + +# Production +npm run start:prod +``` + +--- + +## ๐Ÿšจ Troubleshooting + +### **Common Issues & Solutions** + +#### **Metro Bundler Issues** +```bash +# Clear Metro cache +npm start -- --reset-cache + +# Clear React Native cache +npx react-native start --reset-cache +``` + +#### **Android Build Issues** +```bash +# Clean Android build +cd android +./gradlew clean + +# Clear Android cache +cd android/app/build +rm -rf * +``` + +#### **iOS Build Issues** +```bash +# Clean iOS build +cd ios +xcodebuild clean + +# Reinstall pods +cd ios +pod deintegrate +pod install +``` + +#### **Dependency Issues** +```bash +# Clear npm cache +npm cache clean --force + +# Remove node_modules and reinstall +rm -rf node_modules package-lock.json +npm install +``` + +### **Debug Mode** + +```bash +# Enable debug logging +export DEBUG=* + +# Run with verbose logging +npm start -- --verbose +``` + +--- + +## ๐Ÿ“ž Support & Documentation + +### **Documentation Resources** + +- **API Documentation**: Complete API reference guide +- **User Guide**: End-user documentation and tutorials +- **Developer Guide**: Technical implementation details +- **Architecture Guide**: System design and patterns - **Troubleshooting**: Common issues and solutions -### Contact +### **Contact Information** + - **Technical Support**: dev-support@neoscan.com - **Emergency Support**: emergency-support@neoscan.com - **Feature Requests**: features@neoscan.com +- **Bug Reports**: bugs@neoscan.com + +### **Development Guidelines** + +- **Code Style**: Follow ESLint and Prettier configurations +- **TypeScript**: Use strict typing and proper interfaces +- **Testing**: Write tests for new features +- **Documentation**: Update documentation with code changes +- **Code Review**: All changes require peer review + +--- ## ๐Ÿ“„ License This project is proprietary software developed for healthcare institutions. All rights reserved. +**NeoScan Radiologist App** - Enhancing AI diagnostic accuracy through expert radiologist validation and clinical feedback. + --- -**NeoScan Physician App** - Empowering emergency care with real-time intelligence and seamless workflows. +## ๐Ÿš€ Quick Start for New Developers -/* - * End of File: README.md - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ +### **1. Environment Setup (5 minutes)** +```bash +# Clone and setup +git clone +cd NeoScan_Radiologist +npm install + +# Configure environment +cp .env.example .env +# Edit .env with your API endpoints +``` + +### **2. First Run (3 minutes)** +```bash +# Start development server +npm start + +# In new terminal, run on device +npm run android # or npm run ios +``` + +### **3. Explore the Code (10 minutes)** +- Start with `app/App.tsx` to understand the app structure +- Check `app/modules/Auth/screens/LoginScreen.tsx` for authentication flow +- Review `app/shared/components/` for reusable components +- Examine `app/theme/` for design system + +### **4. Make Your First Change (15 minutes)** +- Modify a component in `app/shared/components/` +- See live updates in your simulator/device +- Understand the hot reload development experience + +--- + +*This README is designed to help developers at all levels understand and contribute to the NeoScan Radiologist App. For additional support, please refer to the documentation or contact the development team.* diff --git a/app/App.tsx b/app/App.tsx index 320c4c6..51a4117 100644 --- a/app/App.tsx +++ b/app/App.tsx @@ -20,127 +20,14 @@ import { selectIsAuthenticated } from './modules/Auth/redux/authSelectors'; // MOCK DATA SECTION - For demonstration and development purposes // ============================================================================ -// Mock dashboard data representing the current state of the ER -const mockDashboard = { - totalPatients: 24, // Total number of patients in ER - criticalPatients: 3, // Number of patients requiring immediate attention - pendingScans: 8, // Number of scans waiting for review - recentReports: 12, // Number of reports generated recently - bedOccupancy: 85, // Percentage of beds currently occupied - departmentStats: { - emergency: 8, // Patients in emergency department - trauma: 4, // Patients in trauma department - cardiac: 3, // Patients in cardiac department - neurology: 2, // Patients in neurology department - pediatrics: 5, // Patients in pediatrics department - icu: 2, // Patients in ICU - }, - shiftInfo: { - currentShift: 'DAY' as const, // Current shift (DAY/NIGHT) - startTime: new Date(), // Shift start time - endTime: new Date(), // Shift end time - attendingPhysician: 'Dr. Smith', // Lead physician on duty - residents: ['Dr. Johnson', 'Dr. Williams'], // Resident physicians - nurses: ['Nurse Brown', 'Nurse Davis'], // Nursing staff - }, - lastUpdated: new Date(), // Last time dashboard was updated -}; - -// Mock patient data representing real patients in the ER -const mockPatients = [ - { - id: '1', // Unique patient identifier - mrn: 'MRN001', // Medical Record Number - firstName: 'John', - lastName: 'Doe', - dateOfBirth: new Date('1985-03-15'), - gender: 'MALE' as const, - age: 38, - bedNumber: 'A1', // Assigned bed number - roomNumber: '101', // Room number - admissionDate: new Date('2024-01-15'), - status: 'ACTIVE' as const, // Current patient status - priority: 'CRITICAL' as const, // Priority level for treatment - department: 'Emergency', - attendingPhysician: 'Dr. Smith', - allergies: [ - { - id: '1', - name: 'Penicillin', - severity: 'SEVERE' as const, - reaction: 'Anaphylaxis' - }, - ], - medications: [ - { - id: '1', - name: 'Morphine', - dosage: '2mg', - frequency: 'Every 4 hours', - route: 'IV', // Administration route - startDate: new Date(), - status: 'ACTIVE' as const, - prescribedBy: 'Dr. Smith', - }, - ], - vitalSigns: { - bloodPressure: { systolic: 140, diastolic: 90, timestamp: new Date() }, - heartRate: { value: 95, timestamp: new Date() }, - temperature: { value: 37.2, timestamp: new Date() }, - respiratoryRate: { value: 18, timestamp: new Date() }, - oxygenSaturation: { value: 98, timestamp: new Date() }, - }, - medicalHistory: [], - currentDiagnosis: 'Chest pain, rule out MI', // Current medical diagnosis - lastUpdated: new Date(), - }, - { - id: '2', - mrn: 'MRN002', - firstName: 'Jane', - lastName: 'Smith', - dateOfBirth: new Date('1990-07-22'), - gender: 'FEMALE' as const, - age: 33, - bedNumber: 'B2', - roomNumber: '102', - admissionDate: new Date('2024-01-15'), - status: 'ACTIVE' as const, - priority: 'HIGH' as const, - department: 'Trauma', - attendingPhysician: 'Dr. Johnson', - allergies: [], - medications: [], - vitalSigns: { - bloodPressure: { systolic: 120, diastolic: 80, timestamp: new Date() }, - heartRate: { value: 88, timestamp: new Date() }, - temperature: { value: 36.8, timestamp: new Date() }, - respiratoryRate: { value: 16, timestamp: new Date() }, - oxygenSaturation: { value: 99, timestamp: new Date() }, - }, - medicalHistory: [], - currentDiagnosis: 'Multiple trauma from MVA', // MVA = Motor Vehicle Accident - lastUpdated: new Date(), - }, -]; - -// Mock alerts representing critical notifications that require immediate attention -const mockAlerts = [ - { - id: '1', - type: 'CRITICAL_FINDING' as const, // Type of alert - priority: 'CRITICAL' as const, // Priority level - title: 'Critical Finding Detected', - message: 'AI has detected a potential brain bleed in CT scan. Immediate review required.', - patientId: '1', // Associated patient - patientName: 'John Doe', - bedNumber: 'A1', - timestamp: new Date(), // When alert was generated - isRead: false, // Whether alert has been read - isAcknowledged: false, // Whether alert has been acknowledged - actionRequired: true, // Whether action is required - }, -]; +// Note: Mock data has been removed for production use +// The following mock data structures were previously defined: +// - mockDashboard: ER dashboard statistics and shift information +// - mockPatients: Patient data with medical records and vital signs +// - mockAlerts: Critical notifications requiring immediate attention +// +// These can be restored for development/testing purposes by uncommenting +// the mock data definitions below this comment block. /** * AppContent Component (Inner Component) diff --git a/app/assets/dicom/test-dicom-viewer.html b/app/assets/dicom/test-dicom-viewer.html deleted file mode 100644 index 84a39b5..0000000 --- a/app/assets/dicom/test-dicom-viewer.html +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - Dummy Page - - - -
-

Welcome to the Dummy Page

-

This is just a placeholder website.

-
- - - -
-
-

About Us

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ac orci vel nisi gravida feugiat.

-
- -
-

Our Services

-
    -
  • Service One
  • -
  • Service Two
  • -
  • Service Three
  • -
-
- -
-

Contact

-
-
-

- -
-

- -
-

- - -
-
-
- -
-

© 2025 Dummy Company. All rights reserved.

-
- - diff --git a/app/modules/AIPrediction/__tests__/AIPredictionCard.test.tsx b/app/modules/AIPrediction/__tests__/AIPredictionCard.test.tsx deleted file mode 100644 index 4c120e4..0000000 --- a/app/modules/AIPrediction/__tests__/AIPredictionCard.test.tsx +++ /dev/null @@ -1,249 +0,0 @@ -/* - * File: AIPredictionCard.test.tsx - * Description: Unit tests for AI Prediction Card 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 AIPredictionCard from '../components/AIPredictionCard'; -import type { AIPredictionCase } from '../types'; - -// ============================================================================ -// MOCK DATA -// ============================================================================ - -const mockPredictionCase: AIPredictionCase = { - patid: 'test-patient-001', - hospital_id: 'hospital-123', - prediction: { - label: 'midline shift', - finding_type: 'pathology', - clinical_urgency: 'urgent', - confidence_score: 0.96, - finding_category: 'abnormal', - primary_severity: 'high', - anatomical_location: 'brain', - }, - created_at: '2024-01-15T10:30:00Z', - updated_at: '2024-01-15T10:30:00Z', - review_status: 'pending', - priority: 'critical', -}; - -const mockProps = { - predictionCase: mockPredictionCase, - onPress: jest.fn(), - onReview: jest.fn(), - onToggleSelect: jest.fn(), - isSelected: false, - showReviewButton: true, -}; - -// ============================================================================ -// UNIT TESTS -// ============================================================================ - -describe('AIPredictionCard', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - // ============================================================================ - // RENDERING TESTS - // ============================================================================ - - describe('Rendering', () => { - it('should render correctly with required props', () => { - const { getByText } = render( - - ); - - expect(getByText('test-patient-001')).toBeTruthy(); - expect(getByText('Midline Shift')).toBeTruthy(); - expect(getByText('96%')).toBeTruthy(); - expect(getByText('Urgent')).toBeTruthy(); - }); - - it('should render review button when showReviewButton is true', () => { - const { getByText } = render(); - expect(getByText('Review')).toBeTruthy(); - }); - - it('should not render review button when showReviewButton is false', () => { - const { queryByText } = render( - - ); - expect(queryByText('Review')).toBeNull(); - }); - - it('should not render review button when status is not pending', () => { - const reviewedCase = { - ...mockPredictionCase, - review_status: 'reviewed' as const, - }; - - const { queryByText } = render( - - ); - expect(queryByText('Review')).toBeNull(); - }); - - it('should render selection checkbox when onToggleSelect is provided', () => { - const { getByRole } = render(); - expect(getByRole('checkbox')).toBeTruthy(); - }); - - it('should show selected state correctly', () => { - const { getByRole } = render( - - ); - - const checkbox = getByRole('checkbox'); - expect(checkbox.props.accessibilityState.checked).toBe(true); - }); - }); - - // ============================================================================ - // INTERACTION TESTS - // ============================================================================ - - describe('Interactions', () => { - it('should call onPress when card is pressed', () => { - const { getByRole } = render(); - - fireEvent.press(getByRole('button')); - expect(mockProps.onPress).toHaveBeenCalledWith(mockPredictionCase); - }); - - it('should call onReview when review button is pressed', () => { - const { getByText } = render(); - - fireEvent.press(getByText('Review')); - expect(mockProps.onReview).toHaveBeenCalledWith('test-patient-001'); - }); - - it('should call onToggleSelect when checkbox is pressed', () => { - const { getByRole } = render(); - - fireEvent.press(getByRole('checkbox')); - expect(mockProps.onToggleSelect).toHaveBeenCalledWith('test-patient-001'); - }); - }); - - // ============================================================================ - // DATA FORMATTING TESTS - // ============================================================================ - - describe('Data formatting', () => { - it('should format confidence score as percentage', () => { - const { getByText } = render(); - expect(getByText('96%')).toBeTruthy(); - }); - - it('should capitalize text correctly', () => { - const { getByText } = render(); - expect(getByText('Midline Shift')).toBeTruthy(); - expect(getByText('Pathology')).toBeTruthy(); - expect(getByText('Abnormal')).toBeTruthy(); - }); - - it('should handle missing anatomical location', () => { - const caseWithoutLocation = { - ...mockPredictionCase, - prediction: { - ...mockPredictionCase.prediction, - anatomical_location: 'not_applicable', - }, - }; - - const { queryByText } = render( - - ); - - // Should not render location when it's 'not_applicable' - expect(queryByText('Not Applicable')).toBeNull(); - }); - }); - - // ============================================================================ - // ACCESSIBILITY TESTS - // ============================================================================ - - describe('Accessibility', () => { - it('should have proper accessibility labels', () => { - const { getByLabelText } = render(); - - expect( - getByLabelText('AI Prediction case for patient test-patient-001') - ).toBeTruthy(); - }); - - it('should have proper accessibility hints', () => { - const { getByRole } = render(); - - const cardButton = getByRole('button'); - expect(cardButton.props.accessibilityHint).toBe( - 'Tap to view detailed prediction information' - ); - }); - }); - - // ============================================================================ - // EDGE CASES TESTS - // ============================================================================ - - describe('Edge cases', () => { - it('should handle missing dates gracefully', () => { - const caseWithoutDates = { - ...mockPredictionCase, - created_at: undefined, - updated_at: undefined, - }; - - const { getByText } = render( - - ); - - expect(getByText('N/A')).toBeTruthy(); - }); - - it('should handle emergency urgency with special styling', () => { - const emergencyCase = { - ...mockPredictionCase, - prediction: { - ...mockPredictionCase.prediction, - clinical_urgency: 'emergency' as const, - }, - }; - - const { getByText } = render( - - ); - - expect(getByText('Emergency')).toBeTruthy(); - }); - }); -}); - -/* - * End of File: AIPredictionCard.test.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/__tests__/aiPredictionAPI.test.ts b/app/modules/AIPrediction/__tests__/aiPredictionAPI.test.ts deleted file mode 100644 index fb85b00..0000000 --- a/app/modules/AIPrediction/__tests__/aiPredictionAPI.test.ts +++ /dev/null @@ -1,361 +0,0 @@ -/* - * File: aiPredictionAPI.test.ts - * Description: Unit tests for AI Prediction API service - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import { aiPredictionAPI } from '../services/aiPredictionAPI'; - -// Mock apisauce -jest.mock('apisauce', () => ({ - create: jest.fn(() => ({ - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - delete: jest.fn(), - })), -})); - -// Mock API utilities -jest.mock('../../../shared/utils', () => ({ - API_CONFIG: { - BASE_URL: 'https://test-api.com', - }, - buildHeaders: jest.fn((options = {}) => ({ - headers: { - 'Content-Type': 'application/json', - ...(options.token && { Authorization: `Bearer ${options.token}` }), - }, - })), -})); - -// ============================================================================ -// MOCK DATA -// ============================================================================ - -const mockToken = 'test-token-123'; -const mockResponse = { - ok: true, - data: { - success: true, - data: [ - { - patid: 'test-001', - hospital_id: 'hospital-001', - prediction: { - label: 'test finding', - finding_type: 'pathology', - clinical_urgency: 'urgent', - confidence_score: 0.95, - finding_category: 'abnormal', - primary_severity: 'high', - anatomical_location: 'brain', - }, - }, - ], - }, -}; - -// ============================================================================ -// UNIT TESTS -// ============================================================================ - -describe('AI Prediction API', () => { - let mockApi: any; - - beforeEach(() => { - // Reset mocks - jest.clearAllMocks(); - - // Get the mocked API instance - const { create } = require('apisauce'); - mockApi = create(); - }); - - // ============================================================================ - // GET ALL PREDICTIONS TESTS - // ============================================================================ - - describe('getAllPredictions', () => { - it('should call GET endpoint with correct parameters', async () => { - mockApi.get.mockResolvedValue(mockResponse); - - const params = { - page: 1, - limit: 20, - urgency: 'urgent', - search: 'test', - }; - - await aiPredictionAPI.getAllPredictions(mockToken, params); - - expect(mockApi.get).toHaveBeenCalledWith( - '/api/ai-cases/all-prediction-results', - params, - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Bearer ${mockToken}`, - }), - }) - ); - }); - - it('should call GET endpoint without parameters', async () => { - mockApi.get.mockResolvedValue(mockResponse); - - await aiPredictionAPI.getAllPredictions(mockToken); - - expect(mockApi.get).toHaveBeenCalledWith( - '/api/ai-cases/all-prediction-results', - {}, - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Bearer ${mockToken}`, - }), - }) - ); - }); - }); - - // ============================================================================ - // GET CASE DETAILS TESTS - // ============================================================================ - - describe('getCaseDetails', () => { - it('should call GET endpoint with correct case ID', async () => { - const caseId = 'test-case-001'; - mockApi.get.mockResolvedValue(mockResponse); - - await aiPredictionAPI.getCaseDetails(caseId, mockToken); - - expect(mockApi.get).toHaveBeenCalledWith( - `/api/ai-cases/prediction-details/${caseId}`, - {}, - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Bearer ${mockToken}`, - }), - }) - ); - }); - }); - - // ============================================================================ - // UPDATE CASE REVIEW TESTS - // ============================================================================ - - describe('updateCaseReview', () => { - it('should call PUT endpoint with correct data', async () => { - const caseId = 'test-case-001'; - const reviewData = { - review_status: 'reviewed' as const, - reviewed_by: 'Dr. Test', - review_notes: 'Test notes', - priority: 'high' as const, - }; - - mockApi.put.mockResolvedValue(mockResponse); - - await aiPredictionAPI.updateCaseReview(caseId, reviewData, mockToken); - - expect(mockApi.put).toHaveBeenCalledWith( - `/api/ai-cases/review/${caseId}`, - reviewData, - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Bearer ${mockToken}`, - }), - }) - ); - }); - }); - - // ============================================================================ - // GET STATISTICS TESTS - // ============================================================================ - - describe('getPredictionStats', () => { - it('should call GET endpoint with time range parameter', async () => { - mockApi.get.mockResolvedValue(mockResponse); - - await aiPredictionAPI.getPredictionStats(mockToken, 'week'); - - expect(mockApi.get).toHaveBeenCalledWith( - '/api/ai-cases/statistics', - { timeRange: 'week' }, - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Bearer ${mockToken}`, - }), - }) - ); - }); - - it('should call GET endpoint without time range parameter', async () => { - mockApi.get.mockResolvedValue(mockResponse); - - await aiPredictionAPI.getPredictionStats(mockToken); - - expect(mockApi.get).toHaveBeenCalledWith( - '/api/ai-cases/statistics', - {}, - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Bearer ${mockToken}`, - }), - }) - ); - }); - }); - - // ============================================================================ - // SEARCH PREDICTIONS TESTS - // ============================================================================ - - describe('searchPredictions', () => { - it('should call GET endpoint with search query and filters', async () => { - const query = 'test search'; - const filters = { - urgency: ['urgent', 'emergency'], - severity: ['high'], - dateRange: { start: '2024-01-01', end: '2024-01-31' }, - }; - - mockApi.get.mockResolvedValue(mockResponse); - - await aiPredictionAPI.searchPredictions(query, mockToken, filters); - - expect(mockApi.get).toHaveBeenCalledWith( - '/api/ai-cases/search', - { - q: query, - filters: JSON.stringify(filters), - }, - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Bearer ${mockToken}`, - }), - }) - ); - }); - - it('should call GET endpoint with only search query', async () => { - const query = 'test search'; - mockApi.get.mockResolvedValue(mockResponse); - - await aiPredictionAPI.searchPredictions(query, mockToken); - - expect(mockApi.get).toHaveBeenCalledWith( - '/api/ai-cases/search', - { q: query }, - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Bearer ${mockToken}`, - }), - }) - ); - }); - }); - - // ============================================================================ - // BULK OPERATIONS TESTS - // ============================================================================ - - describe('bulkUpdateReviews', () => { - it('should call PUT endpoint with case IDs and review data', async () => { - const caseIds = ['case-001', 'case-002', 'case-003']; - const reviewData = { - review_status: 'reviewed' as const, - reviewed_by: 'Dr. Test', - review_notes: 'Bulk review', - }; - - mockApi.put.mockResolvedValue(mockResponse); - - await aiPredictionAPI.bulkUpdateReviews(caseIds, reviewData, mockToken); - - expect(mockApi.put).toHaveBeenCalledWith( - '/api/ai-cases/bulk-review', - { caseIds, reviewData }, - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Bearer ${mockToken}`, - }), - }) - ); - }); - }); - - // ============================================================================ - // SUBMIT FEEDBACK TESTS - // ============================================================================ - - describe('submitPredictionFeedback', () => { - it('should call POST endpoint with feedback data', async () => { - const caseId = 'test-case-001'; - const feedbackData = { - accuracy_rating: 4 as const, - is_accurate: true, - physician_diagnosis: 'Confirmed midline shift', - feedback_notes: 'Accurate prediction', - improvement_suggestions: 'None', - }; - - mockApi.post.mockResolvedValue(mockResponse); - - await aiPredictionAPI.submitPredictionFeedback(caseId, feedbackData, mockToken); - - expect(mockApi.post).toHaveBeenCalledWith( - `/api/ai-cases/feedback/${caseId}`, - feedbackData, - expect.objectContaining({ - headers: expect.objectContaining({ - Authorization: `Bearer ${mockToken}`, - }), - }) - ); - }); - }); - - // ============================================================================ - // ERROR HANDLING TESTS - // ============================================================================ - - describe('Error handling', () => { - it('should handle API errors gracefully', async () => { - const errorResponse = { - ok: false, - problem: 'NETWORK_ERROR', - data: null, - }; - - mockApi.get.mockResolvedValue(errorResponse); - - const result = await aiPredictionAPI.getAllPredictions(mockToken); - expect(result).toEqual(errorResponse); - }); - - it('should handle missing token', async () => { - mockApi.get.mockResolvedValue(mockResponse); - - await aiPredictionAPI.getAllPredictions(''); - - expect(mockApi.get).toHaveBeenCalledWith( - '/api/ai-cases/all-prediction-results', - {}, - expect.objectContaining({ - headers: expect.objectContaining({ - 'Content-Type': 'application/json', - }), - }) - ); - }); - }); -}); - -/* - * End of File: aiPredictionAPI.test.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/__tests__/aiPredictionSlice.test.ts b/app/modules/AIPrediction/__tests__/aiPredictionSlice.test.ts deleted file mode 100644 index 7975d6c..0000000 --- a/app/modules/AIPrediction/__tests__/aiPredictionSlice.test.ts +++ /dev/null @@ -1,231 +0,0 @@ -/* - * File: aiPredictionSlice.test.ts - * Description: Unit tests for AI Prediction Redux slice - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import aiPredictionReducer, { - setSearchQuery, - setUrgencyFilter, - setSeverityFilter, - setCategoryFilter, - clearAllFilters, - toggleShowFilters, - clearError, -} from '../redux/aiPredictionSlice'; -import type { AIPredictionState } from '../types'; - -// ============================================================================ -// MOCK DATA -// ============================================================================ - -const initialState: AIPredictionState = { - predictionCases: [], - currentCase: null, - isLoading: false, - isRefreshing: false, - isLoadingCaseDetails: false, - error: null, - searchQuery: '', - selectedUrgencyFilter: 'all', - selectedSeverityFilter: 'all', - selectedCategoryFilter: 'all', - sortBy: 'date', - sortOrder: 'desc', - currentPage: 1, - itemsPerPage: 20, - totalItems: 0, - lastUpdated: null, - cacheExpiry: null, - showFilters: false, - selectedCaseIds: [], -}; - -// ============================================================================ -// UNIT TESTS -// ============================================================================ - -describe('AI Prediction Slice', () => { - // ============================================================================ - // INITIAL STATE TESTS - // ============================================================================ - - it('should return the initial state', () => { - const result = aiPredictionReducer(undefined, { type: 'unknown' }); - expect(result).toEqual(initialState); - }); - - // ============================================================================ - // SEARCH TESTS - // ============================================================================ - - describe('Search functionality', () => { - it('should handle setSearchQuery', () => { - const searchQuery = 'test search'; - const action = setSearchQuery(searchQuery); - const result = aiPredictionReducer(initialState, action); - - expect(result.searchQuery).toBe(searchQuery); - expect(result.currentPage).toBe(1); // Should reset to first page - }); - - it('should handle empty search query', () => { - const state = { ...initialState, searchQuery: 'existing search' }; - const action = setSearchQuery(''); - const result = aiPredictionReducer(state, action); - - expect(result.searchQuery).toBe(''); - expect(result.currentPage).toBe(1); - }); - }); - - // ============================================================================ - // FILTER TESTS - // ============================================================================ - - describe('Filter functionality', () => { - it('should handle setUrgencyFilter', () => { - const filter = 'urgent'; - const action = setUrgencyFilter(filter); - const result = aiPredictionReducer(initialState, action); - - expect(result.selectedUrgencyFilter).toBe(filter); - expect(result.currentPage).toBe(1); // Should reset to first page - }); - - it('should handle setSeverityFilter', () => { - const filter = 'high'; - const action = setSeverityFilter(filter); - const result = aiPredictionReducer(initialState, action); - - expect(result.selectedSeverityFilter).toBe(filter); - expect(result.currentPage).toBe(1); - }); - - it('should handle setCategoryFilter', () => { - const filter = 'critical'; - const action = setCategoryFilter(filter); - const result = aiPredictionReducer(initialState, action); - - expect(result.selectedCategoryFilter).toBe(filter); - expect(result.currentPage).toBe(1); - }); - - it('should handle clearAllFilters', () => { - const state: AIPredictionState = { - ...initialState, - searchQuery: 'test', - selectedUrgencyFilter: 'urgent', - selectedSeverityFilter: 'high', - selectedCategoryFilter: 'critical', - currentPage: 3, - }; - - const action = clearAllFilters(); - const result = aiPredictionReducer(state, action); - - expect(result.searchQuery).toBe(''); - expect(result.selectedUrgencyFilter).toBe('all'); - expect(result.selectedSeverityFilter).toBe('all'); - expect(result.selectedCategoryFilter).toBe('all'); - expect(result.currentPage).toBe(1); - }); - }); - - // ============================================================================ - // UI STATE TESTS - // ============================================================================ - - describe('UI state functionality', () => { - it('should handle toggleShowFilters', () => { - const action = toggleShowFilters(); - - // Toggle from false to true - const result1 = aiPredictionReducer(initialState, action); - expect(result1.showFilters).toBe(true); - - // Toggle from true to false - const result2 = aiPredictionReducer(result1, action); - expect(result2.showFilters).toBe(false); - }); - - it('should handle clearError', () => { - const state = { ...initialState, error: 'Test error' }; - const action = clearError(); - const result = aiPredictionReducer(state, action); - - expect(result.error).toBe(null); - }); - }); - - // ============================================================================ - // ASYNC ACTION TESTS - // ============================================================================ - - describe('Async actions', () => { - it('should handle fetchAIPredictions.pending', () => { - const action = { type: 'aiPrediction/fetchAIPredictions/pending' }; - const result = aiPredictionReducer(initialState, action); - - expect(result.isLoading).toBe(true); - expect(result.error).toBe(null); - }); - - it('should handle fetchAIPredictions.fulfilled', () => { - const mockCases = [ - { - patid: 'test-001', - hospital_id: 'hospital-001', - prediction: { - label: 'test finding', - finding_type: 'pathology' as const, - clinical_urgency: 'urgent' as const, - confidence_score: 0.95, - finding_category: 'abnormal' as const, - primary_severity: 'high' as const, - anatomical_location: 'brain', - }, - }, - ]; - - const action = { - type: 'aiPrediction/fetchAIPredictions/fulfilled', - payload: { - cases: mockCases, - total: 1, - page: 1, - limit: 20, - }, - }; - - const result = aiPredictionReducer(initialState, action); - - expect(result.isLoading).toBe(false); - expect(result.predictionCases).toEqual(mockCases); - expect(result.totalItems).toBe(1); - expect(result.error).toBe(null); - expect(result.lastUpdated).toBeInstanceOf(Date); - expect(result.cacheExpiry).toBeInstanceOf(Date); - }); - - it('should handle fetchAIPredictions.rejected', () => { - const errorMessage = 'Failed to fetch predictions'; - const action = { - type: 'aiPrediction/fetchAIPredictions/rejected', - payload: errorMessage, - }; - - const result = aiPredictionReducer(initialState, action); - - expect(result.isLoading).toBe(false); - expect(result.error).toBe(errorMessage); - }); - }); -}); - -/* - * End of File: aiPredictionSlice.test.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/components/AIPredictionCard.tsx b/app/modules/AIPrediction/components/AIPredictionCard.tsx deleted file mode 100644 index 590d57a..0000000 --- a/app/modules/AIPrediction/components/AIPredictionCard.tsx +++ /dev/null @@ -1,522 +0,0 @@ -/* - * File: AIPredictionCard.tsx - * Description: Card component for displaying AI prediction case information - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React from 'react'; -import { - View, - Text, - StyleSheet, - TouchableOpacity, - Dimensions, -} from 'react-native'; -import Icon from 'react-native-vector-icons/Feather'; -import { theme } from '../../../theme'; -import { AIPredictionCase, URGENCY_COLORS, SEVERITY_COLORS, CATEGORY_COLORS } from '../types'; - -// ============================================================================ -// INTERFACES -// ============================================================================ - -interface AIPredictionCardProps { - predictionCase: AIPredictionCase; - onPress: (predictionCase: AIPredictionCase) => void; - onReview?: (caseId: string) => void; - isSelected?: boolean; - onToggleSelect?: (caseId: string) => void; - showReviewButton?: boolean; -} - -// ============================================================================ -// CONSTANTS -// ============================================================================ - -const { width } = Dimensions.get('window'); -const CARD_WIDTH = width - 32; // Full width with margins - -// ============================================================================ -// AI PREDICTION CARD COMPONENT -// ============================================================================ - -/** - * AIPredictionCard Component - * - * Purpose: Display AI prediction case information in a card format - * - * Features: - * - Patient ID and hospital information - * - AI prediction results with confidence score - * - Urgency and severity indicators - * - Review status and actions - * - Selection support for bulk operations - * - Modern card design with proper spacing - * - Color-coded priority indicators - * - Accessibility support - */ -const AIPredictionCard: React.FC = ({ - predictionCase, - onPress, - onReview, - isSelected = false, - onToggleSelect, - showReviewButton = true, -}) => { - // ============================================================================ - // HELPER FUNCTIONS - // ============================================================================ - - /** - * Get Urgency Color - * - * Purpose: Get color based on clinical urgency - */ - const getUrgencyColor = (urgency: string): string => { - return URGENCY_COLORS[urgency as keyof typeof URGENCY_COLORS] || theme.colors.textMuted; - }; - - /** - * Get Severity Color - * - * Purpose: Get color based on primary severity - */ - const getSeverityColor = (severity: string): string => { - return SEVERITY_COLORS[severity as keyof typeof SEVERITY_COLORS] || theme.colors.textMuted; - }; - - /** - * Get Category Color - * - * Purpose: Get color based on finding category - */ - const getCategoryColor = (category: string): string => { - return CATEGORY_COLORS[category as keyof typeof CATEGORY_COLORS] || theme.colors.textMuted; - }; - - /** - * Get Review Status Color - * - * Purpose: Get color based on review status - */ - const getReviewStatusColor = (status: string): string => { - switch (status) { - case 'confirmed': - return theme.colors.success; - case 'reviewed': - return theme.colors.info; - case 'disputed': - return theme.colors.warning; - case 'pending': - default: - return theme.colors.error; - } - }; - - /** - * Format Confidence Score - * - * Purpose: Format confidence score as percentage - */ - const formatConfidence = (score: number): string => { - return `${Math.round(score * 100)}%`; - }; - - /** - * Capitalize Text - * - * Purpose: Capitalize first letter of each word - */ - const capitalize = (text: string): string => { - return text.split('_').map(word => - word.charAt(0).toUpperCase() + word.slice(1) - ).join(' '); - }; - - /** - * Format Date - * - * Purpose: Format date for display - */ - const formatDate = (dateString?: string): string => { - if (!dateString) return 'N/A'; - try { - const date = new Date(dateString); - return date.toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - }); - } catch { - return 'N/A'; - } - }; - - // ============================================================================ - // EVENT HANDLERS - // ============================================================================ - - /** - * Handle Card Press - * - * Purpose: Handle card tap to view details - */ - const handleCardPress = () => { - onPress(predictionCase); - }; - - /** - * Handle Review Press - * - * Purpose: Handle review button press - */ - const handleReviewPress = (event: any) => { - event.stopPropagation(); - if (onReview) { - onReview(predictionCase.patid); - } - }; - - /** - * Handle Selection Toggle - * - * Purpose: Handle case selection toggle - */ - const handleSelectionToggle = (event: any) => { - event.stopPropagation(); - if (onToggleSelect) { - onToggleSelect(predictionCase.patid); - } - }; - - // ============================================================================ - // RENDER - // ============================================================================ - - return ( - - {/* Header Section */} - - - - {predictionCase.patid} - - - {formatDate(predictionCase.processed_at)} - - - - - {onToggleSelect && ( - - - - )} - - - - {capitalize(predictionCase.prediction.clinical_urgency)} - - - - - - {/* Prediction Information */} - - - - {capitalize(predictionCase.prediction.label)} - - - - - {formatConfidence(predictionCase.prediction.confidence_score)} - - - - - {/* Finding Details */} - - - Type: - - {capitalize(predictionCase.prediction.finding_type)} - - - - - Category: - - - {capitalize(predictionCase.prediction.finding_category)} - - - - - - {/* Severity and Location */} - - - - - {capitalize(predictionCase.prediction.primary_severity)} Severity - - - - {predictionCase.prediction.anatomical_location !== 'not_applicable' && ( - - - - {capitalize(predictionCase.prediction.anatomical_location)} - - - )} - - - - {/* Footer Section */} - - - - - {capitalize(predictionCase.review_status || 'pending')} - - - - {predictionCase.reviewed_by && ( - - by {predictionCase.reviewed_by} - - )} - - - {showReviewButton && predictionCase.review_status === 'pending' && ( - - - Review - - )} - - - ); -}; - -// ============================================================================ -// STYLES -// ============================================================================ - -const styles = StyleSheet.create({ - container: { - backgroundColor: theme.colors.background, - borderRadius: theme.borderRadius.large, - padding: theme.spacing.lg, - marginHorizontal: theme.spacing.md, - marginVertical: theme.spacing.sm, - width: CARD_WIDTH, - ...theme.shadows.medium, - borderWidth: 1, - borderColor: theme.colors.border, - }, - selectedContainer: { - borderColor: theme.colors.primary, - borderWidth: 2, - }, - emergencyContainer: { - borderLeftWidth: 4, - borderLeftColor: URGENCY_COLORS.emergency, - }, - header: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'flex-start', - marginBottom: theme.spacing.md, - }, - headerLeft: { - flex: 1, - }, - headerRight: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.sm, - }, - patientId: { - fontSize: theme.typography.fontSize.bodyLarge, - fontWeight: theme.typography.fontWeight.bold, - color: theme.colors.textPrimary, - marginBottom: theme.spacing.xs, - }, - date: { - fontSize: theme.typography.fontSize.bodySmall, - color: theme.colors.textSecondary, - }, - selectionButton: { - padding: theme.spacing.xs, - }, - priorityBadge: { - paddingHorizontal: theme.spacing.sm, - paddingVertical: theme.spacing.xs, - borderRadius: theme.borderRadius.small, - }, - priorityText: { - fontSize: theme.typography.fontSize.caption, - fontWeight: theme.typography.fontWeight.medium, - color: theme.colors.background, - }, - predictionSection: { - marginBottom: theme.spacing.md, - }, - predictionHeader: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'flex-start', - marginBottom: theme.spacing.sm, - }, - predictionLabel: { - fontSize: theme.typography.fontSize.bodyLarge, - fontWeight: theme.typography.fontWeight.medium, - color: theme.colors.textPrimary, - flex: 1, - marginRight: theme.spacing.sm, - }, - confidenceContainer: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.xs, - }, - confidenceText: { - fontSize: theme.typography.fontSize.bodyMedium, - fontWeight: theme.typography.fontWeight.medium, - color: theme.colors.primary, - }, - findingDetails: { - marginBottom: theme.spacing.sm, - }, - findingItem: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - marginBottom: theme.spacing.xs, - }, - findingLabel: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textSecondary, - fontWeight: theme.typography.fontWeight.medium, - }, - findingValue: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textPrimary, - }, - categoryBadge: { - paddingHorizontal: theme.spacing.sm, - paddingVertical: theme.spacing.xs, - borderRadius: theme.borderRadius.small, - }, - categoryText: { - fontSize: theme.typography.fontSize.caption, - fontWeight: theme.typography.fontWeight.medium, - color: theme.colors.background, - }, - detailsRow: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - detailItem: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.xs, - flex: 1, - }, - detailText: { - fontSize: theme.typography.fontSize.bodySmall, - color: theme.colors.textSecondary, - }, - footer: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingTop: theme.spacing.sm, - borderTopWidth: 1, - borderTopColor: theme.colors.border, - }, - footerLeft: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.sm, - flex: 1, - }, - reviewStatusBadge: { - paddingHorizontal: theme.spacing.sm, - paddingVertical: theme.spacing.xs, - borderRadius: theme.borderRadius.small, - }, - reviewStatusText: { - fontSize: theme.typography.fontSize.caption, - fontWeight: theme.typography.fontWeight.medium, - color: theme.colors.background, - }, - reviewedBy: { - fontSize: theme.typography.fontSize.caption, - color: theme.colors.textMuted, - fontStyle: 'italic', - }, - reviewButton: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.xs, - paddingHorizontal: theme.spacing.sm, - paddingVertical: theme.spacing.xs, - borderRadius: theme.borderRadius.small, - borderWidth: 1, - borderColor: theme.colors.primary, - }, - reviewButtonText: { - fontSize: theme.typography.fontSize.bodySmall, - color: theme.colors.primary, - fontWeight: theme.typography.fontWeight.medium, - }, -}); - -export default AIPredictionCard; - -/* - * End of File: AIPredictionCard.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/components/EmptyState.tsx b/app/modules/AIPrediction/components/EmptyState.tsx deleted file mode 100644 index 81b26d7..0000000 --- a/app/modules/AIPrediction/components/EmptyState.tsx +++ /dev/null @@ -1,287 +0,0 @@ -/* - * File: EmptyState.tsx - * Description: Empty state component for AI predictions - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React from 'react'; -import { - View, - Text, - StyleSheet, - TouchableOpacity, - Dimensions, -} from 'react-native'; -import Icon from 'react-native-vector-icons/Feather'; -import { theme } from '../../../theme'; - -// ============================================================================ -// INTERFACES -// ============================================================================ - -interface EmptyStateProps { - title?: string; - message?: string; - iconName?: string; - actionText?: string; - onAction?: () => void; - style?: any; - showRefreshButton?: boolean; - onRefresh?: () => void; -} - -// ============================================================================ -// CONSTANTS -// ============================================================================ - -const { width, height } = Dimensions.get('window'); - -// ============================================================================ -// EMPTY STATE COMPONENT -// ============================================================================ - -/** - * EmptyState Component - * - * Purpose: Display empty state for AI predictions - * - * Features: - * - Customizable title and message - * - Icon display with customizable icon - * - Optional action button - * - Refresh functionality - * - Responsive design - * - Modern empty state design - * - Accessibility support - */ -const EmptyState: React.FC = ({ - title = 'No AI Predictions Found', - message = 'There are no AI prediction cases available at the moment. Try adjusting your filters or refresh to see new predictions.', - iconName = 'brain', - actionText = 'Refresh', - onAction, - style, - showRefreshButton = true, - onRefresh, -}) => { - // ============================================================================ - // EVENT HANDLERS - // ============================================================================ - - /** - * Handle Action Press - * - * Purpose: Handle action button press - */ - const handleActionPress = () => { - if (onAction) { - onAction(); - } else if (onRefresh) { - onRefresh(); - } - }; - - // ============================================================================ - // RENDER - // ============================================================================ - - return ( - - {/* Empty State Icon */} - - - - - {/* Empty State Title */} - - {title} - - - {/* Empty State Message */} - - {message} - - - {/* Action Buttons */} - - {/* Primary Action Button */} - {(onAction || onRefresh) && ( - - - - {actionText} - - - )} - - {/* Secondary Refresh Button */} - {showRefreshButton && onRefresh && !onAction && ( - - - - Refresh Data - - - )} - - - {/* Suggestions */} - - Try: - - - - Clearing search filters - - - - Adjusting filter criteria - - - - Refreshing the data - - - - - ); -}; - -// ============================================================================ -// STYLES -// ============================================================================ - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - paddingHorizontal: theme.spacing.xl, - paddingVertical: theme.spacing.xxl, - minHeight: height * 0.4, - }, - iconContainer: { - width: 120, - height: 120, - borderRadius: 60, - backgroundColor: theme.colors.backgroundAlt, - justifyContent: 'center', - alignItems: 'center', - marginBottom: theme.spacing.xl, - ...theme.shadows.small, - }, - icon: { - opacity: 0.6, - }, - title: { - fontSize: theme.typography.fontSize.displaySmall, - fontWeight: theme.typography.fontWeight.bold, - color: theme.colors.textPrimary, - textAlign: 'center', - marginBottom: theme.spacing.md, - }, - message: { - fontSize: theme.typography.fontSize.bodyLarge, - color: theme.colors.textSecondary, - textAlign: 'center', - lineHeight: theme.typography.lineHeight.relaxed * theme.typography.fontSize.bodyLarge, - marginBottom: theme.spacing.xl, - maxWidth: width * 0.8, - }, - buttonsContainer: { - flexDirection: 'row', - gap: theme.spacing.md, - marginBottom: theme.spacing.xl, - }, - actionButton: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: theme.colors.primary, - paddingHorizontal: theme.spacing.lg, - paddingVertical: theme.spacing.md, - borderRadius: theme.borderRadius.medium, - gap: theme.spacing.sm, - ...theme.shadows.medium, - }, - actionButtonText: { - fontSize: theme.typography.fontSize.bodyMedium, - fontWeight: theme.typography.fontWeight.medium, - color: theme.colors.background, - }, - secondaryButton: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: 'transparent', - borderWidth: 1, - borderColor: theme.colors.primary, - paddingHorizontal: theme.spacing.lg, - paddingVertical: theme.spacing.md, - borderRadius: theme.borderRadius.medium, - gap: theme.spacing.sm, - }, - secondaryButtonText: { - fontSize: theme.typography.fontSize.bodyMedium, - fontWeight: theme.typography.fontWeight.medium, - color: theme.colors.primary, - }, - buttonIcon: { - // No additional styles needed - }, - suggestionsContainer: { - alignItems: 'center', - maxWidth: width * 0.8, - }, - suggestionsTitle: { - fontSize: theme.typography.fontSize.bodyMedium, - fontWeight: theme.typography.fontWeight.medium, - color: theme.colors.textSecondary, - marginBottom: theme.spacing.sm, - }, - suggestionsList: { - gap: theme.spacing.sm, - }, - suggestionItem: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.sm, - paddingVertical: theme.spacing.xs, - }, - suggestionText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textMuted, - }, -}); - -export default EmptyState; - -/* - * End of File: EmptyState.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/components/FilterTabs.tsx b/app/modules/AIPrediction/components/FilterTabs.tsx deleted file mode 100644 index 3c1c82d..0000000 --- a/app/modules/AIPrediction/components/FilterTabs.tsx +++ /dev/null @@ -1,368 +0,0 @@ -/* - * File: FilterTabs.tsx - * Description: Filter tabs component for AI predictions - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React from 'react'; -import { - View, - Text, - StyleSheet, - TouchableOpacity, - ScrollView, - Dimensions, -} from 'react-native'; -import Icon from 'react-native-vector-icons/Feather'; -import { theme } from '../../../theme'; -import type { AIPredictionState } from '../types'; - -// ============================================================================ -// INTERFACES -// ============================================================================ - -interface FilterTabsProps { - selectedUrgencyFilter: AIPredictionState['selectedUrgencyFilter']; - selectedSeverityFilter: AIPredictionState['selectedSeverityFilter']; - selectedCategoryFilter: AIPredictionState['selectedCategoryFilter']; - onUrgencyFilterChange: (filter: AIPredictionState['selectedUrgencyFilter']) => void; - onSeverityFilterChange: (filter: AIPredictionState['selectedSeverityFilter']) => void; - onCategoryFilterChange: (filter: AIPredictionState['selectedCategoryFilter']) => void; - onClearFilters: () => void; - filterCounts?: { - urgency: Record; - severity: Record; - category: Record; - }; - activeFiltersCount?: number; -} - -interface FilterOption { - label: string; - value: string; - count?: number; - color?: string; -} - -// ============================================================================ -// CONSTANTS -// ============================================================================ - -const { width } = Dimensions.get('window'); - -const URGENCY_FILTERS: FilterOption[] = [ - { label: 'All', value: 'all' }, - { label: 'Emergency', value: 'emergency', color: '#F44336' }, - { label: 'Urgent', value: 'urgent', color: '#FF5722' }, - { label: 'Moderate', value: 'moderate', color: '#FF9800' }, - { label: 'Low', value: 'low', color: '#FFC107' }, - { label: 'Routine', value: 'routine', color: '#4CAF50' }, -]; - -const SEVERITY_FILTERS: FilterOption[] = [ - { label: 'All', value: 'all' }, - { label: 'High', value: 'high', color: '#F44336' }, - { label: 'Medium', value: 'medium', color: '#FF9800' }, - { label: 'Low', value: 'low', color: '#FFC107' }, - { label: 'None', value: 'none', color: '#4CAF50' }, -]; - -const CATEGORY_FILTERS: FilterOption[] = [ - { label: 'All', value: 'all' }, - { label: 'Critical', value: 'critical', color: '#F44336' }, - { label: 'Abnormal', value: 'abnormal', color: '#FF9800' }, - { label: 'Warning', value: 'warning', color: '#FFC107' }, - { label: 'Normal', value: 'normal', color: '#4CAF50' }, - { label: 'Unknown', value: 'unknown', color: '#9E9E9E' }, -]; - -// ============================================================================ -// FILTER TABS COMPONENT -// ============================================================================ - -/** - * FilterTabs Component - * - * Purpose: Provide filtering functionality for AI predictions - * - * Features: - * - Multiple filter categories (urgency, severity, category) - * - Visual filter counts - * - Active filter indicators - * - Clear all filters functionality - * - Color-coded filter options - * - Horizontal scroll support - * - Responsive design - * - Accessibility support - */ -const FilterTabs: React.FC = ({ - selectedUrgencyFilter, - selectedSeverityFilter, - selectedCategoryFilter, - onUrgencyFilterChange, - onSeverityFilterChange, - onCategoryFilterChange, - onClearFilters, - filterCounts, - activeFiltersCount = 0, -}) => { - // ============================================================================ - // HELPER FUNCTIONS - // ============================================================================ - - /** - * Get Filter Count - * - * Purpose: Get count for specific filter value - */ - const getFilterCount = (category: 'urgency' | 'severity' | 'category', value: string): number => { - return filterCounts?.[category]?.[value] || 0; - }; - - /** - * Render Filter Tab - * - * Purpose: Render individual filter tab - */ - const renderFilterTab = ( - option: FilterOption, - isSelected: boolean, - onPress: () => void, - category: 'urgency' | 'severity' | 'category' - ) => { - const count = getFilterCount(category, option.value); - - return ( - 0 ? `, ${count} items` : ''}`} - > - {option.color && isSelected && ( - - )} - - - {option.label} - - - {count > 0 && ( - - - {count} - - - )} - - ); - }; - - // ============================================================================ - // RENDER - // ============================================================================ - - return ( - - {/* Header with Clear Filters */} - - Filters - - {activeFiltersCount > 0 && ( - - - Clear All - - )} - - - {/* Urgency Filters */} - - Clinical Urgency - - {URGENCY_FILTERS.map((option) => - renderFilterTab( - { ...option, count: getFilterCount('urgency', option.value) }, - selectedUrgencyFilter === option.value, - () => onUrgencyFilterChange(option.value as AIPredictionState['selectedUrgencyFilter']), - 'urgency' - ) - )} - - - - {/* Severity Filters */} - - Primary Severity - - {SEVERITY_FILTERS.map((option) => - renderFilterTab( - { ...option, count: getFilterCount('severity', option.value) }, - selectedSeverityFilter === option.value, - () => onSeverityFilterChange(option.value as AIPredictionState['selectedSeverityFilter']), - 'severity' - ) - )} - - - - {/* Category Filters */} - - Finding Category - - {CATEGORY_FILTERS.map((option) => - renderFilterTab( - { ...option, count: getFilterCount('category', option.value) }, - selectedCategoryFilter === option.value, - () => onCategoryFilterChange(option.value as AIPredictionState['selectedCategoryFilter']), - 'category' - ) - )} - - - - ); -}; - -// ============================================================================ -// STYLES -// ============================================================================ - -const styles = StyleSheet.create({ - container: { - backgroundColor: theme.colors.background, - paddingVertical: theme.spacing.md, - }, - header: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: theme.spacing.md, - marginBottom: theme.spacing.md, - }, - headerTitle: { - fontSize: theme.typography.fontSize.bodyLarge, - fontWeight: theme.typography.fontWeight.bold, - color: theme.colors.textPrimary, - }, - clearButton: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.xs, - paddingHorizontal: theme.spacing.sm, - paddingVertical: theme.spacing.xs, - borderRadius: theme.borderRadius.small, - borderWidth: 1, - borderColor: theme.colors.primary, - }, - clearButtonText: { - fontSize: theme.typography.fontSize.bodySmall, - color: theme.colors.primary, - fontWeight: theme.typography.fontWeight.medium, - }, - filterSection: { - marginBottom: theme.spacing.lg, - }, - sectionTitle: { - fontSize: theme.typography.fontSize.bodyMedium, - fontWeight: theme.typography.fontWeight.medium, - color: theme.colors.textSecondary, - paddingHorizontal: theme.spacing.md, - marginBottom: theme.spacing.sm, - }, - filterRow: { - paddingHorizontal: theme.spacing.md, - gap: theme.spacing.sm, - }, - filterTab: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - borderRadius: theme.borderRadius.medium, - borderWidth: 1, - borderColor: theme.colors.border, - backgroundColor: theme.colors.background, - gap: theme.spacing.xs, - }, - selectedFilterTab: { - borderColor: theme.colors.primary, - backgroundColor: theme.colors.backgroundAccent, - }, - colorIndicator: { - width: 8, - height: 8, - borderRadius: 4, - }, - filterTabText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textSecondary, - fontWeight: theme.typography.fontWeight.medium, - }, - selectedFilterTabText: { - color: theme.colors.primary, - fontWeight: theme.typography.fontWeight.bold, - }, - countBadge: { - backgroundColor: theme.colors.textMuted, - borderRadius: theme.borderRadius.small, - paddingHorizontal: theme.spacing.xs, - paddingVertical: 2, - minWidth: 20, - alignItems: 'center', - }, - selectedCountBadge: { - backgroundColor: theme.colors.primary, - }, - countText: { - fontSize: theme.typography.fontSize.caption, - color: theme.colors.background, - fontWeight: theme.typography.fontWeight.bold, - }, - selectedCountText: { - color: theme.colors.background, - }, -}); - -export default FilterTabs; - -/* - * End of File: FilterTabs.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/components/LoadingState.tsx b/app/modules/AIPrediction/components/LoadingState.tsx deleted file mode 100644 index 4331d7c..0000000 --- a/app/modules/AIPrediction/components/LoadingState.tsx +++ /dev/null @@ -1,139 +0,0 @@ -/* - * File: LoadingState.tsx - * Description: Loading state component for AI predictions - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React from 'react'; -import { - View, - Text, - StyleSheet, - ActivityIndicator, - Dimensions, -} from 'react-native'; -import { theme } from '../../../theme'; - -// ============================================================================ -// INTERFACES -// ============================================================================ - -interface LoadingStateProps { - message?: string; - showSpinner?: boolean; - size?: 'small' | 'large'; - style?: any; -} - -// ============================================================================ -// CONSTANTS -// ============================================================================ - -const { width, height } = Dimensions.get('window'); - -// ============================================================================ -// LOADING STATE COMPONENT -// ============================================================================ - -/** - * LoadingState Component - * - * Purpose: Display loading state for AI predictions - * - * Features: - * - Customizable loading message - * - Optional spinner display - * - Different spinner sizes - * - Custom styling support - * - Centered layout - * - Accessibility support - */ -const LoadingState: React.FC = ({ - message = 'Loading AI predictions...', - showSpinner = true, - size = 'large', - style, -}) => { - // ============================================================================ - // RENDER - // ============================================================================ - - return ( - - {/* Loading Spinner */} - {showSpinner && ( - - )} - - {/* Loading Message */} - - {message} - - - {/* Loading Animation Dots */} - - - - - - - ); -}; - -// ============================================================================ -// STYLES -// ============================================================================ - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - paddingHorizontal: theme.spacing.xl, - paddingVertical: theme.spacing.xxl, - minHeight: height * 0.3, - }, - spinner: { - marginBottom: theme.spacing.lg, - }, - message: { - fontSize: theme.typography.fontSize.bodyLarge, - color: theme.colors.textSecondary, - textAlign: 'center', - fontWeight: theme.typography.fontWeight.medium, - marginBottom: theme.spacing.xl, - }, - dotsContainer: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.sm, - }, - dot: { - width: 8, - height: 8, - borderRadius: 4, - backgroundColor: theme.colors.primary, - }, - dot1: { - opacity: 0.3, - }, - dot2: { - opacity: 0.6, - }, - dot3: { - opacity: 1, - }, -}); - -export default LoadingState; - -/* - * End of File: LoadingState.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/components/SearchBar.tsx b/app/modules/AIPrediction/components/SearchBar.tsx deleted file mode 100644 index 46af82a..0000000 --- a/app/modules/AIPrediction/components/SearchBar.tsx +++ /dev/null @@ -1,226 +0,0 @@ -/* - * File: SearchBar.tsx - * Description: Search bar component for filtering AI predictions - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React, { useState, useCallback } from 'react'; -import { - View, - TextInput, - StyleSheet, - TouchableOpacity, - Dimensions, -} from 'react-native'; -import Icon from 'react-native-vector-icons/Feather'; -import { theme } from '../../../theme'; - -// ============================================================================ -// INTERFACES -// ============================================================================ - -interface SearchBarProps { - value: string; - onChangeText: (text: string) => void; - onClear?: () => void; - placeholder?: string; - autoFocus?: boolean; - disabled?: boolean; -} - -// ============================================================================ -// CONSTANTS -// ============================================================================ - -const { width } = Dimensions.get('window'); - -// ============================================================================ -// SEARCH BAR COMPONENT -// ============================================================================ - -/** - * SearchBar Component - * - * Purpose: Provide search functionality for AI predictions - * - * Features: - * - Real-time search input - * - Clear button functionality - * - Customizable placeholder text - * - Auto-focus support - * - Disabled state support - * - Modern design with icons - * - Responsive width - * - Accessibility support - */ -const SearchBar: React.FC = ({ - value, - onChangeText, - onClear, - placeholder = 'Search predictions...', - autoFocus = false, - disabled = false, -}) => { - // ============================================================================ - // STATE - // ============================================================================ - - const [isFocused, setIsFocused] = useState(false); - - // ============================================================================ - // EVENT HANDLERS - // ============================================================================ - - /** - * Handle Focus - * - * Purpose: Handle input focus state - */ - const handleFocus = useCallback(() => { - setIsFocused(true); - }, []); - - /** - * Handle Blur - * - * Purpose: Handle input blur state - */ - const handleBlur = useCallback(() => { - setIsFocused(false); - }, []); - - /** - * Handle Clear - * - * Purpose: Clear search input - */ - const handleClear = useCallback(() => { - onChangeText(''); - if (onClear) { - onClear(); - } - }, [onChangeText, onClear]); - - /** - * Handle Text Change - * - * Purpose: Handle search text input - */ - const handleTextChange = useCallback((text: string) => { - onChangeText(text); - }, [onChangeText]); - - // ============================================================================ - // RENDER - // ============================================================================ - - return ( - - {/* Search Icon */} - - - {/* Text Input */} - - - {/* Clear Button */} - {value.length > 0 && !disabled && ( - - - - )} - - ); -}; - -// ============================================================================ -// STYLES -// ============================================================================ - -const styles = StyleSheet.create({ - container: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: theme.colors.background, - borderWidth: 1, - borderColor: theme.colors.border, - borderRadius: theme.borderRadius.medium, - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - marginHorizontal: theme.spacing.md, - marginVertical: theme.spacing.sm, - ...theme.shadows.small, - }, - focusedContainer: { - borderColor: theme.colors.primary, - backgroundColor: theme.colors.background, - }, - disabledContainer: { - backgroundColor: theme.colors.backgroundAlt, - opacity: 0.6, - }, - searchIcon: { - marginRight: theme.spacing.sm, - }, - input: { - flex: 1, - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textPrimary, - paddingVertical: 0, // Remove default padding to maintain consistent height - fontFamily: theme.typography.fontFamily.regular, - }, - disabledInput: { - color: theme.colors.textMuted, - }, - clearButton: { - marginLeft: theme.spacing.sm, - padding: theme.spacing.xs, - }, -}); - -export default SearchBar; - -/* - * End of File: SearchBar.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/components/StatsOverview.tsx b/app/modules/AIPrediction/components/StatsOverview.tsx deleted file mode 100644 index 17a4e03..0000000 --- a/app/modules/AIPrediction/components/StatsOverview.tsx +++ /dev/null @@ -1,454 +0,0 @@ -/* - * File: StatsOverview.tsx - * Description: Statistics overview component for AI predictions dashboard - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React from 'react'; -import { - View, - Text, - StyleSheet, - TouchableOpacity, - Dimensions, -} from 'react-native'; -import Icon from 'react-native-vector-icons/Feather'; -import { theme } from '../../../theme'; -import type { AIPredictionStats } from '../types'; - -// ============================================================================ -// INTERFACES -// ============================================================================ - -interface StatsOverviewProps { - stats: AIPredictionStats; - onStatsPress?: (statType: string) => void; - isLoading?: boolean; - style?: any; -} - -interface StatCardProps { - title: string; - value: string | number; - subtitle?: string; - iconName: string; - color: string; - onPress?: () => void; - trend?: number; - isPercentage?: boolean; -} - -// ============================================================================ -// CONSTANTS -// ============================================================================ - -const { width } = Dimensions.get('window'); -const CARD_WIDTH = (width - 48) / 2; // Two cards per row with margins - -// ============================================================================ -// STAT CARD COMPONENT -// ============================================================================ - -/** - * StatCard Component - * - * Purpose: Individual statistics card - */ -const StatCard: React.FC = ({ - title, - value, - subtitle, - iconName, - color, - onPress, - trend, - isPercentage = false, -}) => { - const displayValue = typeof value === 'number' - ? isPercentage - ? `${Math.round(value * 100)}%` - : value.toLocaleString() - : value; - - return ( - - {/* Card Header */} - - - - - - {displayValue} - - - - {trend !== undefined && ( - - = 0 ? 'trending-up' : 'trending-down'} - size={14} - color={trend >= 0 ? theme.colors.success : theme.colors.error} - /> - = 0 ? theme.colors.success : theme.colors.error } - ]}> - {Math.abs(trend).toFixed(1)}% - - - )} - - - {/* Card Content */} - - - {title} - {subtitle && ( - {subtitle} - )} - - - ); -}; - -// ============================================================================ -// STATS OVERVIEW COMPONENT -// ============================================================================ - -/** - * StatsOverview Component - * - * Purpose: Display comprehensive AI predictions statistics - * - * Features: - * - Total cases overview - * - Critical and urgent case counts - * - Review progress tracking - * - Average confidence metrics - * - Trend indicators - * - Interactive stat cards - * - Responsive grid layout - * - Modern card design - * - Accessibility support - */ -const StatsOverview: React.FC = ({ - stats, - onStatsPress, - isLoading = false, - style, -}) => { - // ============================================================================ - // EVENT HANDLERS - // ============================================================================ - - /** - * Handle Stat Press - * - * Purpose: Handle statistics card press - */ - const handleStatPress = (statType: string) => { - if (onStatsPress) { - onStatsPress(statType); - } - }; - - // ============================================================================ - // RENDER - // ============================================================================ - - if (isLoading) { - return ( - - - AI Predictions Overview - - - Loading statistics... - - - ); - } - - return ( - - {/* Section Header */} - - AI Predictions Overview - handleStatPress('all')} - accessibilityRole="button" - accessibilityLabel="View all statistics" - > - View All - - - - - {/* Statistics Grid */} - - {/* Total Cases */} - handleStatPress('total')} - /> - - {/* Critical Cases */} - handleStatPress('critical')} - /> - - {/* Urgent Cases */} - handleStatPress('urgent')} - /> - - {/* Reviewed Cases */} - handleStatPress('reviewed')} - /> - - {/* Pending Cases */} - handleStatPress('pending')} - /> - - {/* Average Confidence */} - handleStatPress('confidence')} - isPercentage={true} - /> - - {/* Today's Cases */} - handleStatPress('today')} - /> - - {/* Weekly Trend */} - = 0 ? '+' : ''}${stats.weeklyTrend.toFixed(1)}%`} - subtitle="vs last week" - iconName={stats.weeklyTrend >= 0 ? 'trending-up' : 'trending-down'} - color={stats.weeklyTrend >= 0 ? theme.colors.success : theme.colors.error} - onPress={() => handleStatPress('trend')} - trend={stats.weeklyTrend} - /> - - - {/* Summary Section */} - - - - - Quick Insights - - - - - Review Progress: - - {Math.round((stats.reviewedCases / stats.totalCases) * 100)}% - - - - - Critical Rate: - - {Math.round((stats.criticalCases / stats.totalCases) * 100)}% - - - - - Daily Average: - - {Math.round(stats.totalCases / 7)} cases - - - - - - - ); -}; - -// ============================================================================ -// STYLES -// ============================================================================ - -const styles = StyleSheet.create({ - container: { - backgroundColor: theme.colors.background, - paddingVertical: theme.spacing.lg, - }, - header: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: theme.spacing.md, - marginBottom: theme.spacing.lg, - }, - sectionTitle: { - fontSize: theme.typography.fontSize.displaySmall, - fontWeight: theme.typography.fontWeight.bold, - color: theme.colors.textPrimary, - }, - viewAllButton: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.xs, - }, - viewAllText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.primary, - fontWeight: theme.typography.fontWeight.medium, - }, - loadingContainer: { - paddingVertical: theme.spacing.xxl, - alignItems: 'center', - }, - loadingText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textMuted, - }, - statsGrid: { - flexDirection: 'row', - flexWrap: 'wrap', - paddingHorizontal: theme.spacing.md, - gap: theme.spacing.md, - }, - statCard: { - width: CARD_WIDTH, - backgroundColor: theme.colors.background, - borderRadius: theme.borderRadius.medium, - borderLeftWidth: 4, - padding: theme.spacing.md, - ...theme.shadows.medium, - }, - cardHeader: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - marginBottom: theme.spacing.sm, - }, - iconContainer: { - width: 36, - height: 36, - borderRadius: 18, - justifyContent: 'center', - alignItems: 'center', - }, - trendContainer: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.xs, - }, - trendText: { - fontSize: theme.typography.fontSize.caption, - fontWeight: theme.typography.fontWeight.medium, - }, - cardContent: { - gap: theme.spacing.xs, - }, - statValue: { - fontSize: theme.typography.fontSize.displayMedium, - fontWeight: theme.typography.fontWeight.bold, - color: theme.colors.textPrimary, - }, - statTitle: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textSecondary, - fontWeight: theme.typography.fontWeight.medium, - }, - statSubtitle: { - fontSize: theme.typography.fontSize.bodySmall, - color: theme.colors.textMuted, - }, - summarySection: { - paddingHorizontal: theme.spacing.md, - marginTop: theme.spacing.lg, - }, - summaryCard: { - backgroundColor: theme.colors.background, - borderRadius: theme.borderRadius.medium, - padding: theme.spacing.lg, - ...theme.shadows.small, - }, - summaryHeader: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.sm, - marginBottom: theme.spacing.md, - }, - summaryTitle: { - fontSize: theme.typography.fontSize.bodyLarge, - fontWeight: theme.typography.fontWeight.bold, - color: theme.colors.textPrimary, - }, - summaryContent: { - gap: theme.spacing.sm, - }, - summaryItem: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - summaryLabel: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textSecondary, - }, - summaryValue: { - fontSize: theme.typography.fontSize.bodyMedium, - fontWeight: theme.typography.fontWeight.bold, - color: theme.colors.textPrimary, - }, -}); - -export default StatsOverview; - -/* - * End of File: StatsOverview.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/components/index.ts b/app/modules/AIPrediction/components/index.ts deleted file mode 100644 index dbc9ef2..0000000 --- a/app/modules/AIPrediction/components/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * File: index.ts - * Description: Components exports for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -export { default as AIPredictionCard } from './AIPredictionCard'; -export { default as SearchBar } from './SearchBar'; -export { default as FilterTabs } from './FilterTabs'; -export { default as LoadingState } from './LoadingState'; -export { default as EmptyState } from './EmptyState'; -export { default as StatsOverview } from './StatsOverview'; - -/* - * End of File: index.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/hooks/index.ts b/app/modules/AIPrediction/hooks/index.ts deleted file mode 100644 index 592bfb7..0000000 --- a/app/modules/AIPrediction/hooks/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * File: index.ts - * Description: Hooks exports for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -export * from './useAIPredictions'; - -/* - * End of File: index.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/hooks/useAIPredictions.ts b/app/modules/AIPrediction/hooks/useAIPredictions.ts deleted file mode 100644 index 613b0b4..0000000 --- a/app/modules/AIPrediction/hooks/useAIPredictions.ts +++ /dev/null @@ -1,383 +0,0 @@ -/* - * File: useAIPredictions.ts - * Description: Custom hook for AI Predictions functionality - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import { useCallback, useEffect, useMemo } from 'react'; -import { useAppDispatch, useAppSelector } from '../../../store/hooks'; - -// Import Redux actions and selectors -import { - fetchAIPredictions, - setSearchQuery, - setUrgencyFilter, - setSeverityFilter, - setCategoryFilter, - clearAllFilters, - updateCaseReview, -} from '../redux'; - -import { - selectPaginatedCases, - selectIsLoading, - selectError, - selectSearchQuery, - selectUrgencyFilter, - selectSeverityFilter, - selectCategoryFilter, - selectCasesStatistics, - selectActiveFiltersCount, - selectCurrentPage, - selectTotalPages, -} from '../redux'; - -// Import auth selector -import { selectUser } from '../../Auth/redux/authSelectors'; - -// Import types -import type { AIPredictionState } from '../types'; - -// ============================================================================ -// INTERFACES -// ============================================================================ - -interface UseAIPredictionsOptions { - autoLoad?: boolean; - refreshInterval?: number; -} - -interface UseAIPredictionsReturn { - // Data - cases: ReturnType; - statistics: ReturnType; - - // Loading states - isLoading: boolean; - error: string | null; - - // Filters - searchQuery: string; - urgencyFilter: AIPredictionState['selectedUrgencyFilter']; - severityFilter: AIPredictionState['selectedSeverityFilter']; - categoryFilter: AIPredictionState['selectedCategoryFilter']; - activeFiltersCount: number; - - // Pagination - currentPage: number; - totalPages: number; - - // Actions - loadPredictions: () => Promise; - refreshPredictions: () => Promise; - setSearch: (query: string) => void; - setUrgency: (filter: AIPredictionState['selectedUrgencyFilter']) => void; - setSeverity: (filter: AIPredictionState['selectedSeverityFilter']) => void; - setCategory: (filter: AIPredictionState['selectedCategoryFilter']) => void; - clearFilters: () => void; - reviewCase: (caseId: string, reviewData?: Partial<{ - review_status: 'pending' | 'reviewed' | 'confirmed' | 'disputed'; - reviewed_by: string; - review_notes: string; - priority: 'critical' | 'high' | 'medium' | 'low'; - }>) => Promise; - - // Computed properties - hasFilters: boolean; - isEmpty: boolean; - hasError: boolean; -} - -// ============================================================================ -// USE AI PREDICTIONS HOOK -// ============================================================================ - -/** - * useAIPredictions Hook - * - * Purpose: Custom hook for managing AI predictions state and actions - * - * Features: - * - Automatic data loading on mount - * - Search and filtering functionality - * - Case review management - * - Error handling - * - Loading states - * - Computed properties for UI state - * - Auto-refresh capability - * - Type-safe actions and selectors - */ -export const useAIPredictions = (options: UseAIPredictionsOptions = {}): UseAIPredictionsReturn => { - const { - autoLoad = true, - refreshInterval, - } = options; - - // ============================================================================ - // REDUX STATE - // ============================================================================ - - const dispatch = useAppDispatch(); - - // Auth state - const user = useAppSelector(selectUser); - - // AI Predictions state - const cases = useAppSelector(selectPaginatedCases); - const statistics = useAppSelector(selectCasesStatistics); - const isLoading = useAppSelector(selectIsLoading); - const error = useAppSelector(selectError); - const searchQuery = useAppSelector(selectSearchQuery); - const urgencyFilter = useAppSelector(selectUrgencyFilter); - const severityFilter = useAppSelector(selectSeverityFilter); - const categoryFilter = useAppSelector(selectCategoryFilter); - const activeFiltersCount = useAppSelector(selectActiveFiltersCount); - const currentPage = useAppSelector(selectCurrentPage); - const totalPages = useAppSelector(selectTotalPages); - - // ============================================================================ - // MEMOIZED VALUES - // ============================================================================ - - /** - * Has Filters - * - * Purpose: Check if any filters are active - */ - const hasFilters = useMemo(() => activeFiltersCount > 0, [activeFiltersCount]); - - /** - * Is Empty - * - * Purpose: Check if the cases list is empty - */ - const isEmpty = useMemo(() => cases.length === 0, [cases.length]); - - /** - * Has Error - * - * Purpose: Check if there's an error - */ - const hasError = useMemo(() => error !== null, [error]); - - // ============================================================================ - // ACTIONS - // ============================================================================ - - /** - * Load Predictions - * - * Purpose: Load AI predictions from API - */ - const loadPredictions = useCallback(async () => { - - if (!user?.access_token) { - throw new Error('User not authenticated'); - } - - try { - const params = { - page: currentPage, - limit: 20, - ...(urgencyFilter !== 'all' && { urgency: urgencyFilter }), - ...(severityFilter !== 'all' && { severity: severityFilter }), - ...(categoryFilter !== 'all' && { category: categoryFilter }), - ...(searchQuery.trim() && { search: searchQuery.trim() }), - }; - - await dispatch(fetchAIPredictions({ - token: user.access_token, - params, - })).unwrap(); - } catch (error) { - console.error('Failed to load AI predictions:', error); - throw error; - } - }, [ - dispatch, - user?.access_token, - currentPage, - urgencyFilter, - severityFilter, - categoryFilter, - searchQuery, - ]); - - /** - * Refresh Predictions - * - * Purpose: Refresh AI predictions data - */ - const refreshPredictions = useCallback(async () => { - await loadPredictions(); - }, [loadPredictions]); - - /** - * Set Search - * - * Purpose: Set search query - */ - const setSearch = useCallback((query: string) => { - dispatch(setSearchQuery(query)); - }, [dispatch]); - - /** - * Set Urgency Filter - * - * Purpose: Set urgency filter - */ - const setUrgency = useCallback((filter: AIPredictionState['selectedUrgencyFilter']) => { - dispatch(setUrgencyFilter(filter)); - }, [dispatch]); - - /** - * Set Severity Filter - * - * Purpose: Set severity filter - */ - const setSeverity = useCallback((filter: AIPredictionState['selectedSeverityFilter']) => { - dispatch(setSeverityFilter(filter)); - }, [dispatch]); - - /** - * Set Category Filter - * - * Purpose: Set category filter - */ - const setCategory = useCallback((filter: AIPredictionState['selectedCategoryFilter']) => { - dispatch(setCategoryFilter(filter)); - }, [dispatch]); - - /** - * Clear Filters - * - * Purpose: Clear all active filters - */ - const clearFilters = useCallback(() => { - dispatch(clearAllFilters()); - }, [dispatch]); - - /** - * Review Case - * - * Purpose: Update case review status - */ - const reviewCase = useCallback(async ( - caseId: string, - reviewData: Partial<{ - review_status: 'pending' | 'reviewed' | 'confirmed' | 'disputed'; - reviewed_by: string; - review_notes: string; - priority: 'critical' | 'high' | 'medium' | 'low'; - }> = {} - ) => { - if (!user?.access_token) { - throw new Error('User not authenticated'); - } - - try { - const defaultReviewData = { - review_status: 'reviewed' as const, - reviewed_by: user.display_name || user.email || 'Current User', - ...reviewData, - }; - - await dispatch(updateCaseReview({ - caseId, - reviewData: defaultReviewData, - token: user.access_token, - })).unwrap(); - } catch (error) { - console.error('Failed to review case:', error); - throw error; - } - }, [dispatch, user]); - - // ============================================================================ - // EFFECTS - // ============================================================================ - - /** - * Auto-load Effect - * - * Purpose: Automatically load predictions on mount if enabled - */ - useEffect(() => { - if (autoLoad && user?.access_token) { - loadPredictions().catch(console.error); - } - }, [autoLoad, user?.access_token, loadPredictions]); - - /** - * Auto-refresh Effect - * - * Purpose: Set up auto-refresh interval if specified - */ - useEffect(() => { - if (!refreshInterval || !user?.access_token) return; - - const interval = setInterval(() => { - loadPredictions().catch(console.error); - }, refreshInterval); - - return () => clearInterval(interval); - }, [refreshInterval, user?.access_token, loadPredictions]); - - /** - * Filter Change Effect - * - * Purpose: Reload data when filters change - */ - useEffect(() => { - if (user?.access_token) { - loadPredictions().catch(console.error); - } - }, [urgencyFilter, severityFilter, categoryFilter, searchQuery, currentPage]); - - // ============================================================================ - // RETURN - // ============================================================================ - - return { - // Data - cases, - statistics, - - // Loading states - isLoading, - error, - - // Filters - searchQuery, - urgencyFilter, - severityFilter, - categoryFilter, - activeFiltersCount, - - // Pagination - currentPage, - totalPages, - - // Actions - loadPredictions, - refreshPredictions, - setSearch, - setUrgency, - setSeverity, - setCategory, - clearFilters, - reviewCase, - - // Computed properties - hasFilters, - isEmpty, - hasError, - }; -}; - -/* - * End of File: useAIPredictions.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/index.ts b/app/modules/AIPrediction/index.ts deleted file mode 100644 index 920e1d5..0000000 --- a/app/modules/AIPrediction/index.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * File: index.ts - * Description: Main exports for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -// ============================================================================ -// COMPONENT EXPORTS -// ============================================================================ - -export * from './components'; - -// ============================================================================ -// SCREEN EXPORTS -// ============================================================================ - -export * from './screens'; - -// ============================================================================ -// NAVIGATION EXPORTS -// ============================================================================ - -export * from './navigation'; - -// ============================================================================ -// REDUX EXPORTS -// ============================================================================ - -export * from './redux'; - -// ============================================================================ -// SERVICE EXPORTS -// ============================================================================ - -export * from './services'; - -// ============================================================================ -// TYPE EXPORTS -// ============================================================================ - -export * from './types'; - -// ============================================================================ -// HOOK EXPORTS -// ============================================================================ - -export * from './hooks'; - -/* - * End of File: index.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/navigation/AIPredictionStackNavigator.tsx b/app/modules/AIPrediction/navigation/AIPredictionStackNavigator.tsx deleted file mode 100644 index c5c070f..0000000 --- a/app/modules/AIPrediction/navigation/AIPredictionStackNavigator.tsx +++ /dev/null @@ -1,249 +0,0 @@ -/* - * File: AIPredictionStackNavigator.tsx - * Description: Stack navigator for AI Prediction module screens - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React from 'react'; -import { createStackNavigator } from '@react-navigation/stack'; -import { TouchableOpacity, Text, StyleSheet } from 'react-native'; -import Icon from 'react-native-vector-icons/Feather'; -import { theme } from '../../../theme'; - -// Import screens -import { AIPredictionsScreen, AIPredictionDetailScreen } from '../screens'; -import { ComingSoonScreen, DicomViewer } from '../../../shared/components'; - -// Import types -import type { AIPredictionStackParamList } from './navigationTypes'; - -// ============================================================================ -// STACK NAVIGATOR SETUP -// ============================================================================ - -const Stack = createStackNavigator(); - -// ============================================================================ -// HEADER COMPONENTS -// ============================================================================ - -/** - * Header Back Button - * - * Purpose: Custom back button for navigation header - */ -const HeaderBackButton: React.FC<{ onPress: () => void }> = ({ onPress }) => ( - - - -); - -/** - * Header Action Button - * - * Purpose: Custom action button for navigation header - */ -const HeaderActionButton: React.FC<{ - iconName: string; - onPress: () => void; - accessibilityLabel?: string; -}> = ({ iconName, onPress, accessibilityLabel }) => ( - - - -); - -// ============================================================================ -// SCREEN OPTIONS -// ============================================================================ - -/** - * Default Screen Options - * - * Purpose: Common screen options for all AI prediction screens - */ -const defaultScreenOptions = { - headerStyle: { - backgroundColor: theme.colors.background, - elevation: 2, - shadowOpacity: 0.1, - shadowRadius: 4, - shadowOffset: { width: 0, height: 2 }, - borderBottomWidth: 1, - borderBottomColor: theme.colors.border, - }, - headerTitleStyle: { - fontSize: theme.typography.fontSize.bodyLarge, - fontWeight: theme.typography.fontWeight.bold, - color: theme.colors.textPrimary, - }, - headerTintColor: theme.colors.textPrimary, - headerBackTitleVisible: false, - gestureEnabled: true, - cardStyleInterpolator: ({ current, layouts }: any) => { - return { - cardStyle: { - transform: [ - { - translateX: current.progress.interpolate({ - inputRange: [0, 1], - outputRange: [layouts.screen.width, 0], - }), - }, - ], - }, - }; - }, -}; - -// ============================================================================ -// AI PREDICTION STACK NAVIGATOR COMPONENT -// ============================================================================ - -/** - * AIPredictionStackNavigator Component - * - * Purpose: Stack navigator for AI prediction module - * - * Features: - * - AI Prediction List screen (main screen) - * - AI Prediction Details screen (case details) - * - AI Prediction Filters screen (advanced filtering) - * - AI Prediction Stats screen (detailed statistics) - * - Custom header styling and buttons - * - Smooth navigation transitions - * - Accessibility support - * - Coming soon screens for unimplemented features - */ -const AIPredictionStackNavigator: React.FC = () => { - return ( - - {/* AI Prediction List Screen */} - ({ - title: 'AI Predictions', - headerLeft: () => null, // No back button on main screen - headerRight: () => ( - { - // Open options menu - // For now, just navigate to stats - // @ts-ignore - navigation.navigate('AIPredictionStats'); - }} - accessibilityLabel="More options" - /> - ), - })} - /> - - {/* AI Prediction Details Screen */} - console.log('DICOM Error:', error)} - onLoad={() => console.log('DICOM Viewer loaded successfully')} - />} - options={({ navigation, route }) => ({ - title: 'Create Suggestion', - headerLeft: () => ( - navigation.goBack()} /> - ), - headerRight: () => ( - { - // Show help for suggestion form - console.log('Show help for case:', route.params?.caseId); - }} - accessibilityLabel="Help" - /> - ), - })} - /> - - {/* AI Prediction Filters Screen */} - ({ - title: 'Advanced Filters', - headerLeft: () => ( - navigation.goBack()} /> - ), - headerRight: () => ( - { - // Reset filters - console.log('Reset filters'); - }} - accessibilityLabel="Reset filters" - /> - ), - })} - /> - - {/* AI Prediction Stats Screen */} - ({ - title: 'Statistics', - headerLeft: () => ( - navigation.goBack()} /> - ), - headerRight: () => ( - { - // Export statistics - console.log('Export stats:', route.params?.timeRange); - }} - accessibilityLabel="Export statistics" - /> - ), - })} - /> - - ); -}; - -// ============================================================================ -// STYLES -// ============================================================================ - -const styles = StyleSheet.create({ - headerButton: { - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - marginHorizontal: theme.spacing.xs, - }, - headerButtonText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.primary, - fontWeight: theme.typography.fontWeight.medium, - }, -}); - -export default AIPredictionStackNavigator; - -/* - * End of File: AIPredictionStackNavigator.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/navigation/index.ts b/app/modules/AIPrediction/navigation/index.ts deleted file mode 100644 index 5094ddf..0000000 --- a/app/modules/AIPrediction/navigation/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * File: index.ts - * Description: Navigation exports for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -export { default as AIPredictionStackNavigator } from './AIPredictionStackNavigator'; -export * from './navigationTypes'; -export * from './navigationUtils'; - -/* - * End of File: index.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/navigation/navigationTypes.ts b/app/modules/AIPrediction/navigation/navigationTypes.ts deleted file mode 100644 index 0f8cf6a..0000000 --- a/app/modules/AIPrediction/navigation/navigationTypes.ts +++ /dev/null @@ -1,169 +0,0 @@ -/* - * File: navigationTypes.ts - * Description: Navigation type definitions for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import type { StackNavigationProp } from '@react-navigation/stack'; -import type { RouteProp } from '@react-navigation/native'; - -// ============================================================================ -// AI PREDICTION STACK PARAM LIST -// ============================================================================ - -/** - * AI Prediction Stack Param List - * - * Purpose: Define navigation parameters for AI prediction screens - * - * Screens: - * - AIPredictionList: Main list of AI predictions - * - AIPredictionDetails: Detailed view of a specific prediction with suggestion form - * - AIPredictionFilters: Advanced filtering options - * - AIPredictionStats: Detailed statistics view - */ -export type AIPredictionStackParamList = { - AIPredictionList: undefined; - AIPredictionDetails: { caseId: string }; - AIPredictionFilters: undefined; - AIPredictionStats: { timeRange?: 'today' | 'week' | 'month' }; -}; - -// ============================================================================ -// NAVIGATION PROP TYPES -// ============================================================================ - -/** - * AI Prediction List Navigation Prop - * - * Purpose: Navigation prop type for AI prediction list screen - */ -export type AIPredictionListNavigationProp = StackNavigationProp< - AIPredictionStackParamList, - 'AIPredictionList' ->; - -/** - * AI Prediction Details Navigation Prop - * - * Purpose: Navigation prop type for AI prediction details screen - */ -export type AIPredictionDetailsNavigationProp = StackNavigationProp< - AIPredictionStackParamList, - 'AIPredictionDetails' ->; - -/** - * AI Prediction Filters Navigation Prop - * - * Purpose: Navigation prop type for AI prediction filters screen - */ -export type AIPredictionFiltersNavigationProp = StackNavigationProp< - AIPredictionStackParamList, - 'AIPredictionFilters' ->; - -/** - * AI Prediction Stats Navigation Prop - * - * Purpose: Navigation prop type for AI prediction statistics screen - */ -export type AIPredictionStatsNavigationProp = StackNavigationProp< - AIPredictionStackParamList, - 'AIPredictionStats' ->; - -// ============================================================================ -// ROUTE PROP TYPES -// ============================================================================ - -/** - * AI Prediction List Route Prop - * - * Purpose: Route prop type for AI prediction list screen - */ -export type AIPredictionListRouteProp = RouteProp< - AIPredictionStackParamList, - 'AIPredictionList' ->; - -/** - * AI Prediction Details Route Prop - * - * Purpose: Route prop type for AI prediction details screen - */ -export type AIPredictionDetailsRouteProp = RouteProp< - AIPredictionStackParamList, - 'AIPredictionDetails' ->; - -/** - * AI Prediction Filters Route Prop - * - * Purpose: Route prop type for AI prediction filters screen - */ -export type AIPredictionFiltersRouteProp = RouteProp< - AIPredictionStackParamList, - 'AIPredictionFilters' ->; - -/** - * AI Prediction Stats Route Prop - * - * Purpose: Route prop type for AI prediction statistics screen - */ -export type AIPredictionStatsRouteProp = RouteProp< - AIPredictionStackParamList, - 'AIPredictionStats' ->; - -// ============================================================================ -// COMBINED PROP TYPES -// ============================================================================ - -/** - * AI Prediction List Screen Props - * - * Purpose: Combined props for AI prediction list screen - */ -export interface AIPredictionListScreenProps { - navigation: AIPredictionListNavigationProp; - route: AIPredictionListRouteProp; -} - -/** - * AI Prediction Details Screen Props - * - * Purpose: Combined props for AI prediction details screen - */ -export interface AIPredictionDetailsScreenProps { - navigation: AIPredictionDetailsNavigationProp; - route: AIPredictionDetailsRouteProp; -} - -/** - * AI Prediction Filters Screen Props - * - * Purpose: Combined props for AI prediction filters screen - */ -export interface AIPredictionFiltersScreenProps { - navigation: AIPredictionFiltersNavigationProp; - route: AIPredictionFiltersRouteProp; -} - -/** - * AI Prediction Stats Screen Props - * - * Purpose: Combined props for AI prediction statistics screen - */ -export interface AIPredictionStatsScreenProps { - navigation: AIPredictionStatsNavigationProp; - route: AIPredictionStatsRouteProp; -} - -/* - * End of File: navigationTypes.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/navigation/navigationUtils.ts b/app/modules/AIPrediction/navigation/navigationUtils.ts deleted file mode 100644 index 7dc12a3..0000000 --- a/app/modules/AIPrediction/navigation/navigationUtils.ts +++ /dev/null @@ -1,251 +0,0 @@ -/* - * File: navigationUtils.ts - * Description: Navigation utility functions for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import { CommonActions } from '@react-navigation/native'; -import type { AIPredictionStackParamList } from './navigationTypes'; - -// ============================================================================ -// NAVIGATION UTILITY FUNCTIONS -// ============================================================================ - -/** - * Navigate to AI Prediction Details - * - * Purpose: Navigate to AI prediction case details screen - * - * @param navigation - Navigation object - * @param caseId - AI prediction case ID - */ -export const navigateToAIPredictionDetails = ( - navigation: any, - caseId: string -) => { - navigation.navigate('AIPredictionDetails', { caseId }); -}; - -/** - * Navigate to AI Prediction Filters - * - * Purpose: Navigate to advanced filters screen - * - * @param navigation - Navigation object - */ -export const navigateToAIPredictionFilters = (navigation: any) => { - navigation.navigate('AIPredictionFilters'); -}; - -/** - * Navigate to AI Prediction Statistics - * - * Purpose: Navigate to detailed statistics screen - * - * @param navigation - Navigation object - * @param timeRange - Optional time range filter - */ -export const navigateToAIPredictionStats = ( - navigation: any, - timeRange?: 'today' | 'week' | 'month' -) => { - navigation.navigate('AIPredictionStats', { timeRange }); -}; - -/** - * Go Back to AI Prediction List - * - * Purpose: Navigate back to AI prediction list screen - * - * @param navigation - Navigation object - */ -export const goBackToAIPredictionList = (navigation: any) => { - navigation.navigate('AIPredictionList'); -}; - -/** - * Reset to AI Prediction List - * - * Purpose: Reset navigation stack to AI prediction list - * - * @param navigation - Navigation object - */ -export const resetToAIPredictionList = (navigation: any) => { - navigation.dispatch( - CommonActions.reset({ - index: 0, - routes: [{ name: 'AIPredictionList' }], - }) - ); -}; - -/** - * Can Go Back - * - * Purpose: Check if navigation can go back - * - * @param navigation - Navigation object - * @returns Boolean indicating if can go back - */ -export const canGoBack = (navigation: any): boolean => { - return navigation.canGoBack(); -}; - -/** - * Get Current Route Name - * - * Purpose: Get the current route name - * - * @param navigation - Navigation object - * @returns Current route name or undefined - */ -export const getCurrentRouteName = (navigation: any): string | undefined => { - return navigation.getCurrentRoute()?.name; -}; - -/** - * Get Current Route Params - * - * Purpose: Get the current route parameters - * - * @param navigation - Navigation object - * @returns Current route params or undefined - */ -export const getCurrentRouteParams = (navigation: any): any => { - return navigation.getCurrentRoute()?.params; -}; - -/** - * Navigate with Replace - * - * Purpose: Navigate to a screen by replacing the current one - * - * @param navigation - Navigation object - * @param routeName - Route name to navigate to - * @param params - Optional route parameters - */ -export const navigateWithReplace = ( - navigation: any, - routeName: keyof AIPredictionStackParamList, - params?: any -) => { - navigation.replace(routeName, params); -}; - -/** - * Navigate with Push - * - * Purpose: Navigate to a screen by pushing it onto the stack - * - * @param navigation - Navigation object - * @param routeName - Route name to navigate to - * @param params - Optional route parameters - */ -export const navigateWithPush = ( - navigation: any, - routeName: keyof AIPredictionStackParamList, - params?: any -) => { - navigation.push(routeName, params); -}; - -/** - * Pop Navigation Stack - * - * Purpose: Pop the specified number of screens from the stack - * - * @param navigation - Navigation object - * @param count - Number of screens to pop (default: 1) - */ -export const popNavigationStack = (navigation: any, count: number = 1) => { - navigation.pop(count); -}; - -/** - * Pop to Top - * - * Purpose: Pop to the top of the navigation stack - * - * @param navigation - Navigation object - */ -export const popToTop = (navigation: any) => { - navigation.popToTop(); -}; - -/** - * Set Navigation Params - * - * Purpose: Set parameters for the current screen - * - * @param navigation - Navigation object - * @param params - Parameters to set - */ -export const setNavigationParams = (navigation: any, params: any) => { - navigation.setParams(params); -}; - -/** - * Add Navigation Listener - * - * Purpose: Add a navigation event listener - * - * @param navigation - Navigation object - * @param eventName - Event name to listen for - * @param callback - Callback function - * @returns Unsubscribe function - */ -export const addNavigationListener = ( - navigation: any, - eventName: string, - callback: (e: any) => void -) => { - return navigation.addListener(eventName, callback); -}; - -/** - * Remove Navigation Listener - * - * Purpose: Remove a navigation event listener - * - * @param navigation - Navigation object - * @param eventName - Event name - * @param callback - Callback function - */ -export const removeNavigationListener = ( - navigation: any, - eventName: string, - callback: (e: any) => void -) => { - navigation.removeListener(eventName, callback); -}; - -/** - * Check if Screen is Focused - * - * Purpose: Check if the current screen is focused - * - * @param navigation - Navigation object - * @returns Boolean indicating if screen is focused - */ -export const isScreenFocused = (navigation: any): boolean => { - return navigation.isFocused(); -}; - -/** - * Get Navigation State - * - * Purpose: Get the current navigation state - * - * @param navigation - Navigation object - * @returns Navigation state - */ -export const getNavigationState = (navigation: any) => { - return navigation.getState(); -}; - -/* - * End of File: navigationUtils.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/redux/aiPredictionSelectors.ts b/app/modules/AIPrediction/redux/aiPredictionSelectors.ts deleted file mode 100644 index aa7f222..0000000 --- a/app/modules/AIPrediction/redux/aiPredictionSelectors.ts +++ /dev/null @@ -1,410 +0,0 @@ -/* - * File: aiPredictionSelectors.ts - * Description: Redux selectors for AI Prediction state - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import { createSelector } from '@reduxjs/toolkit'; -import type { RootState } from '../../../store'; -import { AIPredictionCase } from '../types'; - -// ============================================================================ -// BASE SELECTORS -// ============================================================================ - -/** - * Select AI Prediction State - * - * Purpose: Get the entire AI prediction state - */ -export const selectAIPredictionState = (state: RootState) => state.aiPrediction; - -/** - * Select Prediction Cases - * - * Purpose: Get all AI prediction cases - */ -export const selectPredictionCases = (state: RootState) => state.aiPrediction.predictionCases; - -/** - * Select Current Case - * - * Purpose: Get the currently selected AI prediction case - */ -export const selectCurrentCase = (state: RootState) => state.aiPrediction.currentCase; - -/** - * Select Loading State - * - * Purpose: Get the loading state for AI predictions - */ -export const selectIsLoading = (state: RootState) => state.aiPrediction.isLoading; - -/** - * Select Loading Case Details State - * - * Purpose: Get the loading state for case details - */ -export const selectIsLoadingCaseDetails = (state: RootState) => state.aiPrediction.isLoadingCaseDetails; - -/** - * Select Error - * - * Purpose: Get the current error message - */ -export const selectError = (state: RootState) => state.aiPrediction.error; - -/** - * Select Search Query - * - * Purpose: Get the current search query - */ -export const selectSearchQuery = (state: RootState) => state.aiPrediction.searchQuery; - -/** - * Select Filter States - * - * Purpose: Get all filter states - */ -export const selectUrgencyFilter = (state: RootState) => state.aiPrediction.selectedUrgencyFilter; -export const selectSeverityFilter = (state: RootState) => state.aiPrediction.selectedSeverityFilter; -export const selectCategoryFilter = (state: RootState) => state.aiPrediction.selectedCategoryFilter; - -/** - * Select Sort Options - * - * Purpose: Get current sort configuration - */ -export const selectSortBy = (state: RootState) => state.aiPrediction.sortBy; -export const selectSortOrder = (state: RootState) => state.aiPrediction.sortOrder; - -/** - * Select Pagination - * - * Purpose: Get pagination configuration - */ -export const selectCurrentPage = (state: RootState) => state.aiPrediction.currentPage; -export const selectItemsPerPage = (state: RootState) => state.aiPrediction.itemsPerPage; -export const selectTotalItems = (state: RootState) => state.aiPrediction.totalItems; - -/** - * Select UI State - * - * Purpose: Get UI state flags - */ -export const selectShowFilters = (state: RootState) => state.aiPrediction.showFilters; -export const selectSelectedCaseIds = (state: RootState) => state.aiPrediction.selectedCaseIds; - -// ============================================================================ -// COMPUTED SELECTORS -// ============================================================================ - -/** - * Select Filtered and Sorted Cases - * - * Purpose: Get AI prediction cases filtered and sorted based on current settings - */ -export const selectFilteredAndSortedCases = createSelector( - [ - selectPredictionCases, - selectSearchQuery, - selectUrgencyFilter, - selectSeverityFilter, - selectCategoryFilter, - selectSortBy, - selectSortOrder, - ], - (cases, searchQuery, urgencyFilter, severityFilter, categoryFilter, sortBy, sortOrder) => { - let filteredCases = [...cases]; - - // Apply search filter - if (searchQuery.trim()) { - const query = searchQuery.toLowerCase(); - filteredCases = filteredCases.filter(case_ => - case_.patid.toLowerCase().includes(query) || - case_.prediction.label.toLowerCase().includes(query) || - case_.prediction.anatomical_location.toLowerCase().includes(query) - ); - } - - // Apply urgency filter - if (urgencyFilter !== 'all') { - filteredCases = filteredCases.filter(case_ => - case_.prediction.clinical_urgency === urgencyFilter - ); - } - - // Apply severity filter - if (severityFilter !== 'all') { - filteredCases = filteredCases.filter(case_ => - case_.prediction.primary_severity === severityFilter - ); - } - - // Apply category filter - if (categoryFilter !== 'all') { - filteredCases = filteredCases.filter(case_ => - case_.prediction.finding_category === categoryFilter - ); - } - - // Apply sorting - filteredCases.sort((a, b) => { - let comparison = 0; - - switch (sortBy) { - case 'date': - comparison = new Date(a.created_at || '').getTime() - new Date(b.created_at || '').getTime(); - break; - case 'urgency': - const urgencyOrder = { emergency: 5, urgent: 4, moderate: 3, low: 2, routine: 1 }; - comparison = (urgencyOrder[a.prediction.clinical_urgency as keyof typeof urgencyOrder] || 0) - - (urgencyOrder[b.prediction.clinical_urgency as keyof typeof urgencyOrder] || 0); - break; - case 'confidence': - comparison = a.prediction.confidence_score - b.prediction.confidence_score; - break; - case 'severity': - const severityOrder = { high: 4, medium: 3, low: 2, none: 1 }; - comparison = (severityOrder[a.prediction.primary_severity as keyof typeof severityOrder] || 0) - - (severityOrder[b.prediction.primary_severity as keyof typeof severityOrder] || 0); - break; - default: - break; - } - - return sortOrder === 'desc' ? -comparison : comparison; - }); - - return filteredCases; - } -); - -/** - * Select Paginated Cases - * - * Purpose: Get the current page of filtered and sorted cases - */ -export const selectPaginatedCases = createSelector( - [selectFilteredAndSortedCases, selectCurrentPage, selectItemsPerPage], - (filteredCases, currentPage, itemsPerPage) => { - const startIndex = (currentPage - 1) * itemsPerPage; - const endIndex = startIndex + itemsPerPage; - return filteredCases.slice(startIndex, endIndex); - } -); - -/** - * Select Critical Cases - * - * Purpose: Get cases marked as critical or emergency - */ -export const selectCriticalCases = createSelector( - [selectPredictionCases], - (cases) => cases.filter(case_ => - case_.prediction.clinical_urgency === 'emergency' || - case_.prediction.clinical_urgency === 'urgent' || - case_.prediction.primary_severity === 'high' || - case_.priority === 'critical' - ) -); - -/** - * Select Pending Cases - * - * Purpose: Get cases pending review - */ -export const selectPendingCases = createSelector( - [selectPredictionCases], - (cases) => cases.filter(case_ => case_.review_status === 'pending') -); - -/** - * Select Reviewed Cases - * - * Purpose: Get cases that have been reviewed - */ -export const selectReviewedCases = createSelector( - [selectPredictionCases], - (cases) => cases.filter(case_ => - case_.review_status === 'reviewed' || - case_.review_status === 'confirmed' || - case_.review_status === 'disputed' - ) -); - -/** - * Select Cases by Urgency - * - * Purpose: Group cases by urgency level - */ -export const selectCasesByUrgency = createSelector( - [selectPredictionCases], - (cases) => { - const grouped = { - emergency: [] as AIPredictionCase[], - urgent: [] as AIPredictionCase[], - moderate: [] as AIPredictionCase[], - low: [] as AIPredictionCase[], - routine: [] as AIPredictionCase[], - }; - - cases.forEach(case_ => { - const urgency = case_.prediction.clinical_urgency as keyof typeof grouped; - if (grouped[urgency]) { - grouped[urgency].push(case_); - } - }); - - return grouped; - } -); - -/** - * Select Cases Statistics - * - * Purpose: Get statistical overview of cases - */ -export const selectCasesStatistics = createSelector( - [selectPredictionCases], - (cases) => { - const total = cases.length; - const critical = cases.filter(c => - c.prediction.clinical_urgency === 'emergency' || - c.prediction.clinical_urgency === 'urgent' - ).length; - const pending = cases.filter(c => c.review_status === 'pending').length; - const reviewed = cases.filter(c => - c.review_status === 'reviewed' || - c.review_status === 'confirmed' - ).length; - const averageConfidence = total > 0 - ? cases.reduce((sum, c) => sum + c.prediction.confidence_score, 0) / total - : 0; - - return { - total, - critical, - pending, - reviewed, - averageConfidence: Math.round(averageConfidence * 1000) / 1000, // Round to 3 decimal places - reviewProgress: total > 0 ? Math.round((reviewed / total) * 100) : 0, - }; - } -); - -/** - * Select Filter Counts - * - * Purpose: Get counts for each filter option - */ -export const selectFilterCounts = createSelector( - [selectPredictionCases], - (cases) => { - const urgencyCounts = { - all: cases.length, - emergency: 0, - urgent: 0, - moderate: 0, - low: 0, - routine: 0, - }; - - const severityCounts = { - all: cases.length, - high: 0, - medium: 0, - low: 0, - none: 0, - }; - - const categoryCounts = { - all: cases.length, - normal: 0, - abnormal: 0, - critical: 0, - warning: 0, - unknown: 0, - }; - - cases.forEach(case_ => { - // Count urgency - const urgency = case_.prediction.clinical_urgency as keyof typeof urgencyCounts; - if (urgencyCounts[urgency] !== undefined) { - urgencyCounts[urgency]++; - } - - // Count severity - const severity = case_.prediction.primary_severity as keyof typeof severityCounts; - if (severityCounts[severity] !== undefined) { - severityCounts[severity]++; - } - - // Count category - const category = case_.prediction.finding_category as keyof typeof categoryCounts; - if (categoryCounts[category] !== undefined) { - categoryCounts[category]++; - } - }); - - return { - urgency: urgencyCounts, - severity: severityCounts, - category: categoryCounts, - }; - } -); - -/** - * Select Total Pages - * - * Purpose: Calculate total number of pages based on filtered results - */ -export const selectTotalPages = createSelector( - [selectFilteredAndSortedCases, selectItemsPerPage], - (filteredCases, itemsPerPage) => Math.ceil(filteredCases.length / itemsPerPage) -); - -/** - * Select Has Previous Page - * - * Purpose: Check if there's a previous page available - */ -export const selectHasPreviousPage = createSelector( - [selectCurrentPage], - (currentPage) => currentPage > 1 -); - -/** - * Select Has Next Page - * - * Purpose: Check if there's a next page available - */ -export const selectHasNextPage = createSelector( - [selectCurrentPage, selectTotalPages], - (currentPage, totalPages) => currentPage < totalPages -); - -/** - * Select Active Filters Count - * - * Purpose: Count how many filters are currently active - */ -export const selectActiveFiltersCount = createSelector( - [selectSearchQuery, selectUrgencyFilter, selectSeverityFilter, selectCategoryFilter], - (searchQuery, urgencyFilter, severityFilter, categoryFilter) => { - let count = 0; - if (searchQuery.trim()) count++; - if (urgencyFilter !== 'all') count++; - if (severityFilter !== 'all') count++; - if (categoryFilter !== 'all') count++; - return count; - } -); - -/* - * End of File: aiPredictionSelectors.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/redux/aiPredictionSlice.ts b/app/modules/AIPrediction/redux/aiPredictionSlice.ts deleted file mode 100644 index 005e484..0000000 --- a/app/modules/AIPrediction/redux/aiPredictionSlice.ts +++ /dev/null @@ -1,621 +0,0 @@ -/* - * File: aiPredictionSlice.ts - * Description: Redux slice for AI Prediction state management - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; -import { - AIPredictionCase, - AIPredictionState, - AIPredictionStats, - AIPredictionAPIResponse -} from '../types'; -import { aiPredictionAPI } from '../services'; - -// ============================================================================ -// ASYNC THUNKS -// ============================================================================ - -/** - * Fetch AI Predictions Async Thunk - * - * Purpose: Fetch AI prediction results from API - * - * @param token - Authentication token - * @param params - Optional query parameters for filtering - * @returns Promise with AI prediction data or error - */ -export const fetchAIPredictions = createAsyncThunk( - 'aiPrediction/fetchAIPredictions', - async (payload: { - token: string; - params?: { - page?: number; - limit?: number; - urgency?: string; - severity?: string; - category?: string; - search?: string; - } - }, { rejectWithValue }) => { - try { - const response: any = await aiPredictionAPI.getAllPredictions(payload.token, payload.params); - console.log('AI predictions response:', response); - - if (response.ok && response.data && response.data.success) { - // Add additional metadata to each case for UI purposes - const enhancedCases = response.data.data.map((aiCase: AIPredictionCase) => ({ - ...aiCase, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - review_status: 'pending' as const, - priority: getPriorityFromPrediction(aiCase.prediction) - })); - - console.log('Enhanced AI prediction cases:', enhancedCases); - return { - cases: enhancedCases as AIPredictionCase[], - total: response.data.total || enhancedCases.length, - page: response.data.page || 1, - limit: response.data.limit || 20 - }; - } else { - // Fallback to mock data for development - const mockData = generateMockAIPredictions(); - return { - cases: mockData, - total: mockData.length, - page: 1, - limit: 20 - }; - } - } catch (error: any) { - console.error('Fetch AI predictions error:', error); - return rejectWithValue(error.message || 'Failed to fetch AI predictions.'); - } - } -); - -/** - * Fetch AI Prediction Case Details Async Thunk - * - * Purpose: Fetch detailed information for a specific AI prediction case - * - * @param caseId - AI prediction case ID - * @param token - Authentication token - * @returns Promise with case details or error - */ -export const fetchAIPredictionDetails = createAsyncThunk( - 'aiPrediction/fetchAIPredictionDetails', - async (payload: { caseId: string; token: string }, { rejectWithValue }) => { - try { - const response: any = await aiPredictionAPI.getCaseDetails(payload.caseId, payload.token); - - if (response.ok && response.data) { - return response.data as AIPredictionCase; - } else { - // Fallback to mock data - const mockCase = generateMockAIPredictions().find(c => c.patid === payload.caseId); - if (mockCase) { - return mockCase; - } - throw new Error('Case not found'); - } - } catch (error: any) { - console.error('Fetch AI prediction details error:', error); - return rejectWithValue(error.message || 'Failed to fetch case details.'); - } - } -); - -/** - * Update Case Review Async Thunk - * - * Purpose: Update review status of an AI prediction case - * - * @param caseId - Case ID to update - * @param reviewData - Review data - * @param token - Authentication token - * @returns Promise with updated case or error - */ -export const updateCaseReview = createAsyncThunk( - 'aiPrediction/updateCaseReview', - async (payload: { - caseId: string; - reviewData: { - review_status: 'pending' | 'reviewed' | 'confirmed' | 'disputed'; - reviewed_by?: string; - review_notes?: string; - priority?: 'critical' | 'high' | 'medium' | 'low'; - }; - token: string; - }, { rejectWithValue }) => { - try { - const response: any = await aiPredictionAPI.updateCaseReview( - payload.caseId, - payload.reviewData, - payload.token - ); - - if (response.ok && response.data) { - return { - caseId: payload.caseId, - ...payload.reviewData, - updated_at: new Date().toISOString() - }; - } else { - throw new Error('Failed to update case review'); - } - } catch (error: any) { - console.error('Update case review error:', error); - return rejectWithValue(error.message || 'Failed to update case review.'); - } - } -); - -/** - * Fetch AI Prediction Statistics Async Thunk - * - * Purpose: Fetch statistics for AI predictions dashboard - * - * @param token - Authentication token - * @param timeRange - Time range filter - * @returns Promise with statistics data or error - */ -export const fetchAIPredictionStats = createAsyncThunk( - 'aiPrediction/fetchAIPredictionStats', - async (payload: { token: string; timeRange?: 'today' | 'week' | 'month' }, { rejectWithValue }) => { - try { - const response: any = await aiPredictionAPI.getPredictionStats(payload.token, payload.timeRange); - - if (response.ok && response.data) { - return response.data as AIPredictionStats; - } else { - // Fallback to mock stats - return generateMockStats(); - } - } catch (error: any) { - console.error('Fetch AI prediction stats error:', error); - return rejectWithValue(error.message || 'Failed to fetch statistics.'); - } - } -); - -// ============================================================================ -// HELPER FUNCTIONS -// ============================================================================ - -/** - * Get Priority from AI Prediction - * - * Purpose: Determine case priority based on AI prediction results - */ -function getPriorityFromPrediction(prediction: any): 'critical' | 'high' | 'medium' | 'low' { - if (prediction.clinical_urgency === 'emergency' || prediction.primary_severity === 'high') { - return 'critical'; - } - if (prediction.clinical_urgency === 'urgent' || prediction.primary_severity === 'medium') { - return 'high'; - } - if (prediction.clinical_urgency === 'moderate' || prediction.primary_severity === 'low') { - return 'medium'; - } - return 'low'; -} - -/** - * Generate Mock AI Predictions - * - * Purpose: Generate mock data for development and testing - */ -function generateMockAIPredictions(): AIPredictionCase[] { - return [ - { - patid: "demogw05-08-2017", - hospital_id: "eec24855-d8ae-4fad-8e54-af0480343dc2", - prediction: { - label: "midline shift", - finding_type: "pathology", - clinical_urgency: "urgent", - confidence_score: 0.996, - finding_category: "abnormal", - primary_severity: "high", - anatomical_location: "brain" - }, - created_at: "2024-01-15T10:30:00Z", - updated_at: "2024-01-15T10:30:00Z", - review_status: "pending", - priority: "critical" - }, - { - patid: "demo-patient-002", - hospital_id: "eec24855-d8ae-4fad-8e54-af0480343dc2", - prediction: { - label: "normal brain", - finding_type: "no_pathology", - clinical_urgency: "routine", - confidence_score: 0.892, - finding_category: "normal", - primary_severity: "none", - anatomical_location: "not_applicable" - }, - created_at: "2024-01-15T09:15:00Z", - updated_at: "2024-01-15T09:15:00Z", - review_status: "reviewed", - priority: "low" - }, - { - patid: "demo-patient-003", - hospital_id: "eec24855-d8ae-4fad-8e54-af0480343dc2", - prediction: { - label: "hemorrhage", - finding_type: "pathology", - clinical_urgency: "emergency", - confidence_score: 0.945, - finding_category: "critical", - primary_severity: "high", - anatomical_location: "temporal lobe" - }, - created_at: "2024-01-15T11:45:00Z", - updated_at: "2024-01-15T11:45:00Z", - review_status: "confirmed", - priority: "critical" - } - ]; -} - -/** - * Generate Mock Statistics - * - * Purpose: Generate mock statistics for development - */ -function generateMockStats(): AIPredictionStats { - return { - totalCases: 156, - criticalCases: 23, - urgentCases: 45, - reviewedCases: 89, - pendingCases: 67, - averageConfidence: 0.887, - todaysCases: 12, - weeklyTrend: 15.4 - }; -} - -// ============================================================================ -// INITIAL STATE -// ============================================================================ - -/** - * Initial AI Prediction State - * - * Purpose: Define the initial state for AI predictions - * - * Features: - * - Prediction cases list and management - * - Current case details - * - Loading states for async operations - * - Error handling and messages - * - Search and filtering - * - Pagination support - * - Cache management - */ -const initialState: AIPredictionState = { - // Prediction data - predictionCases: [], - currentCase: null, - - // Loading states - isLoading: false, - isRefreshing: false, - isLoadingCaseDetails: false, - - // Error handling - error: null, - - // Search and filtering - searchQuery: '', - selectedUrgencyFilter: 'all', - selectedSeverityFilter: 'all', - selectedCategoryFilter: 'all', - sortBy: 'date', - sortOrder: 'desc', - - // Pagination - currentPage: 1, - itemsPerPage: 20, - totalItems: 0, - - // Cache management - lastUpdated: null, - cacheExpiry: null, - - // UI state - showFilters: false, - selectedCaseIds: [], -}; - -// ============================================================================ -// AI PREDICTION SLICE -// ============================================================================ - -/** - * AI Prediction Slice - * - * Purpose: Redux slice for AI prediction state management - * - * Features: - * - AI prediction data management - * - Search and filtering - * - Case review management - * - Pagination - * - Caching - * - Error handling - * - Loading states - */ -const aiPredictionSlice = createSlice({ - name: 'aiPrediction', - initialState, - reducers: { - /** - * Clear Error Action - * - * Purpose: Clear AI prediction errors - */ - clearError: (state) => { - state.error = null; - }, - - /** - * Set Search Query Action - * - * Purpose: Set search query for AI predictions - */ - setSearchQuery: (state, action: PayloadAction) => { - state.searchQuery = action.payload; - state.currentPage = 1; // Reset to first page when searching - }, - - /** - * Set Urgency Filter Action - * - * Purpose: Set urgency filter for AI predictions - */ - setUrgencyFilter: (state, action: PayloadAction) => { - state.selectedUrgencyFilter = action.payload; - state.currentPage = 1; // Reset to first page when filtering - }, - - /** - * Set Severity Filter Action - * - * Purpose: Set severity filter for AI predictions - */ - setSeverityFilter: (state, action: PayloadAction) => { - state.selectedSeverityFilter = action.payload; - state.currentPage = 1; // Reset to first page when filtering - }, - - /** - * Set Category Filter Action - * - * Purpose: Set category filter for AI predictions - */ - setCategoryFilter: (state, action: PayloadAction) => { - state.selectedCategoryFilter = action.payload; - state.currentPage = 1; // Reset to first page when filtering - }, - - /** - * Set Sort Action - * - * Purpose: Set sort options for AI predictions - */ - setSort: (state, action: PayloadAction<{ by: 'date' | 'urgency' | 'confidence' | 'severity'; order: 'asc' | 'desc' }>) => { - state.sortBy = action.payload.by; - state.sortOrder = action.payload.order; - }, - - /** - * Set Current Page Action - * - * Purpose: Set current page for pagination - */ - setCurrentPage: (state, action: PayloadAction) => { - state.currentPage = action.payload; - }, - - /** - * Set Items Per Page Action - * - * Purpose: Set items per page for pagination - */ - setItemsPerPage: (state, action: PayloadAction) => { - state.itemsPerPage = action.payload; - state.currentPage = 1; // Reset to first page when changing items per page - }, - - /** - * Set Current Case Action - * - * Purpose: Set the currently selected AI prediction case - */ - setCurrentCase: (state, action: PayloadAction) => { - state.currentCase = action.payload; - }, - - /** - * Update Case in List Action - * - * Purpose: Update an AI prediction case in the list - */ - updateCaseInList: (state, action: PayloadAction) => { - const index = state.predictionCases.findIndex(case_ => case_.patid === action.payload.patid); - if (index !== -1) { - state.predictionCases[index] = action.payload; - } - - // Update current case if it's the same case - if (state.currentCase && state.currentCase.patid === action.payload.patid) { - state.currentCase = action.payload; - } - }, - - /** - * Toggle Show Filters Action - * - * Purpose: Toggle the display of filter options - */ - toggleShowFilters: (state) => { - state.showFilters = !state.showFilters; - }, - - /** - * Clear All Filters Action - * - * Purpose: Reset all filters to default values - */ - clearAllFilters: (state) => { - state.searchQuery = ''; - state.selectedUrgencyFilter = 'all'; - state.selectedSeverityFilter = 'all'; - state.selectedCategoryFilter = 'all'; - state.currentPage = 1; - }, - - /** - * Select Case Action - * - * Purpose: Add/remove case from selected cases - */ - toggleCaseSelection: (state, action: PayloadAction) => { - const caseId = action.payload; - const index = state.selectedCaseIds.indexOf(caseId); - - if (index === -1) { - state.selectedCaseIds.push(caseId); - } else { - state.selectedCaseIds.splice(index, 1); - } - }, - - /** - * Clear Selected Cases Action - * - * Purpose: Clear all selected cases - */ - clearSelectedCases: (state) => { - state.selectedCaseIds = []; - }, - - /** - * Clear Cache Action - * - * Purpose: Clear AI prediction data cache - */ - clearCache: (state) => { - state.predictionCases = []; - state.currentCase = null; - state.lastUpdated = null; - state.cacheExpiry = null; - }, - }, - extraReducers: (builder) => { - // Fetch AI Predictions - builder - .addCase(fetchAIPredictions.pending, (state) => { - state.isLoading = true; - state.error = null; - }) - .addCase(fetchAIPredictions.fulfilled, (state, action) => { - state.isLoading = false; - state.predictionCases = action.payload.cases; - state.totalItems = action.payload.total; - state.lastUpdated = new Date().toLocaleString(); - state.cacheExpiry = new Date(Date.now() + 5 * 60 * 1000).toLocaleString(); // 5 minutes - state.error = null; - }) - .addCase(fetchAIPredictions.rejected, (state, action) => { - state.isLoading = false; - state.error = action.payload as string; - }); - - // Fetch AI Prediction Details - builder - .addCase(fetchAIPredictionDetails.pending, (state) => { - state.isLoadingCaseDetails = true; - state.error = null; - }) - .addCase(fetchAIPredictionDetails.fulfilled, (state, action) => { - state.isLoadingCaseDetails = false; - state.currentCase = action.payload; - state.error = null; - }) - .addCase(fetchAIPredictionDetails.rejected, (state, action) => { - state.isLoadingCaseDetails = false; - state.error = action.payload as string; - }); - - // Update Case Review - builder - .addCase(updateCaseReview.fulfilled, (state, action) => { - // Update case in list - const index = state.predictionCases.findIndex(case_ => case_.patid === action.payload.caseId); - if (index !== -1) { - state.predictionCases[index] = { - ...state.predictionCases[index], - review_status: action.payload.review_status, - reviewed_by: action.payload.reviewed_by, - priority: action.payload.priority, - updated_at: action.payload.updated_at - }; - } - - // Update current case if it's the same case - if (state.currentCase && state.currentCase.patid === action.payload.caseId) { - state.currentCase = { - ...state.currentCase, - review_status: action.payload.review_status, - reviewed_by: action.payload.reviewed_by, - priority: action.payload.priority, - updated_at: action.payload.updated_at - }; - } - }) - .addCase(updateCaseReview.rejected, (state, action) => { - state.error = action.payload as string; - }); - }, -}); - -// ============================================================================ -// EXPORTS -// ============================================================================ - -export const { - clearError, - setSearchQuery, - setUrgencyFilter, - setSeverityFilter, - setCategoryFilter, - setSort, - setCurrentPage, - setItemsPerPage, - setCurrentCase, - updateCaseInList, - toggleShowFilters, - clearAllFilters, - toggleCaseSelection, - clearSelectedCases, - clearCache, -} = aiPredictionSlice.actions; - -export default aiPredictionSlice.reducer; - -/* - * End of File: aiPredictionSlice.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/redux/index.ts b/app/modules/AIPrediction/redux/index.ts deleted file mode 100644 index f93e701..0000000 --- a/app/modules/AIPrediction/redux/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * File: index.ts - * Description: Redux exports for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -export * from './aiPredictionSlice'; -export * from './aiPredictionSelectors'; - -/* - * End of File: index.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/screens/AIPredictionDetailScreen.tsx b/app/modules/AIPrediction/screens/AIPredictionDetailScreen.tsx deleted file mode 100644 index f3e6f9d..0000000 --- a/app/modules/AIPrediction/screens/AIPredictionDetailScreen.tsx +++ /dev/null @@ -1,1324 +0,0 @@ -/* - * File: AIPredictionDetailScreen.tsx - * Description: AI Prediction detail screen with suggestion form for updating AI findings - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React, { useState, useCallback, useEffect } from 'react'; -import { - View, - Text, - StyleSheet, - ScrollView, - TextInput, - TouchableOpacity, - TouchableWithoutFeedback, - Alert, - SafeAreaView, - KeyboardAvoidingView, - Platform, - Dimensions, -} from 'react-native'; -import DateTimePicker from '@react-native-community/datetimepicker'; -import Icon from 'react-native-vector-icons/Feather'; -import { theme } from '../../../theme'; -import { useAppDispatch, useAppSelector } from '../../../store/hooks'; -import { CustomModal } from '../../../shared/components/CustomModal'; -import Toast from 'react-native-toast-message'; -import { aiPredictionAPI } from '../services/aiPredictionAPI'; -import { showError } from '../../../shared/utils/toast'; - -// Import Redux selectors -import { selectCurrentCase, selectIsLoadingCaseDetails, selectError } from '../redux'; -import { selectUser } from '../../Auth/redux/authSelectors'; - -// Import types -import type { AIPredictionDetailsScreenProps } from '../navigation/navigationTypes'; -import type { AIPredictionCase } from '../types'; - -// Get screen dimensions -const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); - -// ============================================================================ -// ENUMS & TYPES -// ============================================================================ - -/** - * Suggestion Type Enum - * - * Purpose: Define available suggestion types as specified by user - */ -export enum SuggestionType { - TREATMENT = 'Treatment', - FOLLOW_UP = 'Follow Up', - DIAGNOSIS = 'Diagnosis', - OTHER = 'Other' -} - -/** - * Priority Enum - * - * Purpose: Define priority levels as specified by user - */ -export enum Priority { - LOW = 'Low', - MEDIUM = 'Medium', - HIGH = 'High', - CRITICAL = 'Critical' -} - -/** - * Related Finding Interface - * - * Purpose: Structure for key-value pairs in related findings - */ -interface RelatedFinding { - key: string; - value: string; -} - -/** - * Suggestion Form Data Interface - * - * Purpose: Structure for suggestion form data - */ -interface SuggestionFormData { - patientId: string; - suggestionType: SuggestionType; - title: string; - suggestionText: string; - confidence: string; - priority: Priority; - category: string; - costEstimate: string; - timeEstimate: string; - expiresAt: Date | null; - aiModelVersion: string; - evidenceSources: string; - contraindications: string; - tags: string; - relatedFindings: RelatedFinding[]; -} - -/** - * API Response Interface - * - * Purpose: Structure for API response data - */ -interface APIResponse { - success: boolean; - message: string; - data: { - created_at: string; - updated_at: string; - status: string; - suggestion_id: string; - }; -} - -// ============================================================================ -// CONSTANTS -// ============================================================================ - -const SUGGESTION_TYPE_OPTIONS = [ - { label: 'Treatment', value: SuggestionType.TREATMENT, icon: 'activity', color: '#4CAF50' }, - { label: 'Follow Up', value: SuggestionType.FOLLOW_UP, icon: 'calendar', color: '#2196F3' }, - { label: 'Diagnosis', value: SuggestionType.DIAGNOSIS, icon: 'search', color: '#FF9800' }, - { label: 'Other', value: SuggestionType.OTHER, icon: 'more-horizontal', color: '#9C27B0' }, -]; - -const PRIORITY_OPTIONS = [ - { label: 'Low', value: Priority.LOW, color: '#4CAF50', bgColor: '#E8F5E8' }, - { label: 'Medium', value: Priority.MEDIUM, color: '#FF9800', bgColor: '#FFF3E0' }, - { label: 'High', value: Priority.HIGH, color: '#FF5722', bgColor: '#FFEBEE' }, - { label: 'Critical', value: Priority.CRITICAL, color: '#F44336', bgColor: '#FFEBEE' }, -]; - -// ============================================================================ -// AI PREDICTION DETAIL SCREEN COMPONENT -// ============================================================================ - -/** - * AIPredictionDetailScreen Component - * - * Purpose: Display AI prediction details and allow suggestion updates - * - * Features: - * - View complete AI prediction details - * - Create/update suggestions for AI findings - * - Form validation and submission - * - Dynamic related findings management - * - Date picker for expiration - * - Dropdown selections for types and priorities - * - Responsive design with keyboard handling - * - Enhanced visual design with modern mobile styling - */ -const AIPredictionDetailScreen: React.FC = ({ - navigation, - route -}) => { - // ============================================================================ - // REDUX STATE - // ============================================================================ - - const dispatch = useAppDispatch(); - const user = useAppSelector(selectUser); - const currentCase = useAppSelector(selectCurrentCase); - const isLoading = useAppSelector(selectIsLoadingCaseDetails); - const error = useAppSelector(selectError); - - // ============================================================================ - // LOCAL STATE - // ============================================================================ - - const [formData, setFormData] = useState({ - patientId: route.params.caseId, - suggestionType: SuggestionType.TREATMENT, - title: '', - suggestionText: '', - confidence: '0.99', - priority: Priority.HIGH, - category: 'Radiology', - costEstimate: '', - timeEstimate: '1-2 hours', - expiresAt: null, - aiModelVersion: 'v2.1.0', - evidenceSources: '', - contraindications: '', - tags: '', - relatedFindings: [ - { key: 'finding_label', value: 'midline shift' }, - { key: 'finding_type', value: 'no_pathology' }, - { key: 'clinical_urgency', value: 'urgent' }, - { key: 'primary_severity', value: 'high' }, - { key: 'modality', value: '/DX' }, - { key: 'institution', value: 'Brighton Radiology' }, - ], - }); - - const [showSuggestionTypeDropdown, setShowSuggestionTypeDropdown] = useState(false); - const [showPriorityDropdown, setShowPriorityDropdown] = useState(false); - const [showDatePicker, setShowDatePicker] = useState(false); - const [isSubmitting, setIsSubmitting] = useState(false); - const [showSuccessModal, setShowSuccessModal] = useState(false); - const [apiResponse, setApiResponse] = useState(null); - - - // ============================================================================ - // EFFECTS - // ============================================================================ - - /** - * Initialize form data from current case - */ - useEffect(() => { - if (currentCase) { - setFormData(prev => ({ - ...prev, - patientId: currentCase.patid, - title: `Recommended CT Scan`, - suggestionText: `Describe your suggestion with clinical reasoning...`, - confidence: currentCase.prediction.confidence_score.toFixed(4), - })); - } - }, [currentCase]); - - // ============================================================================ - // EVENT HANDLERS - // ============================================================================ - - /** - * Handle form field changes - */ - const handleFieldChange = useCallback((field: keyof SuggestionFormData, value: any) => { - setFormData(prev => ({ - ...prev, - [field]: value, - })); - }, []); - - /** - * Handle dropdown selection - */ - const handleDropdownSelect = useCallback((field: keyof SuggestionFormData, value: any) => { - setFormData(prev => ({ - ...prev, - [field]: value, - })); - setShowSuggestionTypeDropdown(false); - setShowPriorityDropdown(false); - }, []); - - /** - * Close all dropdowns - */ - const closeAllDropdowns = useCallback(() => { - setShowSuggestionTypeDropdown(false); - setShowPriorityDropdown(false); - }, []); - - /** - * Handle date selection - */ - const handleDateChange = useCallback((event: any, selectedDate?: Date) => { - setShowDatePicker(false); - if (selectedDate) { - handleFieldChange('expiresAt', selectedDate); - } - }, [handleFieldChange]); - - /** - * Add new related finding - */ - const handleAddRelatedFinding = useCallback(() => { - setFormData(prev => ({ - ...prev, - relatedFindings: [...prev.relatedFindings, { key: '', value: '' }], - })); - }, []); - - /** - * Remove related finding - */ - const handleRemoveRelatedFinding = useCallback((index: number) => { - setFormData(prev => ({ - ...prev, - relatedFindings: prev.relatedFindings.filter((_, i) => i !== index), - })); - }, []); - - /** - * Update related finding - */ - const handleUpdateRelatedFinding = useCallback((index: number, field: 'key' | 'value', value: string) => { - setFormData(prev => ({ - ...prev, - relatedFindings: prev.relatedFindings.map((finding, i) => - i === index ? { ...finding, [field]: value } : finding - ), - })); - }, []); - - /** - * Validate form data - */ - const validateForm = useCallback((): boolean => { - if (!formData.title.trim()) { - showError('Validation Error', 'Title is required'); - return false; - } - if (!formData.suggestionText.trim()) { - showError('Validation Error', 'Suggestion text is required'); - return false; - } - if (!formData.patientId.trim()) { - showError('Validation Error', 'Patient ID is required'); - return false; - } - - // Validate expiry date - must be in the future - if (formData.expiresAt) { - const now = new Date(); - const expiryDate = new Date(formData.expiresAt); - - if (expiryDate <= now) { - showError('Validation Error', 'Expiry date must be in the future'); - return false; - } - } - - return true; - }, [formData]); - - /** - * Handle form submission - */ - const handleSubmitSuggestion = useCallback(async () => { - if (!validateForm()) return; - - setIsSubmitting(true); - try { - // Prepare API payload according to backend structure - const apiPayload = { - patid: formData.patientId, - suggestion_type: formData.suggestionType.toLowerCase().replace(' ', '_'), - suggestion_title: formData.title, - suggestion_text: formData.suggestionText, - confidence_score: parseFloat(formData.confidence), - priority_level: formData.priority.toLowerCase(), - category: formData.category, - related_findings: formData.relatedFindings.reduce((acc, finding) => { - if (finding.key && finding.value) { - acc[finding.key] = finding.value; - } - return acc; - }, {} as Record), - evidence_sources: formData.evidenceSources ? formData.evidenceSources.split(',').map(s => s.trim()) : [], - contraindications: formData.contraindications || 'none', - cost_estimate: formData.costEstimate ? parseFloat(formData.costEstimate) : 0, - time_estimate: formData.timeEstimate, - expires_at: formData.expiresAt ? formData.expiresAt.toISOString() : null, - tags: formData.tags ? formData.tags.split(',').map(s => s.trim()) : [], - ai_model_version: formData.aiModelVersion, - }; - - // Call centralized API service - const response = await aiPredictionAPI.submitAISuggestion(apiPayload, user?.access_token || ''); - console.log('feed back response',response) - if (response.ok && response.data) { - const responseData = response.data as APIResponse; - - if (responseData.success) { - // Show success toast - Toast.show({ - type: 'success', - text1: 'Success!', - text2: responseData.message || 'Suggestion submitted successfully', - position: 'top', - visibilityTime: 4000, - }); - - // Store API response data - setApiResponse(responseData.data); - // Show success modal with API response data - setShowSuccessModal(true); - } else { - throw new Error(responseData.message || 'Failed to submit suggestion'); - } - } else { - throw new Error(response.problem || 'HTTP request failed'); - } - } catch (error) { - console.error('Submission Error:', error); - - // Show error toast - Toast.show({ - type: 'error', - text1: 'Error', - text2: error instanceof Error ? error.message : 'Failed to submit suggestion. Please try again.', - position: 'top', - visibilityTime: 5000, - }); - } finally { - setIsSubmitting(false); - } - }, [formData, validateForm, user]); - - /** - * Handle success modal close - */ - const handleSuccessModalClose = useCallback(() => { - setShowSuccessModal(false); - navigation.goBack(); - }, [navigation]); - - /** - * Handle back navigation - */ - const handleGoBack = useCallback(() => { - navigation.goBack(); - }, [navigation]); - - - - // ============================================================================ - // RENDER FUNCTIONS - // ============================================================================ - - /** - * Render enhanced header - */ - const renderHeader = () => ( - - - - - - AI Prediction Details - Create Medical Suggestion - - - - ); - - /** - * Render enhanced dropdown options - */ - const renderDropdownOptions = useCallback(( - options: Array<{ label: string; value: any; color?: string; bgColor?: string; icon?: string }>, - currentValue: any, - onSelect: (value: any) => void - ) => ( - - {options.map((option) => ( - onSelect(option.value)} - > - - {option.icon && ( - - )} - - {option.label} - - - {currentValue === option.value && ( - - )} - - ))} - - ), []); - - /** - * Render enhanced related findings section - */ - const renderRelatedFindings = useCallback(() => ( - - - - Related Findings - - - {formData.relatedFindings.map((finding, index) => ( - - - handleUpdateRelatedFinding(index, 'key', value)} - placeholder="Finding Key" - placeholderTextColor={theme.colors.textMuted} - /> - handleUpdateRelatedFinding(index, 'value', value)} - placeholder="Finding Value" - placeholderTextColor={theme.colors.textMuted} - /> - - handleRemoveRelatedFinding(index)} - accessibilityRole="button" - accessibilityLabel="Remove related finding" - > - - - - ))} - - - Add Finding - - - - ), [formData.relatedFindings, handleUpdateRelatedFinding, handleRemoveRelatedFinding, handleAddRelatedFinding]); - - // ============================================================================ - // RENDER - // ============================================================================ - - return ( - - - - - {/* Enhanced Header */} - {renderHeader()} - - - - - {/* Patient ID Card */} - - - - Patient Information - - - Patient ID - - - - - {/* Suggestion Type Card */} - - - - Suggestion Type - - - Type - - setShowSuggestionTypeDropdown(!showSuggestionTypeDropdown)} - > - - opt.value === formData.suggestionType)?.icon as any || 'edit-3'} - size={18} - color={SUGGESTION_TYPE_OPTIONS.find(opt => opt.value === formData.suggestionType)?.color || theme.colors.primary} - /> - {formData.suggestionType} - - - - {showSuggestionTypeDropdown && renderDropdownOptions( - SUGGESTION_TYPE_OPTIONS, - formData.suggestionType, - (value) => handleDropdownSelect('suggestionType', value) - )} - - - - - {/* Main Suggestion Card */} - - - - Suggestion Details - - - {/* Title */} - - Title - handleFieldChange('title', value)} - placeholder="Recommended CT Scan" - placeholderTextColor={theme.colors.textMuted} - /> - - - {/* Suggestion Text */} - - Suggestion Text - handleFieldChange('suggestionText', value)} - placeholder="Describe your suggestion with clinical reasoning..." - placeholderTextColor={theme.colors.textMuted} - multiline - numberOfLines={4} - textAlignVertical="top" - /> - - - - {/* Confidence & Priority Card */} - - - - Assessment & Priority - - - {/* Row 1: Confidence and Priority */} - - - Confidence (0-1) - handleFieldChange('confidence', value)} - placeholder="0.9979" - placeholderTextColor={theme.colors.textMuted} - keyboardType="numeric" - /> - - - - Priority - - setShowPriorityDropdown(!showPriorityDropdown)} - > - - opt.value === formData.priority)?.bgColor || theme.colors.backgroundAlt } - ]} /> - {formData.priority} - - - - {showPriorityDropdown && renderDropdownOptions( - PRIORITY_OPTIONS, - formData.priority, - (value) => handleDropdownSelect('priority', value) - )} - - - - - - {/* Category & Cost Card */} - - - - Classification & Resources - - - {/* Row 2: Category and Cost Estimate */} - - - Category - handleFieldChange('category', value)} - placeholder="Radiology" - placeholderTextColor={theme.colors.textMuted} - /> - - - - Cost Estimate (USD) - handleFieldChange('costEstimate', value)} - placeholder="Enter cost" - placeholderTextColor={theme.colors.textMuted} - keyboardType="numeric" - /> - - - - - {/* Time & Expiry Card */} - - - - Timeline - - - {/* Row 3: Time Estimate and Expires At */} - - - Time Estimate - handleFieldChange('timeEstimate', value)} - placeholder="1-2 hours" - placeholderTextColor={theme.colors.textMuted} - /> - - - - Expires At - setShowDatePicker(true)} - > - - - {formData.expiresAt - ? formData.expiresAt.toLocaleDateString() - : 'Select date' - } - - - Date must be in the future - - - - - {/* AI Model & Evidence Card */} - - - - AI Model & Evidence - - - {/* AI Model Version */} - - AI Model Version - handleFieldChange('aiModelVersion', value)} - placeholder="v2.1.0" - placeholderTextColor={theme.colors.textMuted} - /> - - - {/* Evidence Sources */} - - Evidence Sources (comma-separated) - handleFieldChange('evidenceSources', value)} - placeholder="Evidence A, Protocol B" - placeholderTextColor={theme.colors.textMuted} - /> - - - - {/* Contraindications & Tags Card */} - - - - Safety & Organization - - - {/* Contraindications */} - - Contraindications - handleFieldChange('contraindications', value)} - placeholder="Any known contraindications..." - placeholderTextColor={theme.colors.textMuted} - multiline - numberOfLines={3} - textAlignVertical="top" - /> - - - {/* Tags */} - - Tags (comma-separated) - handleFieldChange('tags', value)} - placeholder="emergency, chest, pulmonary" - placeholderTextColor={theme.colors.textMuted} - /> - - - - {/* Related Findings */} - {renderRelatedFindings()} - - {/* Submit Button */} - - {isSubmitting ? ( - - - Submitting... - - ) : ( - - - Submit Suggestion - - )} - - - - - - - - - {/* Date Picker */} - {showDatePicker && ( - - )} - - {/* Success Modal */} - - - {/* Toast Messages */} - - - ); -}; - -// ============================================================================ -// STYLES -// ============================================================================ - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#F8FAFC', - }, - header: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.lg, - backgroundColor: theme.colors.background, - borderBottomWidth: 1, - borderBottomColor: '#E2E8F0', - shadowColor: '#000000', - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.06, - shadowRadius: 2, - elevation: 2, - }, - backButton: { - padding: theme.spacing.sm, - backgroundColor: '#F1F5F9', - borderRadius: theme.borderRadius.medium, - }, - headerContent: { - flex: 1, - alignItems: 'center', - }, - headerTitle: { - fontSize: theme.typography.fontSize.displaySmall, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.textPrimary, - marginBottom: 2, - }, - headerSubtitle: { - fontSize: theme.typography.fontSize.bodySmall, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textSecondary, - }, - headerSpacer: { - width: 40, - }, - keyboardAvoidingView: { - flex: 1, - position: 'relative', - }, - scrollView: { - flex: 1, - }, - scrollContent: { - padding: theme.spacing.md, - paddingBottom: theme.spacing.xl, - }, - mainContent: { - flex: 1, - }, - - // Card Styles - patientCard: { - backgroundColor: theme.colors.background, - borderRadius: theme.borderRadius.large, - padding: theme.spacing.lg, - marginBottom: theme.spacing.lg, - shadowColor: '#000000', - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.05, - shadowRadius: 4, - elevation: 2, - borderLeftWidth: 4, - borderLeftColor: theme.colors.primary, - }, - formCard: { - backgroundColor: theme.colors.background, - borderRadius: theme.borderRadius.large, - padding: theme.spacing.lg, - marginBottom: theme.spacing.lg, - shadowColor: '#000000', - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.05, - shadowRadius: 4, - elevation: 2, - }, - cardHeader: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: theme.spacing.md, - paddingBottom: theme.spacing.sm, - borderBottomWidth: 1, - borderBottomColor: '#E2E8F0', - }, - cardTitle: { - fontSize: theme.typography.fontSize.bodyLarge, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.textPrimary, - marginLeft: theme.spacing.sm, - }, - patientCardHeader: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: theme.spacing.md, - }, - patientCardTitle: { - fontSize: theme.typography.fontSize.bodyLarge, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.textPrimary, - marginLeft: theme.spacing.sm, - }, - - // Form Styles - formGroup: { - marginBottom: theme.spacing.md, - }, - formGroupHalf: { - flex: 1, - }, - formRow: { - flexDirection: 'row', - gap: theme.spacing.md, - }, - label: { - fontSize: theme.typography.fontSize.bodyMedium, - fontFamily: theme.typography.fontFamily.medium, - color: theme.colors.textPrimary, - marginBottom: theme.spacing.sm, - }, - input: { - borderWidth: 1, - borderColor: theme.colors.border, - borderRadius: theme.borderRadius.medium, - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - fontSize: theme.typography.fontSize.bodyMedium, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textPrimary, - backgroundColor: theme.colors.background, - }, - enhancedInput: { - borderWidth: 1, - borderColor: '#E2E8F0', - borderRadius: theme.borderRadius.medium, - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.md, - fontSize: theme.typography.fontSize.bodyMedium, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textPrimary, - backgroundColor: '#FFFFFF', - shadowColor: '#000000', - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.03, - shadowRadius: 1, - elevation: 1, - }, - disabledInput: { - backgroundColor: '#F8FAFC', - color: theme.colors.textSecondary, - borderColor: '#E2E8F0', - }, - textArea: { - height: 100, - textAlignVertical: 'top', - }, - - // Dropdown Styles - dropdownContainer: { - position: 'relative', - }, - dropdown: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - borderWidth: 1, - borderColor: theme.colors.border, - borderRadius: theme.borderRadius.medium, - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - backgroundColor: theme.colors.background, - }, - enhancedDropdown: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - borderWidth: 1, - borderColor: '#E2E8F0', - borderRadius: theme.borderRadius.medium, - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.md, - backgroundColor: '#FFFFFF', - shadowColor: '#000000', - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.03, - shadowRadius: 1, - elevation: 1, - }, - dropdownContent: { - flexDirection: 'row', - alignItems: 'center', - flex: 1, - }, - dropdownText: { - fontSize: theme.typography.fontSize.bodyMedium, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textPrimary, - marginLeft: theme.spacing.sm, - }, - - dropdownOptions: { - position: 'absolute', - top: '100%', - left: 0, - right: 0, - borderWidth: 1, - borderColor: '#E2E8F0', - borderRadius: theme.borderRadius.medium, - backgroundColor: '#FFFFFF', - marginTop: theme.spacing.xs, - shadowColor: '#000000', - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.1, - shadowRadius: 4, - elevation: 3, - }, - dropdownOption: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.md, - borderBottomWidth: 1, - borderBottomColor: '#F1F5F9', - }, - dropdownOptionContent: { - flexDirection: 'row', - alignItems: 'center', - flex: 1, - }, - dropdownOptionIcon: { - marginRight: theme.spacing.sm, - }, - dropdownOptionSelected: { - backgroundColor: '#F8FAFC', - }, - dropdownOptionText: { - fontSize: theme.typography.fontSize.bodyMedium, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textPrimary, - }, - dropdownOptionTextSelected: { - color: theme.colors.primary, - fontFamily: theme.typography.fontFamily.medium, - }, - priorityIndicator: { - width: 12, - height: 12, - borderRadius: 6, - marginRight: theme.spacing.sm, - }, - - // Date Input Styles - dateInput: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - borderWidth: 1, - borderColor: theme.colors.border, - borderRadius: theme.borderRadius.medium, - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - backgroundColor: theme.colors.background, - }, - enhancedDateInput: { - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - borderWidth: 1, - borderColor: '#E2E8F0', - borderRadius: theme.borderRadius.medium, - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.md, - backgroundColor: '#FFFFFF', - shadowColor: '#000000', - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.03, - shadowRadius: 1, - elevation: 1, - }, - dateText: { - fontSize: theme.typography.fontSize.bodyMedium, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textPrimary, - flex: 1, - marginLeft: theme.spacing.sm, - }, - placeholderText: { - color: theme.colors.textMuted, - }, - helperText: { - fontSize: theme.typography.fontSize.bodySmall, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textMuted, - marginTop: theme.spacing.xs, - fontStyle: 'italic', - }, - - // Section Styles - sectionHeader: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: theme.spacing.md, - }, - sectionTitle: { - fontSize: theme.typography.fontSize.bodyLarge, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.textPrimary, - marginLeft: theme.spacing.sm, - }, - - // Related Findings Styles - relatedFindingsContainer: { - backgroundColor: theme.colors.background, - borderRadius: theme.borderRadius.large, - padding: theme.spacing.lg, - marginBottom: theme.spacing.lg, - shadowColor: '#000000', - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.05, - shadowRadius: 4, - elevation: 2, - }, - relatedFindingsContent: { - marginTop: theme.spacing.sm, - }, - relatedFindingItem: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.sm, - marginBottom: theme.spacing.sm, - }, - relatedFindingInputs: { - flex: 1, - flexDirection: 'row', - gap: theme.spacing.sm, - }, - relatedFindingKey: { - flex: 1, - backgroundColor: '#F8FAFC', - borderColor: '#E2E8F0', - }, - relatedFindingValue: { - flex: 1, - backgroundColor: '#F8FAFC', - borderColor: '#E2E8F0', - }, - addButton: { - backgroundColor: theme.colors.primary, - borderRadius: theme.borderRadius.medium, - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - alignItems: 'center', - alignSelf: 'flex-start', - flexDirection: 'row', - gap: theme.spacing.sm, - shadowColor: theme.colors.primary, - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.2, - shadowRadius: 2, - elevation: 2, - }, - addButtonText: { - fontSize: theme.typography.fontSize.bodyMedium, - fontFamily: theme.typography.fontFamily.medium, - color: theme.colors.background, - }, - removeButton: { - padding: theme.spacing.sm, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: '#FEF2F2', - borderRadius: theme.borderRadius.medium, - }, - - // Submit Button Styles - submitButton: { - backgroundColor: theme.colors.primary, - borderRadius: theme.borderRadius.large, - paddingVertical: theme.spacing.lg, - alignItems: 'center', - marginTop: theme.spacing.xl, - shadowColor: theme.colors.primary, - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.2, - shadowRadius: 4, - elevation: 4, - }, - submitButtonDisabled: { - backgroundColor: theme.colors.textMuted, - shadowOpacity: 0.1, - }, - submitButtonContent: { - flexDirection: 'row', - alignItems: 'center', - gap: theme.spacing.sm, - }, - submitButtonText: { - fontSize: theme.typography.fontSize.bodyLarge, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.background, - }, - spinningIcon: { - // Add rotation animation if needed - }, -}); - -export default AIPredictionDetailScreen; - -/* - * End of File: AIPredictionDetailScreen.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/screens/AIPredictionsScreen.tsx b/app/modules/AIPrediction/screens/AIPredictionsScreen.tsx deleted file mode 100644 index ebce060..0000000 --- a/app/modules/AIPrediction/screens/AIPredictionsScreen.tsx +++ /dev/null @@ -1,747 +0,0 @@ -/* - * File: AIPredictionsScreen.tsx - * Description: Main AI Predictions screen with data rendering and management - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React, { useEffect, useCallback, useState } from 'react'; -import { - View, - Text, - StyleSheet, - FlatList, - RefreshControl, - TouchableOpacity, - Alert, - SafeAreaView, -} from 'react-native'; -import Icon from 'react-native-vector-icons/Feather'; -import { theme } from '../../../theme'; -import { useAppDispatch, useAppSelector } from '../../../store/hooks'; - -// Import Redux actions and selectors -import { - fetchAIPredictions, - setSearchQuery, - setUrgencyFilter, - setSeverityFilter, - setCategoryFilter, - setCurrentPage, - clearAllFilters, - toggleShowFilters, - toggleCaseSelection, - clearSelectedCases, - updateCaseReview, -} from '../redux'; - -import { - selectPaginatedCases, - selectIsLoading, - selectError, - selectSearchQuery, - selectUrgencyFilter, - selectSeverityFilter, - selectCategoryFilter, - selectShowFilters, - selectSelectedCaseIds, - selectCasesStatistics, - selectFilterCounts, - selectActiveFiltersCount, - selectCurrentPage, - selectTotalPages, - selectHasNextPage, - selectHasPreviousPage, -} from '../redux'; - -// Import components -import { - AIPredictionCard, - SearchBar, - FilterTabs, - LoadingState, - EmptyState, - StatsOverview, -} from '../components'; - -// Import types -import type { AIPredictionCase } from '../types'; - -// Import auth selector -import { selectUser } from '../../Auth/redux/authSelectors'; - -// ============================================================================ -// INTERFACES -// ============================================================================ - -interface AIPredictionsScreenProps { - navigation: any; - route?: any; -} - -// ============================================================================ -// AI PREDICTIONS SCREEN COMPONENT -// ============================================================================ - -/** - * AIPredictionsScreen Component - * - * Purpose: Main screen for displaying and managing AI prediction cases - * - * Features: - * - Comprehensive AI predictions list - * - Real-time search and filtering - * - Statistics overview dashboard - * - Bulk case selection and actions - * - Pull-to-refresh functionality - * - Pagination support - * - Review status management - * - Modern card-based design - * - Error handling and retry - * - Loading states and empty states - * - Accessibility support - */ -const AIPredictionsScreen: React.FC = ({ navigation }) => { - // ============================================================================ - // REDUX STATE - // ============================================================================ - - const dispatch = useAppDispatch(); - - // Auth state - const user :any = useAppSelector(selectUser); - - // AI Prediction state - const cases = useAppSelector(selectPaginatedCases); - const isLoading = useAppSelector(selectIsLoading); - const error = useAppSelector(selectError); - const searchQuery = useAppSelector(selectSearchQuery); - const urgencyFilter = useAppSelector(selectUrgencyFilter); - const severityFilter = useAppSelector(selectSeverityFilter); - const categoryFilter = useAppSelector(selectCategoryFilter); - const showFilters = useAppSelector(selectShowFilters); - const selectedCaseIds = useAppSelector(selectSelectedCaseIds); - const statistics = useAppSelector(selectCasesStatistics); - const filterCounts = useAppSelector(selectFilterCounts); - const activeFiltersCount = useAppSelector(selectActiveFiltersCount); - const currentPage = useAppSelector(selectCurrentPage); - const totalPages = useAppSelector(selectTotalPages); - const hasNextPage = useAppSelector(selectHasNextPage); - const hasPreviousPage = useAppSelector(selectHasPreviousPage); - - // ============================================================================ - // LOCAL STATE - // ============================================================================ - - const [refreshing, setRefreshing] = useState(false); - const [showStats, setShowStats] = useState(true); - - // ============================================================================ - // EFFECTS - // ============================================================================ - - /** - * Load AI Predictions on Mount - * - * Purpose: Fetch AI predictions when component mounts - */ - console.log('user ===>', user); - useEffect(() => { - if (user?.access_token) { - loadAIPredictions(); - } - }, [user?.access_token]); - - /** - * Load AI Predictions on Filter Change - * - * Purpose: Reload data when filters change - */ - useEffect(() => { - if (user?.access_token) { - loadAIPredictions(); - } - }, [urgencyFilter, severityFilter, categoryFilter, searchQuery, currentPage]); - - // ============================================================================ - // EVENT HANDLERS - // ============================================================================ - - /** - * Load AI Predictions - * - * Purpose: Fetch AI predictions from API - */ - const loadAIPredictions = useCallback(async () => { - if (!user?.access_token) return; - - try { - const params = { - page: currentPage, - limit: 20, - ...(urgencyFilter !== 'all' && { urgency: urgencyFilter }), - ...(severityFilter !== 'all' && { severity: severityFilter }), - ...(categoryFilter !== 'all' && { category: categoryFilter }), - ...(searchQuery.trim() && { search: searchQuery.trim() }), - }; - - await dispatch(fetchAIPredictions({ - token: user.access_token, - params, - })).unwrap(); - } catch (error) { - console.error('Failed to load AI predictions:', error); - // Error is handled by Redux state - } - }, [dispatch, user?.access_token, currentPage, urgencyFilter, severityFilter, categoryFilter, searchQuery]); - - /** - * Handle Refresh - * - * Purpose: Handle pull-to-refresh - */ - const handleRefresh = useCallback(async () => { - setRefreshing(true); - await loadAIPredictions(); - setRefreshing(false); - }, [loadAIPredictions]); - - /** - * Handle Search - * - * Purpose: Handle search query change - */ - const handleSearch = useCallback((query: string) => { - dispatch(setSearchQuery(query)); - }, [dispatch]); - - /** - * Handle Filter Changes - * - * Purpose: Handle filter option changes - */ - const handleUrgencyFilterChange = useCallback((filter: typeof urgencyFilter) => { - dispatch(setUrgencyFilter(filter)); - }, [dispatch]); - - const handleSeverityFilterChange = useCallback((filter: typeof severityFilter) => { - dispatch(setSeverityFilter(filter)); - }, [dispatch]); - - const handleCategoryFilterChange = useCallback((filter: typeof categoryFilter) => { - dispatch(setCategoryFilter(filter)); - }, [dispatch]); - - /** - * Handle Clear Filters - * - * Purpose: Clear all active filters - */ - const handleClearFilters = useCallback(() => { - dispatch(clearAllFilters()); - }, [dispatch]); - - /** - * Handle Toggle Filters - * - * Purpose: Toggle filter visibility - */ - const handleToggleFilters = useCallback(() => { - dispatch(toggleShowFilters()); - }, [dispatch]); - - /** - * Handle Case Press - * - * Purpose: Navigate to case details - */ - const handleCasePress = useCallback((predictionCase: AIPredictionCase) => { - navigation.navigate('AIPredictionDetails', { caseId: predictionCase.patid }); - }, [navigation]); - - /** - * Handle Case Review - * - * Purpose: Handle case review action - */ - const handleCaseReview = useCallback(async (caseId: string) => { - if (!user?.access_token) return; - - try { - await dispatch(updateCaseReview({ - caseId, - reviewData: { - review_status: 'reviewed', - reviewed_by: user.name || user.email || 'Current User', - }, - token: user.access_token, - })).unwrap(); - - Alert.alert( - 'Review Updated', - 'Case has been marked as reviewed.', - [{ text: 'OK' }] - ); - } catch (error) { - Alert.alert( - 'Error', - 'Failed to update case review. Please try again.', - [{ text: 'OK' }] - ); - } - }, [dispatch, user]); - - /** - * Handle Case Selection - * - * Purpose: Handle case selection for bulk operations - */ - const handleCaseSelection = useCallback((caseId: string) => { - dispatch(toggleCaseSelection(caseId)); - }, [dispatch]); - - /** - * Handle Bulk Actions - * - * Purpose: Handle bulk actions on selected cases - */ - const handleBulkReview = useCallback(() => { - if (selectedCaseIds.length === 0) return; - - Alert.alert( - 'Bulk Review', - `Mark ${selectedCaseIds.length} cases as reviewed?`, - [ - { text: 'Cancel', style: 'cancel' }, - { - text: 'Confirm', - onPress: async () => { - // Implement bulk review logic here - // For now, just clear selections - dispatch(clearSelectedCases()); - }, - }, - ] - ); - }, [selectedCaseIds, dispatch]); - - /** - * Handle Page Change - * - * Purpose: Handle pagination - */ - const handlePreviousPage = useCallback(() => { - if (hasPreviousPage) { - dispatch(setCurrentPage(currentPage - 1)); - } - }, [dispatch, currentPage, hasPreviousPage]); - - const handleNextPage = useCallback(() => { - if (hasNextPage) { - dispatch(setCurrentPage(currentPage + 1)); - } - }, [dispatch, currentPage, hasNextPage]); - - /** - * Handle Stats Press - * - * Purpose: Handle statistics card press - */ - const handleStatsPress = useCallback((statType: string) => { - // Navigate to detailed statistics or apply relevant filters - switch (statType) { - case 'critical': - dispatch(setUrgencyFilter('emergency')); - break; - case 'urgent': - dispatch(setUrgencyFilter('urgent')); - break; - case 'pending': - // Filter for pending reviews - break; - default: - break; - } - }, [dispatch]); - - /** - * Handle Retry - * - * Purpose: Handle retry after error - */ - const handleRetry = useCallback(() => { - loadAIPredictions(); - }, [loadAIPredictions]); - - // ============================================================================ - // RENDER FUNCTIONS - // ============================================================================ - - /** - * Render AI Prediction Case - * - * Purpose: Render individual AI prediction case card - */ - const renderPredictionCase = useCallback(({ item }: { item: AIPredictionCase }) => ( - - ), [handleCasePress, handleCaseReview, selectedCaseIds, handleCaseSelection]); - - /** - * Render List Header - * - * Purpose: Render search, filters, and statistics - */ - const renderListHeader = useCallback(() => ( - - {/* Statistics Overview */} - {showStats && ( - - )} - - {/* Search Bar */} - - - {/* Filter Controls */} - - - - - Filters - - {activeFiltersCount > 0 && ( - - {activeFiltersCount} - - )} - - - {selectedCaseIds.length > 0 && ( - - - - Review {selectedCaseIds.length} - - - )} - - - {/* Filter Tabs */} - {showFilters && ( - - )} - - {/* Results Summary */} - - - {statistics.total} predictions found - {activeFiltersCount > 0 && ` (${activeFiltersCount} filters applied)`} - - - - ), [ - showStats, - statistics, - handleStatsPress, - searchQuery, - handleSearch, - showFilters, - handleToggleFilters, - activeFiltersCount, - selectedCaseIds, - handleBulkReview, - urgencyFilter, - severityFilter, - categoryFilter, - handleUrgencyFilterChange, - handleSeverityFilterChange, - handleCategoryFilterChange, - handleClearFilters, - filterCounts, - ]); - - /** - * Render List Footer - * - * Purpose: Render pagination controls - */ - const renderListFooter = useCallback(() => { - if (totalPages <= 1) return null; - - return ( - - - - - Previous - - - - - Page {currentPage} of {totalPages} - - - - - Next - - - - - ); - }, [totalPages, currentPage, hasPreviousPage, hasNextPage, handlePreviousPage, handleNextPage]); - - // ============================================================================ - // RENDER - // ============================================================================ - - return ( - - - {/* Header */} - - AI Predictions - setShowStats(!showStats)} - accessibilityRole="button" - accessibilityLabel="Toggle statistics" - > - - - - - {/* Content */} - {error ? ( - - ) : isLoading && cases.length === 0 ? ( - - ) : cases.length === 0 ? ( - 0 ? handleClearFilters : handleRefresh} - /> - ) : ( - item.patid} - ListHeaderComponent={renderListHeader} - ListFooterComponent={renderListFooter} - refreshControl={ - - } - showsVerticalScrollIndicator={false} - contentContainerStyle={styles.listContent} - accessibilityRole="list" - /> - )} - - ); -}; - -// ============================================================================ -// STYLES -// ============================================================================ - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: theme.colors.background, - }, - header: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.md, - borderBottomWidth: 1, - borderBottomColor: theme.colors.border, - backgroundColor: theme.colors.background, - }, - headerTitle: { - fontSize: theme.typography.fontSize.displaySmall, - fontWeight: theme.typography.fontWeight.bold, - color: theme.colors.textPrimary, - }, - headerButton: { - padding: theme.spacing.sm, - }, - filterControls: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - }, - filterToggle: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - borderRadius: theme.borderRadius.medium, - borderWidth: 1, - borderColor: theme.colors.primary, - gap: theme.spacing.sm, - }, - filterToggleActive: { - backgroundColor: theme.colors.primary, - }, - filterToggleText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.primary, - fontWeight: theme.typography.fontWeight.medium, - }, - filterToggleActiveText: { - color: theme.colors.background, - }, - filterBadge: { - backgroundColor: theme.colors.error, - borderRadius: 10, - minWidth: 20, - height: 20, - justifyContent: 'center', - alignItems: 'center', - }, - filterBadgeText: { - fontSize: theme.typography.fontSize.caption, - color: theme.colors.background, - fontWeight: theme.typography.fontWeight.bold, - }, - bulkActionButton: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: theme.colors.primary, - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - borderRadius: theme.borderRadius.medium, - gap: theme.spacing.sm, - }, - bulkActionText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.background, - fontWeight: theme.typography.fontWeight.medium, - }, - resultsSummary: { - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - }, - resultsText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textSecondary, - }, - listContent: { - paddingBottom: theme.spacing.xl, - }, - paginationContainer: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.lg, - marginTop: theme.spacing.lg, - }, - paginationButton: { - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - borderRadius: theme.borderRadius.medium, - borderWidth: 1, - borderColor: theme.colors.primary, - gap: theme.spacing.xs, - }, - paginationButtonDisabled: { - borderColor: theme.colors.textMuted, - opacity: 0.5, - }, - paginationButtonText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.primary, - fontWeight: theme.typography.fontWeight.medium, - }, - paginationButtonTextDisabled: { - color: theme.colors.textMuted, - }, - paginationInfo: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textSecondary, - fontWeight: theme.typography.fontWeight.medium, - }, -}); - -export default AIPredictionsScreen; - -/* - * End of File: AIPredictionsScreen.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/screens/index.ts b/app/modules/AIPrediction/screens/index.ts deleted file mode 100644 index 4c7627e..0000000 --- a/app/modules/AIPrediction/screens/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * File: index.ts - * Description: Screens exports for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -export { default as AIPredictionsScreen } from './AIPredictionsScreen'; -export { default as AIPredictionDetailScreen } from './AIPredictionDetailScreen'; - -/* - * End of File: index.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/services/aiPredictionAPI.ts b/app/modules/AIPrediction/services/aiPredictionAPI.ts deleted file mode 100644 index c74460e..0000000 --- a/app/modules/AIPrediction/services/aiPredictionAPI.ts +++ /dev/null @@ -1,337 +0,0 @@ -/* - * File: aiPredictionAPI.ts - * Description: API service for AI prediction operations using apisauce - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import { create } from 'apisauce'; -import { API_CONFIG, buildHeaders } from '../../../shared/utils'; - -const api = create({ - baseURL: API_CONFIG.BASE_URL -}); - -/** - * AI Prediction API Service - * - * Purpose: Handle all AI prediction-related API operations - * - * Features: - * - Get AI prediction results for all patients - * - Get individual case prediction details - * - Update case review status - * - Search and filter predictions - * - Get prediction statistics - */ -export const aiPredictionAPI = { - /** - * Get All AI Prediction Results - * - * Purpose: Fetch all AI prediction results from server - * - * @param token - Authentication token - * @param params - Optional query parameters for filtering - * @returns Promise with AI prediction cases data - */ - getAllPredictions: (token: string, params?: { - page?: number; - limit?: number; - urgency?: string; - severity?: string; - category?: string; - search?: string; - }) => { - const queryParams = params ? { ...params } : {}; - return api.get('/api/ai-cases/all-prediction-results', queryParams, buildHeaders({ token })); - }, - - /** - * Get AI Prediction Case Details - * - * Purpose: Fetch detailed information for a specific AI prediction case - * - * @param caseId - AI prediction case ID (patid) - * @param token - Authentication token - * @returns Promise with detailed case prediction data - */ - getCaseDetails: (caseId: string, token: string) => { - return api.get(`/api/ai-cases/prediction-details/${caseId}`, {}, buildHeaders({ token })); - }, - - /** - * Update Case Review Status - * - * Purpose: Update the review status of an AI prediction case - * - * @param caseId - AI prediction case ID - * @param reviewData - Review status and notes - * @param token - Authentication token - * @returns Promise with updated case data - */ - updateCaseReview: (caseId: string, reviewData: { - review_status: 'pending' | 'reviewed' | 'confirmed' | 'disputed'; - reviewed_by?: string; - review_notes?: string; - priority?: 'critical' | 'high' | 'medium' | 'low'; - }, token: string) => { - return api.put(`/api/ai-cases/review/${caseId}`, reviewData, buildHeaders({ token })); - }, - - /** - * Get Prediction Statistics - * - * Purpose: Fetch AI prediction statistics for dashboard - * - * @param token - Authentication token - * @param timeRange - Optional time range filter (today, week, month) - * @returns Promise with prediction statistics - */ - getPredictionStats: (token: string, timeRange?: 'today' | 'week' | 'month') => { - const params = timeRange ? { timeRange } : {}; - return api.get('/api/ai-cases/statistics', params, buildHeaders({ token })); - }, - - /** - * Search AI Prediction Cases - * - * Purpose: Search AI prediction cases by various criteria - * - * @param query - Search query (patient ID, hospital, findings) - * @param token - Authentication token - * @param filters - Additional search filters - * @returns Promise with search results - */ - searchPredictions: (query: string, token: string, filters?: { - urgency?: string[]; - severity?: string[]; - category?: string[]; - dateRange?: { start: string; end: string }; - }) => { - const params = { - q: query, - ...(filters && { filters: JSON.stringify(filters) }) - }; - return api.get('/api/ai-cases/search', params, buildHeaders({ token })); - }, - - /** - * Get Predictions by Hospital - * - * Purpose: Fetch AI predictions filtered by hospital - * - * @param hospitalId - Hospital UUID - * @param token - Authentication token - * @param params - Optional query parameters - * @returns Promise with hospital-specific predictions - */ - getPredictionsByHospital: (hospitalId: string, token: string, params?: { - page?: number; - limit?: number; - urgency?: string; - startDate?: string; - endDate?: string; - }) => { - const queryParams = params ? { ...params } : {}; - return api.get(`/api/ai-cases/hospital/${hospitalId}/predictions`, queryParams, buildHeaders({ token })); - }, - - /** - * Get Critical Predictions - * - * Purpose: Fetch only critical and urgent AI predictions - * - * @param token - Authentication token - * @returns Promise with critical predictions data - */ - getCriticalPredictions: (token: string) => { - return api.get('/api/ai-cases/critical-predictions', {}, buildHeaders({ token })); - }, - - /** - * Bulk Update Case Reviews - * - * Purpose: Update multiple case reviews at once - * - * @param caseIds - Array of case IDs to update - * @param reviewData - Review data to apply to all cases - * @param token - Authentication token - * @returns Promise with bulk update results - */ - bulkUpdateReviews: (caseIds: string[], reviewData: { - review_status: 'pending' | 'reviewed' | 'confirmed' | 'disputed'; - reviewed_by?: string; - review_notes?: string; - }, token: string) => { - return api.put('/api/ai-cases/bulk-review', { - caseIds, - reviewData - }, buildHeaders({ token })); - }, - - /** - * Export Predictions Data - * - * Purpose: Export AI predictions data for reporting - * - * @param token - Authentication token - * @param filters - Export filters - * @param format - Export format (csv, xlsx, pdf) - * @returns Promise with export file data - */ - exportPredictions: (token: string, filters?: { - urgency?: string[]; - severity?: string[]; - dateRange?: { start: string; end: string }; - hospitalId?: string; - }, format: 'csv' | 'xlsx' | 'pdf' = 'csv') => { - const params = { - format, - ...(filters && { filters: JSON.stringify(filters) }) - }; - return api.get('/api/ai-cases/export', params, buildHeaders({ token })); - }, - - /** - * Get Prediction Trends - * - * Purpose: Fetch prediction trends data for analytics - * - * @param token - Authentication token - * @param period - Time period for trends (daily, weekly, monthly) - * @returns Promise with trends data - */ - getPredictionTrends: (token: string, period: 'daily' | 'weekly' | 'monthly' = 'weekly') => { - return api.get('/api/ai-cases/trends', { period }, buildHeaders({ token })); - }, - - /** - * Submit Feedback on Prediction - * - * Purpose: Submit physician feedback on AI prediction accuracy - * - * @param caseId - AI prediction case ID - * @param feedbackData - Feedback data - * @param token - Authentication token - * @returns Promise with feedback submission result - */ - submitPredictionFeedback: (caseId: string, feedbackData: { - accuracy_rating: 1 | 2 | 3 | 4 | 5; - is_accurate: boolean; - physician_diagnosis?: string; - feedback_notes?: string; - improvement_suggestions?: string; - }, token: string) => { - return api.post(`/api/ai-cases/feedback/${caseId}`, feedbackData, buildHeaders({ token })); - }, - - /** - * Submit AI Suggestion - * - * Purpose: Submit physician suggestions for AI findings - * - * @param suggestionData - Suggestion data including patient ID, type, title, text, etc. - * @param token - Authentication token - * @returns Promise with suggestion submission result - */ - submitAISuggestion: (suggestionData: { - patid: string; - suggestion_type: string; - suggestion_title: string; - suggestion_text: string; - confidence_score: number; - priority_level: string; - category: string; - related_findings: Record; - evidence_sources: string[]; - contraindications: string; - cost_estimate: number; - time_estimate: string; - expires_at: string | null; - tags: string[]; - ai_model_version: string; - }, token: string) => { - return api.post('/api/ai-cases/suggestions', suggestionData, buildHeaders({ token })); - }, - - /** - * Get AI Suggestions - * - * Purpose: Fetch AI suggestions for a specific case or all suggestions - * - * @param token - Authentication token - * @param params - Optional query parameters - * @returns Promise with suggestions data - */ - getAISuggestions: (token: string, params?: { - caseId?: string; - patientId?: string; - suggestionType?: string; - priority?: string; - status?: string; - page?: number; - limit?: number; - }) => { - const queryParams = params ? { ...params } : {}; - return api.get('/api/ai-cases/suggestions', queryParams, buildHeaders({ token })); - }, - - /** - * Update AI Suggestion - * - * Purpose: Update an existing AI suggestion - * - * @param suggestionId - Suggestion ID to update - * @param updateData - Updated suggestion data - * @param token - Authentication token - * @returns Promise with updated suggestion data - */ - updateAISuggestion: (suggestionId: string, updateData: { - suggestion_title?: string; - suggestion_text?: string; - priority_level?: string; - category?: string; - related_findings?: Record; - evidence_sources?: string[]; - contraindications?: string; - cost_estimate?: number; - time_estimate?: string; - expires_at?: string | null; - tags?: string[]; - }, token: string) => { - return api.put(`/api/ai-cases/suggestions/${suggestionId}`, updateData, buildHeaders({ token })); - }, - - /** - * Delete AI Suggestion - * - * Purpose: Delete an AI suggestion - * - * @param suggestionId - Suggestion ID to delete - * @param token - Authentication token - * @returns Promise with deletion result - */ - deleteAISuggestion: (suggestionId: string, token: string) => { - return api.delete(`/api/ai-cases/suggestions/${suggestionId}`, {}, buildHeaders({ token })); - }, - - /** - * Get Suggestion Statistics - * - * Purpose: Fetch statistics about AI suggestions - * - * @param token - Authentication token - * @param timeRange - Optional time range filter - * @returns Promise with suggestion statistics - */ - getSuggestionStats: (token: string, timeRange?: 'today' | 'week' | 'month') => { - const params = timeRange ? { timeRange } : {}; - return api.get('/api/ai-cases/suggestions/statistics', params, buildHeaders({ token })); - } -}; - -/* - * End of File: aiPredictionAPI.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/services/index.ts b/app/modules/AIPrediction/services/index.ts deleted file mode 100644 index e410e41..0000000 --- a/app/modules/AIPrediction/services/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * File: index.ts - * Description: Services exports for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -export * from './aiPredictionAPI'; - -/* - * End of File: index.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/types/aiPrediction.ts b/app/modules/AIPrediction/types/aiPrediction.ts deleted file mode 100644 index acf9582..0000000 --- a/app/modules/AIPrediction/types/aiPrediction.ts +++ /dev/null @@ -1,221 +0,0 @@ -/* - * File: aiPrediction.ts - * Description: Type definitions for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -// ============================================================================ -// AI PREDICTION INTERFACES -// ============================================================================ - -/** - * AI Prediction Interface - * - * Purpose: Define the structure of AI prediction data from the API - * - * Based on API response structure: - * - label: Type of medical finding - * - finding_type: Category of the finding - * - clinical_urgency: Urgency level for medical response - * - confidence_score: AI confidence in the prediction (0-1) - * - finding_category: General category of the finding - * - primary_severity: Severity level of the condition - * - anatomical_location: Where the finding is located - */ -export interface AIPrediction { - label: string; - finding_type: 'no_pathology' | 'pathology' | 'abnormal' | 'normal' | 'unknown'; - clinical_urgency: 'urgent' | 'moderate' | 'low' | 'routine' | 'emergency'; - confidence_score: number; // 0.0 to 1.0 - finding_category: 'normal' | 'abnormal' | 'critical' | 'warning' | 'unknown'; - primary_severity: 'high' | 'medium' | 'low' | 'none'; - anatomical_location: string; // 'not_applicable' | specific location -} - -/** - * AI Prediction Case Interface - * - * Purpose: Complete AI prediction case data structure - * - * Features: - * - Patient identification - * - Hospital association - * - AI prediction results - * - Metadata for tracking and display - */ -export interface AIPredictionCase { - patid: string; // Patient ID - hospital_id: string; // Hospital UUID - prediction: AIPrediction; - - // Additional metadata (will be added for UI purposes) - created_at?: string; - updated_at?: string; - reviewed_by?: string; - review_status?: 'pending' | 'reviewed' | 'confirmed' | 'disputed'; - priority?: 'critical' | 'high' | 'medium' | 'low'; - processed_at?: string; -} - -/** - * AI Prediction API Response Interface - * - * Purpose: Define the structure of API response - */ -export interface AIPredictionAPIResponse { - success: boolean; - data: AIPredictionCase[]; - message?: string; - total?: number; - page?: number; - limit?: number; -} - -/** - * AI Prediction State Interface - * - * Purpose: Define Redux state structure for AI predictions - * - * Features: - * - Prediction cases management - * - Current selected case - * - Loading states for async operations - * - Error handling and messages - * - Search and filtering - * - Pagination support - * - Cache management - */ -export interface AIPredictionState { - // Prediction data - predictionCases: AIPredictionCase[]; - currentCase: AIPredictionCase | null; - - // Loading states - isLoading: boolean; - isRefreshing: boolean; - isLoadingCaseDetails: boolean; - - // Error handling - error: string | null; - - // Search and filtering - searchQuery: string; - selectedUrgencyFilter: 'all' | 'urgent' | 'moderate' | 'low' | 'routine' | 'emergency'; - selectedSeverityFilter: 'all' | 'high' | 'medium' | 'low' | 'none'; - selectedCategoryFilter: 'all' | 'normal' | 'abnormal' | 'critical' | 'warning' | 'unknown'; - sortBy: 'date' | 'urgency' | 'confidence' | 'severity'; - sortOrder: 'asc' | 'desc'; - - // Pagination - currentPage: number; - itemsPerPage: number; - totalItems: number; - - // Cache management - lastUpdated: String | null; - cacheExpiry: String | null; - - // UI state - showFilters: boolean; - selectedCaseIds: string[]; -} - -/** - * AI Prediction Filter Options - * - * Purpose: Define available filter options for the UI - */ -export interface AIPredictionFilters { - urgency: Array<{ - label: string; - value: AIPredictionState['selectedUrgencyFilter']; - count?: number; - }>; - severity: Array<{ - label: string; - value: AIPredictionState['selectedSeverityFilter']; - count?: number; - }>; - category: Array<{ - label: string; - value: AIPredictionState['selectedCategoryFilter']; - count?: number; - }>; -} - -/** - * AI Prediction Statistics Interface - * - * Purpose: Define statistics data for dashboard display - */ -export interface AIPredictionStats { - totalCases: number; - criticalCases: number; - urgentCases: number; - reviewedCases: number; - pendingCases: number; - averageConfidence: number; - todaysCases: number; - weeklyTrend: number; // percentage change from last week -} - -/** - * AI Prediction Navigation Props - * - * Purpose: Type safety for navigation between AI prediction screens - */ -export type AIPredictionNavigationProps = { - AIPredictionList: undefined; - AIPredictionDetails: { caseId: string }; - AIPredictionFilters: undefined; - AIPredictionStats: undefined; -}; - -// ============================================================================ -// UTILITY TYPES -// ============================================================================ - -/** - * Prediction Urgency Colors - * - * Purpose: Map urgency levels to UI colors - */ -export const URGENCY_COLORS = { - emergency: '#F44336', // Red - urgent: '#FF5722', // Deep Orange - moderate: '#FF9800', // Orange - low: '#FFC107', // Amber - routine: '#4CAF50', // Green -} as const; - -/** - * Prediction Severity Colors - * - * Purpose: Map severity levels to UI colors - */ -export const SEVERITY_COLORS = { - high: '#F44336', // Red - medium: '#FF9800', // Orange - low: '#FFC107', // Amber - none: '#4CAF50', // Green -} as const; - -/** - * Finding Category Colors - * - * Purpose: Map finding categories to UI colors - */ -export const CATEGORY_COLORS = { - critical: '#F44336', // Red - abnormal: '#FF9800', // Orange - warning: '#FFC107', // Amber - normal: '#4CAF50', // Green - unknown: '#9E9E9E', // Gray -} as const; - -/* - * End of File: aiPrediction.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/AIPrediction/types/index.ts b/app/modules/AIPrediction/types/index.ts deleted file mode 100644 index ea9fa52..0000000 --- a/app/modules/AIPrediction/types/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * File: index.ts - * Description: Type definitions exports for AI Prediction module - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -export * from './aiPrediction'; - -/* - * End of File: index.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/modules/Auth/index.ts b/app/modules/Auth/index.ts index 92afd0f..4863feb 100644 --- a/app/modules/Auth/index.ts +++ b/app/modules/Auth/index.ts @@ -12,11 +12,6 @@ export { default as SignUpScreen } from './screens/SignUpScreen'; // Export navigation export { AuthStackNavigator, - AuthStackParamList, - AuthNavigationProp, - AuthScreenProps, - LoginScreenProps, - SignUpScreenProps, navigateToLogin, navigateToSignUp, goBack, @@ -39,7 +34,7 @@ export { } from './components/signup'; // Export services -export { authAPI } from './services/signupAPI'; + // Export types export type { @@ -59,17 +54,8 @@ export type { // Export Redux export { - loginUser, - ssoLogin, - emergencyAccess, - logoutUser, clearError, - setBiometricEnabled, - setRememberDevice, updateUserProfile, - setSessionToken, - clearSession, - setEmergencyAccess, } from './redux/authSlice'; /* diff --git a/app/modules/Auth/redux/hospitalSlice.ts b/app/modules/Auth/redux/hospitalSlice.ts index 7ea2c08..98eecb3 100644 --- a/app/modules/Auth/redux/hospitalSlice.ts +++ b/app/modules/Auth/redux/hospitalSlice.ts @@ -54,7 +54,6 @@ export const fetchHospitals = createAsyncThunk( async (_, { rejectWithValue }) => { try { const response: any = await authAPI.gethospitals(); - console.log('hospital response', response); if (response.ok && response.data && response.data.data) { return response.data.data; diff --git a/app/modules/Auth/screens/LoginScreen.tsx b/app/modules/Auth/screens/LoginScreen.tsx index 259b9a5..5c6cf81 100644 --- a/app/modules/Auth/screens/LoginScreen.tsx +++ b/app/modules/Auth/screens/LoginScreen.tsx @@ -17,7 +17,6 @@ import { ScrollView, KeyboardAvoidingView, Alert, - Platform, Image, } from 'react-native'; import { useAppDispatch, useAppSelector } from '../../../store/hooks'; diff --git a/app/modules/Auth/screens/ResetPasswordScreen.tsx b/app/modules/Auth/screens/ResetPasswordScreen.tsx index 8e51741..c4b2ff7 100644 --- a/app/modules/Auth/screens/ResetPasswordScreen.tsx +++ b/app/modules/Auth/screens/ResetPasswordScreen.tsx @@ -463,7 +463,7 @@ export const ResetPasswordScreen: React.FC = ({ formData.append('id_photo', preparedFile as any); const response: any = await authAPI.uploadDocument(formData, user?.access_token); - console.log('upload response',response) + if (response.data && response.data.message) { if (response.data.success) { @@ -551,7 +551,7 @@ export const ResetPasswordScreen: React.FC = ({ token: user?.access_token, }); - console.log('reset response', response); + if (response.data && response.data.message) { if (response.data.success) { diff --git a/app/modules/Auth/screens/SignUpScreen.tsx b/app/modules/Auth/screens/SignUpScreen.tsx index 16db417..f12c99d 100644 --- a/app/modules/Auth/screens/SignUpScreen.tsx +++ b/app/modules/Auth/screens/SignUpScreen.tsx @@ -11,14 +11,12 @@ import { StyleSheet, Alert, Text, - TouchableOpacity, ScrollView, KeyboardAvoidingView, Platform } from 'react-native'; import { theme } from '../../../theme/theme'; import { useAppDispatch, useAppSelector } from '../../../store/hooks'; -import Icon from 'react-native-vector-icons/Feather'; // Import signup step components import EmailStep from '../components/signup/EmailStep'; @@ -129,7 +127,7 @@ const SignUpScreen: React.FC = ({ navigation }) => { try { const response :any = await authAPI.validatemail({email}); - console.log('response', response); + if(response.status==409&&response.data.message){ // Show modal instead of toast for already registered email @@ -167,7 +165,6 @@ const SignUpScreen: React.FC = ({ navigation }) => { try { const response:any = await authAPI.validateusername(username); - console.log('response', response); if(response.status==409&&response.data.message){ showError(response.data.message); @@ -221,7 +218,6 @@ const SignUpScreen: React.FC = ({ navigation }) => { * Purpose: Submit final signup data to API */ const onSignUpComplete = async (payload: SignUpData) => { - console.log('final payload', payload); setIsLoading(true); try { @@ -266,9 +262,7 @@ const SignUpScreen: React.FC = ({ navigation }) => { formData = createFormDataWithFile(formFields); } - console.log('payload prepared', formData); const response :any = await authAPI.signup(formData); - console.log('signup response', response); if(response.ok && response.data && response.data.success ) { //@ts-ignore @@ -338,7 +332,6 @@ const SignUpScreen: React.FC = ({ navigation }) => { * Purpose: Render the appropriate step component based on current step */ const renderCurrentStep = () => { - console.log('signupdate', signUpData); const commonProps = { onBack: handleBack, diff --git a/app/modules/Auth/services/biometricService.ts b/app/modules/Auth/services/biometricService.ts deleted file mode 100644 index 746d76a..0000000 --- a/app/modules/Auth/services/biometricService.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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. - */ \ No newline at end of file diff --git a/app/modules/Auth/services/index.ts b/app/modules/Auth/services/index.ts index c4028b5..eda1d7c 100644 --- a/app/modules/Auth/services/index.ts +++ b/app/modules/Auth/services/index.ts @@ -6,7 +6,6 @@ */ export * from './'; -export * from './biometricService'; /* * End of File: index.ts diff --git a/app/modules/Auth/services/signupAPI.ts b/app/modules/Auth/services/signupAPI.ts deleted file mode 100644 index aba2b12..0000000 --- a/app/modules/Auth/services/signupAPI.ts +++ /dev/null @@ -1,392 +0,0 @@ -/* - * File: signupAPI.ts - * Description: Signup API service with validation and hospital endpoints - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import { create } from 'apisauce'; -import { SignUpData, EmailValidationApiResponse, UsernameValidationApiResponse, HospitalListApiResponse, SignUpApiResponse } from '../types/signup'; - -// ============================================================================ -// API CONFIGURATION -// ============================================================================ - -/** - * API Base Configuration - * - * Purpose: Configure the base API client for signup operations - * - * Features: - * - Base URL configuration - * - Request/response interceptors - * - Error handling - * - Timeout settings - */ -const API_BASE_URL = 'https://api.neoscan-physician.com/v1'; // TODO: Replace with actual API URL - -const api = create({ - baseURL: API_BASE_URL, - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, - timeout: 30000, // 30 seconds -}); - -// ============================================================================ -// REQUEST INTERCEPTORS -// ============================================================================ - -/** - * Request Interceptor - * - * Purpose: Add authentication headers and logging - */ -api.addRequestTransform((request) => { - // Add any common headers here - console.log('API Request:', { - method: request.method, - url: request.url, - data: request.data, - }); -}); - -// ============================================================================ -// RESPONSE INTERCEPTORS -// ============================================================================ - -/** - * Response Interceptor - * - * Purpose: Handle common response patterns and errors - */ -api.addResponseTransform((response) => { - console.log('API Response:', { - status: response.status, - url: response.config?.url, - data: response.data, - }); - - // Handle common error patterns - if (response.status === 401) { - // Handle unauthorized access - console.error('Unauthorized access'); - } - - if (response.status === 500) { - // Handle server errors - console.error('Server error occurred'); - } -}); - -// ============================================================================ -// EMAIL VALIDATION API -// ============================================================================ - -/** - * Validate Email - * - * Purpose: Check if email is available for registration - * - * @param email - Email address to validate - * @returns Promise with validation result - */ -export const validatemail = async (email: string): Promise => { - try { - const response = await api.post('/auth/validate-email', { - email, - }); - - if (response.ok && response.data) { - return response.data; - } else { - throw new Error(response.problem || 'Failed to validate email'); - } - } catch (error) { - console.error('Email validation error:', error); - throw error; - } -}; - -// ============================================================================ -// USERNAME VALIDATION API -// ============================================================================ - -/** - * Validate Username - * - * Purpose: Check if username is available for registration - * - * @param username - Username to validate - * @returns Promise with validation result - */ -export const validateusername = async (username: string): Promise => { - try { - const response = await api.post('/auth/validate-username', { - username, - }); - - if (response.ok && response.data) { - return response.data; - } else { - throw new Error(response.problem || 'Failed to validate username'); - } - } catch (error) { - console.error('Username validation error:', error); - throw error; - } -}; - -// ============================================================================ -// HOSPITAL API -// ============================================================================ - -/** - * Get Hospitals List - * - * Purpose: Fetch list of available hospitals - * - * @param params - Query parameters for filtering - * @returns Promise with hospital list - */ -export const gethospitals = async (params?: { - page?: number; - limit?: number; - search?: string; - type?: string; - city?: string; - state?: string; -}): Promise => { - try { - const response = await api.get('/hospitals', params); - - if (response.ok && response.data) { - return response.data; - } else { - throw new Error(response.problem || 'Failed to fetch hospitals'); - } - } catch (error) { - console.error('Get hospitals error:', error); - throw error; - } -}; - -/** - * Get Hospital by ID - * - * Purpose: Fetch specific hospital details - * - * @param hospitalId - Hospital ID - * @returns Promise with hospital details - */ -export const getHospitalById = async (hospitalId: string) => { - try { - const response = await api.get(`/hospitals/${hospitalId}`); - - if (response.ok && response.data) { - return response.data; - } else { - throw new Error(response.problem || 'Failed to fetch hospital details'); - } - } catch (error) { - console.error('Get hospital by ID error:', error); - throw error; - } -}; - -// ============================================================================ -// SIGNUP API -// ============================================================================ - -/** - * Complete Signup - * - * Purpose: Submit complete signup data - * - * @param formData - FormData with signup information - * @returns Promise with signup result - */ -export const signup = async (formData: FormData): Promise => { - try { - const response = await api.post('/auth/signup', formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - }); -console.log('actual response ',response) - if (response.ok && response.data) { - return response.data; - } else { - throw new Error(response.problem || 'Failed to complete signup'); - } - } catch (error) { - console.error('Complete signup error:', error); - throw error; - } -}; - -// ============================================================================ -// MOCK API FUNCTIONS (FOR DEVELOPMENT) -// ============================================================================ - -/** - * Mock Validate Email - * - * Purpose: Mock email validation for development - * - * @param email - Email address to validate - * @returns Promise with mock validation result - */ -export const mockValidatemail = async (email: string): Promise => { - // Simulate API delay - await new Promise(resolve => setTimeout(resolve, 1000)); - - // Mock validation logic - const isAvailable = email !== 'existing@hospital.com'; - - return { - success: true, - isAvailable, - message: isAvailable ? 'Email is available' : 'Email is already registered', - suggestions: isAvailable ? undefined : ['Try a different email address'], - }; -}; - -/** - * Mock Validate Username - * - * Purpose: Mock username validation for development - * - * @param username - Username to validate - * @returns Promise with mock validation result - */ -export const mockValidateusername = async (username: string): Promise => { - // Simulate API delay - await new Promise(resolve => setTimeout(resolve, 800)); - - // Mock validation logic - const isAvailable = username !== 'existinguser'; - - return { - success: true, - isAvailable, - message: isAvailable ? 'Username is available' : 'Username is already taken', - suggestions: isAvailable ? undefined : ['Try adding numbers or special characters'], - }; -}; - -/** - * Mock Get Hospitals - * - * Purpose: Mock hospital list for development - * - * @returns Promise with mock hospital list - */ -export const mockGethospitals = async (): Promise => { - // Simulate API delay - await new Promise(resolve => setTimeout(resolve, 1500)); - - // Mock hospital data - const mockHospitals = [ - { - id: '1', - name: 'General Hospital', - address: '123 Main Street', - city: 'New York', - state: 'NY', - country: 'USA', - phoneNumber: '+1-555-0123', - email: 'info@generalhospital.com', - website: 'https://generalhospital.com', - type: 'GENERAL' as const, - specialties: ['Emergency Medicine', 'Cardiology', 'Neurology'], - isActive: true, - }, - { - id: '2', - name: 'University Medical Center', - address: '456 University Ave', - city: 'Boston', - state: 'MA', - country: 'USA', - phoneNumber: '+1-555-0456', - email: 'info@umc.edu', - website: 'https://umc.edu', - type: 'UNIVERSITY' as const, - specialties: ['Emergency Medicine', 'Trauma', 'Research'], - isActive: true, - }, - { - id: '3', - name: 'Specialty Medical Center', - address: '789 Specialty Blvd', - city: 'Los Angeles', - state: 'CA', - country: 'USA', - phoneNumber: '+1-555-0789', - email: 'info@specialtycenter.com', - website: 'https://specialtycenter.com', - type: 'SPECIALTY' as const, - specialties: ['Cardiology', 'Neurology', 'Oncology'], - isActive: true, - }, - ]; - - return { - success: true, - data: mockHospitals, - total: mockHospitals.length, - page: 1, - limit: 10, - }; -}; - -/** - * Mock Complete Signup - * - * Purpose: Mock signup completion for development - * - * @param signUpData - Complete signup data - * @returns Promise with mock signup result - */ -export const mockSignup = async (signUpData: SignUpData): Promise => { - // Simulate API delay - await new Promise(resolve => setTimeout(resolve, 2000)); - - // Mock successful signup - return { - success: true, - message: 'Account created successfully', - data: { - userId: 'user_' + Date.now(), - email: signUpData.email, - token: 'mock_token_' + Date.now(), - }, - }; -}; - -// ============================================================================ -// EXPORTS -// ============================================================================ - -export const authAPI = { - // Real API functions - validatemail, - validateusername, - gethospitals, - getHospitalById, - signup, - - // Mock API functions (for development) - mockValidatemail, - mockValidateusername, - mockGethospitals, - mockSignup, -}; - -/* - * End of File: signupAPI.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ \ No newline at end of file diff --git a/app/modules/Dashboard/components/BrainPredictionsOverview.tsx b/app/modules/Dashboard/components/BrainPredictionsOverview.tsx deleted file mode 100644 index f124c79..0000000 --- a/app/modules/Dashboard/components/BrainPredictionsOverview.tsx +++ /dev/null @@ -1,613 +0,0 @@ -/* - * File: BrainPredictionsOverview.tsx - * Description: Component to display patient overview statistics based on different AI prediction scenarios for brain conditions - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React from 'react'; -import { - View, - Text, - StyleSheet, - Dimensions, - ScrollView, -} from 'react-native'; -import { PieChart } from 'react-native-chart-kit'; -import { theme } from '../../../theme/theme'; -import { ERDashboard } from '../../../shared/types'; - -/** - * BrainPredictionsOverviewProps Interface - * - * Purpose: Defines the props required by the BrainPredictionsOverview component - * - * Props: - * - dashboard: ERDashboard object containing brain prediction statistics - */ -interface BrainPredictionsOverviewProps { - dashboard: ERDashboard; -} - -/** - * BrainPredictionsOverview Component - * - * Purpose: Display patient overview statistics based on different AI prediction scenarios - * - * Features: - * 1. Shows distribution of patients by brain condition predictions - * 2. Displays AI confidence levels for each prediction type - * 3. Shows critical vs non-critical brain conditions - * 4. Provides summary statistics for quick overview - * 5. Visualizes data using react-native-chart-kit pie chart - * - * Prediction Categories: - * - Hemorrhagic Conditions (IPH, IVH, SAH, SDH, EDH) - * - Ischemic Conditions (Stroke) - * - Structural Changes (Mass effect, Midline shift) - * - Normal Findings - */ -export const BrainPredictionsOverview: React.FC = ({ - dashboard, -}) => { - // ============================================================================ - // MOCK PREDICTION DATA - // ============================================================================ - - /** - * Mock prediction statistics based on brain conditions - * In a real app, this would come from the AI analysis API - */ - const predictionStats = { - // Hemorrhagic Conditions - intraparenchymal: { count: 2, confidence: 94, critical: true }, - intraventricular: { count: 1, confidence: 87, critical: true }, - subarachnoid: { count: 1, confidence: 87, critical: true }, - subdural: { count: 1, confidence: 89, critical: true }, - epidural: { count: 1, confidence: 96, critical: true }, - - // Ischemic Conditions - ischemic_stroke: { count: 1, confidence: 92, critical: true }, - - // Structural Changes - mass_effect: { count: 2, confidence: 91, critical: true }, - midline_shift: { count: 1, confidence: 96, critical: true }, - - // Normal Findings - normal_brain: { count: 3, confidence: 98, critical: false }, - - // Pending Analysis - pending_analysis: { count: 6, confidence: 0, critical: false }, - }; - - // ============================================================================ - // HELPER FUNCTIONS - // ============================================================================ - - /** - * getPredictionLabel Function - * - * Purpose: Get human-readable label for prediction type - * - * @param predictionType - Type of brain prediction - * @returns Human-readable label - */ - const getPredictionLabel = (predictionType: string) => { - if (!predictionType) { - return 'Unknown'; // Default label for undefined types - } - - const labels: { [key: string]: string } = { - intraparenchymal: 'IPH', - intraventricular: 'IVH', - subarachnoid: 'SAH', - subdural: 'SDH', - epidural: 'EDH', - ischemic_stroke: 'Stroke', - mass_effect: 'Mass Effect', - midline_shift: 'Midline Shift', - normal_brain: 'Normal', - pending_analysis: 'Pending', - }; - - return labels[predictionType] || predictionType; - }; - - /** - * getPredictionColor Function - * - * Purpose: Get color based on prediction type and criticality - * - * @param predictionType - Type of brain prediction - * @param isCritical - Whether the condition is critical - * @returns Color string for styling - */ - const getPredictionColor = (predictionType: string, isCritical: boolean) => { - if (!predictionType) { - return theme.colors.textSecondary; // Default color for undefined types - } - - // Define distinct colors for each prediction class - const predictionColors: { [key: string]: string } = { - intraparenchymal: '#E53E3E', // Red for IPH - intraventricular: '#C53030', // Dark Red for IVH - subarachnoid: '#F56565', // Light Red for SAH - subdural: '#FC8181', // Pink Red for SDH - epidural: '#E53E3E', // Red for EDH - ischemic_stroke: '#3182CE', // Blue for Stroke - mass_effect: '#805AD5', // Purple for Mass Effect - midline_shift: '#553C9A', // Dark Purple for Midline Shift - normal_brain: '#38A169', // Green for Normal - pending_analysis: '#D69E2E', // Yellow for Pending - }; - - return predictionColors[predictionType] || theme.colors.textSecondary; - }; - - // ============================================================================ - // COMPUTED VALUES - // ============================================================================ - - /** - * Calculate total patients - */ - const totalPatients = Object.values(predictionStats).reduce((sum, stats) => sum + stats.count, 0); - - /** - * Calculate total critical conditions - */ - const totalCritical = Object.values(predictionStats) - .filter(stats => stats.critical) - .reduce((sum, stats) => sum + stats.count, 0); - - /** - * Calculate total normal findings - */ - const totalNormal = predictionStats.normal_brain.count; - - /** - * Calculate total pending analysis - */ - const totalPending = predictionStats.pending_analysis.count; - - /** - * Calculate average confidence for critical conditions - */ - const criticalConditions = Object.values(predictionStats).filter(stats => stats.critical && stats.confidence > 0); - const averageConfidence = criticalConditions.length > 0 - ? Math.round(criticalConditions.reduce((sum, stats) => sum + stats.confidence, 0) / criticalConditions.length) - : 0; - - /** - * Get all prediction classes by count - */ - const allPredictions = Object.entries(predictionStats) - .filter(([_, stats]) => stats.count > 0) - .sort((a, b) => b[1].count - a[1].count); - - // ============================================================================ - // CHART DATA PREPARATION - // ============================================================================ - - /** - * Prepare chart data for react-native-chart-kit PieChart - */ - const chartData = Object.entries(predictionStats) - .filter(([_, stats]) => stats && stats.count > 0) // Filter out undefined or zero count entries - .map(([predictionType, stats]) => ({ - name: getPredictionLabel(predictionType), - population: stats.count, // react-native-chart-kit uses 'population' property - color: getPredictionColor(predictionType, stats.critical), - legendFontColor: theme.colors.textPrimary, - legendFontSize: 12, - confidence: stats.confidence, - isCritical: stats.critical, - })) - .filter(item => item.population > 0) // Additional filter to ensure no zero count items - .sort((a, b) => b.population - a.population); // Sort by count descending - removed limit to show all classes - - /** - * Chart configuration for react-native-chart-kit - */ - const chartConfig = { - backgroundColor: theme.colors.background, - backgroundGradientFrom: theme.colors.background, - backgroundGradientTo: theme.colors.background, - decimalPlaces: 0, - color: (opacity = 1) => `rgba(33, 150, 243, ${opacity})`, - labelColor: (opacity = 1) => `rgba(33, 33, 33, ${opacity})`, - style: { - borderRadius: 16, - }, - propsForDots: { - r: '6', - strokeWidth: '2', - stroke: theme.colors.primary, - }, - useShadowColorFromDataset: false, - }; - - // ============================================================================ - // RENDER FUNCTIONS - // ============================================================================ - - - - /** - * renderLegend Function - * - * Purpose: Render chart legend with confidence levels in grid layout - * - * @returns Legend component - */ - const renderLegend = () => ( - - Prediction Classes Breakdown - - {chartData.map((item, index) => ( - - - - {item.name} - - {item.population} patients - {item.confidence > 0 && ( - {item.confidence}% - )} - - ))} - - - ); - - // ============================================================================ - // MAIN RENDER - // ============================================================================ - return ( - - {/* Section Header */} - - Brain AI Predictions Overview - - Patient distribution by AI-detected conditions - - - - {/* Pie Chart Section */} - - - - {chartData.length > 0 ? ( - - - {(() => { - try { - return ( - - ); - } catch (error) { - console.warn('PieChart error:', error); - return ( - - Chart temporarily unavailable - - ); - } - })()} - - {renderLegend()} - - ) : ( - - No data available for chart - - )} - - - - {/* Summary Statistics */} - - - Total Patients - {totalPatients} - - - Critical Cases - - {totalCritical} - - - - Pending Analysis - - {totalPending} - - - - Avg Confidence - - {averageConfidence}% - - - - - ); -}; - -// ============================================================================ -// STYLES SECTION -// ============================================================================ - -const styles = StyleSheet.create({ - // Main container - container: { - backgroundColor: theme.colors.background, - borderRadius: theme.borderRadius.large, - padding: theme.spacing.md, - marginVertical: theme.spacing.sm, - borderWidth: 1, - borderColor: theme.colors.border, - }, - - // Header section - header: { - // marginBottom: theme.spacing.lg, - }, - - // Main title - title: { - fontSize: theme.typography.fontSize.displaySmall, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.textPrimary, - marginBottom: theme.spacing.xs, - }, - - // Subtitle - subtitle: { - fontSize: theme.typography.fontSize.bodyMedium, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textSecondary, - }, - - // Statistics grid - statsGrid: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: theme.spacing.xs, - marginBottom: theme.spacing.lg, - }, - - // Individual stat card - statCard: { - flex: 1, - minWidth: '30%', - maxWidth: '32%', - backgroundColor: theme.colors.background, - borderRadius: theme.borderRadius.medium, - padding: theme.spacing.sm, - borderWidth: 1, - borderColor: theme.colors.border, - borderLeftWidth: 3, - alignItems: 'center', - }, - - // Stat value - statValue: { - fontSize: theme.typography.fontSize.displaySmall, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.textPrimary, - marginBottom: theme.spacing.xs, - }, - - // Stat title - statTitle: { - fontSize: theme.typography.fontSize.bodySmall, - fontFamily: theme.typography.fontFamily.semibold, - color: theme.colors.textPrimary, - textAlign: 'center', - marginBottom: theme.spacing.xs, - }, - - // Stat subtitle - statSubtitle: { - fontSize: theme.typography.fontSize.caption, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textSecondary, - textAlign: 'center', - }, - - // Chart section - chartSection: { - marginBottom: theme.spacing.lg, - }, - - // Chart title - chartTitle: { - fontSize: theme.typography.fontSize.bodyLarge, - fontFamily: theme.typography.fontFamily.semibold, - color: theme.colors.textPrimary, - marginBottom: theme.spacing.xs, - }, - - // Chart subtitle - chartSubtitle: { - fontSize: theme.typography.fontSize.bodySmall, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textSecondary, - marginBottom: theme.spacing.md, - }, - - // Chart scroll view - chartScrollView: { - // Removed maxHeight to allow full content to be visible - }, - - // Chart content - chartContent: { - alignItems: 'center', - paddingHorizontal: theme.spacing.sm, - }, - - // Pie chart container - pieChartContainer: { - alignItems: 'center', - backgroundColor: theme.colors.background, - borderRadius: theme.borderRadius.medium, - }, - - // Legend container - legendContainer: { - width: '100%', - paddingHorizontal: theme.spacing.sm, - backgroundColor: theme.colors.backgroundAlt, - borderRadius: theme.borderRadius.medium, - padding: theme.spacing.md, - }, - - // Legend title - legendTitle: { - fontSize: theme.typography.fontSize.bodyLarge, - fontFamily: theme.typography.fontFamily.semibold, - color: theme.colors.textPrimary, - marginBottom: theme.spacing.md, - textAlign: 'center', - }, - - // Legend grid container - legendGrid: { - flexDirection: 'row', - flexWrap: 'wrap', - gap: theme.spacing.sm, - }, - - // Legend grid item - legendGridItem: { - width: '48%', - backgroundColor: theme.colors.background, - borderRadius: theme.borderRadius.small, - padding: theme.spacing.sm, - borderWidth: 1, - borderColor: theme.colors.border, - marginBottom: theme.spacing.xs, - }, - - // Legend item header - legendItemHeader: { - flexDirection: 'row', - alignItems: 'center', - marginBottom: theme.spacing.xs, - }, - - // Legend color - legendColor: { - width: 12, - height: 12, - borderRadius: 6, - marginRight: theme.spacing.xs, - }, - - // Legend label - legendLabel: { - fontSize: theme.typography.fontSize.bodySmall, - fontFamily: theme.typography.fontFamily.semibold, - color: theme.colors.textPrimary, - flex: 1, - }, - - // Legend count - legendCount: { - fontSize: theme.typography.fontSize.caption, - fontFamily: theme.typography.fontFamily.medium, - color: theme.colors.textPrimary, - marginBottom: theme.spacing.xs, - }, - - // Legend confidence - legendConfidence: { - fontSize: theme.typography.fontSize.caption, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textSecondary, - }, - - // No data container - noDataContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - padding: theme.spacing.md, - }, - - // No data text - noDataText: { - fontSize: theme.typography.fontSize.bodyMedium, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textSecondary, - }, - - // Summary container - summaryContainer: { - flexDirection: 'row', - justifyContent: 'space-around', - paddingTop: theme.spacing.md, - borderTopWidth: 1, - borderTopColor: theme.colors.border, - }, - - // Summary item - summaryItem: { - alignItems: 'center', - }, - - // Summary label - summaryLabel: { - fontSize: theme.typography.fontSize.bodySmall, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textSecondary, - marginBottom: theme.spacing.xs, - }, - - // Summary value - summaryValue: { - fontSize: theme.typography.fontSize.displaySmall, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.textPrimary, - }, - - // Error container - errorContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - padding: theme.spacing.md, - backgroundColor: theme.colors.backgroundAlt, - borderRadius: theme.borderRadius.medium, - }, - - // Error text - errorText: { - fontSize: theme.typography.fontSize.bodyMedium, - fontFamily: theme.typography.fontFamily.regular, - color: theme.colors.textSecondary, - textAlign: 'center', - }, -}); - -/* - * End of File: BrainPredictionsOverview.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ \ No newline at end of file diff --git a/app/modules/Dashboard/components/PatientCard.tsx b/app/modules/Dashboard/components/PatientCard.tsx deleted file mode 100644 index 1a04ec6..0000000 --- a/app/modules/Dashboard/components/PatientCard.tsx +++ /dev/null @@ -1,362 +0,0 @@ -/* - * File: PatientCard.tsx - * Description: Patient card component displaying patient information and status - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React from 'react'; -import { - View, - Text, - TouchableOpacity, - StyleSheet, -} from 'react-native'; -import { theme } from '../../../theme/theme'; -import { Patient } from '../../../shared/types/patient'; -import { getPriorityColor, getStatusColor, calculateAge } from '../../../shared/utils/helpers'; - -/** - * PatientCardProps Interface - * - * Purpose: Defines the props required by the PatientCard component - * - * Props: - * - patient: Patient data object containing all patient information - * - onPress: Callback function triggered when the card is pressed - */ -interface PatientCardProps { - patient: Patient; - onPress: (patient: Patient) => void; -} - -/** - * PatientCard Component - * - * Purpose: Display comprehensive patient information in a compact, touchable card format - * - * Features: - * 1. Patient identification (name, age, gender, MRN) - * 2. Priority and status badges with color coding - * 3. Location information (bed, room, attending physician) - * 4. Real-time vital signs display - * 5. Allergy warnings (if any) - * 6. Current diagnosis and last update time - * - * Data Display: - * - Header: Patient name, demographics, and status badges - * - Location: Bed/room assignment and attending physician - * - Vital Signs: BP, HR, Temperature, Oxygen saturation - * - Allergies: Warning display for known allergies - * - Footer: Current diagnosis and timestamp - * - * Interaction: - * - Touchable card that navigates to detailed patient view - * - Visual feedback with activeOpacity for better UX - */ -export const PatientCard: React.FC = ({ - patient, - onPress, -}) => { - // ============================================================================ - // DATA PROCESSING - // ============================================================================ - - // Calculate patient age from date of birth - const age = calculateAge(patient.dateOfBirth); - - // Get color coding for priority and status badges - const priorityColor = getPriorityColor(patient.priority); // Color based on priority level - const statusColor = getStatusColor(patient.status); // Color based on patient status - - /** - * formatVitalSigns Function - * - * Purpose: Format vital signs data for display in the card - * - * Returns: - * - bp: Blood pressure in systolic/diastolic format - * - hr: Heart rate value - * - temp: Temperature in Celsius - * - o2: Oxygen saturation percentage - */ - const formatVitalSigns = () => { - const { vitalSigns } = patient; - return { - bp: `${vitalSigns.bloodPressure.systolic}/${vitalSigns.bloodPressure.diastolic}`, // Format: 120/80 - hr: vitalSigns.heartRate.value, // Heart rate: 75 - temp: vitalSigns.temperature.value, // Temperature: 37.2 - o2: vitalSigns.oxygenSaturation.value, // Oxygen: 98 - }; - }; - - // Format vital signs for display - const vitals = formatVitalSigns(); - - // ============================================================================ - // RENDER SECTION - // ============================================================================ - - return ( - onPress(patient)} // Navigate to patient details - activeOpacity={0.7} // Visual feedback on touch - > - {/* ======================================================================== - * HEADER SECTION - Patient identification and status badges - * ======================================================================== */} - - {/* Patient information section */} - - {/* Patient full name */} - - {patient.firstName} {patient.lastName} - - {/* Patient demographics and MRN */} - - {age} years โ€ข {patient.gender} โ€ข MRN: {patient.mrn} - - - - {/* Status badges section */} - - {/* Priority badge with color coding */} - - - {patient.priority} - - - {/* Status badge with color coding */} - - - {patient.status} - - - - - - {/* ======================================================================== - * LOCATION SECTION - Bed assignment and attending physician - * ======================================================================== */} - - {/* Bed and room location */} - - Bed {patient.bedNumber} โ€ข Room {patient.roomNumber} - - {/* Attending physician */} - - Dr. {patient.attendingPhysician} - - - - {/* ======================================================================== - * VITAL SIGNS SECTION - Real-time patient vitals - * ======================================================================== */} - - {/* Blood Pressure */} - - BP - {vitals.bp} - - {/* Heart Rate */} - - HR - {vitals.hr} - - {/* Temperature */} - - Temp - {vitals.temp}ยฐC - - {/* Oxygen Saturation */} - - Oโ‚‚ - {vitals.o2}% - - - - {/* ======================================================================== - * ALLERGIES SECTION - Warning display for known allergies - * ======================================================================== */} - {patient.allergies.length > 0 && ( - - Allergies: - - {patient.allergies.map(a => a.name).join(', ')} - - - )} - - {/* ======================================================================== - * FOOTER SECTION - Diagnosis and last update time - * ======================================================================== */} - - {/* Current diagnosis */} - - {patient.currentDiagnosis} - - {/* Last update timestamp */} - - Updated {new Date(patient.lastUpdated).toLocaleTimeString()} - - - - ); -}; - -// ============================================================================ -// STYLES SECTION -// ============================================================================ - -const styles = StyleSheet.create({ - // Main card container with shadow and rounded corners - container: { - backgroundColor: theme.colors.cardBackground, - borderRadius: theme.borderRadius.large, - padding: theme.spacing.md, - marginHorizontal: theme.spacing.md, - marginVertical: theme.spacing.sm, - ...theme.shadows.small, // Add subtle shadow for elevation - }, - - // Header section with patient info and badges - header: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'flex-start', - marginBottom: theme.spacing.sm, - }, - - // Patient information container - patientInfo: { - flex: 1, // Take available space - }, - - // Patient name styling - patientName: { - fontSize: theme.typography.fontSize.bodyLarge, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.textPrimary, - marginBottom: theme.spacing.xs, - }, - - // Patient details styling (age, gender, MRN) - patientDetails: { - fontSize: theme.typography.fontSize.bodySmall, - color: theme.colors.textSecondary, - }, - - // Badges container for priority and status - badges: { - flexDirection: 'row', - gap: theme.spacing.xs, // Space between badges - }, - - // Individual badge styling - badge: { - paddingHorizontal: theme.spacing.sm, - paddingVertical: theme.spacing.xs, - borderRadius: theme.borderRadius.small, - }, - - // Badge text styling - badgeText: { - fontSize: theme.typography.fontSize.caption, - fontFamily: theme.typography.fontFamily.medium, - }, - - // Location information container - locationInfo: { - marginBottom: theme.spacing.sm, - }, - - // Bed and room location text - locationText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textPrimary, - fontFamily: theme.typography.fontFamily.medium, - }, - - // Attending physician text - attendingText: { - fontSize: theme.typography.fontSize.bodySmall, - color: theme.colors.textSecondary, - }, - - // Vital signs container with background - vitalSigns: { - flexDirection: 'row', - justifyContent: 'space-between', - backgroundColor: theme.colors.backgroundAccent, // Light background for vitals - borderRadius: theme.borderRadius.medium, - padding: theme.spacing.sm, - marginBottom: theme.spacing.sm, - }, - - // Individual vital sign item - vitalItem: { - alignItems: 'center', // Center align label and value - }, - - // Vital sign label (BP, HR, Temp, Oโ‚‚) - vitalLabel: { - fontSize: theme.typography.fontSize.caption, - color: theme.colors.textSecondary, - fontFamily: theme.typography.fontFamily.medium, - marginBottom: theme.spacing.xs, - }, - - // Vital sign value styling - vitalValue: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textPrimary, - fontFamily: theme.typography.fontFamily.bold, - }, - - // Allergies container - allergiesContainer: { - marginBottom: theme.spacing.sm, - }, - - // Allergies label with warning color - allergiesLabel: { - fontSize: theme.typography.fontSize.bodySmall, - color: theme.colors.warning, // Warning color for allergies - fontFamily: theme.typography.fontFamily.medium, - marginBottom: theme.spacing.xs, - }, - - // Allergies text listing - allergiesText: { - fontSize: theme.typography.fontSize.bodySmall, - color: theme.colors.textSecondary, - }, - - // Footer container with diagnosis and timestamp - footer: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - }, - - // Current diagnosis text - diagnosisText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textPrimary, - fontFamily: theme.typography.fontFamily.medium, - flex: 1, // Take available space - }, - - // Last update timestamp - timeText: { - fontSize: theme.typography.fontSize.caption, - color: theme.colors.textMuted, - }, -}); - -/* - * End of File: PatientCard.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ \ No newline at end of file diff --git a/app/modules/Dashboard/components/QuickActions.tsx b/app/modules/Dashboard/components/QuickActions.tsx deleted file mode 100644 index 5d029a1..0000000 --- a/app/modules/Dashboard/components/QuickActions.tsx +++ /dev/null @@ -1,139 +0,0 @@ -/* - * File: QuickActions.tsx - * Description: Quick actions component providing common emergency actions - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React from 'react'; -import { - View, - Text, - TouchableOpacity, - StyleSheet, - ScrollView, -} from 'react-native'; -import { theme } from '../../../theme/theme'; - -interface QuickAction { - id: string; - title: string; - icon: string; - color: string; -} - -interface QuickActionsProps { - onQuickAction: (action: QuickAction) => void; -} - -const quickActions: QuickAction[] = [ - { - id: 'emergency', - title: 'Emergency', - icon: '๐Ÿšจ', - color: theme.colors.critical, - }, - { - id: 'scan', - title: 'Order Scan', - icon: '๐Ÿ“ท', - color: theme.colors.primary, - }, - { - id: 'medication', - title: 'Medication', - icon: '๐Ÿ’Š', - color: theme.colors.warning, - }, - { - id: 'lab', - title: 'Lab Work', - icon: '๐Ÿงช', - color: theme.colors.info, - }, - { - id: 'consult', - title: 'Consult', - icon: '๐Ÿ‘จโ€โš•๏ธ', - color: theme.colors.success, - }, - { - id: 'transfer', - title: 'Transfer', - icon: '๐Ÿš‘', - color: theme.colors.secondary, - }, -]; - -export const QuickActions: React.FC = ({ - onQuickAction, -}) => { - return ( - - Quick Actions - - {quickActions.map((action) => ( - onQuickAction(action)} - activeOpacity={0.7} - > - - {action.icon} - - {action.title} - - ))} - - - ); -}; - -const styles = StyleSheet.create({ - container: { - marginBottom: theme.spacing.lg, - }, - title: { - fontSize: theme.typography.fontSize.displaySmall, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.textPrimary, - marginBottom: theme.spacing.md, - }, - scrollContainer: { - paddingRight: theme.spacing.md, - }, - actionButton: { - alignItems: 'center', - marginRight: theme.spacing.lg, - minWidth: 80, - }, - iconContainer: { - width: 60, - height: 60, - borderRadius: 30, - justifyContent: 'center', - alignItems: 'center', - marginBottom: theme.spacing.sm, - ...theme.shadows.small, - }, - icon: { - fontSize: 24, - }, - actionTitle: { - fontSize: theme.typography.fontSize.bodySmall, - color: theme.colors.textPrimary, - fontFamily: theme.typography.fontFamily.medium, - textAlign: 'center', - }, -}); - -/* - * End of File: QuickActions.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ \ No newline at end of file diff --git a/app/modules/Dashboard/components/index.ts b/app/modules/Dashboard/components/index.ts index f893da3..5306ebf 100644 --- a/app/modules/Dashboard/components/index.ts +++ b/app/modules/Dashboard/components/index.ts @@ -1,7 +1,17 @@ -export { PatientCard } from './PatientCard'; -export { CriticalAlerts } from './CriticalAlerts'; +/* + * File: index.ts + * Description: Dashboard components exports + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ + export { DashboardHeader } from './DashboardHeader'; -export { QuickActions } from './QuickActions'; +export { CriticalAlerts } from './CriticalAlerts'; export { DepartmentStats } from './DepartmentStats'; -export { BrainPredictionsOverview } from './BrainPredictionsOverview'; -export { FeedbackAnalysisPieChart } from './FeedbackAnalysisPieChart'; \ No newline at end of file +export { FeedbackAnalysisPieChart } from './FeedbackAnalysisPieChart'; + +/* + * End of File: index.ts + * Design & Developed by Tech4Biz Solutions + * Copyright (c) Spurrin Innovations. All rights reserved. + */ \ No newline at end of file diff --git a/app/modules/Dashboard/index.ts b/app/modules/Dashboard/index.ts index 0d0feb1..8d1835c 100644 --- a/app/modules/Dashboard/index.ts +++ b/app/modules/Dashboard/index.ts @@ -6,19 +6,11 @@ */ // Export screens -export { default as DashboardScreen } from './screens/DashboardScreen'; +export { DashboardScreen } from './screens/DashboardScreen'; // Export navigation export { DashboardStackNavigator, - DashboardStackParamList, - DashboardNavigationProp, - DashboardScreenProps, - DashboardScreenProps, - PatientDetailsScreenProps, - AlertDetailsScreenProps, - DepartmentStatsScreenProps, - QuickActionsScreenProps, navigateToERDashboard, navigateToPatientDetails, navigateToAlertDetails, @@ -32,12 +24,19 @@ export { navigateToAlertDetailsAndClearStack, } from './navigation'; +// Export navigation types +export type { + DashboardStackParamList, + DashboardNavigationProp, + DashboardScreenProps, + PatientDetailsScreenProps, + AlertDetailsScreenProps, + DepartmentStatsScreenProps, + QuickActionsScreenProps, +} from './navigation'; + // Export components -export { default as PatientCard } from './components/PatientCard'; -export { default as CriticalAlerts } from './components/CriticalAlerts'; -export { default as DashboardHeader } from './components/DashboardHeader'; -export { default as QuickActions } from './components/QuickActions'; -export { default as DepartmentStats } from './components/DepartmentStats'; +export { CriticalAlerts, DashboardHeader, DepartmentStats } from './components'; // Export hooks export * from './hooks'; @@ -84,39 +83,6 @@ export { selectTimeAnalysis, } from './redux/aiDashboardSelectors'; -export { - fetchAlerts, - acknowledgeAlert, - markAlertAsRead, - clearError as clearAlertsError, - setFilter as setAlertsFilter, - setSort as setAlertsSort, - addAlert, - removeAlert, - updateAlert, - clearAllAlerts, - markAllAsRead, -} from './redux/alertsSlice'; - -export { - setLoading, - showModal, - hideModal, - showOverlay, - hideOverlay, - setCurrentScreen, - clearNavigationStack, - toggleDarkMode, - setFontSize, - toggleHighContrast, - setRefreshing, - setScrolling, - updateLastInteraction, - showError, - clearError as clearUIError, - resetUIState, -} from './redux/uiSlice'; - /* * End of File: index.ts * Design & Developed by Tech4Biz Solutions diff --git a/app/modules/Dashboard/redux/aiDashboardSlice.ts b/app/modules/Dashboard/redux/aiDashboardSlice.ts index 9309a3d..a471577 100644 --- a/app/modules/Dashboard/redux/aiDashboardSlice.ts +++ b/app/modules/Dashboard/redux/aiDashboardSlice.ts @@ -7,6 +7,7 @@ import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; import { dashboardAPI } from '../services/dashboardAPI'; +import { logoutUser } from '../../Auth/redux/authActions'; // ============================================================================ // TYPES @@ -123,10 +124,16 @@ export interface AIDashboardState { */ export const fetchAIDashboardStatistics = createAsyncThunk( 'aiDashboard/fetchStatistics', - async (token: string, { rejectWithValue }) => { + async (token: string, { rejectWithValue, dispatch }) => { try { const response :any = await dashboardAPI.getDashboardStatistics(token); - console.log('statistics response',response); + + // Check for 401 Unauthorized response + if (response.status === 401) { + // Dispatch logout action to clear authentication state + dispatch(logoutUser()); + return rejectWithValue('Session expired. Please login again.'); + } if (response.ok && response.data ) { return response.data as AIDashboardData; @@ -149,10 +156,17 @@ export const fetchAIDashboardStatistics = createAsyncThunk( */ export const refreshAIDashboardStatistics = createAsyncThunk( 'aiDashboard/refreshStatistics', - async (token: string, { rejectWithValue }) => { + async (token: string, { rejectWithValue, dispatch }) => { try { const response = await dashboardAPI.getDashboardStatistics(token); + // Check for 401 Unauthorized response + if (response.status === 401) { + // Dispatch logout action to clear authentication state + dispatch(logoutUser()); + return rejectWithValue('Session expired. Please login again.'); + } + if (response.ok && response.data) { return response.data as AIDashboardData; } else { @@ -174,10 +188,17 @@ export const refreshAIDashboardStatistics = createAsyncThunk( */ export const fetchTimeBasedAnalysis = createAsyncThunk( 'aiDashboard/fetchTimeAnalysis', - async (params: { token: string; timeRange: 'today' | 'week' | 'month' | 'year' }, { rejectWithValue }) => { + async (params: { token: string; timeRange: 'today' | 'week' | 'month' | 'year' }, { rejectWithValue, dispatch }) => { try { const response = await dashboardAPI.getTimeBasedAnalysis(params.token, params.timeRange); + // Check for 401 Unauthorized response + if (response.status === 401) { + // Dispatch logout action to clear authentication state + dispatch(logoutUser()); + return rejectWithValue('Session expired. Please login again.'); + } + if (response.ok && response.data) { return response.data as AIDashboardData; } else { diff --git a/app/modules/Dashboard/redux/alertsSlice.ts b/app/modules/Dashboard/redux/alertsSlice.ts deleted file mode 100644 index beaf0ee..0000000 --- a/app/modules/Dashboard/redux/alertsSlice.ts +++ /dev/null @@ -1,341 +0,0 @@ -/* - * File: alertsSlice.ts - * Description: Alerts state management slice - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit'; -import { AlertType, AlertsState } from '../../../shared/types'; - -// ============================================================================ -// ASYNC THUNKS -// ============================================================================ - -/** - * Fetch Alerts Async Thunk - * - * Purpose: Fetch alerts from API - * - * @returns Promise with alerts data or error - */ -export const fetchAlerts = createAsyncThunk( - 'alerts/fetchAlerts', - async (_, { rejectWithValue }) => { - try { - // TODO: Replace with actual API call - await new Promise(resolve => setTimeout(resolve, 1000)); - - // Mock alerts data - const mockAlerts: AlertType[] = [ - { - id: '1', - type: 'CRITICAL_FINDING', - priority: 'CRITICAL', - title: 'Critical Finding Detected', - message: 'AI has detected a potential brain bleed in CT scan. Immediate review required.', - patientId: '1', - patientName: 'John Doe', - bedNumber: 'A1', - timestamp: new Date(), - isRead: false, - isAcknowledged: false, - actionRequired: true, - }, - { - id: '2', - type: 'VITAL_SIGNS_ALERT', - priority: 'HIGH', - title: 'Vital Signs Alert', - message: 'Patient vitals showing concerning trends. Blood pressure elevated.', - patientId: '2', - patientName: 'Jane Smith', - bedNumber: 'B2', - timestamp: new Date(Date.now() - 300000), // 5 minutes ago - isRead: true, - isAcknowledged: true, - actionRequired: false, - }, - ]; - - return mockAlerts; - } catch (error) { - return rejectWithValue('Failed to fetch alerts.'); - } - } -); - -/** - * Acknowledge Alert Async Thunk - * - * Purpose: Acknowledge an alert - * - * @param alertId - ID of the alert to acknowledge - * @returns Promise with success or error - */ -export const acknowledgeAlert = createAsyncThunk( - 'alerts/acknowledgeAlert', - async (alertId: string, { rejectWithValue }) => { - try { - // TODO: Replace with actual API call - await new Promise(resolve => setTimeout(resolve, 500)); - return alertId; - } catch (error) { - return rejectWithValue('Failed to acknowledge alert.'); - } - } -); - -/** - * Mark Alert as Read Async Thunk - * - * Purpose: Mark an alert as read - * - * @param alertId - ID of the alert to mark as read - * @returns Promise with success or error - */ -export const markAlertAsRead = createAsyncThunk( - 'alerts/markAlertAsRead', - async (alertId: string, { rejectWithValue }) => { - try { - // TODO: Replace with actual API call - await new Promise(resolve => setTimeout(resolve, 300)); - return alertId; - } catch (error) { - return rejectWithValue('Failed to mark alert as read.'); - } - } -); - -// ============================================================================ -// INITIAL STATE -// ============================================================================ - -/** - * Initial Alerts State - * - * Purpose: Define the initial state for alerts - * - * Features: - * - Alerts list and management - * - Loading states for async operations - * - Error handling and messages - * - Real-time updates tracking - */ -const initialState: AlertsState = { - // Alerts data - alerts: [], - - // Loading states - isLoading: false, - isRefreshing: false, - - // Error handling - error: null, - - // Real-time updates - lastUpdated: null, - unreadCount: 0, - criticalCount: 0, - - // Filters and preferences - selectedFilter: 'all', - sortBy: 'timestamp', - sortOrder: 'desc', -}; - -// ============================================================================ -// ALERTS SLICE -// ============================================================================ - -/** - * Alerts Slice - * - * Purpose: Redux slice for alerts state management - * - * Features: - * - Alerts data management - * - Real-time updates - * - Filtering and sorting - * - Error handling - * - Loading states - */ -const alertsSlice = createSlice({ - name: 'alerts', - initialState, - reducers: { - /** - * Clear Error Action - * - * Purpose: Clear alerts errors - */ - clearError: (state) => { - state.error = null; - }, - - /** - * Set Filter Action - * - * Purpose: Set alerts filter - */ - setFilter: (state, action: PayloadAction<'all' | 'critical' | 'unread' | 'acknowledged'>) => { - state.selectedFilter = action.payload; - }, - - /** - * Set Sort Action - * - * Purpose: Set alerts sort options - */ - setSort: (state, action: PayloadAction<{ by: string; order: 'asc' | 'desc' }>) => { - state.sortBy = action.payload.by; - state.sortOrder = action.payload.order; - }, - - /** - * Add Alert Action - * - * Purpose: Add a new alert - */ - addAlert: (state, action: PayloadAction) => { - state.alerts.unshift(action.payload); - state.unreadCount += 1; - if (action.payload.priority === 'CRITICAL') { - state.criticalCount += 1; - } - state.lastUpdated = new Date(); - }, - - /** - * Remove Alert Action - * - * Purpose: Remove an alert - */ - removeAlert: (state, action: PayloadAction) => { - const alertIndex = state.alerts.findIndex(alert => alert.id === action.payload); - if (alertIndex !== -1) { - const alert = state.alerts[alertIndex]; - if (!alert.isRead) { - state.unreadCount -= 1; - } - if (alert.priority === 'CRITICAL') { - state.criticalCount -= 1; - } - state.alerts.splice(alertIndex, 1); - } - }, - - /** - * Update Alert Action - * - * Purpose: Update an existing alert - */ - updateAlert: (state, action: PayloadAction<{ id: string; updates: Partial }>) => { - const alertIndex = state.alerts.findIndex(alert => alert.id === action.payload.id); - if (alertIndex !== -1) { - const oldAlert = state.alerts[alertIndex]; - state.alerts[alertIndex] = { ...oldAlert, ...action.payload.updates }; - - // Update counters - if (action.payload.updates.isRead !== undefined) { - if (action.payload.updates.isRead && !oldAlert.isRead) { - state.unreadCount -= 1; - } else if (!action.payload.updates.isRead && oldAlert.isRead) { - state.unreadCount += 1; - } - } - } - }, - - /** - * Clear All Alerts Action - * - * Purpose: Clear all alerts - */ - clearAllAlerts: (state) => { - state.alerts = []; - state.unreadCount = 0; - state.criticalCount = 0; - }, - - /** - * Mark All as Read Action - * - * Purpose: Mark all alerts as read - */ - markAllAsRead: (state) => { - state.alerts.forEach(alert => { - alert.isRead = true; - }); - state.unreadCount = 0; - }, - }, - extraReducers: (builder) => { - // Fetch Alerts - builder - .addCase(fetchAlerts.pending, (state) => { - state.isLoading = true; - state.error = null; - }) - .addCase(fetchAlerts.fulfilled, (state, action) => { - state.isLoading = false; - state.alerts = action.payload; - state.unreadCount = action.payload.filter(alert => !alert.isRead).length; - state.criticalCount = action.payload.filter(alert => alert.priority === 'CRITICAL').length; - state.lastUpdated = new Date(); - state.error = null; - }) - .addCase(fetchAlerts.rejected, (state, action) => { - state.isLoading = false; - state.error = action.payload as string; - }); - - // Acknowledge Alert - builder - .addCase(acknowledgeAlert.fulfilled, (state, action) => { - const alertIndex = state.alerts.findIndex(alert => alert.id === action.payload); - if (alertIndex !== -1) { - state.alerts[alertIndex].isAcknowledged = true; - } - }) - .addCase(acknowledgeAlert.rejected, (state, action) => { - state.error = action.payload as string; - }); - - // Mark Alert as Read - builder - .addCase(markAlertAsRead.fulfilled, (state, action) => { - const alertIndex = state.alerts.findIndex(alert => alert.id === action.payload); - if (alertIndex !== -1 && !state.alerts[alertIndex].isRead) { - state.alerts[alertIndex].isRead = true; - state.unreadCount -= 1; - } - }) - .addCase(markAlertAsRead.rejected, (state, action) => { - state.error = action.payload as string; - }); - }, -}); - -// ============================================================================ -// EXPORTS -// ============================================================================ - -export const { - clearError, - setFilter, - setSort, - addAlert, - removeAlert, - updateAlert, - clearAllAlerts, - markAllAsRead, -} = alertsSlice.actions; - -export default alertsSlice.reducer; - -/* - * End of File: alertsSlice.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ \ No newline at end of file diff --git a/app/modules/Dashboard/redux/index.ts b/app/modules/Dashboard/redux/index.ts index 59a10e7..ecdb52f 100644 --- a/app/modules/Dashboard/redux/index.ts +++ b/app/modules/Dashboard/redux/index.ts @@ -13,13 +13,9 @@ export * from './dashboardSlice'; // export { default as aiDashboardReducer } from './aiDashboardSlice'; // export * from './aiDashboardSlice'; -// // UI Slice -// export { default as uiReducer } from './uiSlice'; -// export * from './uiSlice'; -// // Alerts Slice -// export { default as alertsReducer } from './alertsSlice'; -// export * from './alertsSlice'; + + // // AI Dashboard Selectors // export * from './aiDashboardSelectors'; diff --git a/app/modules/Dashboard/redux/uiSlice.ts b/app/modules/Dashboard/redux/uiSlice.ts deleted file mode 100644 index f8891d5..0000000 --- a/app/modules/Dashboard/redux/uiSlice.ts +++ /dev/null @@ -1,330 +0,0 @@ -/* - * File: uiSlice.ts - * Description: UI state management slice - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; - -// ============================================================================ -// UI STATE INTERFACE -// ============================================================================ - -/** - * UI State Interface - * - * Purpose: Define the structure of UI state - * - * Features: - * - Loading states for different UI components - * - Modal and overlay management - * - Navigation state - * - Theme and appearance settings - * - User interaction states - */ -interface UIState { - // Loading states - isLoading: boolean; - loadingMessage: string | null; - - // Modal states - isModalOpen: boolean; - modalType: string | null; - modalData: any; - - // Overlay states - isOverlayVisible: boolean; - overlayType: string | null; - - // Navigation states - currentScreen: string | null; - navigationStack: string[]; - - // Theme and appearance - isDarkMode: boolean; - fontSize: 'small' | 'medium' | 'large'; - highContrast: boolean; - - // User interaction states - isRefreshing: boolean; - isScrolling: boolean; - lastInteraction: Date | null; - - // Error states - hasError: boolean; - errorMessage: string | null; - errorType: 'warning' | 'error' | 'info' | null; -} - -// ============================================================================ -// INITIAL STATE -// ============================================================================ - -/** - * Initial UI State - * - * Purpose: Define the initial state for UI - * - * Features: - * - Default loading states - * - Default modal and overlay states - * - Default theme settings - * - Default interaction states - */ -const initialState: UIState = { - // Loading states - isLoading: false, - loadingMessage: null, - - // Modal states - isModalOpen: false, - modalType: null, - modalData: null, - - // Overlay states - isOverlayVisible: false, - overlayType: null, - - // Navigation states - currentScreen: null, - navigationStack: [], - - // Theme and appearance - isDarkMode: false, - fontSize: 'medium', - highContrast: false, - - // User interaction states - isRefreshing: false, - isScrolling: false, - lastInteraction: null, - - // Error states - hasError: false, - errorMessage: null, - errorType: null, -}; - -// ============================================================================ -// UI SLICE -// ============================================================================ - -/** - * UI Slice - * - * Purpose: Redux slice for UI state management - * - * Features: - * - Loading state management - * - Modal and overlay control - * - Navigation state tracking - * - Theme and appearance settings - * - User interaction tracking - * - Error state management - */ -const uiSlice = createSlice({ - name: 'ui', - initialState, - reducers: { - /** - * Set Loading Action - * - * Purpose: Set loading state with optional message - */ - setLoading: (state, action: PayloadAction<{ isLoading: boolean; message?: string }>) => { - state.isLoading = action.payload.isLoading; - state.loadingMessage = action.payload.message || null; - }, - - /** - * Show Modal Action - * - * Purpose: Show a modal with specific type and data - */ - showModal: (state, action: PayloadAction<{ type: string; data?: any }>) => { - state.isModalOpen = true; - state.modalType = action.payload.type; - state.modalData = action.payload.data || null; - }, - - /** - * Hide Modal Action - * - * Purpose: Hide the current modal - */ - hideModal: (state) => { - state.isModalOpen = false; - state.modalType = null; - state.modalData = null; - }, - - /** - * Show Overlay Action - * - * Purpose: Show an overlay with specific type - */ - showOverlay: (state, action: PayloadAction<{ type: string }>) => { - state.isOverlayVisible = true; - state.overlayType = action.payload.type; - }, - - /** - * Hide Overlay Action - * - * Purpose: Hide the current overlay - */ - hideOverlay: (state) => { - state.isOverlayVisible = false; - state.overlayType = null; - }, - - /** - * Set Current Screen Action - * - * Purpose: Set the current screen name - */ - setCurrentScreen: (state, action: PayloadAction) => { - state.currentScreen = action.payload; - if (!state.navigationStack.includes(action.payload)) { - state.navigationStack.push(action.payload); - } - }, - - /** - * Clear Navigation Stack Action - * - * Purpose: Clear the navigation stack - */ - clearNavigationStack: (state) => { - state.navigationStack = []; - }, - - /** - * Toggle Dark Mode Action - * - * Purpose: Toggle dark mode on/off - */ - toggleDarkMode: (state) => { - state.isDarkMode = !state.isDarkMode; - }, - - /** - * Set Font Size Action - * - * Purpose: Set the font size preference - */ - setFontSize: (state, action: PayloadAction<'small' | 'medium' | 'large'>) => { - state.fontSize = action.payload; - }, - - /** - * Toggle High Contrast Action - * - * Purpose: Toggle high contrast mode - */ - toggleHighContrast: (state) => { - state.highContrast = !state.highContrast; - }, - - /** - * Set Refreshing Action - * - * Purpose: Set refreshing state - */ - setRefreshing: (state, action: PayloadAction) => { - state.isRefreshing = action.payload; - }, - - /** - * Set Scrolling Action - * - * Purpose: Set scrolling state - */ - setScrolling: (state, action: PayloadAction) => { - state.isScrolling = action.payload; - }, - - /** - * Update Last Interaction Action - * - * Purpose: Update the last interaction timestamp - */ - updateLastInteraction: (state) => { - state.lastInteraction = new Date(); - }, - - /** - * Show Error Action - * - * Purpose: Show an error message - */ - showError: (state, action: PayloadAction<{ message: string; type?: 'warning' | 'error' | 'info' }>) => { - state.hasError = true; - state.errorMessage = action.payload.message; - state.errorType = action.payload.type || 'error'; - }, - - /** - * Clear Error Action - * - * Purpose: Clear the current error - */ - clearError: (state) => { - state.hasError = false; - state.errorMessage = null; - state.errorType = null; - }, - - /** - * Reset UI State Action - * - * Purpose: Reset UI state to initial values - */ - resetUIState: (state) => { - state.isLoading = false; - state.loadingMessage = null; - state.isModalOpen = false; - state.modalType = null; - state.modalData = null; - state.isOverlayVisible = false; - state.overlayType = null; - state.isRefreshing = false; - state.isScrolling = false; - state.hasError = false; - state.errorMessage = null; - state.errorType = null; - }, - }, -}); - -// ============================================================================ -// EXPORTS -// ============================================================================ - -export const { - setLoading, - showModal, - hideModal, - showOverlay, - hideOverlay, - setCurrentScreen, - clearNavigationStack, - toggleDarkMode, - setFontSize, - toggleHighContrast, - setRefreshing, - setScrolling, - updateLastInteraction, - showError, - clearError, - resetUIState, -} = uiSlice.actions; - -export default uiSlice.reducer; - -/* - * End of File: uiSlice.ts - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ \ No newline at end of file diff --git a/app/modules/Dashboard/screens/DashboardScreen.tsx b/app/modules/Dashboard/screens/DashboardScreen.tsx index a11d9ab..e8e8527 100644 --- a/app/modules/Dashboard/screens/DashboardScreen.tsx +++ b/app/modules/Dashboard/screens/DashboardScreen.tsx @@ -13,15 +13,12 @@ import { ScrollView, TouchableOpacity, RefreshControl, - FlatList, Dimensions, } from 'react-native'; import { theme } from '../../../theme/theme'; -import { DashboardHeader } from '../components/DashboardHeader'; -import { BrainPredictionsOverview } from '../components/BrainPredictionsOverview'; import { FeedbackAnalysisPieChart } from '../components/FeedbackAnalysisPieChart'; import { useAIDashboard } from '../hooks/useAIDashboard'; -import { selectUserDisplayName, selectUserFirstName } from '../../Auth/redux/authSelectors'; +import { selectUserFirstName } from '../../Auth/redux/authSelectors'; import { useAppSelector } from '../../../store/hooks'; /** @@ -571,7 +568,7 @@ export const DashboardScreen: React.FC = ({ Feedback Coverage: {dashboardData.data.feedback_rate_percentage}% - Average Feedback per Prediction: {dashboardData.data.average_feedback_per_prediction} + Average Feedback per Prediction: {isNaN(Number(dashboardData?.data?.average_feedback_per_prediction))? "N/A" : Number(dashboardData.data.average_feedback_per_prediction).toFixed(2) } diff --git a/app/modules/Dashboard/screens/ERDashboardScreen.tsx b/app/modules/Dashboard/screens/ERDashboardScreen.tsx deleted file mode 100644 index 3dd844c..0000000 --- a/app/modules/Dashboard/screens/ERDashboardScreen.tsx +++ /dev/null @@ -1,791 +0,0 @@ -/* - * File: ERDashboardScreen.tsx - * Description: Brain AI Analysis Dashboard - Main dashboard for neurological AI predictions and brain imaging analysis - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React, { useState, useEffect } from 'react'; -import { - View, - Text, - StyleSheet, - ScrollView, - TouchableOpacity, - RefreshControl, - FlatList, - Alert, -} from 'react-native'; -import { theme } from '../../../theme/theme'; -import { ERDashboard, Patient, Alert as AlertType } from '../../../shared/types'; -import { PatientCard } from '../components/PatientCard'; -import { CriticalAlerts } from '../components/CriticalAlerts'; -import { DashboardHeader } from '../components/DashboardHeader'; -import { BrainPredictionsOverview } from '../components/BrainPredictionsOverview'; - -/** - * ERDashboardScreenProps Interface - * - * Purpose: Defines the props required by the ERDashboardScreen component - * - * Props: - * - navigation: React Navigation object for screen navigation - */ -interface ERDashboardScreenProps { - navigation: any; -} - -/** - * Brain AI Prediction Types - * - * Purpose: Defines the different types of brain conditions that AI can predict - */ -type BrainCondition = - | 'Intraparenchymal hemorrhage (IPH)' - | 'Intraventricular hemorrhage (IVH)' - | 'Subarachnoid hemorrhage (SAH)' - | 'Subdural hematoma (SDH)' - | 'Epidural hematoma (EDH)' - | 'Mass effect' - | 'Midline shift' - | 'Intracranial hemorrhage (ICH)' - | 'Stroke (ischemic)' - | 'Normal brain'; - -/** - * ERDashboardScreen Component - * - * Purpose: Brain AI Analysis Dashboard for Emergency Department physicians - * - * Dashboard Features: - * 1. Real-time brain scan analysis with AI predictions - * 2. Critical neurological alerts for immediate attention - * 3. Quick action buttons for brain imaging tasks - * 4. Department statistics focused on neurological cases - * 5. Pull-to-refresh functionality for live updates - * 6. AI prediction confidence scores and analysis - * - * Brain Condition Filtering: - * - All: Shows all brain scan cases - * - Critical: Shows only cases with critical brain conditions - * - Pending: Shows cases awaiting AI analysis - * - * AI Prediction Classes: - * - Intraparenchymal hemorrhage (IPH) - * - Intraventricular hemorrhage (IVH) - * - Subarachnoid hemorrhage (SAH) - * - Subdural hematoma (SDH) - * - Epidural hematoma (EDH) - * - Mass effect - * - Midline shift - * - Intracranial hemorrhage (ICH) - * - Stroke (ischemic) - * - Normal brain - */ -export const ERDashboardScreen: React.FC = ({ - navigation, -}) => { - // ============================================================================ - // STATE MANAGEMENT - // ============================================================================ - - // Refresh state for pull-to-refresh functionality - const [refreshing, setRefreshing] = useState(false); - - // Patient filter state to control which brain cases are displayed - const [selectedFilter, setSelectedFilter] = useState<'all' | 'critical' | 'pending'>('all'); - - // Dashboard data state - const [dashboard, setDashboard] = useState(null); - const [patients, setPatients] = useState([]); - const [alerts, setAlerts] = useState([]); - const [isLoading, setIsLoading] = useState(true); - - // ============================================================================ - // MOCK DATA GENERATION - // ============================================================================ - - /** - * generateMockDashboard Function - * - * Purpose: Generate mock dashboard data focused on brain AI analysis - * - * Returns: ERDashboard object with brain imaging statistics - */ - const generateMockDashboard = (): ERDashboard => ({ - totalPatients: 18, // Total number of brain scan cases - criticalPatients: 5, // Number of critical brain conditions - pendingScans: 6, // Number of scans awaiting AI analysis - recentReports: 12, // Number of AI analysis reports generated - bedOccupancy: 78, // Percentage of neurological beds occupied - departmentStats: { - emergency: 8, // Emergency brain cases - trauma: 4, // Traumatic brain injury cases - cardiac: 3, // Stroke cases (using cardiac as placeholder) - neurology: 2, // General neurology cases - pediatrics: 1, // Neurosurgery cases (using pediatrics as placeholder) - icu: 0, // ICU brain cases - }, - shiftInfo: { - currentShift: 'DAY' as const, // Current shift (DAY/NIGHT) - startTime: new Date(), // Shift start time - endTime: new Date(), // Shift end time - attendingPhysician: 'Dr. Smith', // Lead neurologist on duty - residents: ['Dr. Johnson', 'Dr. Williams'], // Neurology residents - nurses: ['Nurse Brown', 'Nurse Davis'], // Neurology nursing staff - }, - lastUpdated: new Date(), // Last time dashboard was updated - }); - - /** - * generateMockPatients Function - * - * Purpose: Generate mock patient data focused on brain conditions and AI predictions - * - * Returns: Array of Patient objects with brain-related medical data - */ - const generateMockPatients = (): Patient[] => [ - { - id: '1', - mrn: 'MRN001', - firstName: 'John', - lastName: 'Doe', - dateOfBirth: new Date('1985-03-15'), - gender: 'MALE' as const, - age: 38, - bedNumber: 'A1', - roomNumber: '101', - admissionDate: new Date('2024-01-15'), - status: 'ACTIVE' as const, - priority: 'CRITICAL' as const, - department: 'Emergency', - attendingPhysician: 'Dr. Smith', - allergies: [ - { - id: '1', - name: 'Contrast Dye', - severity: 'SEVERE' as const, - reaction: 'Anaphylaxis' - }, - ], - medications: [ - { - id: '1', - name: 'Mannitol', - dosage: '100ml', - frequency: 'Every 6 hours', - route: 'IV', - startDate: new Date(), - status: 'ACTIVE' as const, - prescribedBy: 'Dr. Smith', - }, - ], - vitalSigns: { - bloodPressure: { systolic: 180, diastolic: 110, timestamp: new Date() }, - heartRate: { value: 95, timestamp: new Date() }, - temperature: { value: 37.2, timestamp: new Date() }, - respiratoryRate: { value: 18, timestamp: new Date() }, - oxygenSaturation: { value: 98, timestamp: new Date() }, - }, - medicalHistory: [], - currentDiagnosis: 'AI Predicted: Intraparenchymal hemorrhage (IPH) - 94% confidence', - lastUpdated: new Date(), - }, - { - id: '2', - mrn: 'MRN002', - firstName: 'Jane', - lastName: 'Smith', - dateOfBirth: new Date('1990-07-22'), - gender: 'FEMALE' as const, - age: 33, - bedNumber: 'B2', - roomNumber: '102', - admissionDate: new Date('2024-01-15'), - status: 'ACTIVE' as const, - priority: 'CRITICAL' as const, - department: 'Trauma', - attendingPhysician: 'Dr. Johnson', - allergies: [], - medications: [ - { - id: '2', - name: 'Dexamethasone', - dosage: '4mg', - frequency: 'Every 6 hours', - route: 'IV', - startDate: new Date(), - status: 'ACTIVE' as const, - prescribedBy: 'Dr. Johnson', - }, - ], - vitalSigns: { - bloodPressure: { systolic: 160, diastolic: 90, timestamp: new Date() }, - heartRate: { value: 88, timestamp: new Date() }, - temperature: { value: 36.8, timestamp: new Date() }, - respiratoryRate: { value: 16, timestamp: new Date() }, - oxygenSaturation: { value: 99, timestamp: new Date() }, - }, - medicalHistory: [], - currentDiagnosis: 'AI Predicted: Subdural hematoma (SDH) with mass effect - 89% confidence', - lastUpdated: new Date(), - }, - { - id: '3', - mrn: 'MRN003', - firstName: 'Michael', - lastName: 'Brown', - dateOfBirth: new Date('1978-11-08'), - gender: 'MALE' as const, - age: 45, - bedNumber: 'C3', - roomNumber: '103', - admissionDate: new Date('2024-01-15'), - status: 'PENDING' as const, - priority: 'HIGH' as const, - department: 'Stroke', - attendingPhysician: 'Dr. Williams', - allergies: [ - { - id: '2', - name: 'Aspirin', - severity: 'MODERATE' as const, - reaction: 'Stomach upset' - }, - ], - medications: [ - { - id: '3', - name: 'tPA', - dosage: '0.9mg/kg', - frequency: 'Single dose', - route: 'IV', - startDate: new Date(), - status: 'ACTIVE' as const, - prescribedBy: 'Dr. Williams', - }, - ], - vitalSigns: { - bloodPressure: { systolic: 140, diastolic: 85, timestamp: new Date() }, - heartRate: { value: 110, timestamp: new Date() }, - temperature: { value: 36.9, timestamp: new Date() }, - respiratoryRate: { value: 20, timestamp: new Date() }, - oxygenSaturation: { value: 95, timestamp: new Date() }, - }, - medicalHistory: [], - currentDiagnosis: 'AI Predicted: Stroke (ischemic) - 92% confidence', - lastUpdated: new Date(), - }, - { - id: '4', - mrn: 'MRN004', - firstName: 'Sarah', - lastName: 'Wilson', - dateOfBirth: new Date('1995-04-12'), - gender: 'FEMALE' as const, - age: 28, - bedNumber: 'D4', - roomNumber: '104', - admissionDate: new Date('2024-01-15'), - status: 'ACTIVE' as const, - priority: 'CRITICAL' as const, - department: 'Neurosurgery', - attendingPhysician: 'Dr. Davis', - allergies: [], - medications: [ - { - id: '4', - name: 'Phenytoin', - dosage: '100mg', - frequency: 'Every 8 hours', - route: 'IV', - startDate: new Date(), - status: 'ACTIVE' as const, - prescribedBy: 'Dr. Davis', - }, - ], - vitalSigns: { - bloodPressure: { systolic: 190, diastolic: 120, timestamp: new Date() }, - heartRate: { value: 60, timestamp: new Date() }, - temperature: { value: 38.5, timestamp: new Date() }, - respiratoryRate: { value: 12, timestamp: new Date() }, - oxygenSaturation: { value: 92, timestamp: new Date() }, - }, - medicalHistory: [], - currentDiagnosis: 'AI Predicted: Epidural hematoma (EDH) with midline shift - 96% confidence', - lastUpdated: new Date(), - }, - { - id: '5', - mrn: 'MRN005', - firstName: 'David', - lastName: 'Miller', - dateOfBirth: new Date('1982-09-30'), - gender: 'MALE' as const, - age: 41, - bedNumber: 'E5', - roomNumber: '105', - admissionDate: new Date('2024-01-15'), - status: 'ACTIVE' as const, - priority: 'HIGH' as const, - department: 'Neurology', - attendingPhysician: 'Dr. Brown', - allergies: [ - { - id: '3', - name: 'Latex', - severity: 'SEVERE' as const, - reaction: 'Contact dermatitis' - }, - ], - medications: [], - vitalSigns: { - bloodPressure: { systolic: 130, diastolic: 80, timestamp: new Date() }, - heartRate: { value: 85, timestamp: new Date() }, - temperature: { value: 37.8, timestamp: new Date() }, - respiratoryRate: { value: 22, timestamp: new Date() }, - oxygenSaturation: { value: 97, timestamp: new Date() }, - }, - medicalHistory: [], - currentDiagnosis: 'AI Predicted: Subarachnoid hemorrhage (SAH) - 87% confidence', - lastUpdated: new Date(), - }, - { - id: '6', - mrn: 'MRN006', - firstName: 'Emily', - lastName: 'Johnson', - dateOfBirth: new Date('1988-12-05'), - gender: 'FEMALE' as const, - age: 35, - bedNumber: 'F6', - roomNumber: '106', - admissionDate: new Date('2024-01-15'), - status: 'PENDING' as const, - priority: 'MEDIUM' as const, - department: 'Emergency', - attendingPhysician: 'Dr. Wilson', - allergies: [], - medications: [], - vitalSigns: { - bloodPressure: { systolic: 120, diastolic: 75, timestamp: new Date() }, - heartRate: { value: 72, timestamp: new Date() }, - temperature: { value: 37.0, timestamp: new Date() }, - respiratoryRate: { value: 16, timestamp: new Date() }, - oxygenSaturation: { value: 99, timestamp: new Date() }, - }, - medicalHistory: [], - currentDiagnosis: 'AI Analysis Pending - CT scan uploaded', - lastUpdated: new Date(), - }, - ]; - - /** - * generateMockAlerts Function - * - * Purpose: Generate mock alert data focused on brain AI predictions - * - * Returns: Array of Alert objects with brain-related alerts - */ - const generateMockAlerts = (): AlertType[] => [ - { - id: '1', - type: 'CRITICAL_FINDING' as const, - priority: 'CRITICAL' as const, - title: 'Critical Brain Finding Detected', - message: 'AI has detected Intraparenchymal hemorrhage (IPH) with 94% confidence. Immediate neurosurgical consultation required.', - patientId: '1', - patientName: 'John Doe', - bedNumber: 'A1', - timestamp: new Date(), - isRead: false, - isAcknowledged: false, - actionRequired: true, - }, - { - id: '2', - type: 'VITAL_SIGNS_ALERT' as const, - priority: 'HIGH' as const, - title: 'Elevated ICP Alert', - message: 'Patient Sarah Wilson showing signs of elevated intracranial pressure. BP: 190/120, HR: 60. Immediate intervention needed.', - patientId: '4', - patientName: 'Sarah Wilson', - bedNumber: 'D4', - timestamp: new Date(Date.now() - 300000), // 5 minutes ago - isRead: false, - isAcknowledged: false, - actionRequired: true, - }, - { - id: '3', - type: 'MEDICATION_ALERT' as const, - priority: 'MEDIUM' as const, - title: 'AI Analysis Complete', - message: 'Brain CT analysis complete for Emily Johnson. Results: Normal brain - 98% confidence. No intervention required.', - patientId: '6', - patientName: 'Emily Johnson', - bedNumber: 'F6', - timestamp: new Date(Date.now() - 600000), // 10 minutes ago - isRead: true, - isAcknowledged: true, - actionRequired: false, - }, - { - id: '4', - type: 'CRITICAL_FINDING' as const, - priority: 'CRITICAL' as const, - title: 'Stroke Alert - Time Sensitive', - message: 'Michael Brown diagnosed with ischemic stroke. tPA window closing. Immediate thrombolytic therapy required.', - patientId: '3', - patientName: 'Michael Brown', - bedNumber: 'C3', - timestamp: new Date(Date.now() - 120000), // 2 minutes ago - isRead: false, - isAcknowledged: false, - actionRequired: true, - }, - ]; - - // ============================================================================ - // EFFECTS - // ============================================================================ - - /** - * useEffect for initial data loading - * - * Purpose: Load initial mock data when component mounts - */ - useEffect(() => { - const loadInitialData = async () => { - setIsLoading(true); - - // Simulate API call delay - setTimeout(() => {}, 1000); - - // Generate and set mock data - setDashboard(generateMockDashboard()); - setPatients(generateMockPatients()); - setAlerts(generateMockAlerts()); - - setIsLoading(false); - }; - - loadInitialData(); - }, []); - - // ============================================================================ - // EVENT HANDLERS - // ============================================================================ - - /** - * handleRefresh Function - * - * Purpose: Handle pull-to-refresh functionality to update dashboard data - */ - const handleRefresh = async () => { - setRefreshing(true); - - // Simulate API call with 1-second delay - setTimeout(() => {}, 1000); - - // Update data with fresh mock data - setDashboard(generateMockDashboard()); - setPatients(generateMockPatients()); - setAlerts(generateMockAlerts()); - - setRefreshing(false); - }; - - /** - * handlePatientPress Function - * - * Purpose: Handle patient card press navigation - * - * @param patient - Patient object that was pressed - */ - const handlePatientPress = (patient: Patient) => { - console.log('Patient pressed:', patient.firstName, patient.lastName); - Alert.alert('Brain Analysis Details', `Navigate to ${patient.firstName} ${patient.lastName}'s brain scan analysis`); - }; - - /** - * handleAlertPress Function - * - * Purpose: Handle alert press interaction - * - * @param alert - Alert object that was pressed - */ - const handleAlertPress = (alert: AlertType) => { - console.log('Alert pressed:', alert.title); - Alert.alert('Brain Alert Details', alert.message); - }; - - // ============================================================================ - // DATA PROCESSING - // ============================================================================ - - /** - * filteredPatients - Computed property - * - * Purpose: Filter patients based on selected filter criteria - * - * Filter Options: - * - 'all': Show all brain scan cases - * - 'critical': Show only cases with CRITICAL priority - * - 'pending': Show only cases with PENDING status (awaiting AI analysis) - */ - const filteredPatients = patients.filter(patient => { - switch (selectedFilter) { - case 'critical': - return patient.priority === 'CRITICAL'; - case 'pending': - return patient.status === 'PENDING'; - default: - return true; - } - }); - - /** - * criticalAlerts - Computed property - * - * Purpose: Extract critical alerts from all alerts for immediate display - */ - const criticalAlerts = alerts.filter(alert => alert.priority === 'CRITICAL'); - - /** - * pendingScans - Computed property - * - * Purpose: Identify patients with pending AI analysis - */ - const pendingScans = patients.filter(patient => patient.status === 'PENDING'); - - // ============================================================================ - // RENDER FUNCTIONS - // ============================================================================ - - /** - * renderPatientCard Function - * - * Purpose: Render individual patient card component - * - * @param item - Patient data object - * @returns PatientCard component with patient data and press handler - */ - const renderPatientCard = ({ item }: { item: Patient }) => ( - handlePatientPress(item)} - /> - ); - - /** - * renderHeader Function - * - * Purpose: Render the dashboard header section with all dashboard components - */ - const renderHeader = () => ( - - {/* Dashboard header with shift information and key metrics */} - {dashboard && } - - {/* Critical alerts section - only show if there are critical alerts */} - {criticalAlerts.length > 0 && ( - - )} - - {/* Department statistics showing brain case distribution */} - {dashboard && } - - {/* Brain case filter section with filter buttons */} - - Brain Scan Cases - - {/* All cases filter button */} - setSelectedFilter('all')} - > - - All ({patients.length}) - - - - {/* Critical cases filter button */} - setSelectedFilter('critical')} - > - - Critical ({patients.filter(p => p.priority === 'CRITICAL').length}) - - - - {/* Pending AI analysis filter button */} - setSelectedFilter('pending')} - > - - Pending AI ({patients.filter(p => p.status === 'PENDING').length}) - - - - - - ); - - // ============================================================================ - // LOADING STATE - // ============================================================================ - - /** - * Loading state render - * - * Purpose: Show loading indicator while data is being generated - */ - if (isLoading) { - return ( - - Loading Brain AI Dashboard... - - ); - } - - // ============================================================================ - // MAIN RENDER - // ============================================================================ - - return ( - - {/* FlatList for efficient rendering of patient cards */} - item.id} - ListHeaderComponent={renderHeader} - contentContainerStyle={styles.listContainer} - refreshControl={ - - } - showsVerticalScrollIndicator={false} - /> - - ); -}; - -// ============================================================================ -// STYLES SECTION -// ============================================================================ - -const styles = StyleSheet.create({ - // Main container for the dashboard screen - container: { - flex: 1, - backgroundColor: theme.colors.background, - }, - - // Loading container for initial data loading - loadingContainer: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: theme.colors.background, - }, - - // Loading text styling - loadingText: { - fontSize: theme.typography.fontSize.bodyLarge, - color: theme.colors.textSecondary, - fontFamily: theme.typography.fontFamily.medium, - }, - - // Container for the FlatList content - listContainer: { - paddingBottom: theme.spacing.lg, - }, - - // Header section containing dashboard components - header: { - paddingHorizontal: theme.spacing.md, - }, - - // Container for patient filter section - filterContainer: { - marginTop: theme.spacing.lg, - marginBottom: theme.spacing.md, - }, - - // Section title styling - sectionTitle: { - fontSize: theme.typography.fontSize.displaySmall, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.textPrimary, - marginBottom: theme.spacing.md, - }, - - // Container for filter buttons - filterButtons: { - flexDirection: 'row', - gap: theme.spacing.sm, - }, - - // Individual filter button styling - filterButton: { - paddingHorizontal: theme.spacing.md, - paddingVertical: theme.spacing.sm, - borderRadius: theme.borderRadius.medium, - borderWidth: 1, - borderColor: theme.colors.border, - backgroundColor: theme.colors.background, - }, - - // Active filter button styling - filterButtonActive: { - backgroundColor: theme.colors.primary, - borderColor: theme.colors.primary, - }, - - // Filter button text styling - filterButtonText: { - fontSize: theme.typography.fontSize.bodyMedium, - color: theme.colors.textSecondary, - fontFamily: theme.typography.fontFamily.medium, - }, - - // Active filter button text styling - filterButtonTextActive: { - color: theme.colors.background, - }, -}); - -/* - * End of File: ERDashboardScreen.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ \ No newline at end of file diff --git a/app/modules/PatientCare/redux/patientCareSlice.ts b/app/modules/PatientCare/redux/patientCareSlice.ts index 8222b65..924bc5d 100644 --- a/app/modules/PatientCare/redux/patientCareSlice.ts +++ b/app/modules/PatientCare/redux/patientCareSlice.ts @@ -93,7 +93,6 @@ export const fetchPatients = createAsyncThunk( async (token: string, { rejectWithValue }) => { try { const response: any = await patientAPI.getPatients(token); - console.log('response', response); if (response.ok && response.data&& response.data.data) { // Return the patients data directly from the new API structure diff --git a/app/modules/PatientCare/screens/PatientDetailsScreen.tsx b/app/modules/PatientCare/screens/PatientDetailsScreen.tsx index 0e9498a..b8438f4 100644 --- a/app/modules/PatientCare/screens/PatientDetailsScreen.tsx +++ b/app/modules/PatientCare/screens/PatientDetailsScreen.tsx @@ -36,6 +36,7 @@ import { DicomViewerModal } from '../../../shared/components'; // Import types and API import { patientAPI } from '../services/patientAPI'; import { selectUser } from '../../Auth/redux/authSelectors'; +import { logoutUser } from '../../Auth/redux/authActions'; import { API_CONFIG } from '../../../shared/utils'; import { PatientDetailsScreenProps } from '../navigation/navigationTypes'; @@ -175,6 +176,14 @@ const PatientDetailsScreen: React.FC = ({ navigation, const response: any = await patientAPI.getPatientDetailsById(patientId, user.access_token); + // Check for 401 Unauthorized response + if (response.status === 401) { + // Dispatch logout action to clear authentication state + dispatch(logoutUser()); + setError('Session expired. Please login again.'); + return; + } + if (response.ok && response.data && response.data.data ) { setPatientData(response.data.data as PatientData); } else { @@ -185,7 +194,7 @@ const PatientDetailsScreen: React.FC = ({ navigation, } finally { setIsLoading(false); } - }, [patientId, user?.access_token]); + }, [patientId, user?.access_token, dispatch]); // ============================================================================ // EFFECTS @@ -245,7 +254,6 @@ const PatientDetailsScreen: React.FC = ({ navigation, if (firstPrediction?.preview) { const dicomUrl = API_CONFIG.BASE_URL +'/api/dicom'+ firstPrediction.file_path; - console.log('dicomUrl', dicomUrl); setSelectedDicomData({ dicomUrl, seriesData: series, diff --git a/app/modules/PatientCare/screens/SeriesDetailScreen.tsx b/app/modules/PatientCare/screens/SeriesDetailScreen.tsx index c22c836..9c0f7c2 100644 --- a/app/modules/PatientCare/screens/SeriesDetailScreen.tsx +++ b/app/modules/PatientCare/screens/SeriesDetailScreen.tsx @@ -237,7 +237,6 @@ const SeriesDetailScreen: React.FC = ({ navigation, rou const handleDicomImagePress = useCallback((prediction: Prediction, imageIndex: number) => { if (prediction?.file_path) { const dicomUrl = API_CONFIG.BASE_URL + '/api/dicom' + prediction.file_path; - console.log('DICOM URL:', dicomUrl); setSelectedDicomData({ dicomUrl, @@ -320,11 +319,9 @@ const SeriesDetailScreen: React.FC = ({ navigation, rou is_positive: isPositive }; - console.log('Submitting feedback payload:', feedbackPayload); // Call the actual API const response = await patientAPI.submitFeedback(feedbackPayload, user?.access_token); - console.log('Feedback response:', response); if (!response.ok) { throw new Error(response.problem || 'Failed to submit feedback'); diff --git a/app/modules/Settings/screens/SettingsScreen.tsx b/app/modules/Settings/screens/SettingsScreen.tsx index 75d0eda..dc2bca0 100644 --- a/app/modules/Settings/screens/SettingsScreen.tsx +++ b/app/modules/Settings/screens/SettingsScreen.tsx @@ -188,7 +188,7 @@ export const SettingsScreen: React.FC = ({ id: 'help-support', title: 'Help & Support', subtitle: 'Contact support and view documentation', - icon: 'help', + icon: 'phone', type: 'NAVIGATION', onPress: () => handleNavigation('HELP'), }, @@ -523,7 +523,6 @@ export const SettingsScreen: React.FC = ({ // Update Redux state with new profile photo URL if (responseData.data?.profile_photo_url) { - console.log('Updating user profile with new photo URL:', responseData.data.profile_photo_url); dispatch(updateUserProfile({ self_url: responseData.data.profile_photo_url })); diff --git a/app/shared/components/DICOM_VIEWER_README.md b/app/shared/components/DICOM_VIEWER_README.md deleted file mode 100644 index 7ca6e3a..0000000 --- a/app/shared/components/DICOM_VIEWER_README.md +++ /dev/null @@ -1,207 +0,0 @@ -# DICOM Viewer Component - -## Overview -The DICOM Viewer component is a React Native component that uses WebView to display DICOM medical imaging files. It integrates with Cornerstone.js and Cornerstone WADO Image Loader for robust DICOM file handling. - -## Features -- โœ… WebView-based DICOM rendering -- โœ… Cornerstone.js integration for medical imaging -- โœ… Support for remote DICOM URLs -- โœ… Loading states and error handling -- โœ… Real-time communication with React Native -- โœ… Responsive design for mobile devices - -## Components - -### 1. DicomViewer -The main DICOM viewer component. - -**Props:** -```typescript -interface DicomViewerProps { - dicomUrl: string; // URL to the DICOM file - onError?: (error: string) => void; // Error callback - onLoad?: () => void; // Load success callback -} -``` - -**Usage:** -```typescript -import { DicomViewer } from '../shared/components'; - - console.error('DICOM Error:', error)} - onLoad={() => console.log('DICOM loaded successfully')} -/> -``` - -### 2. DicomViewerTest -A test component for testing different DICOM URLs and debugging issues. - -**Usage:** -```typescript -import { DicomViewerTest } from '../shared/components'; - - -``` - -## How It Works - -### 1. WebView Setup -- Loads a local HTML file (`dicom-viewer.html`) -- Enables JavaScript and DOM storage -- Allows file access and universal access from file URLs - -### 2. Library Loading -- Dynamically loads Cornerstone.js from CDN -- Loads Cornerstone WADO Image Loader -- Initializes the viewer when libraries are ready - -### 3. DICOM Processing -- Receives DICOM URL from React Native via postMessage -- Uses Cornerstone to load and display the DICOM image -- Handles errors and success states - -### 4. Communication -- Sends status messages back to React Native -- Reports loading, success, and error states -- Enables debugging and user feedback - -## Troubleshooting - -### Black Screen Issues - -#### 1. Check Console Logs -Open the React Native debugger and check for: -- WebView loading errors -- JavaScript execution errors -- Network request failures - -#### 2. Verify DICOM URL -- Ensure the URL is accessible from the device -- Check if the URL returns a valid DICOM file -- Verify CORS settings if loading from a different domain - -#### 3. Library Loading Issues -- Check internet connectivity (libraries load from CDN) -- Verify the HTML file path is correct -- Check WebView permissions and settings - -#### 4. Platform-Specific Issues - -**Android:** -- Ensure `allowFileAccess` is enabled -- Check if the HTML file is in the correct assets folder -- Verify WebView permissions in AndroidManifest.xml - -**iOS:** -- Check WebView configuration in Info.plist -- Ensure JavaScript is enabled -- Verify file access permissions - -### Common Error Messages - -#### "Failed to load DICOM viewer libraries" -- Check internet connectivity -- Verify CDN URLs are accessible -- Check WebView JavaScript settings - -#### "Failed to load DICOM image" -- Verify DICOM URL is accessible -- Check if the file is a valid DICOM format -- Ensure the server supports CORS - -#### "Invalid message received from app" -- Check the message format being sent -- Verify the postMessage implementation -- Check WebView message handling - -## Testing - -### 1. Use Sample URLs -The test component includes sample DICOM URLs that are known to work: -- Sample DICOM 1-3 from OHIF examples - -### 2. Test Custom URLs -- Enter your own DICOM URLs -- Test with different file formats -- Verify error handling - -### 3. Debug Mode -- Check console logs in React Native debugger -- Monitor WebView messages -- Use the test component for isolated testing - -## Performance Tips - -### 1. Image Optimization -- Use compressed DICOM files when possible -- Consider implementing progressive loading -- Cache frequently accessed images - -### 2. Memory Management -- Dispose of WebView when not in use -- Monitor memory usage with large DICOM files -- Implement proper cleanup in useEffect - -### 3. Network Optimization -- Use CDN for DICOM files when possible -- Implement retry logic for failed requests -- Consider offline caching for critical images - -## Security Considerations - -### 1. URL Validation -- Validate DICOM URLs before loading -- Implement URL whitelisting if needed -- Sanitize user input for custom URLs - -### 2. WebView Security -- Limit WebView permissions to minimum required -- Implement proper origin whitelisting -- Monitor for malicious content - -### 3. Data Privacy -- Ensure DICOM files don't contain PHI -- Implement proper data handling protocols -- Follow HIPAA compliance guidelines - -## Future Enhancements - -### 1. Offline Support -- Bundle Cornerstone libraries locally -- Implement offline DICOM caching -- Support for local DICOM files - -### 2. Advanced Features -- Multi-planar reconstruction (MPR) -- Measurement tools -- Annotation capabilities -- 3D rendering support - -### 3. Performance Improvements -- WebAssembly integration -- GPU acceleration -- Progressive image loading -- Background processing - -## Support - -For issues and questions: -1. Check this README for common solutions -2. Review console logs and error messages -3. Test with sample URLs first -4. Verify WebView configuration -5. Check platform-specific requirements - -## Dependencies - -- `react-native-webview`: WebView component -- `cornerstone-core`: Medical imaging library -- `cornerstone-wado-image-loader`: DICOM file loader - -## License - -Design & Developed by Tech4Biz Solutions -Copyright (c) Spurrin Innovations. All rights reserved. diff --git a/app/shared/components/DicomViewerModal.example.tsx b/app/shared/components/DicomViewerModal.example.tsx deleted file mode 100644 index 81012be..0000000 --- a/app/shared/components/DicomViewerModal.example.tsx +++ /dev/null @@ -1,241 +0,0 @@ -/* - * File: DicomViewerModal.example.tsx - * Description: Example usage of DicomViewerModal component - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React, { useState } from 'react'; -import { View, TouchableOpacity, Text, StyleSheet } from 'react-native'; -import { DicomViewerModal } from './index'; -import { theme } from '../../theme/theme'; - -// ============================================================================ -// EXAMPLE COMPONENT -// ============================================================================ - -/** - * DicomViewerModalExample Component - * - * Purpose: Demonstrates how to use the DicomViewerModal component - * - * Features: - * - Shows how to pass dicomUrl to modal - * - Demonstrates modal state management - * - Example with patient information - * - Error handling examples - */ -export const DicomViewerModalExample: React.FC = () => { - // ============================================================================ - // STATE MANAGEMENT - // ============================================================================ - - const [isModalVisible, setIsModalVisible] = useState(false); - - // Example DICOM URLs (replace with your actual URLs) - const exampleDicomUrl = 'https://example-dicom-server.com/studies/123/series/456/instances/789'; - - // Example patient data - const patientData = { - name: 'John Doe', - studyDescription: 'CT Brain with Contrast', - }; - - // ============================================================================ - // EVENT HANDLERS - // ============================================================================ - - /** - * Open DICOM viewer modal - */ - const openDicomViewer = () => { - setIsModalVisible(true); - }; - - /** - * Close DICOM viewer modal - */ - const closeDicomViewer = () => { - setIsModalVisible(false); - }; - - // ============================================================================ - // RENDER - // ============================================================================ - - return ( - - {/* Trigger Button */} - - View DICOM Image - - - {/* DICOM Viewer Modal */} - - - ); -}; - -// ============================================================================ -// STYLES -// ============================================================================ - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: theme.colors.background, - padding: theme.spacing.lg, - }, - - button: { - backgroundColor: theme.colors.primary, - paddingHorizontal: theme.spacing.xl, - paddingVertical: theme.spacing.md, - borderRadius: theme.borderRadius.medium, - ...theme.shadows.primary, - }, - - buttonText: { - fontSize: theme.typography.fontSize.bodyLarge, - fontFamily: theme.typography.fontFamily.bold, - color: theme.colors.background, - }, -}); - -// ============================================================================ -// USAGE EXAMPLES IN OTHER COMPONENTS -// ============================================================================ - -/* - -// Example 1: Basic Usage in Patient Details Screen -import { DicomViewerModal } from '../../../shared/components'; - -const PatientDetailsExample = () => { - const [showDicom, setShowDicom] = useState(false); - const dicomUrl = patient.scanResults?.dicomUrl; - - return ( - <> - setShowDicom(true)}> - View Scan Results - - - setShowDicom(false)} - patientName={patient.name} - studyDescription={patient.scanResults?.description} - /> - - ); -}; - -// Example 2: Usage with Series Selection -import { DicomViewerModal } from '../../../shared/components'; - -const SeriesListExample = () => { - const [selectedDicom, setSelectedDicom] = useState(null); - const [modalVisible, setModalVisible] = useState(false); - - const openDicom = (dicomUrl: string) => { - setSelectedDicom(dicomUrl); - setModalVisible(true); - }; - - const closeDicom = () => { - setModalVisible(false); - setSelectedDicom(null); - }; - - return ( - <> - {seriesList.map((series) => ( - openDicom(series.dicomUrl)} - > - {series.description} - - ))} - - - - ); -}; - -// Example 3: Usage with Error Handling -import { DicomViewerModal } from '../../../shared/components'; - -const ErrorHandlingExample = () => { - const [dicomModalState, setDicomModalState] = useState({ - visible: false, - url: '', - title: '', - }); - - const openDicomWithValidation = (url: string, title: string) => { - if (!url) { - Alert.alert('Error', 'No DICOM URL available'); - return; - } - - setDicomModalState({ - visible: true, - url, - title, - }); - }; - - const closeDicomModal = () => { - setDicomModalState({ - visible: false, - url: '', - title: '', - }); - }; - - return ( - <> - openDicomWithValidation(scan.url, scan.title)} - > - View Scan - - - - - ); -}; - -*/ - -/* - * End of File: DicomViewerModal.example.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/shared/components/DicomViewerTest.tsx b/app/shared/components/DicomViewerTest.tsx deleted file mode 100644 index c8c670d..0000000 --- a/app/shared/components/DicomViewerTest.tsx +++ /dev/null @@ -1,252 +0,0 @@ -/* - * File: DicomViewerTest.tsx - * Description: Test component for DICOM viewer with sample URLs - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ - -import React, { useState } from 'react'; -import { View, Text, StyleSheet, TextInput, TouchableOpacity, ScrollView, Alert } from 'react-native'; -import DicomViewer from './DicomViewer'; - -// Sample DICOM URLs for testing -const SAMPLE_DICOM_URLS = [ - { - name: 'Sample DICOM 1', - url: 'https://ohif-dicom-json-example.s3.amazonaws.com/LIDC-IDRI-0001/01-01-2000-30178/3000566.000000-03192/1-001.dcm' - }, - { - name: 'Sample DICOM 2', - url: 'https://ohif-dicom-json-example.s3.amazonaws.com/LIDC-IDRI-0001/01-01-2000-30178/3000566.000000-03192/1-002.dcm' - }, - { - name: 'Sample DICOM 3', - url: 'https://ohif-dicom-json-example.s3.amazonaws.com/LIDC-IDRI-0001/01-01-2000-30178/3000566.000000-03192/1-003.dcm' - } -]; - -export default function DicomViewerTest(): React.ReactElement { - const [dicomUrl, setDicomUrl] = useState(SAMPLE_DICOM_URLS[0].url); - const [customUrl, setCustomUrl] = useState(''); - - const handleUrlSelect = (url: string) => { - setDicomUrl(url); - setCustomUrl(url); - }; - - const handleCustomUrlSubmit = () => { - if (customUrl.trim()) { - setDicomUrl(customUrl.trim()); - } else { - Alert.alert('Error', 'Please enter a valid URL'); - } - }; - - const handleViewerError = (error: string) => { - console.error('DICOM Viewer Error:', error); - Alert.alert('DICOM Viewer Error', error); - }; - - const handleViewerLoad = () => { - console.log('DICOM Viewer loaded successfully'); - }; - - return ( - - - DICOM Viewer Test - Test different DICOM URLs - - - - Sample DICOM URLs: - {SAMPLE_DICOM_URLS.map((sample, index) => ( - handleUrlSelect(sample.url)} - > - - {sample.name} - - - {sample.url} - - - ))} - - - Custom DICOM URL: - - - - Load - - - - - - Current URL: - {dicomUrl} - - - - - DICOM Viewer: - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#F5F5F5', - }, - header: { - backgroundColor: '#2196F3', - padding: 20, - alignItems: 'center', - }, - title: { - fontSize: 24, - fontWeight: 'bold', - color: '#FFFFFF', - marginBottom: 8, - }, - subtitle: { - fontSize: 16, - color: '#E3F2FD', - }, - urlSection: { - flex: 1, - padding: 16, - }, - sectionTitle: { - fontSize: 18, - fontWeight: '600', - color: '#212121', - marginBottom: 12, - marginTop: 16, - }, - urlButton: { - backgroundColor: '#FFFFFF', - padding: 16, - borderRadius: 8, - marginBottom: 8, - borderWidth: 1, - borderColor: '#E0E0E0', - }, - selectedUrlButton: { - backgroundColor: '#E3F2FD', - borderColor: '#2196F3', - }, - urlButtonText: { - fontSize: 16, - fontWeight: '600', - color: '#212121', - marginBottom: 4, - }, - selectedUrlButtonText: { - color: '#1976D2', - }, - urlText: { - fontSize: 12, - color: '#757575', - fontFamily: 'monospace', - }, - selectedUrlText: { - color: '#1976D2', - }, - customUrlSection: { - marginTop: 16, - }, - inputContainer: { - flexDirection: 'row', - alignItems: 'center', - }, - textInput: { - flex: 1, - backgroundColor: '#FFFFFF', - borderWidth: 1, - borderColor: '#E0E0E0', - borderRadius: 8, - padding: 12, - fontSize: 14, - color: '#212121', - marginRight: 8, - }, - submitButton: { - backgroundColor: '#4CAF50', - paddingHorizontal: 20, - paddingVertical: 12, - borderRadius: 8, - }, - submitButtonText: { - color: '#FFFFFF', - fontSize: 14, - fontWeight: '600', - }, - currentUrlSection: { - marginTop: 16, - backgroundColor: '#FFFFFF', - padding: 16, - borderRadius: 8, - borderWidth: 1, - borderColor: '#E0E0E0', - }, - currentUrl: { - fontSize: 12, - color: '#757575', - fontFamily: 'monospace', - backgroundColor: '#F5F5F5', - padding: 8, - borderRadius: 4, - }, - viewerContainer: { - flex: 2, - backgroundColor: '#000000', - margin: 16, - borderRadius: 8, - overflow: 'hidden', - }, - viewerTitle: { - color: '#FFFFFF', - fontSize: 16, - fontWeight: '600', - padding: 12, - backgroundColor: '#333333', - }, -}); - -/* - * End of File: DicomViewerTest.tsx - * Design & Developed by Tech4Biz Solutions - * Copyright (c) Spurrin Innovations. All rights reserved. - */ diff --git a/app/shared/components/index.ts b/app/shared/components/index.ts index 5e1638a..381d89e 100644 --- a/app/shared/components/index.ts +++ b/app/shared/components/index.ts @@ -21,9 +21,6 @@ export { default as DicomViewer } from './DicomViewer'; // DICOM Viewer Modal Component export { default as DicomViewerModal } from './DicomViewerModal'; -// DICOM Viewer Test Component -export { default as DicomViewerTest } from './DicomViewerTest'; - // ============================================================================ // TYPE EXPORTS // ============================================================================ diff --git a/app/shared/types/dashboard.ts b/app/shared/types/dashboard.ts index 66fab45..2b125c5 100644 --- a/app/shared/types/dashboard.ts +++ b/app/shared/types/dashboard.ts @@ -59,14 +59,6 @@ export type PatientSortBy = | 'ADMISSION_DATE' | 'LAST_UPDATED'; -export interface QuickAction { - id: string; - title: string; - icon: string; - action: () => void; - isEnabled: boolean; - requiresConfirmation?: boolean; -} export interface DashboardMetrics { responseTime: number; // average response time in minutes diff --git a/app/shared/utils/constants.ts b/app/shared/utils/constants.ts index 12c6f79..5839775 100644 --- a/app/shared/utils/constants.ts +++ b/app/shared/utils/constants.ts @@ -15,79 +15,6 @@ export const API_CONFIG = { RETRY_DELAY: 1000, } as const; -// WebSocket Configuration -export const WEBSOCKET_CONFIG = { - URL: 'wss://ws.neoscan-physician.com', - RECONNECT_INTERVAL: 5000, - MAX_RECONNECT_ATTEMPTS: 10, - HEARTBEAT_INTERVAL: 30000, -} as const; - -// Session Configuration -export const SESSION_CONFIG = { - TIMEOUT: 8 * 60 * 60 * 1000, // 8 hours - INACTIVITY_TIMEOUT: 30 * 60 * 1000, // 30 minutes - DEVICE_REMEMBER_DURATION: 30 * 24 * 60 * 60 * 1000, // 30 days -} as const; - -// Alert Configuration -export const ALERT_CONFIG = { - CRITICAL_TIMEOUT: 2 * 60 * 1000, // 2 minutes - WARNING_TIMEOUT: 10 * 60 * 1000, // 10 minutes - INFO_TIMEOUT: 30 * 60 * 1000, // 30 minutes - MAX_ALERTS: 100, -} as const; - -// Cache Configuration -export const CACHE_CONFIG = { - PATIENT_DATA: 15 * 60 * 1000, // 15 minutes - MEDICAL_RECORDS: 5 * 60 * 1000, // 5 minutes - USER_SETTINGS: 24 * 60 * 60 * 1000, // 24 hours -} as const; - -// UI Configuration -export const UI_CONFIG = { - ANIMATION_DURATION: 300, - DEBOUNCE_DELAY: 300, - THROTTLE_DELAY: 100, - TOUCH_TARGET_SIZE: 44, -} as const; - -// Medical Constants -export const MEDICAL_CONSTANTS = { - NORMAL_VITAL_SIGNS: { - HEART_RATE: { min: 60, max: 100 }, - BLOOD_PRESSURE: { systolic: { min: 90, max: 140 }, diastolic: { min: 60, max: 90 } }, - TEMPERATURE: { min: 36.1, max: 37.2 }, - RESPIRATORY_RATE: { min: 12, max: 20 }, - OXYGEN_SATURATION: { min: 95, max: 100 }, - }, - CRITICAL_VALUES: { - HEART_RATE: { min: 40, max: 140 }, - BLOOD_PRESSURE: { systolic: { min: 70, max: 200 }, diastolic: { min: 40, max: 120 } }, - TEMPERATURE: { min: 35.0, max: 40.0 }, - RESPIRATORY_RATE: { min: 8, max: 30 }, - OXYGEN_SATURATION: { min: 90, max: 100 }, - }, -} as const; - -// Error Messages -export const ERROR_MESSAGES = { - NETWORK_ERROR: 'Network connection error. Please check your internet connection.', - AUTHENTICATION_ERROR: 'Authentication failed. Please log in again.', - SERVER_ERROR: 'Server error. Please try again later.', - VALIDATION_ERROR: 'Please check your input and try again.', - UNKNOWN_ERROR: 'An unexpected error occurred. Please try again.', -} as const; - -// Success Messages -export const SUCCESS_MESSAGES = { - LOGIN_SUCCESS: 'Successfully logged in.', - LOGOUT_SUCCESS: 'Successfully logged out.', - DATA_SAVED: 'Data saved successfully.', - ALERT_ACKNOWLEDGED: 'Alert acknowledged.', - SETTINGS_UPDATED: 'Settings updated successfully.', -} as const; /* * End of File: constants.ts diff --git a/app/shared/utils/helpers.ts b/app/shared/utils/helpers.ts index 7003f61..228f38e 100644 --- a/app/shared/utils/helpers.ts +++ b/app/shared/utils/helpers.ts @@ -81,7 +81,7 @@ export const groupBy = ( * * Use case: Extracting specific patient data fields */ -export const pick = (obj: T, keys: K[]): Pick => { +export const pick = , K extends keyof T>(obj: T, keys: K[]): Pick => { const result = {} as Pick; keys.forEach(key => { if (key in obj) { @@ -106,7 +106,7 @@ export const pick = (obj: T, keys: K[]): Pick => { * * Use case: Removing sensitive data before sending to API */ -export const omit = (obj: T, keys: K[]): Omit => { +export const omit = , K extends keyof T>(obj: T, keys: K[]): Omit => { const result = { ...obj }; keys.forEach(key => { delete result[key]; @@ -259,7 +259,7 @@ export const debounce = any>( func: T, delay: number ): ((...args: Parameters) => void) => { - let timeoutId: NodeJS.Timeout; + let timeoutId: number; return (...args: Parameters) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); diff --git a/app/store/hooks.ts b/app/store/hooks.ts index 4b6d1c6..1bcee2f 100644 --- a/app/store/hooks.ts +++ b/app/store/hooks.ts @@ -54,12 +54,22 @@ export const useAuth = () => { return { // State user: auth.user, + isLoading: auth.loading, + isLoggingIn: auth.loading, + isLoggingOut: false, isAuthenticated: auth.isAuthenticated, - isLoading: auth.isLoading, - isLoggingIn: auth.isLoggingIn, - isLoggingOut: auth.isLoggingOut, error: auth.error, - loginError: auth.loginError, + + // Computed values + isLoggedIn: auth.isAuthenticated, + hasUser: !!auth.user, + userRole: auth.user?.dashboard_role || null, + userId: auth.user?.user_id || null, + userEmail: auth.user?.email || null, + userName: auth.user?.display_name || null, + userAvatar: auth.user?.profile_photo_url || null, + userPermissions: [], + isOnboarded: auth.user?.onboarded || false, // Actions dispatch, @@ -79,7 +89,6 @@ export const useUser = () => { return { user, isLoggedIn: !!user, - hasPermission: (permission: string) => user?.permissions?.includes(permission) || false, }; }; @@ -115,44 +124,6 @@ export const useDashboard = () => { }; }; -// ============================================================================ -// ALERTS HOOKS -// ============================================================================ - -/** - * Use Alerts Hook - * - * Purpose: Get alerts state and actions - * - * @returns Alerts state and actions - */ -export const useAlerts = () => { - const alerts = useAppSelector((state) => state.alerts); - const dispatch = useAppDispatch(); - - return { - // State - alerts: alerts.alerts, - isLoading: alerts.isLoading, - isRefreshing: alerts.isRefreshing, - error: alerts.error, - lastUpdated: alerts.lastUpdated, - unreadCount: alerts.unreadCount, - criticalCount: alerts.criticalCount, - selectedFilter: alerts.selectedFilter, - sortBy: alerts.sortBy, - sortOrder: alerts.sortOrder, - - // Computed values - criticalAlerts: alerts.alerts.filter(alert => alert.priority === 'CRITICAL'), - unreadAlerts: alerts.alerts.filter(alert => !alert.isRead), - acknowledgedAlerts: alerts.alerts.filter(alert => alert.isAcknowledged), - - // Actions - dispatch, - }; -}; - // ============================================================================ // PATIENT CARE HOOKS // ============================================================================ @@ -186,9 +157,9 @@ export const usePatientCare = () => { lastUpdated: patientCare.lastUpdated, // Computed values - activePatients: patientCare.patients.filter(patient => patient.status === 'ACTIVE'), - criticalPatients: patientCare.patients.filter(patient => patient.priority === 'CRITICAL'), - dischargedPatients: patientCare.patients.filter(patient => patient.status === 'DISCHARGED'), + activePatients: patientCare.patients.filter(patient => patient.patient_info.status === 'Active'), + criticalPatients: patientCare.patients.filter(patient => patient.patient_info.report_status === 'Critical'), + dischargedPatients: patientCare.patients.filter(patient => patient.patient_info.status === 'Discharged'), // Actions dispatch, @@ -228,47 +199,6 @@ export const useSettings = () => { }; }; -// ============================================================================ -// UI HOOKS -// ============================================================================ - -/** - * Use UI Hook - * - * Purpose: Get UI state and actions - * - * @returns UI state and actions - */ -export const useUI = () => { - const ui = useAppSelector((state) => state.ui); - const dispatch = useAppDispatch(); - - return { - // State - isLoading: ui.isLoading, - loadingMessage: ui.loadingMessage, - isModalOpen: ui.isModalOpen, - modalType: ui.modalType, - modalData: ui.modalData, - isOverlayVisible: ui.isOverlayVisible, - overlayType: ui.overlayType, - currentScreen: ui.currentScreen, - navigationStack: ui.navigationStack, - isDarkMode: ui.isDarkMode, - fontSize: ui.fontSize, - highContrast: ui.highContrast, - isRefreshing: ui.isRefreshing, - isScrolling: ui.isScrolling, - lastInteraction: ui.lastInteraction, - hasError: ui.hasError, - errorMessage: ui.errorMessage, - errorType: ui.errorType, - - // Actions - dispatch, - }; -}; - // ============================================================================ // UTILITY HOOKS // ============================================================================ diff --git a/app/store/index.ts b/app/store/index.ts index 14cf052..9156410 100644 --- a/app/store/index.ts +++ b/app/store/index.ts @@ -8,17 +8,14 @@ import { configureStore, combineReducers } from '@reduxjs/toolkit'; import { persistStore, persistReducer, FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER } from 'redux-persist'; import AsyncStorage from '@react-native-async-storage/async-storage'; +import aiDashboardReducer from '../modules/Dashboard/redux/aiDashboardSlice'; // Import all slice reducers from their respective modules import authReducer from '../modules/Auth/redux/authSlice'; import dashboardReducer from '../modules/Dashboard/redux/dashboardSlice'; -import aiDashboardReducer from '../modules/Dashboard/redux/aiDashboardSlice'; import patientCareReducer from '../modules/PatientCare/redux/patientCareSlice'; -import alertsReducer from '../modules/Dashboard/redux/alertsSlice'; import settingsReducer from '../modules/Settings/redux/settingsSlice'; -import uiReducer from '../modules/Dashboard/redux/uiSlice'; import hospitalReducer from '../modules/Auth/redux/hospitalSlice'; -import aiPredictionReducer from '../modules/AIPrediction/redux/aiPredictionSlice'; // ============================================================================ // REDUX PERSIST CONFIGURATION @@ -50,15 +47,11 @@ const persistConfig = { 'auth', // Authentication state (user login, tokens) 'settings', // User preferences and settings 'patientCare', // Patient data cache - 'aiPrediction', // AI prediction data cache ], // Blacklist: Don't persist these reducers blacklist: [ - 'ui', // UI state (loading, modals, etc.) - 'alerts', // Temporary alerts and notifications 'dashboard', // Real-time dashboard data - 'aiDashboard', // AI dashboard statistics (fetched fresh each time) 'hospital', // Hospital data (fetched fresh each time) ], @@ -87,23 +80,17 @@ const persistConfig = { * Structure: * - auth: Authentication and user management * - dashboard: ER dashboard data and statistics - * - aiDashboard: AI analysis dashboard statistics * - patientCare: Patient information and medical records - * - aiPrediction: AI prediction cases and analysis - * - alerts: Critical alerts and notifications * - settings: User preferences and app settings - * - ui: User interface state (loading, modals, etc.) + * - hospital: Hospital data and information */ const rootReducer = combineReducers({ auth: authReducer, dashboard: dashboardReducer, - aiDashboard: aiDashboardReducer, patientCare: patientCareReducer, - aiPrediction: aiPredictionReducer, - alerts: alertsReducer, settings: settingsReducer, - ui: uiReducer, hospital: hospitalReducer, + aiDashboard: aiDashboardReducer, }); // ============================================================================ diff --git a/package.json b/package.json index 73c17f3..3c4fbd6 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,10 @@ "version": "0.0.1", "private": true, "scripts": { - "android": "react-native run-android", - "ios": "react-native run-ios", + "android": "npx react-native run-android", + "ios": "npx react-native run-ios", "lint": "eslint .", - "start": "react-native start", + "start": "npx react-native start", "test": "jest" }, "dependencies": {