patient api integrated dashboard ui created

This commit is contained in:
yashwin-foxy 2025-07-22 17:36:29 +05:30
parent 1e2e4eba47
commit 4ff28e061b
35 changed files with 1067 additions and 89 deletions

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

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

View File

@ -9,11 +9,16 @@ import React from 'react';
import { createStackNavigator } from '@react-navigation/stack'; import { createStackNavigator } from '@react-navigation/stack';
import { DashboardScreen } from '../screens'; import { DashboardScreen } from '../screens';
import { Colors } from '../../../../shared/src/theme'; import { Colors } from '../../../../shared/src/theme';
import DashBoardDetail from '../screens/DashBoardDetail';
export type DashboardStackParamList = { export type DashboardStackParamList = {
Dashboard: undefined; DashboardScreen: undefined;
// Add more screens here as needed DashboardDetailScreen:{
caseType: 'Critical' | 'Routine' | 'Emergency';
}; };
// Add more screens here as needed
}
const Stack = createStackNavigator<DashboardStackParamList>(); const Stack = createStackNavigator<DashboardStackParamList>();
@ -22,15 +27,20 @@ const Stack = createStackNavigator<DashboardStackParamList>();
*/ */
const DashboardNavigator: React.FC = () => ( const DashboardNavigator: React.FC = () => (
<Stack.Navigator <Stack.Navigator
initialRouteName="Dashboard" initialRouteName="DashboardScreen"
screenOptions={{ screenOptions={{
headerStyle: { backgroundColor: Colors.primary }, headerStyle: { backgroundColor: Colors.primary },
}} }}
> >
<Stack.Screen <Stack.Screen
name="Dashboard" name="DashboardScreen"
component={DashboardScreen} component={DashboardScreen}
options={{ title: 'Dashboard' ,headerShown:false}} options={{ title: 'Dashboard' ,headerShown:false}}
/>
<Stack.Screen
name="DashboardDetailScreen"
component={DashBoardDetail}
options={{ title: 'Dashboard' ,headerShown:false}}
/> />
{/* Add more screens here */} {/* Add more screens here */}
</Stack.Navigator> </Stack.Navigator>

View File

@ -6,7 +6,7 @@
*/ */
import { createAsyncThunk } from '@reduxjs/toolkit'; import { createAsyncThunk } from '@reduxjs/toolkit';
import { fetchCasesStart, fetchCasesSuccess, fetchCasesFailure } from './dashboardSlice'; import { fetchCasesStart, fetchCasesSuccess, fetchCasesFailure, fetchPatientsSuccess } from './dashboardSlice';
import { caseAPI } from '../services/caseAPI'; import { caseAPI } from '../services/caseAPI';
/** /**
@ -31,6 +31,23 @@ export const fetchCases = createAsyncThunk(
} }
); );
export const fetchPatients = createAsyncThunk(
'dashboard/fetchPatients',
async (payload, { dispatch, rejectWithValue }) => {
try {
dispatch(fetchCasesStart());
const response :any = await caseAPI.getPatients(payload);
console.log('response i got',response)
if (response.ok && response.data && response.data.data) {
dispatch(fetchPatientsSuccess(response.data.data));
}
} catch (error: any) {
dispatch(fetchCasesFailure(error.message));
return rejectWithValue(error.message);
}
}
);
/* /*
* End of File: dashboardActions.ts * End of File: dashboardActions.ts
* Design & Developed by Tech4Biz Solutions * Design & Developed by Tech4Biz Solutions

View File

@ -10,6 +10,8 @@ import { RootState } from '../../../../app/redux/rootReducer';
export const selectCaseQueue = (state: RootState) => state.dashboard.caseQueue; export const selectCaseQueue = (state: RootState) => state.dashboard.caseQueue;
export const selectDashboardLoading = (state: RootState) => state.dashboard.loading; export const selectDashboardLoading = (state: RootState) => state.dashboard.loading;
export const selectDashboardError = (state: RootState) => state.dashboard.error; export const selectDashboardError = (state: RootState) => state.dashboard.error;
export const selectPatients = (state: RootState) => state.dashboard.patientData;
/* /*
* End of File: dashboardSelectors.ts * End of File: dashboardSelectors.ts

View File

@ -21,12 +21,14 @@ interface DashboardState {
caseQueue: Case[]; caseQueue: Case[];
loading: boolean; loading: boolean;
error: string | null; error: string | null;
patientData:[]
} }
const initialState: DashboardState = { const initialState: DashboardState = {
caseQueue: [], caseQueue: [],
loading: false, loading: false,
error: null, error: null,
patientData:[]
}; };
/** /**
@ -51,6 +53,23 @@ const dashboardSlice = createSlice({
clearCases(state) { clearCases(state) {
state.caseQueue = []; state.caseQueue = [];
}, },
fetchPatientsSuccess(state, action: PayloadAction<[]>) {
const caseTypes: ('Critical' | 'Emergency' | 'Routine')[] = ['Critical', 'Emergency', 'Routine'];
// Function to get random case type
const getRandomCaseType = (): 'Critical' | 'Emergency' | 'Routine' => {
return caseTypes[Math.floor(Math.random() * caseTypes.length)];
};
// Add type attribute to each patient
const patientsWithType :any = action.payload.map((patient: any) => ({
...patient,
type: getRandomCaseType()
}));
state.loading = false;
state.patientData = patientsWithType;
}
}, },
}); });
@ -59,6 +78,7 @@ export const {
fetchCasesSuccess, fetchCasesSuccess,
fetchCasesFailure, fetchCasesFailure,
clearCases, clearCases,
fetchPatientsSuccess
} = dashboardSlice.actions; } = dashboardSlice.actions;
export default dashboardSlice.reducer; export default dashboardSlice.reducer;

View File

@ -5,22 +5,356 @@
* Copyright (c) Spurrin Innovations. All rights reserved. * Copyright (c) Spurrin Innovations. All rights reserved.
*/ */
import React, { useState } from 'react'; import React, { useState, useMemo } from 'react';
import { View, Text, StyleSheet, ScrollView } from 'react-native'; import { View, Text, StyleSheet, ScrollView, TouchableOpacity } from 'react-native';
import { Button } from '../../../../shared/src/components/Button';
import { Card, InfoCard } from '../../../../shared/src/components/Card';
import { SearchInput } from '../../../../shared/src/components/Input';
import { Modal } from '../../../../shared/src/components/Modal';
import { CustomIcon, IconButton } from '../../../../shared/src/components/Icons';
import { CustomHeader } from '../../../../shared/src/components/Header';
import { Colors, Spacing, Typography } from '../../../../shared/src/theme'; import { Colors, Spacing, Typography } from '../../../../shared/src/theme';
import { useSelector } from 'react-redux';
import { selectPatients } from '../redux';
interface PatientDetails {
Date: string;
Name: string;
PatID: string;
PatAge: string;
PatSex: string;
Status: string;
InstName: string;
Modality: 'DX' | 'CT' | 'MR';
ReportStatus: string | null;
}
interface Series {
Path: string[];
SerDes: string;
ViePos: string | null;
pngpath: string;
SeriesNum: string;
ImgTotalinSeries: string;
}
export interface MedicalCase {
id: number;
patientdetails: PatientDetails;
series: Series[];
created_at: string;
updated_at: string;
series_id: string | null;
type: 'Critical' | 'Routine' | 'Emergency';
}
interface DashBoardDetailProps {
navigation: any;
route: {
params: {
caseType: 'Critical' | 'Routine' | 'Emergency';
};
};
}
/** /**
* DashboardScreen demonstrates usage of all shared, themed components. * DashBoardDetail - Shows detailed list of patients for a specific case type
* This is a reference implementation for other modules.
*/ */
const DashBoardDetail: React.FC = () => { const DashBoardDetail: React.FC<DashBoardDetailProps> = ({ navigation, route }) => {
const [search, setSearch] = useState('');
const [selectedPatient, setSelectedPatient] = useState<MedicalCase | null>(null);
const [modalVisible, setModalVisible] = useState(false);
const [sortBy, setSortBy] = useState<'date' | 'name' | 'age'>('date');
const caseType = route.params?.caseType || 'Critical';
const patientData: MedicalCase[] = useSelector(selectPatients) || [];
// Filter patients by case type and search
const filteredPatients = useMemo(() => {
let filtered = patientData.filter(case_ => case_.type === caseType);
if (search.trim()) {
filtered = filtered.filter(case_ =>
case_.patientdetails.Name.toLowerCase().includes(search.toLowerCase()) ||
case_.patientdetails.PatID.toLowerCase().includes(search.toLowerCase()) ||
case_.patientdetails.InstName.toLowerCase().includes(search.toLowerCase())
);
}
// Sort patients
return filtered.sort((a, b) => {
switch (sortBy) {
case 'name':
return a.patientdetails.Name.localeCompare(b.patientdetails.Name);
case 'age':
return parseInt(b.patientdetails.PatAge) - parseInt(a.patientdetails.PatAge);
case 'date':
default:
return new Date(b.created_at).getTime() - new Date(a.created_at).getTime();
}
});
}, [patientData, caseType, search, sortBy]);
const getCaseTypeConfig = (type: string) => {
switch (type) {
case 'Critical':
return {
color: Colors.error,
icon: 'alert-circle',
bgColor: '#FFF5F5' // Light red background
};
case 'Emergency':
return {
color: '#FF8C00',
icon: 'alert',
bgColor: '#FFF8E1' // Light orange background
};
case 'Routine':
return {
color: Colors.success,
icon: 'check-circle',
bgColor: '#F0FFF4' // Light green background
};
default:
return {
color: Colors.primary,
icon: 'info',
bgColor: Colors.background
};
}
};
const typeConfig = getCaseTypeConfig(caseType);
const handlePatientPress = (patient: MedicalCase) => {
setSelectedPatient(patient);
setModalVisible(true);
};
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
const getModalityColor = (modality: string) => {
switch (modality) {
case 'CT': return '#4A90E2';
case 'MR': return '#7B68EE';
case 'DX': return '#50C878';
default: return Colors.textSecondary;
}
};
const renderPatientCard = (patient: MedicalCase) => (
<TouchableOpacity
key={patient.id}
onPress={() => handlePatientPress(patient)}
activeOpacity={0.7}
>
<Card style={[styles.patientCard, { borderLeftColor: typeConfig.color }]}>
<View style={styles.cardHeader}>
<View style={styles.patientInfo}>
<Text style={styles.patientName}>
{patient.patientdetails.Name}
</Text>
<Text style={styles.patientId}>
ID: {patient.patientdetails.PatID}
</Text>
</View>
<View style={styles.urgencyBadge}>
<CustomIcon
name={typeConfig.icon}
color={typeConfig.color}
size={16}
/>
</View>
</View>
<View style={styles.cardBody}>
<View style={styles.infoRow}>
<View style={styles.infoItem}>
<Text style={styles.infoLabel}>Age</Text>
<Text style={styles.infoValue}>{patient.patientdetails.PatAge}</Text>
</View>
<View style={styles.infoItem}>
<Text style={styles.infoLabel}>Sex</Text>
<Text style={styles.infoValue}>{patient.patientdetails.PatSex}</Text>
</View>
<View style={styles.infoItem}>
<Text style={styles.infoLabel}>Modality</Text>
<Text style={[styles.infoValue, styles.modalityText, {
color: getModalityColor(patient.patientdetails.Modality?.substring(1))
}]}>
{patient.patientdetails.Modality?.substring(1)}
</Text>
</View>
</View>
<View style={styles.institutionRow}>
<CustomIcon name="hospital-building" color={Colors.textPrimary} size={14} />
<Text style={styles.institutionText}>
{patient.patientdetails.InstName}
</Text>
</View>
<View style={styles.bottomRow}>
<Text style={styles.dateText}>
{formatDate(patient.created_at)}
</Text>
<Text style={[styles.statusText, {
color: patient.patientdetails.Status === 'Active' ? Colors.success : Colors.textSecondary
}]}>
{patient.patientdetails.Status}
</Text>
</View>
</View>
<View style={styles.cardFooter}>
<Text style={styles.seriesText}>
{patient.series.length} Series Available
</Text>
<IconButton
name="chevron-right"
color={typeConfig.color}
size={20}
onPress={() => handlePatientPress(patient)}
/>
</View>
</Card>
</TouchableOpacity>
);
return ( return (
<ScrollView style={styles.container} contentContainerStyle={styles.content}> <View style={styles.container}>
<Text style={{color:'#FFFFFF'}} >Dashboard Detail screen</Text> <CustomHeader
title={`${caseType} Cases`}
showBackButton={true}
// onBackPress={() => navigation.goBack()}
/>
<ScrollView contentContainerStyle={styles.content}>
{/* Summary Card */}
<InfoCard style={[styles.summaryCard, { backgroundColor: typeConfig.bgColor }]}>
<View style={styles.summaryHeader}>
<CustomIcon name={typeConfig.icon} color={typeConfig.color} size={28} />
<View style={styles.summaryText}>
<Text style={styles.summaryTitle}>
{caseType} Cases
</Text>
<Text style={styles.summaryCount}>
{filteredPatients.length} Patient{filteredPatients.length !== 1 ? 's' : ''}
</Text>
</View>
</View>
</InfoCard>
{/* Search and Sort */}
<View style={styles.controlsContainer}>
<SearchInput
placeholder={`Search ${caseType.toLowerCase()} cases...`}
value={search}
onChangeText={setSearch}
containerStyle={styles.searchInput}
/>
<View style={styles.sortContainer}>
<Text style={styles.sortLabel}>Sort by:</Text>
<View style={styles.sortButtons}>
{(['date', 'name', 'age'] as const).map(option => (
<TouchableOpacity
key={option}
style={[
styles.sortButton,
sortBy === option && { backgroundColor: typeConfig.color }
]}
onPress={() => setSortBy(option)}
>
<Text style={[
styles.sortButtonText,
sortBy === option && { color: '#FFFFFF' }
]}>
{option.charAt(0).toUpperCase() + option.slice(1)}
</Text>
</TouchableOpacity>
))}
</View>
</View>
</View>
{/* Patient List */}
{filteredPatients.length > 0 ? (
filteredPatients.map(renderPatientCard)
) : (
<Card style={styles.emptyCard}>
<CustomIcon name="inbox" color={Colors.textSecondary} size={48} />
<Text style={styles.emptyTitle}>
No {caseType.toLowerCase()} cases found
</Text>
<Text style={styles.emptySubtitle}>
{search ? 'Try adjusting your search terms' : 'All clear for now!'}
</Text>
</Card>
)}
{/* Patient Detail Modal */}
<Modal
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}
>
{selectedPatient && (
<View style={styles.modalContent}>
<Text style={styles.modalTitle}>Patient Details</Text>
<View style={styles.modalSection}>
<Text style={styles.modalSectionTitle}>Basic Information</Text>
<Text style={styles.modalText}>Name: {selectedPatient.patientdetails.Name}</Text>
<Text style={styles.modalText}>ID: {selectedPatient.patientdetails.PatID}</Text>
<Text style={styles.modalText}>Age: {selectedPatient.patientdetails.PatAge}</Text>
<Text style={styles.modalText}>Sex: {selectedPatient.patientdetails.PatSex}</Text>
<Text style={styles.modalText}>Status: {selectedPatient.patientdetails.Status}</Text>
</View>
<View style={styles.modalSection}>
<Text style={styles.modalSectionTitle}>Medical Information</Text>
<Text style={styles.modalText}>Modality: {selectedPatient.patientdetails.Modality}</Text>
<Text style={styles.modalText}>Institution: {selectedPatient.patientdetails.InstName}</Text>
<Text style={styles.modalText}>Report Status: {selectedPatient.patientdetails.ReportStatus || 'Pending'}</Text>
<Text style={styles.modalText}>Case Type: {selectedPatient.type}</Text>
</View>
<View style={styles.modalSection}>
<Text style={styles.modalSectionTitle}>Series Information</Text>
<Text style={styles.modalText}>Available Series: {selectedPatient.series.length}</Text>
<Text style={styles.modalText}>Created: {formatDate(selectedPatient.created_at)}</Text>
<Text style={styles.modalText}>Updated: {formatDate(selectedPatient.updated_at)}</Text>
</View>
<View style={styles.modalButtons}>
<Button
title="View Images"
onPress={() => {
// Navigate to image viewer
setModalVisible(false);
// navigation.navigate('ImageViewer', { patientId: selectedPatient.id });
}}
style={[styles.modalButton, { backgroundColor: typeConfig.color }]}
/>
<Button
title="Close"
onPress={() => setModalVisible(false)}
style={styles.modalButton}
/>
</View>
</View>
)}
</Modal>
</ScrollView> </ScrollView>
</View>
); );
}; };
@ -31,8 +365,206 @@ const styles = StyleSheet.create({
}, },
content: { content: {
padding: Spacing.lg, padding: Spacing.lg,
flex:1 },
} summaryCard: {
marginBottom: Spacing.lg,
},
summaryHeader: {
flexDirection: 'row',
alignItems: 'center',
},
summaryText: {
marginLeft: Spacing.md,
flex: 1,
},
summaryTitle: {
fontFamily: Typography.fontFamily.bold,
fontSize: Typography.fontSize.lg,
color: Colors.textPrimary,
},
summaryCount: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.md,
color: Colors.textSecondary,
marginTop: Spacing.xs,
},
controlsContainer: {
marginBottom: Spacing.lg,
},
searchInput: {
marginBottom: Spacing.md,
},
sortContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
sortLabel: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.sm,
color: Colors.textSecondary,
},
sortButtons: {
flexDirection: 'row',
},
sortButton: {
paddingHorizontal: Spacing.sm,
paddingVertical: Spacing.xs,
backgroundColor: Colors.cardBackground,
borderRadius: 16,
marginLeft: Spacing.xs,
},
sortButtonText: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.sm,
color: Colors.textSecondary,
},
patientCard: {
marginBottom: Spacing.md,
borderLeftWidth: 4,
},
cardHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginBottom: Spacing.sm,
},
patientInfo: {
flex: 1,
},
patientName: {
fontFamily: Typography.fontFamily.bold,
fontSize: Typography.fontSize.md,
color: Colors.textPrimary,
},
patientId: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.sm,
color: Colors.textSecondary,
marginTop: Spacing.xs,
},
urgencyBadge: {
padding: Spacing.xs,
borderRadius: 12,
backgroundColor: 'rgba(0,0,0,0.05)',
},
cardBody: {
marginBottom: Spacing.sm,
},
infoRow: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: Spacing.sm,
},
infoItem: {
flex: 1,
alignItems: 'center',
},
infoLabel: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.xs,
color: Colors.textSecondary,
marginBottom: Spacing.xs,
},
infoValue: {
fontFamily: Typography.fontFamily.bold,
fontSize: Typography.fontSize.sm,
color: Colors.textPrimary,
},
modalityText: {
fontWeight: 'bold',
},
institutionRow: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: Spacing.sm,
},
institutionText: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.sm,
color: Colors.textSecondary,
marginLeft: Spacing.xs,
flex: 1,
},
bottomRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
dateText: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.xs,
color: Colors.textSecondary,
},
statusText: {
fontFamily: Typography.fontFamily.bold,
fontSize: Typography.fontSize.xs,
},
cardFooter: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingTop: Spacing.sm,
borderTopWidth: 1,
borderTopColor: '#E5E5E5',
},
seriesText: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.sm,
color: Colors.textSecondary,
},
emptyCard: {
alignItems: 'center',
padding: Spacing.xl,
marginTop: Spacing.xl,
},
emptyTitle: {
fontFamily: Typography.fontFamily.bold,
fontSize: Typography.fontSize.lg,
color: Colors.textPrimary,
marginTop: Spacing.md,
textAlign: 'center',
},
emptySubtitle: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.md,
color: Colors.textSecondary,
marginTop: Spacing.xs,
textAlign: 'center',
},
modalContent: {
maxHeight: '80%',
},
modalTitle: {
fontFamily: Typography.fontFamily.bold,
fontSize: Typography.fontSize.lg,
color: Colors.primary,
marginBottom: Spacing.lg,
textAlign: 'center',
},
modalSection: {
marginBottom: Spacing.md,
},
modalSectionTitle: {
fontFamily: Typography.fontFamily.bold,
fontSize: Typography.fontSize.md,
color: Colors.textPrimary,
marginBottom: Spacing.sm,
},
modalText: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.sm,
color: Colors.textPrimary,
marginBottom: Spacing.xs,
},
modalButtons: {
flexDirection: 'row',
justifyContent: 'space-around',
marginTop: Spacing.lg,
},
modalButton: {
flex: 0.4,
},
}); });
export default DashBoardDetail; export default DashBoardDetail;

View File

@ -5,7 +5,7 @@
* Copyright (c) Spurrin Innovations. All rights reserved. * Copyright (c) Spurrin Innovations. All rights reserved.
*/ */
import React, { useState } from 'react'; import React, { useEffect, useState, useMemo } from 'react';
import { View, Text, StyleSheet, ScrollView } from 'react-native'; import { View, Text, StyleSheet, ScrollView } from 'react-native';
import { Button } from '../../../../shared/src/components/Button'; import { Button } from '../../../../shared/src/components/Button';
import { Card, InfoCard } from '../../../../shared/src/components/Card'; import { Card, InfoCard } from '../../../../shared/src/components/Card';
@ -13,8 +13,50 @@ import { TextInput, SearchInput } from '../../../../shared/src/components/Input'
import { Modal, ConfirmModal, AlertModal } from '../../../../shared/src/components/Modal'; import { Modal, ConfirmModal, AlertModal } from '../../../../shared/src/components/Modal';
import { Spinner, LoadingOverlay } from '../../../../shared/src/components/Loading'; import { Spinner, LoadingOverlay } from '../../../../shared/src/components/Loading';
import { CustomIcon, IconButton } from '../../../../shared/src/components/Icons'; import { CustomIcon, IconButton } from '../../../../shared/src/components/Icons';
import { CustomHeader } from '../../../../shared/src/components/Header'; // Import CustomHeader import { CustomHeader } from '../../../../shared/src/components/Header';
import { Colors, Spacing, Typography } from '../../../../shared/src/theme'; import { Colors, Spacing, Typography } from '../../../../shared/src/theme';
import { useDispatch, useSelector } from 'react-redux';
import { fetchPatients, selectPatients } from '../redux';
interface PatientDetails {
Date: string;
Name: string;
PatID: string;
PatAge: string;
PatSex: string;
Status: string;
InstName: string;
Modality: 'DX' | 'CT' | 'MR';
ReportStatus: string | null;
}
interface Series {
Path: string[];
SerDes: string;
ViePos: string | null;
pngpath: string;
SeriesNum: string;
ImgTotalinSeries: string;
}
export interface MedicalCase {
id: number;
patientdetails: PatientDetails;
series: Series[];
created_at: string;
updated_at: string;
series_id: string | null;
type: 'Critical' | 'Routine' | 'Emergency';
}
interface CaseStats {
total: number;
modalityCounts: {
DX: number;
CT: number;
MR: number;
};
}
/** /**
* DashboardScreen - The primary landing screen after login, featuring a custom header, * DashboardScreen - The primary landing screen after login, featuring a custom header,
@ -27,6 +69,125 @@ const DashboardScreen: React.FC<{ navigation: any }> = ({ navigation }) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const [input, setInput] = useState(''); const [input, setInput] = useState('');
const [selectedCase, setSelectedCase] = useState<MedicalCase | null>(null);
const dispatch = useDispatch();
const { access_token, display_name } = useSelector((state: any) => state.auth.user);
const patientData: MedicalCase[] = useSelector(selectPatients) || [];
useEffect(() => {
//@ts-ignore
dispatch(fetchPatients(access_token));
}, []);
// Filter cases based on search
const filteredCases = useMemo(() => {
if (!search.trim()) return patientData;
return patientData.filter(case_ =>
case_.patientdetails.Name.toLowerCase().includes(search.toLowerCase()) ||
case_.patientdetails.PatID.toLowerCase().includes(search.toLowerCase()) ||
case_.patientdetails.InstName.toLowerCase().includes(search.toLowerCase())
);
}, [patientData, search]);
// Calculate statistics for each case type
const caseStats = useMemo(() => {
const stats: Record<string, CaseStats> = {
Critical: { total: 0, modalityCounts: { DX: 0, CT: 0, MR: 0 } },
Routine: { total: 0, modalityCounts: { DX: 0, CT: 0, MR: 0 } },
Emergency: { total: 0, modalityCounts: { DX: 0, CT: 0, MR: 0 } }
};
filteredCases.forEach(case_ => {
const type = case_.type;
const modality = case_.patientdetails.Modality;
stats[type].total += 1;
stats[type].modalityCounts[modality?.substring(1)] += 1;
});
return stats;
}, [filteredCases]);
const getCaseIcon = (type: string) => {
switch (type) {
case 'Critical':
return { name: 'alert', color: Colors.error };
case 'Emergency':
return { name: 'alert', color: '#FF8C00' }; // Orange color for emergency
case 'Routine':
return { name: 'check-circle', color: Colors.success };
default:
return { name: 'info', color: Colors.primary };
}
};
const getCaseColor = (type: string) => {
switch (type) {
case 'Critical':
return Colors.error;
case 'Emergency':
return '#FF8C00'; // Orange
case 'Routine':
return Colors.success;
default:
return Colors.textSecondary;
}
};
const handleCasePress = (type: string) => {
const casesOfType = filteredCases.filter(case_ => case_.type === type);
if (casesOfType.length > 0) {
navigation.navigate('DashboardDetailScreen',{caseType:type})
// setSelectedCase(casesOfType[0]);
// setModalVisible(true);
}
};
const renderCaseCard = (type: 'Critical' | 'Routine' | 'Emergency') => {
const stats = caseStats[type];
const icon = getCaseIcon(type);
const color = getCaseColor(type);
if (stats.total === 0) return null; // Don't render card if no cases of this type
console.log('counts data',stats.modalityCounts)
return (
<Card key={type} style={styles.caseCard}>
<View style={styles.cardHeader}>
<View style={styles.titleRow}>
<CustomIcon name={icon.name} color={icon.color} size={24} />
<Text style={styles.cardTitle}>{type} Cases</Text>
</View>
<Text style={[styles.totalCount, { color }]}>
{stats.total} Total
</Text>
</View>
<View style={styles.modalityContainer}>
<Text style={styles.modalityLabel}>By Modality:</Text>
<View style={styles.modalityRow}>
{(['DX', 'CT', 'MR'] as const).map(modality => (
<View key={modality} style={styles.modalityItem}>
<Text style={styles.modalityType}>{modality}</Text>
<Text style={[styles.modalityCount, { color }]}>
{stats.modalityCounts[modality]}
</Text>
</View>
))}
</View>
</View>
<Button
title={`View ${type} Cases`}
onPress={() => handleCasePress(type)}
style={[styles.button, { backgroundColor: color }]}
/>
</Card>
);
};
console.log('patients data', patientData);
return ( return (
<View style={styles.container}> <View style={styles.container}>
@ -35,70 +196,90 @@ const DashboardScreen: React.FC<{ navigation: any }> = ({ navigation }) => {
showBackButton={false} showBackButton={false}
/> />
<ScrollView contentContainerStyle={styles.content}> <ScrollView contentContainerStyle={styles.content}>
<SearchInput {/* <SearchInput
placeholder="Search cases..." placeholder="Search cases..."
value={search} value={search}
onChangeText={setSearch} onChangeText={setSearch}
containerStyle={styles.search} containerStyle={styles.search}
/> /> */}
<InfoCard> <InfoCard>
<Text style={styles.infoTitle}>Welcome, Dr. Smith</Text> <Text style={styles.infoTitle}>Welcome, Dr. {display_name}</Text>
<Text style={styles.infoText}> <Text style={styles.infoText}>
On-Call Status: <Text style={styles.active}>ACTIVE</Text> On-Call Status: <Text style={styles.active}>ACTIVE</Text>
</Text> </Text>
<Text style={styles.infoText}>
Total Cases: <Text style={styles.active}>{filteredCases.length}</Text>
</Text>
</InfoCard> </InfoCard>
{/* Dynamic Case Cards */}
{(['Critical', 'Emergency', 'Routine'] as const).map(type =>
renderCaseCard(type)
)}
{filteredCases.length === 0 && (
<Card> <Card>
<Text style={styles.cardTitle}>Critical Cases</Text> <Text style={styles.noDataText}>
<View style={styles.row}> {search ? 'No cases found matching your search.' : 'No cases available.'}
<CustomIcon name="alert" color={Colors.error} size={24} /> </Text>
<Text style={styles.criticalText}>Bed 3 - Hemorrhage (93% AI)</Text>
<Button
title="Review Now"
onPress={() => setModalVisible(true)}
style={styles.button}
/>
</View>
</Card>
<Card>
<Text style={styles.cardTitle}>Routine Cases</Text>
<View style={styles.row}>
<CustomIcon name="check-circle" color={Colors.success} size={24} />
<Text style={styles.routineText}>Bed 12 - Headache (99% AI)</Text>
<IconButton
name="chevron-right"
onPress={() => setConfirmVisible(true)}
/>
</View>
</Card> </Card>
)}
<TextInput <TextInput
placeholder="Add note..." placeholder="Add note..."
value={input} value={input}
onChangeText={setInput} onChangeText={setInput}
style={styles.input} style={styles.input}
/> />
<Button
title="Show Alert"
onPress={() => setAlertVisible(true)}
style={styles.button}
/>
<Button
title={loading ? 'Loading...' : 'Show Loading Overlay'}
onPress={() => setLoading(true)}
style={styles.button}
/>
{/* <Spinner style={styles.spinner} /> */}
{/* Modals */} {/* Modals */}
<Modal <Modal
visible={modalVisible} visible={modalVisible}
onRequestClose={() => setModalVisible(false)}> onRequestClose={() => setModalVisible(false)}>
<Text style={styles.modalTitle}>Critical Case Details</Text> {selectedCase && (
<Text>Patient: John Doe, 45M</Text> <>
<Text style={styles.modalTitle}>
{selectedCase.type} Case Details
</Text>
<View style={styles.modalContent}>
<Text style={styles.modalText}>
Patient: {selectedCase.patientdetails.Name}
</Text>
<Text style={styles.modalText}>
ID: {selectedCase.patientdetails.PatID}
</Text>
<Text style={styles.modalText}>
Age: {selectedCase.patientdetails.PatAge}
</Text>
<Text style={styles.modalText}>
Sex: {selectedCase.patientdetails.PatSex}
</Text>
<Text style={styles.modalText}>
Modality: {selectedCase.patientdetails.Modality}
</Text>
<Text style={styles.modalText}>
Institution: {selectedCase.patientdetails.InstName}
</Text>
<Text style={styles.modalText}>
Status: {selectedCase.patientdetails.Status}
</Text>
<Text style={styles.modalText}>
Series: {selectedCase.series.length} available
</Text>
</View>
<Button <Button
title="Close" title="Close"
onPress={() => setModalVisible(false)} onPress={() => {
setModalVisible(false);
setSelectedCase(null);
}}
style={styles.button} style={styles.button}
/> />
</>
)}
</Modal> </Modal>
<ConfirmModal <ConfirmModal
visible={confirmVisible} visible={confirmVisible}
title="Confirm Action" title="Confirm Action"
@ -108,6 +289,7 @@ const DashboardScreen: React.FC<{ navigation: any }> = ({ navigation }) => {
}} }}
onCancel={() => setConfirmVisible(false)} onCancel={() => setConfirmVisible(false)}
/> />
<AlertModal <AlertModal
visible={alertVisible} visible={alertVisible}
title="Critical Alert" title="Critical Alert"
@ -116,6 +298,7 @@ const DashboardScreen: React.FC<{ navigation: any }> = ({ navigation }) => {
iconColor={Colors.error} iconColor={Colors.error}
onDismiss={() => setAlertVisible(false)} onDismiss={() => setAlertVisible(false)}
/> />
<LoadingOverlay visible={loading} /> <LoadingOverlay visible={loading} />
</ScrollView> </ScrollView>
</View> </View>
@ -143,43 +326,74 @@ const styles = StyleSheet.create({
fontFamily: Typography.fontFamily.regular, fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.md, fontSize: Typography.fontSize.md,
color: Colors.textSecondary, color: Colors.textSecondary,
marginBottom: Spacing.xs,
}, },
active: { active: {
color: Colors.success, color: Colors.success,
fontWeight: 'bold', fontFamily: Typography.fontFamily.bold,
},
caseCard: {
marginBottom: Spacing.md,
},
cardHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: Spacing.md,
},
titleRow: {
flexDirection: 'row',
alignItems: 'center',
}, },
cardTitle: { cardTitle: {
fontFamily: Typography.fontFamily.bold, fontFamily: Typography.fontFamily.bold,
fontSize: Typography.fontSize.md, fontSize: Typography.fontSize.md,
color: Colors.textPrimary, color: Colors.textPrimary,
marginBottom: Spacing.sm, marginLeft: Spacing.sm,
}, },
row: { totalCount: {
fontFamily: Typography.fontFamily.bold,
fontSize: Typography.fontSize.lg,
},
modalityContainer: {
marginBottom: Spacing.md,
},
modalityLabel: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.sm,
color: Colors.textSecondary,
marginBottom: Spacing.xs,
},
modalityRow: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-around',
},
modalityItem: {
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between',
marginBottom: Spacing.sm,
},
criticalText: {
color: Colors.error,
flex: 1, flex: 1,
marginLeft: Spacing.sm,
fontFamily: Typography.fontFamily.regular,
}, },
routineText: { modalityType: {
color: Colors.success,
flex: 1,
marginLeft: Spacing.sm,
fontFamily: Typography.fontFamily.regular, fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.sm,
color: Colors.textSecondary,
},
modalityCount: {
fontFamily: Typography.fontFamily.bold,
fontSize: Typography.fontSize.md,
marginTop: Spacing.xs,
}, },
button: { button: {
marginLeft: Spacing.sm, marginTop: Spacing.sm,
}, },
input: { input: {
marginVertical: Spacing.md, marginVertical: Spacing.md,
}, },
spinner: { noDataText: {
marginVertical: Spacing.md, fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.md,
color: Colors.textSecondary,
textAlign: 'center',
padding: Spacing.lg,
}, },
modalTitle: { modalTitle: {
fontFamily: Typography.fontFamily.bold, fontFamily: Typography.fontFamily.bold,
@ -188,6 +402,15 @@ const styles = StyleSheet.create({
marginBottom: Spacing.md, marginBottom: Spacing.md,
textAlign: 'center', textAlign: 'center',
}, },
modalContent: {
marginBottom: Spacing.md,
},
modalText: {
fontFamily: Typography.fontFamily.regular,
fontSize: Typography.fontSize.md,
color: Colors.textPrimary,
marginBottom: Spacing.xs,
},
}); });
export default DashboardScreen; export default DashboardScreen;

View File

@ -21,6 +21,11 @@ const api = create({
export const caseAPI = { export const caseAPI = {
getCases: (token:string) => api.get('/api/dicom/medpacks-sync/get-synced-medpacks-data',{},buildHeaders({token})), getCases: (token:string) => api.get('/api/dicom/medpacks-sync/get-synced-medpacks-data',{},buildHeaders({token})),
// get patients data
getPatients: (token:string) =>{
console.log('token',token)
return api.get('/api/dicom/medpacks-sync/get-synced-medpacks-data',{},buildHeaders({token}))
}
// Add more endpoints as needed // Add more endpoints as needed
}; };

View File

@ -11,7 +11,7 @@ import { ProfileScreen, SettingsScreen, PreferencesScreen } from '../screens';
import { Colors } from '../../../../shared/src/theme'; import { Colors } from '../../../../shared/src/theme';
export type ProfileStackParamList = { export type ProfileStackParamList = {
Profile: undefined; ProfileScreen: undefined;
Settings: undefined; Settings: undefined;
Preferences: undefined; Preferences: undefined;
}; };
@ -23,13 +23,13 @@ const Stack = createStackNavigator<ProfileStackParamList>();
*/ */
const ProfileNavigator: React.FC = () => ( const ProfileNavigator: React.FC = () => (
<Stack.Navigator <Stack.Navigator
initialRouteName="Profile" initialRouteName="ProfileScreen"
screenOptions={{ screenOptions={{
headerStyle: { backgroundColor: Colors.primary }, headerStyle: { backgroundColor: Colors.primary },
}} }}
> >
<Stack.Screen <Stack.Screen
name="Profile" name="ProfileScreen"
component={ProfileScreen} component={ProfileScreen}
options={{ title: 'Profile',headerShown:false }} options={{ title: 'Profile',headerShown:false }}
/> />

View File

@ -202,7 +202,10 @@ const ProfileScreen: React.FC<{ navigation: any }> = ({ navigation }) => {
/> />
<TouchableOpacity <TouchableOpacity
style={styles.editIconContainer} style={styles.editIconContainer}
onPress={() => handleEdit('Profile Photo', user.profile_photo_url || '')}> // onPress={() =>
// handleEdit('Profile Photo', user.profile_photo_url || '')
// }
>
<Icon name="edit-2" size={16} color={Colors.background} /> <Icon name="edit-2" size={16} color={Colors.background} />
</TouchableOpacity> </TouchableOpacity>
</View> </View>

View File

@ -11,6 +11,14 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
2B63C139D1C7487D854E5414 /* Roboto-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0E209063CB8A4F2A9B5F73FC /* Roboto-Black.ttf */; };
E2F63E44EA94431C9697867A /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 33A1869F0A6846C980E48C0A /* Roboto-Bold.ttf */; };
F86F120BE48D4CFF9916459C /* Roboto-ExtraBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3C216A8717FF4CE8811E0980 /* Roboto-ExtraBold.ttf */; };
CE2ADC3F43A3462DAAB5A976 /* Roboto-ExtraLight.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2BFC5F3B38744854821CB455 /* Roboto-ExtraLight.ttf */; };
60FAFE61E4C54472B6A3B3D1 /* Roboto-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = A583FF188F1E46B98CDC84F2 /* Roboto-Light.ttf */; };
4EE41339FC4D437C8731CECE /* Roboto-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 78E14701D8014CB08C8BB0F3 /* Roboto-Medium.ttf */; };
92D1CEAF3AB14A22A0261A5F /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17E4616E779A4CCCBEF89C5E /* Roboto-Regular.ttf */; };
A6058591ADBE4B1FAA793275 /* Roboto-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 3E358D5508C340C2826B03DA /* Roboto-SemiBold.ttf */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -35,6 +43,14 @@
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = NeoScan_Radiologist/AppDelegate.swift; sourceTree = "<group>"; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = NeoScan_Radiologist/AppDelegate.swift; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = NeoScan_Radiologist/LaunchScreen.storyboard; sourceTree = "<group>"; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = NeoScan_Radiologist/LaunchScreen.storyboard; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
0E209063CB8A4F2A9B5F73FC /* Roboto-Black.ttf */ = {isa = PBXFileReference; name = "Roboto-Black.ttf"; path = "../shared/src/assets/fonts/Roboto-Black.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
33A1869F0A6846C980E48C0A /* Roboto-Bold.ttf */ = {isa = PBXFileReference; name = "Roboto-Bold.ttf"; path = "../shared/src/assets/fonts/Roboto-Bold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
3C216A8717FF4CE8811E0980 /* Roboto-ExtraBold.ttf */ = {isa = PBXFileReference; name = "Roboto-ExtraBold.ttf"; path = "../shared/src/assets/fonts/Roboto-ExtraBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
2BFC5F3B38744854821CB455 /* Roboto-ExtraLight.ttf */ = {isa = PBXFileReference; name = "Roboto-ExtraLight.ttf"; path = "../shared/src/assets/fonts/Roboto-ExtraLight.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
A583FF188F1E46B98CDC84F2 /* Roboto-Light.ttf */ = {isa = PBXFileReference; name = "Roboto-Light.ttf"; path = "../shared/src/assets/fonts/Roboto-Light.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
78E14701D8014CB08C8BB0F3 /* Roboto-Medium.ttf */ = {isa = PBXFileReference; name = "Roboto-Medium.ttf"; path = "../shared/src/assets/fonts/Roboto-Medium.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
17E4616E779A4CCCBEF89C5E /* Roboto-Regular.ttf */ = {isa = PBXFileReference; name = "Roboto-Regular.ttf"; path = "../shared/src/assets/fonts/Roboto-Regular.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
3E358D5508C340C2826B03DA /* Roboto-SemiBold.ttf */ = {isa = PBXFileReference; name = "Roboto-SemiBold.ttf"; path = "../shared/src/assets/fonts/Roboto-SemiBold.ttf"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = unknown; explicitFileType = undefined; includeInIndex = 0; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -93,6 +109,7 @@
83CBBA001A601CBA00E9B192 /* Products */, 83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */, 2D16E6871FA4F8E400B85C8A /* Frameworks */,
BBD78D7AC51CEA395F1C20DB /* Pods */, BBD78D7AC51CEA395F1C20DB /* Pods */,
29546F9717134CF38C4927C5 /* Resources */,
); );
indentWidth = 2; indentWidth = 2;
sourceTree = "<group>"; sourceTree = "<group>";
@ -116,6 +133,22 @@
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
29546F9717134CF38C4927C5 /* Resources */ = {
isa = "PBXGroup";
children = (
0E209063CB8A4F2A9B5F73FC /* Roboto-Black.ttf */,
33A1869F0A6846C980E48C0A /* Roboto-Bold.ttf */,
3C216A8717FF4CE8811E0980 /* Roboto-ExtraBold.ttf */,
2BFC5F3B38744854821CB455 /* Roboto-ExtraLight.ttf */,
A583FF188F1E46B98CDC84F2 /* Roboto-Light.ttf */,
78E14701D8014CB08C8BB0F3 /* Roboto-Medium.ttf */,
17E4616E779A4CCCBEF89C5E /* Roboto-Regular.ttf */,
3E358D5508C340C2826B03DA /* Roboto-SemiBold.ttf */,
);
name = Resources;
sourceTree = "<group>";
path = "";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@ -185,6 +218,14 @@
files = ( files = (
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
2B63C139D1C7487D854E5414 /* Roboto-Black.ttf in Resources */,
E2F63E44EA94431C9697867A /* Roboto-Bold.ttf in Resources */,
F86F120BE48D4CFF9916459C /* Roboto-ExtraBold.ttf in Resources */,
CE2ADC3F43A3462DAAB5A976 /* Roboto-ExtraLight.ttf in Resources */,
60FAFE61E4C54472B6A3B3D1 /* Roboto-Light.ttf in Resources */,
4EE41339FC4D437C8731CECE /* Roboto-Medium.ttf in Resources */,
92D1CEAF3AB14A22A0261A5F /* Roboto-Regular.ttf in Resources */,
A6058591ADBE4B1FAA793275 /* Roboto-SemiBold.ttf in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -26,14 +26,13 @@
<true/> <true/>
<key>NSAppTransportSecurity</key> <key>NSAppTransportSecurity</key>
<dict> <dict>
<!-- Do not change NSAllowsArbitraryLoads to true, or you will risk app rejection! -->
<key>NSAllowsArbitraryLoads</key> <key>NSAllowsArbitraryLoads</key>
<false/> <false/>
<key>NSAllowsLocalNetworking</key> <key>NSAllowsLocalNetworking</key>
<true/> <true/>
</dict> </dict>
<key>NSLocationWhenInUseUsageDescription</key> <key>NSLocationWhenInUseUsageDescription</key>
<string></string> <string/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key> <key>UIRequiredDeviceCapabilities</key>
@ -48,5 +47,16 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>UIAppFonts</key>
<array>
<string>Roboto-Black.ttf</string>
<string>Roboto-Bold.ttf</string>
<string>Roboto-ExtraBold.ttf</string>
<string>Roboto-ExtraLight.ttf</string>
<string>Roboto-Light.ttf</string>
<string>Roboto-Medium.ttf</string>
<string>Roboto-Regular.ttf</string>
<string>Roboto-SemiBold.ttf</string>
</array>
</dict> </dict>
</plist> </plist>

View File

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

7
react-native.config.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = {
project: {
ios: {},
android: {},
},
assets: ['./shared/src/assets/fonts'], // adjust according to your path
};

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.

View File

@ -51,7 +51,7 @@ const styles = StyleSheet.create({
}, },
text: { text: {
color: Colors.background, color: Colors.background,
fontSize: Typography.fontSize.md, fontSize: Typography.fontSize.sm,
fontFamily: Typography.fontFamily.bold, fontFamily: Typography.fontFamily.bold,
}, },
disabled: { disabled: {

View File

@ -11,7 +11,7 @@ import { cardStyles } from './Card.styles';
interface CardProps { interface CardProps {
children: ReactNode; children: ReactNode;
style?: ViewStyle; style?: ViewStyle | [ViewStyle,{}];
} }
/** /**

View File

@ -11,7 +11,7 @@ import { cardStyles } from './Card.styles';
interface InfoCardProps { interface InfoCardProps {
children: ReactNode; children: ReactNode;
style?: ViewStyle; style?: ViewStyle |[ViewStyle,{}];
} }
/** /**

View File

@ -8,8 +8,12 @@
// Typography system for the Radiologist App // Typography system for the Radiologist App
export const Typography = { export const Typography = {
fontFamily: { fontFamily: {
regular: 'Roboto-Regular',
bold:'Roboto-Bold', bold:'Roboto-Bold',
exraBold:'Roboto-ExtraBold',
medium:'Roboto-Medium',
light:'Roboto-Light',
regular:'Roboto-Regular',
semiBold:'Roboto-SemiBold'
}, },
fontSize: { fontSize: {
xs: 12, xs: 12,

View File

@ -0,0 +1,30 @@
interface PatientDetails {
Date: string;
Name: string;
PatID: string;
PatAge: string;
PatSex: string;
Status: string;
InstName: string;
Modality: 'DX'|'CT'|'MR';
ReportStatus: string | null;
}
interface Series {
Path: string[];
SerDes: string;
ViePos: string | null;
pngpath: string;
SeriesNum: string;
ImgTotalinSeries: string;
}
export interface MedicalCase {
id: number;
patientdetails: PatientDetails;
series: Series[];
created_at: string;
updated_at: string;
series_id: string | null;
type:'Critical'|'Routine'|'Emergency'
}