diff --git a/android/app/src/main/assets/fonts/Roboto-Black.ttf b/android/app/src/main/assets/fonts/Roboto-Black.ttf new file mode 100644 index 0000000..d51221a Binary files /dev/null and b/android/app/src/main/assets/fonts/Roboto-Black.ttf differ diff --git a/android/app/src/main/assets/fonts/Roboto-Bold.ttf b/android/app/src/main/assets/fonts/Roboto-Bold.ttf new file mode 100644 index 0000000..9d7cf22 Binary files /dev/null and b/android/app/src/main/assets/fonts/Roboto-Bold.ttf differ diff --git a/android/app/src/main/assets/fonts/Roboto-ExtraBold.ttf b/android/app/src/main/assets/fonts/Roboto-ExtraBold.ttf new file mode 100644 index 0000000..7092a88 Binary files /dev/null and b/android/app/src/main/assets/fonts/Roboto-ExtraBold.ttf differ diff --git a/android/app/src/main/assets/fonts/Roboto-ExtraLight.ttf b/android/app/src/main/assets/fonts/Roboto-ExtraLight.ttf new file mode 100644 index 0000000..75608c6 Binary files /dev/null and b/android/app/src/main/assets/fonts/Roboto-ExtraLight.ttf differ diff --git a/android/app/src/main/assets/fonts/Roboto-Light.ttf b/android/app/src/main/assets/fonts/Roboto-Light.ttf new file mode 100644 index 0000000..6fcd5f9 Binary files /dev/null and b/android/app/src/main/assets/fonts/Roboto-Light.ttf differ diff --git a/android/app/src/main/assets/fonts/Roboto-Medium.ttf b/android/app/src/main/assets/fonts/Roboto-Medium.ttf new file mode 100644 index 0000000..d629e98 Binary files /dev/null and b/android/app/src/main/assets/fonts/Roboto-Medium.ttf differ diff --git a/android/app/src/main/assets/fonts/Roboto-Regular.ttf b/android/app/src/main/assets/fonts/Roboto-Regular.ttf new file mode 100644 index 0000000..7e3bb2f Binary files /dev/null and b/android/app/src/main/assets/fonts/Roboto-Regular.ttf differ diff --git a/android/app/src/main/assets/fonts/Roboto-SemiBold.ttf b/android/app/src/main/assets/fonts/Roboto-SemiBold.ttf new file mode 100644 index 0000000..3f34834 Binary files /dev/null and b/android/app/src/main/assets/fonts/Roboto-SemiBold.ttf differ diff --git a/android/link-assets-manifest.json b/android/link-assets-manifest.json new file mode 100644 index 0000000..83de0c0 --- /dev/null +++ b/android/link-assets-manifest.json @@ -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" + } + ] +} diff --git a/app/modules/Dashboard/navigation/DashboardNavigator.tsx b/app/modules/Dashboard/navigation/DashboardNavigator.tsx index d464839..b538b35 100644 --- a/app/modules/Dashboard/navigation/DashboardNavigator.tsx +++ b/app/modules/Dashboard/navigation/DashboardNavigator.tsx @@ -9,11 +9,16 @@ import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import { DashboardScreen } from '../screens'; import { Colors } from '../../../../shared/src/theme'; +import DashBoardDetail from '../screens/DashBoardDetail'; export type DashboardStackParamList = { - Dashboard: undefined; + DashboardScreen: undefined; + DashboardDetailScreen:{ + caseType: 'Critical' | 'Routine' | 'Emergency'; + }; // Add more screens here as needed -}; + +} const Stack = createStackNavigator(); @@ -22,15 +27,20 @@ const Stack = createStackNavigator(); */ const DashboardNavigator: React.FC = () => ( + {/* Add more screens here */} diff --git a/app/modules/Dashboard/redux/dashboardActions.ts b/app/modules/Dashboard/redux/dashboardActions.ts index 60e80ba..759c0ca 100644 --- a/app/modules/Dashboard/redux/dashboardActions.ts +++ b/app/modules/Dashboard/redux/dashboardActions.ts @@ -6,7 +6,7 @@ */ import { createAsyncThunk } from '@reduxjs/toolkit'; -import { fetchCasesStart, fetchCasesSuccess, fetchCasesFailure } from './dashboardSlice'; +import { fetchCasesStart, fetchCasesSuccess, fetchCasesFailure, fetchPatientsSuccess } from './dashboardSlice'; 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 * Design & Developed by Tech4Biz Solutions diff --git a/app/modules/Dashboard/redux/dashboardSelectors.ts b/app/modules/Dashboard/redux/dashboardSelectors.ts index 5ce76a9..8bc5ecb 100644 --- a/app/modules/Dashboard/redux/dashboardSelectors.ts +++ b/app/modules/Dashboard/redux/dashboardSelectors.ts @@ -10,6 +10,8 @@ import { RootState } from '../../../../app/redux/rootReducer'; export const selectCaseQueue = (state: RootState) => state.dashboard.caseQueue; export const selectDashboardLoading = (state: RootState) => state.dashboard.loading; export const selectDashboardError = (state: RootState) => state.dashboard.error; +export const selectPatients = (state: RootState) => state.dashboard.patientData; + /* * End of File: dashboardSelectors.ts diff --git a/app/modules/Dashboard/redux/dashboardSlice.ts b/app/modules/Dashboard/redux/dashboardSlice.ts index ea88f17..529c542 100644 --- a/app/modules/Dashboard/redux/dashboardSlice.ts +++ b/app/modules/Dashboard/redux/dashboardSlice.ts @@ -21,12 +21,14 @@ interface DashboardState { caseQueue: Case[]; loading: boolean; error: string | null; + patientData:[] } const initialState: DashboardState = { caseQueue: [], loading: false, error: null, + patientData:[] }; /** @@ -51,6 +53,23 @@ const dashboardSlice = createSlice({ clearCases(state) { 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, fetchCasesFailure, clearCases, + fetchPatientsSuccess } = dashboardSlice.actions; export default dashboardSlice.reducer; diff --git a/app/modules/Dashboard/screens/DashBoardDetail.tsx b/app/modules/Dashboard/screens/DashBoardDetail.tsx index 942a074..699fd60 100644 --- a/app/modules/Dashboard/screens/DashBoardDetail.tsx +++ b/app/modules/Dashboard/screens/DashBoardDetail.tsx @@ -5,22 +5,356 @@ * Copyright (c) Spurrin Innovations. All rights reserved. */ -import React, { useState } from 'react'; -import { View, Text, StyleSheet, ScrollView } from 'react-native'; +import React, { useState, useMemo } from 'react'; +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 { 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. - * This is a reference implementation for other modules. + * DashBoardDetail - Shows detailed list of patients for a specific case type */ -const DashBoardDetail: React.FC = () => { +const DashBoardDetail: React.FC = ({ navigation, route }) => { + const [search, setSearch] = useState(''); + const [selectedPatient, setSelectedPatient] = useState(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) => ( + handlePatientPress(patient)} + activeOpacity={0.7} + > + + + + + {patient.patientdetails.Name} + + + ID: {patient.patientdetails.PatID} + + + + + + + + + + + Age + {patient.patientdetails.PatAge} + + + Sex + {patient.patientdetails.PatSex} + + + Modality + + {patient.patientdetails.Modality?.substring(1)} + + + + + + + + {patient.patientdetails.InstName} + + + + + + {formatDate(patient.created_at)} + + + {patient.patientdetails.Status} + + + + + + + {patient.series.length} Series Available + + handlePatientPress(patient)} + /> + + + + ); return ( - - Dashboard Detail screen + + navigation.goBack()} + /> - + + {/* Summary Card */} + + + + + + {caseType} Cases + + + {filteredPatients.length} Patient{filteredPatients.length !== 1 ? 's' : ''} + + + + + + {/* Search and Sort */} + + + + + Sort by: + + {(['date', 'name', 'age'] as const).map(option => ( + setSortBy(option)} + > + + {option.charAt(0).toUpperCase() + option.slice(1)} + + + ))} + + + + + {/* Patient List */} + {filteredPatients.length > 0 ? ( + filteredPatients.map(renderPatientCard) + ) : ( + + + + No {caseType.toLowerCase()} cases found + + + {search ? 'Try adjusting your search terms' : 'All clear for now!'} + + + )} + + {/* Patient Detail Modal */} + setModalVisible(false)} + > + {selectedPatient && ( + + Patient Details + + + Basic Information + Name: {selectedPatient.patientdetails.Name} + ID: {selectedPatient.patientdetails.PatID} + Age: {selectedPatient.patientdetails.PatAge} + Sex: {selectedPatient.patientdetails.PatSex} + Status: {selectedPatient.patientdetails.Status} + + + + Medical Information + Modality: {selectedPatient.patientdetails.Modality} + Institution: {selectedPatient.patientdetails.InstName} + Report Status: {selectedPatient.patientdetails.ReportStatus || 'Pending'} + Case Type: {selectedPatient.type} + + + + Series Information + Available Series: {selectedPatient.series.length} + Created: {formatDate(selectedPatient.created_at)} + Updated: {formatDate(selectedPatient.updated_at)} + + + +