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 (
+
+ );
+ }
+
+ 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 ?? '-'}
+ {/*
+
+
+
+
+
+ */}
+
+
+
+
+ );
+ }
+
+ 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) => (
+
+ ))}
+
+ )}
+
+
+
+
+
+
+ );
+}