diff --git a/Modules/Internal/Http/Controllers/Api/Linksehat/PrescriptionController.php b/Modules/Internal/Http/Controllers/Api/Linksehat/PrescriptionController.php new file mode 100644 index 00000000..91d50556 --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/Linksehat/PrescriptionController.php @@ -0,0 +1,240 @@ +toArray(); + $prescription = Prescription::query() + ->with(['livechat', 'user', 'items']); + if ($request->has('search')) { + $search = $request->search; + $prescription->where(function ($query) use ($search) { + $query->where('sDokterName', 'LIKE', '%' . $search . "%") + ->orWhere('sKodeResep', 'LIKE', '%' . $search . "%"); + }); + } + + if (($request->has('prescription_start') || $request->has('prescription_end')) + && !empty($request->prescription_start) + && !empty($request->prescription_end) + ) { + + + $prescription = $prescription->where(function($q) use ($request) { + $q->where('dTanggalResep', '>=', $request->prescription_start) + ->where('dTanggalResep', '<=', $request->prescription_end); + }); + } + + $prescriptions = $prescription->orderBy('dUpdateOn', 'DESC') + ->paginate(); + + return Helper::responseJson(Helper::paginateResources(ReportPrescriptionResource::collection($prescriptions))); + } + + /** + * 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 Resep Online'; + // Membuat penulis entitas Spout + $writer = WriterEntityFactory::createXLSXWriter(); + // Membuka penulis untuk menulis ke file + $writer->openToFile(public_path('files/Report-Resep-Online.xlsx')); + + $headerArray = [ + 'No', + 'Prescription Code', + 'Date Consultation', + 'Patient', + 'Doctor', + 'Jenis Obat (Drugs)', + 'Jumlah Obat (QTY)', + 'Cara Minum Obat', + ]; + + // Sheet 1 + $writer->getCurrentSheet()->setName('Data'); + $headerRow = WriterEntityFactory::createRowFromArray($headerArray); + $writer->addRow($headerRow); + + // Query prescription data + $prescriptionQuery = Prescription::query() + ->with(['livechat', 'user', 'items']); + + if ($request->has('search')) { + $search = $request->search; + $prescriptionQuery->where(function ($query) use ($search) { + $query->where('sDokterName', 'LIKE', '%' . $search . '%') + ->orWhere('sKodeResep', 'LIKE', '%' . $search . '%'); + }); + } + + if ($request->has('prescription_start') && $request->has('prescription_end') && + !empty($request->prescription_start) && !empty($request->prescription_end)) { + $prescriptionQuery->whereBetween('dTanggalResep', [$request->prescription_start, $request->prescription_end]); + } + + $prescriptions = $prescriptionQuery->get(); + + if ($prescriptions->isNotEmpty()) { + $no = 1; + foreach ($prescriptions as $index => $row) { + if ($row->items->isNotEmpty()) { + $rowData = [ + $no++, + $row->sKodeResep ?? '-', + $row->dTanggalResep ? Carbon::parse($row->dTanggalResep)->format('Y-m-d') : '-', + $row->user->name ?? '-', + $row->sDokterName ?? '-', + ]; + + // Create a row from the array and add it to the writer + $rowEntity = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($rowEntity); + foreach ($row->items as $item) { + $rowSubData = [ + '', + '', + '', + '', + '', + $item->sItemName ?? '-', + $item->nQty ?? '-', + $item->sSigna ?? '-' + ]; + $subData = WriterEntityFactory::createRowFromArray($rowSubData); + $writer->addRow($subData); + } + } else { + $rowData = [ + $no++, + $row->sKodeResep ?? '-', + $row->dTanggalResep ? Carbon::parse($row->dTanggalResep)->format('Y-m-d') : '-', + $row->user->name ?? '-', + $row->sDokterName ?? '-', + ]; + // 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 Resep Online " . date('Y-m-d h:i:s'), + 'file_url' => url('files/Report-Resep-Online.xlsx') + ]); + } + +} diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index c25f25e4..71284233 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -31,6 +31,8 @@ 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\Linksehat\RujukanController; +use Modules\Internal\Http\Controllers\Api\Linksehat\PrescriptionController as PrescriptionControllerReport; use Modules\Internal\Http\Controllers\Api\LivechatController; use Modules\Internal\Http\Controllers\Api\MemberController; use Modules\Internal\Http\Controllers\Api\OptionController; @@ -88,6 +90,10 @@ Route::prefix('internal')->group(function () { // Report LMS Route::get('linksehat/phr', [HealthRecordController::class, 'index']); Route::get('linksehat/phr/generate-excel', [HealthRecordController::class, 'generateExcel']); + Route::get('linksehat/prescription', [PrescriptionControllerReport::class, 'index']); + Route::get('linksehat/prescription/generate-excel', [PrescriptionControllerReport::class, 'generateExcel']); + Route::get('linksehat/rujukan', [RujukanController::class, 'index']); + Route::get('linksehat/rujukan/generate-excel', [RujukanController::class, 'generateExcel']); Route::post('logout', [AuthController::class, 'logout'])->name('logout'); Route::get('/user', function (Request $request) { diff --git a/Modules/Internal/Transformers/ReportPrescriptionResource.php b/Modules/Internal/Transformers/ReportPrescriptionResource.php new file mode 100644 index 00000000..baefd72f --- /dev/null +++ b/Modules/Internal/Transformers/ReportPrescriptionResource.php @@ -0,0 +1,32 @@ +user ? $this->user->sFirstName .' '. $this->user->sLastName : '-'; + + $data = [ + 'id' => $this->nID, + 'patient_name' => $patientName, + 'livechat' => $this->livechat, + 'prescription_code' => $this->sKodeResep, + 'date_consultation' => $this->dTanggalResep ? Carbon::parse($this->dTanggalResep)->format('Y-m-d H:i:s') : null, + 'doctor_name' => $this->sDokterName ? $this->sDokterName : '-', + 'items' => $this->items ? $this->items : [], + ]; + + return $data; + } +} diff --git a/app/Models/OLDLMS/Prescription.php b/app/Models/OLDLMS/Prescription.php index 9c3252d8..3aca5f9e 100755 --- a/app/Models/OLDLMS/Prescription.php +++ b/app/Models/OLDLMS/Prescription.php @@ -52,4 +52,18 @@ class Prescription extends Model 'dTanggalResep' => 'datetime', ]; + public function user() + { + return $this->belongsTo(User::class, 'nIDUser', 'nID'); + } + + public function items() + { + return $this->hasMany(PrescriptionItem::class, 'nIDPrescription', 'nID'); + } + + public function livechat(){ + return $this->belongsTo(Livechat::class, 'nIDLivechat', 'nID'); + } + } diff --git a/app/Models/OLDLMS/PrescriptionItem.php b/app/Models/OLDLMS/PrescriptionItem.php index 5c289e28..d05d7942 100755 --- a/app/Models/OLDLMS/PrescriptionItem.php +++ b/app/Models/OLDLMS/PrescriptionItem.php @@ -50,4 +50,10 @@ class PrescriptionItem extends Model ]; protected $primaryKey = 'nID'; + + public function prescription() + { + return $this->belongsTo(Prescription::class, 'nIDPrescription', 'nID'); + } + } diff --git a/frontend/dashboard/src/pages/Report/Prescription/Index.tsx b/frontend/dashboard/src/pages/Report/Prescription/Index.tsx index ff5c923e..f6ee8766 100755 --- a/frontend/dashboard/src/pages/Report/Prescription/Index.tsx +++ b/frontend/dashboard/src/pages/Report/Prescription/Index.tsx @@ -1,37 +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 '../Prescription/List'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import Page from '../../../components/Page'; +import useSettings from '../../../hooks/useSettings'; +import List from './List'; -export default function Prescription(){ - const { themeStretch } = useSettings(); +export default function LinksehatPayments() { + const { themeStretch } = useSettings(); - const { id } = useParams(); + const { id } = useParams(); - const pageTitle = 'Prescription'; + const pageTitle = 'Resep Online'; + return ( + + + - return( - - - - - - - - - ); -} \ No newline at end of file + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/Prescription/List.tsx b/frontend/dashboard/src/pages/Report/Prescription/List.tsx index 7ca00adb..7ea1d96e 100755 --- a/frontend/dashboard/src/pages/Report/Prescription/List.tsx +++ b/frontend/dashboard/src/pages/Report/Prescription/List.tsx @@ -26,6 +26,8 @@ import { Autocomplete, InputAdornment, IconButton, + InputLabel, + Menu, } from '@mui/material'; import { @@ -39,6 +41,7 @@ import { 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'; @@ -49,9 +52,8 @@ 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 { User } from '../../../Models/User'; - import SvgIconStyle from '../../../components/SvgIconStyle'; import { GridSearchIcon } from '@mui/x-data-grid'; @@ -59,370 +61,520 @@ 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(){ +// ---------------------------------------------------------------------- + +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(''); - - //handle search - const handleSearchChange = (event: any) => { - const newSearchText = event.target.value ?? ''; - setSearchText(newSearchText); - }; - - const handleSearchSubmit = (event: any) => { - event.preventDefault(); - - props.onSearch(searchText); - }; + // SEARCH + const searchInput = useRef(null); + const [searchText, setSearchText] = useState(''); + const [importLoading, setImportLoading] = useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); - useEffect(() => { - // Trigger First Search - setSearchText(searchParams.get('search') ?? ''); - }, []); - - const item = [ - { - id: '', - value: '', - name: 'Semua', - }, - ]; + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; - return ( -
- - - { - if (event.key === 'Enter') { - handleSearchSubmit(event); + /* ------------------------------ 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/prescription/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(), ['prescription_start', date ?? '']]; + if (!searchParams.get('prescription_end')) { + entries = [...entries, ['prescription_end', date ?? '']]; } - }} - value={searchText} - InputProps={{ - startAdornment: ( - - - - ), - placeholder: 'Search', - }} + 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(), ['prescription_end', date ?? '']]; + if (!searchParams.get('prescription_start')) { + entries = [...entries, ['prescription_start', date ?? '']]; + } + const filter = Object.fromEntries(entries); + + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => ( + - - - - ); -} -function FilterForm(props: any) { - return( + )} + /> + + + + + } + sx={{ p: 1.8 }} + onClick={handleClick} + loading={importLoading} + > + Export + + + {handleGetData('')}}>Download Excel + + +
+ + ); + } + + function FilterForm(props: any) { + // IMPORT + return ( - - + container + spacing={2} + sx={{ p: 2, justifyContent: 'space-between', alignItems: 'center' }} + > + + + - - ); -} + ); + } -function createData(doctor: Practitioner): Practitioner { - return { - ...doctor, - /* user: doctor.user ? new User(doctor.user) : null; */ - }; -} + //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); + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(true); + 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 ( + + + + setOpen(!open)}> + {open ? : } + + + {row.prescription_code ?? '-'} + {row.date_consultation ? fDateTime(row.date_consultation) : '-'} + {row.patient_name ?? '-'} + {row.doctor_name ?? '-'} + {/* + + + + + + */} + - return ( - - - - setOpen(!open)}> - {open ? : } - - + {/* COLLAPSIBLE ROW */} + + + + + + + + + Jenis Obat (Drugs) + + + Jumlah Obat (QTY) + + + Cara Minum Obat + - {row.user ? row.user.sFirstName : '-'} - {row.nIDDokter ? row.nIDDokter : '-'} - {row.nRating ? row.nRating : '-'} - {row.sNotes ? row.sNotes : '-'} - {row.dCreateOn ? row.dCreateOn : '-'} + {row.items?.map((item) => ( + + + {item.sItemName} + + + {item.nQty} + + + {item.sSigna} + + + ))} - {/* - - - - - - */} - - {/* COLLAPSIBLE ROW */} - - -{/* - - - - - - Metode Pembayaran - - - : {row.payment_method ? row.payment_method : '-'} - - - - Jenis Benefit - - - : - - - - Durasi - - - : {row.duration ? row.duration : '-'} - - - */} - - + + + + + + {/* END COLLAPSIBLE ROW */} - {/* END COLLAPSIBLE ROW */} - { - setOpenDialog(false); - }} - aria-labelledby="alert-dialog-title" - aria-describedby="alert-dialog-description" - > - - - - Apakah anda yakin ingin menghapus - - - {row.name}? - - - - - {/* */} - - - + { + 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/prescription', { + 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, + prescription_start: searchFilterAppointmentStart, + prescription_end: searchFilterAppointmentEnd, + }); + setSearchParamsFilter({ + search: searchFilter, + organization_id: searchFilterOrganization, + payment_status: searchFilterPaymentStatus, + prescription_start: searchFilterAppointmentStart, + prescription_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 */} + + + + + + + + Prescription Code + + + Date + + + Patient + + + Doctor + + {/* + Aksi + */} + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} +
+
+ + +
+
); } - -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('/doctorrating ', { - params: filter, - }); - setDataTableLoading(false); - setDataTableData(response.data); -}; - -// const applyFilter = async (searchFilter: string) => { -// await loadDataTableData({ search: searchFilter }); -// setSearchParams({ search: searchFilter }); -// }; - -const applyItems = async ( - searchFilter: string, - searchFilterOrganization: string, - searchFilterSpecialities: string -) => { - await loadDataTableData({ - search: searchFilter, - organization_id: searchFilterOrganization, - speciality_id: searchFilterSpecialities, - }); - setSearchParamsFilter({ - search: searchFilter, - organization_id: searchFilterOrganization, - speciality_id: searchFilterSpecialities, - }); -}; - -const handlePageChange = (event: ChangeEvent, value: number) => { - const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); - loadDataTableData(filter); - setSearchParams(filter); -}; - -useEffect(() => { - loadDataTableData(); -}, []); - -return ( - - {/* */} - - - - - {/* The Main Table */} - - - - - {/* */} - - - Nama User - - - ID Dokter - - - Rating - - - Notes - - - Created On - - - -{/* - - - Tanggal Booking - - - Tanggal Appointment - - - Faskes - - - Nama Dokter - - - Spesialisasi - - - Pasien - - - Dokter - - */} - - {dataTableIsLoading ? ( - - - - Loading - - - - ) : (dataTableData.data && dataTableData.data.length === 0) ? ( - - - - - No Data - - - - ) : ( - - {dataTableData && dataTableData.map((row) => ( - - ))} - - )} -
-
- - -
-
-); -} \ No newline at end of file diff --git a/frontend/dashboard/src/pages/Report/Prescription/listnya b/frontend/dashboard/src/pages/Report/Prescription/listnya deleted file mode 100755 index e69de29b..00000000 diff --git a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Create.tsx b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Create.tsx deleted file mode 100755 index efb7a395..00000000 --- a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Create.tsx +++ /dev/null @@ -1,93 +0,0 @@ -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 deleted file mode 100755 index 39885db8..00000000 --- a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Form.tsx +++ /dev/null @@ -1,260 +0,0 @@ -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/Show.tsx b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Show.tsx deleted file mode 100755 index be9d1c46..00000000 --- a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Show.tsx +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100755 index 8105b8b1..00000000 --- a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/View.tsx +++ /dev/null @@ -1,275 +0,0 @@ -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 d12d8ae6..2fa32d41 100755 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -12,7 +12,6 @@ import VerifyCode from '../pages/auth/VerifyCode'; import { AuthProvider } from '../contexts/LaravelAuthContext'; import AuthGuard from '../guards/AuthGuard'; import { Link, useParams, useSearchParams } from 'react-router-dom'; -import Prescription from '@/pages/Report/Prescription/Index'; import DoctorRating from '@/pages/Report/DoctorRating_v2/Index'; import KatalogDokter from '@/pages/Report/KatalogDokter/Index'; @@ -460,7 +459,14 @@ export default function Router() { path: 'report/phr', element: , }, - + { + path: 'report/prescription', + element: , + }, + { + path: 'report/rujukan', + element: , + }, { path: 'claims', element: , @@ -727,6 +733,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 RujukanPasien = Loadable(lazy(() => import('../pages/Report/Rujukan/Index'))); +const Prescription = Loadable(lazy(() => import('../pages/Report/Prescription/Index'))); const MasterDrug = Loadable(lazy(() => import('../pages/Master/Drug/Index'))); diff --git a/public/files/Report-Resep-Online.xlsx b/public/files/Report-Resep-Online.xlsx new file mode 100644 index 00000000..9149e22f Binary files /dev/null and b/public/files/Report-Resep-Online.xlsx differ diff --git a/public/files/Report-Riwayat-Rekam-Medis.xlsx b/public/files/Report-Riwayat-Rekam-Medis.xlsx index 95d6896a..a5dd42e7 100644 Binary files a/public/files/Report-Riwayat-Rekam-Medis.xlsx and b/public/files/Report-Riwayat-Rekam-Medis.xlsx differ