diff --git a/Modules/Internal/Http/Controllers/Api/Linksehat/HealthRecordController.php b/Modules/Internal/Http/Controllers/Api/Linksehat/HealthRecordController.php new file mode 100644 index 00000000..9300194d --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/Linksehat/HealthRecordController.php @@ -0,0 +1,243 @@ +toArray(); + $livechat = Livechat::query() + ->with([ 'user', 'doctor', 'doctor.user', 'doctor.user.detail', 'doctor.speciality', 'appointment', 'healthCare', 'summary']); + + if ($request->has('search')) { + $search = $request->search; + $livechat->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('livechat_start') || $request->has('livechat_end')) + && !empty($request->livechat_start) + && !empty($request->livechat_end) + ) { + + + $livechat = $livechat->where(function($q) use ($request) { + $q->where('dCreateOn', '>=', $request->livechat_start) + ->where('dCreateOn', '<=', $request->livechat_end); + }); + } + + if ($request->has('healthcare_id') && !empty($request->healthcare_id)) { + $livechat->where('nIDHealthCare', $request->healthcare_id); + } + + $livechats = $livechat->orderBy('dUpdateOn', 'DESC') + ->paginate(); + + return Helper::responseJson(Helper::paginateResources(ReportPhrResource::collection($livechats))); + } + + /** + * 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); + $livechats = Livechat::query() + ->with([ 'user', 'doctor', 'doctor.user', 'doctor.user.detail', 'doctor.speciality', 'appointment', 'healthCare', 'summary']); + + if ($request->has('search')) { + $search = $request->search; + $livechats->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('livechat_start') || $request->has('livechat_end')) + && !empty($request->livechat_start) + && !empty($request->livechat_end) + ) { + $livechats = $livechats->where(function($q) use ($request) { + $q->where('dCreateOn', '>=', $request->livechat_start) + ->where('dCreateOn', '<=', $request->livechat_end); + }); + } + + $livechats = $livechats->get(); + + if ($livechats){ + foreach ($livechats 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/Routes/api.php b/Modules/Internal/Routes/api.php index b93c14fb..3a59ed6c 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -29,6 +29,7 @@ use Modules\Internal\Http\Controllers\Api\DrugController; use Modules\Internal\Http\Controllers\Api\FormulariumController; use Modules\Internal\Http\Controllers\Api\FormulariumTemplateController; use Modules\Internal\Http\Controllers\Api\Linksehat\PaymentController; +use Modules\Internal\Http\Controllers\Api\Linksehat\HealthRecordController; use Modules\Internal\Http\Controllers\Api\LivechatController; use Modules\Internal\Http\Controllers\Api\MemberController; use Modules\Internal\Http\Controllers\Api\OptionController; @@ -72,6 +73,9 @@ Route::prefix('internal')->group(function () { Route::get('linksehat/payments', [PaymentController::class, 'index']); Route::get('linksehat/payments/generate-excel', [PaymentController::class, 'generateExcel']); + + + Route::get('diagnosis', [RequestLogController::class, 'diagnosis']); Route::get('drugs', [DrugController::class, 'drugList']); Route::get('units', [DrugController::class, 'unitList']); @@ -80,6 +84,10 @@ Route::prefix('internal')->group(function () { Route::middleware('auth:sanctum')->group(function () { + // Report LMS + Route::get('linksehat/phr', [HealthRecordController::class, 'index']); + Route::get('linksehat/phr/generate-excel', [HealthRecordController::class, 'generateExcel']); + Route::post('logout', [AuthController::class, 'logout'])->name('logout'); Route::get('/user', function (Request $request) { return $request->user(); diff --git a/Modules/Internal/Transformers/ReportPhrResource.php b/Modules/Internal/Transformers/ReportPhrResource.php new file mode 100644 index 00000000..b42f2d98 --- /dev/null +++ b/Modules/Internal/Transformers/ReportPhrResource.php @@ -0,0 +1,38 @@ +doctor && $this->doctor->user && $this->doctor->user->detail) { + $doctor_name = $this->doctor->user->detail->sTitlePrefix . ' ' . $this->doctor->user->fullname; + } + $data = [ + 'id' => $this->nID, + 'healthcare' => $this->healthCare ? $this->healthCare->sHealthCare : null, + 'patient_name' => $this->user ? $this->user->sFirstName : null, + 'doctor_name' => $doctor_name, + 'specialis' => $this->doctor ? $this->doctor->speciality->sSpesialis : null, + 'date_consultation' => $this->summary ? Carbon::parse($this->dCreateOn)->format('Y-m-d H:i:s') : null , + 'subject' => $this->summary ? $this->summary->sSubjective : null, + 'object' => $this->summary ? $this->summary->sObjective : null, + 'assessment' => $this->summary ? $this->summary->sAssessment : null, + 'plan' => $this->summary ? unserialize($this->summary->sPlan) : null, + ]; + + return $data; + } +} diff --git a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Create.tsx b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Create.tsx new file mode 100644 index 00000000..efb7a395 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Create.tsx @@ -0,0 +1,93 @@ +import { useEffect, useState } from 'react'; +import { paramCase } from 'change-case'; +import { useParams, useLocation } from 'react-router-dom'; +// @mui +import { Container, Stack } from '@mui/material'; +import useSettings from '../../../hooks/useSettings'; +import Page from '../../../components/Page'; +import Form from './Form'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import axios from '../../../utils/axios'; +import { Practitioner } from '../../../@types/doctor'; +import ButtonBack from '../../../components/ButtonBack'; + +export default function Create() { + const { themeStretch } = useSettings(); + const { id } = useParams(); + + const isEdit = id ? true : false; + + const [currentPractitioner, setCurrentPractitioner] = useState(); + + useEffect(() => { + if (isEdit) { + axios.get('/doctors/' + id).then((res) => { + setCurrentPractitioner(res.data); + }); + } + }, [id]); + + return ( + + + + {/* */} + + + +
+ + + ); +} +// const pageTitle = 'Create Data Dokter'; +// return ( +// +// +// + +// +// +// +// +// +// +// +// +// +// ); +// } diff --git a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Form.tsx b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Form.tsx new file mode 100644 index 00000000..39885db8 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Form.tsx @@ -0,0 +1,260 @@ +import * as Yup from 'yup'; +import { useSnackbar } from 'notistack'; +import { useNavigate } from 'react-router-dom'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import MenuItem from '@mui/material/MenuItem'; + +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import * as React from 'react'; + +// form +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +// @mui +import { styled } from '@mui/material/styles'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Avatar, + Button, + ButtonGroup, + Card, + FormHelperText, + Grid, + Stack, + Typography, + TextField, + Chip, +} from '@mui/material'; + +import CancelIcon from '@mui/icons-material/Cancel'; + +// components +import { + FormProvider, + RHFTextField, + RHFRadioGroup, + RHFUploadAvatar, + RHFSwitch, + RHFEditor, + RHFDatepicker, + RHFMultiCheckbox, + RHFCheckbox, + RHFCustomMultiCheckbox, +} from '../../../components/hook-form'; +import axios from '../../../utils/axios'; +import { fCurrency } from '../../../utils/formatNumber'; +import { Practitioner } from '../../../@types/doctor'; + +import { Label, Rowing } from '@mui/icons-material'; + +const LabelStyle = styled(Typography)(({ theme }) => ({ + ...theme.typography.subtitle2, + color: theme.palette.text.secondary, + marginBottom: theme.spacing(1), +})); + +const HeaderStyle = styled('header')(({ theme }) => ({ + paddingBottom: theme.spacing(5), + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +})); + +const Title = styled(Typography)(({ theme }) => ({ + ...theme.typography.h4, + boxShadow: 'none', + // paddingBottom: theme.spacing(3), + fontWeight: 700, + color: '#005B7F', +})); + +interface FormValuesProps extends Partial { + taxes: boolean; + inStock: boolean; +} + +type Props = { + isEdit: boolean; + currentPractitioner?: Practitioner; +}; + +const Span = styled(Typography)(({ theme }) => ({ + boxShadow: 'none', + paddingBottom: theme.spacing(1), +})); + +const Text = styled(Typography)(({ theme }) => ({ + boxShadow: 'none', + paddingBottom: theme.spacing(3), +})); + +export default function PractitionerForm({ isEdit, currentPractitioner }: Props) { + const navigate = useNavigate(); + const [practitioner_group, setPractitionerGroups] = useState([]); + + // const [ errors, setErrors ] = useState<{ [key: string]: string }>({}); + + const { enqueueSnackbar } = useSnackbar(); + + const NewCorporateSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + // file: Yup.boolean().required('Corporate Status is required'), + }); + + const defaultValues = useMemo( + () => ({ + id: currentPractitioner?.id, + name: currentPractitioner?.name || '', + address: currentPractitioner?.address || '', + birth_date: currentPractitioner?.birth_date || '', + gender: currentPractitioner?.gender || '', + description: currentPractitioner?.description || '', + birth_place: currentPractitioner?.birth_place || '', + active: currentPractitioner?.active === 1 ? true : false, + avatar_url: currentPractitioner?.avatar_url || '', + doctor_id: currentPractitioner?.doctor_id || '', + organizations: currentPractitioner?.organizations || [], + specialities: currentPractitioner?.specialities || [], + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [currentPractitioner] + ); + + console.log('defaultValues', defaultValues); + + function StatusLabel({ value }: { value: boolean }) { + return ( + + ); + } + const methods = useForm({ + resolver: yupResolver(NewCorporateSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const values = watch(); + + useEffect(() => { + if (isEdit && currentPractitioner) { + reset(defaultValues); + } + if (!isEdit) { + reset(defaultValues); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isEdit, currentPractitioner]); + + const handleActivate = (event: React.ChangeEvent) => { + setValue('active', event.target.checked); + + console.log('event.target.checked', event.target.checked); + + const formData = new FormData(); + formData.append('active', event.target.checked ? '1' : '0'); + formData.append('_method', 'PUT'); + axios.post('/doctors/' + currentPractitioner?.id ?? '', formData); + + enqueueSnackbar('active Updated Successfully!', { variant: 'success' }); + }; + + return ( + + + + {/* */} + + + + Data Dokter + + + {/* Status Rumah Sakit */} + + + + + Informasi Umum + + + + + Nama Dokter + {currentPractitioner?.name ? currentPractitioner?.name : '-'} + No Telp + {currentPractitioner?.phone ? currentPractitioner?.phone : '-'} + Tempat Lahir + + {currentPractitioner?.birth_place ? currentPractitioner?.birth_place : '-'} + + Alamat + {currentPractitioner?.address ? currentPractitioner?.address : '-'} + + + Jenis Kelamin + {currentPractitioner?.gender ? currentPractitioner?.gender : '-'} + Email + {currentPractitioner?.email ? currentPractitioner?.email : '-'} + Tanggal Lahir + + {currentPractitioner?.birth_date ? currentPractitioner?.birth_date : '-'} + + + + + + Tempat Praktik + {currentPractitioner?.organizations?.map((item, index) => ( + + + + {item.name} + + + + ))} + + + Spesialisasi + {currentPractitioner?.specialities?.map((item, index) => ( + + + + {item.name} + + + + ))} + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Index.tsx b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Index.tsx new file mode 100644 index 00000000..3c1ff53c --- /dev/null +++ b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/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 = 'Riwayat Medis Peserta'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/List.tsx b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/List.tsx new file mode 100644 index 00000000..8a412049 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/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) => ( + + ))} + + )} +
+
+ + +
+
+ ); +} diff --git a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Show.tsx b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Show.tsx new file mode 100644 index 00000000..be9d1c46 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Show.tsx @@ -0,0 +1,53 @@ +import { useEffect, useState } from 'react'; +import { paramCase } from 'change-case'; +import { useParams, useLocation } from 'react-router-dom'; +// @mui +import { Container, Stack } from '@mui/material'; +import useSettings from '../../../hooks/useSettings'; +import Page from '../../../components/Page'; +import View from './View'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import axios from '../../../utils/axios'; +import { Appointment } from '../../../@types/doctor'; + +export default function Create() { + const { themeStretch } = useSettings(); + const { id } = useParams(); + + const isEdit = id ? true : false; + + const [currentAppointment, setCurrentAppointment] = useState(); + + useEffect(() => { + if (isEdit) { + axios.get('/appointments/' + id).then((res) => { + setCurrentAppointment(res.data); + }); + } + }, [id]); + + return ( + + + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/View.tsx b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/View.tsx new file mode 100644 index 00000000..8105b8b1 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/View.tsx @@ -0,0 +1,275 @@ +import * as Yup from 'yup'; +import { useSnackbar } from 'notistack'; +import { useNavigate } from 'react-router-dom'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import MenuItem from '@mui/material/MenuItem'; + +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import * as React from 'react'; + +// form +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +// @mui +import { styled } from '@mui/material/styles'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Avatar, + Button, + ButtonGroup, + Card, + FormHelperText, + Grid, + Stack, + Typography, + TextField, + Chip, + Badge, + Divider, +} from '@mui/material'; + +import CancelIcon from '@mui/icons-material/Cancel'; + +// components +import { + FormProvider, + RHFTextField, + RHFRadioGroup, + RHFUploadAvatar, + RHFSwitch, + RHFEditor, + RHFDatepicker, + RHFMultiCheckbox, + RHFCheckbox, + RHFCustomMultiCheckbox, +} from '../../../components/hook-form'; +import axios from '../../../utils/axios'; +import { fCurrency } from '../../../utils/formatNumber'; +import { Appointment } from '../../../@types/doctor'; + +import { Label, Rowing, Spa } from '@mui/icons-material'; +import { border } from '@mui/system'; + +const LabelStyle = styled(Typography)(({ theme }) => ({ + ...theme.typography.subtitle2, + color: theme.palette.text.secondary, + marginBottom: theme.spacing(1), +})); + +const HeaderStyle = styled('header')(({ theme }) => ({ + paddingBottom: theme.spacing(5), + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +})); + +const Title = styled(Typography)(({ theme }) => ({ + ...theme.typography.h4, + boxShadow: 'none', + // paddingBottom: theme.spacing(3), + fontWeight: 700, + color: '#005B7F', +})); + +interface FormValuesProps extends Partial { + taxes: boolean; + inStock: boolean; +} + +type Props = { + isEdit: boolean; + currentAppointment?: Appointment; +}; + +const Span = styled(Typography)(({ theme }) => ({ + boxShadow: 'none', + paddingBottom: theme.spacing(1), +})); + +const Text = styled(Typography)(({ theme }) => ({ + boxShadow: 'none', + paddingBottom: theme.spacing(3), +})); + +export default function AppointmentForm({ isEdit, currentAppointment }: Props) { + const navigate = useNavigate(); + + // const [ errors, setErrors ] = useState<{ [key: string]: string }>({}); + + const { enqueueSnackbar } = useSnackbar(); + + const NewCorporateSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + // file: Yup.boolean().required('Corporate Status is required'), + }); + + const defaultValues = useMemo( + () => ({ + id: currentAppointment?.id, + name: currentAppointment?.name || '', + address: currentAppointment?.address || '', + birth_date: currentAppointment?.birth_date || '', + gender: currentAppointment?.gender || '', + description: currentAppointment?.description || '', + birth_place: currentAppointment?.birth_place || '', + active: currentAppointment?.active === 1 ? true : false, + avatar_url: currentAppointment?.avatar_url || '', + doctor_id: currentAppointment?.doctor_id || '', + organizations: currentAppointment?.organizations || [], + specialities: currentAppointment?.specialities || [], + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [currentAppointment] + ); + + const methods = useForm({ + resolver: yupResolver(NewCorporateSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const values = watch(); + + useEffect(() => { + if (isEdit && currentAppointment) { + reset(defaultValues); + } + if (!isEdit) { + reset(defaultValues); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isEdit, currentAppointment]); + + return ( + + + + {/* */} + + + + } + spacing={2} + > + Data Appointment + + + + + + + + + + + Tanggal Booking : + + {currentAppointment?.date_created ? currentAppointment?.date_created : '-'} + + + + + + Tanggal Appointment : + + {currentAppointment?.date_appointment + ? currentAppointment?.date_appointment + : '-'} + + + + + + + Nama Dokter + + {currentAppointment?.doctor_name ? currentAppointment?.doctor_name : '-'} + + Faskes + + {currentAppointment?.health_care ? currentAppointment?.health_care : '-'} + + + + Spesialis + {currentAppointment?.speciality ? currentAppointment?.speciality : '-'} + Appointment Via Web/App + + {currentAppointment?.appointment_media + ? currentAppointment?.appointment_media + : '-'} + + + + + + + + Data Pembayaran + + + + {currentAppointment?.payment_detail !== null ? ( + + + Metode Pembayaran + + {currentAppointment?.payment_method ? currentAppointment?.payment_method : '-'} + + Harga + + {currentAppointment?.payment_detail?.gross_amount + ? currentAppointment?.payment_detail?.gross_amount + : '-'} + + Mata Uang + + {currentAppointment?.payment_detail?.currency + ? currentAppointment?.payment_detail?.currency + : '-'} + + + + Tipe Pembayaran + + {currentAppointment?.payment_detail?.payment_type + ? currentAppointment?.payment_detail?.payment_type + : '-'} + + Waktu Transaksi + + {currentAppointment?.payment_detail?.transaction_time + ? currentAppointment?.payment_detail?.transaction_time + : '-'} + + Status + + {currentAppointment?.payment_detail?.status_message + ? currentAppointment?.payment_detail?.status_message + : '-'} + + + + ) : ( + Belum ada pembayaran + )} + + + + + ); +} diff --git a/frontend/dashboard/src/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index f3681f67..61acc03b 100644 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -451,6 +451,11 @@ export default function Router() { element: , }, + { + path: 'report/phr', + element: , + }, + { path: 'claims', element: , @@ -716,6 +721,8 @@ const EPrescriptionShow = Loadable(lazy(() => import('../pages/EPrescription/Liv const LinksehatPayment = Loadable(lazy(() => import('../pages/Report/LinksehatPayments/Index'))); +const RiwayatMedisPeserta = Loadable(lazy(() => import('../pages/Report/RiwayatMedisPeserta/Index'))); + const MasterDrug = Loadable(lazy(() => import('../pages/Master/Drug/Index'))); const MasterFormularium = Loadable(lazy(() => import('../pages/Master/Formularium/Index')));