diff --git a/Modules/Internal/Http/Controllers/Api/Linksehat/RujukanController.php b/Modules/Internal/Http/Controllers/Api/Linksehat/RujukanController.php new file mode 100644 index 00000000..a43414c9 --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/Linksehat/RujukanController.php @@ -0,0 +1,243 @@ +toArray(); + $rujukan = Rujukan::query() + ->with(['livechat', 'livechat.user']); + + if ($request->has('search')) { + $search = $request->search; + $rujukan->where(function ($query) use ($search) { + $query->where('nID', $search) + ->orWhereHas('user', function ($detail) use ($search) { + $detail->where('sFirstName', 'LIKE', '%' . $search . '%') + ->orWhere('sLastName', 'LIKE', '%' . $search . '%'); + }) + ->orWhereHas('healthCare', function ($detail) use ($search) { + $detail->where('sHealthCare', 'LIKE', '%' . $search . '%'); + }); + }); + } + + if (($request->has('rujukan_start') || $request->has('rujukan_end')) + && !empty($request->rujukan_start) + && !empty($request->rujukan_end) + ) { + + + $rujukan = $rujukan->where(function($q) use ($request) { + $q->where('dCreateOn', '>=', $request->rujukan_start) + ->where('dCreateOn', '<=', $request->rujukan_end); + }); + } + + if ($request->has('healthcare_id') && !empty($request->healthcare_id)) { + $rujukan->where('nIDHealthCare', $request->healthcare_id); + } + + $rujukans = $rujukan->orderBy('dUpdateOn', 'DESC') + ->paginate(); + + return Helper::responseJson(Helper::paginateResources(ReportRujukanResource::collection($rujukans))); + } + + /** + * Show the form for creating a new resource. + * @return Renderable + */ + public function create() + { + return view('internal::create'); + } + + /** + * Store a newly created resource in storage. + * @param Request $request + * @return Renderable + */ + public function store(Request $request) + { + // + } + + /** + * Show the specified resource. + * @param int $id + * @return Renderable + */ + public function show($id) + { + return view('internal::show'); + } + + /** + * Show the form for editing the specified resource. + * @param int $id + * @return Renderable + */ + public function edit($id) + { + return view('internal::edit'); + } + + /** + * Update the specified resource in storage. + * @param Request $request + * @param int $id + * @return Renderable + */ + public function update(Request $request, $id) + { + // + } + + /** + * Remove the specified resource from storage. + * @param int $id + * @return Renderable + */ + public function destroy($id) + { + // + } + + // Function to determine if a string is serialized + private function is_serialized($string) { + return ($string == 'b:0;' || @unserialize($string) !== false); + } + + // Function to determine if a string is JSON + private function is_json($string) { + json_decode($string); + return (json_last_error() == JSON_ERROR_NONE); + } + + // Function to safely process the plan + private function processPlan($sPlan) { + if ($this->is_serialized($sPlan)) { + $unserializedPlan = @unserialize($sPlan); + if ($unserializedPlan !== false || $sPlan === 'b:0;') { + return $unserializedPlan; + } + } elseif ($this->is_json($sPlan)) { + $jsonPlan = json_decode($sPlan, true); + if (json_last_error() == JSON_ERROR_NONE) { + return $jsonPlan; + } + } + return $sPlan; // Treat as plain text if not serialized or JSON + } + + public function generateExcel(Request $request){ + Helper::setCustomPHPIniSettings(); + + $file_name = 'Data Report Riwayat Rekam Medis'; + // Membuat penulis entitas Spout + $writer = WriterEntityFactory::createXLSXWriter(); + // Membuka penulis untuk menulis ke file + $writer->openToFile(public_path('files/Report-Riwayat-Rekam-Medis.xlsx')); + $headerArray = [ + 'Healthcare', + 'Patient', + 'Doctor', + 'Speciality', + 'Date', + 'Keluhan (Subjective)', + 'Pemerikasan Fisik Online (Objective)', + 'Diagnosa (Assessment)', + 'Tata Laksana (Plan)', + ]; + // Sheet 1 + $writer->getCurrentSheet()->setName('Data'); + $headers_map_to_table_fields = $headerArray; + $headerRow = WriterEntityFactory::createRowFromArray($headers_map_to_table_fields); + $writer->addRow($headerRow); + $rujukans = Livechat::query() + ->with([ 'user', 'doctor', 'doctor.user', 'doctor.user.detail', 'doctor.speciality', 'appointment', 'healthCare', 'summary']); + + if ($request->has('search')) { + $search = $request->search; + $rujukans->where(function ($query) use ($search) { + $query->where('nID', $search) + ->orWhereHas('user', function ($detail) use ($search) { + $detail->where('sFirstName', 'LIKE', '%' . $search . '%') + ->orWhere('sLastName', 'LIKE', '%' . $search . '%'); + }) + ->orWhereHas('healthCare', function ($detail) use ($search) { + $detail->where('sHealthCare', 'LIKE', '%' . $search . '%'); + }); + }); + } + + if (($request->has('rujukan_start') || $request->has('rujukan_end')) + && !empty($request->rujukan_start) + && !empty($request->rujukan_end) + ) { + $rujukans = $rujukans->where(function($q) use ($request) { + $q->where('dCreateOn', '>=', $request->rujukan_start) + ->where('dCreateOn', '<=', $request->rujukan_end); + }); + } + + $rujukans = $rujukans->get(); + + if ($rujukans){ + foreach ($rujukans as $index => $row) { + $doctor_name = '-'; + if ($row->doctor && $row->doctor->user && $row->doctor->user->detail) { + $doctor_name = $row->doctor->user->detail->sTitlePrefix . ' ' . $row->doctor->user->fullname; + } + $speciality = $row->doctor->speciality->sSpesialis ?? '-'; + // Process the plan + $plan = '-'; + if ($row->summary && $row->summary->sPlan) { + $plan = $this->processPlan($row->summary->sPlan); + } + + $rowData = [ + $row->healthCare ? $row->healthCare->sHealthCare : '-', + $row->user ? $row->user->sFirstName : '-', + $doctor_name, + $speciality, + $row->summary ? Carbon::parse($row->dCreateOn)->format('Y-m-d H:i:s') : '-', + $row->summary ? $row->summary->sSubjective : '-', + $row->summary ? $row->summary->sObjective : '-', + $row->summary ? $row->summary->sAssessment : '-', + is_array($plan) ? implode(', ', $plan) : $plan, // Handle arrays from unserialized or JSON data + ]; + + // Create a row from the array and add it to the writer + $rowEntity = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($rowEntity); + } + } + $writer->close(); + return Helper::responseJson([ + 'file_name' => "Data Riwayat Log " . date('Y-m-d h:i:s'), + "file_url" => url('files/Report-Riwayat-Rekam-Medis.xlsx') + ]); + } +} diff --git a/Modules/Internal/Transformers/ReportRujukanResource.php b/Modules/Internal/Transformers/ReportRujukanResource.php new file mode 100644 index 00000000..3ad38f6b --- /dev/null +++ b/Modules/Internal/Transformers/ReportRujukanResource.php @@ -0,0 +1,46 @@ +livechat->doctor && $this->livechat->doctor->user && $this->livechat->doctor->user->detail) { + $doctor_name = $this->livechat->doctor->user->detail->sTitlePrefix . ' ' . $this->livechat->doctor->user->fullname; + } + + $diagnosis = null; + $plan = null; + + if ($this->livechat->summary){ + $diagnosis = $this->livechat->summary->sAssessment; + $plan = $this->livechat->summary->sPlan ? unserialize($this->livechat->summary->sPlan) : $this->livechat->summary->sPlan; + } + // dd($this->livechat->summary); + $data = [ + 'id' => $this->nID, + 'no_rujukan' => $this->sNoRujukan ? $this->sNoRujukan : null, + 'patient_name' => $this->livechat ? $this->livechat->user->sFirstName : null, + 'healthcare' => $this->healthcare ? $this->healthcare->sHealthCare : null, + 'doctor_name' => $doctor_name, + 'departement' => $this->sDepartement ? $this->sDepartement : null, + 'date_rujukan' => $this->dCreatedOn ? Carbon::parse($this->dCreateOn)->format('Y-m-d H:i:s') : null , + 'diagnosa' => $diagnosis, + 'plan' => $plan, + ]; + + return $data; + } +} diff --git a/app/Models/OLDLMS/Rujukan.php b/app/Models/OLDLMS/Rujukan.php new file mode 100644 index 00000000..2c930e1c --- /dev/null +++ b/app/Models/OLDLMS/Rujukan.php @@ -0,0 +1,52 @@ + 'Menunggu Konfirmasi', + // 1 => 'Diterima', + // 2 => 'Ditolak', + // 3 => 'Selesai', + // 4 => 'Expired', + // ]; + + const CREATED_AT = 'dCreateOn'; + const UPDATED_AT = 'dUpdateOn'; + const DELETED_AT = 'dDeleteOn'; + + protected $connection = 'oldlms'; + + protected $table = 'tx_rujukan'; + + protected $primaryKey = 'nID'; + + // protected $appends = [ + // 'status_name', + // ]; + + public function livechat(){ + return $this->belongsTo(Livechat::class, 'nIDLivechat', 'nID'); + } + + public function healthcare(){ + return $this->belongsTo(Healthcare::class, 'nIDHealthcare', 'nID'); + } + + +} diff --git a/frontend/dashboard/src/pages/Report/Rujukan/Index.tsx b/frontend/dashboard/src/pages/Report/Rujukan/Index.tsx new file mode 100644 index 00000000..bc840fe3 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/Rujukan/Index.tsx @@ -0,0 +1,35 @@ +import { Card, Grid, Container } from '@mui/material'; +import { useParams } from 'react-router-dom'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import Page from '../../../components/Page'; +import useSettings from '../../../hooks/useSettings'; +import List from './List'; + +export default function LinksehatPayments() { + const { themeStretch } = useSettings(); + + const { id } = useParams(); + + const pageTitle = 'Rujukan'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/Rujukan/List.tsx b/frontend/dashboard/src/pages/Report/Rujukan/List.tsx new file mode 100644 index 00000000..8a412049 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/Rujukan/List.tsx @@ -0,0 +1,550 @@ +import { + Box, + Button, + Card, + Collapse, + Paper, + Select, + SelectChangeEvent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Typography, + Stack, + ButtonGroup, + Grid, + Chip, + Dialog, + DialogContent, + DialogContentText, + DialogActions, + FormControl, + Autocomplete, + InputAdornment, + IconButton, + InputLabel, + Menu, +} from '@mui/material'; + +import { + Link, + NavLink as RouterLink, + useSearchParams, + useNavigate, + useParams, +} from 'react-router-dom'; +// hooks +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +// components +import AutocompleteHealthcare from '@/components/autocomplete/AutocompleteHealthcare'; +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import { Icd } from '../../../@types/diagnosis'; +import BasePagination from '../../../components/BasePagination'; +import { Practitioner } from '../../../@types/doctor'; +import CreateIcon from '@mui/icons-material/Create'; +import { Props } from '../../../components/editor/index'; +import { red } from '@mui/material/colors'; +import { margin, padding } from '@mui/system'; +import { enqueueSnackbar } from 'notistack'; +import { fNumber } from '@/utils/formatNumber'; +import { Controller } from 'react-hook-form'; + +import SvgIconStyle from '../../../components/SvgIconStyle'; +import { GridSearchIcon } from '@mui/x-data-grid'; +import { Search } from '@mui/icons-material'; +import { Icon } from '@iconify/react'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import { MenuItem } from '@mui/material'; +import { fDateOnly, fDateTime } from '@/utils/formatTime'; +import AutocompleteLinksehatHealthcare from '@/components/autocomplete/AutocompleteLinksehatHealthcare'; +import { LoadingButton } from '@mui/lab'; +import UploadIcon from '@mui/icons-material/Upload'; + +// ---------------------------------------------------------------------- + +export default function List() { + // Generate the every row of the table + + const navigate = useNavigate(); + const { organization_id } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const [organizationOptions, setOrganizationOptions] = useState([]); + const [searchParamsPaymentStatus, setSearchParamsPaymentStatus] = useSearchParams(); + const [searchParamsOrganizations, setSearchParamsOrganizations] = useSearchParams(); + const [searchParamsSpecialities, setSearchParamsSpecialities] = useSearchParams(); + const [searchParamsFilter, setSearchParamsFilter] = useSearchParams(); + + useEffect(() => { + // axios.get(`/search-organizations`).then((response) => { + // setOrganizationOptions(response.data); + // }); + }, []); + + function Filter(props: any) { + // SEARCH + const searchInput = useRef(null); + const [searchText, setSearchText] = useState(''); + const [importLoading, setImportLoading] = useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + /* ------------------------------ handle params ----------------------------- */ + const [appliedParams, setAppliedParams] = useState({}); + const params = { + searchParams: searchParams, + setSearchParams: setSearchParams, + appliedParams: appliedParams, + setAppliedParams: setAppliedParams, + }; + + const handleGetData = (type :string) => { + const parameters = + Object.keys(appliedParams).length !== 0 + ? appliedParams + : Object.fromEntries([...searchParams.entries()]); + setImportLoading(true); + axios.get('/linksehat/phr/generate-excel', { + params: { ...parameters }, + }).then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + setImportLoading(false); + }); + // axios.get(`report/logs/export`) + // .then((response) => { + // const link = document.createElement('a'); + // link.href = response.data.data.file_url; + // link.setAttribute('download', response.data.data.file_name); + // document.body.appendChild(link); + // link.click(); + // handleClose(); + // }) + } + + //handle search + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + + props.onSearch(searchText); + }; + + useEffect(() => { + // Trigger First Search + setSearchText(searchParams.get('search') ?? ''); + }, []); + + + + return ( +
+ + + { + if (event.key === 'Enter') { + // handleSearchSubmit(event); + + const filter = Object.fromEntries([ + ...searchParams.entries(), + ['search', searchText], + ]); + setSearchParams(filter); + loadDataTableData(filter); + } + }} + label="Search" + value={searchText} + InputProps={{ + // startAdornment: ( + // + // + // + // ), + placeholder: 'Nama Pasien', + }} + /> + + + + { + try { + if (value && !!Date.parse(value)) { + const date = value ? fDateOnly(value) : ''; + var entries = [...searchParams.entries(), ['livechat_start', date ?? '']]; + if (!searchParams.get('livechat_end')) { + entries = [...entries, ['livechat_end', date ?? '']]; + } + const filter = Object.fromEntries(entries); + + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => } + /> + + + + + + { + try { + if (value && !!Date.parse(value)) { + const date = fDateOnly(value); + var entries = [...searchParams.entries(), ['livechat_end', date ?? '']]; + if (!searchParams.get('livechat_start')) { + entries = [...entries, ['livechat_start', date ?? '']]; + } + const filter = Object.fromEntries(entries); + + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => ( + + )} + /> + + + + + } + sx={{ p: 1.8 }} + onClick={handleClick} + loading={importLoading} + > + Export + + + {handleGetData('')}}>Download Excel + + + +
+ ); + } + + function FilterForm(props: any) { + // IMPORT + return ( + + + + + + ); + } + + //TODO Create PaymentType + function createData(payments: any): any { + return { + ...payments, + }; + } + + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + const [openDialog, setOpenDialog] = React.useState(false); + + const handleDelete = (model: any) => { + axios + .delete(`/doctors/${row.id}`) + .then((res) => { + setDataTableData({ + ...dataTableData, + data: dataTableData.data.filter((model) => model.id != row.id), + }); + enqueueSnackbar('Data berhasil dihapus', { variant: 'success' }); + }) + .catch((error) => { + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; + + return ( + + + {row.healthcare ?? '-'} + {row.patient_name ?? '-'} + {row.doctor_name ?? '-'} + {row.specialis ?? '-' } + {row.date_consultation ? fDateTime(row.date_consultation) : '-'} + {row.subject ?? '-'} + {row.object ?? '-'} + {row.assessment ?? '-'} + {row.plan ?? '-'} + {/* + + + + + + */} + + + { + setOpenDialog(false); + }} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + + + + Apakah anda yakin ingin menghapus + + + {row.name}? + + + + + + + + + ); + } + + const headStyle = { + fontWeight: 'bold', + }; + // Dummy Default Data + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const [dataTableLastRequest, setDataTableLastRequest] = useState(0); + const [dataTableResponseState, setDataTableResponseState] = useState('idle'); + const [dataTableData, setDataTableData] = useState({ + current_page: 1, + data: [], + path: '', + first_page_url: '', + last_page: 1, + last_page_url: '', + next_page_url: '', + prev_page_url: '', + per_page: 10, + from: 0, + to: 0, + total: 0, + }); + const [dataTablePage, setDataTablePage] = useState(5); + + const loadDataTableData = async (appliedFilter: any | null = null) => { + setDataTableLoading(true); + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + const response = await axios.get('/linksehat/phr', { + params: filter, + }); + setDataTableLoading(false); + setDataTableData(response.data.data); + }; + + // const applyFilter = async (searchFilter: string) => { + // await loadDataTableData({ search: searchFilter }); + // setSearchParams({ search: searchFilter }); + // }; + + const applyItems = async ( + searchFilter: string, + searchFilterOrganization: string, + searchFilterPaymentStatus: string, + searchFilterAppointmentStart: string, + searchFilterAppointmentEnd: string + ) => { + await loadDataTableData({ + search: searchFilter, + organization_id: searchFilterOrganization, + payment_status: searchFilterPaymentStatus, + livechat_start: searchFilterAppointmentStart, + livechat_end: searchFilterAppointmentEnd, + }); + setSearchParamsFilter({ + search: searchFilter, + organization_id: searchFilterOrganization, + payment_status: searchFilterPaymentStatus, + livechat_start: searchFilterAppointmentStart, + livechat_end: searchFilterAppointmentEnd, + }); + }; + + const handlePageChange = (event: ChangeEvent, value: number) => { + const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); + loadDataTableData(filter); + setSearchParams(filter); + }; + + useEffect(() => { + loadDataTableData(); + }, []); + + return ( + + {/* */} + + + + + {/* The Main Table */} + + + + + + Healthcare + + + Patient + + + Doctor + + + Speciality + + + Date + + + Subjective + + + Objective + + + Assessment + + + Plan + + + + {/* + Aksi + */} + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} +
+
+ + +
+
+ ); +}