patient api integrated dashboard ui created
This commit is contained in:
parent
1e2e4eba47
commit
4ff28e061b
BIN
android/app/src/main/assets/fonts/Roboto-Black.ttf
Normal file
BIN
android/app/src/main/assets/fonts/Roboto-Black.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/Roboto-Bold.ttf
Normal file
BIN
android/app/src/main/assets/fonts/Roboto-Bold.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/Roboto-ExtraBold.ttf
Normal file
BIN
android/app/src/main/assets/fonts/Roboto-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/Roboto-ExtraLight.ttf
Normal file
BIN
android/app/src/main/assets/fonts/Roboto-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/Roboto-Light.ttf
Normal file
BIN
android/app/src/main/assets/fonts/Roboto-Light.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/Roboto-Medium.ttf
Normal file
BIN
android/app/src/main/assets/fonts/Roboto-Medium.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/Roboto-Regular.ttf
Normal file
BIN
android/app/src/main/assets/fonts/Roboto-Regular.ttf
Normal file
Binary file not shown.
BIN
android/app/src/main/assets/fonts/Roboto-SemiBold.ttf
Normal file
BIN
android/app/src/main/assets/fonts/Roboto-SemiBold.ttf
Normal file
Binary file not shown.
37
android/link-assets-manifest.json
Normal file
37
android/link-assets-manifest.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
DashboardDetailScreen:{
|
||||||
|
caseType: 'Critical' | 'Routine' | 'Emergency';
|
||||||
|
};
|
||||||
// Add more screens here as needed
|
// 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>
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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>
|
<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>
|
||||||
|
</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;
|
||||||
|
|||||||
@ -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>
|
||||||
<Card>
|
|
||||||
<Text style={styles.cardTitle}>Critical Cases</Text>
|
{/* Dynamic Case Cards */}
|
||||||
<View style={styles.row}>
|
{(['Critical', 'Emergency', 'Routine'] as const).map(type =>
|
||||||
<CustomIcon name="alert" color={Colors.error} size={24} />
|
renderCaseCard(type)
|
||||||
<Text style={styles.criticalText}>Bed 3 - Hemorrhage (93% AI)</Text>
|
)}
|
||||||
<Button
|
|
||||||
title="Review Now"
|
{filteredCases.length === 0 && (
|
||||||
onPress={() => setModalVisible(true)}
|
<Card>
|
||||||
style={styles.button}
|
<Text style={styles.noDataText}>
|
||||||
/>
|
{search ? 'No cases found matching your search.' : 'No cases available.'}
|
||||||
</View>
|
</Text>
|
||||||
</Card>
|
</Card>
|
||||||
<Card>
|
)}
|
||||||
<Text style={styles.cardTitle}>Routine Cases</Text>
|
|
||||||
<View style={styles.row}>
|
|
||||||
<CustomIcon name="check-circle" color={Colors.success} size={24} />
|
|
||||||
<Text style={styles.routineText}>Bed 12 - Headache (99% AI)</Text>
|
|
||||||
<IconButton
|
|
||||||
name="chevron-right"
|
|
||||||
onPress={() => setConfirmVisible(true)}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</Card>
|
|
||||||
<TextInput
|
<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>
|
<>
|
||||||
<Button
|
<Text style={styles.modalTitle}>
|
||||||
title="Close"
|
{selectedCase.type} Case Details
|
||||||
onPress={() => setModalVisible(false)}
|
</Text>
|
||||||
style={styles.button}
|
<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
|
||||||
|
title="Close"
|
||||||
|
onPress={() => {
|
||||||
|
setModalVisible(false);
|
||||||
|
setSelectedCase(null);
|
||||||
|
}}
|
||||||
|
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;
|
||||||
|
|||||||
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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 }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
37
ios/link-assets-manifest.json
Normal file
37
ios/link-assets-manifest.json
Normal 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
7
react-native.config.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
project: {
|
||||||
|
ios: {},
|
||||||
|
android: {},
|
||||||
|
},
|
||||||
|
assets: ['./shared/src/assets/fonts'], // adjust according to your path
|
||||||
|
};
|
||||||
BIN
shared/src/assets/fonts/Roboto-Black.ttf
Normal file
BIN
shared/src/assets/fonts/Roboto-Black.ttf
Normal file
Binary file not shown.
BIN
shared/src/assets/fonts/Roboto-Bold.ttf
Normal file
BIN
shared/src/assets/fonts/Roboto-Bold.ttf
Normal file
Binary file not shown.
BIN
shared/src/assets/fonts/Roboto-ExtraBold.ttf
Normal file
BIN
shared/src/assets/fonts/Roboto-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
shared/src/assets/fonts/Roboto-ExtraLight.ttf
Normal file
BIN
shared/src/assets/fonts/Roboto-ExtraLight.ttf
Normal file
Binary file not shown.
BIN
shared/src/assets/fonts/Roboto-Light.ttf
Normal file
BIN
shared/src/assets/fonts/Roboto-Light.ttf
Normal file
Binary file not shown.
BIN
shared/src/assets/fonts/Roboto-Medium.ttf
Normal file
BIN
shared/src/assets/fonts/Roboto-Medium.ttf
Normal file
Binary file not shown.
BIN
shared/src/assets/fonts/Roboto-Regular.ttf
Normal file
BIN
shared/src/assets/fonts/Roboto-Regular.ttf
Normal file
Binary file not shown.
BIN
shared/src/assets/fonts/Roboto-SemiBold.ttf
Normal file
BIN
shared/src/assets/fonts/Roboto-SemiBold.ttf
Normal file
Binary file not shown.
@ -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: {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { cardStyles } from './Card.styles';
|
|||||||
|
|
||||||
interface CardProps {
|
interface CardProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
style?: ViewStyle;
|
style?: ViewStyle | [ViewStyle,{}];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { cardStyles } from './Card.styles';
|
|||||||
|
|
||||||
interface InfoCardProps {
|
interface InfoCardProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
style?: ViewStyle;
|
style?: ViewStyle |[ViewStyle,{}];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
30
shared/src/utils/types/Case.ts
Normal file
30
shared/src/utils/types/Case.ts
Normal 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'
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user