From f75f8ddb51c8a9628b1594cdb858c30740c1bdd0 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 23 Jan 2025 14:55:58 +0700 Subject: [PATCH 1/2] tambah fitur dokter online --- .../Api/DoctorOnlineController.php | 252 ++++ Modules/Internal/Routes/api.php | 4 + app/Models/OLDLMS/DoctorOnline.php | 35 + database/seeders/NavigationSeeder.php | 5 + database/seeders/PermissionTableSeeder.php | 1 + .../src/pages/Report/DoctorOnline/Index.tsx | 35 + .../src/pages/Report/DoctorOnline/List.tsx | 1012 +++++++++++++++++ .../src/pages/Report/Rujukan/Index.tsx | 0 .../src/pages/Report/Rujukan/List.tsx | 0 frontend/dashboard/src/routes/index.tsx | 5 + .../Report-Data-Dokter-Online-all-all.xlsx | Bin 0 -> 3389 bytes 11 files changed, 1349 insertions(+) create mode 100755 Modules/Internal/Http/Controllers/Api/DoctorOnlineController.php create mode 100755 app/Models/OLDLMS/DoctorOnline.php create mode 100755 frontend/dashboard/src/pages/Report/DoctorOnline/Index.tsx create mode 100755 frontend/dashboard/src/pages/Report/DoctorOnline/List.tsx mode change 100644 => 100755 frontend/dashboard/src/pages/Report/Rujukan/Index.tsx mode change 100644 => 100755 frontend/dashboard/src/pages/Report/Rujukan/List.tsx create mode 100644 public/files/Report-Data-Dokter-Online-all-all.xlsx diff --git a/Modules/Internal/Http/Controllers/Api/DoctorOnlineController.php b/Modules/Internal/Http/Controllers/Api/DoctorOnlineController.php new file mode 100755 index 00000000..faf7b23f --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/DoctorOnlineController.php @@ -0,0 +1,252 @@ +where('nID', $id); + } + + $doctorRatings = $query->with([ + 'user' => function ($query) { + $query->select('nID', 'sFirstName'); // Select only necessary columns + } + ]) + ->select('nIDUser', 'sDate', 'sStatus') + ->get(); + + + // $prescriptions->toArray(); + // dd($prescriptions); + + return response()->json($doctorRatings); + // return response()->json(Helper::paginateResources(LivechatResource::collection($livechat))); + } + + public function getData(Request $request) + { + $limit = $request->has('per_page') ? $request->input('per_page') : 50; + $results = DB::connection('oldlms')->table('tx_users_online') + ->leftJoin('tm_users', 'tx_users_online.nIDUser', '=', 'tm_users.nID') + ->leftJoin('tm_dokter', 'tx_users_online.nIDUser', '=', 'tm_dokter.nIDUser') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('tm_users.sFirstname', 'like', "%" . $search . "%"); + $query->orWhere('tx_users_online.sStatus', 'like', "%" . $search . "%"); + }); + }) + ->when($request->has('orderBy'), function ($query) use ($request) { + $orderBy = $request->orderBy; + $direction = $request->order ?? 'asc'; + + $query->orderBy($orderBy, $direction); + }) + ->when($request->input('start_date') , function ($query, $start_date) { + $query->where(function ($query) use ($start_date) { + $query->where('tx_users_online.sDate', '>=', $start_date. ' 00:00:00'); + }); + }) + ->when($request->input('end_date') , function ($query, $end_date) { + $query->where(function ($query) use ($end_date) { + $query->where('tx_users_online.sDate', '<=', $end_date. ' 23:59:59'); + }); + }) + ->select( + DB::connection('oldlms')->raw("CONCAT('dr. ', tm_users.sFirstName, ' ', IFNULL(tm_users.sMiddleName, ''), ' ', IFNULL(tm_users.sLastName, '')) as nama_dokter"), + 'tx_users_online.sStatus', + 'tx_users_online.sDate' + ) + ->paginate($limit); + + return response()->json(Helper::paginateResources($results)); + } + + public function export(Request $request) + { + $start_date = $request->input('start_date') ? $request->input('start_date') : 'all'; + $end_date = $request->input('end_date') ? $request->input('end_date') : 'all'; + $writer = WriterEntityFactory::createXLSXWriter(); + $writer->openToFile(public_path('files/Report-Data-Rating-Dokter-'.$start_date.'-'.$end_date.'.xlsx')); + $header = [ + 'No', + 'Nama Dokter', + 'Date', + 'Status', + ]; + $style = (new StyleBuilder()) + ->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + + $headerRow = WriterEntityFactory::createRowFromArray($header, $style); + $writer->addRow($headerRow); + // ============================ + $results = DB::connection('oldlms')->table('tx_users_online') + ->leftJoin('tm_users', 'tx_users_online.nIDUser', '=', 'tm_users.nID') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('tm_users.sFirstname', 'like', "%" . $search . "%"); + $query->orWhere('tm_users.sLastname', 'like', "%" . $search . "%"); + }); + }) + ->when($request->has('orderBy'), function ($query) use ($request) { + $orderBy = $request->orderBy; + $direction = $request->order ?? 'asc'; + + $query->orderBy($orderBy, $direction); + }) + ->when($request->input('start_date') , function ($query, $start_date) { + $query->where(function ($query) use ($start_date) { + $query->where('tx_users_online.sDate', '>=', $start_date. ' 00:00:00'); + }); + }) + ->when($request->input('end_date') , function ($query, $end_date) { + $query->where(function ($query) use ($end_date) { + $query->where('tx_users_online.sDate', '<=', $end_date. ' 23:59:59'); + }); + }) + // ->when($request->input('provider') , function ($query, $provider) { + // $query->where(function ($query) use ($provider) { + // $query->where('request_logs.organization_id', '=', $provider); + // }); + // }) + // ->where('files.fileable_type', '=', 'App\Models\RequestLog') + // ->where('request_logs.final_log', '=', '1') + // ->where('request_logs.status_final_log', '=', 'approved') + ->select( + DB::connection('oldlms')->raw("CONCAT('dr. ', tm_users.sFirstName, ' ', IFNULL(tm_users.sMiddleName, ''), ' ', IFNULL(tm_users.sLastName, '')) as nama_dokter"), + 'tx_users_online.sStatus', + 'tx_users_online.sDate' + ) + ->get(); + $no=0; + foreach($results as $item) + { + $no++; + $rowData = [ + $no, + $item->nama_dokter, + $item->sDate, + $item->sStatus + ]; + $style = (new StyleBuilder()) + //->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + $row = WriterEntityFactory::createRowFromArray($rowData, $style); + $writer->addRow($row); + } + $footer = [ + '', + '', + '', + '', + ]; + $style = (new StyleBuilder()) + ->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + + $footerRow = WriterEntityFactory::createRowFromArray($footer, $style); + $writer->addRow($footerRow); + + $writer->close(); + + return Helper::responseJson([ + 'file_name' => 'Report-Data-Dokter-Online'. $start_date.'-'.$end_date, + "file_url" => url('files/Report-Data-Dokter-Online-'. $start_date.'-'.$end_date.'.xlsx') + ]); + } + + /** + * 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) + { + + } + + /** + * 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) + { + // + } +} diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 327e07b0..3fde67d7 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -26,6 +26,7 @@ use Modules\Internal\Http\Controllers\Api\DivisionController; use Modules\Internal\Http\Controllers\Api\HospitalController; use Modules\Internal\Http\Controllers\Api\DoctorController; use Modules\Internal\Http\Controllers\Api\DoctorRatingController; +use Modules\Internal\Http\Controllers\Api\DoctorOnlineController; use Modules\Internal\Http\Controllers\Api\DrugController; use Modules\Internal\Http\Controllers\Api\FormulariumController; use Modules\Internal\Http\Controllers\Api\FormulariumTemplateController; @@ -350,6 +351,9 @@ Route::prefix('internal')->group(function () { Route::get('get-doctorrating', [DoctorRatingController::class, 'getData']); Route::get('export-doctorrating', [DoctorRatingController::class, 'export']); + Route::get('get-doctoronline', [DoctorOnlineController::class, 'getData']); + Route::get('export-doctoronline', [DoctorOnlineController::class, 'export']); + Route::get('get-dokter-katalog', [KatalogDokterController::class, 'getData']); Route::get('export-dokter-katalog', [KatalogDokterController::class, 'export']); diff --git a/app/Models/OLDLMS/DoctorOnline.php b/app/Models/OLDLMS/DoctorOnline.php new file mode 100755 index 00000000..db9dbc11 --- /dev/null +++ b/app/Models/OLDLMS/DoctorOnline.php @@ -0,0 +1,35 @@ +belongsTo(User::class, 'nIDUser'); + } + + // Include additional fields in the model's JSON form + protected $appends = [ + 'user_first_name', // Include the attribute for user's first name + ]; + + // Define an accessor to get the first name of the related user + public function getUserFirstNameAttribute() + { + return $this->user ? $this->user->sFirstName : null; + } +} diff --git a/database/seeders/NavigationSeeder.php b/database/seeders/NavigationSeeder.php index 4a4610d0..7e1cf74b 100755 --- a/database/seeders/NavigationSeeder.php +++ b/database/seeders/NavigationSeeder.php @@ -173,6 +173,11 @@ class NavigationSeeder extends Seeder 'path' => '/report/doctor-rating', 'permission' => 'report-doctor-rating' ], + [ + 'title' => 'Doctor Online', + 'path' => '/report/doctor-online', + 'permission' => 'report-doctor-online' + ], [ 'title' => 'Katalog Dokter', 'path' => '/report/katalog-dokter', diff --git a/database/seeders/PermissionTableSeeder.php b/database/seeders/PermissionTableSeeder.php index cecb8f9e..f2d6af41 100755 --- a/database/seeders/PermissionTableSeeder.php +++ b/database/seeders/PermissionTableSeeder.php @@ -69,6 +69,7 @@ class PermissionTableSeeder extends Seeder 'report-livechat-list', 'report-livechat-payment', 'report-doctor-rating', + 'report-doctor-online', 'user-role-list', 'user-access-list', 'report-katalog-dokter' diff --git a/frontend/dashboard/src/pages/Report/DoctorOnline/Index.tsx b/frontend/dashboard/src/pages/Report/DoctorOnline/Index.tsx new file mode 100755 index 00000000..e93d773f --- /dev/null +++ b/frontend/dashboard/src/pages/Report/DoctorOnline/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 Index() { + const { themeStretch } = useSettings(); + + const { id } = useParams(); + + const pageTitle = 'Doctor Online'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/DoctorOnline/List.tsx b/frontend/dashboard/src/pages/Report/DoctorOnline/List.tsx new file mode 100755 index 00000000..4c5f3114 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/DoctorOnline/List.tsx @@ -0,0 +1,1012 @@ +// @mui +import { + Box, + Grid, + Button, + Card, + Collapse, + IconButton, + MenuItem, + Table, + TableBody, + TableCell, + TableRow, + TextField, + Typography, + Stack, + Menu, + ButtonGroup, + Tooltip, + TableHead, + Checkbox, + InputAdornment, + TableSortLabel, + FormControl + } from '@mui/material'; + import { visuallyHidden } from '@mui/utils'; + + import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers'; + import { fDateOnly } from '@/utils/formatTime'; + + import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; + import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined'; + import AssessmentIcon from '@mui/icons-material/Assessment'; + // hooks + import React, { ChangeEvent, useEffect, useRef, useState } from 'react'; + import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom'; + + import { LoadingButton } from '@mui/lab'; + // components + import axios from '../../../utils/axios'; + import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../../@types/paginated-data'; + import DataTable from '../../../components/LaravelTable'; + import { fCurrency } from '../../utils/formatNumber'; + import EditRoundedIcon from '@mui/icons-material/EditRounded'; + import { Chip } from '@mui/material'; + import Iconify from '@/components/Iconify'; + import { enqueueSnackbar } from 'notistack'; + import { fDate, fDateTime } from '../../../utils/formatTime'; + import { Claims } from '@/@types/claims'; + import Label from '@/components/Label'; + import { capitalizeFirstLetter } from '@/utils/formatString'; + import TableMoreMenu from '@/components/table/TableMoreMenu'; + import Edit from '@mui/icons-material/Edit'; + import { Download } from '@mui/icons-material'; + import { Add, Search } from '@mui/icons-material'; + import Autocomplete from '@mui/material/Autocomplete'; + + import DownloadIcon from '@mui/icons-material/Download'; + + import UploadIcon from '@mui/icons-material/Upload'; + import CancelIcon from '@mui/icons-material/Cancel'; + import CheckCircleIcon from '@mui/icons-material/CheckCircle'; + + import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material'; + import CloseIcon from '@mui/icons-material/Close'; + + + export default function List() { + const [selectAll, setSelectAll] = useState(false); + const [selectedRows, setSelectedRows] = useState([]); + const [providers, setProviders] = useState(null); + // const [searchText, setSearchText] = useState(''); + const [order, setOrder] = useState('desc'); + const [orderBy, setOrderBy] = useState('sDate'); + const [perPage, setPerPage] = useState(0); + + const handleChange = (event, newValue) => { + // Jika newValue tidak undefined, atur nilai dataProvider + if (newValue !== undefined) { + setDataProvider(newValue.service_code); + } else { + // Jika tidak ada yang dipilih, set dataProvider menjadi string kosong + setDataProvider(null); + } + }; + // Dummy data + const dummyServices = [ + { service_code: '1', name: 'Service 1' }, + { service_code: '2', name: 'Service 2' }, + { service_code: '3', name: 'Service 3' }, + // tambahkan data lain sesuai kebutuhan + ]; + + + + const handleSelectAll = () => { + setSelectAll(!selectAll); + if (!selectAll) { + const requestedIds = dataTableData.data + .filter(row => row.status === 'approved') // Memfilter baris dengan status 'requested' + .map(row => row.id); // Mengambil hanya ID dari baris-baris yang memenuhi kondisi + setSelectedRows(requestedIds); + } else { + setSelectedRows([]); + } + }; + + const handleRowSelect = (id) => { + if (selectedRows.includes(id)) { + setSelectedRows(selectedRows.filter(rowId => rowId !== id)); + } else { + setSelectedRows([...selectedRows, id]); + } + }; + + const [searchParams, setSearchParams] = useSearchParams(); + const [startDate, setStartDate] = useState(null); + const [searchText, setSearchText] = useState(''); + const [endDate, setEndDate] = useState(null); + const navigate = useNavigate(); + const [dataProvider, setDataProvider] = useState(null); + + useEffect(() => { + if (startDate !== null || endDate !== null || dataProvider !== null + || order !== null || orderBy !== null || perPage !== 0) { + loadDataTableData(); + getProvider(); + } + }, [startDate, endDate, dataProvider, order, orderBy, perPage]); + + const [isLoading, setIsLoading] = useState(false); + const [isLoadingImport, setIsLoadingImport] = useState(false); + const handleExportReport = async () => { + + + const year = startDate?.getFullYear(); + const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate = year && month && day ? `${year}-${month}-${day}` : ''; + + const year1 = endDate?.getFullYear(); + const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : ''; + + + + var filter = Object.fromEntries([...searchParams.entries()]); + setIsLoading(true) + await axios + .get('/export-doctoronline',{ + params: { + search: searchText, + start_date: formattedDate ? formattedDate : null, + end_date:formattedDate1, + provider: dataProvider, + order: order, + orderBy: orderBy, + page: perPage, + } + }) + .then((res) => { + enqueueSnackbar('Data berhasil di Export', { + variant: 'success', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }); + setIsLoading(false) + + document.location.href = res.data.data.file_url; + }) + .catch((err) => + enqueueSnackbar('Data Gagal di Export', { + variant: 'error', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }) + + ); + }; + + + function SearchInput(props: any) { + // SEARCH + const searchInput = useRef(null); + + + + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + props.onSearch({ search: searchText }); // Trigger to Parent + }; + + const handleGetData = (type :string) => { + axios.get(`claims/1/data-claim`) + .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(); + }) + } + + useEffect(() => { + // Trigger First Search + // setSearchText(searchParams.get('search') ?? ''); + }, []); + + return ( +
+ + + + +
+ ); + } + + function ImportForm(props: any) { + // IMPORT + // Create Button Menu + const [anchorEl, setAnchorEl] = React.useState(null); + + return ( +
+ + + {/* */} + +
+ ); + } + + const searchInput = useRef(null); + + + //handle search + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + loadDataTableData(); + }; + + + + useEffect(() => { + // Trigger First Search + //setSearchText(searchText); + }, []); + + const item = [ + { + id: '', + value: '', + name: 'Semua', + }, + ]; + + // const handleClick = () => { + + // } + + + + // Dummy Default Data + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const [dataTableData, setDataTableData] = useState( + LaravelPaginatedDataDefault + ); + + + + const loadDataTableData = async (appliedFilter: any | null = null) => { + setDataTableLoading(true); + const year = startDate?.getFullYear(); + const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate = year && month && day ? `${year}-${month}-${day}` : ''; + + const year1 = endDate?.getFullYear(); + const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : ''; + + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + const response = await axios.get('/get-doctoronline', { + params: { + search: searchText, + start_date: formattedDate ? formattedDate : null, + end_date:formattedDate1, + provider: dataProvider, + order: order, + orderBy: orderBy, + page: perPage, + } + }); + + setDataTableLoading(false); + + setDataTableData(response.data); + }; + + const getProvider = async () => { + const response = await axios.get('/claims/get-provider'); + setProviders(response.data) + } + + const applyFilter = async (searchFilter: { search: string }) => { + await loadDataTableData(searchFilter); + setSearchParams(searchFilter); + }; + + const handlePageChange = (event: ChangeEvent, value: number): void => { + setPerPage(value); + }; + + const [openDialogSubmit, setOpenDialogSubmit] = useState(false); + const handleCloseDialogSubmit = () => { + setOpenDialogSubmit(false); + } + + function toTitleCase(str: string | null) { + return str.replace(/\w\S*/g, function(txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); + } + + const [approve, setApprove] = useState(''); + + const [reasonDecline, setReasonDecline] = useState(''); + + const handleReasonDeclineChange = (event) => { + setReasonDecline(event.target.value); + // Tambahkan logika yang diperlukan di sini + }; + + const handleSubmitData = async () => { + try { + const response = await axios.post('download-zip', { selectedRows: selectedRows }); + const fileUrl = response.data.file_url; // Perbaikan disini + enqueueSnackbar('Data berhasil di download', { variant: 'success' }); + window.open(fileUrl, '_blank'); + setOpenDialogSubmit(false); + setTimeout(() => { + window.location.reload(); + }, 5000); // Reload the page after 5 seconds + } catch (error) { + enqueueSnackbar('Data Gagal di download', { variant: 'error' }); + } + }; + + const handleSubmitData1 = () => { + //approve or decline + if (!reasonDecline && approve == 'decline') { + enqueueSnackbar('Mohon isi alasan', { variant: 'warning' }); + return false; + } + Promise.all(selectedRows.map(send_bulk)) + .then(() => { + enqueueSnackbar('All requests processed successfully', { variant: 'success' }); + setOpenDialogSubmit(false); + setTimeout(() => { + window.location.reload(); + }, 5000); // Reload the page after 5 seconds + }) + .catch((error) => { + enqueueSnackbar(error.response?.data?.message ?? 'Something went wrong!', { variant: 'error' }); + }); + }; + + function send_bulk(id) { + return axios.post(`claims/${id}/${approve}`, { reasonDecline: reasonDecline }); + } + + + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); + const importClaimManagement = useRef(null); + const [currentImportFileName, setCurrentImportFileName] = useState(null); + const [importLoading, setImportLoading] = useState(false); + const [importResult, setImportResult] = useState(null); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const handleImportButton = () => { + if (importClaimManagement?.current) { + handleClose(); + importClaimManagement.current ? importClaimManagement.current.click() : console.log('No File selected'); + } else { + alert('No file selected'); + } + }; + const handleCancelImportButton = () => { + if(importClaimManagement.current) + { + importClaimManagement.current.value = ''; + importClaimManagement.current.dispatchEvent(new Event('change', { bubbles: true })); + } + }; + const handleImportChange = (event: any) => { + if (event.target.files[0]) { + setCurrentImportFileName(event.target.files[0].name); + } else { + setCurrentImportFileName(null); + } + }; + const handleUpload = () => { + if(importClaimManagement.current && importClaimManagement.current.files) + { + if (importClaimManagement.current?.files.length) { + const formData = new FormData(); + formData.append('file', importClaimManagement.current?.files[0]); + setImportLoading(true); + axios + .post('claims/import', formData) + .then((response) => { + handleCancelImportButton(); + loadDataTableData(); + setImportResult(response.data); + setImportLoading(false); + enqueueSnackbar('Success Import Claim Managemenet', { variant: 'success' }); + }) + .catch((response) => { + enqueueSnackbar( + 'Looks like something went wrong. Please check your data and try again. ' + + response.message, + { variant: 'error' } + ); + setImportLoading(false); + }); + } else { + enqueueSnackbar('No File Selected', { variant: 'warning' }); + } + } + }; + const handleGetTemplate = () => { + axios.get('claims/download-template').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(); + }); + + + }; + + const handleExportReportFiled = async () => { + + await axios + .post('claims/exportFiled', { params: importResult?.data.result_rows }) + .then((res) => { + enqueueSnackbar('Data berhasil di Export', { + variant: 'success', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }); + setIsLoading(false) + + document.location.href = res.data.data.file_url; + }) + .catch((err) => + enqueueSnackbar('Data Gagal di Export', { + variant: 'error', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }) + + ); + }; + + + // useEffect(() => { + // loadDataTableData(); + // getProvider(); + // }, []); + + const headStyle = { + fontWeight: 'bold', + }; + // const headCells = [ + // { + // id: 'dCreateOn', + // align: 'left', + // label: 'Date Submission', + // isSort: true, + // }, + // { + // id: 'code', + // align: 'left', + // label: 'Code', + // isSort: true, + // }, + // { + // id: 'name', + // align: 'left', + // label: 'Name', + // isSort: false, + // }, + // { + // id: 'provider', + // align: 'left', + // label: 'Provider', + // isSort: false, + // }, + // { + // id: 'files', + // align: 'left', + // label: 'Nama File', + // isSort: false, + // }, + // ]; + const headCells = [ + { + id: 'nama_dokter', + align: 'left', + label: 'Nama Dokter', + isSort: true, + }, + { + id: 'sDate', + align: 'left', + label: 'Date', + isSort: true, + }, + { + id: 'sStatus', + align: 'left', + label: 'Status', + isSort: false, + } + ]; + + const orders = { + order: order, + setOrder: setOrder, + orderBy: orderBy, + setOrderBy: setOrderBy, + }; + const createSortHandler = (property: string) => (event: React.MouseEvent) => { + handleRequestSort(event, property); + }; + const handleRequestSort = async (event: React.MouseEvent, property: string) => { + const isAsc = orders?.orderBy === property && orders?.order === 'asc'; + + orders?.setOrder(isAsc ? 'desc' : 'asc'); + orders?.setOrderBy(property); + }; + // Called on every row to map the data to the columns + function createData(data: Claims): Claims { + return { + ...data, + }; + } + + { + /* ------------------ TABLE ROW ------------------ */ + } + function Row(props: { row: ReturnType, isSelected: boolean, onSelect: (id: string) => void }) { + const { row, isSelected, onSelect } = props; + // Memperbaiki destrukturisasi props + + const handleRowCheckboxChange = () => { + onSelect(row.id); // Panggil fungsi onSelect dari komponen induk dengan id baris saat checkbox di baris diklik + }; + + const [open, setOpen] = React.useState(false); + + const test = 1000; + + return ( + + *': { borderBottom: 'unset' } }}> + {/* + setOpen(!open)}> + {open ? : } + + */} + {/* + + */} + {row?.nama_dokter} + {row?.sDate ? fDateTime(row?.sDate) : ''} + {row?.sStatus == 'Offline' ? : } + {/* {row?.dCreateOn ? fDateTime(row?.dCreateOn) : ''} */} + {/* {row?.code} */} + {/* {row.code} */} + {/* {row?.provider} */} + + {/* COLLAPSIBLE ROW */} + + + + {/* + + Description : {row.description} + + */} + + + + + ); + } + { + /* ------------------ END TABLE ROW ------------------ */ + } + + + + function TableContent() { + return ( + + {/* ------------------ TABLE HEADER ------------------ */} + + + {selectedRows.length > 0 ? ( + <> + + + + {selectedRows.length > 0 ? selectedRows.length : '0'}  Selected + + + + + + {/* + + */} + + + + + + ) : ( + <> + {/* + + */} + {headCells && + headCells.map((headCell, index) => ( + + {headCell.isSort ? ( + + {headCell.label} + {orders?.orderBy === headCell.id ? ( + + {orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + ) : ( + headCell.label + )} + + ))} + + + )} + + + + + {/* ------------------ END TABLE HEADER ------------------ */} + + {/* ------------------ TABLE ROW ------------------ */} + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length === 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} + {/* ------------------ END TABLE ROW ------------------ */} +
+ ); + } + + return ( + + + +
+ + + {!currentImportFileName && ( + <> + + { + if (event.key === 'Enter') { + handleSearchSubmit(event); + } + }} + InputProps={{ + startAdornment: ( + + + + ), + placeholder: 'Search Name', + }} + /> + + + + { + + // loadDataTableData(); + setStartDate(value); + }} + renderInput={(params) => } + /> + + + + { + setEndDate(value); + }} + renderInput={(params) => ( + + )} + /> + + + {/* + { + providers ? ( + option.name || ''} + value={providers.find((item) => item.id === dataProvider) || null} + onChange={(event, value) => { + if (value) { + setDataProvider(value.id); + } else { + setDataProvider(null); + } + }} + renderInput={(params) => ( + + )} + /> + ):( + <> + Loading... + + ) + } + */} + + {/* + } + sx={{ p: 1.8 }} + loading={isLoadingImport} + onClick={handleClick} + > + + Import + + + + + Import + + { + handleGetTemplate(); + }} + > + Download Template + + + */} + + } + sx={{ p: 1.8 }} + onClick={handleExportReport} + loading={isLoading} + > + + Export to Excel + + + + + + )} + {currentImportFileName && ( + + + + + + + + } + sx={{ p: 1.8 }} + onClick={handleUpload} + loading={importLoading} + > + Upload + + + + )} + {importResult && ( + + + Last Import Result :{' '} + + {importResult.data.total_success_row ?? 0} + {' '} + Row Processed,{' '} + + {importResult.data.total_failed_row} + {' '} + Failed, + {/* {importResult.data.failed_rows.map((row, index) => ( + [Code={row.code ? row.code : 'Required'}] + ))} */} +  Report: +  Download Data Result Import + + + )} + +
+
+
+ + } + /> + + + + + Confirmation + + + + + + + + + + Are you sure to Download this files selected ? + {approve == "decline" ? ( + + + + ): ''} + + + + + + + +
+ ); + } diff --git a/frontend/dashboard/src/pages/Report/Rujukan/Index.tsx b/frontend/dashboard/src/pages/Report/Rujukan/Index.tsx old mode 100644 new mode 100755 diff --git a/frontend/dashboard/src/pages/Report/Rujukan/List.tsx b/frontend/dashboard/src/pages/Report/Rujukan/List.tsx old mode 100644 new mode 100755 diff --git a/frontend/dashboard/src/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index 2fa32d41..c29ee22d 100755 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -13,6 +13,7 @@ import { AuthProvider } from '../contexts/LaravelAuthContext'; import AuthGuard from '../guards/AuthGuard'; import { Link, useParams, useSearchParams } from 'react-router-dom'; import DoctorRating from '@/pages/Report/DoctorRating_v2/Index'; +import DoctorOnline from '@/pages/Report/DoctorOnline/Index'; import KatalogDokter from '@/pages/Report/KatalogDokter/Index'; // ---------------------------------------------------------------------- @@ -446,6 +447,10 @@ export default function Router() { path: 'report/doctor-rating', element: , }, + { + path: 'report/doctor-online', + element: , + }, { path: 'report/katalog-dokter', element: , diff --git a/public/files/Report-Data-Dokter-Online-all-all.xlsx b/public/files/Report-Data-Dokter-Online-all-all.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..5a4872298b4c43d9fb376854aa98930f50fd47da GIT binary patch literal 3389 zcmZ`*2{hDw7oIRCWXmYTSQ4LPM#hpz_BBk|x1@}nk+G|+L&h?;gi?LhLX?tyYqIYl z8QCYzqSL}A-p6hJC1S42>yz4x1O4A{vXfTXmcH}zP^;fYSCMtJ1 znLQA}x-0bn{H1vQT%CY|r%5 z+zU*ba_u&|hMK5GRj1ae_MdbA1 zw(K!eyKv@)Aa#2QJiSDsGo{+aSLtKRhcnxGuCnH4^jmz$n;k6*@4hE?C>$>cEBrzn zbHtynS=vmEpQRT`xnr~@(WF)HH{xvm z!jsGoD$1`G@NWXEa!U6ahY^P}S1D+a&IPir21<_cPv%%pKNmE1Kko)ZU7Zi&EY*ue zN6RZ`jG^&wqv*ki>&ErYq#Rd1-ZL%mp`A6oH1AS@38i{Vi4}ExeDblF@n39->BI#0PhIp-$np-q4wdr{1Q@P`NSH zoPabojf-djji5g!xLlnun7M`IELi-CO8IQR3Tvt}-L+Wgz?5h1@u}srKV3}kfX_y= z3u`i-W;5jQl9L{lN!m40vAt%?ziTqnrSbd=!wl|8L-?Km5iwpK_fY+E8&f-Zd5O$> zSkV`H5d|1P(H=lij)PjpI-t>>Sj6w^{Jyx^eM*=RaSqzC?R#pyl_%)o;Yn$_6SrYs z;FVISEH?9x#_4Q1&1!b zcydcfzxFZ|Ru+uhQe4-@Txlo=7b>PfjX%AkQx+=nCAOQESXOAg@FW&k%t}eToGO@B z(T#Cukm#E!zznTOaM(R|OY2l^upYEU2 zQv$9Hx;vp$hto!7$)APg)=9Um@x%TaDk0wA8lR@_;c&1}@b(QZ(9_!&qN(LM!Q)3Y z_@ddk1$+85U~sc!bg9&h*s7`N_Gm}&NynC6_@{ic@tU!O=d-Jw!Xk{MMkir1HL?A?*9 zknT6BQw@t6MO9NDrel$PdlnDSPzoIAA3CrOHXdkO15Xb}H~Zga#?KPudqBN4Vy4tLKWfAtY=~SZ+einyQ5u%PLgy1C6ybjV!Wuxo% zsB^JZQK{W^5Ki^VQz>dv4?fok6kmTkhH(`hGZKG#Jh zWm}$fSbUtrVNnBVaUs)vC0f}FVrwKBKSc)?)4D4+^CXX%c5o9z#rvXIbz9RA+B;~{ zRTTD^m?o-o;sX8$LKG+GSmu9=smlSPPRd_OIY;in<7T%NW_#mTcA7RPf6jPuOF^A{ z&oVu?1n-g)pL$TYEAjX;?9907jr2w|IYqUG36}Cl5(QsQNLkrScDQM()3yA-k(I@E z^1LT%7X~F-c03rlKP(Ar>!&}=fBma+Yx&gSQgGBWWXc6-oB|HZ-|4h*ci(4IH@1Gi z3{)3)@NogUYkUHZc)lm#IY^jiez2{KphZuu4BvC?BR`_Jzi%TSEG|lT{;Pk#){$w4 zW#754Uh*Y!BJ>uIUHHPT*F0;km)v?RqGZRc)(1XUU4_i8zSc0zPb$vi5KR@)XzQPP zl;O9|6iZG%+U(TpWdYWweSE{I*9VoRZaAZ&T`$+RmRc@oZkoS0ZNL@j%5j&b*KrLQ zV_X|C#dtU=lsideKOrliMi_wmPp|*g-}TzSj132>gMY(y7N(jtBLIItqG~KpXnD)% zn!q1vK02ZVwTjQX;ik3wbC(cjC%3I7f3p=%E2f`kPJc|u{G`7ahS_8eS?+4m!KjD2 zkO0vzTe{M0&XnC%6)#EADpeWFxIWrCBzKre0hQ!Mx0XD z*Wo!o6dJGMiO6w`|IGo}&`JlGeE72N@I#tJ{x#FLkA>G(jZMxQ=7EdaQkIJJyz5(T z9bo_`*({|USGjgOA!F~3#A629DDS~z_eUDIyjFEdCs8^_s4i3g z`=$$U^81q&ynp?}-b)#Pa%1}g2Z6GJ{eWJi*xx9@luOS67z+plfDcw6N+{)YJ%FkK z^Y*Xj{@<*nj6!Mu2T^9hzoY!iFrbV>>6-^}e$xI)gwjPRBT%~6L4*>>KX@4>oU&UF p;52l9ga6gLlxWJfI6w~q=)W}yN(T(&0|L Date: Thu, 23 Jan 2025 15:32:06 +0700 Subject: [PATCH 2/2] bugs fix --- .../Api/DoctorOnlineController.php | 143 ++++++++---------- 1 file changed, 65 insertions(+), 78 deletions(-) diff --git a/Modules/Internal/Http/Controllers/Api/DoctorOnlineController.php b/Modules/Internal/Http/Controllers/Api/DoctorOnlineController.php index faf7b23f..d7571212 100755 --- a/Modules/Internal/Http/Controllers/Api/DoctorOnlineController.php +++ b/Modules/Internal/Http/Controllers/Api/DoctorOnlineController.php @@ -84,69 +84,76 @@ class DoctorOnlineController extends Controller public function export(Request $request) { + // Menyimpan tanggal mulai dan tanggal selesai dari request $start_date = $request->input('start_date') ? $request->input('start_date') : 'all'; $end_date = $request->input('end_date') ? $request->input('end_date') : 'all'; + + // Membuat writer untuk file XLSX $writer = WriterEntityFactory::createXLSXWriter(); - $writer->openToFile(public_path('files/Report-Data-Rating-Dokter-'.$start_date.'-'.$end_date.'.xlsx')); + + // Pastikan folder 'files' ada dan bisa ditulis + $filePath = public_path('files'); + if (!is_dir($filePath)) { + mkdir($filePath, 0755, true); // Membuat folder jika belum ada + } + + // Menyimpan file Excel ke folder yang sesuai + $fileName = 'Report-Data-Rating-Dokter-' . $start_date . '-' . $end_date . '.xlsx'; + $writer->openToFile(public_path('files/' . $fileName)); + + // Header Excel $header = [ 'No', 'Nama Dokter', 'Date', 'Status', ]; - $style = (new StyleBuilder()) - ->setFontBold() - // ->setFontSize(15) - // ->setFontColor(Color::BLUE) - // ->setShouldWrapText() - ->setCellAlignment(CellAlignment::LEFT) - // ->setBackgroundColor(Color::YELLOW) - ->build(); - $headerRow = WriterEntityFactory::createRowFromArray($header, $style); + // Styling untuk header (bold) + $headerStyle = (new StyleBuilder()) + ->setFontBold() // Menambahkan font bold hanya pada header + ->setCellAlignment(CellAlignment::LEFT) + ->build(); + + // Menambahkan header ke dalam file + $headerRow = WriterEntityFactory::createRowFromArray($header, $headerStyle); $writer->addRow($headerRow); - // ============================ - $results = DB::connection('oldlms')->table('tx_users_online') - ->leftJoin('tm_users', 'tx_users_online.nIDUser', '=', 'tm_users.nID') - ->when($request->input('search'), function ($query, $search) { - $query->where(function ($query) use ($search) { - $query->orWhere('tm_users.sFirstname', 'like', "%" . $search . "%"); - $query->orWhere('tm_users.sLastname', 'like', "%" . $search . "%"); - }); - }) - ->when($request->has('orderBy'), function ($query) use ($request) { - $orderBy = $request->orderBy; - $direction = $request->order ?? 'asc'; - $query->orderBy($orderBy, $direction); - }) - ->when($request->input('start_date') , function ($query, $start_date) { - $query->where(function ($query) use ($start_date) { - $query->where('tx_users_online.sDate', '>=', $start_date. ' 00:00:00'); - }); - }) - ->when($request->input('end_date') , function ($query, $end_date) { - $query->where(function ($query) use ($end_date) { - $query->where('tx_users_online.sDate', '<=', $end_date. ' 23:59:59'); - }); - }) - // ->when($request->input('provider') , function ($query, $provider) { - // $query->where(function ($query) use ($provider) { - // $query->where('request_logs.organization_id', '=', $provider); - // }); - // }) - // ->where('files.fileable_type', '=', 'App\Models\RequestLog') - // ->where('request_logs.final_log', '=', '1') - // ->where('request_logs.status_final_log', '=', 'approved') - ->select( - DB::connection('oldlms')->raw("CONCAT('dr. ', tm_users.sFirstName, ' ', IFNULL(tm_users.sMiddleName, ''), ' ', IFNULL(tm_users.sLastName, '')) as nama_dokter"), - 'tx_users_online.sStatus', - 'tx_users_online.sDate' + // Query untuk mengambil data dari database + $results = DB::connection('oldlms')->table('tx_users_online') + ->leftJoin('tm_users', 'tx_users_online.nIDUser', '=', 'tm_users.nID') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('tm_users.sFirstname', 'like', "%" . $search . "%"); + $query->orWhere('tm_users.sLastname', 'like', "%" . $search . "%"); + }); + }) + ->when($request->has('orderBy'), function ($query) use ($request) { + $orderBy = $request->orderBy; + $direction = $request->order ?? 'asc'; + $query->orderBy($orderBy, $direction); + }) + ->when($request->input('start_date'), function ($query, $start_date) { + $query->where('tx_users_online.sDate', '>=', $start_date . ' 00:00:00'); + }) + ->when($request->input('end_date'), function ($query, $end_date) { + $query->where('tx_users_online.sDate', '<=', $end_date . ' 23:59:59'); + }) + ->select( + DB::connection('oldlms')->raw("CONCAT('dr. ', tm_users.sFirstName, ' ', IFNULL(tm_users.sMiddleName, ''), ' ', IFNULL(tm_users.sLastName, '')) as nama_dokter"), + 'tx_users_online.sStatus', + 'tx_users_online.sDate' ) - ->get(); - $no=0; - foreach($results as $item) - { + ->get(); + + // Styling untuk baris data (tidak ada bold) + $dataStyle = (new StyleBuilder()) + ->setCellAlignment(CellAlignment::LEFT) + ->build(); + + // Menambahkan data baris ke dalam file + $no = 0; + foreach ($results as $item) { $no++; $rowData = [ $no, @@ -154,43 +161,23 @@ class DoctorOnlineController extends Controller $item->sDate, $item->sStatus ]; - $style = (new StyleBuilder()) - //->setFontBold() - // ->setFontSize(15) - // ->setFontColor(Color::BLUE) - // ->setShouldWrapText() - ->setCellAlignment(CellAlignment::LEFT) - // ->setBackgroundColor(Color::YELLOW) - ->build(); - $row = WriterEntityFactory::createRowFromArray($rowData, $style); + + $row = WriterEntityFactory::createRowFromArray($rowData, $dataStyle); $writer->addRow($row); } - $footer = [ - '', - '', - '', - '', - ]; - $style = (new StyleBuilder()) - ->setFontBold() - // ->setFontSize(15) - // ->setFontColor(Color::BLUE) - // ->setShouldWrapText() - ->setCellAlignment(CellAlignment::LEFT) - // ->setBackgroundColor(Color::YELLOW) - ->build(); - - $footerRow = WriterEntityFactory::createRowFromArray($footer, $style); - $writer->addRow($footerRow); + // Menutup writer setelah selesai menulis file $writer->close(); + // Mengirimkan response JSON return Helper::responseJson([ - 'file_name' => 'Report-Data-Dokter-Online'. $start_date.'-'.$end_date, - "file_url" => url('files/Report-Data-Dokter-Online-'. $start_date.'-'.$end_date.'.xlsx') + 'file_name' => $fileName, + 'file_url' => url('files/' . $fileName), ]); } + + /** * Show the form for creating a new resource. * @return Renderable