A modern React Native mobile application built with Expo for real-time patient queue management and online consultations.
- Real-time Queue Tracking: Live position updates and wait time estimates
- Role-based Interface: Separate experiences for patients and doctors
- WebSocket Integration: Instant updates for queue changes and status
- Mobile-first Design: Optimized for iOS and Android devices
- Offline Resilience: Graceful handling of network connectivity issues
- Cross-platform: Single codebase for iOS, Android, and web
- TypeScript: Full type safety and better developer experience
- Queue Status Dashboard: Real-time position and estimated wait time
- Doctor Selection: Choose from available healthcare providers
- Doctor Information: View specializations, fees, and availability
- Queue Management: Complete control over patient consultations
- Availability Toggle: Easy online/offline status management
- Real-time Updates: Instant notifications for queue changes
- Node.js (v16.0.0 or higher)
- Expo CLI (
npm install -g @expo/cli) - iOS Simulator (Mac) or Android Studio (Windows/Mac/Linux)
- Physical device (optional, for testing)
- ngrok (for local development with WebSocket support)
# Navigate to mobile app directory
cd medp-frontend
# Install dependencies
npm install
# Install Expo dependencies
npx expo install-
Install ngrok:
# Using npm npm install -g ngrok # Or download from https://ngrok.com/download
-
Start ngrok tunnel (after starting your backend server):
# If your backend runs on port 3001 ngrok http 3001 -
Copy the generated HTTPS URL (e.g., https://xxxx-xx-xx-xxx-xx.ngrok-free.app)
Edit src\config\config.ts:
export const config = {
development: {
baseURL: "https://your-ngrok-url.ngrok-free.app", // Replace with your ngrok URL
wsURL: "wss://your-ngrok-url.ngrok-free.app",
},
production: {
baseURL: "https://your-production-api.com",
wsURL: "wss://your-production-api.com",
},
};
<!-- **Find Your IP Address:** -->
<!-- ```bash -->
<!-- # Windows
ipconfig | findstr "IPv4"
# Mac/Linux
ifconfig | grep inet -->
<!-- # Example: http://192.168.1.105:3001 -->
<!-- ``` -->
#### Expo Configuration
Update `app.json` if needed:
```json
{
"expo": {
"name": "MedPharma Queue",
"slug": "medp-queue",
"version": "1.0.0",
"platforms": ["ios", "android", "web"],
"ios": {
"bundleIdentifier": "com.yourcompany.medpqueue"
},
"android": {
"package": "com.yourcompany.medpqueue"
}
}
}The project includes a WebSocket testing tool (test-websocket.html) that allows you to:
- Test real-time queue management features
- Simulate patient and doctor interactions
- Monitor WebSocket events
- Debug connection issues
To use the WebSocket tester:
- Open
test-websocket.htmlin your browser - Enter your ngrok URL in the server URL field
- Click "Connect" to establish WebSocket connection
- Use the patient and doctor testing panels to simulate various scenarios
-
Start the backend server first (follow backend README)
-
Start ngrok tunnel:
ngrok http 3001
-
Update config with ngrok URL
-
Start the Expo development server:
- Open
test-websocket.htmlin your browser - Use the testing panels to:
- Simulate patient queue interactions
- Test doctor availability updates
- Monitor real-time events
- Debug connection issues
Key testing scenarios:
- Patient joining/leaving queue
- Doctor toggling availability
- Queue position updates
- Consultation status changes
- Connection error handling
queueUpdate: Position and wait time updatesconsultationStarted: Doctor ready to see patientconsultationCompleted: Consultation finishedpatientRemoved: Removed from queue
patientAdded: New patient joined queuequeueChanged: Queue status changesdoctorAvailabilityUpdate: Availability status changes
The application includes robust error handling for:
- WebSocket connection issues
- Network connectivity problems
- Server errors
- Invalid queue operations
Error recovery strategies:
- Automatic reconnection attempts
- Offline mode support
- Graceful degradation
- User-friendly error messages
- Always test WebSocket features using the provided tester
- Monitor ngrok status for connection issues
- Check browser console for detailed error messages
- Use mock data for testing edge cases
- Remember to update ngrok URL after tunnel restarts
# Start Expo development server
npm start
# Or with specific platform
npm run android # Android emulator/device
npm run ios # iOS simulator (Mac only)
npm run web # Web browser-
Install Expo Go on your mobile device:
-
Scan QR Code from the terminal or Expo DevTools
-
Ensure same network: Device and development computer on same WiFi
# Install Xcode from Mac App Store
# Start iOS simulator
npm run ios
# Or manually open simulator
open -a Simulator# Install Android Studio
# Create and start AVD (Android Virtual Device)
npm run android
# Or start emulator manually
~/Android/Sdk/emulator/emulator -avd YOUR_AVD_NAMERoleSelectionScreen
β
BookingScreen (Patient/Doctor)
β
PatientQueueScreen OR DoctorDashboard
RoleSelectionScreen: Choose patient or doctor roleBookingScreen: Doctor selection and queue joiningPatientQueueScreen: Real-time queue status for patientsDoctorDashboard: Queue management for doctors
QueueContext: Global queue state managementSocketContext: WebSocket connection and events
- Real-time WebSocket communication
- API calls for queue operations
- Local state persistence
src/
βββ screens/ # Main app screens
β βββ RoleSelectionScreen.tsx
β βββ BookingScreen.tsx
β βββ PatientQueueScreen.tsx
β βββ DoctorDashboard.tsx
βββ components/ # Reusable components
β βββ common/ # Shared components
β βββ patient/ # Patient-specific components
β βββ doctor/ # Doctor-specific components
βββ context/ # React Context providers
β βββ QueueContext.tsx
β βββ SocketContext.tsx
βββ hooks/ # Custom React hooks
βββ services/ # API and external services
βββ utils/ # Utility functions
βββ types/ # TypeScript definitions
βββ styles/ # Styling and themes
// src/components/common/NewComponent.tsx
import React from "react";
import { View, Text, StyleSheet } from "react-native";
interface NewComponentProps {
title: string;
onPress: () => void;
}
const NewComponent: React.FC<NewComponentProps> = ({ title, onPress }) => {
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
padding: 16,
backgroundColor: "#ffffff",
borderRadius: 8,
},
title: {
fontSize: 18,
fontWeight: "bold",
color: "#1e293b",
},
});
export default NewComponent;import NewComponent from "../components/common/NewComponent";
// Inside your screen component
<NewComponent title="Hello World" onPress={() => console.log("Pressed!")} />;import { useQueue } from '../context/QueueContext';
const MyComponent = () => {
const { state, addPatientToQueue, getPatientPosition } = useQueue();
const handleAddPatient = () => {
const patientId = addPatientToQueue({
name: 'John Doe',
doctorId: 'doc1',
estimatedDuration: 15,
});
};
return (
// Your component JSX
);
};import { useSocket } from '../context/SocketContext';
const MyComponent = () => {
const { socket, isConnected, joinPatientRoom } = useSocket();
useEffect(() => {
if (isConnected) {
joinPatientRoom(patientId, doctorId);
}
}, [isConnected]);
return (
// Your component JSX
);
};// Use consistent spacing
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20, // Standard padding
backgroundColor: "#f8fafc",
},
card: {
backgroundColor: "#ffffff",
borderRadius: 16, // Rounded corners
padding: 20,
marginBottom: 16, // Consistent spacing
shadowColor: "#000", // Subtle shadows
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 4, // Android shadow
},
title: {
fontSize: 24, // Large titles
fontWeight: "bold",
color: "#1e293b", // Dark text
marginBottom: 8,
},
subtitle: {
fontSize: 16, // Regular text
color: "#64748b", // Muted text
lineHeight: 22,
},
});// Define colors for consistency
const colors = {
primary: "#3b82f6", // Blue
success: "#10b981", // Green
warning: "#f59e0b", // Orange
error: "#ef4444", // Red
text: {
primary: "#1e293b", // Dark gray
secondary: "#64748b", // Medium gray
muted: "#9ca3af", // Light gray
},
background: {
primary: "#ffffff", // White
secondary: "#f8fafc", // Light gray
accent: "#eff6ff", // Light blue
},
};# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage// __tests__/components/PatientCard.test.tsx
import React from "react";
import { render, fireEvent } from "@testing-library/react-native";
import PatientCard from "../src/components/doctor/PatientCard";
describe("PatientCard", () => {
const mockPatient = {
id: "123",
name: "John Doe",
status: "waiting",
joinedAt: new Date(),
};
it("renders patient information correctly", () => {
const { getByText } = render(
<PatientCard patient={mockPatient} onStatusChange={() => {}} />
);
expect(getByText("John Doe")).toBeTruthy();
expect(getByText("waiting")).toBeTruthy();
});
it("calls onStatusChange when button pressed", () => {
const mockOnStatusChange = jest.fn();
const { getByText } = render(
<PatientCard patient={mockPatient} onStatusChange={mockOnStatusChange} />
);
fireEvent.press(getByText("Start Consultation"));
expect(mockOnStatusChange).toHaveBeenCalledWith("123", "consulting");
});
});-
Patient Flow Testing
- Select patient role
- Choose doctor and join queue
- Verify real-time position updates
- Test status transitions
-
Doctor Flow Testing
- Select doctor role
- Access dashboard
- Manage patient queue
- Test availability toggle
-
WebSocket Testing
- Test connection/disconnection
- Verify real-time updates
- Test network interruption recovery
# Create development build
eas build --profile development --platform all
# Create development build for specific platform
eas build --profile development --platform android
eas build --profile development --platform ios# Install EAS CLI
npm install -g @expo/eas-cli
# Login to Expo
eas login
# Configure build
eas build:configure# Build for both platforms
eas build --platform all
# Build for specific platform
eas build --platform android # Google Play Store
eas build --platform ios # Apple App Store# Submit to stores
eas submit --platform all
# Submit to specific store
eas submit --platform android # Google Play
eas submit --platform ios # Apple App Store# Create update
eas update --branch production --message "Bug fixes and improvements"
# Create update for specific channel
eas update --channel preview --message "Preview features"// app.config.js
export default {
expo: {
name: "MedPharma Queue",
slug: "medp-queue",
extra: {
apiUrl: process.env.API_URL || "http://localhost:3001", //or ngrok link
socketUrl: process.env.SOCKET_URL || "http://localhost:3001",
},
},
};
// Access in app
import Constants from "expo-constants";
const apiUrl = Constants.expoConfig?.extra?.apiUrl;// Platform-specific code
import { Platform } from "react-native";
const styles = StyleSheet.create({
container: {
paddingTop: Platform.OS === "ios" ? 44 : 20, // iOS status bar
},
});
// Platform-specific components
const HeaderComponent = Platform.select({
ios: () => <IOSHeader />,
android: () => <AndroidHeader />,
default: () => <DefaultHeader />,
});# Enable React Native debugger
# Install React Native Debugger app
# Shake device or Cmd+D (iOS) / Cmd+M (Android)
# Enable remote debugging
# Access: http://localhost:8081/debugger-ui
# Flipper integration (advanced)
# Install Flipper desktop app
# Enable Flipper in your appMetro bundler cache issues:
# Clear Expo cache
expo start --clear
# Clear npm cache
npm start -- --reset-cacheiOS Simulator issues:
# Reset iOS Simulator
Device β Erase All Content and Settings
# Rebuild app
rm -rf node_modules
npm install
npm run iosAndroid emulator issues:
# Wipe emulator data
# In Android Studio: AVD Manager β Actions β Wipe Data
# Restart ADB
adb kill-server
adb start-serverWebSocket connection issues:
- Verify backend server is running
- Check IP address in src\config\config.ts
- Ensure firewall allows connections
- Test with device on same network
-
Image Optimization
// Use optimized image component import { Image } from "expo-image"; <Image source={{ uri: "https://example.com/image.jpg" }} style={{ width: 200, height: 200 }} placeholder="Loading..." contentFit="cover" transition={1000} />;
-
List Performance
// Use FlatList for large datasets <FlatList data={patients} renderItem={({ item }) => <PatientCard patient={item} />} keyExtractor={(item) => item.id} removeClippedSubviews={true} maxToRenderPerBatch={10} updateCellsBatchingPeriod={50} windowSize={10} />
-
State Management
// Use React.memo to prevent unnecessary re-renders const PatientCard = React.memo<PatientCardProps>(({ patient }) => { return ( // Component JSX ); }); // Use useCallback for event handlers const handleStatusUpdate = useCallback((patientId: string, status: string) => { updatePatientStatus(patientId, status); }, [updatePatientStatus]);
-
Physical Device
- Enable Developer Mode in Settings
- Trust computer in device settings
- Run:
npm run ios --device
-
TestFlight (Beta Testing)
- Upload build to App Store Connect
- Add beta testers
- Send TestFlight invitations
-
Physical Device
- Enable Developer Options
- Enable USB Debugging
- Run:
npm run android --device
-
Google Play Console (Beta Testing)
- Upload AAB to Play Console
- Create internal/closed testing track
- Add beta testers
- Expo Documentation: https://docs.expo.dev/
- React Navigation: https://reactnavigation.org/
- React Native: https://reactnative.dev/
- Socket.io Client: https://socket.io/docs/v4/client-api/
App won't start:
- Clear cache:
expo start --clear - Restart Metro:
npx react-native start --reset-cache - Reinstall dependencies:
rm -rf node_modules && npm install
Real-time updates not working:
- Check backend server is running
- Verify WebSocket URL in SocketContext
- Test network connectivity
- Check console for connection errors
Build failures:
- Check Expo CLI version:
expo --version - Update dependencies:
npx expo install --fix - Check platform compatibility
- Review error logs in EAS Build
This project is licensed under the MIT License - see the LICENSE file for details.