Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(CARE-101): create medical records #69

Merged
merged 3 commits into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"axios": "^1.3.4",
"classnames": "^2.5.1",
"dayjs": "^1.11.8",
"framer-motion": "^11.0.5",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"query-string": "^8.2.0",
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/apis/doctor.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import queryString from 'query-string'
import { PatientOfDoctor } from 'src/types/users.type'
import { PagingFilter, PagingResponse } from 'src/types/utils.type'
import http from 'src/utils/http'

const URL_DOCTORS_MY_PATIENT = 'doctors/my-patients'

const doctorApi = {
getMyPatients: (examinationFilter: PagingFilter) => {
const patientsFilterParams = queryString.stringify(examinationFilter)
return http.get<PagingResponse<PatientOfDoctor[]>>(`${URL_DOCTORS_MY_PATIENT}?${patientsFilterParams}`)
}
}

export default doctorApi
8 changes: 7 additions & 1 deletion frontend/src/apis/examination.api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import http from 'src/utils/http'
import queryString from 'query-string'
import { PagingFilter, PagingResponse } from 'src/types/utils.type'
import { ExaminationType } from 'src/types/examination.type'
import { ExaminationReqBody, ExaminationType, ExaminationUpdateReqBody } from 'src/types/examination.type'

const URL_MY_EXAMINATION = 'examinations/my-examinations'
const URL_EXAMINATIONS = 'examinations'
Expand All @@ -13,6 +13,12 @@ const examinationApi = {
},
getExaminationById: (idMedicalRecord: number) => {
return http.get<ExaminationType>(`${URL_EXAMINATIONS}/${idMedicalRecord}`)
},
postExamination: (examinationBody: ExaminationReqBody) => {
return http.post<ExaminationType>(`${URL_EXAMINATIONS}`, examinationBody)
},
putExamination: (examinationUpdateBody: ExaminationUpdateReqBody) => {
return http.put<{ message: string }>(`${URL_EXAMINATIONS}`, examinationUpdateBody)
}
}

Expand Down
10 changes: 10 additions & 0 deletions frontend/src/apis/hospitals.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { HospitalListName } from 'src/types/hospitals.type'
import http from 'src/utils/http'

const URL_HOSPITALS_LIST_NAME = 'hospitals/list-name'

const hospitalsApi = {
getHospitalsListName: () => http.get<HospitalListName[]>(URL_HOSPITALS_LIST_NAME)
}

export default hospitalsApi
13 changes: 13 additions & 0 deletions frontend/src/apis/medicalHistories.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { MedicalHistory } from 'src/types/medicalHistorys.type'
import http from 'src/utils/http'

const MEDICAL_HISTORY = 'medical-histories'
const MEDICAL_HISTORY_PATIENTS = 'medical-histories/patients'

const medicalHistoryApi = {
getMedicalHistoryOfPatient: (patientId: number) => {
return http.get<MedicalHistory>(`${MEDICAL_HISTORY_PATIENTS}/${patientId}`)
}
}

export default medicalHistoryApi
6 changes: 5 additions & 1 deletion frontend/src/apis/medicalTests.api.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import http from 'src/utils/http'
import { MedialTestType } from 'src/types/medicalTests.type'
import { MedialTestType, MedicalTestReqBody } from 'src/types/medicalTests.type'

const URL_MEDICAL_TEST = 'medical-tests'
const URL_MEDICALTEST_EXAMINATION = 'medical-tests/examination'

const medicalTestsApi = {
getMedicalTestExaminationById: (medicalTestId: number) => {
return http.get<MedialTestType[]>(`${URL_MEDICALTEST_EXAMINATION}/${medicalTestId}`)
},
createMedicalTest: (medicalTestBody: MedicalTestReqBody) => {
return http.post<MedialTestType>(`${URL_MEDICAL_TEST}`, medicalTestBody)
}
}

Expand Down
6 changes: 3 additions & 3 deletions frontend/src/apis/patient.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { PagingFilter, PagingResponse } from 'src/types/utils.type'
import http from 'src/utils/http'

const URL_PATIENTS = 'patients'
const URL_MY_PATIENTS = 'patients/my-patients'

const patientApi = {
getMyPatients: () => {
return http.get<PatientOfDoctor[]>(URL_MY_PATIENTS)
getPatients: (patientFilter: { keyword: string }) => {
const patientsFilterParams = queryString.stringify(patientFilter)
return http.get<PagingResponse<PatientOfDoctor[]>>(`${URL_PATIENTS}?${patientsFilterParams}`)
},
getPatientById: (id: number) => {
return http.get<PatientOfDoctor>(`${URL_PATIENTS}/${id}`)
Expand Down
13 changes: 7 additions & 6 deletions frontend/src/assets/images/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import authImage1 from './authImage1.jpg'
import authImage2 from './authImage2.jpg'
import authImage3 from './authImage3.jpg'
import authImage4 from './authImage4.jpg'
import authImage5 from './authImage5.jpg'
import authImage1 from 'src/assets/images/authImage1.jpg'
import authImage2 from 'src/assets/images/authImage2.jpg'
import authImage3 from 'src/assets/images/authImage3.jpg'
import authImage4 from 'src/assets/images/authImage4.jpg'
import authImage5 from 'src/assets/images/authImage5.jpg'
import searchNoData from 'src/assets/images/searchNoData.jpg'

export { authImage1, authImage2, authImage3, authImage4, authImage5 }
export { authImage1, authImage2, authImage3, authImage4, authImage5, searchNoData }
Binary file added frontend/src/assets/images/searchNoData.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 28 additions & 0 deletions frontend/src/components/modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { twMerge } from 'tailwind-merge'
export interface ModalProps {
children: React.ReactNode
modalRef: React.MutableRefObject<HTMLDialogElement | null>
containerClass?: string
closeBtnClass?: string
}

export default function Modal({ children, modalRef, containerClass, closeBtnClass }: ModalProps) {
return (
<dialog ref={modalRef} className={twMerge('modal', containerClass)}>
<div
className='modal-box flex min-h-[90vh] max-w-[80%] flex-col gap-4 overflow-y-auto overflow-x-hidden p-8'
id='modal-box'
>
<form method='dialog'>
<button className={twMerge('btn btn-circle btn-ghost btn-sm absolute right-2 top-2', closeBtnClass)}>
βœ•
</button>
</form>
{children}
</div>
<form method='dialog' className='modal-backdrop'>
<button>close</button>
</form>
</dialog>
)
}
22 changes: 22 additions & 0 deletions frontend/src/components/no-data-display/NoDataDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { HiOutlineInformationCircle } from 'react-icons/hi2'
import { twMerge } from 'tailwind-merge'

export interface NoDataDisplayProps {
title: string
description: string
actions?: React.ReactNode
containerClass?: string
}

export default function NoDataDisplay({ title, description, containerClass, actions }: NoDataDisplayProps) {
return (
<div className={twMerge('px-10 py-4', containerClass)}>
<div className='flex flex-col items-center justify-center py-12'>
<HiOutlineInformationCircle className='h-20 w-20' />
<p className='mb-2 text-xl font-semibold text-gray-600'>{title}</p>
<p className='mb-6 text-center text-gray-500'>{description}</p>
{actions}
</div>
</div>
)
}
62 changes: 43 additions & 19 deletions frontend/src/components/pagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,51 @@
import classNames from 'classnames'
import { HiChevronLeft, HiChevronRight } from 'react-icons/hi2'

export default function Pagination() {
import Button from 'src/components/button/Button'
export interface PaginationProps {
currentPage: number
totalPages: number
onPageChange: (page: number) => void
}
export default function Pagination({ currentPage, totalPages, onPageChange }: PaginationProps) {
const handleChangePage = (page: number) => {
onPageChange(page)
}
return (
<div className='flex items-center gap-2'>
<button className='btn btn-sm' disabled>
<HiChevronLeft />
</button>
{Array(3)
<div className={classNames({ 'cursor-not-allowed': currentPage === 1 })}>
<Button
className='btn-sm'
Icon={HiChevronLeft}
iconClass='w-[unset] h-[unset]'
disabled={currentPage === 1}
onClick={() => handleChangePage(currentPage - 1)}
/>
</div>

{Array(totalPages)
.fill(0)
.map((_, index) => (
<button
key={index}
className={classNames('btn btn-sm', {
'btn-primary': index === 0
})}
>
{index + 1}
</button>
))}
<button className='btn btn-sm'>
<HiChevronRight />
</button>
.map((_, index) => {
const page = index + 1
return (
<Button
key={page}
className={classNames('btn-sm', {
'btn-primary text-white': page === currentPage
})}
title={page.toString()}
onClick={() => handleChangePage(page)}
/>
)
})}
<div className={classNames({ 'cursor-not-allowed': currentPage === totalPages })}>
<Button
className='btn-sm'
Icon={HiChevronRight}
iconClass='w-[unset] h-[unset]'
disabled={currentPage === totalPages}
onClick={() => handleChangePage(currentPage + 1)}
/>
</div>
</div>
)
}
33 changes: 33 additions & 0 deletions frontend/src/components/table/MedicalTestTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { MedicalTestSchema } from 'src/utils/rules'
import { twMerge } from 'tailwind-merge'

export interface MedicalTestTableProps {
medicalTestData: MedicalTestSchema[]
containerClass?: string
tableClass?: string
}

export default function MedicalTestTable({ medicalTestData, containerClass, tableClass }: MedicalTestTableProps) {
return (
<div className={twMerge('overflow-x-auto', containerClass)}>
<table className={twMerge('table', tableClass)}>
<thead>
<tr className='border-black/70'>
<th className='w-1/2'>Medical test</th>
<th className='w-1/4'>Value</th>
<th className='w-1/4 text-right'>Unit</th>
</tr>
</thead>
<tbody>
{medicalTestData.map((medicalTest, index) => (
<tr key={index} className='hover border-black/20'>
<td>{medicalTest.parameter}</td>
<td>{medicalTest.index}</td>
<td className='text-right'>{medicalTest.unit}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
3 changes: 2 additions & 1 deletion frontend/src/constants/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export const AUTH_FIELD_NAME = {

export const INSURANCE_AREA_NUMBER = 4
export const PAGE_SIZE_DEFAULT = 10
export const PRESCRIPTION_ROW_NAME = 'prescriptions'
export const PRESCRIPTION_ROW_NAME = 'prescriptionRow'
export const DEFAULT_PAGING_SIZE = 10
Loading
Loading