first commit

This commit is contained in:
yashwin-foxy 2025-08-05 18:01:36 +05:30
parent 5c241a0a23
commit 7a9e610b7f
123 changed files with 21139 additions and 224 deletions

351
.cursor/rules/appflow.mdc Normal file
View File

@ -0,0 +1,351 @@
---
alwaysApply: true
---
# Physician App - Application Flow Rules
## 🚀 App Launch & Authentication Flow
### 1. Initial App Launch
```
App Launch → Splash Screen → Authentication Check
[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
├── Critical Alerts (Priority 1)
├── Pending Scans (Priority 2)
├── Recent Reports (Priority 3)
└── All Patients (Complete View)
```
### 5. Real-time Data Flow
```
WebSocket Connection → Live Updates
├── Patient Status Changes
├── New Scan Results
├── Critical Findings
├── Bed Assignments
└── Shift Changes
```
### 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.

View File

@ -0,0 +1,308 @@
---
alwaysApply: true
---
# Physician App - Project Structure & File Naming Rules
## 📁 Directory Structure Rules
### 1. Root Level Organization
```
NeoScan_Physician/
├── app/ # Main application code
├── docs/ # Documentation
├── android/ # Android native code
├── ios/ # iOS native code
├── index.js # React Native entry point
├── package.json # Dependencies
├── tsconfig.json # TypeScript config
├── metro.config.js # Metro bundler config
├── babel.config.js # Babel config
└── .eslintrc.js # ESLint config
```
### 2. App Directory Structure
```
app/
├── modules/ # Feature-based modules
├── shared/ # Shared utilities & components
├── store/ # Redux store configuration
├── navigation/ # Navigation setup
├── theme/ # Styling & theming
├── config/ # Configuration files
├── assets/ # Static assets
├── localization/ # i18n
├── App.tsx # Root component
└── index.tsx # App entry point
```
## 🏗️ Module Architecture Rules
### 3. Module Structure (Feature-based)
Each module MUST follow this structure:
```
modules/ModuleName/
├── components/ # Reusable UI components
├── screens/ # Screen components
├── hooks/ # Custom hooks
├── redux/ # State management
├── services/ # API & external services
├── __tests__/ # Test files
└── index.ts # Module exports
```
### 4. Required Modules
- **Auth/** - Authentication & SSO
- **Dashboard/** - ER Dashboard & patient tracking
- **PatientCare/** - Patient details & medical records
- **Settings/** - User preferences & app settings
## 📝 File Naming Conventions
### 5. Component Files
- **PascalCase** for all component files
- **Suffix with type**: `.tsx` for components, `.ts` for utilities
- **Examples**:
- `LoginScreen.tsx`
- `PatientCard.tsx`
- `CriticalAlerts.tsx`
- `HospitalSSO.tsx`
### 6. Hook Files
- **camelCase** with `use` prefix
- **Examples**:
- `useAuth.ts`
- `usePatientList.ts`
- `useRealTimeAlerts.ts`
- `useCriticalAlerts.ts`
### 7. Service Files
- **camelCase** with descriptive names
- **Suffix with type**: `API.ts`, `Service.ts`
- **Examples**:
- `authAPI.ts`
- `patientCareAPI.ts`
- `notificationService.ts`
- `emrIntegration.ts`
### 8. Redux Files
- **camelCase** with descriptive suffixes
- **Examples**:
- `authSlice.ts`
- `erDashboardSlice.ts`
- `patientCareActions.ts`
- `dashboardSelectors.ts`
### 9. Test Files
- **Same name as source file** + `.test.ts` or `.test.tsx`
- **Examples**:
- `LoginScreen.test.tsx`
- `useAuth.test.ts`
- `authSlice.test.ts`
- `PatientCard.test.tsx`
## 🔧 Shared Components Rules
### 10. UI Components Structure
```
shared/components/
├── UI/ # Basic UI components
│ ├── Button.tsx
│ ├── Input.tsx
│ ├── Card.tsx
│ ├── Modal.tsx
│ ├── Badge.tsx
│ ├── Spinner.tsx
│ ├── Alert.tsx
│ ├── Dropdown.tsx
│ ├── Tabs.tsx
│ ├── ProgressBar.tsx
│ └── index.ts
├── Forms/ # Form-related components
│ ├── FormField.tsx
│ ├── ValidationMessage.tsx
│ ├── FormContainer.tsx
│ └── index.ts
├── Icons/ # Icon components
│ ├── MedicalIcons.tsx
│ ├── StatusIcons.tsx
│ ├── NavigationIcons.tsx
│ └── index.ts
└── index.ts
```
### 11. Utility Files
```
shared/utils/
├── api.ts # API utilities
├── constants.ts # App constants
├── helpers.ts # Helper functions
├── validators.ts # Validation functions
├── formatters.ts # Data formatting
├── dateUtils.ts # Date utilities
├── medicalUtils.ts # Medical-specific utilities
├── imageUtils.ts # Image processing
├── stringUtils.ts # String manipulation
└── index.ts
```
## 🎨 Assets Organization
### 12. Image Assets
```
assets/images/
├── logos/ # Hospital & app logos
├── icons/ # UI icons
│ ├── medical/ # Medical-specific icons
│ ├── ui/ # General UI icons
│ └── status/ # Status indicators
├── backgrounds/ # Background images
└── placeholders/ # Placeholder images
```
### 13. Asset Naming
- **kebab-case** for all asset files
- **Examples**:
- `hospital-logo.png`
- `critical-alert.mp3`
- `ct-scan-placeholder.png`
- `emergency-bg.jpg`
## 📱 Navigation Structure
### 14. Navigation Files
```
navigation/
├── AppNavigator.tsx # Root navigator
├── AuthNavigator.tsx # Authentication flow
├── MainNavigator.tsx # Main app flow
├── TabNavigator.tsx # Tab navigation
├── navigationTypes.ts # Type definitions
├── navigationUtils.ts # Navigation utilities
└── __tests__/
```
## 🔐 Configuration Rules
### 15. Environment & Config
```
config/
├── env.ts # Environment variables
├── api.ts # API configuration
├── websocket.ts # WebSocket config
├── notifications.ts # Notification config
├── security.ts # Security settings
└── index.ts
```
## 📚 Documentation Rules
### 16. Documentation Structure
```
docs/
├── README.md # Project overview
├── ARCHITECTURE.md # Architecture documentation
├── API.md # API documentation
├── DEPLOYMENT.md # Deployment guide
├── TESTING.md # Testing guidelines
├── SECURITY.md # Security guidelines
└── 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
### 18. Dependencies Organization
- **Core dependencies** in root `package.json`
- **Platform-specific** dependencies in respective folders
- **Dev dependencies** clearly separated
- **Peer dependencies** explicitly declared
## 🔄 Import/Export Rules
### 19. Import Conventions
- **Absolute imports** for shared utilities
- **Relative imports** for module-internal files
- **Index files** for clean imports
- **Barrel exports** for module APIs
### 20. Export Patterns
```typescript
// Module index.ts
export { default as ComponentName } from './components/ComponentName';
export { useHookName } from './hooks/useHookName';
export { actionName } from './redux/actions';
export type { TypeName } from './types';
```
## 🚫 Naming Restrictions
### 21. Forbidden Patterns
- ❌ No spaces in file names
- ❌ No special characters except `-` and `_`
- ❌ No uppercase in utility/service files
- ❌ No generic names like `utils.ts` or `helpers.ts`
- ❌ No abbreviations unless universally understood
### 22. Required Patterns
- ✅ Descriptive, self-documenting names
- ✅ Consistent casing within categories
- ✅ Clear separation of concerns
- ✅ Meaningful directory structure
- ✅ Proper TypeScript extensions
## 📋 File Size Guidelines
### 23. Component Limits
- **Single component files**: Max 300 lines
- **Complex components**: Split into smaller components
- **Utility files**: Max 200 lines
- **Service files**: Max 150 lines per service
### 24. Module Limits
- **Module components**: Max 10 files per category
- **Module services**: Max 5 files
- **Module hooks**: Max 8 files
- **Module redux**: Max 6 files
## 🔍 Code Organization Principles
### 25. Separation of Concerns
- **UI Logic** in components
- **Business Logic** in hooks/services
- **State Management** in Redux
- **Data Fetching** in services
- **Utilities** in shared/utils
### 26. Reusability
- **Shared components** in shared/components
- **Common utilities** in shared/utils
- **Type definitions** in shared/types
- **Constants** in shared/constants
This structure ensures maintainability, scalability, and consistency across the Physician App codebase.
and it should add proper comments in the each file for better understanding the flow.
should follow rule file while generating code.Each file should contain this as header
/*
* File: FILE_NAME.tsx
* Description: Main chat screen component
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
file footer
/*
* End of File: ChatScreen.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
and it should add proper comments in the file for better understanding the flow.
should follow rule file while generating code.

718
.cursor/rules/themeflow.mdc Normal file
View File

@ -0,0 +1,718 @@
---
alwaysApply: true
---
# Physician App - Theme & UI Design Rules
## 🎨 Color Palette - "Modern Healthcare Blue"
### 1. Primary Color Scheme
```typescript
// Primary Colors
Primary: '#2196F3' // Material Blue - Main brand color
Secondary: '#1976D2' // Darker Blue - Secondary actions
Tertiary: '#E3F2FD' // Very Light Blue - Backgrounds
Quaternary: '#0D47A1' // Deep Blue - Accents
// Text Colors
TextPrimary: '#212121' // Dark Gray - Main text
TextSecondary: '#757575' // Medium Gray - Secondary text
TextMuted: '#9E9E9E' // Light Gray - Muted text
// Background Colors
Background: '#FFFFFF' // White - Primary background
BackgroundAlt: '#FAFAFA' // Light Gray - Alternative background
BackgroundAccent: '#F5F5F5' // Soft Gray - Accent backgrounds
```
### 2. Status & Feedback Colors
```typescript
// Status Colors
Success: '#4CAF50' // Material Green - Success states
Warning: '#FF9800' // Material Orange - Warning states
Error: '#F44336' // Material Red - Error states
Info: '#2196F3' // Material Blue - Information states
// UI Elements
Border: '#E0E0E0' // Light Gray Border
CardBackground: '#FFFFFF' // White - Card backgrounds
Shadow: 'rgba(0, 0, 0, 0.1)' // Subtle Gray Shadow
```
## 🏗️ Typography System
### 3. Font Hierarchy
```typescript
// Font Families
PrimaryFont: 'Roboto' // Main font family
SecondaryFont: 'Medical-Icons' // Icon font
// Font Weights
Light: 300
Regular: 400
Medium: 500
Bold: 700
// Font Sizes
DisplayLarge: 32px // Main headings
DisplayMedium: 24px // Section headings
DisplaySmall: 20px // Subsection headings
BodyLarge: 16px // Body text
BodyMedium: 14px // Secondary text
BodySmall: 12px // Captions
Caption: 10px // Small labels
```
### 4. Line Heights & Spacing
```typescript
// Line Heights
Tight: 1.2 // Headings
Normal: 1.4 // Body text
Relaxed: 1.6 // Long text
// Letter Spacing
Tight: -0.5px // Headings
Normal: 0px // Body text
Wide: 0.5px // Labels
```
## 📐 Spacing & Layout
### 5. Spacing Scale
```typescript
// Base Spacing Unit: 4px
Spacing: {
xs: 4, // 4px
sm: 8, // 8px
md: 16, // 16px
lg: 24, // 24px
xl: 32, // 32px
xxl: 48, // 48px
xxxl: 64 // 64px
}
```
### 6. Layout Rules
```typescript
// Container Max Widths
Mobile: 375
Tablet: 768
Desktop: 1024
// Border Radius
Small: 4
Medium: 8
Large: 12
XLarge: 16
Round: 50
// Shadows (React Native StyleSheet)
Small: {
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2
}
Medium: {
shadowColor: '#000000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.15,
shadowRadius: 8,
elevation: 4
}
Large: {
shadowColor: '#000000',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.2,
shadowRadius: 16,
elevation: 8
}
```
## 🎯 Component Design Rules
### 7. Button Design System
```typescript
// Button Variants
Primary: {
backgroundColor: '#2196F3',
borderColor: '#2196F3',
borderRadius: 8,
shadowColor: '#2196F3',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 3
}
Secondary: {
backgroundColor: 'transparent',
borderColor: '#2196F3',
borderRadius: 8
}
Success: {
backgroundColor: '#4CAF50',
borderColor: '#4CAF50',
borderRadius: 8,
shadowColor: '#4CAF50',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 3
}
Critical: {
backgroundColor: '#F44336',
borderColor: '#F44336',
borderRadius: 8,
shadowColor: '#F44336',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 3
}
// Button Sizes
Small: { paddingHorizontal: 16, paddingVertical: 8, fontSize: 14, borderRadius: 6 }
Medium: { paddingHorizontal: 24, paddingVertical: 12, fontSize: 16, borderRadius: 8 }
Large: { paddingHorizontal: 32, paddingVertical: 16, fontSize: 18, borderRadius: 10 }
```
### 8. Card Design Rules
```typescript
// Card Variants
Default: {
backgroundColor: '#FFFFFF',
borderColor: '#E0E0E0',
borderRadius: 12,
padding: 16,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2
}
Elevated: {
backgroundColor: '#FFFFFF',
borderColor: '#E0E0E0',
borderRadius: 12,
padding: 16,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.15,
shadowRadius: 8,
elevation: 4
}
Critical: {
backgroundColor: '#FFEBEE',
borderColor: '#F44336',
borderRadius: 12,
padding: 16,
shadowColor: '#F44336',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2
}
PatientCard: {
backgroundColor: '#FFFFFF',
borderColor: '#E0E0E0',
borderRadius: 16,
padding: 20,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
elevation: 2
}
```
### 9. Input Field Design
```typescript
// Input States
Default: {
borderColor: '#E0E0E0',
backgroundColor: '#FFFFFF',
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 12
}
Focused: {
borderColor: '#2196F3',
backgroundColor: '#FFFFFF',
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 12
}
Error: {
borderColor: '#F44336',
backgroundColor: '#FFEBEE',
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 12
}
Disabled: {
borderColor: '#E0E0E0',
backgroundColor: '#F5F5F5',
borderRadius: 8,
paddingHorizontal: 12,
paddingVertical: 12
}
```
## 🚨 Alert & Status Design (Following Screenshot UI)
### 10. Alert Priority System
```typescript
// Critical Alerts
Critical: {
backgroundColor: '#FFEBEE',
borderColor: '#F44336',
borderRadius: 8,
padding: 16
}
// Warning Alerts
Warning: {
backgroundColor: '#FFF3E0',
borderColor: '#FF9800',
borderRadius: 8,
padding: 16
}
// Success Alerts
Success: {
backgroundColor: '#E8F5E8',
borderColor: '#4CAF50',
borderRadius: 8,
padding: 16
}
// Info Alerts
Info: {
backgroundColor: '#E3F2FD',
borderColor: '#2196F3',
borderRadius: 8,
padding: 16
}
// Patient Status
Active: {
backgroundColor: '#E3F2FD',
borderColor: '#2196F3',
borderRadius: 12,
paddingHorizontal: 8,
paddingVertical: 4
}
Pending: {
backgroundColor: '#FFF3E0',
borderColor: '#FF9800',
borderRadius: 12,
paddingHorizontal: 8,
paddingVertical: 4
}
Completed: {
backgroundColor: '#E8F5E8',
borderColor: '#4CAF50',
borderRadius: 12,
paddingHorizontal: 8,
paddingVertical: 4
}
```
### 11. Badge & Tag Design (Following Screenshot UI)
```typescript
// Status Badges
StatusBadge: {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 12,
fontSize: 12,
fontWeight: '500'
}
// Priority Badges
PriorityBadge: {
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 16,
fontSize: 12,
fontWeight: '600'
}
// Medical Badges
MedicalBadge: {
paddingHorizontal: 10,
paddingVertical: 5,
borderRadius: 14,
fontSize: 11,
fontWeight: '500'
}
```
## 📱 Screen-Specific Design Rules
### 12. ER Dashboard Screen Design (Following Screenshot UI)
```typescript
// Header Section
Header: {
backgroundColor: '#FFFFFF',
paddingHorizontal: 16,
paddingVertical: 12,
borderBottomColor: '#E0E0E0',
borderBottomWidth: 1
}
// Critical Alerts Section (Like promotional banner but for emergencies)
CriticalAlerts: {
backgroundColor: '#FFEBEE',
borderColor: '#F44336',
borderRadius: 16,
padding: 20,
marginHorizontal: 16,
marginVertical: 12,
shadowColor: '#F44336',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 8,
elevation: 4
}
// Patient List Section (Like specialist filter)
PatientList: {
paddingHorizontal: 16,
paddingVertical: 8,
backgroundColor: '#FFFFFF'
}
// Quick Actions (Like service icons but for emergency actions)
QuickActions: {
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: '#F5F5F5',
justifyContent: 'center',
alignItems: 'center',
marginHorizontal: 8
}
// Patient Cards (Like doctor cards but for patients)
PatientCard: {
backgroundColor: '#FFFFFF',
borderRadius: 16,
padding: 16,
marginHorizontal: 8,
marginVertical: 4,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
elevation: 2,
minWidth: 200
}
```
### 13. Patient Details Screen Design (Following Screenshot UI)
```typescript
// Patient Header
PatientHeader: {
backgroundColor: '#FFFFFF',
padding: 20,
borderBottomColor: '#E0E0E0',
borderBottomWidth: 1
}
// Patient Info Card
PatientInfoCard: {
backgroundColor: '#FFFFFF',
borderRadius: 16,
padding: 20,
margin: 16,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 4
}
// Vital Signs Section (Like contact options but for medical data)
VitalSigns: {
flexDirection: 'row',
justifyContent: 'space-around',
paddingVertical: 16,
borderBottomColor: '#E0E0E0',
borderBottomWidth: 1
}
// Medical History Section (Like calendar section but for medical records)
MedicalHistory: {
backgroundColor: '#FFFFFF',
borderRadius: 12,
padding: 16,
margin: 16,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.08,
shadowRadius: 4,
elevation: 2
}
// Action Buttons (Like booking button but for medical actions)
ActionButton: {
backgroundColor: '#2196F3',
borderRadius: 12,
padding: 16,
margin: 16,
alignItems: 'center',
shadowColor: '#2196F3',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 6
}
```
### 14. Login Screen Design (Following Screenshot UI)
```typescript
// Login Container
LoginContainer: {
backgroundColor: '#F1FDFF',
padding: 24,
borderRadius: 12,
shadowColor: '#2196F3',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.15,
shadowRadius: 8,
elevation: 4
}
// Hospital Logo
Logo: {
width: 120,
height: 120,
marginBottom: 32
}
// Form Fields
FormField: {
marginBottom: 16,
backgroundColor: '#FFFFFF',
borderColor: '#E0E0E0',
borderRadius: 8
}
// Login Button
LoginButton: {
backgroundColor: '#2196F3',
borderRadius: 8,
padding: 16,
fontSize: 16,
fontWeight: '600'
}
```
## 🎨 Icon Design System
### 15. Icon Specifications
```typescript
// Icon Sizes
IconSizes: {
xs: 12, // Extra small
sm: 16, // Small
md: 24, // Medium (default)
lg: 32, // Large
xl: 48 // Extra large
}
// Icon Colors
IconColors: {
Primary: '#2196F3',
Secondary: '#1976D2',
Success: '#4CAF50',
Warning: '#FF9800',
Error: '#F44336',
Info: '#2196F3',
Muted: '#9E9E9E'
}
// Medical Icons
MedicalIcons: {
Stethoscope: 'medical-stethoscope',
Heart: 'medical-heart',
Brain: 'medical-brain',
Emergency: 'medical-emergency',
Patient: 'medical-patient',
Bed: 'medical-bed',
Alert: 'medical-alert',
VitalSigns: 'medical-vital-signs',
Medication: 'medical-medication',
Lab: 'medical-lab',
Imaging: 'medical-imaging',
Surgery: 'medical-surgery'
}
```
## 📊 Data Visualization
### 16. Chart Colors
```typescript
// Chart Color Palette
ChartColors: {
Primary: '#2196F3',
Secondary: '#1976D2',
Tertiary: '#E3F2FD',
Quaternary: '#0D47A1',
Success: '#4CAF50',
Warning: '#FF9800',
Error: '#F44336'
}
// Vital Signs Charts
VitalSignsChart: {
backgroundColor: '#FFFFFF',
gridColor: '#E0E0E0',
lineColor: '#2196F3',
pointColor: '#1976D2'
}
// Patient Status Charts
PatientStatusChart: {
backgroundColor: '#FFFFFF',
gridColor: '#E0E0E0',
lineColor: '#4CAF50',
pointColor: '#2E7D32'
}
```
## 🌙 Dark Mode Support
### 17. Dark Mode Colors
```typescript
// Dark Mode Palette
DarkMode: {
Background: '#121212',
BackgroundAlt: '#1E1E1E',
BackgroundAccent: '#2D2D2D',
TextPrimary: '#FFFFFF',
TextSecondary: '#B0B0B0',
TextMuted: '#808080',
Border: '#404040',
CardBackground: '#1E1E1E',
Primary: '#2196F3',
Secondary: '#1976D2'
}
```
## 📱 Responsive Design
### 18. Breakpoint System
```typescript
// Breakpoints
Breakpoints: {
Mobile: 375,
Tablet: 768,
Desktop: 1024,
LargeDesktop: 1440
}
// Responsive Spacing
ResponsiveSpacing: {
Mobile: { padding: 16, margin: 8 },
Tablet: { padding: 24, margin: 16 },
Desktop: { padding: 32, margin: 24 }
}
```
## 🎯 Accessibility Rules
### 19. Accessibility Standards
```typescript
// Color Contrast
ContrastRatios: {
Normal: 4.5, // Minimum for normal text
Large: 3.0, // Minimum for large text
UI: 3.0 // Minimum for UI elements
}
// Touch Targets
TouchTargets: {
Minimum: 44, // Minimum touch target size
Preferred: 48 // Preferred touch target size
}
// Focus Indicators
FocusIndicator: {
Color: '#2196F3',
Width: 2,
Style: 'solid'
}
```
## 🔄 Animation & Transitions
### 20. Animation Rules
```typescript
// Animation Durations
Durations: {
Fast: 150, // Quick interactions
Normal: 300, // Standard transitions
Slow: 500 // Complex animations
}
// Easing Functions
Easing: {
Standard: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
Deceleration: 'cubic-bezier(0.0, 0.0, 0.2, 1)',
Acceleration: 'cubic-bezier(0.4, 0.0, 1, 1)'
}
// Transition Types
Transitions: {
Fade: { opacity: [0, 1], duration: 300 },
Slide: { transform: [{ translateY: [20, 0] }], duration: 300 },
Scale: { transform: [{ scale: [0.95, 1] }], duration: 200 }
}
```
## 🎨 Design Tokens
### 21. Design Token Structure
```typescript
// Theme Object Structure
Theme: {
colors: ColorPalette,
typography: TypographySystem,
spacing: SpacingScale,
borderRadius: BorderRadiusScale,
shadows: ShadowSystem,
breakpoints: BreakpointSystem,
animations: AnimationSystem
}
// Usage Example
const styles = StyleSheet.create({
container: {
backgroundColor: theme.colors.background,
padding: theme.spacing.md,
borderRadius: theme.borderRadius.medium,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2
}
});
```
This comprehensive theme system ensures consistency, accessibility, and modern healthcare aesthetics across the Physician App, following the clean and professional design patterns shown in the reference screenshots while maintaining the ER workflow functionality.

2
.env Normal file
View File

@ -0,0 +1,2 @@
BASE_URL='https://neoscan-backend.tech4bizsolutions.com'
# BASE_URL='http://192.168.1.87:3000'

355
PROJECT_STRUCTURE.md Normal file
View File

@ -0,0 +1,355 @@
/*
* File: PROJECT_STRUCTURE.md
* Description: Complete project structure and architecture documentation
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
# NeoScan Physician App - Project Structure
## 📁 Complete Directory Structure
```
NeoScan_Physician/
├── app/ # Main application code
│ ├── modules/ # Feature-based modules
│ │ ├── Auth/ # Authentication module
│ │ │ ├── components/ # Auth-specific components
│ │ │ ├── screens/ # Auth screens
│ │ │ │ └── LoginScreen.tsx # Main login screen
│ │ │ ├── hooks/ # Auth custom hooks
│ │ │ ├── redux/ # Auth state management
│ │ │ ├── services/ # Auth API services
│ │ │ └── index.ts # Auth module exports
│ │ ├── 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
│ │ │ │ └── ERDashboardScreen.tsx # Main ER dashboard
│ │ │ ├── hooks/ # Dashboard custom hooks
│ │ │ ├── redux/ # Dashboard state management
│ │ │ ├── services/ # Dashboard API services
│ │ │ └── index.ts # Dashboard module exports
│ │ ├── PatientCare/ # Patient management module
│ │ │ ├── components/ # Patient care components
│ │ │ ├── screens/ # Patient care screens
│ │ │ ├── hooks/ # Patient care hooks
│ │ │ ├── redux/ # Patient care state
│ │ │ ├── services/ # Patient care services
│ │ │ └── index.ts # Patient care exports
│ │ └── Settings/ # App settings module
│ │ ├── components/ # Settings components
│ │ ├── screens/ # Settings screens
│ │ ├── hooks/ # Settings hooks
│ │ ├── redux/ # Settings state
│ │ ├── services/ # Settings services
│ │ └── index.ts # Settings exports
│ ├── shared/ # Shared utilities & components
│ │ ├── components/ # Reusable UI components
│ │ │ ├── UI/ # Basic UI components
│ │ │ │ ├── Button.tsx # Button component
│ │ │ │ ├── Input.tsx # Input field component
│ │ │ │ ├── Card.tsx # Card component
│ │ │ │ ├── Modal.tsx # Modal component
│ │ │ │ ├── Badge.tsx # Badge component
│ │ │ │ ├── Spinner.tsx # Loading spinner
│ │ │ │ ├── Alert.tsx # Alert component
│ │ │ │ ├── Dropdown.tsx # Dropdown component
│ │ │ │ ├── Tabs.tsx # Tab component
│ │ │ │ ├── ProgressBar.tsx # Progress bar
│ │ │ │ └── index.ts # UI components export
│ │ │ ├── Forms/ # Form-related components
│ │ │ │ ├── FormField.tsx # Form field component
│ │ │ │ ├── ValidationMessage.tsx # Validation message
│ │ │ │ ├── FormContainer.tsx # Form container
│ │ │ │ └── index.ts # Form components export
│ │ │ ├── Icons/ # Icon components
│ │ │ │ ├── MedicalIcons.tsx # Medical-specific icons
│ │ │ │ ├── StatusIcons.tsx # Status indicators
│ │ │ │ ├── NavigationIcons.tsx # Navigation icons
│ │ │ │ └── index.ts # Icon components export
│ │ │ └── index.ts # Shared components export
│ │ ├── utils/ # Utility functions
│ │ │ ├── api.ts # API utilities
│ │ │ ├── constants.ts # App constants
│ │ │ ├── helpers.ts # Helper functions
│ │ │ ├── validators.ts # Validation functions
│ │ │ ├── formatters.ts # Data formatting
│ │ │ ├── dateUtils.ts # Date utilities
│ │ │ ├── medicalUtils.ts # Medical-specific utilities
│ │ │ ├── imageUtils.ts # Image processing
│ │ │ ├── stringUtils.ts # String manipulation
│ │ │ └── index.ts # Utils export
│ │ ├── types/ # TypeScript type definitions
│ │ │ ├── auth.ts # Authentication types
│ │ │ ├── patient.ts # Patient-related types
│ │ │ ├── dashboard.ts # Dashboard types
│ │ │ ├── alerts.ts # Alert types
│ │ │ ├── common.ts # Common types
│ │ │ └── index.ts # Types export
│ │ └── index.ts # Shared module export
│ ├── theme/ # Design system & theming
│ │ ├── colors.ts # Color palette
│ │ ├── typography.ts # Typography system
│ │ ├── spacing.ts # Spacing & layout
│ │ ├── shadows.ts # Shadow system
│ │ ├── animations.ts # Animation system
│ │ ├── theme.ts # Main theme object
│ │ └── index.ts # Theme export
│ ├── navigation/ # Navigation setup
│ │ ├── AppNavigator.tsx # Root navigator
│ │ ├── AuthNavigator.tsx # Authentication flow
│ │ ├── MainNavigator.tsx # Main app flow
│ │ ├── TabNavigator.tsx # Tab navigation
│ │ ├── navigationTypes.ts # Navigation types
│ │ ├── navigationUtils.ts # Navigation utilities
│ │ └── __tests__/ # Navigation tests
│ ├── store/ # Redux store configuration
│ │ ├── index.ts # Store configuration
│ │ ├── rootReducer.ts # Root reducer
│ │ ├── middleware.ts # Custom middleware
│ │ └── __tests__/ # Store tests
│ ├── config/ # Configuration files
│ │ ├── env.ts # Environment variables
│ │ ├── api.ts # API configuration
│ │ ├── websocket.ts # WebSocket config
│ │ ├── notifications.ts # Notification config
│ │ ├── security.ts # Security settings
│ │ └── index.ts # Config export
│ ├── assets/ # Static assets
│ │ ├── images/ # Image assets
│ │ │ ├── logos/ # Hospital & app logos
│ │ │ ├── icons/ # UI icons
│ │ │ │ ├── medical/ # Medical-specific icons
│ │ │ │ ├── ui/ # General UI icons
│ │ │ │ └── status/ # Status indicators
│ │ │ ├── backgrounds/ # Background images
│ │ │ └── placeholders/ # Placeholder images
│ │ ├── fonts/ # Font files
│ │ └── sounds/ # Audio assets
│ ├── localization/ # Internationalization
│ │ ├── en/ # English translations
│ │ ├── es/ # Spanish translations
│ │ ├── fr/ # French translations
│ │ └── index.ts # i18n configuration
│ ├── App.tsx # Root component
│ └── index.tsx # App entry point
├── android/ # Android native code
│ ├── app/ # Android app module
│ │ ├── build.gradle # App build configuration
│ │ ├── debug.keystore # Debug keystore
│ │ ├── proguard-rules.pro # ProGuard rules
│ │ └── src/ # Source code
│ │ ├── debug/ # Debug configuration
│ │ │ └── AndroidManifest.xml # Debug manifest
│ │ └── main/ # Main source
│ │ ├── AndroidManifest.xml # Main manifest
│ │ ├── java/ # Java source
│ │ │ └── com/ # Package structure
│ │ │ └── neoscan_physician/
│ │ │ ├── MainActivity.kt # Main activity
│ │ │ └── MainApplication.kt # Application class
│ │ └── res/ # Resources
│ │ ├── drawable/ # Drawable resources
│ │ ├── mipmap-*/ # App icons
│ │ └── values/ # Values
│ │ ├── strings.xml # String resources
│ │ └── styles.xml # Styles
│ ├── build.gradle # Project build configuration
│ ├── gradle/ # Gradle wrapper
│ ├── gradle.properties # Gradle properties
│ ├── gradlew # Gradle wrapper script
│ ├── gradlew.bat # Windows gradle wrapper
│ └── settings.gradle # Gradle settings
├── ios/ # iOS native code
│ ├── NeoScan_Physician/ # iOS app
│ │ ├── AppDelegate.swift # App delegate
│ │ ├── Images.xcassets/ # Image assets
│ │ │ ├── AppIcon.appiconset/ # App icons
│ │ │ └── Contents.json # Asset catalog
│ │ ├── Info.plist # App info
│ │ ├── LaunchScreen.storyboard # Launch screen
│ │ └── PrivacyInfo.xcprivacy # Privacy info
│ ├── NeoScan_Physician.xcodeproj/ # Xcode project
│ │ ├── project.pbxproj # Project file
│ │ └── xcshareddata/ # Shared data
│ │ └── xcschemes/ # Build schemes
│ │ └── NeoScan_Physician.xcscheme
│ └── Podfile # CocoaPods configuration
├── __tests__/ # Test files
│ ├── App.test.tsx # App component tests
│ ├── components/ # Component tests
│ ├── utils/ # Utility tests
│ └── integration/ # Integration tests
├── docs/ # Documentation
│ ├── README.md # Project overview
│ ├── ARCHITECTURE.md # Architecture documentation
│ ├── API.md # API documentation
│ ├── DEPLOYMENT.md # Deployment guide
│ ├── TESTING.md # Testing guidelines
│ ├── SECURITY.md # Security guidelines
│ └── wireframes/ # UI wireframes
├── .gitignore # Git ignore rules
├── .eslintrc.js # ESLint configuration
├── .prettierrc.js # Prettier configuration
├── .watchmanconfig # Watchman configuration
├── app.json # App configuration
├── babel.config.js # Babel configuration
├── jest.config.js # Jest configuration
├── metro.config.js # Metro bundler configuration
├── package.json # Dependencies and scripts
├── package-lock.json # Locked dependencies
├── tsconfig.json # TypeScript configuration
├── index.js # React Native entry point
├── setup.sh # Unix setup script
├── setup.bat # Windows setup script
├── README.md # Project README
├── PROJECT_STRUCTURE.md # This file
└── Gemfile # Ruby dependencies (iOS)
```
## 🏗️ Module Architecture
### Auth Module
**Purpose**: Handles all authentication and authorization functionality
- **LoginScreen**: Hospital SSO, credential login, emergency access
- **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
- **ERDashboardScreen**: 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
### Components
- **PascalCase** for all component files
- **Suffix with type**: `.tsx` for components, `.ts` for utilities
- **Examples**: `LoginScreen.tsx`, `PatientCard.tsx`, `CriticalAlerts.tsx`
### Hooks
- **camelCase** with `use` prefix
- **Examples**: `useAuth.ts`, `usePatientList.ts`, `useRealTimeAlerts.ts`
### Services
- **camelCase** with descriptive names
- **Suffix with type**: `API.ts`, `Service.ts`
- **Examples**: `authAPI.ts`, `patientCareAPI.ts`, `notificationService.ts`
### Redux
- **camelCase** with descriptive suffixes
- **Examples**: `authSlice.ts`, `erDashboardSlice.ts`, `patientCareActions.ts`
### Tests
- **Same name as source file** + `.test.ts` or `.test.tsx`
- **Examples**: `LoginScreen.test.tsx`, `useAuth.test.ts`, `authSlice.test.ts`
## 🔧 Configuration Files
### Theme System
- **colors.ts**: Complete color palette with healthcare blue theme
- **typography.ts**: Font families, weights, sizes, and spacing
- **spacing.ts**: Spacing scale, border radius, breakpoints
- **shadows.ts**: Shadow system for elevation
- **animations.ts**: Animation durations and easing functions
- **theme.ts**: Main theme object combining all design tokens
### Type Definitions
- **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
- **constants.ts**: App constants, API configs, timeouts
- **helpers.ts**: Common utility functions and helpers
- **validators.ts**: Form validation and data validation
- **formatters.ts**: Data formatting and display utilities
- **medicalUtils.ts**: Medical-specific utility functions
## 🎨 Design System
### Color Palette
- **Primary**: #2196F3 (Material Blue)
- **Secondary**: #1976D2 (Darker Blue)
- **Critical**: #F44336 (Material Red)
- **Warning**: #FF9800 (Material Orange)
- **Success**: #4CAF50 (Material Green)
### Component Library
- **UI Components**: Basic building blocks (Button, Input, Card, etc.)
- **Form Components**: Form-specific components with validation
- **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
1. **Clone the repository**
2. **Run setup script**: `./setup.sh` (Unix) or `setup.bat` (Windows)
3. **Start development server**: `npm start`
4. **Run on device**: `npm run android` or `npm run ios`
## 📱 Key Features Implemented
### ✅ Completed
- **Theme System**: Complete design system with healthcare blue theme
- **Authentication**: Login screen with SSO and credential options
- **ER Dashboard**: Main dashboard with patient cards and statistics
- **Patient Cards**: Comprehensive patient information display
- **Critical Alerts**: High-priority alert system
- **Quick Actions**: Emergency procedure shortcuts
- **Department Stats**: Real-time department overview
- **Navigation**: Tab-based navigation structure
- **Type Safety**: Complete TypeScript implementation
### 🔄 In Progress
- **Redux Store**: State management implementation
- **API Integration**: Backend service integration
- **Real-time Updates**: WebSocket implementation
- **Push Notifications**: Critical alert notifications
### 📋 Planned
- **Patient Details**: Comprehensive patient management screens
- **Medical Records**: Complete medical history management
- **Settings**: App configuration and preferences
- **Offline Support**: Offline data access and sync
- **Testing**: Comprehensive test suite
- **Documentation**: Complete API and user documentation
This structure provides a solid foundation for a comprehensive healthcare application with proper separation of concerns, type safety, and modern React Native best practices.
/*
* End of File: PROJECT_STRUCTURE.md
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

344
README.md
View File

@ -1,97 +1,317 @@
This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli).
/*
* File: README.md
* Description: Project documentation and setup instructions
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
# Getting Started
# NeoScan Physician App
> **Note**: Make sure you have completed the [Set Up Your Environment](https://reactnative.dev/docs/set-up-your-environment) guide before proceeding.
A comprehensive React Native application designed for emergency department physicians to manage patient care, critical alerts, and medical workflows in real-time.
## Step 1: Start Metro
## 🚀 Features
First, you will need to run **Metro**, the JavaScript build tool for React Native.
### 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
To start the Metro dev server, run the following command from the root of your React Native project:
### 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)
```sh
# Using npm
npm start
### 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
# OR using Yarn
yarn start
### 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
## 🏗️ Architecture
### Project Structure
```
NeoScan_Physician/
├── 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
```
## Step 2: Build and run your app
### 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
With Metro running, open a new terminal window/pane from the root of your React Native project, and use one of the following commands to build and run your Android or iOS app:
## 🎨 Design System
### Android
### 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)
```sh
# Using npm
npm run android
### 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)
# OR using Yarn
yarn android
### 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
## 📱 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 <repository-url>
cd NeoScan_Physician
```
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
```
### iOS
## 📱 Platform Support
For iOS, remember to install CocoaPods dependencies (this only needs to be run on first clone or after updating native deps).
### iOS Features
- Face ID authentication
- Apple Health integration
- Siri shortcuts
- iOS notifications
The first time you create a new project, run the Ruby bundler to install CocoaPods itself:
### Android Features
- Fingerprint authentication
- Google Fit integration
- Android Auto
- Android notifications
```sh
bundle install
```
## 🔄 Real-time Updates
Then, and every time you update your native dependencies, run:
### 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
```sh
bundle exec pod install
```
### Data Synchronization
- **Server Priority**: Server data overrides local
- **Timestamp Comparison**: Latest data wins
- **User Confirmation**: Manual resolution for conflicts
- **Audit Trail**: All changes tracked
For more information, please visit [CocoaPods Getting Started guide](https://guides.cocoapods.org/using/getting-started.html).
## 📋 Development Guidelines
```sh
# Using npm
npm run ios
### Code Style
- **TypeScript**: Strict type checking
- **ESLint**: Code quality enforcement
- **Prettier**: Code formatting
- **Conventional Commits**: Git commit messages
# OR using Yarn
yarn ios
```
### Component Guidelines
- **Single Responsibility**: One component, one purpose
- **Reusability**: Shared components in shared/
- **Type Safety**: Full TypeScript coverage
- **Accessibility**: WCAG 2.1 compliance
If everything is set up correctly, you should see your new app running in the Android Emulator, iOS Simulator, or your connected device.
### Performance Guidelines
- **Lazy Loading**: Components loaded on demand
- **Memoization**: React.memo for expensive components
- **Image Optimization**: Compressed and cached images
- **Bundle Size**: Minimal dependencies
This is one way to run your app — you can also build it directly from Android Studio or Xcode.
## 🚀 Deployment
## Step 3: Modify your app
### 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
Now that you have successfully run the app, let's make changes!
### 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
Open `App.tsx` in your text editor of choice and make some changes. When you save, your app will automatically update and reflect these changes — this is powered by [Fast Refresh](https://reactnative.dev/docs/fast-refresh).
## 📞 Support
When you want to forcefully reload, for example to reset the state of your app, you can perform a full reload:
### Documentation
- **API Documentation**: Complete API reference
- **User Guide**: End-user documentation
- **Developer Guide**: Technical documentation
- **Troubleshooting**: Common issues and solutions
- **Android**: Press the <kbd>R</kbd> key twice or select **"Reload"** from the **Dev Menu**, accessed via <kbd>Ctrl</kbd> + <kbd>M</kbd> (Windows/Linux) or <kbd>Cmd ⌘</kbd> + <kbd>M</kbd> (macOS).
- **iOS**: Press <kbd>R</kbd> in iOS Simulator.
### Contact
- **Technical Support**: dev-support@neoscan.com
- **Emergency Support**: emergency-support@neoscan.com
- **Feature Requests**: features@neoscan.com
## Congratulations! :tada:
## 📄 License
You've successfully run and modified your React Native App. :partying_face:
This project is proprietary software developed for healthcare institutions. All rights reserved.
### Now what?
---
- If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
- If you're curious to learn more about React Native, check out the [docs](https://reactnative.dev/docs/getting-started).
**NeoScan Physician App** - Empowering emergency care with real-time intelligence and seamless workflows.
# Troubleshooting
If you're having issues getting the above steps to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.
# Learn More
To learn more about React Native, take a look at the following resources:
- [React Native Website](https://reactnative.dev) - learn more about React Native.
- [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
- [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
- [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
- [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.
/*
* End of File: README.md
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

618
THEME_FLOW.md Normal file
View File

@ -0,0 +1,618 @@
# Physician App - Theme System & Design Flow
## 🎨 Design System Overview
### **Project Theme Structure**
```
app/theme/
├── theme.ts # Main theme configuration
├── colors.ts # Color palette definitions
├── typography.ts # Font families, sizes, weights
├── spacing.ts # Spacing scale and breakpoints
├── shadows.ts # Shadow and elevation system
└── animations.ts # Animation durations and easing
```
---
## 🎯 Color Palette - "Modern Healthcare Blue"
### **Primary Colors**
```typescript
Primary: '#2196F3' // Material Blue - Main brand color
Secondary: '#1976D2' // Darker Blue - Secondary actions
Tertiary: '#E3F2FD' // Very Light Blue - Backgrounds
Quaternary: '#0D47A1' // Deep Blue - Accents
```
### **Text Colors**
```typescript
TextPrimary: '#212121' // Dark Gray - Main text
TextSecondary: '#757575' // Medium Gray - Secondary text
TextMuted: '#9E9E9E' // Light Gray - Muted text
```
### **Background Colors**
```typescript
Background: '#FFFFFF' // White - Primary background
BackgroundAlt: '#FAFAFA' // Light Gray - Alternative background
BackgroundAccent: '#F5F5F5' // Soft Gray - Accent backgrounds
```
### **Status & Feedback Colors**
```typescript
Success: '#4CAF50' // Material Green - Success states
Warning: '#FF9800' // Material Orange - Warning states
Error: '#F44336' // Material Red - Error states
Info: '#2196F3' // Material Blue - Information states
Critical: '#F44336' // Critical alerts and emergencies
```
### **UI Elements**
```typescript
Border: '#E0E0E0' // Light Gray Border
CardBackground: '#FFFFFF' // White - Card backgrounds
Shadow: 'rgba(0, 0, 0, 0.1)' // Subtle Gray Shadow
```
---
## 📝 Typography System
### **Font Families (Roboto)**
```typescript
// Available Font Families
fontFamily: {
bold: 'Roboto-Bold',
medium: 'Roboto-Medium',
regular: 'Roboto-Regular',
light: 'Roboto-Light',
semibold: 'Roboto-SemiBold',
extrabold: 'Roboto-ExtraBold',
}
```
### **Font Weights**
```typescript
fontWeight: {
light: '300',
regular: '400',
medium: '500',
bold: '700',
}
```
### **Font Sizes**
```typescript
fontSize: {
displayLarge: 32, // Main headings
displayMedium: 24, // Section headings
displaySmall: 20, // Subsection headings
bodyLarge: 16, // Body text
bodyMedium: 14, // Secondary text
bodySmall: 12, // Captions
caption: 10, // Small labels
}
```
### **Line Heights & Spacing**
```typescript
lineHeight: {
tight: 1.2, // Headings
normal: 1.4, // Body text
relaxed: 1.6, // Long text
}
letterSpacing: {
tight: -0.5, // Headings
normal: 0, // Body text
wide: 0.5, // Labels
}
```
---
## 📐 Spacing & Layout
### **Spacing Scale (Base: 4px)**
```typescript
spacing: {
xs: 4, // 4px
sm: 8, // 8px
md: 16, // 16px
lg: 24, // 24px
xl: 32, // 32px
xxl: 48, // 48px
xxxl: 64 // 64px
}
```
### **Border Radius**
```typescript
borderRadius: {
small: 4,
medium: 8,
large: 12,
xlarge: 16,
round: 50,
}
```
### **Breakpoints**
```typescript
breakpoints: {
mobile: 375,
tablet: 768,
desktop: 1024,
largeDesktop: 1440,
}
```
---
## 🎨 Icon System
### **React Native Vector Icons**
#### **Available Icon Sets**
```typescript
// Primary Icon Sets
import Icon from 'react-native-vector-icons/Feather'; // Clean, minimal icons
import Icon from 'react-native-vector-icons/MaterialIcons'; // Material Design icons
import Icon from 'react-native-vector-icons/Ionicons'; // iOS-style icons
```
#### **Icon Usage Guidelines**
```typescript
// Standard Icon Implementation
<Icon
name="mail" // Icon name
size={20} // Size in pixels
color={theme.colors.textSecondary} // Color from theme
style={styles.icon} // Additional styling
/>
// Icon Sizes
IconSizes: {
xs: 12, // Extra small
sm: 16, // Small
md: 24, // Medium (default)
lg: 32, // Large
xl: 48 // Extra large
}
```
#### **Common Icon Names**
```typescript
// Navigation Icons
'home', 'menu', 'arrow-left', 'arrow-right', 'chevron-down', 'chevron-up'
// Action Icons
'plus', 'minus', 'edit', 'delete', 'save', 'cancel', 'check', 'close'
// Medical Icons
'heart', 'user', 'settings', 'bell', 'search', 'filter', 'calendar'
// Status Icons
'check-circle', 'alert-circle', 'info', 'warning', 'error'
```
---
## 🏗️ Component Design Rules
### **Button Design System**
```typescript
// Button Variants
Primary: {
backgroundColor: theme.colors.primary,
borderColor: theme.colors.primary,
borderRadius: theme.borderRadius.medium,
shadowColor: theme.colors.primary,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 3
}
Secondary: {
backgroundColor: 'transparent',
borderColor: theme.colors.primary,
borderRadius: theme.borderRadius.medium
}
Success: {
backgroundColor: theme.colors.success,
borderColor: theme.colors.success,
borderRadius: theme.borderRadius.medium,
shadowColor: theme.colors.success,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 3
}
Critical: {
backgroundColor: theme.colors.critical,
borderColor: theme.colors.critical,
borderRadius: theme.borderRadius.medium,
shadowColor: theme.colors.critical,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 3
}
```
### **Input Field Design**
```typescript
// Input States
Default: {
borderColor: theme.colors.border,
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.md
}
Focused: {
borderColor: theme.colors.primary,
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.md
}
Error: {
borderColor: theme.colors.critical,
backgroundColor: theme.colors.criticalBackground,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.md
}
```
### **Card Design Rules**
```typescript
// Card Variants
Default: {
backgroundColor: theme.colors.cardBackground,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.large,
padding: theme.spacing.lg,
shadowColor: theme.colors.shadow,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2
}
Elevated: {
backgroundColor: theme.colors.cardBackground,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.large,
padding: theme.spacing.lg,
shadowColor: theme.colors.shadow,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.15,
shadowRadius: 8,
elevation: 4
}
```
---
## 🚨 Alert & Status Design
### **Alert Priority System**
```typescript
// Critical Alerts
Critical: {
backgroundColor: theme.colors.criticalBackground,
borderColor: theme.colors.critical,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md
}
// Warning Alerts
Warning: {
backgroundColor: theme.colors.warningBackground,
borderColor: theme.colors.warning,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md
}
// Success Alerts
Success: {
backgroundColor: theme.colors.successBackground,
borderColor: theme.colors.success,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md
}
// Info Alerts
Info: {
backgroundColor: theme.colors.infoBackground,
borderColor: theme.colors.info,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md
}
```
### **Status Badges**
```typescript
// Status Badges
StatusBadge: {
paddingHorizontal: theme.spacing.sm,
paddingVertical: theme.spacing.xs,
borderRadius: theme.borderRadius.round,
fontSize: theme.typography.fontSize.bodySmall,
fontWeight: theme.typography.fontWeight.medium
}
// Priority Badges
PriorityBadge: {
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.sm,
borderRadius: theme.borderRadius.round,
fontSize: theme.typography.fontSize.bodySmall,
fontWeight: theme.typography.fontWeight.bold
}
```
---
## 📱 Screen-Specific Design Rules
### **Login Screen Design**
```typescript
// Login Container
LoginContainer: {
backgroundColor: theme.colors.background,
padding: theme.spacing.lg,
borderRadius: theme.borderRadius.large,
shadowColor: theme.colors.shadow,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.15,
shadowRadius: 8,
elevation: 4
}
// Input Container with Icons
InputContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: theme.colors.backgroundAlt,
borderColor: theme.colors.border,
borderWidth: 1,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
paddingVertical: 2,
marginBottom: theme.spacing.md
}
// Input Icon
InputIcon: {
marginRight: theme.spacing.sm,
color: theme.colors.textSecondary
}
```
### **Dashboard Screen Design**
```typescript
// Header Section
Header: {
backgroundColor: theme.colors.background,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.sm,
borderBottomColor: theme.colors.border,
borderBottomWidth: 1
}
// Critical Alerts Section
CriticalAlerts: {
backgroundColor: theme.colors.criticalBackground,
borderColor: theme.colors.critical,
borderRadius: theme.borderRadius.large,
padding: theme.spacing.lg,
marginHorizontal: theme.spacing.md,
marginVertical: theme.spacing.md,
shadowColor: theme.colors.critical,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 8,
elevation: 4
}
```
---
## 🔄 Animation & Transitions
### **Animation Rules**
```typescript
// Animation Durations
Durations: {
Fast: 150, // Quick interactions
Normal: 300, // Standard transitions
Slow: 500 // Complex animations
}
// Easing Functions
Easing: {
Standard: 'cubic-bezier(0.4, 0.0, 0.2, 1)',
Deceleration: 'cubic-bezier(0.0, 0.0, 0.2, 1)',
Acceleration: 'cubic-bezier(0.4, 0.0, 1, 1)'
}
// Transition Types
Transitions: {
Fade: { opacity: [0, 1], duration: 300 },
Slide: { transform: [{ translateY: [20, 0] }], duration: 300 },
Scale: { transform: [{ scale: [0.95, 1] }], duration: 200 }
}
```
---
## 🎯 Usage Guidelines
### **Theme Import**
```typescript
// Import theme in components
import { theme } from '../theme/theme';
// Usage in StyleSheet
const styles = StyleSheet.create({
container: {
backgroundColor: theme.colors.background,
padding: theme.spacing.md,
borderRadius: theme.borderRadius.medium,
shadowColor: theme.colors.shadow,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 2
},
title: {
fontSize: theme.typography.fontSize.displayMedium,
fontWeight: theme.typography.fontWeight.bold,
color: theme.colors.textPrimary,
fontFamily: theme.typography.fontFamily.bold
}
});
```
### **Icon Implementation**
```typescript
// Import icon
import Icon from 'react-native-vector-icons/Feather';
// Usage in components
<Icon
name="mail"
size={20}
color={theme.colors.textSecondary}
style={styles.icon}
/>
```
### **Font Usage**
```typescript
// Typography with font families
const styles = StyleSheet.create({
heading: {
fontSize: theme.typography.fontSize.displayMedium,
fontWeight: theme.typography.fontWeight.bold,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
lineHeight: theme.typography.lineHeight.tight
},
body: {
fontSize: theme.typography.fontSize.bodyLarge,
fontWeight: theme.typography.fontWeight.regular,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
lineHeight: theme.typography.lineHeight.normal
}
});
```
---
## 🔧 Configuration Files
### **TypeScript Declaration**
```typescript
// app/types/react-native-vector-icons.d.ts
declare module 'react-native-vector-icons/Feather' {
import { Component } from 'react';
import { TextProps } from 'react-native';
interface IconProps extends TextProps {
name: string;
size?: number;
color?: string;
}
export default class Icon extends Component<IconProps> {}
}
```
### **Font Configuration**
```json
// react-native.config.js
module.exports = {
assets: ['./app/assets/fonts/'],
};
```
---
## 📋 Best Practices
### **Design Consistency**
- ✅ Always use theme colors instead of hardcoded values
- ✅ Use consistent spacing from the spacing scale
- ✅ Apply proper typography hierarchy
- ✅ Use appropriate icon sizes and colors
- ✅ Maintain consistent border radius values
### **Performance**
- ✅ Use StyleSheet.create for all styles
- ✅ Minimize inline styles
- ✅ Use theme constants for repeated values
- ✅ Optimize icon usage with proper sizing
### **Accessibility**
- ✅ Maintain proper color contrast ratios
- ✅ Use semantic color names
- ✅ Provide adequate touch targets (44px minimum)
- ✅ Support dynamic text sizing
### **Maintainability**
- ✅ Keep theme centralized and well-documented
- ✅ Use semantic naming for colors and spacing
- ✅ Document any custom theme extensions
- ✅ Version control theme changes
---
## 🚀 Quick Reference
### **Common Theme Values**
```typescript
// Colors
theme.colors.primary // #2196F3
theme.colors.textPrimary // #212121
theme.colors.background // #FFFFFF
theme.colors.critical // #F44336
// Spacing
theme.spacing.md // 16px
theme.spacing.lg // 24px
theme.spacing.xl // 32px
// Typography
theme.typography.fontSize.bodyLarge // 16px
theme.typography.fontWeight.bold // 700
theme.typography.fontFamily.bold // 'Roboto-Bold'
// Border Radius
theme.borderRadius.medium // 8px
theme.borderRadius.large // 12px
theme.borderRadius.round // 50px
```
### **Icon Quick Reference**
```typescript
// Common Icons
<Icon name="mail" size={20} color={theme.colors.textSecondary} />
<Icon name="lock" size={20} color={theme.colors.textSecondary} />
<Icon name="eye" size={22} color={theme.colors.textSecondary} />
<Icon name="eye-off" size={22} color={theme.colors.textSecondary} />
<Icon name="check" size={24} color={theme.colors.success} />
<Icon name="alert-circle" size={24} color={theme.colors.critical} />
```
This comprehensive theme system ensures consistency, accessibility, and maintainability across the Physician App while providing a modern healthcare-focused design experience.

View File

@ -1,6 +1,7 @@
apply plugin: "com.android.application"
apply plugin: "org.jetbrains.kotlin.android"
apply plugin: "com.facebook.react"
apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle"
/**
* This is the configuration block to customize your React Native Android app.
@ -117,3 +118,5 @@ dependencies {
implementation jscFlavor
}
}
apply from: file("../../node_modules/react-native-vector-icons/fonts.gradle")

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

View File

@ -0,0 +1,37 @@
{
"migIndex": 1,
"data": [
{
"path": "app/assets/fonts/Roboto-Black.ttf",
"sha1": "d1678489a8d5645f16486ec52d77b651ff0bf327"
},
{
"path": "app/assets/fonts/Roboto-Bold.ttf",
"sha1": "508c35dee818addce6cc6d1fb6e42f039da5a7cf"
},
{
"path": "app/assets/fonts/Roboto-ExtraBold.ttf",
"sha1": "3dbfd71b6fbcfbd8e7ee8a8dd033dc5aaad63249"
},
{
"path": "app/assets/fonts/Roboto-ExtraLight.ttf",
"sha1": "df556e64732e5c272349e13cb5f87591a1ae779b"
},
{
"path": "app/assets/fonts/Roboto-Light.ttf",
"sha1": "318b44c0a32848f78bf11d4fbf3355d00647a796"
},
{
"path": "app/assets/fonts/Roboto-Medium.ttf",
"sha1": "fa5192203f85ddb667579e1bdf26f12098bb873b"
},
{
"path": "app/assets/fonts/Roboto-Regular.ttf",
"sha1": "3bff51436aa7eb995d84cfc592cc63e1316bb400"
},
{
"path": "app/assets/fonts/Roboto-SemiBold.ttf",
"sha1": "9ca139684fe902c8310dd82991648376ac9838db"
}
]
}

224
app/App.tsx Normal file
View File

@ -0,0 +1,224 @@
/*
* File: App.tsx
* Description: Main application component with navigation setup
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useRef } from 'react';
import { StatusBar } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { NavigationContainer, NavigationContainerRef } from '@react-navigation/native';
import { theme } from './theme/theme';
import { RootStackNavigator, setNavigationRef } from './navigation';
import { StoreProvider } from './store/Provider';
import Toast from 'react-native-toast-message';
import { useAppSelector } from './store/hooks';
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
},
];
/**
* AppContent Component (Inner Component)
*
* Purpose: Inner component that uses Redux hooks for authentication state
*
* Features:
* 1. Connect to Redux store for authentication state
* 2. Set up navigation container with global reference
* 3. Configure status bar appearance
* 4. Render the main app navigator based on authentication state
*
* Navigation Flow:
* 1. App starts Check Redux authentication status
* 2. If not authenticated Show LoginScreen
* 3. If authenticated Show MainTabNavigator (dashboard)
*/
function AppContent() {
// ============================================================================
// REDUX STATE
// ============================================================================
// Get authentication state from Redux
const isAuthenticated = useAppSelector(selectIsAuthenticated);
// Navigation reference for programmatic navigation
const navigationRef = useRef<NavigationContainerRef<any> | null>(null);
// ============================================================================
// EFFECTS
// ============================================================================
// Set up navigation reference for global access
React.useEffect(() => {
setNavigationRef(navigationRef);
}, []);
// ============================================================================
// RENDER SECTION
// ============================================================================
return (
<SafeAreaProvider>
<NavigationContainer ref={navigationRef}>
<StatusBar
barStyle="dark-content" // Dark text on light background
backgroundColor={theme.colors.background} // Status bar background color
/>
<RootStackNavigator isAuthenticated={isAuthenticated} />
<Toast
position='bottom'
bottomOffset={20}
/>
</NavigationContainer>
</SafeAreaProvider>
);
}
/**
* App Component (Root Component)
*
* Purpose: Root component that wraps the entire application with Redux Provider
*
* Features:
* 1. Provide Redux store context
* 2. Wrap the main app content
* 3. Enable Redux state management throughout the app
*/
export default function App() {
return (
<StoreProvider>
<AppContent />
</StoreProvider>
);
}
/*
* End of File: App.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

View File

@ -0,0 +1,634 @@
/*
* File: DocumentUploadStep.tsx
* Description: Document upload step component for signup flow with image picker
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState } from 'react';
import {
View,
Text,
TouchableOpacity,
StyleSheet,
ScrollView,
Image,
Alert,
PermissionsAndroid,
Platform,
KeyboardAvoidingView,
} from 'react-native';
import {
launchImageLibrary,
launchCamera,
ImagePickerResponse,
MediaType,
} from 'react-native-image-picker';
import { theme } from '../../../../theme/theme';
import { DocumentUploadStepProps } from '../../types/signup';
import Icon from 'react-native-vector-icons/Feather';
import { showError, showSuccess } from '../../../../shared/utils/toast';
// ============================================================================
// INTERFACES
// ============================================================================
/**
* ImageData Interface
*
* Purpose: Defines the structure for image data
*/
interface ImageData {
uri: string;
name: string;
type: string;
size?: number;
}
// ============================================================================
// DOCUMENT UPLOAD STEP COMPONENT
// ============================================================================
/**
* DocumentUploadStep Component
*
* Purpose: Fourth step of signup flow - document upload with image picker
*
* Features:
* - Camera and gallery image selection
* - Image preview with file details
* - Real-time file size and type display
* - Permission handling for camera
* - Modern UI with proper header alignment
* - Continue button with loading state
* - Back navigation
*/
const DocumentUploadStep: React.FC<DocumentUploadStepProps> = ({
onContinue,
onBack,
data,
isLoading,
}) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
const [selectedImage, setSelectedImage] = useState<ImageData | null>(
data.id_photo_url ? {
uri: data.id_photo_url,
name: 'uploaded_document.jpg',
type: 'image/jpeg',
} : null
);
// ============================================================================
// PERMISSION HANDLERS
// ============================================================================
/**
* Request Camera Permission
*
* Purpose: Request camera permission for Android devices
*
* @returns Promise<boolean> - Whether permission was granted
*/
const requestCameraPermission = async (): Promise<boolean> => {
if (Platform.OS === 'android') {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Camera Permission',
message: 'This app needs camera permission to capture images.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
}
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
} catch (err) {
console.warn('Camera permission error:', err);
return false;
}
}
return true;
};
// ============================================================================
// IMAGE PICKER HANDLERS
// ============================================================================
/**
* Handle Image Picker Selection
*
* Purpose: Show options for camera or gallery selection
*/
const handleImagePicker = () => {
Alert.alert(
'Select Image',
'Choose how you want to upload your document',
[
{
text: 'Camera',
onPress: () => handleCameraCapture(),
},
{
text: 'Gallery',
onPress: () => handleGalleryPicker(),
},
{
text: 'Cancel',
style: 'cancel',
},
]
);
};
/**
* Handle Camera Capture
*
* Purpose: Launch camera to capture image
*/
const handleCameraCapture = async () => {
const hasPermission = await requestCameraPermission();
if (!hasPermission) {
showError('Permission Error', 'Camera permission is required to capture images.');
return;
}
const options = {
mediaType: 'photo' as MediaType,
maxWidth: 2000,
maxHeight: 2000,
includeBase64: false,
};
launchCamera(options, (response: ImagePickerResponse) => {
if (response.didCancel) {
return;
}
if (response.errorMessage) {
showError('Camera Error', response.errorMessage);
return;
}
if (response.assets && response.assets[0]) {
const asset = response.assets[0];
const imageData: ImageData = {
uri: asset.uri!,
name: asset.fileName || `document_${Date.now()}.jpg`,
type: asset.type || 'image/jpeg',
size: asset.fileSize,
};
setSelectedImage(imageData);
showSuccess('Success', 'Document captured successfully!');
}
});
};
/**
* Handle Gallery Picker
*
* Purpose: Launch image library to select image
*/
const handleGalleryPicker = () => {
const options = {
mediaType: 'photo' as MediaType,
maxWidth: 2000,
maxHeight: 2000,
includeBase64: false,
};
launchImageLibrary(options, (response: ImagePickerResponse) => {
if (response.didCancel) {
return;
}
if (response.errorMessage) {
showError('Gallery Error', response.errorMessage);
return;
}
if (response.assets && response.assets[0]) {
const asset = response.assets[0];
const imageData: ImageData = {
uri: asset.uri!,
name: asset.fileName || `document_${Date.now()}.jpg`,
type: asset.type || 'image/jpeg',
size: asset.fileSize,
};
setSelectedImage(imageData);
showSuccess('Success', 'Document selected from gallery!');
}
});
};
// ============================================================================
// UTILITY FUNCTIONS
// ============================================================================
/**
* Format File Size
*
* Purpose: Convert bytes to human readable format
*
* @param bytes - File size in bytes
* @returns Formatted file size string
*/
const formatFileSize = (bytes?: number): string => {
if (!bytes) return '';
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
if (bytes === 0) return '0 Bytes';
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
};
/**
* Get File Type Display
*
* Purpose: Get display name for file type
*
* @param type - MIME type
* @returns Display name for file type
*/
const getFileTypeDisplay = (type: string): string => {
if (type.includes('jpeg') || type.includes('jpg')) return 'JPEG';
if (type.includes('png')) return 'PNG';
if (type.includes('gif')) return 'GIF';
if (type.includes('webp')) return 'WebP';
return 'Image';
};
// ============================================================================
// EVENT HANDLERS
// ============================================================================
/**
* Handle Continue
*
* Purpose: Proceed to next step with selected image
*/
const handleContinue = () => {
if (!selectedImage) {
showError('Validation Error', 'Please upload a document to continue.');
return;
}
onContinue(selectedImage.uri);
};
/**
* Handle Remove Image
*
* Purpose: Remove selected image
*/
const handleRemoveImage = () => {
setSelectedImage(null);
};
// ============================================================================
// RENDER
// ============================================================================
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.container}
>
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
>
{/* Header */}
<View style={styles.header}>
<TouchableOpacity onPress={onBack} style={styles.backButton}>
<Icon name="arrow-left" size={24} color={theme.colors.textPrimary} />
</TouchableOpacity>
<View style={styles.headerContent}>
<Text style={styles.title}>Upload Document</Text>
<Text style={styles.subtitle}>Step 4 of 5</Text>
</View>
<View style={styles.headerSpacer} />
</View>
{/* Content */}
<View style={styles.content}>
<Text style={styles.sectionTitle}>Upload your ID document</Text>
<Text style={styles.description}>
Please upload a clear photo of your government-issued ID for verification.
</Text>
{/* Document Upload Area */}
<TouchableOpacity
style={styles.uploadContainer}
onPress={handleImagePicker}
disabled={isLoading}
>
<View style={styles.uploadContent}>
{selectedImage ? (
<View style={styles.imagePreviewContainer}>
<Image source={{ uri: selectedImage.uri }} style={styles.imagePreview} />
<TouchableOpacity
style={styles.imageOverlay}
onPress={handleRemoveImage}
>
<Icon name="x" size={20} color={theme.colors.background} />
</TouchableOpacity>
<Text style={styles.uploadedText}>Document Uploaded</Text>
<Text style={styles.fileName}>{selectedImage.name}</Text>
<View style={styles.fileDetails}>
<Text style={styles.fileType}>{getFileTypeDisplay(selectedImage.type)}</Text>
{selectedImage.size && (
<Text style={styles.fileSize}> {formatFileSize(selectedImage.size)}</Text>
)}
</View>
</View>
) : (
<>
<Icon name="image" size={48} color={theme.colors.textSecondary} />
<Text style={styles.uploadText}>Tap to upload document</Text>
<Text style={styles.uploadSubtext}>JPG, PNG supported</Text>
</>
)}
</View>
</TouchableOpacity>
{selectedImage && (
<TouchableOpacity
style={styles.changeButton}
onPress={handleImagePicker}
disabled={isLoading}
>
<Text style={styles.changeButtonText}>Change Document</Text>
</TouchableOpacity>
)}
{/* Continue Button */}
<TouchableOpacity
style={[
styles.continueButton,
(!selectedImage || isLoading) && styles.continueButtonDisabled,
]}
onPress={handleContinue}
disabled={!selectedImage || isLoading}
>
<Text style={[
styles.continueButtonText,
(!selectedImage || isLoading) && styles.continueButtonTextDisabled,
]}>
{isLoading ? 'Processing...' : 'Continue'}
</Text>
</TouchableOpacity>
</View>
</ScrollView>
</KeyboardAvoidingView>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
// Scroll view
scrollView: {
flex: 1,
},
// Scroll content
scrollContent: {
flexGrow: 1,
paddingHorizontal: theme.spacing.sm,
},
// Header section
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingTop: theme.spacing.xl,
paddingBottom: theme.spacing.lg,
marginBottom: theme.spacing.lg,
},
// Back button
backButton: {
padding: theme.spacing.sm,
borderRadius: theme.borderRadius.medium,
backgroundColor: theme.colors.backgroundAlt,
},
// Header content
headerContent: {
flex: 1,
alignItems: 'center',
},
// Header spacer
headerSpacer: {
width: 40,
},
// 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,
},
// Content section
content: {
flex: 1,
justifyContent: 'center',
paddingBottom: theme.spacing.xl,
},
// Section title
sectionTitle: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
// Description
description: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xl,
},
// Upload container
uploadContainer: {
backgroundColor: theme.colors.backgroundAlt,
borderWidth: 2,
borderColor: theme.colors.border,
borderStyle: 'dashed',
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.xl,
marginBottom: theme.spacing.md,
minHeight: 200,
},
// Upload content
uploadContent: {
alignItems: 'center',
},
// Image preview container
imagePreviewContainer: {
alignItems: 'center',
width: '100%',
},
// Image preview
imagePreview: {
width: '100%',
height: 150,
borderRadius: theme.borderRadius.medium,
marginBottom: theme.spacing.sm,
resizeMode: 'contain',
},
// Image overlay (remove button)
imageOverlay: {
position: 'absolute',
top: 0,
right: 20,
backgroundColor: 'rgba(0, 0, 0, 0.4)',
borderRadius: 17,
width: 34,
height: 34,
justifyContent: 'center',
alignItems: 'center',
},
// Upload text
uploadText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
marginTop: theme.spacing.sm,
},
// Upload subtext
uploadSubtext: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginTop: theme.spacing.xs,
},
// Uploaded text
uploadedText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.success,
marginTop: theme.spacing.sm,
},
// File name
fileName: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginTop: theme.spacing.xs,
textAlign: 'center',
maxWidth: '80%',
},
// File details
fileDetails: {
flexDirection: 'row',
alignItems: 'center',
marginTop: theme.spacing.xs,
},
// File type
fileType: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
},
// File size
fileSize: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
},
// Change button
changeButton: {
alignSelf: 'center',
marginBottom: theme.spacing.xl,
},
// Change button text
changeButtonText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.primary,
},
// Continue button
continueButton: {
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.medium,
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
alignItems: 'center',
marginBottom: theme.spacing.lg,
...theme.shadows.primary,
},
// Continue button disabled
continueButtonDisabled: {
backgroundColor: theme.colors.border,
opacity: 0.6,
},
// Continue button text
continueButtonText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.background,
},
// Continue button text disabled
continueButtonTextDisabled: {
color: theme.colors.textMuted,
},
});
export default DocumentUploadStep;
/*
* End of File: DocumentUploadStep.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,232 @@
/*
* File: EmailAlreadyRegisteredModal.tsx
* Description: Modal for when email is already registered
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import {
View,
Text,
TouchableOpacity,
StyleSheet,
Modal,
Alert,
} from 'react-native';
import { theme } from '../../../../theme/theme';
// ============================================================================
// INTERFACES
// ============================================================================
interface EmailAlreadyRegisteredModalProps {
visible: boolean;
onClose: () => void;
onGoToLogin: () => void;
}
// ============================================================================
// EMAIL ALREADY REGISTERED MODAL COMPONENT
// ============================================================================
/**
* EmailAlreadyRegisteredModal Component
*
* Purpose: Modal shown when user tries to register with an existing email
*
* Features:
* - Informative message about existing email
* - Option to go to login
* - Option to close and try different email
*/
const EmailAlreadyRegisteredModal: React.FC<EmailAlreadyRegisteredModalProps> = ({
visible,
onClose,
onGoToLogin,
}) => {
// ============================================================================
// HANDLERS
// ============================================================================
/**
* Handle Go To Login
*
* Purpose: Navigate to login screen
*/
const handleGoToLogin = () => {
onClose();
onGoToLogin();
};
/**
* Handle Try Different Email
*
* Purpose: Close modal and allow user to try different email
*/
const handleTryDifferentEmail = () => {
onClose();
};
// ============================================================================
// RENDER
// ============================================================================
return (
<Modal
visible={visible}
transparent
animationType="fade"
onRequestClose={onClose}
>
<View style={styles.overlay}>
<View style={styles.modalContainer}>
{/* Header */}
<View style={styles.header}>
<Text style={styles.title}>Email Already Registered</Text>
<Text style={styles.subtitle}>
This email address is already associated with an account.
</Text>
</View>
{/* Content */}
<View style={styles.content}>
<Text style={styles.message}>
It looks like you already have an account with us. You can either:
</Text>
<View style={styles.optionsContainer}>
<Text style={styles.optionText}>
Sign in to your existing account
</Text>
<Text style={styles.optionText}>
Try a different email address
</Text>
</View>
</View>
{/* Actions */}
<View style={styles.actions}>
<TouchableOpacity
style={styles.secondaryButton}
onPress={handleTryDifferentEmail}
>
<Text style={styles.secondaryButtonText}>Try Different Email</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.primaryButton}
onPress={handleGoToLogin}
>
<Text style={styles.primaryButtonText}>Go to Login</Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: theme.spacing.lg,
},
modalContainer: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.large,
padding: theme.spacing.xl,
width: '100%',
maxWidth: 400,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 8,
},
header: {
alignItems: 'center',
marginBottom: theme.spacing.lg,
},
title: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
textAlign: 'center',
},
subtitle: {
fontSize: theme.typography.bodyMedium,
color: theme.colors.textSecondary,
textAlign: 'center',
},
content: {
marginBottom: theme.spacing.xl,
},
message: {
fontSize: theme.typography.bodyMedium,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.md,
},
optionsContainer: {
marginLeft: theme.spacing.sm,
},
optionText: {
fontSize: theme.typography.bodyMedium,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xs,
},
actions: {
flexDirection: 'row',
justifyContent: 'space-between',
gap: theme.spacing.md,
},
secondaryButton: {
flex: 1,
backgroundColor: theme.colors.background,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
alignItems: 'center',
},
secondaryButtonText: {
fontSize: theme.typography.bodyMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
primaryButton: {
flex: 1,
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.medium,
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
alignItems: 'center',
shadowColor: theme.colors.primary,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 3,
},
primaryButtonText: {
fontSize: theme.typography.bodyMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.background,
},
});
export default EmailAlreadyRegisteredModal;
/*
* End of File: EmailAlreadyRegisteredModal.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,370 @@
/*
* File: EmailStep.tsx
* Description: Email step component for signup flow
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
Alert,
KeyboardAvoidingView,
Platform,
ScrollView,
} from 'react-native';
import { theme } from '../../../../theme/theme';
import { EmailStepProps } from '../../types/signup';
import Icon from 'react-native-vector-icons/Feather';
// ============================================================================
// EMAIL STEP COMPONENT
// ============================================================================
/**
* EmailStep Component
*
* Purpose: First step of signup flow - email validation
*
* Features:
* - Email input with validation
* - Real-time email format checking
* - Continue button with loading state
* - Back navigation
* - Modern header with proper alignment
*/
const EmailStep: React.FC<EmailStepProps> = ({
onContinue,
onBack,
data,
isLoading,
}) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
const [email, setEmail] = useState(data.email || '');
const [emailError, setEmailError] = useState('');
// ============================================================================
// VALIDATION FUNCTIONS
// ============================================================================
/**
* Validate Email Format
*
* Purpose: Check if email format is valid
*/
const validateEmailFormat = (email: string): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
/**
* Handle Email Change
*
* Purpose: Update email and clear errors
*/
const handleEmailChange = (text: string) => {
setEmail(text);
setEmailError('');
};
/**
* Handle Continue
*
* Purpose: Validate email and proceed to next step
*/
const handleContinue = () => {
// Clear previous errors
setEmailError('');
// Validate email format
if (!email.trim()) {
setEmailError('Email is required');
return;
}
if (!validateEmailFormat(email.trim())) {
setEmailError('Please enter a valid email address');
return;
}
// Call parent handler
onContinue(email.trim());
};
// ============================================================================
// RENDER
// ============================================================================
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
>
{/* Header */}
<View style={styles.header}>
<TouchableOpacity onPress={onBack} style={styles.backButton}>
<Icon
name="arrow-left"
size={24}
color={theme.colors.textPrimary}
/>
</TouchableOpacity>
<View style={styles.headerContent}>
<Text style={styles.title}>Create Account</Text>
<Text style={styles.subtitle}>Step 1 of 5</Text>
</View>
<View style={styles.headerSpacer} />
</View>
{/* Content */}
<View style={styles.content}>
<Text style={styles.sectionTitle}>Enter your email address</Text>
<Text style={styles.description}>
We'll use this email to create your account and send you important updates.
</Text>
{/* Email Input */}
<View style={styles.inputContainer}>
<Text style={styles.inputLabel}>Email Address</Text>
<TextInput
style={[
styles.input,
emailError ? styles.inputError : null,
]}
placeholder="Enter your email address"
placeholderTextColor={theme.colors.textMuted}
value={email}
onChangeText={handleEmailChange}
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
autoComplete="email"
textContentType="emailAddress"
editable={!isLoading}
/>
{emailError ? (
<Text style={styles.errorText}>{emailError}</Text>
) : null}
</View>
{/* Continue Button */}
<TouchableOpacity
style={[
styles.continueButton,
(!email.trim() || isLoading) ? styles.continueButtonDisabled : null,
]}
onPress={handleContinue}
disabled={!email.trim() || isLoading}
>
<Text style={[
styles.continueButtonText,
(!email.trim() || isLoading) ? styles.continueButtonTextDisabled : null,
]}>
{isLoading ? 'Validating...' : 'Continue'}
</Text>
</TouchableOpacity>
{/* Additional Info */}
<View style={styles.infoContainer}>
<Text style={styles.infoText}>
By continuing, you agree to our Terms of Service and Privacy Policy.
</Text>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
// Scroll view
scrollView: {
flex: 1,
},
// Scroll content
scrollContent: {
flexGrow: 1,
paddingHorizontal: theme.spacing.sm,
},
// Header section
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingTop: theme.spacing.xl,
paddingBottom: theme.spacing.lg,
marginBottom: theme.spacing.lg,
},
// Back button
backButton: {
padding: theme.spacing.sm,
borderRadius: theme.borderRadius.medium,
backgroundColor: theme.colors.backgroundAlt,
},
// Header content
headerContent: {
flex: 1,
alignItems: 'center',
},
// Header spacer
headerSpacer: {
width: 40,
},
// 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,
},
// Content section
content: {
flex: 1,
justifyContent: 'center',
paddingBottom: theme.spacing.xl,
},
// Section title
sectionTitle: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
// Description
description: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xl,
},
// Input container
inputContainer: {
marginBottom: theme.spacing.xl,
},
// Input label
inputLabel: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
// Input field
input: {
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.md,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textPrimary,
backgroundColor: theme.colors.background,
},
// Input error state
inputError: {
borderColor: theme.colors.error,
},
// Error text
errorText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.error,
marginTop: theme.spacing.xs,
},
// Continue button
continueButton: {
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.medium,
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
alignItems: 'center',
marginBottom: theme.spacing.lg,
...theme.shadows.primary,
},
// Continue button disabled
continueButtonDisabled: {
backgroundColor: theme.colors.border,
opacity: 0.6,
},
// Continue button text
continueButtonText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.background,
},
// Continue button text disabled
continueButtonTextDisabled: {
color: theme.colors.textMuted,
},
// Info container
infoContainer: {
alignItems: 'center',
},
// Info text
infoText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
textAlign: 'center',
},
});
export default EmailStep;
/*
* End of File: EmailStep.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,592 @@
/*
* File: HospitalSelectionStep.tsx
* Description: Hospital selection step component for signup flow
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState, useMemo } from 'react';
import {
View,
Text,
TouchableOpacity,
StyleSheet,
ScrollView,
FlatList,
ActivityIndicator,
TextInput,
KeyboardAvoidingView,
Platform,
} from 'react-native';
import { theme } from '../../../../theme/theme';
import { HospitalSelectionStepProps, Hospital } from '../../types/signup';
import Icon from 'react-native-vector-icons/Feather';
// ============================================================================
// HOSPITAL SELECTION STEP COMPONENT
// ============================================================================
/**
* HospitalSelectionStep Component
*
* Purpose: Fifth step of signup flow - hospital selection
*
* Features:
* - Hospital list display from Redux state
* - Hospital selection with visual feedback
* - Search functionality for hospitals
* - Loading states and error handling
* - Continue button with validation (sticky bottom)
* - Back navigation with modern header
* - Scrollable hospital list
*/
const HospitalSelectionStep: React.FC<HospitalSelectionStepProps> = ({
onContinue,
onBack,
data,
isLoading,
hospitals,
hospitalLoading,
}) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
const [selectedHospitalId, setSelectedHospitalId] = useState(data.hospital_id || '');
const [searchQuery, setSearchQuery] = useState('');
// ============================================================================
// COMPUTED VALUES
// ============================================================================
/**
* Filtered Hospitals
*
* Purpose: Filter hospitals based on search query
*/
const filteredHospitals = useMemo(() => {
if (!hospitals) return [];
if (!searchQuery.trim()) return hospitals;
const query = searchQuery.toLowerCase();
return hospitals.filter(hospital =>
hospital.hospital_name?.toLowerCase().includes(query)
);
}, [hospitals, searchQuery]);
// ============================================================================
// HANDLERS
// ============================================================================
/**
* Handle Hospital Selection
*
* Purpose: Select a hospital
*/
const handleHospitalSelect = (hospitalId: string) => {
setSelectedHospitalId(hospitalId);
};
/**
* Handle Continue
*
* Purpose: Validate selection and proceed to next step
*/
const handleContinue = () => {
if (!selectedHospitalId) {
// Show error or alert
return;
}
onContinue(selectedHospitalId);
};
/**
* Handle Search Input Change
*
* Purpose: Update search query
*/
const handleSearchChange = (text: string) => {
setSearchQuery(text);
};
/**
* Clear Search
*
* Purpose: Clear search query
*/
const handleClearSearch = () => {
setSearchQuery('');
};
// ============================================================================
// RENDER FUNCTIONS
// ============================================================================
/**
* Render Hospital Item
*
* Purpose: Render individual hospital item
*/
const renderHospitalItem = ({ item }: { item: Hospital }) => {
const isSelected = selectedHospitalId === item.hospital_id;
return (
<TouchableOpacity
style={[
styles.hospitalItem,
isSelected && styles.hospitalItemSelected,
]}
onPress={() => handleHospitalSelect(item.hospital_id || '')}
disabled={isLoading}
>
<View style={styles.hospitalContent}>
<View style={styles.hospitalInfo}>
<Text style={[
styles.hospitalName,
isSelected && styles.hospitalNameSelected,
]}>
{item.hospital_name || 'Unknown Hospital'}
</Text>
</View>
{isSelected && (
<View style={styles.selectedIcon}>
<Icon name="check" size={20} color={theme.colors.background} />
</View>
)}
</View>
</TouchableOpacity>
);
};
/**
* Render Search Input
*
* Purpose: Render search input field
*/
const renderSearchInput = () => (
<View style={styles.searchContainer}>
<View style={styles.searchInputWrapper}>
<Icon name="search" size={20} color={theme.colors.textMuted} style={styles.searchIcon} />
<TextInput
style={styles.searchInput}
placeholder="Search hospitals..."
placeholderTextColor={theme.colors.textMuted}
value={searchQuery}
onChangeText={handleSearchChange}
autoCapitalize="none"
autoCorrect={false}
editable={!hospitalLoading}
/>
{searchQuery.length > 0 && (
<TouchableOpacity onPress={handleClearSearch} style={styles.clearButton}>
<Icon name="x" size={18} color={theme.colors.textMuted} />
</TouchableOpacity>
)}
</View>
</View>
);
/**
* Render Loading State
*
* Purpose: Show loading indicator while fetching hospitals
*/
const renderLoadingState = () => (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color={theme.colors.primary} />
<Text style={styles.loadingText}>Loading hospitals...</Text>
</View>
);
/**
* Render Empty State
*
* Purpose: Show message when no hospitals are available
*/
const renderEmptyState = () => (
<View style={styles.emptyContainer}>
<Icon name="building" size={48} color={theme.colors.textMuted} />
<Text style={styles.emptyTitle}>
{searchQuery ? 'No Hospitals Found' : 'No Hospitals Available'}
</Text>
<Text style={styles.emptyText}>
{searchQuery
? 'Try adjusting your search terms or browse all hospitals.'
: 'There are no hospitals available at the moment. Please try again later.'
}
</Text>
</View>
);
/**
* Render Search Results Info
*
* Purpose: Show search results count
*/
const renderSearchResultsInfo = () => {
if (!searchQuery.trim()) return null;
return (
<View style={styles.searchResultsInfo}>
<Text style={styles.searchResultsText}>
{filteredHospitals.length} hospital{filteredHospitals.length !== 1 ? 's' : ''} found
</Text>
</View>
);
};
// ============================================================================
// RENDER
// ============================================================================
return (
<View style={styles.container}>
{/* Header */}
<View style={styles.header}>
<TouchableOpacity onPress={onBack} style={styles.backButton}>
<Icon name="arrow-left" size={24} color={theme.colors.textPrimary} />
</TouchableOpacity>
<View style={styles.headerContent}>
<Text style={styles.title}>Create Account</Text>
<Text style={styles.subtitle}>Step 5 of 5</Text>
</View>
<View style={styles.headerSpacer} />
</View>
{/* Content */}
<View style={styles.content}>
<Text style={styles.sectionTitle}>Select Your Hospital</Text>
<Text style={styles.description}>
Choose the hospital where you work or will be practicing.
</Text>
{/* Search Input */}
{renderSearchInput()}
{/* Search Results Info */}
{renderSearchResultsInfo()}
{/* Hospital List */}
<View style={styles.hospitalListContainer}>
{hospitalLoading ? (
renderLoadingState()
) : filteredHospitals.length > 0 ? (
<FlatList
data={filteredHospitals}
renderItem={renderHospitalItem}
keyExtractor={(item) => item.hospital_id || ''}
showsVerticalScrollIndicator={true}
contentContainerStyle={styles.hospitalList}
keyboardShouldPersistTaps="handled"
/>
) : (
renderEmptyState()
)}
</View>
</View>
{/* Sticky Continue Button */}
<View style={styles.stickyButtonContainer}>
<TouchableOpacity
style={[
styles.continueButton,
(!selectedHospitalId || isLoading || hospitalLoading)
? styles.continueButtonDisabled
: null,
]}
onPress={handleContinue}
disabled={!selectedHospitalId || isLoading || hospitalLoading}
>
<Text style={[
styles.continueButtonText,
(!selectedHospitalId || isLoading || hospitalLoading)
? styles.continueButtonTextDisabled
: null,
]}>
{isLoading ? 'Creating Account...' : 'Create Account'}
</Text>
</TouchableOpacity>
</View>
</View>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
// Header section
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingTop: theme.spacing.xl,
paddingBottom: theme.spacing.lg,
paddingHorizontal: theme.spacing.lg,
borderBottomWidth: 1,
borderBottomColor: theme.colors.border,
},
// Back button
backButton: {
padding: theme.spacing.sm,
borderRadius: theme.borderRadius.medium,
backgroundColor: theme.colors.backgroundAlt,
},
// Header content
headerContent: {
flex: 1,
alignItems: 'center',
},
// Header spacer
headerSpacer: {
width: 40,
},
// 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,
},
// Content section
content: {
flex: 1,
paddingHorizontal: theme.spacing.lg,
paddingTop: theme.spacing.lg,
},
// Section title
sectionTitle: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
// Description
description: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.lg,
},
// Search container
searchContainer: {
marginBottom: theme.spacing.lg,
},
// Search input wrapper
searchInputWrapper: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: theme.colors.backgroundAlt,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
},
// Search icon
searchIcon: {
marginRight: theme.spacing.sm,
},
// Search input
searchInput: {
flex: 1,
paddingVertical: theme.spacing.md,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textPrimary,
},
// Clear button
clearButton: {
padding: theme.spacing.xs,
marginLeft: theme.spacing.sm,
},
// Search results info
searchResultsInfo: {
marginBottom: theme.spacing.md,
},
// Search results text
searchResultsText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
},
// Hospital list container
hospitalListContainer: {
flex: 1,
},
// Hospital list
hospitalList: {
paddingBottom: theme.spacing.lg,
},
// Hospital item
hospitalItem: {
backgroundColor: theme.colors.background,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md,
marginBottom: theme.spacing.sm,
},
// Hospital item selected
hospitalItemSelected: {
borderColor: theme.colors.primary,
backgroundColor: theme.colors.tertiary,
},
// Hospital content
hospitalContent: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
// Hospital info
hospitalInfo: {
flex: 1,
},
// Hospital name
hospitalName: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
},
// Hospital name selected
hospitalNameSelected: {
color: theme.colors.primary,
fontFamily: theme.typography.fontFamily.bold,
},
// Selected icon
selectedIcon: {
width: 24,
height: 24,
borderRadius: 12,
backgroundColor: theme.colors.primary,
alignItems: 'center',
justifyContent: 'center',
},
// Loading container
loadingContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: theme.spacing.xxl,
},
// Loading text
loadingText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginTop: theme.spacing.md,
},
// Empty container
emptyContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: theme.spacing.xxl,
},
// Empty title
emptyTitle: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginTop: theme.spacing.md,
marginBottom: theme.spacing.sm,
},
// Empty text
emptyText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
textAlign: 'center',
paddingHorizontal: theme.spacing.lg,
},
// Sticky button container
stickyButtonContainer: {
paddingHorizontal: theme.spacing.lg,
paddingVertical: theme.spacing.lg,
backgroundColor: theme.colors.background,
borderTopWidth: 1,
borderTopColor: theme.colors.border,
},
// Continue button
continueButton: {
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.medium,
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
alignItems: 'center',
...theme.shadows.primary,
},
// Continue button disabled
continueButtonDisabled: {
backgroundColor: theme.colors.border,
opacity: 0.6,
},
// Continue button text
continueButtonText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.background,
},
// Continue button text disabled
continueButtonTextDisabled: {
color: theme.colors.textMuted,
},
});
export default HospitalSelectionStep;
/*
* End of File: HospitalSelectionStep.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,399 @@
/*
* File: NameStep.tsx
* Description: Name step component for signup flow
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
KeyboardAvoidingView,
Platform,
ScrollView,
} from 'react-native';
import { theme } from '../../../../theme/theme';
import { NameStepProps } from '../../types/signup';
import Icon from 'react-native-vector-icons/Feather';
// ============================================================================
// NAME STEP COMPONENT
// ============================================================================
/**
* NameStep Component
*
* Purpose: Third step of signup flow - personal information
*
* Features:
* - First name, last name, and username inputs
* - Real-time validation with error handling
* - Continue button with loading state
* - Back navigation with modern header
*/
const NameStep: React.FC<NameStepProps> = ({
onContinue,
onBack,
data,
isLoading,
}) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
const [firstName, setFirstName] = useState(data.first_name || '');
const [lastName, setLastName] = useState(data.last_name || '');
const [username, setUsername] = useState(data.username || '');
const [errors, setErrors] = useState({
firstName: '',
lastName: '',
username: '',
});
// ============================================================================
// VALIDATION FUNCTIONS
// ============================================================================
/**
* Validate Input Fields
*
* Purpose: Check if all fields are valid
*/
const validateFields = (): boolean => {
const newErrors = {
firstName: '',
lastName: '',
username: '',
};
if (!firstName.trim()) {
newErrors.firstName = 'First name is required';
}
if (!lastName.trim()) {
newErrors.lastName = 'Last name is required';
}
if (!username.trim()) {
newErrors.username = 'Username is required';
} else if (username.length < 3) {
newErrors.username = 'Username must be at least 3 characters';
}
setErrors(newErrors);
return !Object.values(newErrors).some(error => error !== '');
};
/**
* Handle Continue
*
* Purpose: Validate fields and proceed to next step
*/
const handleContinue = () => {
if (validateFields()) {
onContinue(firstName.trim(), lastName.trim(), username.trim());
}
};
// ============================================================================
// RENDER
// ============================================================================
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
>
{/* Header */}
<View style={styles.header}>
<TouchableOpacity onPress={onBack} style={styles.backButton}>
<Icon name="arrow-left" size={24} color={theme.colors.textPrimary} />
</TouchableOpacity>
<View style={styles.headerContent}>
<Text style={styles.title}>Create Account</Text>
<Text style={styles.subtitle}>Step 3 of 5</Text>
</View>
<View style={styles.headerSpacer} />
</View>
{/* Content */}
<View style={styles.content}>
<Text style={styles.sectionTitle}>Tell us about yourself</Text>
<Text style={styles.description}>
Please provide your name and choose a username for your account.
</Text>
{/* First Name Input */}
<View style={styles.inputContainer}>
<Text style={styles.inputLabel}>First Name</Text>
<TextInput
style={[
styles.input,
errors.firstName ? styles.inputError : null,
]}
placeholder="Enter your first name"
placeholderTextColor={theme.colors.textMuted}
value={firstName}
onChangeText={(text) => {
setFirstName(text);
setErrors(prev => ({ ...prev, firstName: '' }));
}}
autoCapitalize="words"
autoCorrect={false}
editable={!isLoading}
/>
{errors.firstName ? (
<Text style={styles.errorText}>{errors.firstName}</Text>
) : null}
</View>
{/* Last Name Input */}
<View style={styles.inputContainer}>
<Text style={styles.inputLabel}>Last Name</Text>
<TextInput
style={[
styles.input,
errors.lastName ? styles.inputError : null,
]}
placeholder="Enter your last name"
placeholderTextColor={theme.colors.textMuted}
value={lastName}
onChangeText={(text) => {
setLastName(text);
setErrors(prev => ({ ...prev, lastName: '' }));
}}
autoCapitalize="words"
autoCorrect={false}
editable={!isLoading}
/>
{errors.lastName ? (
<Text style={styles.errorText}>{errors.lastName}</Text>
) : null}
</View>
{/* Username Input */}
<View style={styles.inputContainer}>
<Text style={styles.inputLabel}>Username</Text>
<TextInput
style={[
styles.input,
errors.username ? styles.inputError : null,
]}
placeholder="Choose a username"
placeholderTextColor={theme.colors.textMuted}
value={username}
onChangeText={(text) => {
setUsername(text);
setErrors(prev => ({ ...prev, username: '' }));
}}
autoCapitalize="none"
autoCorrect={false}
editable={!isLoading}
/>
{errors.username ? (
<Text style={styles.errorText}>{errors.username}</Text>
) : null}
</View>
{/* Continue Button */}
<TouchableOpacity
style={[
styles.continueButton,
(!firstName.trim() || !lastName.trim() || !username.trim() || isLoading)
? styles.continueButtonDisabled
: null,
]}
onPress={handleContinue}
disabled={!firstName.trim() || !lastName.trim() || !username.trim() || isLoading}
>
<Text style={[
styles.continueButtonText,
(!firstName.trim() || !lastName.trim() || !username.trim() || isLoading)
? styles.continueButtonTextDisabled
: null,
]}>
{isLoading ? 'Validating...' : 'Continue'}
</Text>
</TouchableOpacity>
</View>
</ScrollView>
</KeyboardAvoidingView>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
// Scroll view
scrollView: {
flex: 1,
},
// Scroll content
scrollContent: {
flexGrow: 1,
paddingHorizontal: theme.spacing.sm,
},
// Header section
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingTop: theme.spacing.xl,
paddingBottom: theme.spacing.lg,
marginBottom: theme.spacing.lg,
},
// Back button
backButton: {
padding: theme.spacing.sm,
borderRadius: theme.borderRadius.medium,
backgroundColor: theme.colors.backgroundAlt,
},
// Header content
headerContent: {
flex: 1,
alignItems: 'center',
},
// Header spacer
headerSpacer: {
width: 40,
},
// 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,
},
// Content section
content: {
flex: 1,
justifyContent: 'center',
paddingBottom: theme.spacing.xl,
},
// Section title
sectionTitle: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
// Description
description: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xl,
},
// Input container
inputContainer: {
marginBottom: theme.spacing.xl,
},
// Input label
inputLabel: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
// Input field
input: {
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.md,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textPrimary,
backgroundColor: theme.colors.background,
},
// Input error state
inputError: {
borderColor: theme.colors.error,
},
// Error text
errorText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.error,
marginTop: theme.spacing.xs,
},
// Continue button
continueButton: {
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.medium,
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
alignItems: 'center',
marginBottom: theme.spacing.lg,
...theme.shadows.primary,
},
// Continue button disabled
continueButtonDisabled: {
backgroundColor: theme.colors.border,
opacity: 0.6,
},
// Continue button text
continueButtonText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.background,
},
// Continue button text disabled
continueButtonTextDisabled: {
color: theme.colors.textMuted,
},
});
export default NameStep;
/*
* End of File: NameStep.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,628 @@
/*
* File: PasswordStep.tsx
* Description: Password step component for signup flow with comprehensive validation
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState, useEffect } from 'react';
import {
View,
Text,
TextInput,
TouchableOpacity,
StyleSheet,
KeyboardAvoidingView,
Platform,
ScrollView,
} from 'react-native';
import { theme } from '../../../../theme/theme';
import { PasswordStepProps } from '../../types/signup';
import Icon from 'react-native-vector-icons/Feather';
// ============================================================================
// INTERFACES
// ============================================================================
/**
* PasswordRule Interface
*
* Purpose: Defines the structure for password validation rules
*/
interface PasswordRule {
id: string;
label: string;
validator: (password: string) => boolean;
isValid: boolean;
}
// ============================================================================
// PASSWORD STEP COMPONENT
// ============================================================================
/**
* PasswordStep Component
*
* Purpose: Second step of signup flow - password creation with comprehensive validation
*
* Features:
* - Password input with visibility toggle
* - Comprehensive password validation rules
* - Real-time password strength checking
* - Visual feedback for each requirement
* - Continue button with loading state
* - Back navigation with modern header
*/
const PasswordStep: React.FC<PasswordStepProps> = ({
onContinue,
onBack,
data,
isLoading,
}) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
const [password, setPassword] = useState(data.password || '');
const [confirmPassword, setConfirmPassword] = useState('');
const [passwordError, setPasswordError] = useState('');
const [confirmPasswordError, setConfirmPasswordError] = useState('');
const [isPasswordVisible, setPasswordVisible] = useState(false);
const [isConfirmPasswordVisible, setConfirmPasswordVisible] = useState(false);
// Password validation rules
const [passwordRules, setPasswordRules] = useState<PasswordRule[]>([
{
id: 'length',
label: 'At least 8 characters',
validator: (pwd: string) => pwd.length >= 8,
isValid: false,
},
{
id: 'uppercase',
label: 'One uppercase letter',
validator: (pwd: string) => /[A-Z]/.test(pwd),
isValid: false,
},
{
id: 'lowercase',
label: 'One lowercase letter',
validator: (pwd: string) => /[a-z]/.test(pwd),
isValid: false,
},
{
id: 'number',
label: 'One number',
validator: (pwd: string) => /\d/.test(pwd),
isValid: false,
},
{
id: 'special',
label: 'One special character',
validator: (pwd: string) => /[!@#$%^&*(),.?":{}|<>]/.test(pwd),
isValid: false,
},
{
id: 'match',
label: 'Passwords match',
validator: (pwd: string) => pwd === confirmPassword && confirmPassword.length > 0,
isValid: false,
},
]);
// ============================================================================
// EFFECTS
// ============================================================================
/**
* useEffect for password validation
*
* Purpose: Update password rules when password or confirm password changes
*/
useEffect(() => {
updatePasswordRules(password);
}, [password, confirmPassword]);
// ============================================================================
// VALIDATION FUNCTIONS
// ============================================================================
/**
* Update Password Rules
*
* Purpose: Update password validation rules based on current password and confirm password
*
* @param pwd - Current password value
*/
const updatePasswordRules = (pwd: string) => {
setPasswordRules(prevRules =>
prevRules.map(rule => {
if (rule.id === 'match') {
return {
...rule,
isValid: pwd === confirmPassword && confirmPassword.length > 0,
};
}
return {
...rule,
isValid: rule.validator(pwd),
};
})
);
};
/**
* Validate Password
*
* Purpose: Check if all password requirements are met
*
* @param pwd - Password to validate
* @returns boolean indicating if password meets all requirements
*/
const validatePassword = (pwd: string): boolean => {
return passwordRules.every(rule => rule.isValid);
};
/**
* Handle Password Change
*
* Purpose: Update password and clear errors
*/
const handlePasswordChange = (text: string) => {
setPassword(text);
setPasswordError('');
// Clear confirm password error if passwords now match
if (confirmPassword && text === confirmPassword) {
setConfirmPasswordError('');
}
// Update password rules to reflect the match status
updatePasswordRules(text);
};
/**
* Handle Confirm Password Change
*
* Purpose: Update confirm password and validate match
*/
const handleConfirmPasswordChange = (text: string) => {
setConfirmPassword(text);
setConfirmPasswordError('');
// Check if passwords match
if (text && text !== password) {
setConfirmPasswordError('Passwords do not match');
}
// Update password rules to reflect the match status
updatePasswordRules(password);
};
/**
* Handle Continue
*
* Purpose: Validate password and proceed to next step
*/
const handleContinue = () => {
// Clear previous errors
setPasswordError('');
setConfirmPasswordError('');
// Validate password
if (!password.trim()) {
setPasswordError('Password is required');
return;
}
if (!validatePassword(password.trim())) {
setPasswordError('Please meet all password requirements');
return;
}
// Validate confirm password
if (!confirmPassword.trim()) {
setConfirmPasswordError('Please confirm your password');
return;
}
if (password.trim() !== confirmPassword.trim()) {
setConfirmPasswordError('Passwords do not match');
return;
}
// Call parent handler
onContinue(password.trim());
};
/**
* Render Password Rule
*
* Purpose: Render individual password validation rule
*
* @param rule - Password rule to render
* @returns JSX element for the rule
*/
const renderPasswordRule = (rule: PasswordRule) => (
<View key={rule.id} style={styles.ruleContainer}>
<View style={[styles.checkbox, rule.isValid && styles.checkboxChecked]}>
{rule.isValid && (
<Icon name="check" size={12} color={theme.colors.background} />
)}
</View>
<Text style={[styles.ruleText, rule.isValid && styles.ruleTextValid]}>
{rule.label}
</Text>
</View>
);
// ============================================================================
// RENDER
// ============================================================================
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={false}
>
{/* Header */}
<View style={styles.header}>
<TouchableOpacity onPress={onBack} style={styles.backButton}>
<Icon name="arrow-left" size={24} color={theme.colors.textPrimary} />
</TouchableOpacity>
<View style={styles.headerContent}>
<Text style={styles.title}>Create Account</Text>
<Text style={styles.subtitle}>Step 2 of 5</Text>
</View>
<View style={styles.headerSpacer} />
</View>
{/* Content */}
<View style={styles.content}>
<Text style={styles.sectionTitle}>Create a strong password</Text>
<Text style={styles.description}>
Choose a password that meets all the security requirements below.
</Text>
{/* Password Input */}
<View style={styles.inputContainer}>
<Text style={styles.inputLabel}>Password</Text>
<View style={[
styles.inputWrapper,
passwordError ? styles.inputWrapperError : null,
]}>
<TextInput
style={styles.input}
placeholder="Enter your password"
placeholderTextColor={theme.colors.textMuted}
value={password}
onChangeText={handlePasswordChange}
secureTextEntry={!isPasswordVisible}
autoCapitalize="none"
autoCorrect={false}
editable={!isLoading}
/>
<TouchableOpacity
onPress={() => setPasswordVisible(!isPasswordVisible)}
style={styles.eyeIcon}
disabled={isLoading}
>
<Icon
name={isPasswordVisible ? 'eye-off' : 'eye'}
size={22}
color={theme.colors.textSecondary}
/>
</TouchableOpacity>
</View>
{passwordError ? (
<Text style={styles.errorText}>{passwordError}</Text>
) : null}
</View>
{/* Confirm Password Input */}
<View style={styles.inputContainer}>
<Text style={styles.inputLabel}>Confirm Password</Text>
<View style={[
styles.inputWrapper,
confirmPasswordError ? styles.inputWrapperError : null,
]}>
<TextInput
style={styles.input}
placeholder="Confirm your password"
placeholderTextColor={theme.colors.textMuted}
value={confirmPassword}
onChangeText={handleConfirmPasswordChange}
secureTextEntry={!isConfirmPasswordVisible}
autoCapitalize="none"
autoCorrect={false}
editable={!isLoading}
/>
<TouchableOpacity
onPress={() => setConfirmPasswordVisible(!isConfirmPasswordVisible)}
style={styles.eyeIcon}
disabled={isLoading}
>
<Icon
name={isConfirmPasswordVisible ? 'eye-off' : 'eye'}
size={22}
color={theme.colors.textSecondary}
/>
</TouchableOpacity>
</View>
{confirmPasswordError ? (
<Text style={styles.errorText}>{confirmPasswordError}</Text>
) : null}
</View>
{/* Password Requirements */}
<View style={styles.requirementsContainer}>
<Text style={styles.requirementsTitle}>Password Requirements:</Text>
<View style={styles.rulesGrid}>
{passwordRules.map(renderPasswordRule)}
</View>
</View>
{/* Continue Button */}
<TouchableOpacity
style={[
styles.continueButton,
(!password.trim() || !confirmPassword.trim() || isLoading || !validatePassword(password) || password !== confirmPassword) ? styles.continueButtonDisabled : null,
]}
onPress={handleContinue}
disabled={!password.trim() || !confirmPassword.trim() || isLoading || !validatePassword(password) || password !== confirmPassword}
>
<Text style={[
styles.continueButtonText,
(!password.trim() || !confirmPassword.trim() || isLoading || !validatePassword(password) || password !== confirmPassword) ? styles.continueButtonTextDisabled : null,
]}>
{isLoading ? 'Processing...' : 'Continue'}
</Text>
</TouchableOpacity>
</View>
</ScrollView>
</KeyboardAvoidingView>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
// Scroll view
scrollView: {
flex: 1,
},
// Scroll content
scrollContent: {
flexGrow: 1,
paddingHorizontal: theme.spacing.sm,
},
// Header section
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingTop: theme.spacing.xl,
paddingBottom: theme.spacing.lg,
marginBottom: theme.spacing.lg,
},
// Back button
backButton: {
padding: theme.spacing.sm,
borderRadius: theme.borderRadius.medium,
backgroundColor: theme.colors.backgroundAlt,
},
// Header content
headerContent: {
flex: 1,
alignItems: 'center',
},
// Header spacer
headerSpacer: {
width: 40,
},
// 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,
},
// Content section
content: {
flex: 1,
justifyContent: 'center',
paddingBottom: theme.spacing.xl,
},
// Section title
sectionTitle: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
// Description
description: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xl,
},
// Input container
inputContainer: {
marginBottom: theme.spacing.xl,
},
// Input label
inputLabel: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
// Input wrapper
inputWrapper: {
flexDirection: 'row',
alignItems: 'center',
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
backgroundColor: theme.colors.background,
},
// Input field
input: {
flex: 1,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.md,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textPrimary,
},
// Eye icon
eyeIcon: {
padding: theme.spacing.sm,
},
// Input wrapper error state
inputWrapperError: {
borderColor: theme.colors.error,
},
// Error text
errorText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.error,
marginTop: theme.spacing.xs,
},
// Requirements container
requirementsContainer: {
marginBottom: theme.spacing.xl,
padding: theme.spacing.md,
backgroundColor: theme.colors.backgroundAlt,
borderRadius: theme.borderRadius.medium,
},
// Requirements title
requirementsTitle: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
// Rules grid
rulesGrid: {
gap: theme.spacing.xs,
},
// Rule container
ruleContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: theme.spacing.xs,
},
// Checkbox
checkbox: {
width: 18,
height: 18,
borderRadius: 4,
borderWidth: 2,
borderColor: theme.colors.border,
backgroundColor: theme.colors.backgroundAlt,
marginRight: theme.spacing.sm,
alignItems: 'center',
justifyContent: 'center',
},
// Checkbox checked
checkboxChecked: {
backgroundColor: theme.colors.primary,
borderColor: theme.colors.primary,
},
// Rule text
ruleText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
flex: 1,
},
// Rule text valid
ruleTextValid: {
color: theme.colors.success,
},
// Continue button
continueButton: {
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.medium,
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
alignItems: 'center',
marginBottom: theme.spacing.lg,
...theme.shadows.primary,
},
// Continue button disabled
continueButtonDisabled: {
backgroundColor: theme.colors.border,
opacity: 0.6,
},
// Continue button text
continueButtonText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.background,
},
// Continue button text disabled
continueButtonTextDisabled: {
color: theme.colors.textMuted,
},
});
export default PasswordStep;
/*
* End of File: PasswordStep.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,20 @@
/*
* File: index.ts
* Description: Barrel exports for signup components
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// Export all signup step components
export { default as EmailStep } from './EmailStep';
export { default as PasswordStep } from './PasswordStep';
export { default as NameStep } from './NameStep';
export { default as DocumentUploadStep } from './DocumentUploadStep';
export { default as HospitalSelectionStep } from './HospitalSelectionStep';
export { default as EmailAlreadyRegisteredModal } from './EmailAlreadyRegisteredModal';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

79
app/modules/Auth/index.ts Normal file
View File

@ -0,0 +1,79 @@
/*
* File: index.ts
* Description: Main exports for Auth module
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// Export screens
export { default as LoginScreen } from './screens/LoginScreen';
export { default as SignUpScreen } from './screens/SignUpScreen';
// Export navigation
export {
AuthStackNavigator,
AuthStackParamList,
AuthNavigationProp,
AuthScreenProps,
LoginScreenProps,
SignUpScreenProps,
navigateToLogin,
navigateToSignUp,
goBack,
resetToLogin,
resetToSignUp,
replaceWithLogin,
replaceWithSignUp,
navigateToSignUpAndClearStack,
navigateToLoginAndClearStack,
} from './navigation';
// Export signup components
export {
EmailStep,
PasswordStep,
NameStep,
DocumentUploadStep,
HospitalSelectionStep,
EmailAlreadyRegisteredModal,
} from './components/signup';
// Export services
export { authAPI } from './services/signupAPI';
// Export types
export type {
SignUpData,
SignUpStep,
EmailStepProps,
PasswordStepProps,
NameStepProps,
DocumentUploadStepProps,
HospitalSelectionStepProps,
EmailValidationApiResponse,
UsernameValidationApiResponse,
HospitalListApiResponse,
SignUpApiResponse,
Hospital,
} from './types/signup';
// Export Redux
export {
loginUser,
ssoLogin,
emergencyAccess,
logoutUser,
clearError,
setBiometricEnabled,
setRememberDevice,
updateUserProfile,
setSessionToken,
clearSession,
setEmergencyAccess,
} from './redux/authSlice';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,101 @@
/*
* File: AuthStackNavigator.tsx
* Description: Stack navigator for authentication screens within the Auth module
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
// Import authentication screens
import LoginScreen from '../screens/LoginScreen';
import SignUpScreen from '../screens/SignUpScreen';
// Import navigation types
import { AuthStackParamList } from './navigationTypes';
import { theme } from '../../../theme';
// Create stack navigator for Auth module
const Stack = createStackNavigator<AuthStackParamList>();
/**
* AuthStackNavigator - Manages navigation between authentication screens
*
* This navigator handles the flow between:
* - LoginScreen: Main authentication screen
* - SignUpScreen: Multi-step registration process
*
* Features:
* - Clean header styling
* - Smooth transitions between screens
* - Type-safe navigation parameters
*/
const AuthStackNavigator: React.FC = () => {
return (
<Stack.Navigator
initialRouteName="Login"
screenOptions={{
// Header styling for authentication screens
headerStyle: {
backgroundColor: '#FFFFFF',
elevation: 0, // Remove shadow on Android
shadowOpacity: 0, // Remove shadow on iOS
},
headerTitleStyle: {
fontFamily: theme.typography.fontFamily.medium,
fontSize: 18,
color: '#212121',
},
headerTintColor: '#2196F3', // Back button and title color
// headerBackTitleVisible: false, // Hide back title on iOS
cardStyle: {
backgroundColor: '#FFFFFF',
},
// Smooth transitions
transitionSpec: {
open: {
animation: 'timing',
config: {
duration: 300,
},
},
close: {
animation: 'timing',
config: {
duration: 300,
},
},
},
}}
>
{/* Login Screen - Main authentication entry point */}
<Stack.Screen
name="Login"
component={LoginScreen}
options={{
title: 'Sign In',
headerShown: false, // Hide header for login screen
}}
/>
{/* Sign Up Screen - Multi-step registration process */}
<Stack.Screen
name="SignUp"
component={SignUpScreen}
options={{
title: 'Create Account',
headerShown: false, // Hide header for signup screen
}}
/>
</Stack.Navigator>
);
};
export default AuthStackNavigator;
/*
* End of File: AuthStackNavigator.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,37 @@
/*
* File: index.ts
* Description: Barrel exports for Auth module navigation
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// Export main navigator
export { default as AuthStackNavigator } from './AuthStackNavigator';
// Export navigation types
export type {
AuthStackParamList,
AuthNavigationProp,
AuthScreenProps,
LoginScreenProps,
SignUpScreenProps,
} from './navigationTypes';
// Export navigation utilities
export {
navigateToLogin,
navigateToSignUp,
goBack,
resetToLogin,
resetToSignUp,
replaceWithLogin,
replaceWithSignUp,
navigateToSignUpAndClearStack,
navigateToLoginAndClearStack,
} from './navigationUtils';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,63 @@
/*
* File: navigationTypes.ts
* Description: TypeScript types for Auth module navigation
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { StackNavigationProp } from '@react-navigation/stack';
/**
* AuthStackParamList - Defines the parameter list for Auth stack navigator
*
* This interface defines all the screens available in the Auth module
* and their associated navigation parameters.
*/
export type AuthStackParamList = {
// Login screen - Main authentication entry point
Login: undefined;
// Sign Up screen - Multi-step registration process
SignUp: undefined;
};
/**
* AuthNavigationProp - Type for navigation prop in Auth screens
*
* This type provides type-safe navigation methods for screens
* within the Auth module.
*/
export type AuthNavigationProp = StackNavigationProp<AuthStackParamList>;
/**
* AuthScreenProps - Base props interface for Auth screens
*
* This interface provides the common props that all Auth screens
* will receive, including navigation and route.
*/
export interface AuthScreenProps<T extends keyof AuthStackParamList> {
navigation: AuthNavigationProp;
route: {
key: string;
name: T;
params: AuthStackParamList[T];
};
}
/**
* LoginScreenProps - Props for LoginScreen component
*/
export type LoginScreenProps = AuthScreenProps<'Login'>;
/**
* SignUpScreenProps - Props for SignUpScreen component
*/
export type SignUpScreenProps = AuthScreenProps<'SignUp'>;
/*
* End of File: navigationTypes.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,107 @@
/*
* File: navigationUtils.ts
* Description: Navigation utilities for Auth module
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { AuthNavigationProp } from './navigationTypes';
/**
* AuthNavigationUtils - Utility functions for Auth module navigation
*
* This module provides helper functions for common navigation patterns
* within the Auth module, ensuring consistent navigation behavior.
*/
/**
* Navigate to Login screen
* @param navigation - Navigation prop from React Navigation
*/
export const navigateToLogin = (navigation: AuthNavigationProp): void => {
navigation.navigate('Login');
};
/**
* Navigate to Sign Up screen
* @param navigation - Navigation prop from React Navigation
*/
export const navigateToSignUp = (navigation: AuthNavigationProp): void => {
navigation.navigate('SignUp');
};
/**
* Go back to previous screen
* @param navigation - Navigation prop from React Navigation
*/
export const goBack = (navigation: AuthNavigationProp): void => {
if (navigation.canGoBack()) {
navigation.goBack();
}
};
/**
* Reset navigation stack to Login screen
* @param navigation - Navigation prop from React Navigation
*/
export const resetToLogin = (navigation: AuthNavigationProp): void => {
navigation.reset({
index: 0,
routes: [{ name: 'Login' }],
});
};
/**
* Reset navigation stack to Sign Up screen
* @param navigation - Navigation prop from React Navigation
*/
export const resetToSignUp = (navigation: AuthNavigationProp): void => {
navigation.reset({
index: 0,
routes: [{ name: 'SignUp' }],
});
};
/**
* Replace current screen with Login screen
* @param navigation - Navigation prop from React Navigation
*/
export const replaceWithLogin = (navigation: AuthNavigationProp): void => {
navigation.replace('Login');
};
/**
* Replace current screen with Sign Up screen
* @param navigation - Navigation prop from React Navigation
*/
export const replaceWithSignUp = (navigation: AuthNavigationProp): void => {
navigation.replace('SignUp');
};
/**
* Navigate to Sign Up screen and clear back stack
* @param navigation - Navigation prop from React Navigation
*/
export const navigateToSignUpAndClearStack = (navigation: AuthNavigationProp): void => {
navigation.reset({
index: 0,
routes: [{ name: 'SignUp' }],
});
};
/**
* Navigate to Login screen and clear back stack
* @param navigation - Navigation prop from React Navigation
*/
export const navigateToLoginAndClearStack = (navigation: AuthNavigationProp): void => {
navigation.reset({
index: 0,
routes: [{ name: 'Login' }],
});
};
/*
* End of File: navigationUtils.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,74 @@
/*
* File: authActions.ts
* Description: Async actions (thunks) for Auth state
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createAsyncThunk } from '@reduxjs/toolkit';
import { logout } from './authSlice';
import { authAPI } from '../services/authAPI';
import { showError, showSuccess } from '../../../shared/utils/toast';
/**
* Thunk to login user
*/
export const login = createAsyncThunk(
'auth/login',
async (credentials: { email: string; password: string }, { rejectWithValue }) => {
try {
const response:any = await authAPI.login(credentials.email, credentials.password,'web');
console.log('user response',response)
if(response.data.message && !response.data.success){
showError(response.data.message)
return rejectWithValue(response.data.message);
}
if(response.data.message && response.data.success){
showSuccess(response.data.message)
}
if (response.ok && response.data && response.data.data) {
// Return the user data for the fulfilled case
return {...response.data.data.user,access_token:response.data.data.access_token};
} else {
const errorMessage = response.data?.message || response.problem || 'Unknown error';
return rejectWithValue(errorMessage);
}
} catch (error: any) {
return rejectWithValue(error.message);
}
}
);
/**
* Thunk to logout user
*/
export const logoutUser = createAsyncThunk(
'auth/logout',
async (_, { dispatch, rejectWithValue }) => {
try {
// TODO: Add logout API call if needed
// const response = await authAPI.logout();
// For now, just dispatch the logout action
dispatch(logout());
// Show success message
showSuccess('Logged out successfully');
return true;
} catch (error: any) {
console.error('Logout error:', error);
return rejectWithValue(error.message);
}
}
);
/*
* End of File: authActions.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,48 @@
/*
* File: authSelectors.ts
* Description: Selectors for Auth redux state
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { RootState } from '../../../store';
export const selectUser = (state: RootState) => state.auth.user;
export const selectAuthLoading = (state: RootState) => state.auth.loading;
export const selectAuthError = (state: RootState) => state.auth.error;
export const selectIsAuthenticated = (state: RootState) => state.auth.isAuthenticated;
export const selectHospitals = (state: RootState) => state.hospital.hospitals;
// User profile selectors
export const selectUserProfile = (state: RootState) => state.auth.user;
export const selectUserDisplayName = (state: RootState) => state.auth.user?.display_name;
export const selectUserEmail = (state: RootState) => state.auth.user?.email;
export const selectUserFirstName = (state: RootState) => state.auth.user?.first_name;
export const selectUserLastName = (state: RootState) => state.auth.user?.last_name;
export const selectUserHospitalId = (state: RootState) => state.auth.user?.hospital_id;
export const selectUserProfilePhoto = (state: RootState) => state.auth.user?.profile_photo_url;
export const selectUserThemeColor = (state: RootState) => state.auth.user?.theme_color;
export const selectUserAccentColor = (state: RootState) => state.auth.user?.accent_color;
// Onboarding selectors
export const selectIsOnboarded = (state: RootState) => state.auth.user?.onboarded;
export const selectOnboardingCompleted = (state: RootState) => state.auth.user?.onboarding_completed;
export const selectOnboardingStep = (state: RootState) => state.auth.user?.onboarding_step;
export const selectOnboardingMessage = (state: RootState) => state.auth.user?.onboarding_message;
// Dashboard settings selectors
export const selectDashboardSettings = (state: RootState) => state.auth.user?.dashboard_settings;
export const selectDashboardTheme = (state: RootState) => state.auth.user?.dashboard_settings?.theme;
export const selectDashboardLanguage = (state: RootState) => state.auth.user?.dashboard_settings?.language;
export const selectDashboardTimezone = (state: RootState) => state.auth.user?.dashboard_settings?.timezone;
// Notification preferences selectors
export const selectNotificationPreferences = (state: RootState) => state.auth.user?.notification_preferences;
export const selectCriticalAlertsPreferences = (state: RootState) => state.auth.user?.notification_preferences?.critical_alerts;
export const selectSystemNotificationsPreferences = (state: RootState) => state.auth.user?.notification_preferences?.system_notifications;
/*
* End of File: authSelectors.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,103 @@
/*
* File: authSlice.ts
* Description: Redux slice for Auth state management
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { User, NotificationPreferences, DashboardSettings } from '../../../shared/types/auth';
import { login } from './authActions';
// Use User type from shared types as UserProfile
type UserProfile = User;
// Auth state type
interface AuthState {
user: UserProfile | null;
loading: boolean;
error: string | null;
isAuthenticated: boolean;
}
const initialState: AuthState = {
user: null,
loading: false,
error: null,
isAuthenticated: false,
};
/**
* Auth slice for managing authentication state
*/
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
logout(state) {
state.user = null;
state.isAuthenticated = false;
state.loading = false;
state.error = null;
},
updateOnboarded(state, action: PayloadAction<boolean>) {
if (state.user) {
state.user.onboarded = action.payload;
}
},
updateUserProfile(state, action: PayloadAction<Partial<UserProfile>>) {
if (state.user) {
state.user = { ...state.user, ...action.payload };
}
},
updateNotificationPreferences(state, action: PayloadAction<NotificationPreferences>) {
if (state.user) {
state.user.notification_preferences = action.payload;
}
},
updateDashboardSettings(state, action: PayloadAction<DashboardSettings>) {
if (state.user) {
state.user.dashboard_settings = action.payload;
}
},
clearError(state) {
state.error = null;
}
},
extraReducers: (builder) => {
// Login thunk cases
builder
.addCase(login.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(login.fulfilled, (state, action) => {
state.loading = false;
state.user = action.payload as UserProfile;
state.isAuthenticated = true;
state.error = null;
})
.addCase(login.rejected, (state, action) => {
state.loading = false;
state.error = action.payload as string || 'Login failed';
state.isAuthenticated = false;
});
},
});
export const {
logout,
updateOnboarded,
updateUserProfile,
updateNotificationPreferences,
updateDashboardSettings,
clearError
} = authSlice.actions;
export default authSlice.reducer;
/*
* End of File: authSlice.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,129 @@
/*
* File: hospitalSelectors.ts
* Description: Redux selectors for Hospital state
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createSelector } from '@reduxjs/toolkit';
import { RootState } from '../../../store';
// ============================================================================
// BASE SELECTORS
// ============================================================================
/**
* Select Hospital State
*
* Purpose: Get the entire hospital state from Redux store
*/
export const selectHospitalState = (state: RootState) => state.hospital;
/**
* Select Hospitals List
*
* Purpose: Get the list of hospitals
*/
export const selectHospitals = (state: RootState) => state.hospital.hospitals;
/**
* Select Hospital Loading State
*
* Purpose: Get the loading state for hospital operations
*/
export const selectHospitalLoading = (state: RootState) => state.hospital.loading;
/**
* Select Hospital Error
*
* Purpose: Get any error from hospital operations
*/
export const selectHospitalError = (state: RootState) => state.hospital.error;
// ============================================================================
// DERIVED SELECTORS
// ============================================================================
/**
* Select Hospitals Count
*
* Purpose: Get the total number of hospitals
*/
export const selectHospitalsCount = createSelector(
[selectHospitals],
(hospitals) => hospitals?.length || 0
);
/**
* Select Hospital by ID
*
* Purpose: Get a specific hospital by its ID
*/
export const selectHospitalById = createSelector(
[selectHospitals, (_state: RootState, hospitalId: string) => hospitalId],
(hospitals, hospitalId) =>
hospitals?.find(hospital => hospital.hospital_id === hospitalId) || null
);
/**
* Select Hospital Names
*
* Purpose: Get an array of hospital names for display
*/
export const selectHospitalNames = createSelector(
[selectHospitals],
(hospitals) =>
hospitals?.map(hospital => hospital.hospital_name).filter(Boolean) || []
);
/**
* Select Hospitals for Dropdown
*
* Purpose: Get hospitals formatted for dropdown/select components
*/
export const selectHospitalsForDropdown = createSelector(
[selectHospitals],
(hospitals) =>
hospitals?.map(hospital => ({
label: hospital.hospital_name || 'Unknown Hospital',
value: hospital.hospital_id || '',
})).filter(item => item.value) || []
);
/**
* Select Filtered Hospitals
*
* Purpose: Get hospitals filtered by search term
*/
export const selectFilteredHospitals = createSelector(
[selectHospitals, (_state: RootState, searchTerm: string) => searchTerm],
(hospitals, searchTerm) => {
if (!searchTerm.trim()) return hospitals || [];
const term = searchTerm.toLowerCase();
return hospitals?.filter(hospital =>
hospital.hospital_name?.toLowerCase().includes(term)
) || [];
}
);
/**
* Select Hospital Loading Status
*
* Purpose: Get comprehensive loading status for hospital operations
*/
export const selectHospitalStatus = createSelector(
[selectHospitalLoading, selectHospitalError, selectHospitalsCount],
(loading, error, count) => ({
loading,
error,
hasData: count > 0,
isEmpty: count === 0,
})
);
/*
* End of File: hospitalSelectors.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,170 @@
/*
* File: hospitalSlice.ts
* Description: Redux slice for Hospital state management (non-persisted)
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { authAPI } from '../services/authAPI';
import { showError } from '../../../shared/utils/toast';
// ============================================================================
// INTERFACES
// ============================================================================
/**
* Hospital Interface
*
* Purpose: Defines the structure for hospital data
*/
interface Hospital {
hospital_id: string | null;
hospital_name: string | null;
}
/**
* Hospital State Interface
*
* Purpose: Defines the structure for hospital state
*/
interface HospitalState {
hospitals: Hospital[] | null;
loading: boolean;
error: string | null;
}
// ============================================================================
// ASYNC THUNKS
// ============================================================================
/**
* Fetch Hospitals Async Thunk
*
* Purpose: Fetch hospital list from API
*
* Features:
* - API integration with error handling
* - Loading state management
* - Toast notifications for errors
* - Automatic state updates
*/
export const fetchHospitals = createAsyncThunk(
'hospital/fetchHospitals',
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;
} else {
showError('Error while fetching hospital list');
return rejectWithValue('Failed to fetch hospitals');
}
} catch (error: any) {
console.log('Hospital fetch error:', error);
showError('Error while fetching hospital list');
return rejectWithValue(error.message || 'Network error');
}
}
);
// ============================================================================
// INITIAL STATE
// ============================================================================
const initialState: HospitalState = {
hospitals: [],
loading: false,
error: null,
};
// ============================================================================
// HOSPITAL SLICE
// ============================================================================
/**
* Hospital Slice
*
* Purpose: Manages hospital-related state
*
* Features:
* - Hospital list management
* - Loading states for API calls
* - Error handling and display
* - Non-persisted state (not stored in AsyncStorage)
*/
const hospitalSlice = createSlice({
name: 'hospital',
initialState,
reducers: {
/**
* Set Hospitals Action
*
* Purpose: Manually set hospital list
*/
setHospitals(state, action: PayloadAction<Hospital[]>) {
state.hospitals = action.payload;
state.loading = false;
state.error = null;
},
/**
* Clear Hospitals Action
*
* Purpose: Clear hospital list and reset state
*/
clearHospitals(state) {
state.hospitals = [];
state.loading = false;
state.error = null;
},
/**
* Clear Error Action
*
* Purpose: Clear error state
*/
clearError(state) {
state.error = null;
},
},
extraReducers: (builder) => {
builder
// Fetch Hospitals - Pending
.addCase(fetchHospitals.pending, (state) => {
state.loading = true;
state.error = null;
})
// Fetch Hospitals - Fulfilled
.addCase(fetchHospitals.fulfilled, (state, action) => {
state.loading = false;
state.hospitals = action.payload;
state.error = null;
})
// Fetch Hospitals - Rejected
.addCase(fetchHospitals.rejected, (state, action) => {
state.loading = false;
state.error = action.payload as string || 'Failed to fetch hospitals';
});
},
});
// ============================================================================
// EXPORTS
// ============================================================================
export const {
setHospitals,
clearHospitals,
clearError,
} = hospitalSlice.actions;
export default hospitalSlice.reducer;
/*
* End of File: hospitalSlice.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,16 @@
/*
* File: index.ts
* Description: Barrel export for Auth redux
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export { default as authReducer } from './authSlice';
export * from './authActions';
export * from './authSelectors';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,420 @@
/*
* File: LoginScreen.tsx
* Description: Login screen with credential-based authentication
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState } from 'react';
import {
View,
Text,
StyleSheet,
TouchableWithoutFeedback,
Keyboard,
TouchableOpacity,
TextInput,
ScrollView,
KeyboardAvoidingView,
Alert,
Platform,
Image,
} from 'react-native';
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
import { login } from '../redux/authActions';
import { selectAuthLoading } from '../redux/authSelectors';
import { theme } from '../../../theme/theme';
import { validateEmail } from '../../../shared/utils/validators';
import Icon from 'react-native-vector-icons/Feather';
import { AuthNavigationProp } from '../navigation';
/**
* LoginScreenProps Interface
*
* Purpose: Defines the props required by the LoginScreen component
*
* Props:
* - navigation: React Navigation object for screen navigation
*/
interface LoginScreenProps {
navigation: AuthNavigationProp;
}
/**
* LoginScreen Component
*
* Purpose: Main authentication screen for credential-based login
*
* Authentication Flow:
* 1. Email/Password validation
* 2. Redux action dispatch for login
* 3. Loading state management
* 4. Error handling and user feedback
*
* Features:
* - Keyboard-aware layout for better UX
* - Form validation and error handling
* - Loading states during authentication
* - Password visibility toggle
* - Navigation to sign up screen
* - Responsive design for different screen sizes
*/
const LoginScreen: React.FC<LoginScreenProps> = ({ navigation }) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
// Form input states
const [email, setEmail] = useState(''); // User's email address
const [password, setPassword] = useState(''); // User's password
const [isPasswordVisible, setPasswordVisible] = useState(false); // Password visibility toggle
// Redux state
const dispatch = useAppDispatch();
const loading = useAppSelector(selectAuthLoading);
// ============================================================================
// EVENT HANDLERS
// ============================================================================
/**
* handleLogin Function
*
* Purpose: Process credential-based login with email and password
*
* Flow:
* 1. Validate that both email and password are provided
* 2. Validate email format
* 3. Show error alert if validation fails
* 4. Dispatch Redux login action with credentials
*/
const handleLogin = () => {
// Validate required fields
if (!email.trim() || !password.trim()) {
Alert.alert('Validation Error', 'Email and password are required.');
return;
}
// Validate email format
if (!validateEmail(email)) {
Alert.alert('Validation Error', 'Please enter a valid email address.');
return;
}
// Dispatch login action
dispatch(login({ email, password }));
};
/**
* handleSignUp Function
*
* Purpose: Navigate to the SignUpScreen for new user registration
*
* Flow: Navigate to SignUp screen using React Navigation
*/
const handleSignUp = () => {
navigation.navigate('SignUp');
};
/**
* togglePasswordVisibility Function
*
* Purpose: Toggle password field visibility for better UX
*/
const togglePasswordVisibility = () => {
setPasswordVisible(!isPasswordVisible);
};
// ============================================================================
// RENDER SECTION
// ============================================================================
return (
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
<View style={styles.content}>
{/* ========================================================================
* HEADER SECTION - App branding and title
* ======================================================================== */}
<View style={styles.header}>
<Text style={styles.title}>Physician</Text>
<Text style={styles.subtitle}>Emergency Department Access</Text>
</View>
<View style={styles.imageContainer}>
<Image source={require('../../../assets/images/hospital-logo.png')} style={styles.image} />
</View>
{/* ========================================================================
* LOGIN FORM - Main authentication interface
* ======================================================================== */}
<View style={styles.formContainer}>
{/* Email Input */}
<View style={styles.inputContainer}>
<Icon name="mail" size={20} color={theme.colors.textSecondary} style={styles.inputIcon} />
<TextInput
placeholder="Email"
value={email}
onChangeText={setEmail}
style={styles.inputField}
autoCapitalize="none"
keyboardType="email-address"
placeholderTextColor={theme.colors.textMuted}
editable={!loading}
/>
</View>
{/* Password Input */}
<View style={styles.inputContainer}>
<Icon name="lock" size={20} color={theme.colors.textSecondary} style={styles.inputIcon} />
<TextInput
placeholder="Password"
value={password}
onChangeText={setPassword}
style={styles.inputField}
secureTextEntry={!isPasswordVisible}
placeholderTextColor={theme.colors.textMuted}
editable={!loading}
/>
<TouchableOpacity
onPress={togglePasswordVisibility}
style={styles.eyeIcon}
disabled={loading}
>
<Icon
name={isPasswordVisible ? 'eye-off' : 'eye'}
size={22}
color={theme.colors.textSecondary}
/>
</TouchableOpacity>
</View>
{/* Login Button */}
<TouchableOpacity
style={[styles.button, styles.loginButton, loading && styles.buttonDisabled]}
onPress={handleLogin}
disabled={loading}
>
{loading ? (
<View style={styles.loadingContainer}>
<Text style={styles.buttonText}>Logging in...</Text>
</View>
) : (
<Text style={styles.buttonText}>Login</Text>
)}
</TouchableOpacity>
{/* Divider */}
<View style={styles.divider}>
<View style={styles.dividerLine} />
<Text style={styles.dividerText}>OR</Text>
<View style={styles.dividerLine} />
</View>
{/* Sign Up Button */}
<TouchableOpacity
style={[styles.button, styles.signUpButton]}
onPress={handleSignUp}
disabled={loading}
>
<Text style={styles.signUpButtonText}>Sign Up</Text>
</TouchableOpacity>
</View>
{/* ========================================================================
* FOOTER - Security and information message
* ======================================================================== */}
<View style={styles.footer}>
<Text style={styles.footerText}>
Secure access to patient information and critical alerts
</Text>
</View>
</View>
</ScrollView>
</KeyboardAvoidingView>
</TouchableWithoutFeedback>
);
};
// ============================================================================
// STYLES SECTION
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
// Content wrapper
content: {
flex: 1,
justifyContent: 'center',
padding: theme.spacing.lg,
},
// Header section
header: {
alignItems: 'center',
marginBottom: theme.spacing.xxl,
},
// App title
title: {
fontSize: theme.typography.fontSize.displayMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.primary,
marginBottom: theme.spacing.sm,
textAlign: 'center',
},
// App subtitle
subtitle: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
textAlign: 'center',
},
// Form container
formContainer: {
marginBottom: theme.spacing.xl,
},
// Input container
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: theme.colors.backgroundAlt,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
marginBottom: theme.spacing.md,
paddingHorizontal: theme.spacing.md,
paddingVertical: 2,
},
// Input icon
inputIcon: {
marginRight: theme.spacing.sm,
},
// Input field
inputField: {
flex: 1,
paddingVertical: theme.spacing.md,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textPrimary,
},
// Eye icon for password visibility
eyeIcon: {
paddingLeft: theme.spacing.sm,
},
// Base button styling
button: {
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
borderRadius: theme.borderRadius.medium,
alignItems: 'center',
marginBottom: theme.spacing.md,
},
// Login button
loginButton: {
backgroundColor: theme.colors.primary,
...theme.shadows.primary,
},
// Sign up button
signUpButton: {
backgroundColor: theme.colors.background,
borderWidth: 1,
borderColor: theme.colors.primary,
},
// Disabled button
buttonDisabled: {
opacity: 0.6,
},
// Button text
buttonText: {
color: theme.colors.background,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.medium,
},
// Sign up button text
signUpButtonText: {
color: theme.colors.primary,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.medium,
},
// Loading container
loadingContainer: {
flexDirection: 'row',
alignItems: 'center',
},
// Divider
divider: {
flexDirection: 'row',
alignItems: 'center',
marginVertical: theme.spacing.lg,
},
// Divider line
dividerLine: {
flex: 1,
height: 1,
backgroundColor: theme.colors.border,
},
// Divider text
dividerText: {
marginHorizontal: theme.spacing.md,
color: theme.colors.textSecondary,
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
},
// Footer
footer: {
alignItems: 'center',
},
// Footer text
footerText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textMuted,
textAlign: 'center',
},
// Image container
imageContainer: {
alignItems: 'center',
marginBottom: theme.spacing.xl,
},
// Image
image: {
width: '100%',
height: 150,
},
});
export default LoginScreen;
/*
* End of File: LoginScreen.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,641 @@
/*
* File: ResetPasswordScreen.tsx
* Description: Password reset screen for onboarding flow
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
TouchableOpacity,
ScrollView,
KeyboardAvoidingView,
Platform,
Alert,
} from 'react-native';
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
import { updateOnboarded, logout } from '../redux/authSlice';
import { authAPI } from '../services/authAPI';
import { showError, showSuccess } from '../../../shared/utils/toast';
import { theme } from '../../../theme/theme';
import Icon from 'react-native-vector-icons/Feather';
import { AuthNavigationProp } from '../navigation/navigationTypes';
/**
* ResetPasswordScreenProps Interface
*
* Purpose: Defines the props required by the ResetPasswordScreen component
*
* Props:
* - navigation: React Navigation object for screen navigation (optional when used in root stack)
*/
interface ResetPasswordScreenProps {
navigation?: AuthNavigationProp;
}
/**
* PasswordRule Interface
*
* Purpose: Defines the structure for password validation rules
*/
interface PasswordRule {
id: string;
label: string;
validator: (password: string) => boolean;
isValid: boolean;
}
/**
* ResetPasswordScreen Component
*
* Purpose: Password reset screen for users who need to set their initial password
*
* Features:
* 1. Password and confirm password input fields
* 2. Real-time password validation with visual feedback
* 3. Password visibility toggles
* 4. Password strength requirements display
* 5. Integration with Redux for onboarding status
* 6. API integration for password change
*
* Password Requirements:
* - At least 8 characters
* - One uppercase letter
* - One lowercase letter
* - One number
* - One special character
* - Passwords must match
*/
export const ResetPasswordScreen: React.FC<ResetPasswordScreenProps> = ({
navigation,
}) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
// Form input states
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [isPasswordVisible, setPasswordVisible] = useState(false);
const [isConfirmPasswordVisible, setConfirmPasswordVisible] = useState(false);
const [loading, setLoading] = useState(false);
// Redux state
const dispatch = useAppDispatch();
const user = useAppSelector((state) => state.auth.user);
// Password validation rules
const [passwordRules, setPasswordRules] = useState<PasswordRule[]>([
{
id: 'length',
label: 'At least 8 characters',
validator: (pwd: string) => pwd.length >= 8,
isValid: false,
},
{
id: 'uppercase',
label: 'One uppercase letter',
validator: (pwd: string) => /[A-Z]/.test(pwd),
isValid: false,
},
{
id: 'lowercase',
label: 'One lowercase letter',
validator: (pwd: string) => /[a-z]/.test(pwd),
isValid: false,
},
{
id: 'number',
label: 'One number',
validator: (pwd: string) => /\d/.test(pwd),
isValid: false,
},
{
id: 'special',
label: 'One special character',
validator: (pwd: string) => /[!@#$%^&*(),.?":{}|<>]/.test(pwd),
isValid: false,
},
{
id: 'match',
label: 'Passwords match',
validator: (pwd: string) => pwd.length > 0 && confirmPassword.length > 0 && pwd === confirmPassword,
isValid: false,
},
]);
// ============================================================================
// EFFECTS
// ============================================================================
/**
* useEffect for password validation
*
* Purpose: Update password rules when password or confirm password changes
*/
useEffect(() => {
updatePasswordRules(password);
}, [password, confirmPassword]);
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
/**
* validatePassword Function
*
* Purpose: Check if all password requirements are met
*
* @param pwd - Password to validate
* @returns boolean indicating if password meets all requirements
*/
const validatePassword = (pwd: string): boolean => {
return passwordRules.every(rule => rule.isValid);
};
/**
* updatePasswordRules Function
*
* Purpose: Update password validation rules based on current password
*
* @param pwd - Current password value
*/
const updatePasswordRules = (pwd: string) => {
setPasswordRules(prevRules =>
prevRules.map(rule => {
if (rule.id === 'match') {
return {
...rule,
isValid: pwd.length > 0 && confirmPassword.length > 0 && pwd === confirmPassword,
};
}
return {
...rule,
isValid: rule.validator(pwd),
};
})
);
};
// ============================================================================
// EVENT HANDLERS
// ============================================================================
/**
* handlePasswordChange Function
*
* Purpose: Handle password input changes
*
* @param pwd - New password value
*/
const handlePasswordChange = (pwd: string) => {
setPassword(pwd);
};
/**
* handleConfirmPasswordChange Function
*
* Purpose: Handle confirm password input changes
*
* @param pwd - New confirm password value
*/
const handleConfirmPasswordChange = (pwd: string) => {
setConfirmPassword(pwd);
};
/**
* handleReset Function
*
* Purpose: Handle password reset submission
*
* Flow:
* 1. Validate required fields
* 2. Validate password requirements
* 3. Check password match
* 4. Call API to change password
* 5. Update onboarding status on success
*/
const handleReset = async () => {
// Validate required fields
if (!password.trim() || !confirmPassword.trim()) {
Alert.alert('Validation Error', 'Both password fields are required.');
return;
}
// Validate password requirements
if (!validatePassword(password)) {
Alert.alert('Validation Error', 'Please meet all password requirements.');
return;
}
// Check password match
if (password !== confirmPassword) {
Alert.alert('Validation Error', 'Passwords do not match.');
return;
}
setLoading(true);
try {
// Call API to change password
const response: any = await authAPI.changepassword({
password,
token: user?.access_token,
});
console.log('reset response', response);
if (response.data && response.data.message) {
if (response.data.success) {
showSuccess(response.data.message);
// Update onboarding status
dispatch(updateOnboarded(true));
// Navigate to main app
// The app will automatically navigate to MainTabNavigator due to Redux state change
} else {
showError(response.data.message);
}
} else {
showError('Error while changing password');
}
} catch (error: any) {
console.error('Password reset error:', error);
showError('Failed to reset password. Please try again.');
} finally {
setLoading(false);
}
};
/**
* handleBack Function
*
* Purpose: Handle back navigation - logs out user since they can't go back to login
*/
const handleBack = () => {
Alert.alert(
'Sign Out',
'Are you sure you want to sign out? You will need to log in again.',
[
{
text: 'Cancel',
style: 'cancel',
},
{
text: 'Sign Out',
style: 'destructive',
onPress: () => {
// Dispatch logout action
dispatch(logout());
},
},
]
);
};
/**
* renderPasswordRule Function
*
* Purpose: Render individual password validation rule
*
* @param rule - Password rule to render
* @returns JSX element for the rule
*/
const renderPasswordRule = (rule: PasswordRule) => (
<View key={rule.id} style={styles.ruleContainer}>
<View style={[styles.checkbox, rule.isValid && styles.checkboxChecked]}>
{rule.isValid && (
<Icon name="check" size={12} color={theme.colors.background} />
)}
</View>
<Text style={[styles.ruleText, rule.isValid && styles.ruleTextValid]}>
{rule.label}
</Text>
</View>
);
// ============================================================================
// RENDER SECTION
// ============================================================================
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={styles.container}
>
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
showsVerticalScrollIndicator={false}
>
{/* Header */}
<View style={styles.header}>
<TouchableOpacity onPress={handleBack} style={styles.backButton}>
<Icon name="log-out" size={24} color={theme.colors.textPrimary} />
</TouchableOpacity>
<Text style={styles.headerTitle}>Set Your Password</Text>
<View style={styles.headerSpacer} />
</View>
{/* Icon */}
<View style={styles.iconContainer}>
<Icon name="lock" size={64} color={theme.colors.primary} />
</View>
{/* Title and Subtitle */}
<Text style={styles.title}>Set your password</Text>
<Text style={styles.subtitle}>
Create a strong password to complete your account setup
</Text>
{/* Password Input */}
<View style={styles.inputContainer}>
<Icon name="lock" size={20} color={theme.colors.textSecondary} style={styles.inputIcon} />
<TextInput
placeholder="Password"
value={password}
onChangeText={handlePasswordChange}
style={styles.inputField}
secureTextEntry={!isPasswordVisible}
placeholderTextColor={theme.colors.textMuted}
editable={!loading}
/>
<TouchableOpacity
onPress={() => setPasswordVisible(!isPasswordVisible)}
style={styles.eyeIcon}
disabled={loading}
>
<Icon
name={isPasswordVisible ? 'eye-off' : 'eye'}
size={22}
color={theme.colors.textSecondary}
/>
</TouchableOpacity>
</View>
{/* Confirm Password Input */}
<View style={styles.inputContainer}>
<Icon name="lock" size={20} color={theme.colors.textSecondary} style={styles.inputIcon} />
<TextInput
placeholder="Confirm Password"
value={confirmPassword}
onChangeText={handleConfirmPasswordChange}
style={styles.inputField}
secureTextEntry={!isConfirmPasswordVisible}
placeholderTextColor={theme.colors.textMuted}
editable={!loading}
/>
<TouchableOpacity
onPress={() => setConfirmPasswordVisible(!isConfirmPasswordVisible)}
style={styles.eyeIcon}
disabled={loading}
>
<Icon
name={isConfirmPasswordVisible ? 'eye-off' : 'eye'}
size={22}
color={theme.colors.textSecondary}
/>
</TouchableOpacity>
</View>
{/* Password Rules Section */}
<View style={styles.rulesContainer}>
<Text style={styles.rulesTitle}>Password Requirements:</Text>
<View style={styles.rulesGrid}>
{passwordRules.map(renderPasswordRule)}
</View>
</View>
{/* Reset Button */}
<TouchableOpacity
style={[
styles.button,
styles.resetButton,
loading && styles.buttonDisabled,
]}
onPress={handleReset}
disabled={loading}
>
{loading ? (
<View style={styles.loadingContainer}>
<Text style={styles.buttonText}>Setting Password...</Text>
</View>
) : (
<Text style={styles.buttonText}>Set Password</Text>
)}
</TouchableOpacity>
</ScrollView>
</KeyboardAvoidingView>
);
};
// ============================================================================
// STYLES SECTION
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
// Scroll view
scrollView: {
flex: 1,
},
// Scroll content
scrollContent: {
flexGrow: 1,
padding: theme.spacing.lg,
},
// Header section
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: theme.spacing.xl,
},
// Back button
backButton: {
padding: theme.spacing.sm,
borderRadius: theme.borderRadius.medium,
backgroundColor: theme.colors.backgroundAlt,
},
// Header title
headerTitle: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
// Header spacer
headerSpacer: {
width: 40,
},
// Icon container
iconContainer: {
alignItems: 'center',
marginBottom: theme.spacing.xl,
},
// Title
title: {
fontSize: theme.typography.fontSize.displayMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
textAlign: 'center',
marginBottom: theme.spacing.sm,
},
// Subtitle
subtitle: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
textAlign: 'center',
marginBottom: theme.spacing.xl,
},
// Input container
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: theme.colors.backgroundAlt,
borderWidth: 1,
borderColor: theme.colors.border,
borderRadius: theme.borderRadius.medium,
marginBottom: theme.spacing.md,
paddingHorizontal: theme.spacing.md,
paddingVertical: 2,
},
// Input icon
inputIcon: {
marginRight: theme.spacing.sm,
},
// Input field
inputField: {
flex: 1,
paddingVertical: theme.spacing.md,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textPrimary,
},
// Eye icon
eyeIcon: {
padding: theme.spacing.sm,
},
// Rules container
rulesContainer: {
marginTop: theme.spacing.sm,
marginBottom: theme.spacing.xl,
paddingHorizontal: theme.spacing.sm,
},
// Rules title
rulesTitle: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
},
// Rules grid
rulesGrid: {
gap: theme.spacing.xs,
},
// Rule container
ruleContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: theme.spacing.xs,
},
// Checkbox
checkbox: {
width: 18,
height: 18,
borderRadius: 4,
borderWidth: 2,
borderColor: theme.colors.border,
backgroundColor: theme.colors.backgroundAlt,
marginRight: theme.spacing.sm,
alignItems: 'center',
justifyContent: 'center',
},
// Checkbox checked
checkboxChecked: {
backgroundColor: theme.colors.primary,
borderColor: theme.colors.primary,
},
// Rule text
ruleText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
flex: 1,
},
// Rule text valid
ruleTextValid: {
color: theme.colors.success,
},
// Base button
button: {
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
borderRadius: theme.borderRadius.medium,
alignItems: 'center',
marginBottom: theme.spacing.md,
},
// Reset button
resetButton: {
backgroundColor: theme.colors.primary,
...theme.shadows.primary,
},
// Disabled button
buttonDisabled: {
opacity: 0.6,
},
// Button text
buttonText: {
color: theme.colors.background,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.medium,
},
// Loading container
loadingContainer: {
flexDirection: 'row',
alignItems: 'center',
},
});
export default ResetPasswordScreen;
/*
* End of File: ResetPasswordScreen.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,503 @@
/*
* File: SignUpScreen.tsx
* Description: Multi-step signup screen with validation and Redux integration
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useEffect, useState } from 'react';
import {
View,
StyleSheet,
StatusBar,
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';
import PasswordStep from '../components/signup/PasswordStep';
import NameStep from '../components/signup/NameStep';
import DocumentUploadStep from '../components/signup/DocumentUploadStep';
import HospitalSelectionStep from '../components/signup/HospitalSelectionStep';
import EmailAlreadyRegisteredModal from '../components/signup/EmailAlreadyRegisteredModal';
// Import API service
// Import hospital Redux functionality
import { fetchHospitals } from '../redux/hospitalSlice';
import { selectHospitalLoading, selectHospitals } from '../redux/hospitalSelectors';
// Import types
import { SignUpData, SignUpStep } from '../types/signup';
import { authAPI } from '../services/authAPI';
import { showError, showSuccess } from '../../../shared/utils/toast';
// ============================================================================
// INTERFACES
// ============================================================================
interface SignUpScreenProps {
navigation: any;
}
// ============================================================================
// SIGNUP SCREEN COMPONENT
// ============================================================================
/**
* SignUpScreen Component
*
* Purpose: Multi-step signup flow with validation and Redux integration
*
* Features:
* - Step-by-step signup process (email password name document hospital)
* - Real-time validation with visual feedback
* - Email and username availability checks
* - Hospital selection with search
* - Document upload with preview
* - Progress tracking with visual progress bar
* - Modern UI with icons and proper typography
* - Keyboard-aware layout for better UX
* - Redux state management
* - Loading states and error handling
*/
const SignUpScreen: React.FC<SignUpScreenProps> = ({ navigation }) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
const [currentStep, setCurrentStep] = useState<SignUpStep>('email');
const [showEmailRegisteredModal, setShowEmailRegisteredModal] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [signUpData, setSignUpData] = useState<Partial<SignUpData>>({
email: '',
password: '',
first_name: '',
last_name: '',
username: '',
id_photo_url: null,
hospital_id: '',
});
const dispatch = useAppDispatch();
// ============================================================================
// REDUX STATE
// ============================================================================
const hospitals = useAppSelector(selectHospitals);
const hospitalLoading = useAppSelector(selectHospitalLoading);
// ============================================================================
// STEP CONFIGURATION
// ============================================================================
const steps: SignUpStep[] = ['email', 'password', 'name', 'document', 'hospital'];
const currentStepIndex = steps.indexOf(currentStep);
// ============================================================================
// EFFECTS
// ============================================================================
useEffect(() => {
// Fetch hospitals on component mount
dispatch(fetchHospitals());
}, [dispatch]);
// ============================================================================
// STEP HANDLERS
// ============================================================================
/**
* Handle Email Step Continue
*
* Purpose: Validate email and proceed to next step
*/
const handleEmailContinue = async (email: string) => {
setIsLoading(true);
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
setShowEmailRegisteredModal(true)
}
if(response.status==200&&response.data.message){
setSignUpData(prev => ({ ...prev, email }));
setCurrentStep('password');
}
} catch (error) {
Alert.alert('Error', 'Failed to validate email. Please try again.');
} finally {
setIsLoading(false);
}
};
/**
* Handle Password Step Continue
*
* Purpose: Validate password and proceed to next step
*/
const handlePasswordContinue = (password: string) => {
setSignUpData(prev => ({ ...prev, password }));
setCurrentStep('name');
};
/**
* Handle Name Step Continue
*
* Purpose: Validate name and username, then proceed to next step
*/
const handleNameContinue = async (firstName: string, lastName: string, username: string) => {
setIsLoading(true);
try {
const response:any = await authAPI.validateusername(username);
console.log('response', response);
if(response.status==409&&response.data.message){
showError(response.data.message);
}
if(response.status==200&&response.data.message){
setSignUpData(prev => ({
...prev,
first_name: firstName,
last_name: lastName,
username: username
}));
setCurrentStep('document');
}
} catch (error) {
Alert.alert('Error', 'Failed to validate username. Please try again.');
} finally {
setIsLoading(false);
}
};
/**
* Handle Document Upload Step Continue
*
* Purpose: Save document and proceed to next step
*/
const handleDocumentContinue = (documentUri: string) => {
setSignUpData(prev => ({ ...prev, id_photo_url: documentUri }));
setCurrentStep('hospital');
};
/**
* Handle Hospital Selection Step Continue
*
* Purpose: Complete signup process
*/
const handleHospitalContinue = async (hospitalId: string) => {
const finalData: SignUpData = {
...signUpData,
hospital_id: hospitalId,
} as SignUpData;
setSignUpData(finalData);
// Call completion handler
await onSignUpComplete(finalData);
};
/**
* Complete Signup Process
*
* Purpose: Submit final signup data to API
*/
const onSignUpComplete = async (payload: SignUpData) => {
console.log('final payload', payload);
setIsLoading(true);
try {
const formData = new FormData();
let role = 'er_physician';
formData.append('email', payload.email);
formData.append('password', payload.password);
formData.append('first_name', payload.first_name);
formData.append('last_name', payload.last_name);
formData.append('username', payload.username);
formData.append('dashboard_role', role);
formData.append('hospital_id', payload.hospital_id);
// Attach file if exists
if (payload.id_photo_url) {
const filePath = payload.id_photo_url;
const file = {
uri: filePath,
name: 'id_photo',
type: 'image/jpg',
};
formData.append('id_photo_url', file as any);
}
console.log('payload prepared', formData);
const response :any = await authAPI.signup(formData);
console.log('signup response', response);
if(response.ok && response.data && response.data.success ) {
//@ts-ignore
showSuccess('Sign Up Successfully')
navigation.navigate('Login');
// dispatch(setHospitals(response.data.data))
} else {
showError('error while signup');
if( response.data && response.data.message ) {
//@ts-ignore
showError(response.data.message)
}
}
} catch (error: any) {
console.log('error', error);
Alert.alert('Error', 'Failed to create account. Please try again.');
} finally {
setIsLoading(false);
}
};
// ============================================================================
// NAVIGATION HANDLERS
// ============================================================================
/**
* Handle Back Navigation
*
* Purpose: Navigate to previous step or go back to previous screen
*/
const handleBack = () => {
if (currentStepIndex > 0) {
const previousStep = steps[currentStepIndex - 1];
setCurrentStep(previousStep);
} else {
navigation.goBack();
}
};
/**
* Handle Modal Close
*
* Purpose: Close email already registered modal
*/
const handleCloseModal = () => {
setShowEmailRegisteredModal(false);
};
/**
* Handle Go To Login
*
* Purpose: Navigate to login screen
*/
const handleGoToLogin = () => {
setShowEmailRegisteredModal(false);
navigation.navigate('Login');
};
// ============================================================================
// RENDER FUNCTIONS
// ============================================================================
/**
* Render Current Step
*
* Purpose: Render the appropriate step component based on current step
*/
const renderCurrentStep = () => {
console.log('signupdate', signUpData);
const commonProps = {
onBack: handleBack,
data: signUpData,
isLoading,
};
switch (currentStep) {
case 'email':
return (
<EmailStep
{...commonProps}
onContinue={handleEmailContinue}
/>
);
case 'password':
return (
<PasswordStep
{...commonProps}
onContinue={handlePasswordContinue}
/>
);
case 'name':
return (
<NameStep
{...commonProps}
onContinue={handleNameContinue}
/>
);
case 'document':
return (
<DocumentUploadStep
{...commonProps}
onContinue={handleDocumentContinue}
/>
);
case 'hospital':
return (
<HospitalSelectionStep
{...commonProps}
onContinue={handleHospitalContinue}
hospitals={hospitals}
hospitalLoading={hospitalLoading}
/>
);
default:
return (
<EmailStep
{...commonProps}
onContinue={handleEmailContinue}
/>
);
}
};
// ============================================================================
// RENDER
// ============================================================================
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<StatusBar
barStyle="dark-content"
backgroundColor={theme.colors.background}
/>
{/* Conditional Content Rendering */}
{currentStep === 'hospital' ? (
// For hospital step, render without ScrollView to avoid conflicts
<View style={styles.content}>
{renderCurrentStep()}
</View>
) : (
// For other steps, use ScrollView for proper scrolling
<ScrollView
style={styles.content}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.contentContainer}
>
{renderCurrentStep()}
</ScrollView>
)}
{/* Progress Bar - Bottom */}
<View style={styles.progressContainer}>
<View style={styles.progressBar}>
<View
style={[
styles.progressFill,
{ width: `${((currentStepIndex + 1) / steps.length) * 100}%` }
]}
/>
</View>
<Text style={styles.progressText}>
{Math.round(((currentStepIndex + 1) / steps.length) * 100)}% Complete
</Text>
</View>
<EmailAlreadyRegisteredModal
visible={showEmailRegisteredModal}
onClose={handleCloseModal}
onGoToLogin={handleGoToLogin}
/>
</KeyboardAvoidingView>
);
};
// ============================================================================
// STYLES
// ============================================================================
const styles = StyleSheet.create({
// Main container
container: {
flex: 1,
backgroundColor: theme.colors.background,
},
// Progress container
progressContainer: {
paddingHorizontal: theme.spacing.lg,
paddingVertical: theme.spacing.md,
backgroundColor: theme.colors.background,
borderTopWidth: 1,
borderTopColor: theme.colors.border,
},
// Progress bar
progressBar: {
height: 4,
backgroundColor: theme.colors.border,
borderRadius: theme.borderRadius.round,
marginBottom: theme.spacing.sm,
overflow: 'hidden',
},
// Progress fill
progressFill: {
height: '100%',
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.round,
},
// Progress text
progressText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
textAlign: 'center',
},
// Content area
content: {
flex: 1,
},
// Content container
contentContainer: {
flexGrow: 1,
padding: theme.spacing.lg,
paddingBottom: theme.spacing.xl,
},
});
export default SignUpScreen;
/*
* End of File: SignUpScreen.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,38 @@
/*
* File: authAPI.ts
* Description: API service for authentication using apisauce
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { create } from 'apisauce';
import { API_CONFIG } from '../../../shared/utils/constants';
import { buildHeaders } from '../../../shared/utils/api';
const api = create({
baseURL: API_CONFIG.BASE_URL, // TODO: Replace with actual endpoint
});
/**
* login - authenticates user with email and password
*/
export const authAPI = {
login: (email: string, password: string,platform:string) => api.post('/api/auth/auth/login', { email, password,platform },buildHeaders()),
//fetch hospital list
gethospitals: () => api.get('/api/hospitals/hospitals/app_user/hospitals', {},buildHeaders()),
//user signup
signup: (formData:any) => api.post('/api/auth/auth/admin/create-user-fromapp', formData,buildHeaders({ contentType: 'multipart/form-data' })),
//validate email
validatemail: (payload:{email:string}) => api.post('/api/auth/auth/check-email', payload,buildHeaders()),
//change password
changepassword: (payload:{password:string,token:string | undefined}) => api.post('/api/auth/onboarding/change-password', {password:payload.password},buildHeaders({token:payload.token})),
//validate username
validateusername: (username:string|undefined) => api.post('/api/auth/auth/check-username', {username},buildHeaders())
// Add more endpoints as needed
};
/*
* End of File: authAPI.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,23 @@
/*
* File: biometricService.ts
* Description: Service for biometric authentication (stub)
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export const biometricService = {
authenticate: async () => {
// TODO: Implement biometric authentication
return true;
},
isAvailable: async () => {
// TODO: Check if biometric is available
return true;
},
};
/*
* End of File: biometricService.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,15 @@
/*
* File: index.ts
* Description: Barrel export for Auth services
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export * from './';
export * from './biometricService';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,392 @@
/*
* 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<EmailValidationApiResponse> => {
try {
const response = await api.post<EmailValidationApiResponse>('/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<UsernameValidationApiResponse> => {
try {
const response = await api.post<UsernameValidationApiResponse>('/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<HospitalListApiResponse> => {
try {
const response = await api.get<HospitalListApiResponse>('/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<SignUpApiResponse> => {
try {
const response = await api.post<SignUpApiResponse>('/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<EmailValidationApiResponse> => {
// 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<UsernameValidationApiResponse> => {
// 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<HospitalListApiResponse> => {
// 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<SignUpApiResponse> => {
// 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.
*/

View File

@ -0,0 +1,256 @@
/*
* File: signup.ts
* Description: Type definitions for signup flow
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// ============================================================================
// SIGNUP STEP TYPES
// ============================================================================
/**
* SignUpStep Type
*
* Purpose: Define the different steps in the signup process
*/
export type SignUpStep = 'email' | 'password' | 'name' | 'document' | 'hospital';
// ============================================================================
// SIGNUP DATA INTERFACES
// ============================================================================
/**
* SignUpData Interface
*
* Purpose: Complete signup data structure matching reference code
*/
export interface SignUpData {
// Email and Password
email: string;
password: string;
// Personal Information
first_name: string;
last_name: string;
username: string;
// Document
id_photo_url: string | null;
// Hospital Information
hospital_id: string;
}
// ============================================================================
// STEP COMPONENT PROPS INTERFACES
// ============================================================================
/**
* Base Step Props Interface
*
* Purpose: Common props for all step components
*/
export interface BaseStepProps {
onBack: () => void;
data: Partial<SignUpData>;
isLoading: boolean;
}
/**
* Email Step Props Interface
*
* Purpose: Props for email step component
*/
export interface EmailStepProps extends BaseStepProps {
onContinue: (email: string) => void;
}
/**
* Password Step Props Interface
*
* Purpose: Props for password step component
*/
export interface PasswordStepProps extends BaseStepProps {
onContinue: (password: string) => void;
}
/**
* Name Step Props Interface
*
* Purpose: Props for name step component
*/
export interface NameStepProps extends BaseStepProps {
onContinue: (firstName: string, lastName: string, username: string) => void;
}
/**
* Document Upload Step Props Interface
*
* Purpose: Props for document upload step component
*/
export interface DocumentUploadStepProps extends BaseStepProps {
onContinue: (documentUri: string) => void;
}
/**
* Hospital Selection Step Props Interface
*
* Purpose: Props for hospital selection step component
*/
export interface HospitalSelectionStepProps extends BaseStepProps {
onContinue: (hospitalId: string) => void;
hospitals: Hospital[] | null;
hospitalLoading: boolean;
}
// ============================================================================
// VALIDATION INTERFACES
// ============================================================================
/**
* Validation Result Interface
*
* Purpose: Result of validation operations
*/
export interface ValidationResult {
isValid: boolean;
message?: string;
}
/**
* Email Validation Result Interface
*
* Purpose: Result of email validation
*/
export interface EmailValidationResult extends ValidationResult {
isAvailable: boolean;
suggestions?: string[];
}
/**
* Username Validation Result Interface
*
* Purpose: Result of username validation
*/
export interface UsernameValidationResult extends ValidationResult {
isAvailable: boolean;
suggestions?: string[];
}
// ============================================================================
// API RESPONSE INTERFACES
// ============================================================================
/**
* SignUp API Response Interface
*
* Purpose: Response from signup API
*/
export interface SignUpApiResponse {
success: boolean;
message: string;
data?: {
userId: string;
email: string;
token?: string;
};
errors?: {
[key: string]: string[];
};
}
/**
* Email Validation API Response Interface
*
* Purpose: Response from email validation API
*/
export interface EmailValidationApiResponse {
success: boolean;
isAvailable: boolean;
message: string;
suggestions?: string[];
}
/**
* Username Validation API Response Interface
*
* Purpose: Response from username validation API
*/
export interface UsernameValidationApiResponse {
success: boolean;
isAvailable: boolean;
message: string;
suggestions?: string[];
}
// ============================================================================
// HOSPITAL INTERFACES
// ============================================================================
/**
* Hospital Interface
*
* Purpose: Hospital information for selection
*/
export interface Hospital {
hospital_id: string | null;
hospital_name: string | null;
}
/**
* Hospital List API Response Interface
*
* Purpose: Response from hospital list API
*/
export interface HospitalListApiResponse {
success: boolean;
data: Hospital[];
total: number;
page: number;
limit: number;
}
// ============================================================================
// FORM VALIDATION INTERFACES
// ============================================================================
/**
* Form Validation Rules Interface
*
* Purpose: Validation rules for form fields
*/
export interface FormValidationRules {
required?: boolean;
minLength?: number;
maxLength?: number;
pattern?: RegExp;
custom?: (value: any) => boolean;
}
/**
* Form Field Validation Interface
*
* Purpose: Validation state for form fields
*/
export interface FormFieldValidation {
isValid: boolean;
message: string;
isDirty: boolean;
isTouched: boolean;
}
/**
* Form Validation State Interface
*
* Purpose: Overall form validation state
*/
export interface FormValidationState {
[key: string]: FormFieldValidation;
}
/*
* End of File: signup.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,188 @@
/*
* File: CriticalAlerts.tsx
* Description: Critical alerts component displaying emergency notifications
* 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';
import { Alert } from '../../../shared/types/alerts';
interface CriticalAlertsProps {
alerts: Alert[];
onAlertPress: (alert: Alert) => void;
}
export const CriticalAlerts: React.FC<CriticalAlertsProps> = ({
alerts,
onAlertPress,
}) => {
if (alerts.length === 0) {
return null;
}
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>🚨 Critical Alerts</Text>
<Text style={styles.count}>{alerts.length}</Text>
</View>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.scrollContainer}
>
{alerts.map((alert) => (
<TouchableOpacity
key={alert.id}
style={styles.alertCard}
onPress={() => onAlertPress(alert)}
activeOpacity={0.7}
>
<View style={styles.alertHeader}>
<Text style={styles.alertType}>{alert.type.replace('_', ' ')}</Text>
<Text style={styles.alertTime}>
{new Date(alert.timestamp).toLocaleTimeString()}
</Text>
</View>
<Text style={styles.alertTitle}>{alert.title}</Text>
<Text style={styles.alertMessage} numberOfLines={2}>
{alert.message}
</Text>
{alert.patientName && (
<View style={styles.patientInfo}>
<Text style={styles.patientName}>{alert.patientName}</Text>
{alert.bedNumber && (
<Text style={styles.bedNumber}>Bed {alert.bedNumber}</Text>
)}
</View>
)}
{alert.actionRequired && (
<View style={styles.actionRequired}>
<Text style={styles.actionText}>Action Required</Text>
</View>
)}
</TouchableOpacity>
))}
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: theme.colors.criticalBackground,
borderColor: theme.colors.critical,
borderWidth: 1,
borderRadius: theme.borderRadius.large,
padding: theme.spacing.md,
marginBottom: theme.spacing.lg,
...theme.shadows.critical,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: theme.spacing.md,
},
title: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.critical,
},
count: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.critical,
backgroundColor: theme.colors.background,
paddingHorizontal: theme.spacing.sm,
paddingVertical: theme.spacing.xs,
borderRadius: theme.borderRadius.small,
},
scrollContainer: {
paddingRight: theme.spacing.md,
},
alertCard: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md,
marginRight: theme.spacing.md,
minWidth: 280,
...theme.shadows.small,
},
alertHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: theme.spacing.sm,
},
alertType: {
fontSize: theme.typography.fontSize.bodySmall,
color: theme.colors.critical,
textTransform: 'uppercase',
},
alertTime: {
fontSize: theme.typography.fontSize.caption,
color: theme.colors.textMuted,
},
alertTitle: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.xs,
},
alertMessage: {
fontSize: theme.typography.fontSize.bodyMedium,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.sm,
lineHeight: theme.typography.lineHeight.normal * theme.typography.fontSize.bodyMedium,
},
patientInfo: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: theme.spacing.sm,
},
patientName: {
fontSize: theme.typography.fontSize.bodyMedium,
color: theme.colors.textPrimary,
},
bedNumber: {
fontSize: theme.typography.fontSize.bodySmall,
color: theme.colors.textSecondary,
backgroundColor: theme.colors.backgroundAccent,
paddingHorizontal: theme.spacing.sm,
paddingVertical: theme.spacing.xs,
borderRadius: theme.borderRadius.small,
},
actionRequired: {
backgroundColor: theme.colors.critical,
borderRadius: theme.borderRadius.small,
paddingHorizontal: theme.spacing.sm,
paddingVertical: theme.spacing.xs,
alignSelf: 'flex-start',
},
actionText: {
fontSize: theme.typography.fontSize.caption,
color: theme.colors.background,
textTransform: 'uppercase',
},
});
/*
* End of File: CriticalAlerts.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,120 @@
/*
* File: DashboardHeader.tsx
* Description: Dashboard header component displaying ER department overview and statistics
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import {
View,
Text,
StyleSheet,
} from 'react-native';
import { theme } from '../../../theme/theme';
import { ERDashboard } from '../../../shared/types/dashboard';
interface DashboardHeaderProps {
dashboard: ERDashboard;
}
export const DashboardHeader: React.FC<DashboardHeaderProps> = ({
dashboard,
}) => {
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Emergency Department</Text>
<Text style={styles.subtitle}>
{dashboard.shiftInfo.currentShift} Shift {dashboard.shiftInfo.attendingPhysician}
</Text>
</View>
<View style={styles.statsContainer}>
<View style={styles.statItem}>
<Text style={styles.statValue}>{dashboard.totalPatients}</Text>
<Text style={styles.statLabel}>Total Patients</Text>
</View>
<View style={styles.statItem}>
<Text style={[styles.statValue, styles.criticalValue]}>
{dashboard.criticalPatients}
</Text>
<Text style={styles.statLabel}>Critical</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statValue}>{dashboard.pendingScans}</Text>
<Text style={styles.statLabel}>Pending Scans</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statValue}>{dashboard.bedOccupancy}%</Text>
<Text style={styles.statLabel}>Bed Occupancy</Text>
</View>
</View>
<View style={styles.lastUpdated}>
<Text style={styles.lastUpdatedText}>
Last updated: {dashboard.lastUpdated.toLocaleTimeString()}
</Text>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.large,
padding: theme.spacing.lg,
marginBottom: theme.spacing.lg,
...theme.shadows.medium,
},
header: {
marginBottom: theme.spacing.lg,
},
title: {
fontSize: theme.typography.fontSize.displayMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.xs,
},
subtitle: {
fontSize: theme.typography.fontSize.bodyMedium,
color: theme.colors.textSecondary,
},
statsContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: theme.spacing.lg,
},
statItem: {
alignItems: 'center',
flex: 1,
},
statValue: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.primary,
marginBottom: theme.spacing.xs,
},
criticalValue: {
color: theme.colors.critical,
},
statLabel: {
fontSize: theme.typography.fontSize.bodySmall,
color: theme.colors.textSecondary,
textAlign: 'center',
},
lastUpdated: {
alignItems: 'center',
},
lastUpdatedText: {
fontSize: theme.typography.fontSize.caption,
color: theme.colors.textMuted,
},
});
/*
* End of File: DashboardHeader.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,102 @@
/*
* File: DepartmentStats.tsx
* Description: Department statistics component displaying patient counts per department
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import {
View,
Text,
StyleSheet,
} from 'react-native';
import { theme } from '../../../theme/theme';
import { DepartmentStats as DepartmentStatsType } from '../../../shared/types/dashboard';
interface DepartmentStatsProps {
stats: DepartmentStatsType;
}
export const DepartmentStats: React.FC<DepartmentStatsProps> = ({
stats,
}) => {
const departments = [
{ key: 'emergency', label: 'Emergency', color: theme.colors.primary },
{ key: 'trauma', label: 'Trauma', color: theme.colors.critical },
{ key: 'cardiac', label: 'Cardiac', color: theme.colors.warning },
{ key: 'neurology', label: 'Neurology', color: theme.colors.info },
{ key: 'pediatrics', label: 'Pediatrics', color: theme.colors.success },
{ key: 'icu', label: 'ICU', color: theme.colors.secondary },
];
return (
<View style={styles.container}>
<Text style={styles.title}>Department Overview</Text>
<View style={styles.statsGrid}>
{departments.map((dept) => (
<View key={dept.key} style={styles.statItem}>
<View style={[styles.colorIndicator, { backgroundColor: dept.color }]} />
<View style={styles.statContent}>
<Text style={styles.statValue}>
{stats[dept.key as keyof DepartmentStatsType]}
</Text>
<Text style={styles.statLabel}>{dept.label}</Text>
</View>
</View>
))}
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.large,
padding: theme.spacing.lg,
marginBottom: theme.spacing.lg,
...theme.shadows.medium,
},
title: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.lg,
},
statsGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'space-between',
},
statItem: {
flexDirection: 'row',
alignItems: 'center',
width: '48%',
marginBottom: theme.spacing.md,
},
colorIndicator: {
width: 12,
height: 12,
borderRadius: 6,
marginRight: theme.spacing.sm,
},
statContent: {
flex: 1,
},
statValue: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
statLabel: {
fontSize: theme.typography.fontSize.bodySmall,
color: theme.colors.textSecondary,
},
});
/*
* End of File: DepartmentStats.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,362 @@
/*
* 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<PatientCardProps> = ({
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 (
<TouchableOpacity
style={styles.container}
onPress={() => onPress(patient)} // Navigate to patient details
activeOpacity={0.7} // Visual feedback on touch
>
{/* ========================================================================
* HEADER SECTION - Patient identification and status badges
* ======================================================================== */}
<View style={styles.header}>
{/* Patient information section */}
<View style={styles.patientInfo}>
{/* Patient full name */}
<Text style={styles.patientName}>
{patient.firstName} {patient.lastName}
</Text>
{/* Patient demographics and MRN */}
<Text style={styles.patientDetails}>
{age} years {patient.gender} MRN: {patient.mrn}
</Text>
</View>
{/* Status badges section */}
<View style={styles.badges}>
{/* Priority badge with color coding */}
<View style={[styles.badge, { backgroundColor: priorityColor + '20' }]}>
<Text style={[styles.badgeText, { color: priorityColor }]}>
{patient.priority}
</Text>
</View>
{/* Status badge with color coding */}
<View style={[styles.badge, { backgroundColor: statusColor + '20' }]}>
<Text style={[styles.badgeText, { color: statusColor }]}>
{patient.status}
</Text>
</View>
</View>
</View>
{/* ========================================================================
* LOCATION SECTION - Bed assignment and attending physician
* ======================================================================== */}
<View style={styles.locationInfo}>
{/* Bed and room location */}
<Text style={styles.locationText}>
Bed {patient.bedNumber} Room {patient.roomNumber}
</Text>
{/* Attending physician */}
<Text style={styles.attendingText}>
Dr. {patient.attendingPhysician}
</Text>
</View>
{/* ========================================================================
* VITAL SIGNS SECTION - Real-time patient vitals
* ======================================================================== */}
<View style={styles.vitalSigns}>
{/* Blood Pressure */}
<View style={styles.vitalItem}>
<Text style={styles.vitalLabel}>BP</Text>
<Text style={styles.vitalValue}>{vitals.bp}</Text>
</View>
{/* Heart Rate */}
<View style={styles.vitalItem}>
<Text style={styles.vitalLabel}>HR</Text>
<Text style={styles.vitalValue}>{vitals.hr}</Text>
</View>
{/* Temperature */}
<View style={styles.vitalItem}>
<Text style={styles.vitalLabel}>Temp</Text>
<Text style={styles.vitalValue}>{vitals.temp}°C</Text>
</View>
{/* Oxygen Saturation */}
<View style={styles.vitalItem}>
<Text style={styles.vitalLabel}>O</Text>
<Text style={styles.vitalValue}>{vitals.o2}%</Text>
</View>
</View>
{/* ========================================================================
* ALLERGIES SECTION - Warning display for known allergies
* ======================================================================== */}
{patient.allergies.length > 0 && (
<View style={styles.allergiesContainer}>
<Text style={styles.allergiesLabel}>Allergies:</Text>
<Text style={styles.allergiesText}>
{patient.allergies.map(a => a.name).join(', ')}
</Text>
</View>
)}
{/* ========================================================================
* FOOTER SECTION - Diagnosis and last update time
* ======================================================================== */}
<View style={styles.footer}>
{/* Current diagnosis */}
<Text style={styles.diagnosisText}>
{patient.currentDiagnosis}
</Text>
{/* Last update timestamp */}
<Text style={styles.timeText}>
Updated {new Date(patient.lastUpdated).toLocaleTimeString()}
</Text>
</View>
</TouchableOpacity>
);
};
// ============================================================================
// 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.
*/

View File

@ -0,0 +1,139 @@
/*
* 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<QuickActionsProps> = ({
onQuickAction,
}) => {
return (
<View style={styles.container}>
<Text style={styles.title}>Quick Actions</Text>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.scrollContainer}
>
{quickActions.map((action) => (
<TouchableOpacity
key={action.id}
style={styles.actionButton}
onPress={() => onQuickAction(action)}
activeOpacity={0.7}
>
<View style={[styles.iconContainer, { backgroundColor: action.color + '20' }]}>
<Text style={styles.icon}>{action.icon}</Text>
</View>
<Text style={styles.actionTitle}>{action.title}</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
);
};
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.
*/

View File

@ -0,0 +1,91 @@
/*
* File: index.ts
* Description: Main exports for Dashboard module
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// Export screens
export { default as ERDashboardScreen } from './screens/ERDashboardScreen';
// Export navigation
export {
DashboardStackNavigator,
DashboardStackParamList,
DashboardNavigationProp,
DashboardScreenProps,
ERDashboardScreenProps,
PatientDetailsScreenProps,
AlertDetailsScreenProps,
DepartmentStatsScreenProps,
QuickActionsScreenProps,
navigateToERDashboard,
navigateToPatientDetails,
navigateToAlertDetails,
navigateToDepartmentStats,
navigateToQuickActions,
goBack,
resetToERDashboard,
replaceWithERDashboard,
navigateToERDashboardAndClearStack,
navigateToPatientDetailsAndClearStack,
navigateToAlertDetailsAndClearStack,
} 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 Redux
export {
fetchDashboardData,
refreshDashboardData,
clearError,
setFilter,
setSort,
updateConnectionStatus,
updateLastUpdated,
updateDashboardData,
} from './redux/dashboardSlice';
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
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,91 @@
/*
* File: DashboardStackNavigator.tsx
* Description: Stack navigator for dashboard screens within the Dashboard module
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
// Import dashboard screens
import { ERDashboardScreen } from '../screens/ERDashboardScreen';
// Import navigation types
import { DashboardStackParamList } from './navigationTypes';
import { theme } from '../../../theme';
// Create stack navigator for Dashboard module
const Stack = createStackNavigator<DashboardStackParamList>();
/**
* DashboardStackNavigator - Manages navigation between dashboard screens
*
* This navigator handles the flow between:
* - ERDashboardScreen: Main ER dashboard with patient overview
* - Future screens: Patient details, alerts, reports, etc.
*
* Features:
* - Clean header styling
* - Smooth transitions between screens
* - Type-safe navigation parameters
* - Healthcare-focused design
*/
const DashboardStackNavigator: React.FC = () => {
return (
<Stack.Navigator
initialRouteName="ERDashboard"
screenOptions={{
// Header styling for dashboard screens
headerStyle: {
backgroundColor: '#FFFFFF',
elevation: 0, // Remove shadow on Android
shadowOpacity: 0, // Remove shadow on iOS
},
headerTitleStyle: {
fontFamily: theme.typography.fontFamily.medium,
fontSize: 18,
color: '#212121',
},
headerTintColor: '#2196F3', // Back button and title color
headerBackTitleVisible: false, // Hide back title on iOS
cardStyle: {
backgroundColor: '#FFFFFF',
},
// Smooth transitions
transitionSpec: {
open: {
animation: 'timing',
config: {
duration: 300,
},
},
close: {
animation: 'timing',
config: {
duration: 300,
},
},
},
}}
>
{/* ER Dashboard Screen - Main dashboard entry point */}
<Stack.Screen
name="ERDashboard"
component={ERDashboardScreen}
options={{
title: 'ER Dashboard',
headerShown: false, // Hide header for main dashboard
}}
/>
</Stack.Navigator>
);
};
export default DashboardStackNavigator;
/*
* End of File: DashboardStackNavigator.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,47 @@
/*
* File: index.ts
* Description: Barrel exports for Dashboard module navigation
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// Export main navigator
export { default as DashboardStackNavigator } from './DashboardStackNavigator';
// Export navigation types
export type {
DashboardStackParamList,
DashboardNavigationProp,
DashboardScreenProps,
ERDashboardScreenProps,
PatientDetailsScreenProps,
AlertDetailsScreenProps,
DepartmentStatsScreenProps,
QuickActionsScreenProps,
ERDashboardScreenParams,
PatientDetailsScreenParams,
AlertDetailsScreenParams,
DepartmentStatsScreenParams,
QuickActionsScreenParams,
} from './navigationTypes';
// Export navigation utilities
export {
navigateToERDashboard,
navigateToPatientDetails,
navigateToAlertDetails,
navigateToDepartmentStats,
navigateToQuickActions,
goBack,
resetToERDashboard,
replaceWithERDashboard,
navigateToERDashboardAndClearStack,
navigateToPatientDetailsAndClearStack,
navigateToAlertDetailsAndClearStack,
} from './navigationUtils';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,171 @@
/*
* File: navigationTypes.ts
* Description: TypeScript types for Dashboard module navigation
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { StackNavigationProp } from '@react-navigation/stack';
import { Patient, Alert as AlertType, ERDashboard } from '../../../shared/types';
/**
* DashboardStackParamList - Defines the parameter list for Dashboard stack navigator
*
* This interface defines all the screens available in the Dashboard module
* and their associated navigation parameters.
*/
export type DashboardStackParamList = {
// ER Dashboard screen - Main dashboard with patient overview
ERDashboard: ERDashboardScreenParams;
// Patient Details screen - Detailed patient information
PatientDetails: PatientDetailsScreenParams;
// Alert Details screen - Detailed alert information
AlertDetails: AlertDetailsScreenParams;
// Department Stats screen - Department-specific statistics
DepartmentStats: DepartmentStatsScreenParams;
// Quick Actions screen - Quick action menu
QuickActions: QuickActionsScreenParams;
};
/**
* DashboardNavigationProp - Type for navigation prop in Dashboard screens
*
* This type provides type-safe navigation methods for screens
* within the Dashboard module.
*/
export type DashboardNavigationProp = StackNavigationProp<DashboardStackParamList>;
/**
* DashboardScreenProps - Base props interface for Dashboard screens
*
* This interface provides the common props that all Dashboard screens
* will receive, including navigation and route.
*/
export interface DashboardScreenProps<T extends keyof DashboardStackParamList> {
navigation: DashboardNavigationProp;
route: {
key: string;
name: T;
params: DashboardStackParamList[T];
};
}
// ============================================================================
// SCREEN PARAMETER TYPES
// ============================================================================
/**
* ERDashboardScreenParams
*
* Purpose: Parameters passed to the ER dashboard screen
*
* Parameters:
* - filter: Optional filter to apply to dashboard data
* - refresh: Optional flag to force refresh
*/
export interface ERDashboardScreenParams {
filter?: 'all' | 'critical' | 'active' | 'pending';
refresh?: boolean;
}
/**
* PatientDetailsScreenParams
*
* Purpose: Parameters for the patient details screen
*
* Parameters:
* - patientId: Required patient ID to display details
* - patient: Optional patient data to pre-populate
* - fromScreen: Optional source screen for back navigation
*/
export interface PatientDetailsScreenParams {
patientId: string;
patient?: Patient;
fromScreen?: keyof DashboardStackParamList;
}
/**
* AlertDetailsScreenParams
*
* Purpose: Parameters for the alert details screen
*
* Parameters:
* - alertId: Required alert ID to display details
* - alert: Optional alert data to pre-populate
* - fromScreen: Optional source screen for back navigation
*/
export interface AlertDetailsScreenParams {
alertId: string;
alert?: AlertType;
fromScreen?: keyof DashboardStackParamList;
}
/**
* DepartmentStatsScreenParams
*
* Purpose: Parameters for the department stats screen
*
* Parameters:
* - department: Required department name
* - dateRange: Optional date range for statistics
*/
export interface DepartmentStatsScreenParams {
department: string;
dateRange?: {
start: Date;
end: Date;
};
}
/**
* QuickActionsScreenParams
*
* Purpose: Parameters for the quick actions screen
*
* Parameters:
* - actionType: Optional action type to pre-select
* - patientId: Optional patient ID for patient-specific actions
*/
export interface QuickActionsScreenParams {
actionType?: 'emergency' | 'scan' | 'report' | 'consultation';
patientId?: string;
}
// ============================================================================
// SCREEN-SPECIFIC PROPS TYPES
// ============================================================================
/**
* ERDashboardScreenProps - Props for ERDashboardScreen component
*/
export type ERDashboardScreenProps = DashboardScreenProps<'ERDashboard'>;
/**
* PatientDetailsScreenProps - Props for PatientDetailsScreen component
*/
export type PatientDetailsScreenProps = DashboardScreenProps<'PatientDetails'>;
/**
* AlertDetailsScreenProps - Props for AlertDetailsScreen component
*/
export type AlertDetailsScreenProps = DashboardScreenProps<'AlertDetails'>;
/**
* DepartmentStatsScreenProps - Props for DepartmentStatsScreen component
*/
export type DepartmentStatsScreenProps = DashboardScreenProps<'DepartmentStats'>;
/**
* QuickActionsScreenProps - Props for QuickActionsScreen component
*/
export type QuickActionsScreenProps = DashboardScreenProps<'QuickActions'>;
/*
* End of File: navigationTypes.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,209 @@
/*
* File: navigationUtils.ts
* Description: Navigation utilities for Dashboard module
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { DashboardNavigationProp } from './navigationTypes';
import { Patient, Alert as AlertType } from '../../../shared/types';
/**
* DashboardNavigationUtils - Utility functions for Dashboard module navigation
*
* This module provides helper functions for common navigation patterns
* within the Dashboard module, ensuring consistent navigation behavior.
*/
/**
* Navigate to ER Dashboard screen
* @param navigation - Navigation prop from React Navigation
* @param params - Optional parameters for the dashboard
*/
export const navigateToERDashboard = (
navigation: DashboardNavigationProp,
params?: {
filter?: 'all' | 'critical' | 'active' | 'pending';
refresh?: boolean;
}
): void => {
navigation.navigate('ERDashboard', params);
};
/**
* Navigate to Patient Details screen
* @param navigation - Navigation prop from React Navigation
* @param patientId - Required patient ID
* @param patient - Optional patient data
* @param fromScreen - Optional source screen
*/
export const navigateToPatientDetails = (
navigation: DashboardNavigationProp,
patientId: string,
patient?: Patient,
fromScreen?: keyof import('./navigationTypes').DashboardStackParamList
): void => {
navigation.navigate('PatientDetails', {
patientId,
patient,
fromScreen,
});
};
/**
* Navigate to Alert Details screen
* @param navigation - Navigation prop from React Navigation
* @param alertId - Required alert ID
* @param alert - Optional alert data
* @param fromScreen - Optional source screen
*/
export const navigateToAlertDetails = (
navigation: DashboardNavigationProp,
alertId: string,
alert?: AlertType,
fromScreen?: keyof import('./navigationTypes').DashboardStackParamList
): void => {
navigation.navigate('AlertDetails', {
alertId,
alert,
fromScreen,
});
};
/**
* Navigate to Department Stats screen
* @param navigation - Navigation prop from React Navigation
* @param department - Required department name
* @param dateRange - Optional date range for statistics
*/
export const navigateToDepartmentStats = (
navigation: DashboardNavigationProp,
department: string,
dateRange?: {
start: Date;
end: Date;
}
): void => {
navigation.navigate('DepartmentStats', {
department,
dateRange,
});
};
/**
* Navigate to Quick Actions screen
* @param navigation - Navigation prop from React Navigation
* @param actionType - Optional action type to pre-select
* @param patientId - Optional patient ID for patient-specific actions
*/
export const navigateToQuickActions = (
navigation: DashboardNavigationProp,
actionType?: 'emergency' | 'scan' | 'report' | 'consultation',
patientId?: string
): void => {
navigation.navigate('QuickActions', {
actionType,
patientId,
});
};
/**
* Go back to previous screen
* @param navigation - Navigation prop from React Navigation
*/
export const goBack = (navigation: DashboardNavigationProp): void => {
if (navigation.canGoBack()) {
navigation.goBack();
}
};
/**
* Reset navigation stack to ER Dashboard screen
* @param navigation - Navigation prop from React Navigation
* @param params - Optional parameters for the dashboard
*/
export const resetToERDashboard = (
navigation: DashboardNavigationProp,
params?: {
filter?: 'all' | 'critical' | 'active' | 'pending';
refresh?: boolean;
}
): void => {
navigation.reset({
index: 0,
routes: [{ name: 'ERDashboard', params }],
});
};
/**
* Replace current screen with ER Dashboard screen
* @param navigation - Navigation prop from React Navigation
* @param params - Optional parameters for the dashboard
*/
export const replaceWithERDashboard = (
navigation: DashboardNavigationProp,
params?: {
filter?: 'all' | 'critical' | 'active' | 'pending';
refresh?: boolean;
}
): void => {
navigation.replace('ERDashboard', params);
};
/**
* Navigate to ER Dashboard and clear back stack
* @param navigation - Navigation prop from React Navigation
* @param params - Optional parameters for the dashboard
*/
export const navigateToERDashboardAndClearStack = (
navigation: DashboardNavigationProp,
params?: {
filter?: 'all' | 'critical' | 'active' | 'pending';
refresh?: boolean;
}
): void => {
navigation.reset({
index: 0,
routes: [{ name: 'ERDashboard', params }],
});
};
/**
* Navigate to Patient Details and clear back stack
* @param navigation - Navigation prop from React Navigation
* @param patientId - Required patient ID
* @param patient - Optional patient data
*/
export const navigateToPatientDetailsAndClearStack = (
navigation: DashboardNavigationProp,
patientId: string,
patient?: Patient
): void => {
navigation.reset({
index: 0,
routes: [{ name: 'PatientDetails', params: { patientId, patient } }],
});
};
/**
* Navigate to Alert Details and clear back stack
* @param navigation - Navigation prop from React Navigation
* @param alertId - Required alert ID
* @param alert - Optional alert data
*/
export const navigateToAlertDetailsAndClearStack = (
navigation: DashboardNavigationProp,
alertId: string,
alert?: AlertType
): void => {
navigation.reset({
index: 0,
routes: [{ name: 'AlertDetails', params: { alertId, alert } }],
});
};
/*
* End of File: navigationUtils.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,341 @@
/*
* 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<AlertType>) => {
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<string>) => {
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<AlertType> }>) => {
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.
*/

View File

@ -0,0 +1,280 @@
/*
* File: dashboardSlice.ts
* Description: Dashboard state management slice
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { ERDashboard, DashboardState } from '../../../shared/types';
// ============================================================================
// ASYNC THUNKS
// ============================================================================
/**
* Fetch Dashboard Data Async Thunk
*
* Purpose: Fetch dashboard data from API
*
* @returns Promise with dashboard data or error
*/
export const fetchDashboardData = createAsyncThunk(
'dashboard/fetchDashboardData',
async (_, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
// Simulate API call with timeout
await new Promise(resolve => setTimeout(resolve, 1500));
// Mock dashboard data
const mockDashboard: ERDashboard = {
totalPatients: 24,
criticalPatients: 3,
pendingScans: 8,
recentReports: 12,
bedOccupancy: 85,
departmentStats: {
emergency: 8,
trauma: 4,
cardiac: 3,
neurology: 2,
pediatrics: 5,
icu: 2,
},
shiftInfo: {
currentShift: 'DAY',
startTime: new Date(),
endTime: new Date(),
attendingPhysician: 'Dr. Smith',
residents: ['Dr. Johnson', 'Dr. Williams'],
nurses: ['Nurse Brown', 'Nurse Davis'],
},
lastUpdated: new Date(),
};
return mockDashboard;
} catch (error) {
return rejectWithValue('Failed to fetch dashboard data.');
}
}
);
/**
* Refresh Dashboard Data Async Thunk
*
* Purpose: Refresh dashboard data
*
* @returns Promise with updated dashboard data or error
*/
export const refreshDashboardData = createAsyncThunk(
'dashboard/refreshDashboardData',
async (_, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 1000));
// Mock refreshed dashboard data
const mockDashboard: ERDashboard = {
totalPatients: 26,
criticalPatients: 2,
pendingScans: 6,
recentReports: 15,
bedOccupancy: 87,
departmentStats: {
emergency: 9,
trauma: 3,
cardiac: 4,
neurology: 2,
pediatrics: 6,
icu: 2,
},
shiftInfo: {
currentShift: 'DAY',
startTime: new Date(),
endTime: new Date(),
attendingPhysician: 'Dr. Smith',
residents: ['Dr. Johnson', 'Dr. Williams'],
nurses: ['Nurse Brown', 'Nurse Davis'],
},
lastUpdated: new Date(),
};
return mockDashboard;
} catch (error) {
return rejectWithValue('Failed to refresh dashboard data.');
}
}
);
// ============================================================================
// INITIAL STATE
// ============================================================================
/**
* Initial Dashboard State
*
* Purpose: Define the initial state for dashboard
*
* Features:
* - Dashboard data and statistics
* - Loading states for async operations
* - Error handling and messages
* - Real-time updates tracking
*/
const initialState: DashboardState = {
// Dashboard data
dashboard: null,
// Loading states
isLoading: false,
isRefreshing: false,
// Error handling
error: null,
// Real-time updates
lastUpdated: null,
isConnected: true,
// Filters and preferences
selectedFilter: 'all',
sortBy: 'priority',
sortOrder: 'desc',
};
// ============================================================================
// DASHBOARD SLICE
// ============================================================================
/**
* Dashboard Slice
*
* Purpose: Redux slice for dashboard state management
*
* Features:
* - Dashboard data management
* - Real-time updates
* - Filtering and sorting
* - Error handling
* - Loading states
*/
const dashboardSlice = createSlice({
name: 'dashboard',
initialState,
reducers: {
/**
* Clear Error Action
*
* Purpose: Clear dashboard errors
*/
clearError: (state) => {
state.error = null;
},
/**
* Set Filter Action
*
* Purpose: Set dashboard filter
*/
setFilter: (state, action: PayloadAction<'all' | 'critical' | 'active' | 'pending'>) => {
state.selectedFilter = action.payload;
},
/**
* Set Sort Action
*
* Purpose: Set dashboard sort options
*/
setSort: (state, action: PayloadAction<{ by: string; order: 'asc' | 'desc' }>) => {
state.sortBy = action.payload.by;
state.sortOrder = action.payload.order;
},
/**
* Update Connection Status Action
*
* Purpose: Update real-time connection status
*/
updateConnectionStatus: (state, action: PayloadAction<boolean>) => {
state.isConnected = action.payload;
},
/**
* Update Last Updated Action
*
* Purpose: Update last updated timestamp
*/
updateLastUpdated: (state, action: PayloadAction<Date>) => {
state.lastUpdated = action.payload;
},
/**
* Update Dashboard Data Action
*
* Purpose: Update dashboard data manually
*/
updateDashboardData: (state, action: PayloadAction<Partial<ERDashboard>>) => {
if (state.dashboard) {
state.dashboard = { ...state.dashboard, ...action.payload };
state.lastUpdated = new Date();
}
},
},
extraReducers: (builder) => {
// Fetch Dashboard Data
builder
.addCase(fetchDashboardData.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(fetchDashboardData.fulfilled, (state, action) => {
state.isLoading = false;
state.dashboard = action.payload;
state.lastUpdated = action.payload.lastUpdated;
state.error = null;
})
.addCase(fetchDashboardData.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload as string;
});
// Refresh Dashboard Data
builder
.addCase(refreshDashboardData.pending, (state) => {
state.isRefreshing = true;
state.error = null;
})
.addCase(refreshDashboardData.fulfilled, (state, action) => {
state.isRefreshing = false;
state.dashboard = action.payload;
state.lastUpdated = action.payload.lastUpdated;
state.error = null;
})
.addCase(refreshDashboardData.rejected, (state, action) => {
state.isRefreshing = false;
state.error = action.payload as string;
});
},
});
// ============================================================================
// EXPORTS
// ============================================================================
export const {
clearError,
setFilter,
setSort,
updateConnectionStatus,
updateLastUpdated,
updateDashboardData,
} = dashboardSlice.actions;
export default dashboardSlice.reducer;
/*
* End of File: dashboardSlice.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,330 @@
/*
* 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<string>) => {
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<boolean>) => {
state.isRefreshing = action.payload;
},
/**
* Set Scrolling Action
*
* Purpose: Set scrolling state
*/
setScrolling: (state, action: PayloadAction<boolean>) => {
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.
*/

View File

@ -0,0 +1,752 @@
/*
* File: ERDashboardScreen.tsx
* Description: Main ER dashboard screen displaying patient list, critical alerts, and department statistics
* 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 { QuickActions } from '../components/QuickActions';
import { DepartmentStats } from '../components/DepartmentStats';
/**
* ERDashboardScreenProps Interface
*
* Purpose: Defines the props required by the ERDashboardScreen component
*
* Props:
* - navigation: React Navigation object for screen navigation
*/
interface ERDashboardScreenProps {
navigation: any;
}
/**
* ERDashboardScreen Component
*
* Purpose: Main dashboard screen for Emergency Department physicians
*
* Dashboard Features:
* 1. Real-time patient overview with filtering capabilities
* 2. Critical alerts display for immediate attention
* 3. Quick action buttons for common tasks
* 4. Department statistics and bed occupancy
* 5. Pull-to-refresh functionality for live updates
* 6. Internal data generation for demonstration
*
* Patient Filtering:
* - All: Shows all patients in the system
* - Critical: Shows only patients with critical priority
* - Active: Shows only patients with active status
*
* Data Flow:
* 1. Generate mock data internally for demonstration
* 2. Filter patients based on selected filter
* 3. Display critical alerts at the top
* 4. Show patient cards in scrollable list
* 5. Handle refresh and navigation interactions
*/
export const ERDashboardScreen: React.FC<ERDashboardScreenProps> = ({
navigation,
}) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
// Refresh state for pull-to-refresh functionality
const [refreshing, setRefreshing] = useState(false);
// Patient filter state to control which patients are displayed
const [selectedFilter, setSelectedFilter] = useState<'all' | 'critical' | 'active'>('all');
// Dashboard data state
const [dashboard, setDashboard] = useState<ERDashboard | null>(null);
const [patients, setPatients] = useState<Patient[]>([]);
const [alerts, setAlerts] = useState<AlertType[]>([]);
const [isLoading, setIsLoading] = useState(true);
// ============================================================================
// MOCK DATA GENERATION
// ============================================================================
/**
* generateMockDashboard Function
*
* Purpose: Generate mock dashboard data for demonstration
*
* Returns: ERDashboard object with realistic ER statistics
*/
const generateMockDashboard = (): ERDashboard => ({
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
});
/**
* generateMockPatients Function
*
* Purpose: Generate mock patient data for demonstration
*
* Returns: Array of Patient objects with realistic medical data
*/
const generateMockPatients = (): Patient[] => [
{
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(),
},
{
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: 'MEDIUM' as const,
department: 'Cardiac',
attendingPhysician: 'Dr. Williams',
allergies: [
{
id: '2',
name: 'Aspirin',
severity: 'MODERATE' as const,
reaction: 'Stomach upset'
},
],
medications: [
{
id: '2',
name: 'Nitroglycerin',
dosage: '0.4mg',
frequency: 'As needed',
route: 'Sublingual',
startDate: new Date(),
status: 'ACTIVE' as const,
prescribedBy: 'Dr. Williams',
},
],
vitalSigns: {
bloodPressure: { systolic: 160, diastolic: 100, 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: 'Hypertension, chest pain',
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: 'Neurology',
attendingPhysician: 'Dr. Davis',
allergies: [],
medications: [
{
id: '3',
name: 'Mannitol',
dosage: '100ml',
frequency: 'Every 6 hours',
route: 'IV',
startDate: new Date(),
status: 'ACTIVE' as const,
prescribedBy: 'Dr. Davis',
},
],
vitalSigns: {
bloodPressure: { systolic: 180, diastolic: 110, 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: 'Suspected intracranial hemorrhage',
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: 'Pediatrics',
attendingPhysician: 'Dr. Brown',
allergies: [
{
id: '3',
name: 'Latex',
severity: 'SEVERE' as const,
reaction: 'Contact dermatitis'
},
],
medications: [],
vitalSigns: {
bloodPressure: { systolic: 110, diastolic: 70, 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: 'Fever of unknown origin',
lastUpdated: new Date(),
},
];
/**
* generateMockAlerts Function
*
* Purpose: Generate mock alert data for demonstration
*
* Returns: Array of Alert objects with realistic medical alerts
*/
const generateMockAlerts = (): AlertType[] => [
{
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: '4', // Associated patient
patientName: 'Sarah Wilson',
bedNumber: 'D4',
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
},
{
id: '2',
type: 'VITAL_SIGNS_ALERT' as const,
priority: 'HIGH' as const,
title: 'Vital Sign Alert',
message: 'Blood pressure elevated: 180/110. Patient requires immediate attention.',
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: 'Medication Due',
message: 'Morphine due for patient John Doe in 15 minutes.',
patientId: '1',
patientName: 'John Doe',
bedNumber: 'A1',
timestamp: new Date(Date.now() - 600000), // 10 minutes ago
isRead: true,
isAcknowledged: true,
actionRequired: false,
},
];
// ============================================================================
// EFFECTS
// ============================================================================
/**
* useEffect for initial data loading
*
* Purpose: Load initial mock data when component mounts
*
* Flow:
* 1. Set loading state to true
* 2. Generate mock data
* 3. Set data in state
* 4. Set loading state to false
*/
useEffect(() => {
const loadInitialData = async () => {
setIsLoading(true);
// Simulate API call delay
await new Promise(resolve => setTimeout(resolve, 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
*
* Flow:
* 1. Set refreshing state to true (show loading indicator)
* 2. Simulate API call with delay
* 3. Update data with fresh mock data
* 4. Set refreshing state to false (hide loading indicator)
*/
const handleRefresh = async () => {
setRefreshing(true); // Show refresh indicator
// Simulate API call with 1-second delay
await new Promise(resolve => setTimeout(resolve, 1000));
// Update data with fresh mock data
setDashboard(generateMockDashboard());
setPatients(generateMockPatients());
setAlerts(generateMockAlerts());
setRefreshing(false); // Hide refresh indicator
};
/**
* handlePatientPress Function
*
* Purpose: Handle patient card press navigation
*
* @param patient - Patient object that was pressed
*/
const handlePatientPress = (patient: Patient) => {
// TODO: Navigate to patient details screen
console.log('Patient pressed:', patient.firstName, patient.lastName);
Alert.alert('Patient Details', `Navigate to ${patient.firstName} ${patient.lastName}'s details`);
};
/**
* handleAlertPress Function
*
* Purpose: Handle alert press interaction
*
* @param alert - Alert object that was pressed
*/
const handleAlertPress = (alert: AlertType) => {
// TODO: Navigate to alert details or patient details
console.log('Alert pressed:', alert.title);
Alert.alert('Alert Details', alert.message);
};
// ============================================================================
// DATA PROCESSING
// ============================================================================
/**
* filteredPatients - Computed property
*
* Purpose: Filter patients based on selected filter criteria
*
* Filter Options:
* - 'all': Show all patients regardless of status or priority
* - 'critical': Show only patients with CRITICAL priority
* - 'active': Show only patients with ACTIVE status
*/
const filteredPatients = patients.filter(patient => {
switch (selectedFilter) {
case 'critical':
return patient.priority === 'CRITICAL'; // Only critical priority patients
case 'active':
return patient.status === 'ACTIVE'; // Only active status patients
default:
return true; // All patients
}
});
/**
* criticalAlerts - Computed property
*
* Purpose: Extract critical alerts from all alerts for immediate display
*
* Filter: Only alerts with CRITICAL priority that require immediate attention
*/
const criticalAlerts = alerts.filter(alert => alert.priority === 'CRITICAL');
/**
* pendingScans - Computed property
*
* Purpose: Identify patients with pending scans or high priority needs
*
* Filter: Patients with PENDING status or HIGH priority
*/
const pendingScans = patients.filter(patient =>
patient.status === 'PENDING' || patient.priority === 'HIGH'
);
// ============================================================================
// 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 }) => (
<PatientCard
patient={item}
onPress={() => handlePatientPress(item)} // Navigate to patient details
/>
);
/**
* renderHeader Function
*
* Purpose: Render the dashboard header section with all dashboard components
*
* Components included:
* - DashboardHeader: Shows shift info and key statistics
* - CriticalAlerts: Displays urgent alerts requiring attention
* - QuickActions: Provides quick access to common functions
* - DepartmentStats: Shows department-wise patient distribution
* - Filter buttons: Allow filtering of patient list
*/
const renderHeader = () => (
<View style={styles.header}>
{/* Dashboard header with shift information and key metrics */}
{dashboard && <DashboardHeader dashboard={dashboard} />}
{/* Critical alerts section - only show if there are critical alerts */}
{criticalAlerts.length > 0 && (
<CriticalAlerts
alerts={criticalAlerts}
onAlertPress={handleAlertPress} // Handle alert interaction
/>
)}
{/* Quick action buttons for common tasks */}
<QuickActions
onQuickAction={(action) => {
// TODO: Implement quick action handlers
console.log('Quick action:', action);
}}
/>
{/* Department statistics showing patient distribution */}
{dashboard && <DepartmentStats stats={dashboard.departmentStats} />}
{/* Patient filter section with filter buttons */}
<View style={styles.filterContainer}>
<Text style={styles.sectionTitle}>Patients</Text>
<View style={styles.filterButtons}>
{/* All patients filter button */}
<TouchableOpacity
style={[
styles.filterButton,
selectedFilter === 'all' && styles.filterButtonActive
]}
onPress={() => setSelectedFilter('all')}
>
<Text style={[
styles.filterButtonText,
selectedFilter === 'all' && styles.filterButtonTextActive
]}>
All ({patients.length})
</Text>
</TouchableOpacity>
{/* Critical patients filter button */}
<TouchableOpacity
style={[
styles.filterButton,
selectedFilter === 'critical' && styles.filterButtonActive
]}
onPress={() => setSelectedFilter('critical')}
>
<Text style={[
styles.filterButtonText,
selectedFilter === 'critical' && styles.filterButtonTextActive
]}>
Critical ({patients.filter(p => p.priority === 'CRITICAL').length})
</Text>
</TouchableOpacity>
{/* Active patients filter button */}
<TouchableOpacity
style={[
styles.filterButton,
selectedFilter === 'active' && styles.filterButtonActive
]}
onPress={() => setSelectedFilter('active')}
>
<Text style={[
styles.filterButtonText,
selectedFilter === 'active' && styles.filterButtonTextActive
]}>
Active ({patients.filter(p => p.status === 'ACTIVE').length})
</Text>
</TouchableOpacity>
</View>
</View>
</View>
);
// ============================================================================
// LOADING STATE
// ============================================================================
/**
* Loading state render
*
* Purpose: Show loading indicator while data is being generated
*/
if (isLoading) {
return (
<View style={styles.loadingContainer}>
<Text style={styles.loadingText}>Loading Dashboard...</Text>
</View>
);
}
// ============================================================================
// MAIN RENDER
// ============================================================================
return (
<View style={styles.container}>
{/* FlatList for efficient rendering of patient cards */}
<FlatList
data={filteredPatients} // Filtered patient data
renderItem={renderPatientCard} // Render function for each patient
keyExtractor={(item) => item.id} // Unique key for each patient
ListHeaderComponent={renderHeader} // Header with dashboard components
contentContainerStyle={styles.listContainer} // Container styling
refreshControl={
<RefreshControl
refreshing={refreshing} // Show refresh indicator
onRefresh={handleRefresh} // Handle refresh action
colors={[theme.colors.primary]} // Refresh indicator color
tintColor={theme.colors.primary} // iOS refresh indicator color
/>
}
showsVerticalScrollIndicator={false} // Hide scroll indicator for cleaner look
/>
</View>
);
};
// ============================================================================
// 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, // Add bottom padding for tab bar
},
// Header section containing dashboard components
header: {
paddingHorizontal: theme.spacing.md, // Horizontal padding for content
},
// Container for patient filter section
filterContainer: {
marginTop: theme.spacing.lg, // Top margin for separation
marginBottom: theme.spacing.md, // Bottom margin for list spacing
},
// 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', // Horizontal layout
gap: theme.spacing.sm, // Space between buttons
},
// 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, // Primary color background
borderColor: theme.colors.primary, // Primary color border
},
// 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, // White text on primary background
},
});
/*
* End of File: ERDashboardScreen.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,475 @@
/*
* File: patientCareSlice.ts
* Description: Patient care state management slice
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { Patient, PatientCareState } from '../../../shared/types';
// ============================================================================
// ASYNC THUNKS
// ============================================================================
/**
* Fetch Patients Async Thunk
*
* Purpose: Fetch patients list from API
*
* @returns Promise with patients data or error
*/
export const fetchPatients = createAsyncThunk(
'patientCare/fetchPatients',
async (_, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 1500));
// Mock patients data
const mockPatients: Patient[] = [
{
id: '1',
mrn: 'MRN001',
firstName: 'John',
lastName: 'Doe',
dateOfBirth: new Date('1985-03-15'),
gender: 'MALE',
age: 38,
bedNumber: 'A1',
roomNumber: '101',
admissionDate: new Date('2024-01-15'),
status: 'ACTIVE',
priority: 'CRITICAL',
department: 'Emergency',
attendingPhysician: 'Dr. Smith',
allergies: [
{
id: '1',
name: 'Penicillin',
severity: 'SEVERE',
reaction: 'Anaphylaxis',
},
],
medications: [
{
id: '1',
name: 'Morphine',
dosage: '2mg',
frequency: 'Every 4 hours',
route: 'IV',
startDate: new Date(),
status: 'ACTIVE',
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',
lastUpdated: new Date(),
},
{
id: '2',
mrn: 'MRN002',
firstName: 'Jane',
lastName: 'Smith',
dateOfBirth: new Date('1990-07-22'),
gender: 'FEMALE',
age: 33,
bedNumber: 'B2',
roomNumber: '102',
admissionDate: new Date('2024-01-15'),
status: 'ACTIVE',
priority: 'HIGH',
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',
lastUpdated: new Date(),
},
];
return mockPatients;
} catch (error) {
return rejectWithValue('Failed to fetch patients.');
}
}
);
/**
* Fetch Patient Details Async Thunk
*
* Purpose: Fetch detailed patient information
*
* @param patientId - ID of the patient to fetch
* @returns Promise with patient details or error
*/
export const fetchPatientDetails = createAsyncThunk(
'patientCare/fetchPatientDetails',
async (patientId: string, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 1000));
// Mock patient details (same as above but with more detailed info)
const mockPatient: Patient = {
id: patientId,
mrn: `MRN${patientId.padStart(3, '0')}`,
firstName: 'John',
lastName: 'Doe',
dateOfBirth: new Date('1985-03-15'),
gender: 'MALE',
age: 38,
bedNumber: 'A1',
roomNumber: '101',
admissionDate: new Date('2024-01-15'),
status: 'ACTIVE',
priority: 'CRITICAL',
department: 'Emergency',
attendingPhysician: 'Dr. Smith',
allergies: [
{
id: '1',
name: 'Penicillin',
severity: 'SEVERE',
reaction: 'Anaphylaxis',
},
],
medications: [
{
id: '1',
name: 'Morphine',
dosage: '2mg',
frequency: 'Every 4 hours',
route: 'IV',
startDate: new Date(),
status: 'ACTIVE',
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',
lastUpdated: new Date(),
};
return mockPatient;
} catch (error) {
return rejectWithValue('Failed to fetch patient details.');
}
}
);
/**
* Update Patient Async Thunk
*
* Purpose: Update patient information
*
* @param patientData - Updated patient data
* @returns Promise with updated patient or error
*/
export const updatePatient = createAsyncThunk(
'patientCare/updatePatient',
async (patientData: Partial<Patient> & { id: string }, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 800));
return patientData;
} catch (error) {
return rejectWithValue('Failed to update patient.');
}
}
);
// ============================================================================
// INITIAL STATE
// ============================================================================
/**
* Initial Patient Care State
*
* Purpose: Define the initial state for patient care
*
* Features:
* - Patients list and management
* - Current patient details
* - Loading states for async operations
* - Error handling and messages
* - Search and filtering
*/
const initialState: PatientCareState = {
// Patients data
patients: [],
currentPatient: null,
// Loading states
isLoading: false,
isRefreshing: false,
isLoadingPatientDetails: false,
// Error handling
error: null,
// Search and filtering
searchQuery: '',
selectedFilter: 'all',
sortBy: 'priority',
sortOrder: 'desc',
// Pagination
currentPage: 1,
itemsPerPage: 20,
totalItems: 0,
// Cache
lastUpdated: null,
cacheExpiry: null,
};
// ============================================================================
// PATIENT CARE SLICE
// ============================================================================
/**
* Patient Care Slice
*
* Purpose: Redux slice for patient care state management
*
* Features:
* - Patient data management
* - Search and filtering
* - Pagination
* - Caching
* - Error handling
* - Loading states
*/
const patientCareSlice = createSlice({
name: 'patientCare',
initialState,
reducers: {
/**
* Clear Error Action
*
* Purpose: Clear patient care errors
*/
clearError: (state) => {
state.error = null;
},
/**
* Set Search Query Action
*
* Purpose: Set search query for patients
*/
setSearchQuery: (state, action: PayloadAction<string>) => {
state.searchQuery = action.payload;
state.currentPage = 1; // Reset to first page when searching
},
/**
* Set Filter Action
*
* Purpose: Set patient filter
*/
setFilter: (state, action: PayloadAction<'all' | 'active' | 'discharged' | 'critical'>) => {
state.selectedFilter = action.payload;
state.currentPage = 1; // Reset to first page when filtering
},
/**
* Set Sort Action
*
* Purpose: Set patient sort options
*/
setSort: (state, action: PayloadAction<{ by: string; 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<number>) => {
state.currentPage = action.payload;
},
/**
* Set Items Per Page Action
*
* Purpose: Set items per page for pagination
*/
setItemsPerPage: (state, action: PayloadAction<number>) => {
state.itemsPerPage = action.payload;
state.currentPage = 1; // Reset to first page when changing items per page
},
/**
* Set Current Patient Action
*
* Purpose: Set the currently selected patient
*/
setCurrentPatient: (state, action: PayloadAction<Patient | null>) => {
state.currentPatient = action.payload;
},
/**
* Update Patient in List Action
*
* Purpose: Update a patient in the patients list
*/
updatePatientInList: (state, action: PayloadAction<Patient>) => {
const index = state.patients.findIndex(patient => patient.id === action.payload.id);
if (index !== -1) {
state.patients[index] = action.payload;
}
// Update current patient if it's the same patient
if (state.currentPatient && state.currentPatient.id === action.payload.id) {
state.currentPatient = action.payload;
}
},
/**
* Add Patient Action
*
* Purpose: Add a new patient to the list
*/
addPatient: (state, action: PayloadAction<Patient>) => {
state.patients.unshift(action.payload);
state.totalItems += 1;
},
/**
* Remove Patient Action
*
* Purpose: Remove a patient from the list
*/
removePatient: (state, action: PayloadAction<string>) => {
const index = state.patients.findIndex(patient => patient.id === action.payload);
if (index !== -1) {
state.patients.splice(index, 1);
state.totalItems -= 1;
}
// Clear current patient if it's the same patient
if (state.currentPatient && state.currentPatient.id === action.payload) {
state.currentPatient = null;
}
},
/**
* Clear Cache Action
*
* Purpose: Clear patient data cache
*/
clearCache: (state) => {
state.patients = [];
state.currentPatient = null;
state.lastUpdated = null;
state.cacheExpiry = null;
},
},
extraReducers: (builder) => {
// Fetch Patients
builder
.addCase(fetchPatients.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(fetchPatients.fulfilled, (state, action) => {
state.isLoading = false;
state.patients = action.payload;
state.totalItems = action.payload.length;
state.lastUpdated = new Date();
state.cacheExpiry = new Date(Date.now() + 5 * 60 * 1000); // 5 minutes
state.error = null;
})
.addCase(fetchPatients.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload as string;
});
// Fetch Patient Details
builder
.addCase(fetchPatientDetails.pending, (state) => {
state.isLoadingPatientDetails = true;
state.error = null;
})
.addCase(fetchPatientDetails.fulfilled, (state, action) => {
state.isLoadingPatientDetails = false;
state.currentPatient = action.payload;
state.error = null;
})
.addCase(fetchPatientDetails.rejected, (state, action) => {
state.isLoadingPatientDetails = false;
state.error = action.payload as string;
});
// Update Patient
builder
.addCase(updatePatient.fulfilled, (state, action) => {
// Update patient in list
const index = state.patients.findIndex(patient => patient.id === action.payload.id);
if (index !== -1) {
state.patients[index] = { ...state.patients[index], ...action.payload };
}
// Update current patient if it's the same patient
if (state.currentPatient && state.currentPatient.id === action.payload.id) {
state.currentPatient = { ...state.currentPatient, ...action.payload };
}
})
.addCase(updatePatient.rejected, (state, action) => {
state.error = action.payload as string;
});
},
});
// ============================================================================
// EXPORTS
// ============================================================================
export const {
clearError,
setSearchQuery,
setFilter,
setSort,
setCurrentPage,
setItemsPerPage,
setCurrentPatient,
updatePatientInList,
addPatient,
removePatient,
clearCache,
} = patientCareSlice.actions;
export default patientCareSlice.reducer;
/*
* End of File: patientCareSlice.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,310 @@
/*
* File: ProfileCard.tsx
* Description: Profile card component displaying user information
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Image } from 'react-native';
import { theme } from '../../../theme/theme';
import { UserProfile } from '../../../shared/types';
/**
* ProfileCardProps Interface
*
* Purpose: Defines the props required by the ProfileCard component
*
* Props:
* - profile: User profile data to display
* - onPress: Callback function when card is pressed
*/
interface ProfileCardProps {
profile: UserProfile;
onPress: () => void;
}
/**
* ProfileCard Component
*
* Purpose: Displays user profile information in a card format
*
* Features:
* - Profile picture display
* - User name and credentials
* - Department and role information
* - Contact information
* - Interactive card design
*/
export const ProfileCard: React.FC<ProfileCardProps> = ({ profile, onPress }) => {
/**
* formatCredentials Function
*
* Purpose: Format credentials array into a readable string
*
* @returns Formatted credentials string
*/
const formatCredentials = (): string => {
return profile.credentials.join(', ');
};
/**
* formatSpecialties Function
*
* Purpose: Format specialties array into a readable string
*
* @returns Formatted specialties string
*/
const formatSpecialties = (): string => {
return profile.specialties.slice(0, 2).join(', ');
};
return (
<TouchableOpacity style={styles.container} onPress={onPress} activeOpacity={0.7}>
{/* Profile picture and basic info */}
<View style={styles.header}>
<View style={styles.imageContainer}>
{profile.profilePicture ? (
<Image
source={require('../../../assets/images/default-avatar.png')}
style={styles.profileImage}
resizeMode="cover"
onError={(error) => {
console.log('Profile image loading error:', error.nativeEvent.error);
}}
/>
) : (
<View style={styles.fallbackAvatar}>
<Text style={styles.fallbackText}>
{profile.firstName.charAt(0)}{profile.lastName.charAt(0)}
</Text>
</View>
)}
</View>
<View style={styles.basicInfo}>
<Text style={styles.name}>
{profile.firstName} {profile.lastName}
</Text>
<Text style={styles.credentials}>{formatCredentials()}</Text>
<Text style={styles.specialties}>{formatSpecialties()}</Text>
</View>
<View style={styles.editIcon}>
<Text style={styles.editText}>Edit</Text>
</View>
</View>
{/* Department and role information */}
<View style={styles.infoSection}>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>Department:</Text>
<Text style={styles.infoValue}>{profile.department}</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>Role:</Text>
<Text style={styles.infoValue}>
{profile.role.replace('_', ' ').toLowerCase().replace(/\b\w/g, l => l.toUpperCase())}
</Text>
</View>
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>Experience:</Text>
<Text style={styles.infoValue}>{profile.yearsOfExperience} years</Text>
</View>
</View>
{/* Contact information */}
<View style={styles.contactSection}>
<View style={styles.contactRow}>
<Text style={styles.contactLabel}>Email:</Text>
<Text style={styles.contactValue} numberOfLines={1}>
{profile.email}
</Text>
</View>
<View style={styles.contactRow}>
<Text style={styles.contactLabel}>Phone:</Text>
<Text style={styles.contactValue}>{profile.phoneNumber}</Text>
</View>
</View>
</TouchableOpacity>
);
};
// ============================================================================
// STYLES SECTION
// ============================================================================
const styles = StyleSheet.create({
// Main container for the profile card
container: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.large,
padding: theme.spacing.lg,
marginVertical: theme.spacing.md,
shadowColor: '#000000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
borderColor: theme.colors.border,
borderWidth: 1,
},
// Header section with profile picture and basic info
header: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: theme.spacing.md,
},
// Profile image container
imageContainer: {
marginRight: theme.spacing.md,
},
// Profile image styling
profileImage: {
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: theme.colors.backgroundAlt,
borderWidth: 2,
borderColor: theme.colors.border,
},
// Basic information section
basicInfo: {
flex: 1,
},
// User name styling
name: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.xs,
},
// Credentials styling
credentials: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.primary,
marginBottom: theme.spacing.xs,
},
// Specialties styling
specialties: {
fontSize: theme.typography.fontSize.bodySmall,
color: theme.colors.textSecondary,
},
// Edit icon/text styling
editIcon: {
paddingHorizontal: theme.spacing.sm,
paddingVertical: theme.spacing.xs,
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.small,
},
// Edit text styling
editText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.background,
},
// Information section styling
infoSection: {
marginBottom: theme.spacing.md,
paddingTop: theme.spacing.md,
borderTopColor: theme.colors.border,
borderTopWidth: 1,
},
// Information row styling
infoRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: theme.spacing.sm,
},
// Information label styling
infoLabel: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
},
// Information value styling
infoValue: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
textAlign: 'right',
flex: 1,
},
// Contact section styling
contactSection: {
paddingTop: theme.spacing.md,
borderTopColor: theme.colors.border,
borderTopWidth: 1,
},
// Contact row styling
contactRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: theme.spacing.sm,
},
// Contact label styling
contactLabel: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
},
// Contact value styling
contactValue: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
textAlign: 'right',
flex: 1,
},
// Fallback avatar styling
fallbackAvatar: {
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: theme.colors.primary,
justifyContent: 'center',
alignItems: 'center',
borderWidth: 2,
borderColor: theme.colors.border,
},
// Fallback text styling
fallbackText: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.background,
textAlign: 'center',
},
});
/*
* End of File: ProfileCard.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,68 @@
/*
* File: SettingsHeader.tsx
* Description: Header component for the settings screen
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { theme } from '../../../theme/theme';
/**
* SettingsHeaderProps Interface
*
* Purpose: Defines the props required by the SettingsHeader component
*
* Props:
* - title: Title text to display in the header
*/
interface SettingsHeaderProps {
title: string;
}
/**
* SettingsHeader Component
*
* Purpose: Displays the header for the settings screen
*
* Features:
* - Clean, minimal header design
* - Consistent with app theme
* - Proper spacing and typography
*/
export const SettingsHeader: React.FC<SettingsHeaderProps> = ({ title }) => {
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
</View>
);
};
// ============================================================================
// STYLES SECTION
// ============================================================================
const styles = StyleSheet.create({
// Main container for the header
container: {
backgroundColor: theme.colors.background,
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.lg,
borderBottomColor: theme.colors.border,
borderBottomWidth: 1,
},
// Title text styling
title: {
fontSize: theme.typography.fontSize.displayMedium,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
},
});
/*
* End of File: SettingsHeader.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,299 @@
/*
* File: SettingsItemComponent.tsx
* Description: Settings item component displaying individual settings options
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Switch } from 'react-native';
import { theme } from '../../../theme/theme';
import { SettingsItem } from '../../../shared/types';
import Icon from 'react-native-vector-icons/Feather';
/**
* SettingsItemComponentProps Interface
*
* Purpose: Defines the props required by the SettingsItemComponent
*
* Props:
* - item: Settings item data to display
* - isLast: Boolean indicating if this is the last item in the section
*/
interface SettingsItemComponentProps {
item: SettingsItem;
isLast: boolean;
}
/**
* SettingsItemComponent Component
*
* Purpose: Displays individual settings items with different interaction types
*
* Features:
* - Navigation items with chevron
* - Toggle items with switch
* - Action items with custom styling
* - Icon support (placeholder)
* - Consistent styling and spacing
*/
export const SettingsItemComponent: React.FC<SettingsItemComponentProps> = ({ item, isLast }) => {
/**
* renderIcon Function
*
* Purpose: Render icon for the settings item
*
* @returns Icon component or placeholder
*/
const renderIcon = () => {
// TODO: Implement actual icon rendering
return (
<View style={styles.iconPlaceholder}>
<Icon name={item.icon} size={24} color={theme.colors.textPrimary} />
</View>
);
};
/**
* renderValue Function
*
* Purpose: Render the value/action component based on item type
*
* @returns Value component (switch, chevron, or custom)
*/
const renderValue = () => {
switch (item.type) {
case 'TOGGLE':
return (
<Switch
value={item.value || false}
onValueChange={item.onPress}
trackColor={{ false: theme.colors.border, true: theme.colors.primary }}
thumbColor={theme.colors.background}
ios_backgroundColor={theme.colors.border}
/>
);
case 'NAVIGATION':
return (
<Text style={styles.chevron}></Text>
);
case 'ACTION':
return (
<Text style={styles.actionText}>Sign Out</Text>
);
default:
return null;
}
};
/**
* getItemStyle Function
*
* Purpose: Get appropriate styling based on item type and position
*
* @returns Style object for the item container
*/
const getItemStyle = () => {
const baseStyle = [styles.container];
// Add border bottom if not the last item
if (!isLast) {
baseStyle.push(styles.withBorder);
}
// Add special styling for action items
if (item.type === 'ACTION') {
baseStyle.push(styles.actionItem);
}
// Add disabled styling if item is disabled
if (item.disabled) {
baseStyle.push(styles.disabledItem);
}
return baseStyle;
};
/**
* getTextStyle Function
*
* Purpose: Get appropriate text styling based on item state
*
* @returns Style object for text
*/
const getTextStyle = () => {
if (item.disabled) {
return [styles.title, styles.disabledText];
}
if (item.type === 'ACTION') {
return [styles.title, styles.actionTitle];
}
return styles.title;
};
/**
* getSubtitleStyle Function
*
* Purpose: Get appropriate subtitle styling based on item state
*
* @returns Style object for subtitle
*/
const getSubtitleStyle = () => {
if (item.disabled) {
return [styles.subtitle, styles.disabledText];
}
return styles.subtitle;
};
return (
<TouchableOpacity
style={getItemStyle()}
onPress={item.onPress}
disabled={item.disabled}
activeOpacity={0.7}
>
{/* Icon */}
{renderIcon()}
{/* Content */}
<View style={styles.content}>
<View style={styles.textContainer}>
<Text style={getTextStyle()}>{item.title}</Text>
{item.subtitle && (
<Text style={getSubtitleStyle()}>{item.subtitle}</Text>
)}
</View>
{/* Badge */}
{item.badge && (
<View style={styles.badge}>
<Text style={styles.badgeText}>{item.badge}</Text>
</View>
)}
</View>
{/* Value/Action */}
{renderValue()}
</TouchableOpacity>
);
};
// ============================================================================
// STYLES SECTION
// ============================================================================
const styles = StyleSheet.create({
// Main container for the settings item
container: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: theme.spacing.md,
paddingVertical: theme.spacing.md,
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.medium,
},
// Container with bottom border
withBorder: {
borderBottomColor: theme.colors.border,
borderBottomWidth: 1,
},
// Action item styling
actionItem: {
backgroundColor: theme.colors.background,
},
// Disabled item styling
disabledItem: {
opacity: 0.5,
},
// Icon placeholder styling
iconPlaceholder: {
width: 24,
height: 24,
marginRight: theme.spacing.md,
justifyContent: 'center',
alignItems: 'center',
},
// Icon text styling
iconText: {
fontSize: 16,
},
// Content container
content: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
// Text container
textContainer: {
flex: 1,
},
// Title text styling
title: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.xs,
},
// Action title styling
actionTitle: {
color: theme.colors.error,
},
// Subtitle text styling
subtitle: {
fontSize: theme.typography.fontSize.bodySmall,
color: theme.colors.textSecondary,
},
// Disabled text styling
disabledText: {
color: theme.colors.textMuted,
},
// Badge styling
badge: {
backgroundColor: theme.colors.primary,
borderRadius: theme.borderRadius.small,
paddingHorizontal: theme.spacing.sm,
paddingVertical: theme.spacing.xs,
marginLeft: theme.spacing.sm,
},
// Badge text styling
badgeText: {
fontSize: theme.typography.fontSize.caption,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.background,
},
// Chevron styling for navigation items
chevron: {
fontSize: theme.typography.fontSize.bodyLarge,
color: theme.colors.textMuted,
fontFamily: theme.typography.fontFamily.bold,
},
// Action text styling
actionText: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.error,
},
});
/*
* End of File: SettingsItemComponent.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,90 @@
/*
* File: SettingsSectionComponent.tsx
* Description: Settings section component displaying grouped settings items
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { theme } from '../../../theme/theme';
import { SettingsSectionData } from '../../../shared/types';
import { SettingsItemComponent } from './SettingsItemComponent';
/**
* SettingsSectionComponentProps Interface
*
* Purpose: Defines the props required by the SettingsSectionComponent
*
* Props:
* - section: Settings section data to display
*/
interface SettingsSectionComponentProps {
section: SettingsSectionData;
}
/**
* SettingsSectionComponent Component
*
* Purpose: Displays a settings section with grouped items
*
* Features:
* - Section title display
* - Grouped settings items
* - Consistent styling and spacing
* - Clean visual separation between sections
*/
export const SettingsSectionComponent: React.FC<SettingsSectionComponentProps> = ({ section }) => {
return (
<View style={styles.container}>
{/* Section title */}
<Text style={styles.sectionTitle}>{section.title}</Text>
{/* Settings items */}
<View style={styles.itemsContainer}>
{section.items.map((item, index) => (
<SettingsItemComponent
key={item.id}
item={item}
isLast={index === section.items.length - 1}
/>
))}
</View>
</View>
);
};
// ============================================================================
// STYLES SECTION
// ============================================================================
const styles = StyleSheet.create({
// Main container for the settings section
container: {
marginBottom: theme.spacing.lg,
},
// Section title styling
sectionTitle: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.sm,
paddingHorizontal: theme.spacing.sm,
},
// Container for settings items
itemsContainer: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.medium,
borderColor: theme.colors.border,
borderWidth: 1,
...theme.shadows.primary,
},
});
/*
* End of File: SettingsSectionComponent.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,80 @@
/*
* File: index.ts
* Description: Settings module exports
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// ============================================================================
// SCREENS
// ============================================================================
export { default as SettingsScreen } from './screens/SettingsScreen';
// ============================================================================
// NAVIGATION
// ============================================================================
export {
SettingsStackNavigator,
SettingsStackParamList,
SettingsNavigationProp,
SettingsScreenProps,
ProfileEditScreenProps,
SecuritySettingsScreenProps,
NotificationSettingsScreenProps,
ClinicalPreferencesScreenProps,
PrivacySettingsScreenProps,
AccessibilitySettingsScreenProps,
AboutScreenProps,
HelpSupportScreenProps,
navigateToSettings,
navigateToProfileEdit,
navigateToSecuritySettings,
navigateToNotificationSettings,
navigateToClinicalPreferences,
navigateToPrivacySettings,
navigateToAccessibilitySettings,
navigateToAbout,
navigateToHelpSupport,
goBack,
resetToSettings,
replaceWithSettings,
navigateToSettingsAndClearStack,
navigateToProfileEditAndClearStack,
navigateToSecuritySettingsAndClearStack,
} from './navigation';
// ============================================================================
// COMPONENTS
// ============================================================================
export { default as SettingsHeader } from './components/SettingsHeader';
export { default as ProfileCard } from './components/ProfileCard';
export { default as SettingsSectionComponent } from './components/SettingsSectionComponent';
export { default as SettingsItemComponent } from './components/SettingsItemComponent';
// ============================================================================
// REDUX
// ============================================================================
export {
fetchUserProfile,
updateUserProfile,
fetchUserPreferences,
updateUserPreferences,
clearError,
setProfileValidation,
setPreferencesValidation,
updateProfileField,
updatePreferenceField,
resetProfile,
resetPreferences,
clearSettingsCache,
} from './redux/settingsSlice';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,91 @@
/*
* File: SettingsStackNavigator.tsx
* Description: Stack navigator for settings screens within the Settings module
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
// Import settings screens
import { SettingsScreen } from '../screens/SettingsScreen';
// Import navigation types
import { SettingsStackParamList } from './navigationTypes';
import { theme } from '../../../theme';
// Create stack navigator for Settings module
const Stack = createStackNavigator<SettingsStackParamList>();
/**
* SettingsStackNavigator - Manages navigation between settings screens
*
* This navigator handles the flow between:
* - SettingsScreen: Main settings screen with profile and preferences
* - Future screens: Profile edit, security settings, notifications, etc.
*
* Features:
* - Clean header styling
* - Smooth transitions between screens
* - Type-safe navigation parameters
* - Settings-focused design
*/
const SettingsStackNavigator: React.FC = () => {
return (
<Stack.Navigator
initialRouteName="Settings"
screenOptions={{
// Header styling for settings screens
headerStyle: {
backgroundColor: '#FFFFFF',
elevation: 0, // Remove shadow on Android
shadowOpacity: 0, // Remove shadow on iOS
},
headerTitleStyle: {
fontFamily: theme.typography.fontFamily.medium,
fontSize: 18,
color: '#212121',
},
headerTintColor: '#2196F3', // Back button and title color
headerBackTitleVisible: false, // Hide back title on iOS
cardStyle: {
backgroundColor: '#FFFFFF',
},
// Smooth transitions
transitionSpec: {
open: {
animation: 'timing',
config: {
duration: 300,
},
},
close: {
animation: 'timing',
config: {
duration: 300,
},
},
},
}}
>
{/* Settings Screen - Main settings entry point */}
<Stack.Screen
name="Settings"
component={SettingsScreen}
options={{
title: 'Settings',
headerShown: false, // Hide header for main settings screen
}}
/>
</Stack.Navigator>
);
};
export default SettingsStackNavigator;
/*
* End of File: SettingsStackNavigator.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,59 @@
/*
* File: index.ts
* Description: Barrel exports for Settings module navigation
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// Export main navigator
export { default as SettingsStackNavigator } from './SettingsStackNavigator';
// Export navigation types
export type {
SettingsStackParamList,
SettingsNavigationProp,
SettingsScreenProps,
SettingsScreenProps as MainSettingsScreenProps,
ProfileEditScreenProps,
SecuritySettingsScreenProps,
NotificationSettingsScreenProps,
ClinicalPreferencesScreenProps,
PrivacySettingsScreenProps,
AccessibilitySettingsScreenProps,
AboutScreenProps,
HelpSupportScreenProps,
SettingsScreenParams,
ProfileEditScreenParams,
SecuritySettingsScreenParams,
NotificationSettingsScreenParams,
ClinicalPreferencesScreenParams,
PrivacySettingsScreenParams,
AccessibilitySettingsScreenParams,
AboutScreenParams,
HelpSupportScreenParams,
} from './navigationTypes';
// Export navigation utilities
export {
navigateToSettings,
navigateToProfileEdit,
navigateToSecuritySettings,
navigateToNotificationSettings,
navigateToClinicalPreferences,
navigateToPrivacySettings,
navigateToAccessibilitySettings,
navigateToAbout,
navigateToHelpSupport,
goBack,
resetToSettings,
replaceWithSettings,
navigateToSettingsAndClearStack,
navigateToProfileEditAndClearStack,
navigateToSecuritySettingsAndClearStack,
} from './navigationUtils';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,252 @@
/*
* File: navigationTypes.ts
* Description: TypeScript types for Settings module navigation
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { StackNavigationProp } from '@react-navigation/stack';
import { UserProfile, UserPreferences } from '../../../shared/types';
/**
* SettingsStackParamList - Defines the parameter list for Settings stack navigator
*
* This interface defines all the screens available in the Settings module
* and their associated navigation parameters.
*/
export type SettingsStackParamList = {
// Settings screen - Main settings with profile and preferences
Settings: SettingsScreenParams;
// Profile Edit screen - Edit user profile information
ProfileEdit: ProfileEditScreenParams;
// Security Settings screen - Security and privacy settings
SecuritySettings: SecuritySettingsScreenParams;
// Notification Settings screen - Notification preferences
NotificationSettings: NotificationSettingsScreenParams;
// Clinical Preferences screen - Clinical workflow preferences
ClinicalPreferences: ClinicalPreferencesScreenParams;
// Privacy Settings screen - Privacy and data settings
PrivacySettings: PrivacySettingsScreenParams;
// Accessibility Settings screen - Accessibility preferences
AccessibilitySettings: AccessibilitySettingsScreenParams;
// About screen - App information and version
About: AboutScreenParams;
// Help & Support screen - Help documentation and support
HelpSupport: HelpSupportScreenParams;
};
/**
* SettingsNavigationProp - Type for navigation prop in Settings screens
*
* This type provides type-safe navigation methods for screens
* within the Settings module.
*/
export type SettingsNavigationProp = StackNavigationProp<SettingsStackParamList>;
/**
* SettingsScreenProps - Base props interface for Settings screens
*
* This interface provides the common props that all Settings screens
* will receive, including navigation and route.
*/
export interface SettingsScreenProps<T extends keyof SettingsStackParamList> {
navigation: SettingsNavigationProp;
route: {
key: string;
name: T;
params: SettingsStackParamList[T];
};
}
// ============================================================================
// SCREEN PARAMETER TYPES
// ============================================================================
/**
* SettingsScreenParams
*
* Purpose: Parameters passed to the main settings screen
*
* Parameters:
* - section: Optional section to navigate to within settings
* - refresh: Optional flag to force refresh
*/
export interface SettingsScreenParams {
section?: 'profile' | 'security' | 'notifications' | 'clinical' | 'privacy' | 'accessibility' | 'about' | 'help';
refresh?: boolean;
}
/**
* ProfileEditScreenParams
*
* Purpose: Parameters for the profile edit screen
*
* Parameters:
* - profile: Optional profile data to pre-populate
* - fromScreen: Optional source screen for back navigation
*/
export interface ProfileEditScreenParams {
profile?: UserProfile;
fromScreen?: keyof SettingsStackParamList;
}
/**
* SecuritySettingsScreenParams
*
* Purpose: Parameters for the security settings screen
*
* Parameters:
* - showBiometricSetup: Optional flag to show biometric setup
* - fromScreen: Optional source screen for back navigation
*/
export interface SecuritySettingsScreenParams {
showBiometricSetup?: boolean;
fromScreen?: keyof SettingsStackParamList;
}
/**
* NotificationSettingsScreenParams
*
* Purpose: Parameters for the notification settings screen
*
* Parameters:
* - preferences: Optional notification preferences to pre-populate
* - fromScreen: Optional source screen for back navigation
*/
export interface NotificationSettingsScreenParams {
preferences?: UserPreferences['notificationPreferences'];
fromScreen?: keyof SettingsStackParamList;
}
/**
* ClinicalPreferencesScreenParams
*
* Purpose: Parameters for the clinical preferences screen
*
* Parameters:
* - preferences: Optional clinical preferences to pre-populate
* - fromScreen: Optional source screen for back navigation
*/
export interface ClinicalPreferencesScreenParams {
preferences?: UserPreferences['clinicalPreferences'];
fromScreen?: keyof SettingsStackParamList;
}
/**
* PrivacySettingsScreenParams
*
* Purpose: Parameters for the privacy settings screen
*
* Parameters:
* - preferences: Optional privacy preferences to pre-populate
* - fromScreen: Optional source screen for back navigation
*/
export interface PrivacySettingsScreenParams {
preferences?: UserPreferences['privacyPreferences'];
fromScreen?: keyof SettingsStackParamList;
}
/**
* AccessibilitySettingsScreenParams
*
* Purpose: Parameters for the accessibility settings screen
*
* Parameters:
* - preferences: Optional accessibility preferences to pre-populate
* - fromScreen: Optional source screen for back navigation
*/
export interface AccessibilitySettingsScreenParams {
preferences?: UserPreferences['accessibilityPreferences'];
fromScreen?: keyof SettingsStackParamList;
}
/**
* AboutScreenParams
*
* Purpose: Parameters for the about screen
*
* Parameters:
* - showChangelog: Optional flag to show changelog
* - showLicenses: Optional flag to show licenses
*/
export interface AboutScreenParams {
showChangelog?: boolean;
showLicenses?: boolean;
}
/**
* HelpSupportScreenParams
*
* Purpose: Parameters for the help and support screen
*
* Parameters:
* - topic: Optional help topic to navigate to
* - showContact: Optional flag to show contact form
*/
export interface HelpSupportScreenParams {
topic?: 'faq' | 'troubleshooting' | 'contact' | 'feedback';
showContact?: boolean;
}
// ============================================================================
// SCREEN-SPECIFIC PROPS TYPES
// ============================================================================
/**
* SettingsScreenProps - Props for SettingsScreen component
*/
export type SettingsScreenProps = SettingsScreenProps<'Settings'>;
/**
* ProfileEditScreenProps - Props for ProfileEditScreen component
*/
export type ProfileEditScreenProps = SettingsScreenProps<'ProfileEdit'>;
/**
* SecuritySettingsScreenProps - Props for SecuritySettingsScreen component
*/
export type SecuritySettingsScreenProps = SettingsScreenProps<'SecuritySettings'>;
/**
* NotificationSettingsScreenProps - Props for NotificationSettingsScreen component
*/
export type NotificationSettingsScreenProps = SettingsScreenProps<'NotificationSettings'>;
/**
* ClinicalPreferencesScreenProps - Props for ClinicalPreferencesScreen component
*/
export type ClinicalPreferencesScreenProps = SettingsScreenProps<'ClinicalPreferences'>;
/**
* PrivacySettingsScreenProps - Props for PrivacySettingsScreen component
*/
export type PrivacySettingsScreenProps = SettingsScreenProps<'PrivacySettings'>;
/**
* AccessibilitySettingsScreenProps - Props for AccessibilitySettingsScreen component
*/
export type AccessibilitySettingsScreenProps = SettingsScreenProps<'AccessibilitySettings'>;
/**
* AboutScreenProps - Props for AboutScreen component
*/
export type AboutScreenProps = SettingsScreenProps<'About'>;
/**
* HelpSupportScreenProps - Props for HelpSupportScreen component
*/
export type HelpSupportScreenProps = SettingsScreenProps<'HelpSupport'>;
/*
* End of File: navigationTypes.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,264 @@
/*
* File: navigationUtils.ts
* Description: Navigation utilities for Settings module
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { SettingsNavigationProp } from './navigationTypes';
import { UserProfile, UserPreferences } from '../../../shared/types';
/**
* SettingsNavigationUtils - Utility functions for Settings module navigation
*
* This module provides helper functions for common navigation patterns
* within the Settings module, ensuring consistent navigation behavior.
*/
/**
* Navigate to Settings screen
* @param navigation - Navigation prop from React Navigation
* @param params - Optional parameters for the settings screen
*/
export const navigateToSettings = (
navigation: SettingsNavigationProp,
params?: {
section?: 'profile' | 'security' | 'notifications' | 'clinical' | 'privacy' | 'accessibility' | 'about' | 'help';
refresh?: boolean;
}
): void => {
navigation.navigate('Settings', params);
};
/**
* Navigate to Profile Edit screen
* @param navigation - Navigation prop from React Navigation
* @param profile - Optional profile data to pre-populate
* @param fromScreen - Optional source screen for back navigation
*/
export const navigateToProfileEdit = (
navigation: SettingsNavigationProp,
profile?: UserProfile,
fromScreen?: keyof import('./navigationTypes').SettingsStackParamList
): void => {
navigation.navigate('ProfileEdit', {
profile,
fromScreen,
});
};
/**
* Navigate to Security Settings screen
* @param navigation - Navigation prop from React Navigation
* @param showBiometricSetup - Optional flag to show biometric setup
* @param fromScreen - Optional source screen for back navigation
*/
export const navigateToSecuritySettings = (
navigation: SettingsNavigationProp,
showBiometricSetup?: boolean,
fromScreen?: keyof import('./navigationTypes').SettingsStackParamList
): void => {
navigation.navigate('SecuritySettings', {
showBiometricSetup,
fromScreen,
});
};
/**
* Navigate to Notification Settings screen
* @param navigation - Navigation prop from React Navigation
* @param preferences - Optional notification preferences to pre-populate
* @param fromScreen - Optional source screen for back navigation
*/
export const navigateToNotificationSettings = (
navigation: SettingsNavigationProp,
preferences?: UserPreferences['notificationPreferences'],
fromScreen?: keyof import('./navigationTypes').SettingsStackParamList
): void => {
navigation.navigate('NotificationSettings', {
preferences,
fromScreen,
});
};
/**
* Navigate to Clinical Preferences screen
* @param navigation - Navigation prop from React Navigation
* @param preferences - Optional clinical preferences to pre-populate
* @param fromScreen - Optional source screen for back navigation
*/
export const navigateToClinicalPreferences = (
navigation: SettingsNavigationProp,
preferences?: UserPreferences['clinicalPreferences'],
fromScreen?: keyof import('./navigationTypes').SettingsStackParamList
): void => {
navigation.navigate('ClinicalPreferences', {
preferences,
fromScreen,
});
};
/**
* Navigate to Privacy Settings screen
* @param navigation - Navigation prop from React Navigation
* @param preferences - Optional privacy preferences to pre-populate
* @param fromScreen - Optional source screen for back navigation
*/
export const navigateToPrivacySettings = (
navigation: SettingsNavigationProp,
preferences?: UserPreferences['privacyPreferences'],
fromScreen?: keyof import('./navigationTypes').SettingsStackParamList
): void => {
navigation.navigate('PrivacySettings', {
preferences,
fromScreen,
});
};
/**
* Navigate to Accessibility Settings screen
* @param navigation - Navigation prop from React Navigation
* @param preferences - Optional accessibility preferences to pre-populate
* @param fromScreen - Optional source screen for back navigation
*/
export const navigateToAccessibilitySettings = (
navigation: SettingsNavigationProp,
preferences?: UserPreferences['accessibilityPreferences'],
fromScreen?: keyof import('./navigationTypes').SettingsStackParamList
): void => {
navigation.navigate('AccessibilitySettings', {
preferences,
fromScreen,
});
};
/**
* Navigate to About screen
* @param navigation - Navigation prop from React Navigation
* @param showChangelog - Optional flag to show changelog
* @param showLicenses - Optional flag to show licenses
*/
export const navigateToAbout = (
navigation: SettingsNavigationProp,
showChangelog?: boolean,
showLicenses?: boolean
): void => {
navigation.navigate('About', {
showChangelog,
showLicenses,
});
};
/**
* Navigate to Help & Support screen
* @param navigation - Navigation prop from React Navigation
* @param topic - Optional help topic to navigate to
* @param showContact - Optional flag to show contact form
*/
export const navigateToHelpSupport = (
navigation: SettingsNavigationProp,
topic?: 'faq' | 'troubleshooting' | 'contact' | 'feedback',
showContact?: boolean
): void => {
navigation.navigate('HelpSupport', {
topic,
showContact,
});
};
/**
* Go back to previous screen
* @param navigation - Navigation prop from React Navigation
*/
export const goBack = (navigation: SettingsNavigationProp): void => {
if (navigation.canGoBack()) {
navigation.goBack();
}
};
/**
* Reset navigation stack to Settings screen
* @param navigation - Navigation prop from React Navigation
* @param params - Optional parameters for the settings screen
*/
export const resetToSettings = (
navigation: SettingsNavigationProp,
params?: {
section?: 'profile' | 'security' | 'notifications' | 'clinical' | 'privacy' | 'accessibility' | 'about' | 'help';
refresh?: boolean;
}
): void => {
navigation.reset({
index: 0,
routes: [{ name: 'Settings', params }],
});
};
/**
* Replace current screen with Settings screen
* @param navigation - Navigation prop from React Navigation
* @param params - Optional parameters for the settings screen
*/
export const replaceWithSettings = (
navigation: SettingsNavigationProp,
params?: {
section?: 'profile' | 'security' | 'notifications' | 'clinical' | 'privacy' | 'accessibility' | 'about' | 'help';
refresh?: boolean;
}
): void => {
navigation.replace('Settings', params);
};
/**
* Navigate to Settings and clear back stack
* @param navigation - Navigation prop from React Navigation
* @param params - Optional parameters for the settings screen
*/
export const navigateToSettingsAndClearStack = (
navigation: SettingsNavigationProp,
params?: {
section?: 'profile' | 'security' | 'notifications' | 'clinical' | 'privacy' | 'accessibility' | 'about' | 'help';
refresh?: boolean;
}
): void => {
navigation.reset({
index: 0,
routes: [{ name: 'Settings', params }],
});
};
/**
* Navigate to Profile Edit and clear back stack
* @param navigation - Navigation prop from React Navigation
* @param profile - Optional profile data to pre-populate
*/
export const navigateToProfileEditAndClearStack = (
navigation: SettingsNavigationProp,
profile?: UserProfile
): void => {
navigation.reset({
index: 0,
routes: [{ name: 'ProfileEdit', params: { profile } }],
});
};
/**
* Navigate to Security Settings and clear back stack
* @param navigation - Navigation prop from React Navigation
* @param showBiometricSetup - Optional flag to show biometric setup
*/
export const navigateToSecuritySettingsAndClearStack = (
navigation: SettingsNavigationProp,
showBiometricSetup?: boolean
): void => {
navigation.reset({
index: 0,
routes: [{ name: 'SecuritySettings', params: { showBiometricSetup } }],
});
};
/*
* End of File: navigationUtils.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,394 @@
/*
* File: settingsSlice.ts
* Description: Settings state management slice
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { UserProfile, UserPreferences, SettingsState } from '../../../shared/types';
// ============================================================================
// ASYNC THUNKS
// ============================================================================
/**
* Fetch User Profile Async Thunk
*
* Purpose: Fetch user profile from API
*
* @returns Promise with user profile data or error
*/
export const fetchUserProfile = createAsyncThunk(
'settings/fetchUserProfile',
async (_, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 1000));
// Mock user profile data
const mockProfile: UserProfile = {
id: '1',
email: 'john.smith@hospital.com',
firstName: 'Dr. John',
lastName: 'Smith',
role: 'ATTENDING_PHYSICIAN',
department: 'Emergency Medicine',
credentials: ['MD', 'FACEP'],
specialties: ['Emergency Medicine', 'Trauma'],
profilePicture: null,
phoneNumber: '+1-555-0123',
yearsOfExperience: 15,
isActive: true,
lastLogin: new Date().toISOString(),
permissions: ['READ_PATIENTS', 'WRITE_PATIENTS', 'VIEW_ALERTS'],
};
return mockProfile;
} catch (error) {
return rejectWithValue('Failed to fetch user profile.');
}
}
);
/**
* Update User Profile Async Thunk
*
* Purpose: Update user profile information
*
* @param profileData - Updated profile data
* @returns Promise with updated profile or error
*/
export const updateUserProfile = createAsyncThunk(
'settings/updateUserProfile',
async (profileData: Partial<UserProfile> & { id: string }, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 800));
return profileData;
} catch (error) {
return rejectWithValue('Failed to update user profile.');
}
}
);
/**
* Fetch User Preferences Async Thunk
*
* Purpose: Fetch user preferences from API
*
* @returns Promise with user preferences data or error
*/
export const fetchUserPreferences = createAsyncThunk(
'settings/fetchUserPreferences',
async (_, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 500));
// Mock user preferences data
const mockPreferences: UserPreferences = {
notifications: {
criticalAlerts: true,
patientUpdates: true,
shiftChanges: false,
systemMaintenance: false,
soundEnabled: true,
vibrationEnabled: true,
},
clinical: {
autoRefreshInterval: 30,
showVitalSigns: true,
showAllergies: true,
showMedications: true,
defaultView: 'list',
},
privacy: {
biometricEnabled: true,
sessionTimeout: 30,
dataRetention: 90,
auditLogging: true,
},
accessibility: {
fontSize: 'medium',
highContrast: false,
screenReader: false,
reducedMotion: false,
},
};
return mockPreferences;
} catch (error) {
return rejectWithValue('Failed to fetch user preferences.');
}
}
);
/**
* Update User Preferences Async Thunk
*
* Purpose: Update user preferences
*
* @param preferences - Updated preferences data
* @returns Promise with updated preferences or error
*/
export const updateUserPreferences = createAsyncThunk(
'settings/updateUserPreferences',
async (preferences: Partial<UserPreferences>, { rejectWithValue }) => {
try {
// TODO: Replace with actual API call
await new Promise(resolve => setTimeout(resolve, 600));
return preferences;
} catch (error) {
return rejectWithValue('Failed to update user preferences.');
}
}
);
// ============================================================================
// INITIAL STATE
// ============================================================================
/**
* Initial Settings State
*
* Purpose: Define the initial state for settings
*
* Features:
* - User profile management
* - User preferences management
* - Loading states for async operations
* - Error handling and messages
* - Settings validation
*/
const initialState: SettingsState = {
// User profile
userProfile: null,
// User preferences
userPreferences: null,
// Loading states
isLoading: false,
isUpdatingProfile: false,
isUpdatingPreferences: false,
// Error handling
error: null,
// Settings validation
isProfileValid: true,
isPreferencesValid: true,
// Last updated timestamps
profileLastUpdated: null,
preferencesLastUpdated: null,
};
// ============================================================================
// SETTINGS SLICE
// ============================================================================
/**
* Settings Slice
*
* Purpose: Redux slice for settings state management
*
* Features:
* - User profile management
* - User preferences management
* - Settings validation
* - Error handling
* - Loading states
*/
const settingsSlice = createSlice({
name: 'settings',
initialState,
reducers: {
/**
* Clear Error Action
*
* Purpose: Clear settings errors
*/
clearError: (state) => {
state.error = null;
},
/**
* Set Profile Validation Action
*
* Purpose: Set profile validation status
*/
setProfileValidation: (state, action: PayloadAction<boolean>) => {
state.isProfileValid = action.payload;
},
/**
* Set Preferences Validation Action
*
* Purpose: Set preferences validation status
*/
setPreferencesValidation: (state, action: PayloadAction<boolean>) => {
state.isPreferencesValid = action.payload;
},
/**
* Update Profile Field Action
*
* Purpose: Update a specific profile field
*/
updateProfileField: (state, action: PayloadAction<{ field: keyof UserProfile; value: any }>) => {
if (state.userProfile) {
(state.userProfile as any)[action.payload.field] = action.payload.value;
state.profileLastUpdated = new Date();
}
},
/**
* Update Preference Field Action
*
* Purpose: Update a specific preference field
*/
updatePreferenceField: (state, action: PayloadAction<{ path: string; value: any }>) => {
if (state.userPreferences) {
const path = action.payload.path.split('.');
let current: any = state.userPreferences;
for (let i = 0; i < path.length - 1; i++) {
current = current[path[i]];
}
current[path[path.length - 1]] = action.payload.value;
state.preferencesLastUpdated = new Date();
}
},
/**
* Reset Profile Action
*
* Purpose: Reset profile to last saved state
*/
resetProfile: (state) => {
// TODO: Implement reset logic
state.isProfileValid = true;
},
/**
* Reset Preferences Action
*
* Purpose: Reset preferences to last saved state
*/
resetPreferences: (state) => {
// TODO: Implement reset logic
state.isPreferencesValid = true;
},
/**
* Clear Settings Cache Action
*
* Purpose: Clear settings data cache
*/
clearSettingsCache: (state) => {
state.userProfile = null;
state.userPreferences = null;
state.profileLastUpdated = null;
state.preferencesLastUpdated = null;
},
},
extraReducers: (builder) => {
// Fetch User Profile
builder
.addCase(fetchUserProfile.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(fetchUserProfile.fulfilled, (state, action) => {
state.isLoading = false;
state.userProfile = action.payload;
state.profileLastUpdated = new Date();
state.error = null;
})
.addCase(fetchUserProfile.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload as string;
});
// Update User Profile
builder
.addCase(updateUserProfile.pending, (state) => {
state.isUpdatingProfile = true;
state.error = null;
})
.addCase(updateUserProfile.fulfilled, (state, action) => {
state.isUpdatingProfile = false;
if (state.userProfile) {
state.userProfile = { ...state.userProfile, ...action.payload };
state.profileLastUpdated = new Date();
}
state.error = null;
})
.addCase(updateUserProfile.rejected, (state, action) => {
state.isUpdatingProfile = false;
state.error = action.payload as string;
});
// Fetch User Preferences
builder
.addCase(fetchUserPreferences.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(fetchUserPreferences.fulfilled, (state, action) => {
state.isLoading = false;
state.userPreferences = action.payload;
state.preferencesLastUpdated = new Date();
state.error = null;
})
.addCase(fetchUserPreferences.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload as string;
});
// Update User Preferences
builder
.addCase(updateUserPreferences.pending, (state) => {
state.isUpdatingPreferences = true;
state.error = null;
})
.addCase(updateUserPreferences.fulfilled, (state, action) => {
state.isUpdatingPreferences = false;
if (state.userPreferences) {
state.userPreferences = { ...state.userPreferences, ...action.payload };
state.preferencesLastUpdated = new Date();
}
state.error = null;
})
.addCase(updateUserPreferences.rejected, (state, action) => {
state.isUpdatingPreferences = false;
state.error = action.payload as string;
});
},
});
// ============================================================================
// EXPORTS
// ============================================================================
export const {
clearError,
setProfileValidation,
setPreferencesValidation,
updateProfileField,
updatePreferenceField,
resetProfile,
resetPreferences,
clearSettingsCache,
} = settingsSlice.actions;
export default settingsSlice.reducer;
/*
* End of File: settingsSlice.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,610 @@
/*
* File: SettingsScreen.tsx
* Description: Main settings screen with profile management and app preferences
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React, { useState, useEffect } from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
Alert,
RefreshControl,
Image,
} from 'react-native';
import { theme } from '../../../theme/theme';
import {
SettingsSection,
SettingsSectionData,
SettingsItem
} from '../../../shared/types';
import { SettingsHeader } from '../components/SettingsHeader';
import { SettingsSectionComponent } from '../components/SettingsSectionComponent';
import { ProfileCard } from '../components/ProfileCard';
import { CustomModal } from '../../../shared/components';
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
import { logoutUser } from '../../Auth/redux/authActions';
import {
selectUser,
selectUserDisplayName,
selectUserEmail,
selectUserFirstName,
selectUserLastName,
selectUserProfilePhoto,
selectNotificationPreferences,
selectDashboardSettings
} from '../../Auth/redux/authSelectors';
/**
* SettingsScreenProps Interface
*
* Purpose: Defines the props required by the SettingsScreen component
*
* Props:
* - navigation: React Navigation object for screen navigation
*/
interface SettingsScreenProps {
navigation: any;
}
/**
* SettingsScreen Component
*
* Purpose: Main settings screen for user profile management and app preferences
*
* Features:
* 1. User profile overview and quick access
* 2. Comprehensive settings sections
* 3. Navigation to detailed settings screens
* 4. Pull-to-refresh functionality
* 5. Mock data generation for demonstration
*
* Settings Sections:
* - Profile: Personal and professional information
* - Notifications: Alert and notification preferences
* - Clinical: Clinical workflow preferences
* - Privacy: Security and privacy settings
* - Accessibility: Accessibility features
* - About: App information and help
* - Logout: Sign out functionality
*/
export const SettingsScreen: React.FC<SettingsScreenProps> = ({
navigation,
}) => {
// ============================================================================
// STATE MANAGEMENT
// ============================================================================
// Settings sections state
const [settingsSections, setSettingsSections] = useState<SettingsSectionData[]>([]);
// UI state
const [refreshing, setRefreshing] = useState(false);
// Modal state
const [modalVisible, setModalVisible] = useState(false);
const [modalConfig, setModalConfig] = useState({
title: '',
message: '',
type: 'info' as 'success' | 'error' | 'warning' | 'info' | 'confirm',
onConfirm: () => {},
showCancel: false,
icon: '',
});
// Redux dispatch and selectors
const dispatch = useAppDispatch();
// User data from Redux
const user = useAppSelector(selectUser);
const userDisplayName = useAppSelector(selectUserDisplayName);
const userEmail = useAppSelector(selectUserEmail);
const userFirstName = useAppSelector(selectUserFirstName);
const userLastName = useAppSelector(selectUserLastName);
const userProfilePhoto = useAppSelector(selectUserProfilePhoto);
const notificationPreferences = useAppSelector(selectNotificationPreferences);
const dashboardSettings = useAppSelector(selectDashboardSettings);
console.log('user details i got', user);
// ============================================================================
// SETTINGS SECTIONS GENERATION
// ============================================================================
/**
* generateSettingsSections Function
*
* Purpose: Generate settings sections with items for the settings screen
*
* Returns: Array of SettingsSectionData with navigation and action items
*/
const generateSettingsSections = (): SettingsSectionData[] => [
{
id: 'PROFILE',
title: 'Profile & Account',
items: [
{
id: 'edit-profile',
title: 'Edit Profile',
subtitle: 'Update personal and professional information',
icon: 'user',
type: 'NAVIGATION',
onPress: () => handleNavigation('PROFILE'),
},
{
id: 'change-password',
title: 'Change Password',
subtitle: 'Update your account password',
icon: 'lock',
type: 'NAVIGATION',
onPress: () => handleNavigation('CHANGE_PASSWORD'),
},
{
id: 'security-settings',
title: 'Security Settings',
subtitle: 'Two-factor authentication and biometrics',
icon: 'shield',
type: 'NAVIGATION',
onPress: () => handleNavigation('SECURITY'),
},
],
},
{
id: 'NOTIFICATIONS',
title: 'Notifications',
items: [
{
id: 'notification-preferences',
title: 'Notification Preferences',
subtitle: 'Manage alert and notification settings',
icon: 'bell',
type: 'NAVIGATION',
onPress: () => handleNavigation('NOTIFICATIONS'),
},
{
id: 'quiet-hours',
title: 'Quiet Hours',
subtitle: 'Set do not disturb periods',
icon: 'moon',
type: 'NAVIGATION',
onPress: () => handleNavigation('QUIET_HOURS'),
},
{
id: 'push-notifications',
title: 'Push Notifications',
subtitle: 'Enable or disable push notifications',
icon: 'phone',
type: 'TOGGLE',
value: notificationPreferences?.system_notifications.push,
onPress: () => handleToggleSetting('pushNotifications'),
},
],
},
{
id: 'PRIVACY',
title: 'Privacy & Security',
items: [
{
id: 'privacy-settings',
title: 'Privacy Settings',
subtitle: 'Manage data sharing and privacy controls',
icon: 'settings',
type: 'NAVIGATION',
onPress: () => handleNavigation('PRIVACY'),
},
{
id: 'biometric-auth',
title: 'Biometric Authentication',
subtitle: 'Use fingerprint or face ID',
icon: 'lock',
type: 'TOGGLE',
value: false, // TODO: Add biometric auth preference to user data
onPress: () => handleToggleSetting('biometricAuth'),
},
{
id: 'session-timeout',
title: 'Session Timeout',
subtitle: 'Auto-logout after inactivity',
icon: 'clock',
type: 'NAVIGATION',
onPress: () => handleNavigation('SESSION_TIMEOUT'),
},
],
},
{
id: 'ABOUT',
title: 'About & Support',
items: [
{
id: 'app-info',
title: 'App Information',
subtitle: 'Version, build number, and details',
icon: 'smartphone',
type: 'NAVIGATION',
onPress: () => handleNavigation('APP_INFO'),
},
{
id: 'help-support',
title: 'Help & Support',
subtitle: 'Contact support and view documentation',
icon: 'help',
type: 'NAVIGATION',
onPress: () => handleNavigation('HELP'),
},
{
id: 'feedback',
title: 'Send Feedback',
subtitle: 'Report bugs or suggest improvements',
icon: 'rss',
type: 'NAVIGATION',
onPress: () => handleNavigation('FEEDBACK'),
},
],
},
{
id: 'LOGOUT',
title: 'Account',
items: [
{
id: 'logout',
title: 'Sign Out',
subtitle: 'Sign out of your account',
icon: 'log-out',
type: 'ACTION',
onPress: handleLogout,
},
],
},
];
// ============================================================================
// EFFECTS
// ============================================================================
/**
* useEffect for initial settings sections generation
*
* Purpose: Generate settings sections when component mounts or user data changes
*/
useEffect(() => {
setSettingsSections(generateSettingsSections());
}, [user, notificationPreferences, dashboardSettings]);
// ============================================================================
// EVENT HANDLERS
// ============================================================================
/**
* handleRefresh Function
*
* Purpose: Handle pull-to-refresh functionality to update settings data
*
* Flow:
* 1. Set refreshing state to true (show loading indicator)
* 2. Simulate API call with delay
* 3. Regenerate settings sections with current user data
* 4. Set refreshing state to false (hide loading indicator)
*/
const handleRefresh = async () => {
setRefreshing(true);
// Simulate API call with 1-second delay
await new Promise<void>(resolve => setTimeout(() => resolve(), 1000));
// Regenerate settings sections with current user data
setSettingsSections(generateSettingsSections());
setRefreshing(false);
};
/**
* handleNavigation Function
*
* Purpose: Handle navigation to different settings screens
*
* @param screen - Screen to navigate to
*/
const handleNavigation = (screen: string) => {
// TODO: Implement navigation to specific settings screens
console.log('Navigate to:', screen);
setModalConfig({
title: 'Navigation',
message: `Navigate to ${screen} screen`,
type: 'info',
onConfirm: () => {},
showCancel: false,
icon: 'info',
});
setModalVisible(true);
};
/**
* handleToggleSetting Function
*
* Purpose: Handle toggle settings changes
*
* @param setting - Setting to toggle
*/
const handleToggleSetting = (setting: string) => {
// TODO: Implement setting toggle logic
console.log('Toggle setting:', setting);
setModalConfig({
title: 'Setting Toggle',
message: `Toggle ${setting} setting`,
type: 'info',
icon: 'info',
onConfirm: () => {},
showCancel: false,
});
setModalVisible(true);
};
/**
* handleLogout Function
*
* Purpose: Handle user logout with Redux integration
*
* Flow:
* 1. Show confirmation dialog
* 2. Dispatch logout action to Redux
* 3. Clear authentication state
* 4. Show success message
* 5. Automatically navigate to login screen via Redux state change
*/
const handleLogout = () => {
setModalConfig({
title: 'Sign Out',
message: 'Are you sure you want to sign out?',
type: 'confirm',
icon: 'log-out',
onConfirm: async () => {
try {
// Dispatch logout thunk to Redux
await dispatch(logoutUser());
// Log the logout action
console.log('User logged out successfully');
} catch (error) {
console.error('Logout error:', error);
setModalConfig({
title: 'Error',
message: 'Failed to sign out. Please try again.',
type: 'error',
icon: 'info',
onConfirm: () => {},
showCancel: false,
});
setModalVisible(true);
}
},
showCancel: true,
});
setModalVisible(true);
};
/**
* handleProfilePress Function
*
* Purpose: Handle profile card press navigation
*/
const handleProfilePress = () => {
handleNavigation('PROFILE');
};
// ============================================================================
// MAIN RENDER
// ============================================================================
return (
<View style={styles.container}>
{/* Settings header with title */}
<SettingsHeader title="Settings" />
{/* Scrollable settings content */}
<ScrollView
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
colors={[theme.colors.primary]}
tintColor={theme.colors.primary}
/>
}
showsVerticalScrollIndicator={false}
>
{/* Profile card section */}
{user && (
<View style={styles.profileCard}>
<TouchableOpacity onPress={handleProfilePress} activeOpacity={0.7}>
<View style={styles.profileHeader}>
<View style={styles.profileImageContainer}>
{user.profile_photo_url ? (
<Image
source={{ uri: user.profile_photo_url }}
style={styles.profileImage}
resizeMode="cover"
/>
) : (
<View style={styles.fallbackAvatar}>
<Text style={styles.fallbackText}>
{user.first_name.charAt(0)}{user.last_name.charAt(0)}
</Text>
</View>
)}
</View>
<View style={styles.profileInfo}>
<Text style={styles.profileName}>
{user.display_name || `${user.first_name} ${user.last_name}`}
</Text>
<Text style={styles.profileEmail}>{user.email}</Text>
<Text style={styles.profileRole}>{user.dashboard_role}</Text>
</View>
<View style={styles.editIcon}>
<Text style={styles.editText}>Edit</Text>
</View>
</View>
</TouchableOpacity>
</View>
)}
{/* Settings sections */}
{settingsSections.map((section) => (
<SettingsSectionComponent
key={section.id}
section={section}
/>
))}
{/* Bottom spacing for tab bar */}
<View style={styles.bottomSpacing} />
</ScrollView>
{/* Custom Modal */}
<CustomModal
visible={modalVisible}
title={modalConfig.title}
message={modalConfig.message}
type={modalConfig.type}
onConfirm={modalConfig.onConfirm}
showCancel={modalConfig.showCancel}
icon={modalConfig.icon}
confirmText={modalConfig.type === 'confirm' ? 'Sign Out' : 'OK'}
cancelText="Cancel"
onClose={() => setModalVisible(false)}
/>
</View>
);
};
// ============================================================================
// STYLES SECTION
// ============================================================================
const styles = StyleSheet.create({
// Main container for the settings 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,
},
// Scroll view styling
scrollView: {
flex: 1,
},
// Scroll content styling
scrollContent: {
paddingHorizontal: theme.spacing.md,
},
// Bottom spacing for tab bar
bottomSpacing: {
height: theme.spacing.xl,
},
// Profile card styles
profileCard: {
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.medium,
padding: theme.spacing.md,
marginBottom: theme.spacing.md,
...theme.shadows.primary,
},
profileHeader: {
flexDirection: 'row',
alignItems: 'center',
},
profileImageContainer: {
marginRight: theme.spacing.md,
},
profileImage: {
width: 60,
height: 60,
borderRadius: 30,
},
fallbackAvatar: {
width: 60,
height: 60,
borderRadius: 30,
backgroundColor: theme.colors.primary,
justifyContent: 'center',
alignItems: 'center',
},
fallbackText: {
color: theme.colors.background,
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
},
profileInfo: {
flex: 1,
},
profileName: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
marginBottom: theme.spacing.xs,
},
profileEmail: {
fontSize: theme.typography.fontSize.bodyMedium,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
marginBottom: theme.spacing.xs,
},
profileRole: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.primary,
},
editIcon: {
paddingHorizontal: theme.spacing.sm,
paddingVertical: theme.spacing.xs,
backgroundColor: theme.colors.backgroundAlt,
borderRadius: theme.borderRadius.small,
},
editText: {
fontSize: theme.typography.fontSize.bodySmall,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.primary,
},
});
/*
* End of File: SettingsScreen.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,148 @@
/*
* File: MainTabNavigator.tsx
* Description: Bottom tab navigator for the main app interface
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { theme } from '../theme/theme';
import { DashboardStackNavigator } from '../modules/Dashboard/navigation';
import { SettingsStackNavigator } from '../modules/Settings/navigation';
import { MainTabParamList } from './navigationTypes';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
// Create the bottom tab navigator
const Tab = createBottomTabNavigator<MainTabParamList>();
/**
* MainTabNavigator Component
*
* Purpose: Creates the bottom tab navigation for the main app interface
*
* Tab Structure:
* - Dashboard: Main ER dashboard with patient overview and statistics
* - Patients: Detailed patient list and management interface
* - Alerts: Critical notifications and alert management
* - Reports: Medical reports and documentation access
* - Settings: User preferences and app configuration
*
* Features:
* - Consistent styling with app theme
* - Tab-specific icons and labels
* - Proper parameter passing to screens
* - Accessibility support
*/
export const MainTabNavigator: React.FC = () => {
return (
<Tab.Navigator
screenOptions={{
// Tab bar styling for active and inactive states
tabBarActiveTintColor: theme.colors.primary, // Blue color for active tab
tabBarInactiveTintColor: theme.colors.textMuted, // Gray color for inactive tab
// Tab bar container styling
tabBarStyle: {
backgroundColor: theme.colors.background,
borderTopColor: theme.colors.border,
borderTopWidth: 1,
paddingBottom: 8,
paddingTop: 8,
height: 60, // Fixed height for tab bar
},
// Header styling for each screen
headerStyle: {
backgroundColor: theme.colors.background,
borderBottomColor: theme.colors.border,
borderBottomWidth: 1,
},
headerTitleStyle: {
color: theme.colors.textPrimary,
fontFamily: theme.typography.fontFamily.bold,
},
}}
>
{/* Dashboard Tab - Main ER overview */}
<Tab.Screen
name="Dashboard"
component={DashboardStackNavigator}
options={{
title: 'ER Dashboard',
tabBarLabel: 'Dashboard',
// TODO: Add tab bar icon
tabBarIcon: ({ color, size }) => (
<MaterialIcons name="dashboard" size={size} color={color} />
),
headerShown: false,
}}
/>
{/* Patients Tab - Patient list and management */}
<Tab.Screen
name="Patients"
component={DashboardStackNavigator} // TODO: Replace with actual PatientsScreen
options={{
title: 'Patient List',
tabBarLabel: 'Patients',
// TODO: Add tab bar icon
tabBarIcon: ({ color, size }) => (
<MaterialIcons name="people" size={size} color={color} />
),
headerShown: false,
}}
/>
{/* Alerts Tab - Critical notifications */}
<Tab.Screen
name="Alerts"
component={DashboardStackNavigator} // TODO: Replace with actual AlertsScreen
options={{
title: 'Alerts',
tabBarLabel: 'Alerts',
// TODO: Add tab bar icon
tabBarIcon: ({ color, size }) => (
<MaterialIcons name="notifications" size={size} color={color} />
),
headerShown: false,
}}
/>
{/* Reports Tab - Medical documentation */}
<Tab.Screen
name="Reports"
component={DashboardStackNavigator} // TODO: Replace with actual ReportsScreen
options={{
title: 'Reports',
tabBarLabel: 'Reports',
// TODO: Add tab bar icon
tabBarIcon: ({ color, size }) => (
<MaterialIcons name="description" size={size} color={color} />
),
headerShown: false,
}}
/>
{/* Settings Tab - User preferences */}
<Tab.Screen
name="Settings"
component={SettingsStackNavigator}
options={{
title: 'Settings',
tabBarLabel: 'Settings',
tabBarIcon: ({ color, size }) => (
<MaterialIcons name="settings" size={size} color={color} />
),
headerShown: false,
}}
/>
</Tab.Navigator>
);
};
/*
* End of File: MainTabNavigator.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,80 @@
/*
* File: RootStackNavigator.tsx
* Description: Root stack navigator managing authentication and main app flow
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import { AuthStackNavigator } from '../modules/Auth/navigation';
import { MainTabNavigator } from './MainTabNavigator';
import { RootStackParamList } from './navigationTypes';
import { useAppSelector } from '../store/hooks';
import { selectIsAuthenticated, selectIsOnboarded } from '../modules/Auth/redux/authSelectors';
import ResetPasswordScreen from '../modules/Auth/screens/ResetPasswordScreen';
// Create the stack navigator
const Stack = createStackNavigator<RootStackParamList>();
/**
* RootStackNavigatorProps Interface
*
* Purpose: Defines the props required by the RootStackNavigator component
*
* Props:
* - isAuthenticated: Boolean indicating if user is logged in
*/
interface RootStackNavigatorProps {
isAuthenticated: boolean;
}
/**
* RootStackNavigator Component
*
* Purpose: Manages the main navigation flow between authentication, onboarding, and main app
*
* Navigation Flow:
* 1. App starts Check authentication status
* 2. If not authenticated Show AuthStackNavigator (LoginScreen)
* 3. If authenticated but not onboarded Show ResetPasswordScreen
* 4. If authenticated and onboarded Show MainTabNavigator (dashboard)
*
* Features:
* - Conditional rendering based on authentication and onboarding status
* - Seamless transition between auth, onboarding, and main app
* - Proper prop passing to child components
* - Hidden headers for custom styling
*/
export const RootStackNavigator: React.FC<RootStackNavigatorProps> = ({
isAuthenticated,
}) => {
// Get onboarding status from Redux
const isOnboarded = useAppSelector(selectIsOnboarded);
return (
<Stack.Navigator
screenOptions={{
headerShown: false, // Hide default headers for custom styling
}}
>
{/* Conditional rendering based on authentication and onboarding status */}
{!isAuthenticated ? (
// Show auth stack if user is not authenticated
<Stack.Screen name="Auth" component={AuthStackNavigator} />
) : !isOnboarded ? (
// Show reset password screen if user is authenticated but not onboarded
<Stack.Screen name="ResetPassword" component={ResetPasswordScreen} />
) : (
// Show main app tabs if user is authenticated and onboarded
<Stack.Screen name="Main" component={MainTabNavigator} />
)}
</Stack.Navigator>
);
};
/*
* End of File: RootStackNavigator.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

59
app/navigation/index.ts Normal file
View File

@ -0,0 +1,59 @@
/*
* File: index.ts
* Description: Main navigation module exports
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// ============================================================================
// NAVIGATION COMPONENTS
// ============================================================================
// Main navigation components
export { MainTabNavigator } from './MainTabNavigator';
export { RootStackNavigator } from './RootStackNavigator';
// ============================================================================
// NAVIGATION TYPES
// ============================================================================
// Type definitions for navigation
export type {
RootStackParamList,
MainTabParamList,
DashboardScreenParams,
PatientsScreenParams,
AlertsScreenParams,
ReportsScreenParams,
SettingsScreenParams,
NavigationProps,
TabNavigationProps,
NavigationRef,
} from './navigationTypes';
// ============================================================================
// NAVIGATION UTILITIES
// ============================================================================
// Navigation utility functions
export {
navigationRef,
setNavigationRef,
navigateToScreen,
goBack,
resetNavigation,
navigateToDashboard,
navigateToPatientDetails,
navigateToAlerts,
navigateToReports,
navigateToSettings,
navigateToLogin,
navigateToMainApp,
handleNavigationError,
} from './navigationUtils';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,190 @@
/*
* File: navigationTypes.ts
* Description: TypeScript type definitions for navigation parameters and routes
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { NavigatorScreenParams } from '@react-navigation/native';
import { ERDashboard, Patient, Alert as AlertType } from '../shared/types';
// ============================================================================
// ROOT NAVIGATION TYPES
// ============================================================================
/**
* RootStackParamList
*
* Purpose: Defines the main navigation stack parameters for the entire app
*
* Routes:
* - Auth: Authentication stack navigator
* - Main: Main app with bottom tab navigation
*/
export type RootStackParamList = {
Auth: undefined; // Auth stack navigator handles its own screens
ResetPassword: undefined; // Reset password screen for onboarding
Main: NavigatorScreenParams<MainTabParamList>; // Main app with tab navigation
};
// ============================================================================
// MAIN TAB NAVIGATION TYPES
// ============================================================================
/**
* MainTabParamList
*
* Purpose: Defines the bottom tab navigation parameters
*
* Tabs:
* - Dashboard: ER dashboard with patient overview
* - Patients: Patient list and management
* - Alerts: Critical notifications and alerts
* - Reports: Medical reports and documentation
* - Settings: User preferences and app configuration
*/
export type MainTabParamList = {
Dashboard: DashboardScreenParams; // Dashboard with initial data
Patients: PatientsScreenParams; // Patient list screen
Alerts: AlertsScreenParams; // Alerts screen
Reports: ReportsScreenParams; // Reports screen
Settings: SettingsScreenParams; // Settings screen
};
// ============================================================================
// SCREEN PARAMETER TYPES
// ============================================================================
/**
* DashboardScreenParams
*
* Purpose: Parameters passed to the dashboard screen
*
* Parameters:
* - dashboard: ER dashboard data including statistics and shift info
* - patients: Array of patient data for display
* - alerts: Array of critical alerts and notifications
*/
export interface DashboardScreenParams {
dashboard: ERDashboard;
patients: Patient[];
alerts: AlertType[];
}
/**
* PatientsScreenParams
*
* Purpose: Parameters for the patients screen
*
* Parameters:
* - filter: Optional filter to apply to patient list
* - searchQuery: Optional search term for patient search
*/
export interface PatientsScreenParams {
filter?: 'all' | 'critical' | 'active' | 'pending';
searchQuery?: string;
}
/**
* AlertsScreenParams
*
* Purpose: Parameters for the alerts screen
*
* Parameters:
* - priority: Optional priority filter for alerts
* - unreadOnly: Optional flag to show only unread alerts
*/
export interface AlertsScreenParams {
priority?: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
unreadOnly?: boolean;
}
/**
* ReportsScreenParams
*
* Purpose: Parameters for the reports screen
*
* Parameters:
* - patientId: Optional patient ID to filter reports
* - dateRange: Optional date range for report filtering
*/
export interface ReportsScreenParams {
patientId?: string;
dateRange?: {
start: Date;
end: Date;
};
}
/**
* SettingsScreenParams
*
* Purpose: Parameters for the settings screen
*
* Parameters:
* - section: Optional section to navigate to within settings
*/
export interface SettingsScreenParams {
section?: 'profile' | 'notifications' | 'security' | 'about';
}
// ============================================================================
// NAVIGATION PROPS TYPES
// ============================================================================
/**
* NavigationProps
*
* Purpose: Common navigation props used across components
*
* Properties:
* - navigation: Navigation object for screen navigation
* - route: Current route information and parameters
*/
export interface NavigationProps<T extends keyof RootStackParamList> {
navigation: any; // Will be properly typed when navigation is set up
route: {
params: RootStackParamList[T];
};
}
/**
* TabNavigationProps
*
* Purpose: Navigation props for tab screens
*
* Properties:
* - navigation: Tab navigation object
* - route: Current tab route information
*/
export interface TabNavigationProps<T extends keyof MainTabParamList> {
navigation: any; // Will be properly typed when navigation is set up
route: {
params: MainTabParamList[T];
};
}
// ============================================================================
// NAVIGATION UTILITY TYPES
// ============================================================================
/**
* NavigationRef
*
* Purpose: Reference to the navigation object for programmatic navigation
*
* Usage:
* - Used for navigation from outside React components
* - Enables navigation from services, utilities, or event handlers
*/
export type NavigationRef = {
navigate: (name: keyof RootStackParamList, params?: any) => void;
goBack: () => void;
reset: (state: any) => void;
};
/*
* End of File: navigationTypes.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,263 @@
/*
* File: navigationUtils.ts
* Description: Navigation utility functions for common navigation operations
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { NavigationRef } from './navigationTypes';
// ============================================================================
// NAVIGATION REFERENCE
// ============================================================================
/**
* navigationRef
*
* Purpose: Global navigation reference for programmatic navigation
*
* Usage:
* - Set this reference in the main App component
* - Use for navigation from outside React components
* - Enables navigation from services, utilities, or event handlers
*
* Example:
* navigationRef.current?.navigate('Main');
*/
export let navigationRef: React.RefObject<NavigationRef> | null = null;
/**
* setNavigationRef
*
* Purpose: Set the global navigation reference
*
* @param ref - Navigation reference object
*
* Usage:
* Called from the main App component to set the navigation reference
*/
export const setNavigationRef = (ref: React.RefObject<NavigationRef | null>) => {
navigationRef = ref as React.RefObject<NavigationRef>;
};
// ============================================================================
// NAVIGATION HELPER FUNCTIONS
// ============================================================================
/**
* navigateToScreen
*
* Purpose: Navigate to a specific screen with optional parameters
*
* @param screenName - Name of the screen to navigate to
* @param params - Optional parameters to pass to the screen
*
* Example:
* navigateToScreen('Main', { tab: 'Dashboard' });
*/
export const navigateToScreen = (screenName: string, params?: any) => {
if (navigationRef?.current) {
navigationRef.current.navigate(screenName as any, params);
}
};
/**
* goBack
*
* Purpose: Navigate back to the previous screen
*
* Example:
* goBack();
*/
export const goBack = () => {
if (navigationRef?.current) {
navigationRef.current.goBack();
}
};
/**
* resetNavigation
*
* Purpose: Reset the navigation state to a specific screen
*
* @param screenName - Name of the screen to reset to
* @param params - Optional parameters for the screen
*
* Example:
* resetNavigation('Login'); // Reset to login screen
*/
export const resetNavigation = (screenName: string, params?: any) => {
if (navigationRef?.current) {
navigationRef.current.reset({
index: 0,
routes: [{ name: screenName, params }],
});
}
};
// ============================================================================
// SCREEN-SPECIFIC NAVIGATION FUNCTIONS
// ============================================================================
/**
* navigateToDashboard
*
* Purpose: Navigate to the dashboard with specific data
*
* @param dashboard - Dashboard data to pass
* @param patients - Patient data to pass
* @param alerts - Alert data to pass
*
* Example:
* navigateToDashboard(dashboardData, patientList, alertList);
*/
export const navigateToDashboard = (
dashboard: any,
patients: any[],
alerts: any[]
) => {
navigateToScreen('Main', {
screen: 'Dashboard',
params: {
dashboard,
patients,
alerts,
},
});
};
/**
* navigateToPatientDetails
*
* Purpose: Navigate to patient details screen
*
* @param patientId - ID of the patient to view
*
* Example:
* navigateToPatientDetails('patient123');
*/
export const navigateToPatientDetails = (patientId: string) => {
navigateToScreen('Main', {
screen: 'Patients',
params: {
patientId,
},
});
};
/**
* navigateToAlerts
*
* Purpose: Navigate to alerts screen with optional filters
*
* @param priority - Optional priority filter
* @param unreadOnly - Optional flag for unread alerts only
*
* Example:
* navigateToAlerts('CRITICAL', true);
*/
export const navigateToAlerts = (priority?: string, unreadOnly?: boolean) => {
navigateToScreen('Main', {
screen: 'Alerts',
params: {
priority,
unreadOnly,
},
});
};
/**
* navigateToReports
*
* Purpose: Navigate to reports screen with optional filters
*
* @param patientId - Optional patient ID filter
* @param dateRange - Optional date range filter
*
* Example:
* navigateToReports('patient123', { start: new Date(), end: new Date() });
*/
export const navigateToReports = (patientId?: string, dateRange?: any) => {
navigateToScreen('Main', {
screen: 'Reports',
params: {
patientId,
dateRange,
},
});
};
/**
* navigateToSettings
*
* Purpose: Navigate to settings screen with optional section
*
* @param section - Optional section to navigate to within settings
*
* Example:
* navigateToSettings('profile');
*/
export const navigateToSettings = (section?: string) => {
navigateToScreen('Main', {
screen: 'Settings',
params: {
section,
},
});
};
// ============================================================================
// AUTHENTICATION NAVIGATION FUNCTIONS
// ============================================================================
/**
* navigateToLogin
*
* Purpose: Navigate to login screen
*
* Example:
* navigateToLogin();
*/
export const navigateToLogin = () => {
resetNavigation('Login');
};
/**
* navigateToMainApp
*
* Purpose: Navigate to main app after successful authentication
*
* Example:
* navigateToMainApp();
*/
export const navigateToMainApp = () => {
resetNavigation('Main');
};
// ============================================================================
// ERROR HANDLING
// ============================================================================
/**
* handleNavigationError
*
* Purpose: Handle navigation errors gracefully
*
* @param error - Navigation error object
* @param fallbackScreen - Fallback screen to navigate to
*
* Example:
* handleNavigationError(error, 'Login');
*/
export const handleNavigationError = (error: any, fallbackScreen: string) => {
console.error('Navigation error:', error);
// Navigate to fallback screen
resetNavigation(fallbackScreen);
};
/*
* End of File: navigationUtils.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,348 @@
/*
* File: CustomModal.tsx
* Description: Custom modal component with matching UI design
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import React from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Modal,
TouchableWithoutFeedback,
Dimensions,
} from 'react-native';
import { theme } from '../../theme/theme';
import Icon from 'react-native-vector-icons/Feather';
// ============================================================================
// INTERFACES
// ============================================================================
/**
* CustomModalProps Interface
*
* Purpose: Defines the props required by the CustomModal component
*
* Props:
* - visible: Whether the modal is visible
* - title: Modal title
* - message: Modal message/content
* - type: Modal type (success, error, warning, info, confirm)
* - onConfirm: Callback for confirm action
* - onCancel: Callback for cancel action
* - confirmText: Text for confirm button
* - cancelText: Text for cancel button
* - onClose: Callback for closing modal
* - showCancel: Whether to show cancel button
* - icon: Custom icon name
*/
interface CustomModalProps {
visible: boolean;
title: string;
message: string;
type?: 'success' | 'error' | 'warning' | 'info' | 'confirm';
onConfirm?: () => void;
onCancel?: () => void;
confirmText?: string;
cancelText?: string;
onClose?: () => void;
showCancel?: boolean;
icon?: string;
}
// ============================================================================
// CUSTOM MODAL COMPONENT
// ============================================================================
/**
* CustomModal Component
*
* Purpose: Displays a custom modal with consistent UI design
*
* Features:
* - Multiple modal types (success, error, warning, info, confirm)
* - Customizable buttons and text
* - Consistent theme styling
* - Backdrop tap to close
* - Icon support
* - Responsive design
*/
export const CustomModal: React.FC<CustomModalProps> = ({
visible,
title,
message,
type = 'info',
onConfirm,
onCancel,
confirmText = 'OK',
cancelText = 'Cancel',
onClose,
showCancel = false,
icon,
}) => {
// ============================================================================
// MODAL CONFIGURATION
// ============================================================================
/**
* Get modal configuration based on type
*/
const getModalConfig = () => {
switch (type) {
case 'success':
return {
icon: icon || 'check-circle',
iconColor: theme.colors.success,
backgroundColor: theme.colors.background,
borderColor: theme.colors.success,
};
case 'error':
return {
icon: icon || 'alert-circle',
iconColor: theme.colors.error,
backgroundColor: theme.colors.background,
borderColor: theme.colors.error,
};
case 'warning':
return {
icon: icon || 'alert-triangle',
iconColor: theme.colors.warning,
backgroundColor: theme.colors.background,
borderColor: theme.colors.warning,
};
case 'confirm':
return {
icon: icon || 'help-circle',
iconColor: theme.colors.primary,
backgroundColor: theme.colors.background,
borderColor: theme.colors.primary,
};
default:
return {
icon: icon || 'info',
iconColor: theme.colors.info,
backgroundColor: theme.colors.background,
borderColor: theme.colors.info,
};
}
};
const config = getModalConfig();
// ============================================================================
// EVENT HANDLERS
// ============================================================================
/**
* Handle confirm action
*/
const handleConfirm = () => {
if (onConfirm) {
onConfirm();
}
if (onClose) {
onClose();
}
};
/**
* Handle cancel action
*/
const handleCancel = () => {
if (onCancel) {
onCancel();
}
if (onClose) {
onClose();
}
};
/**
* Handle backdrop press
*/
const handleBackdropPress = () => {
if (onClose) {
onClose();
}
};
// ============================================================================
// RENDER
// ============================================================================
return (
<Modal
visible={visible}
transparent
animationType="fade"
onRequestClose={onClose}
>
<TouchableWithoutFeedback onPress={handleBackdropPress}>
<View style={styles.backdrop}>
<TouchableWithoutFeedback>
<View style={[
styles.modalContainer,
{
backgroundColor: config.backgroundColor,
borderColor: config.borderColor,
}
]}>
{/* Icon */}
<View style={[styles.iconContainer, { backgroundColor: config.iconColor + '20' }]}>
<Icon name={config.icon} size={32} color={config.iconColor} />
</View>
{/* Title */}
<Text style={styles.title}>{title}</Text>
{/* Message */}
<Text style={styles.message}>{message}</Text>
{/* Buttons */}
<View style={styles.buttonContainer}>
{showCancel && (
<TouchableOpacity
style={[styles.button, styles.cancelButton]}
onPress={handleCancel}
activeOpacity={0.7}
>
<Text style={styles.cancelButtonText}>{cancelText}</Text>
</TouchableOpacity>
)}
<TouchableOpacity
style={[
styles.button,
styles.confirmButton,
{ backgroundColor: config.iconColor },
showCancel && styles.confirmButtonWithCancel,
]}
onPress={handleConfirm}
activeOpacity={0.7}
>
<Text style={styles.confirmButtonText}>{confirmText}</Text>
</TouchableOpacity>
</View>
</View>
</TouchableWithoutFeedback>
</View>
</TouchableWithoutFeedback>
</Modal>
);
};
// ============================================================================
// STYLES
// ============================================================================
const { width: screenWidth } = Dimensions.get('window');
const styles = StyleSheet.create({
// Backdrop
backdrop: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: theme.spacing.lg,
},
// Modal container
modalContainer: {
width: screenWidth - theme.spacing.lg * 2,
backgroundColor: theme.colors.background,
borderRadius: theme.borderRadius.large,
padding: theme.spacing.xl,
alignItems: 'center',
borderWidth: 2,
...theme.shadows.large,
},
// Icon container
iconContainer: {
width: 80,
height: 80,
borderRadius: 40,
justifyContent: 'center',
alignItems: 'center',
marginBottom: theme.spacing.lg,
},
// Title
title: {
fontSize: theme.typography.fontSize.displaySmall,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.textPrimary,
textAlign: 'center',
marginBottom: theme.spacing.md,
},
// Message
message: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.regular,
color: theme.colors.textSecondary,
textAlign: 'center',
lineHeight: theme.typography.fontSize.bodyLarge * 1.4,
marginBottom: theme.spacing.xl,
},
// Button container
buttonContainer: {
flexDirection: 'row',
width: '100%',
gap: theme.spacing.md,
},
// Button base
button: {
flex: 1,
paddingVertical: theme.spacing.md,
paddingHorizontal: theme.spacing.lg,
borderRadius: theme.borderRadius.medium,
alignItems: 'center',
justifyContent: 'center',
...theme.shadows.primary,
},
// Cancel button
cancelButton: {
backgroundColor: theme.colors.backgroundAlt,
borderWidth: 1,
borderColor: theme.colors.border,
},
// Cancel button text
cancelButtonText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.medium,
color: theme.colors.textSecondary,
},
// Confirm button
confirmButton: {
backgroundColor: theme.colors.primary,
},
// Confirm button with cancel
confirmButtonWithCancel: {
flex: 1,
},
// Confirm button text
confirmButtonText: {
fontSize: theme.typography.fontSize.bodyLarge,
fontFamily: theme.typography.fontFamily.bold,
color: theme.colors.background,
},
});
/*
* End of File: CustomModal.tsx
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,14 @@
/*
* File: index.ts
* Description: Shared components exports
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export { CustomModal } from './CustomModal';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

115
app/shared/types/alerts.ts Normal file
View File

@ -0,0 +1,115 @@
/*
* File: alerts.ts
* Description: Alert and notification type definitions
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export interface Alert {
id: string;
type: AlertType;
priority: AlertPriority;
title: string;
message: string;
patientId?: string;
patientName?: string;
bedNumber?: string;
timestamp: Date;
isRead: boolean;
isAcknowledged: boolean;
actionRequired: boolean;
expiresAt?: Date;
metadata?: AlertMetadata;
}
export type AlertType =
| 'CRITICAL_FINDING'
| 'VITAL_SIGNS_ALERT'
| 'SCAN_COMPLETED'
| 'MEDICATION_ALERT'
| 'BED_ASSIGNMENT'
| 'SHIFT_CHANGE'
| 'SYSTEM_ALERT'
| 'EMERGENCY_CODE';
export interface AlertMetadata {
scanId?: string;
vitalSigns?: {
type: string;
value: number;
normalRange: string;
};
medication?: {
name: string;
dosage: string;
reason: string;
};
location?: {
floor: string;
room: string;
bed: string;
};
aiFindings?: {
confidence: number;
summary: string;
recommendations: string[];
};
}
export interface CriticalFinding {
id: string;
patientId: string;
patientName: string;
bedNumber: string;
findingType: CriticalFindingType;
severity: 'LIFE_THREATENING' | 'URGENT' | 'SERIOUS';
description: string;
aiSummary: string;
recommendations: string[];
timestamp: Date;
acknowledgedBy?: string;
acknowledgedAt?: Date;
actionTaken?: string;
followUpRequired: boolean;
}
export type CriticalFindingType =
| 'BRAIN_BLEED'
| 'PULMONARY_EMBOLISM'
| 'AORTIC_DISSECTION'
| 'APPENDICITIS'
| 'FRACTURE'
| 'INTERNAL_BLEEDING'
| 'CARDIAC_ARREST'
| 'STROKE'
| 'SEPSIS'
| 'OTHER';
export interface AlertSettings {
pushNotifications: boolean;
soundAlerts: boolean;
vibrationAlerts: boolean;
criticalAlertsOnly: boolean;
quietHours: {
enabled: boolean;
startTime: string;
endTime: string;
};
alertTypes: {
[key in AlertType]: boolean;
};
}
export interface AlertResponse {
alertId: string;
response: 'ACKNOWLEDGED' | 'DISMISSED' | 'ESCALATED';
notes?: string;
timestamp: Date;
userId: string;
}
/*
* End of File: alerts.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

93
app/shared/types/auth.ts Normal file
View File

@ -0,0 +1,93 @@
/*
* File: auth.ts
* Description: Authentication and user-related type definitions
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export interface NotificationPreferences {
critical_alerts: {
email: boolean;
in_app: boolean;
push: boolean;
sms: boolean;
};
system_notifications: {
push: boolean;
email: boolean;
in_app: boolean;
};
}
export interface DashboardSettings {
theme: string;
language: string;
timezone: string;
}
export interface User {
user_id: string;
email: string;
first_name: string;
last_name: string;
display_name: string;
hospital_id: string;
dashboard_role: string;
profile_photo_url: string | null;
theme_color: string | null;
accent_color: string | null;
notification_preferences: NotificationPreferences;
dashboard_settings: DashboardSettings;
onboarded: boolean;
onboarding_completed: boolean;
onboarding_step: number;
onboarding_message: string;
access_token: string;
}
export type UserRole =
| 'ER_PHYSICIAN'
| 'RESIDENT'
| 'MEDICAL_STUDENT'
| 'EMERGENCY_ACCESS'
| 'TEMPORARY_ACCESS';
export interface AuthState {
user: User | null;
isAuthenticated: boolean;
isLoading: boolean;
error: string | null;
sessionExpiry: Date | null;
deviceId: string | null;
}
export interface LoginCredentials {
email: string;
password: string;
rememberDevice?: boolean;
}
export interface SSOLoginData {
token: string;
hospitalId: string;
redirectUrl?: string;
}
export interface EmergencyAccess {
accessCode: string;
reason: string;
duration: number; // minutes
}
export interface SessionData {
token: string;
refreshToken: string;
expiresAt: Date;
deviceId: string;
}
/*
* End of File: auth.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,92 @@
/*
* File: common.ts
* Description: Common type definitions used across the application
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
message?: string;
timestamp: Date;
}
export interface PaginatedResponse<T> {
data: T[];
pagination: {
page: number;
limit: number;
total: number;
totalPages: number;
};
}
export interface LoadingState {
isLoading: boolean;
error: string | null;
}
export interface NavigationParams {
[key: string]: any;
}
export interface ScreenProps {
navigation: any;
route: {
params: NavigationParams;
};
}
export interface FormField {
name: string;
label: string;
type: 'text' | 'email' | 'password' | 'number' | 'select' | 'date' | 'textarea';
required: boolean;
validation?: (value: any) => string | null;
options?: { label: string; value: any }[];
placeholder?: string;
defaultValue?: any;
}
export interface ValidationError {
field: string;
message: string;
}
export interface AppSettings {
theme: 'light' | 'dark' | 'auto';
language: string;
notifications: boolean;
biometricAuth: boolean;
autoLogout: boolean;
offlineMode: boolean;
}
export interface DeviceInfo {
id: string;
name: string;
platform: 'ios' | 'android';
version: string;
model: string;
isTablet: boolean;
}
export interface NetworkStatus {
isConnected: boolean;
type: 'wifi' | 'cellular' | 'none';
strength?: number;
}
export interface ErrorBoundaryState {
hasError: boolean;
error?: Error;
errorInfo?: any;
}
/*
* End of File: common.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,83 @@
/*
* File: dashboard.ts
* Description: Dashboard and ER-related type definitions
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { Patient, PatientStatus, AlertPriority } from './patient';
export interface ERDashboard {
totalPatients: number;
criticalPatients: number;
pendingScans: number;
recentReports: number;
bedOccupancy: number;
departmentStats: DepartmentStats;
shiftInfo: ShiftInfo;
lastUpdated: Date;
}
export interface DepartmentStats {
emergency: number;
trauma: number;
cardiac: number;
neurology: number;
pediatrics: number;
icu: number;
}
export interface ShiftInfo {
currentShift: 'DAY' | 'NIGHT' | 'EVENING';
startTime: Date;
endTime: Date;
attendingPhysician: string;
residents: string[];
nurses: string[];
}
export interface PatientList {
patients: Patient[];
filters: PatientFilters;
sortBy: PatientSortBy;
isLoading: boolean;
error: string | null;
}
export interface PatientFilters {
status: PatientStatus[];
priority: AlertPriority[];
department: string[];
bedNumber?: string;
attendingPhysician?: string;
}
export type PatientSortBy =
| 'PRIORITY'
| 'NAME'
| 'BED_NUMBER'
| '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
patientSatisfaction: number; // percentage
criticalAlertsResolved: number;
scansReviewed: number;
patientsDischarged: number;
}
/*
* End of File: dashboard.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

19
app/shared/types/index.ts Normal file
View File

@ -0,0 +1,19 @@
/*
* File: index.ts
* Description: Shared type definitions exports for the Physician App
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export * from './auth';
export * from './patient';
export * from './dashboard';
export * from './alerts';
export * from './settings';
export * from './common';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

118
app/shared/types/patient.ts Normal file
View File

@ -0,0 +1,118 @@
/*
* File: patient.ts
* Description: Patient-related type definitions for medical data
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export interface Patient {
id: string;
mrn: string; // Medical Record Number
firstName: string;
lastName: string;
dateOfBirth: Date;
gender: 'MALE' | 'FEMALE' | 'OTHER';
age: number;
bedNumber: string;
roomNumber: string;
admissionDate: Date;
status: PatientStatus;
priority: AlertPriority;
department: string;
attendingPhysician: string;
allergies: Allergy[];
medications: Medication[];
vitalSigns: VitalSigns;
medicalHistory: MedicalHistory[];
currentDiagnosis: string;
lastUpdated: Date;
}
export type PatientStatus =
| 'ACTIVE'
| 'PENDING'
| 'DISCHARGED'
| 'TRANSFERRED'
| 'CRITICAL';
export type AlertPriority =
| 'CRITICAL'
| 'HIGH'
| 'MEDIUM'
| 'LOW';
export interface VitalSigns {
bloodPressure: {
systolic: number;
diastolic: number;
timestamp: Date;
};
heartRate: {
value: number;
timestamp: Date;
};
temperature: {
value: number;
timestamp: Date;
};
respiratoryRate: {
value: number;
timestamp: Date;
};
oxygenSaturation: {
value: number;
timestamp: Date;
};
}
export interface Allergy {
id: string;
name: string;
severity: 'MILD' | 'MODERATE' | 'SEVERE';
reaction: string;
notes?: string;
}
export interface Medication {
id: string;
name: string;
dosage: string;
frequency: string;
route: string;
startDate: Date;
endDate?: Date;
status: 'ACTIVE' | 'DISCONTINUED' | 'COMPLETED';
prescribedBy: string;
}
export interface MedicalHistory {
id: string;
condition: string;
diagnosisDate: Date;
status: 'ACTIVE' | 'RESOLVED' | 'CHRONIC';
notes?: string;
treatingPhysician: string;
}
export interface ScanResult {
id: string;
patientId: string;
scanType: 'CT' | 'MRI' | 'XRAY' | 'ULTRASOUND';
bodyPart: string;
status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'REVIEWED';
orderedBy: string;
orderedDate: Date;
completedDate?: Date;
reviewedBy?: string;
reviewedDate?: Date;
findings: string;
aiSummary?: string;
images: string[];
priority: AlertPriority;
}
/*
* End of File: patient.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

View File

@ -0,0 +1,414 @@
/*
* File: settings.ts
* Description: Settings and user profile type definitions
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
// ============================================================================
// USER PROFILE TYPES
// ============================================================================
/**
* UserProfile Interface
*
* Purpose: Defines the complete user profile structure
*
* Properties:
* - Personal information (name, contact, demographics)
* - Professional information (credentials, specialties)
* - Hospital information (department, role, permissions)
* - Preferences and settings
*/
export interface UserProfile {
id: string;
mrn: string; // Medical Record Number
employeeId: string; // Hospital Employee ID
// Personal Information
firstName: string;
lastName: string;
middleName?: string;
email: string;
phoneNumber: string;
dateOfBirth: Date;
gender: 'MALE' | 'FEMALE' | 'OTHER';
address: Address;
// Professional Information
credentials: string[]; // MD, PhD, etc.
specialties: string[]; // Emergency Medicine, Trauma, etc.
boardCertifications: string[]; // Board certifications
licenseNumber: string; // Medical license number
licenseExpiryDate: Date;
yearsOfExperience: number;
// Hospital Information
department: string; // Emergency Department, etc.
role: ProfileUserRole; // Attending, Resident, etc.
shiftPreference: 'DAY' | 'NIGHT' | 'FLEXIBLE';
supervisor?: string; // Direct supervisor
permissions: UserPermissions;
// Profile Settings
profilePicture?: string; // URL to profile picture
emergencyContact: EmergencyContact;
preferences: UserPreferences;
// Metadata
createdAt: Date;
updatedAt: Date;
lastLoginAt: Date;
isActive: boolean;
}
/**
* Address Interface
*
* Purpose: Defines address structure for user profile
*/
export interface Address {
street: string;
city: string;
state: string;
zipCode: string;
country: string;
}
/**
* EmergencyContact Interface
*
* Purpose: Defines emergency contact information
*/
export interface EmergencyContact {
name: string;
relationship: string;
phoneNumber: string;
email?: string;
address?: Address;
}
/**
* ProfileUserRole Type
*
* Purpose: Defines different user roles in the hospital system for profile settings
*/
export type ProfileUserRole =
| 'ATTENDING_PHYSICIAN'
| 'RESIDENT_PHYSICIAN'
| 'FELLOW'
| 'MEDICAL_STUDENT'
| 'NURSE_PRACTITIONER'
| 'PHYSICIAN_ASSISTANT'
| 'ADMINISTRATOR'
| 'RESEARCHER';
/**
* UserPermissions Interface
*
* Purpose: Defines user permissions and access levels
*/
export interface UserPermissions {
canViewPatients: boolean;
canEditPatients: boolean;
canViewReports: boolean;
canEditReports: boolean;
canViewAlerts: boolean;
canAcknowledgeAlerts: boolean;
canManageUsers: boolean;
canViewAnalytics: boolean;
canExportData: boolean;
canAccessAdminPanel: boolean;
}
// ============================================================================
// USER PREFERENCES TYPES
// ============================================================================
/**
* UserPreferences Interface
*
* Purpose: Defines user preferences and settings
*/
export interface UserPreferences {
// Display Preferences
theme: 'LIGHT' | 'DARK' | 'AUTO';
fontSize: 'SMALL' | 'MEDIUM' | 'LARGE';
language: string; // ISO language code
timezone: string; // IANA timezone
// Notification Preferences
notifications: NotificationPreferences;
// Clinical Preferences
clinical: ClinicalPreferences;
// Privacy Preferences
privacy: PrivacyPreferences;
// Accessibility Preferences
accessibility: AccessibilityPreferences;
}
/**
* NotificationPreferences Interface
*
* Purpose: Defines notification settings
*/
export interface NotificationPreferences {
pushNotifications: boolean;
emailNotifications: boolean;
smsNotifications: boolean;
// Alert Types
criticalAlerts: boolean;
patientUpdates: boolean;
scanResults: boolean;
medicationReminders: boolean;
shiftChanges: boolean;
systemMaintenance: boolean;
// Timing
quietHours: {
enabled: boolean;
startTime: string; // HH:MM format
endTime: string; // HH:MM format
};
// Sound and Vibration
soundEnabled: boolean;
vibrationEnabled: boolean;
customSound?: string; // Custom notification sound
}
/**
* ClinicalPreferences Interface
*
* Purpose: Defines clinical workflow preferences
*/
export interface ClinicalPreferences {
// Default Views
defaultDashboardView: 'LIST' | 'GRID' | 'TIMELINE';
defaultPatientSort: 'PRIORITY' | 'NAME' | 'ADMISSION_TIME' | 'BED_NUMBER';
showPatientPhotos: boolean;
// Clinical Alerts
autoAcknowledgeNonCritical: boolean;
alertTimeoutMinutes: number; // Auto-dismiss after X minutes
// Documentation
autoSaveInterval: number; // Seconds
defaultReportTemplate: string;
enableVoiceNotes: boolean;
// Patient Care
showAllergiesProminently: boolean;
showMedicationInteractions: boolean;
enableClinicalDecisionSupport: boolean;
}
/**
* PrivacyPreferences Interface
*
* Purpose: Defines privacy and security settings
*/
export interface PrivacyPreferences {
// Session Management
sessionTimeoutMinutes: number;
requireBiometricAuth: boolean;
autoLockOnBackground: boolean;
// Data Sharing
allowAnalytics: boolean;
allowCrashReporting: boolean;
allowUsageStatistics: boolean;
// Privacy Controls
showPatientPhotos: boolean;
enableScreenCapture: boolean;
enableScreenshot: boolean;
}
/**
* AccessibilityPreferences Interface
*
* Purpose: Defines accessibility settings
*/
export interface AccessibilityPreferences {
// Visual
highContrastMode: boolean;
reduceMotion: boolean;
boldText: boolean;
// Audio
screenReaderEnabled: boolean;
audioDescriptions: boolean;
// Interaction
largerTouchTargets: boolean;
reduceTransparency: boolean;
}
// ============================================================================
// SETTINGS SCREEN TYPES
// ============================================================================
/**
* SettingsSection Type
*
* Purpose: Defines different sections in the settings screen
*/
export type SettingsSection =
| 'PROFILE'
| 'NOTIFICATIONS'
| 'CLINICAL'
| 'PRIVACY'
| 'ACCESSIBILITY'
| 'ABOUT'
| 'HELP'
| 'LOGOUT';
/**
* SettingsItem Interface
*
* Purpose: Defines a single settings item
*/
export interface SettingsItem {
id: string;
title: string;
subtitle?: string;
icon: string;
type: 'NAVIGATION' | 'TOGGLE' | 'SELECTION' | 'ACTION';
value?: any;
onPress?: () => void;
disabled?: boolean;
badge?: string;
}
/**
* SettingsSection Interface
*
* Purpose: Defines a settings section with items
*/
export interface SettingsSectionData {
id: SettingsSection;
title: string;
items: SettingsItem[];
}
// ============================================================================
// PROFILE UPDATE TYPES
// ============================================================================
/**
* ProfileUpdateData Interface
*
* Purpose: Defines data structure for profile updates
*/
export interface ProfileUpdateData {
firstName?: string;
lastName?: string;
middleName?: string;
email?: string;
phoneNumber?: string;
address?: Partial<Address>;
emergencyContact?: Partial<EmergencyContact>;
specialties?: string[];
boardCertifications?: string[];
shiftPreference?: 'DAY' | 'NIGHT' | 'FLEXIBLE';
}
/**
* ProfileUpdateRequest Interface
*
* Purpose: Defines API request for profile updates
*/
export interface ProfileUpdateRequest {
userId: string;
updates: ProfileUpdateData;
reason?: string; // Reason for update
}
/**
* ProfileUpdateResponse Interface
*
* Purpose: Defines API response for profile updates
*/
export interface ProfileUpdateResponse {
success: boolean;
message: string;
updatedProfile?: UserProfile;
errors?: string[];
}
// ============================================================================
// PASSWORD AND SECURITY TYPES
// ============================================================================
/**
* PasswordChangeRequest Interface
*
* Purpose: Defines password change request
*/
export interface PasswordChangeRequest {
currentPassword: string;
newPassword: string;
confirmPassword: string;
}
/**
* SecuritySettings Interface
*
* Purpose: Defines security-related settings
*/
export interface SecuritySettings {
twoFactorEnabled: boolean;
biometricEnabled: boolean;
passwordExpiryDays: number;
failedLoginAttempts: number;
lastPasswordChange: Date;
securityQuestions: SecurityQuestion[];
}
/**
* SecurityQuestion Interface
*
* Purpose: Defines security question structure
*/
export interface SecurityQuestion {
id: string;
question: string;
answer: string; // Hashed answer
}
// ============================================================================
// EXPORT TYPES
// ============================================================================
export type {
UserProfile,
Address,
EmergencyContact,
ProfileUserRole,
UserPermissions,
UserPreferences,
NotificationPreferences,
ClinicalPreferences,
PrivacyPreferences,
AccessibilityPreferences,
SettingsSection,
SettingsItem,
SettingsSectionData,
ProfileUpdateData,
ProfileUpdateRequest,
ProfileUpdateResponse,
PasswordChangeRequest,
SecuritySettings,
SecurityQuestion,
};
/*
* End of File: settings.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

13
app/shared/utils/api.ts Normal file
View File

@ -0,0 +1,13 @@
interface BuildHeadersParams {
token?: string;
contentType?: string;
}
export const buildHeaders = ({ token, contentType }: BuildHeadersParams = {}) => {
const headers: Record<string, string> = {};
if (token) headers['Authorization'] = `Bearer ${token}`;
if (contentType) headers['Content-Type'] = contentType;
return { headers };
};

View File

@ -0,0 +1,95 @@
/*
* File: constants.ts
* Description: Application constants and configuration values
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import Config from 'react-native-config';
// API Configuration
export const API_CONFIG = {
BASE_URL:Config.BASE_URL,
TIMEOUT: 30000,
RETRY_ATTEMPTS: 3,
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
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

524
app/shared/utils/helpers.ts Normal file
View File

@ -0,0 +1,524 @@
/*
* File: helpers.ts
* Description: General helper functions for common operations
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import { AlertPriority, PatientStatus } from '../types';
// ============================================================================
// ARRAY HELPERS - Functions for array manipulation and processing
// ============================================================================
/**
* chunk Function
*
* Purpose: Split an array into smaller chunks of specified size
*
* @param array - The array to be chunked
* @param size - The size of each chunk
* @returns Array of arrays, each containing up to 'size' elements
*
* Example:
* chunk([1, 2, 3, 4, 5, 6], 2) [[1, 2], [3, 4], [5, 6]]
*
* Use case: Pagination, displaying data in groups
*/
export const chunk = <T>(array: T[], size: number): T[][] => {
const chunks: T[][] = [];
for (let i = 0; i < array.length; i += size) {
chunks.push(array.slice(i, i + size));
}
return chunks;
};
/**
* groupBy Function
*
* Purpose: Group array elements by a specified key function
*
* @param array - The array to be grouped
* @param key - Function that returns the grouping key for each element
* @returns Object with keys as group names and values as arrays of grouped items
*
* Example:
* groupBy(patients, patient => patient.department)
* { 'Emergency': [patient1, patient2], 'ICU': [patient3] }
*
* Use case: Organizing patients by department, status, or priority
*/
export const groupBy = <T, K extends keyof any>(
array: T[],
key: (item: T) => K
): Record<K, T[]> => {
return array.reduce((groups, item) => {
const group = key(item);
if (!groups[group]) {
groups[group] = [];
}
groups[group].push(item);
return groups;
}, {} as Record<K, T[]>);
};
// ============================================================================
// OBJECT HELPERS - Functions for object manipulation and transformation
// ============================================================================
/**
* pick Function
*
* Purpose: Create a new object with only specified properties from the original
*
* @param obj - The source object
* @param keys - Array of property names to include
* @returns New object containing only the specified properties
*
* Example:
* pick({ name: 'John', age: 30, id: 1 }, ['name', 'age'])
* { name: 'John', age: 30 }
*
* Use case: Extracting specific patient data fields
*/
export const pick = <T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> => {
const result = {} as Pick<T, K>;
keys.forEach(key => {
if (key in obj) {
result[key] = obj[key];
}
});
return result;
};
/**
* omit Function
*
* Purpose: Create a new object excluding specified properties from the original
*
* @param obj - The source object
* @param keys - Array of property names to exclude
* @returns New object without the specified properties
*
* Example:
* omit({ name: 'John', age: 30, id: 1 }, ['id'])
* { name: 'John', age: 30 }
*
* Use case: Removing sensitive data before sending to API
*/
export const omit = <T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> => {
const result = { ...obj };
keys.forEach(key => {
delete result[key];
});
return result;
};
// ============================================================================
// STRING HELPERS - Functions for string manipulation and formatting
// ============================================================================
/**
* capitalize Function
*
* Purpose: Capitalize the first letter of a string and lowercase the rest
*
* @param str - The string to capitalize
* @returns Capitalized string
*
* Example:
* capitalize('john doe') 'John doe'
*
* Use case: Formatting patient names, department names
*/
export const capitalize = (str: string): string => {
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
};
/**
* truncate Function
*
* Purpose: Truncate a string to specified length and add ellipsis
*
* @param str - The string to truncate
* @param length - Maximum length before truncation
* @returns Truncated string with ellipsis if needed
*
* Example:
* truncate('Very long diagnosis text', 15) 'Very long diagn...'
*
* Use case: Displaying long text in limited space (diagnoses, notes)
*/
export const truncate = (str: string, length: number): string => {
if (str.length <= length) return str;
return str.slice(0, length) + '...';
};
// ============================================================================
// NUMBER HELPERS - Functions for number manipulation and validation
// ============================================================================
/**
* clamp Function
*
* Purpose: Constrain a number between minimum and maximum values
*
* @param value - The number to clamp
* @param min - Minimum allowed value
* @param max - Maximum allowed value
* @returns Clamped value within the specified range
*
* Example:
* clamp(150, 0, 100) 100
* clamp(-10, 0, 100) 0
*
* Use case: Validating vital signs, age ranges, scores
*/
export const clamp = (value: number, min: number, max: number): number => {
return Math.min(Math.max(value, min), max);
};
/**
* roundToDecimal Function
*
* Purpose: Round a number to specified decimal places
*
* @param value - The number to round
* @param decimals - Number of decimal places
* @returns Rounded number
*
* Example:
* roundToDecimal(3.14159, 2) 3.14
*
* Use case: Formatting vital signs, medication dosages, scores
*/
export const roundToDecimal = (value: number, decimals: number): number => {
return Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals);
};
// ============================================================================
// BOOLEAN HELPERS - Functions for boolean operations and validation
// ============================================================================
/**
* isTruthy Function
*
* Purpose: Check if a value is truthy (converts to true)
*
* @param value - The value to check
* @returns Boolean indicating if value is truthy
*
* Example:
* isTruthy('hello') true
* isTruthy(0) false
*
* Use case: Validating form inputs, checking optional fields
*/
export const isTruthy = (value: any): boolean => {
return Boolean(value);
};
/**
* isFalsy Function
*
* Purpose: Check if a value is falsy (converts to false)
*
* @param value - The value to check
* @returns Boolean indicating if value is falsy
*
* Example:
* isFalsy('') true
* isFalsy(null) true
*
* Use case: Checking for empty or null values
*/
export const isFalsy = (value: any): boolean => {
return !Boolean(value);
};
// ============================================================================
// FUNCTION HELPERS - Functions for function manipulation and optimization
// ============================================================================
/**
* debounce Function
*
* Purpose: Create a debounced version of a function that delays execution
*
* @param func - The function to debounce
* @param delay - Delay in milliseconds
* @returns Debounced function
*
* Example:
* const debouncedSearch = debounce(searchPatients, 300);
* // Only executes search after 300ms of no input
*
* Use case: Search inputs, API calls, form validation
*/
export const debounce = <T extends (...args: any[]) => any>(
func: T,
delay: number
): ((...args: Parameters<T>) => void) => {
let timeoutId: NodeJS.Timeout;
return (...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
};
/**
* throttle Function
*
* Purpose: Create a throttled version of a function that limits execution frequency
*
* @param func - The function to throttle
* @param delay - Minimum time between executions in milliseconds
* @returns Throttled function
*
* Example:
* const throttledScroll = throttle(handleScroll, 100);
* // Only executes once every 100ms maximum
*
* Use case: Scroll events, resize events, real-time updates
*/
export const throttle = <T extends (...args: any[]) => any>(
func: T,
delay: number
): ((...args: Parameters<T>) => void) => {
let lastCall = 0;
return (...args: Parameters<T>) => {
const now = Date.now();
if (now - lastCall >= delay) {
lastCall = now;
func(...args);
}
};
};
// ============================================================================
// MEDICAL HELPERS - Healthcare-specific utility functions
// ============================================================================
/**
* getPriorityColor Function
*
* Purpose: Get the appropriate color for alert priority levels
*
* @param priority - The alert priority level
* @returns Hex color code for the priority
*
* Color Mapping:
* - CRITICAL: Red (#F44336) - Immediate attention required
* - HIGH: Orange (#FF9800) - High priority attention
* - MEDIUM: Blue (#2196F3) - Normal priority
* - LOW: Green (#4CAF50) - Low priority
* - Default: Gray (#9E9E9E) - Unknown priority
*
* Use case: Color-coding alerts, patient cards, status indicators
*/
export const getPriorityColor = (priority: AlertPriority): string => {
switch (priority) {
case 'CRITICAL':
return '#F44336'; // Red for critical alerts
case 'HIGH':
return '#FF9800'; // Orange for high priority
case 'MEDIUM':
return '#2196F3'; // Blue for medium priority
case 'LOW':
return '#4CAF50'; // Green for low priority
default:
return '#9E9E9E'; // Gray for unknown priority
}
};
/**
* getStatusColor Function
*
* Purpose: Get the appropriate color for patient status levels
*
* @param status - The patient status
* @returns Hex color code for the status
*
* Color Mapping:
* - CRITICAL: Red (#F44336) - Critical patient condition
* - ACTIVE: Blue (#2196F3) - Currently under care
* - PENDING: Orange (#FF9800) - Waiting for treatment
* - DISCHARGED: Green (#4CAF50) - Successfully discharged
* - TRANSFERRED: Gray (#9E9E9E) - Transferred to another facility
*
* Use case: Color-coding patient cards, status badges, dashboard indicators
*/
export const getStatusColor = (status: PatientStatus): string => {
switch (status) {
case 'CRITICAL':
return '#F44336'; // Red for critical patients
case 'ACTIVE':
return '#2196F3'; // Blue for active patients
case 'PENDING':
return '#FF9800'; // Orange for pending patients
case 'DISCHARGED':
return '#4CAF50'; // Green for discharged patients
case 'TRANSFERRED':
return '#9E9E9E'; // Gray for transferred patients
default:
return '#9E9E9E'; // Gray for unknown status
}
};
/**
* calculateAge Function
*
* Purpose: Calculate age from date of birth
*
* @param dateOfBirth - The patient's date of birth
* @returns Age in years
*
* Logic:
* - Calculates year difference
* - Adjusts for month and day to get accurate age
* - Handles leap years and month length variations
*
* Example:
* calculateAge(new Date('1990-05-15')) 33 (if current year is 2023)
*
* Use case: Displaying patient age, age-based calculations, demographics
*/
export const calculateAge = (dateOfBirth: Date): number => {
const today = new Date();
let age = today.getFullYear() - dateOfBirth.getFullYear();
const monthDiff = today.getMonth() - dateOfBirth.getMonth();
// Adjust age if birthday hasn't occurred this year
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < dateOfBirth.getDate())) {
age--;
}
return age;
};
// ============================================================================
// VALIDATION HELPERS - Functions for data validation
// ============================================================================
/**
* isValidEmail Function
*
* Purpose: Validate email address format
*
* @param email - The email address to validate
* @returns Boolean indicating if email is valid
*
* Validation Rules:
* - Must contain @ symbol
* - Must have text before and after @
* - Must have domain extension
*
* Use case: Login forms, user registration, contact information
*/
export const isValidEmail = (email: string): boolean => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
/**
* isValidPhone Function
*
* Purpose: Validate phone number format
*
* @param phone - The phone number to validate
* @returns Boolean indicating if phone number is valid
*
* Validation Rules:
* - Minimum 10 digits
* - Allows +, spaces, hyphens, parentheses
* - International format supported
*
* Use case: Contact information, emergency contacts
*/
export const isValidPhone = (phone: string): boolean => {
const phoneRegex = /^\+?[\d\s\-\(\)]{10,}$/;
return phoneRegex.test(phone);
};
/**
* isValidMRN Function
*
* Purpose: Validate Medical Record Number format
*
* @param mrn - The MRN to validate
* @returns Boolean indicating if MRN is valid
*
* Validation Rules:
* - 6-12 characters long
* - Alphanumeric only (A-Z, 0-9)
* - No special characters or spaces
*
* Use case: Patient identification, medical record lookup
*/
export const isValidMRN = (mrn: string): boolean => {
const mrnRegex = /^[A-Z0-9]{6,12}$/;
return mrnRegex.test(mrn);
};
// ============================================================================
// ASYNC HELPERS - Functions for asynchronous operations
// ============================================================================
/**
* sleep Function
*
* Purpose: Create a delay for specified milliseconds
*
* @param ms - Milliseconds to delay
* @returns Promise that resolves after the delay
*
* Example:
* await sleep(1000); // Wait for 1 second
*
* Use case: Rate limiting, loading states, animation delays
*/
export const sleep = (ms: number): Promise<void> => {
return new Promise(resolve => setTimeout(resolve, ms));
};
/**
* retry Function
*
* Purpose: Retry an async function with exponential backoff
*
* @param fn - The async function to retry
* @param retries - Number of retry attempts (default: 3)
* @param delay - Initial delay in milliseconds (default: 1000)
* @returns Promise that resolves with function result or rejects after all retries
*
* Retry Strategy:
* - Exponential backoff (delay doubles after each failure)
* - Stops after specified number of retries
* - Throws error if all retries fail
*
* Example:
* const result = await retry(fetchPatientData, 3, 1000);
*
* Use case: API calls, network requests, database operations
*/
export const retry = async <T>(
fn: () => Promise<T>,
retries: number = 3,
delay: number = 1000
): Promise<T> => {
try {
return await fn();
} catch (error) {
if (retries > 0) {
await sleep(delay);
return retry(fn, retries - 1, delay * 2); // Exponential backoff
}
throw error;
}
};
/*
* End of File: helpers.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

17
app/shared/utils/index.ts Normal file
View File

@ -0,0 +1,17 @@
/*
* File: index.ts
* Description: Shared utility functions exports for the Physician App
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
export * from './api';
export * from './constants';
export * from './helpers';
export * from './validators';
/*
* End of File: index.ts
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

48
app/shared/utils/toast.ts Normal file
View File

@ -0,0 +1,48 @@
/*
* File: Toast.js
* Description: Utility for displaying toast notifications
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/
import Toast from 'react-native-toast-message';
//shows success toast
const showSuccess=(text1='Successfull',text2='')=>{
Toast.show({
type: 'success',
text1,
text2
});
}
//shows error text
const showError=(text1='something went wrong',text2='')=>{
Toast.show({
type: 'error',
text1,
text2
});
}
//shows warning text
const showWarning=(text1='you have some warning',text2='')=>{
Toast.show({
type: 'info',
text1,
text2
});
}
const handleError=()=>{
}
export {showSuccess,showError,showWarning,handleError};
/*
* End of File: Toast.js
* Design & Developed by Tech4Biz Solutions
* Copyright (c) Spurrin Innovations. All rights reserved.
*/

Some files were not shown because too many files have changed in this diff Show More