From 9710ce9977fceb23d18db037a094b2ae864adb2c Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Tue, 16 Apr 2024 16:00:23 +0700 Subject: [PATCH 01/52] Download zip --- .../Http/Controllers/Api/ClaimController.php | 141 ++- Modules/Internal/Routes/api.php | 10 +- .../Transformers/RequestLogShowResource.php | 4 +- .../layouts/dashboard/navbar/NavConfig.tsx | 1 + .../pages/CustomerService/FinalLog/Detail.tsx | 48 +- .../src/pages/Report/FilesProvider/Index.tsx | 35 + .../src/pages/Report/FilesProvider/List.tsx | 999 ++++++++++++++++++ frontend/dashboard/src/routes/index.tsx | 8 +- 8 files changed, 1191 insertions(+), 55 deletions(-) create mode 100644 frontend/dashboard/src/pages/Report/FilesProvider/Index.tsx create mode 100644 frontend/dashboard/src/pages/Report/FilesProvider/List.tsx diff --git a/Modules/Internal/Http/Controllers/Api/ClaimController.php b/Modules/Internal/Http/Controllers/Api/ClaimController.php index 5b33b40c..a7e214b8 100644 --- a/Modules/Internal/Http/Controllers/Api/ClaimController.php +++ b/Modules/Internal/Http/Controllers/Api/ClaimController.php @@ -34,6 +34,9 @@ use Modules\Internal\Transformers\RequestLogResource; use App\Models\RequestLog; +use ZipArchive; +use File; + use PDF; class ClaimController extends Controller @@ -91,13 +94,13 @@ class ClaimController extends Controller // (SELECT plans.code FROM plans WHERE plans.id = member_plans.plan_id LIMIT 1) AS plan_code // '), DB::raw(' - (SELECT plans.code - FROM plans + (SELECT plans.code + FROM plans WHERE plans.id IN ( - SELECT member_plans.plan_id - FROM member_plans + SELECT member_plans.plan_id + FROM member_plans WHERE member_plans.member_id = claim_requests.member_id - ) + ) AND plans.service_code = claim_requests.service_code) AS plan_code '), DB::raw(' @@ -116,13 +119,103 @@ class ClaimController extends Controller 'claim_requests.status_claim_management as status', ) ->paginate($limit); - + return response()->json(Helper::paginateResources($results)); } - public function downloadTemplate() + public function filesProvider(Request $request) + { + $limit = $request->has('per_page') ? $request->input('per_page') : 50; + $results = DB::table('request_logs') + ->leftJoin('members', 'request_logs.member_id', '=', 'members.id') + ->join('files', 'request_logs.id', '=', 'files.fileable_id') + // ->leftJoin('member_plans', 'member_plans.member_id', '=', 'members.id') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('members.name', 'like', "%" . $search . "%"); + $query->orWhere('request_logs.code', 'like', "%" . $search . "%"); + $query->orWhere('members.member_id', '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('request_logs.created_at', '>=', $start_date. ' 00:00:00'); + }); + }) + ->when($request->input('end_date') , function ($query, $end_date) { + $query->where(function ($query) use ($end_date) { + $query->where('request_logs.created_at', '<=', $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( + 'files.original_name as files', + 'files.id', + 'files.id AS id_log', + 'request_logs.code as code', + 'members.name', + 'request_logs.created_at', + DB::raw(' + (SELECT organizations.name FROM organizations WHERE organizations.id = request_logs.organization_id LIMIT 1) AS provider + '), + 'request_logs.status_final_log as status', + DB::raw("CONCAT('" . env('APP_URL') . "/storage/', path) as path") + ) + ->paginate($limit); + + + + return response()->json(Helper::paginateResources($results)); + } + + public function downloadZip(Request $request) + { + $selectedRows = $request->selectedRows; // asumsi $selectedRows berisi array ID file yang dipilih + $files = []; + + // Ambil path file dari database atau sumber lain sesuai dengan $selectedRows + $data = DB::table('files') + ->whereIn('id', $selectedRows) + ->select('path') + ->get(); + + foreach ($data as $value) { + $files[] = storage_path('app/public/' . $value->path); + } + + $zipFileName = 'downloaded_files.zip'; + $zip = new ZipArchive(); + + if ($zip->open(storage_path('app/public/' . $zipFileName), ZipArchive::CREATE | ZipArchive::OVERWRITE)) { + foreach ($files as $file) { + $zip->addFile($file, basename($file)); + } + + $zip->close(); + + // Mengembalikan response berupa URL file zip + return response()->json(['file_url' => env('APP_URL').Storage::url($zipFileName)], 200); + } else { + return response()->json(['message' => 'Gagal membuat file ZIP.'], 500); + } + } + + public function downloadTemplate() { return Helper::responseJson([ 'file_name' => "Template - Claim - Management.xlsx", @@ -155,7 +248,7 @@ class ClaimController extends Controller 'approval_by_claim_management' => auth()->user()->id, 'approval_date_claim_management' => date('Y-m-d H:i:s'), ]); - + if ($affectedRows === 0) { $check_status = DB::table('claim_requests') ->where('code','=', $row['code']) @@ -183,16 +276,16 @@ class ClaimController extends Controller $row['error'] = $e->getMessage(); if(!$row['code']) { - $row['error'] = 'Kolom Code wajib isi'; + $row['error'] = 'Kolom Code wajib isi'; } if(!$row['qc']) { - $row['error'] = 'Kolom QC wajib isi'; + $row['error'] = 'Kolom QC wajib isi'; } $result_rows[] = $row; $failedRows[] = $row; } - } + } $response = [ 'message' => 'File uploaded and data saved to database', @@ -217,7 +310,7 @@ class ClaimController extends Controller $row[] = $data[0][$i]; $header[] = $data[0][0]; } - + $filed = []; foreach ($header[0] as $value) { @@ -228,18 +321,18 @@ class ClaimController extends Controller $filed[] = $modelColumn; } } - + $result = []; foreach ($row as $subarray) { $trimmedSubarray = []; for ($i = 0; $i < count($filed); $i++) { $trimmedSubarray[$filed[$i]] = $subarray[$i] ? $subarray[$i] : null; } - + $result[] = $trimmedSubarray; } return $result; - } + } public function exportClaimManagement(Request $request) { @@ -314,13 +407,13 @@ class ClaimController extends Controller '), 'claim_requests.created_at', DB::raw(' - (SELECT plans.code - FROM plans + (SELECT plans.code + FROM plans WHERE plans.id IN ( - SELECT member_plans.plan_id - FROM member_plans + SELECT member_plans.plan_id + FROM member_plans WHERE member_plans.member_id = claim_requests.member_id - ) + ) AND plans.service_code = claim_requests.service_code) AS plan_code '), // DB::raw(' @@ -391,7 +484,7 @@ class ClaimController extends Controller ->setCellAlignment(CellAlignment::LEFT) // ->setBackgroundColor(Color::YELLOW) ->build(); - + $footerRow = WriterEntityFactory::createRowFromArray($footer, $style); $writer->addRow($footerRow); @@ -426,10 +519,10 @@ class ClaimController extends Controller $headerRow = WriterEntityFactory::createRowFromArray($header, $style); $writer->addRow($headerRow); // ============================ - + foreach($request->params as $item) { - + $rowData = [ $item['code'], $item['qc'], @@ -461,7 +554,7 @@ class ClaimController extends Controller ->setCellAlignment(CellAlignment::LEFT) // ->setBackgroundColor(Color::YELLOW) ->build(); - + $footerRow = WriterEntityFactory::createRowFromArray($footer, $style); $writer->addRow($footerRow); diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 320b5501..52bfb748 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -238,6 +238,8 @@ Route::prefix('internal')->group(function () { Route::post('claims/{claim_id}/set-final-encounter', [ClaimEncounterController::class, 'setFinalEncounter']); Route::get('claims', [ClaimController::class, 'index']); + Route::get('claims-files-provider', [ClaimController::class, 'filesProvider']); + Route::post('download-zip', [ClaimController::class, 'downloadZip']); Route::get('claims/download-template', [ClaimController::class, 'downloadTemplate']); Route::post('claims/import', [ClaimController::class, 'import']); Route::post('claims/exportFiled/', [ClaimController::class, 'exportFiled']); @@ -281,20 +283,20 @@ Route::prefix('internal')->group(function () { Route::post('customer-service/request/{id}/delete_file', [RequestLogController::class, 'deleteFiles']); Route::post('customer-service/request/final-log', [RequestLogController::class, 'updateFinalLog']); - + // search diagnosis Route::get('diagnosis', [RequestLogController::class, 'diagnosis']); - + // insert benefit Route::post('customer-service/request/insert-benefit', [RequestLogBenefitController::class, 'store']); Route::post('customer-service/request/benefit_data/{id}', [RequestLogBenefitController::class, 'destroy']); Route::put('customer-service/request/benefit_data/{id}', [RequestLogBenefitController::class, 'update']); - + // insert medicine Route::post('customer-service/request/medicine-data', [RequestLogMedicineController::class, 'store']); Route::delete('customer-service/request/medicine-data/{id}', [RequestLogMedicineController::class, 'destroy']); Route::put('customer-service/request/medicine-data/{id}', [RequestLogMedicineController::class, 'update']); - + Route::get('search-organizations', [OrganizationController::class, 'searchOrganization']); Route::get('search-specialities', [SpecialityController::class, 'searchSpeciality']); Route::resource('organizations', OrganizationController::class); diff --git a/Modules/Internal/Transformers/RequestLogShowResource.php b/Modules/Internal/Transformers/RequestLogShowResource.php index 1591701a..89f7e458 100644 --- a/Modules/Internal/Transformers/RequestLogShowResource.php +++ b/Modules/Internal/Transformers/RequestLogShowResource.php @@ -42,7 +42,7 @@ class RequestLogShowResource extends JsonResource $claimCode = $claimRequest->code; $isReversal = false; $isRole = auth()->user()->role_id; - if ($requestLog['status'] == 'approved' && + if ($requestLog['status'] == 'approved' && $requestLog['status_final_log'] == 'approved' && $claimRequest->status == 'approved' && $claimRequest->status_claim_management == 'approved' && @@ -108,7 +108,7 @@ class RequestLogShowResource extends JsonResource ->whereIn('code', $diagnosis) ->select('code', 'name') ->get(); - } + } $data = [ 'id' => $requestLog['id'], diff --git a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx index 17334caf..73c6570f 100644 --- a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx +++ b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx @@ -95,6 +95,7 @@ const navConfig = [ { title: 'REPORT', children: [ + { title: 'Files Provider', path: 'report/files-provider'}, { title: 'Letter of Guarantee', path: '/report/logs' }, { title: 'Appointment', path: '/report/appointments' }, { title: 'Live Chat', path: '/report/live-chat' }, diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx index 02a158e7..55a6066d 100644 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Detail.tsx @@ -45,7 +45,7 @@ import DialogHospitalCare from './Components/DialogHospitalCare'; import DialogBenefit from './Components/DialogBenefit'; import DialogMedicine from './Components/DialogMedicine'; import DialogDeleteBenfit from './Components/DialogDeleteBenefit'; -import DialogEditBenefit from './Components/DialogEditBenefit'; +import DialogEditBenefit from './Components/DialogEditBenefit'; import DialogDeleteMedicine from './Components/DialogDeleteMedicine' import MoreMenu from '@/components/MoreMenu'; @@ -69,7 +69,7 @@ export default function Detail() { const { themeStretch } = useSettings(); const [requestLog, setRequestLog] = useState(); const [isReversal, setIsReversal] = useState(false); - + const { id } = useParams(); @@ -111,7 +111,7 @@ export default function Detail() { // Handel Delete Detail Benefit const [idBenefitData, setIdBenefitData] = useState(); const [openDialogDeleteBenefit, setDialogDeleteBenefit] = useState(false) - + const [idMedicineData, setIdMedicineData] = useState(); const [openDialogDeleteMedicine, setDialogDeleteMedicine] = useState(false) @@ -121,7 +121,7 @@ export default function Detail() { const [openDialogEditBenefit, setDialogEditBenefit] = useState(false) const [BenefitConfigurationData, setBenefitConfigurationData] = useState(); - // Buat total data + // Buat total data const totalAmountIncurred = (requestLog?.benefit_data || []).reduce((accumulator, item) => { return accumulator + (item.amount_incurred || 0); }, 0); @@ -134,13 +134,13 @@ export default function Detail() { const totalExcessPaid = (requestLog?.benefit_data || []).reduce((accumulator, item) => { return accumulator + (item.excess_paid || 0); }, 0); - + const total = { totalAmountIncurred : totalAmountIncurred, totalAmountApproved : totalAmountApprove, totalAmountNotApproved : totalAmountNotApprove, totalExcessPaid : totalExcessPaid, - } + } // Handle Delete File LOG const [pathFile, setPathFile] = useState('') const [dialogDeleteFIleLog, setDialogDeleteFileLog] = useState(false) @@ -159,7 +159,7 @@ export default function Detail() { {/* Detail */} - + Detail @@ -180,7 +180,7 @@ export default function Detail() { } /> ) : null } - + Provider @@ -265,16 +265,16 @@ export default function Detail() { {/* Service */} - - + {/* Exclusion */} - {/* */} @@ -297,7 +297,7 @@ export default function Detail() { {requestLog?.benefit_data?.map((item, index) => ( - + @@ -422,14 +422,14 @@ export default function Detail() { - + ))}

{requestLog?.benefit_data && requestLog.benefit_data.length > 0 ? ( - + @@ -438,7 +438,7 @@ export default function Detail() { Total Benefit - + @@ -511,13 +511,13 @@ export default function Detail() { - + - ) + ) : ( null - )} + )} {/* PR Buat pindahin ke componen */} @@ -530,7 +530,7 @@ export default function Detail() { requestLog={requestLog} openDialog={openDialogBenefit} setOpenDialog={setDialogBenefit} - + /> {/* Dialog Edit */} {/* Dialog Delete */} - {/* Dialog Edit Detai; */} - ) : null } - + {requestLog?.files?.map((documentType, index) => ( @@ -640,7 +640,7 @@ export default function Detail() { ) : null } - + ))} @@ -696,7 +696,7 @@ export default function Detail() { ) : null} - + diff --git a/frontend/dashboard/src/pages/Report/FilesProvider/Index.tsx b/frontend/dashboard/src/pages/Report/FilesProvider/Index.tsx new file mode 100644 index 00000000..d3de2a44 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/FilesProvider/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 = 'Files Provider'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/FilesProvider/List.tsx b/frontend/dashboard/src/pages/Report/FilesProvider/List.tsx new file mode 100644 index 00000000..6dffa607 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/FilesProvider/List.tsx @@ -0,0 +1,999 @@ +// @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('created_at'); + 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('/claims/export-claim-management',{ + 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('/claims-files-provider', { + 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: 'created_at', + 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 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?.created_at ? fDateTime(row?.created_at) : ''} + {row?.code} + {/* {row.code} */} + {row?.name} + {row?.provider} + + + {row.files ? row.files : '-'} + + + + {/* 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 Code or 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 + + + + */} + + )} + {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/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index c6534bfa..a1e4c8ae 100644 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -418,6 +418,10 @@ export default function Router() { path: 'report/appointments/:id/edit', element: , }, + { + path: 'report/files-provider', + element: , + }, { path: 'report/live-chat', element: , @@ -630,7 +634,7 @@ const InpatientMonitoring = Loadable(lazy(() => import('../pages/CaseManagement/ /** * Customer Service */ -// Request +// Request const RequestLog = Loadable(lazy(() => import('../pages/CustomerService/Request/Index'))) const RequestLogDetail = Loadable(lazy(() => import('../pages/CustomerService/Request/Detail'))) // Final LOG @@ -662,6 +666,8 @@ const Appointment = Loadable(lazy(() => import('../pages/Report/Appointments/Ind const AppointmentCreate = Loadable(lazy(() => import('../pages/Report/Appointments/Create'))); const AppointmentShow = Loadable(lazy(() => import('../pages/Report/Appointments/Show'))); +const FilesProvider = Loadable(lazy(() => import('../pages/Report/FilesProvider/Index'))); + const Livechat = Loadable(lazy(() => import('../pages/Report/Livechat/Index'))); const LivechatCreate = Loadable(lazy(() => import('../pages/Report/Livechat/Create'))); const LivechatShow = Loadable(lazy(() => import('../pages/Report/Livechat/Show'))); From 1f26289b7aa1a2a0211bdbf09b00c43966eec543 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 18 Apr 2024 08:46:49 +0700 Subject: [PATCH 02/52] API Home dan Linking --- .../Internal/Transformers/DoctorResource.php | 2 + .../Http/Controllers/Api/HomeController.php | 75 +++++ .../Controllers/Api/LinkingController.php | 37 +++ Modules/Linksehat/Routes/api.php | 7 + .../Transformers/Home/HomeResource.php | 297 ++++++++++++++++++ app/Helpers/Helper.php | 30 ++ app/Http/Resources/OLDLMS/MemberResource.php | 48 +++ app/Models/Person.php | 2 + ...2_133447_add_coloumn_to_benefits_table.php | 32 ++ ..._16_134401_add_column_to_persons_table.php | 39 +++ ...add_column_to_practitioner_roles_table.php | 32 ++ 11 files changed, 601 insertions(+) create mode 100644 Modules/Linksehat/Http/Controllers/Api/HomeController.php create mode 100644 Modules/Linksehat/Transformers/Home/HomeResource.php create mode 100644 database/migrations/2024_02_12_133447_add_coloumn_to_benefits_table.php create mode 100644 database/migrations/2024_04_16_134401_add_column_to_persons_table.php create mode 100644 database/migrations/2024_04_16_170457_add_column_to_practitioner_roles_table.php diff --git a/Modules/Internal/Transformers/DoctorResource.php b/Modules/Internal/Transformers/DoctorResource.php index 5889b3a5..b49477d1 100644 --- a/Modules/Internal/Transformers/DoctorResource.php +++ b/Modules/Internal/Transformers/DoctorResource.php @@ -61,6 +61,8 @@ class DoctorResource extends JsonResource 'speciality_id' => $item->speciality->id, ]; }), + 'period_start' => $items->pluck('period_start')->first(), + 'period_end' => $items->pluck('period_end')->first(), ]; }); diff --git a/Modules/Linksehat/Http/Controllers/Api/HomeController.php b/Modules/Linksehat/Http/Controllers/Api/HomeController.php new file mode 100644 index 00000000..a405e697 --- /dev/null +++ b/Modules/Linksehat/Http/Controllers/Api/HomeController.php @@ -0,0 +1,75 @@ +where('nId', $request->id) + ->first(); + return Helper::responseJson([ + 'home' => HomeResource::make($user, $request), + ]); + } + + public function listHospital(Request $request){ + // Hospital List + $hospitalList = []; + $hospitals = Organization::where([ + 'type' => 'hospital', + 'status' => 'active', + ]) + ->with('currentAddress') + ->get()->toArray(); + foreach($hospitals as $hospital){ + $lat = 0; + $lang = 0; + if ($hospital['current_address']['lat']){ + $lat = $hospital['current_address']['lat']; + } + if ($hospital['current_address']['lng']){ + $lang = $hospital['current_address']['lng']; + } + + $address = ''; + if ($hospital['current_address']['text']){ + $address = $hospital['current_address']['text']; + } + + $radius = 0; + if ($lat && $lang && $request->longitude && $request->latitude){ + $radius = round(Helper::calculateDistance($lat, $lang, $request->latitude, $request->longitude), 2); + } + + $data = [ + 'name' => $hospital['name'], + 'radius' => $radius, + 'image' => '', + 'address' => $address + ]; + + array_push($hospitalList, $data); + } + usort($hospitalList, function($a, $b) { + return $a['radius'] <=> $b['radius']; + }); + return Helper::responseJson([ + 'hospital' => $hospitalList + ]); + } +} diff --git a/Modules/Linksehat/Http/Controllers/Api/LinkingController.php b/Modules/Linksehat/Http/Controllers/Api/LinkingController.php index 899dbfe2..331eeb68 100644 --- a/Modules/Linksehat/Http/Controllers/Api/LinkingController.php +++ b/Modules/Linksehat/Http/Controllers/Api/LinkingController.php @@ -5,6 +5,8 @@ namespace Modules\Linksehat\Http\Controllers\Api; use App\Helpers\Helper; use App\Http\Controllers\Controller; use App\Models\Person; +use App\Models\RequestLogBenefit; +use App\Models\RequestLog; use App\Models\Corporate; use App\Models\Member; use App\Models\OLDLMS\User; @@ -135,4 +137,39 @@ class LinkingController extends Controller $message = $member->currentPolicy->corporate->welcome_message; return Helper::responseJson(data: MemberResource::make($member), message: $message); } + + public function card_detail($member_id, $id){ + $member = Member::where('member_id', $member_id)->get()->toArray(); + $requestLogBenefits = RequestLogBenefit::where('request_log_id', $id)->with('benefit')->get()->toArray(); + $requestLog = RequestLog::find($id)->first(); + $benefitItem = []; + $dataRequestLog = [ + 'code' => $requestLog['code'], + 'diagnosis' => Helper::diagnosisName($requestLog['diagnosis']), + 'service_type' => Helper::serviceName($requestLog['service_code']), + + ]; + foreach($requestLogBenefits as $requestLogBenefit) { + $data = [ + 'benefit_item' => $requestLogBenefit['benefit']['description'], + 'amount_incurred' => $requestLogBenefit['amount_incurred'], + 'amount_approved' => $requestLogBenefit['amount_approved'], + 'amount_not_approved' => $requestLogBenefit['amount_not_approved'], + 'excess_paid' => $requestLogBenefit['excess_paid'], + ]; + + $benefitItem[] = $data; + }; + $dataRequestLog['benefit_item'] = $benefitItem; + + // dd($dataRequestLog); + // $data = [ + // 'id' => $requestLog['id'], + // 'code' => $requestLog['code'], + // 'submission_date' => Carbon::parse($requestLog['submission_date'])->format('d M Y H:i:s'), + // 'provider_name' => $requestLog['organization']['name'], + // 'service' => Helper::serviceName($requestLog['service_code']) + // ]; + return Helper::responseJson(data:$dataRequestLog); + } } diff --git a/Modules/Linksehat/Routes/api.php b/Modules/Linksehat/Routes/api.php index 07fe7532..8dfff2f5 100644 --- a/Modules/Linksehat/Routes/api.php +++ b/Modules/Linksehat/Routes/api.php @@ -12,6 +12,7 @@ use Modules\Linksehat\Http\Controllers\Api\ProfileController; use Modules\Linksehat\Http\Controllers\Api\SearchController; use Modules\Linksehat\Http\Controllers\Api\SpecialityController; use Modules\Linksehat\Http\Controllers\Api\LinkingController; +use Modules\Linksehat\Http\Controllers\Api\HomeController; /* |-------------------------------------------------------------------------- @@ -63,6 +64,11 @@ Route::prefix('linksehat')->group(function () { Route::get('doctors/{id}', 'show')->name('doctors.show'); }); + Route::controller(HomeController::class)->group(function () { + Route::get('home', 'index')->name('homes.index'); + Route::get('home/hospital', 'listHospital')->name('homes.listHospital'); + }); + Route::middleware('auth:sanctum')->group(function () { Route::get('profile/{id}', [ProfileController::class, 'index'])->name('profile'); @@ -81,6 +87,7 @@ Route::prefix('linksehat')->group(function () { Route::post('manual-linking', [LinkingController::class, 'linkingValidate']); Route::get('card/{member_id}', [LinkingController::class, 'card']); + Route::get('card/{member_id}/{log_id}', [LinkingController::class, 'card_detail']); }); }); diff --git a/Modules/Linksehat/Transformers/Home/HomeResource.php b/Modules/Linksehat/Transformers/Home/HomeResource.php new file mode 100644 index 00000000..834a7fe9 --- /dev/null +++ b/Modules/Linksehat/Transformers/Home/HomeResource.php @@ -0,0 +1,297 @@ +request = $request; + parent::__construct($resource); + } + + public function toArray($request) + { + $memberProfile = User::with('detail')->where('nIDUser', $this->nID)->get()->toArray(); + $dataMemberProfile = []; + + $userInsurance = UserInsurance::where('nIDUser', $this->nID)->get()->first(); + $memberId = null; + $linking = false; + + $jam = date('G'); // Ambil jam dalam format 24 jam + + if ($jam < 12) { + $wellcome = "Good Morning!"; + } elseif ($jam < 18) { + $wellcome = "Good Afternoon!"; + } else { + $wellcome = "Good Evening!"; + } + + if($userInsurance){ + $memberId = $userInsurance->sNoPolis; + $linking = true; + } else { + $member = Member::where('email', $this->sEmail)->get()->first(); + $person = Person::where('phone', $this->sPhone)->get()->first(); + if ($member || $person){ // Autolinking + $corporateEmployee = CorporateEmployee::where('member_id', $member->id)->get()->first(); // cek corporate id empolyee/member + if ($corporateEmployee){ + $corporate = Corporate::findOrFail($corporateEmployee->corporate_id)->automatic_linking; // cek autocomplete + if ($corporate){ + if($member) { + // Insert into database linksehat + $insurance = UserInsurance::updateOrCreate( + [ + 'nIDUser' => $this->nID, + ], + [ + 'nIDUser' => $this->nID, + 'nIDInsurance' => $_ENV['LINKSEHAT_ASO_INSURANCE_ID'], + 'sNoPolis' => $member->member_id, + 'sNamaPeserta' => $member->fullName, + 'sKartuPeserta' => '', + 'sLayanan' => 'RJ,TC', + 'dStartDate' => $member->members_effective_date, + 'dExpireDate' => $member->members_expire_date, + 'dTanggalLahir' => $member->birth_date, + 'nNoKTP' => $member->nric != '' ? $member->nric : 0 , + 'sIsConfrimed' => 1, + 'sStatus' => 1, + ]); + $message = $member->currentPolicy->corporate->welcome_message; + $linking = true; + + $memberId = $member->member_id; + } + } + } + } + }; + + if (count($memberProfile) > 0){ + + $urlAvatarDefault = $this->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $this->detail->sImage ?? $urlAvatarDefault; + $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $this->nIDHubunganKeluarga)->first('sHubunganKeluarga'); + + $dataUser = [ + 'id' => $this->nID, + 'name' => $this->sFirstName . ' ' . $this->sLastName, + 'relationship' => $relationship ? $relationship->sHubunganKeluarga : '-', + 'avatar' => $avatarMember + ]; + + array_push($dataMemberProfile, $dataUser); + + foreach($memberProfile as $m){ + $urlAvatarDefault = $m['detail']['nIDJenisKelamin'] == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $m['detail']['sImage'] ?? $urlAvatarDefault; + $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $m['nIDHubunganKeluarga'])->first('sHubunganKeluarga'); + + $data = [ + 'id' => $m['nID'], + 'name' => $m['full_name'], + 'relationship' => $relationship ? $relationship->sHubunganKeluarga : '-', + 'avatar' => $avatarMember, + ]; + + array_push( $dataMemberProfile, $data); + } + + + } else { + $nID = $this->nIDUser ? $this->nIDUser : $this->nID; + if ($nID){ + $memberProfile = User::with('detail')->where('nIDUser', $nID)->get()->toArray(); + + $dataMember = User::with('detail')->where('nID', $nID)->get()->first(); + + if ($this->detail){ + $urlAvatarDefault = $this->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + } else { + $urlAvatarDefault = 'https://linksehat.dev/assets/img/users/male-avatar.png'; + } + $avatar = $this->detail->sImage ?? $urlAvatarDefault; + + $avatarMember = $dataMember->detail->sImage ?? $urlAvatarDefault; + $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $this->nIDHubunganKeluarga)->first('sHubunganKeluarga'); + + $dataUser = [ + 'id' => $dataMember->nID, + 'name' => $dataMember->sFirstName . ' ' . $dataMember->sLastName, + 'relationship' => 'Me', + 'avatar' => $avatarMember + ]; + array_push($dataMemberProfile, $dataUser); + + if (count($memberProfile) > 0){ + foreach($memberProfile as $m){ + $urlAvatarDefault = $m['detail']['nIDJenisKelamin'] == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $m['detail']['sImage'] ?? $urlAvatarDefault; + $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $m['nIDHubunganKeluarga'])->first('sHubunganKeluarga'); + + $data = [ + 'id' => $m['nID'], + 'name' => $m['full_name'], + 'relationship' => $relationship->sHubunganKeluarga, + 'avatar' => $avatarMember, + ]; + + array_push( $dataMemberProfile, $data); + } + } + } else { + + } + } + + // Principal + if ($this->detail){ + $urlAvatarDefault = $this->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + } else { + $urlAvatarDefault = 'https://linksehat.dev/assets/img/users/male-avatar.png'; + } + $avatar = $this->detail->sImage ?? $urlAvatarDefault; + + // Doctor livechat + $doctors = Practitioner::with('person', 'practitionerRoles.organization', 'practitionerRoles.speciality') + ->whereHas('person', function ($query) { + $query->where('isOnline', 1); // hanya online + }) + ->get() + ->toArray(); + $doctorsLivechat = []; + foreach($doctors as $doctor){ + $specialist = 'Umum'; + $year = 0; + $price = 0; + if (!empty($doctor['person']['start_date_work'])) { + $starExperience = Carbon::parse($doctor['person']['start_date_work'])->format('Y-m-d'); + $experience = Carbon::createFromFormat('Y-m-d', $starExperience); + $year = $experience->diffInYears(Carbon::now()); + } + if ($doctor['practitioner_roles']) { + if ($doctor['practitioner_roles'][0]['speciality']){ + $specialist = $doctor['practitioner_roles'][0]['speciality']['name']; + } + if ($doctor['practitioner_roles'][0]['price']){ + $price = $doctor['practitioner_roles'][0]['price']; + } + } + $data = [ + 'full_name' => $doctor['person']['name'], + 'specialist' => $specialist, + 'experience' => $year, + 'review' => $doctor['person']['review'], + 'price' => $price, + 'price_real' => $price + ]; + array_push($doctorsLivechat, $data); + } + + // Hospital List + $hospitalList = []; + + $hospitals = Organization::where([ + 'type' => 'hospital', + 'status' => 'active', + ]) + ->with('currentAddress') + ->get()->toArray(); + foreach($hospitals as $hospital){ + $lat = 0; + $lang = 0; + if ($hospital['current_address']['lat']){ + $lat = $hospital['current_address']['lat']; + } + if ($hospital['current_address']['lng']){ + $lang = $hospital['current_address']['lng']; + } + + $address = ''; + if ($hospital['current_address']['text']){ + $address = $hospital['current_address']['text']; + } + + $radius = 0; + if ($lat && $lang && $request->longitude && $request->latitude){ + $radius = round(Helper::calculateDistance($lat, $lang, $request->latitude, $request->longitude), 2); + } + + $data = [ + 'name' => $hospital['name'], + 'radius' => $radius, + 'image' => '', + 'address' => $address + ]; + + array_push($hospitalList, $data); + } + + + usort($hospitalList, function($a, $b) { + return $a['radius'] <=> $b['radius']; + }); + + $hospitalList = array_slice($hospitalList, 0, 5); + + return [ + 'id' => $this->nID, + 'message' => $wellcome, + 'full_name' => $this->sFirstName . ' '. $this->sLastName, + 'avatar' => $avatar, + // 'member_type' => $this->nIDUser ? 'Dependent' : 'Principal', + // 'member_profile' => $dataMemberProfile, + 'member_id' => $memberId, + 'linking' => $linking, + 'doctors_livechat' => [ + $doctorsLivechat + ], + 'hospital' => $hospitalList + + + // [ + // [ + // 'name' => 'PRIMAYA HOSPITAL NORTH BEKASI', + // 'image' => '', + // 'range' => 6 + // ], + // [ + // 'name' => 'PRIMAYA HOSPITAL TANGERANG', + // 'image' => '', + // 'range' => 10 + // ], + // ] + ]; + } +} diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 19088571..12892c3d 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -11,6 +11,7 @@ use Illuminate\Support\Facades\DB; use App\Models\Member; use App\Models\User; use App\Models\Service; +use App\Models\Icd; use DateTime; class Helper @@ -117,6 +118,16 @@ class Helper } } + public static function diagnosisName($code) + { + $icd = Icd::where('code', $code)->get()->first(); + if ($icd){ + return $icd->name; + } else { + return '-'; + } + } + public static function paginateResources($resource) { return [ @@ -443,4 +454,23 @@ class Helper } } + public static function calculateDistance($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371) { + // Convert degrees to radians + $latFrom = deg2rad($latitudeFrom); + $lonFrom = deg2rad($longitudeFrom); + $latTo = deg2rad($latitudeTo); + $lonTo = deg2rad($longitudeTo); + + // Calculate the change in coordinates + $deltaLat = $latTo - $latFrom; + $deltaLon = $lonTo - $lonFrom; + + // Apply Haversine formula + $a = sin($deltaLat / 2) * sin($deltaLat / 2) + cos($latFrom) * cos($latTo) * sin($deltaLon / 2) * sin($deltaLon / 2); + $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); + $distance = $earthRadius * $c; + + return $distance; + } + } diff --git a/app/Http/Resources/OLDLMS/MemberResource.php b/app/Http/Resources/OLDLMS/MemberResource.php index 610ce9cf..14d5f04d 100644 --- a/app/Http/Resources/OLDLMS/MemberResource.php +++ b/app/Http/Resources/OLDLMS/MemberResource.php @@ -4,6 +4,12 @@ namespace App\Http\Resources\OLDLMS; use App\Services\ClaimService; use App\Models\Corporate; +use App\Models\CorporateBenefit; +use App\Models\RequestLog; +use App\Models\RequestLogBenefit; +use App\Models\MemberPlan; +use App\Helpers\Helper; +use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; @@ -21,7 +27,46 @@ class MemberResource extends JsonResource $currentMemberPlan = $this->memberPlans?->first(); $limitTelecon = $currentMemberPlan->plan->limit_telecon ?? null; $limitTelecon = $this->totalUsage >= 6 ? null : $limitTelecon; + $services = MemberPlan::where('member_id', $this->id)->with('plan')->get()->toArray(); + $dataServices = []; + if ($services) { + foreach($services as $service) { + $serviceName = Helper::serviceName($service['plan']['service_code']); + $benefits = CorporateBenefit::where('plan_id', $service['plan_id'])->with('benefit')->get()->toArray(); + $dataBenefit = []; + foreach($benefits as $benefit){ + $dataBenefitItem = $benefit['benefit']['description']; + array_push($dataBenefit, $dataBenefitItem); + } + $data = [ + 'name' => $serviceName, + 'benefit' => $dataBenefit + ]; + + array_push($dataServices, $data); + } + } + // LOG + $dataLog = []; + $requestLogs = RequestLog::where('member_id', $this->id)->with('organization')->get()->toArray(); + $totalBenefit = 0; + if ($requestLogs) { + foreach($requestLogs as $requestLog) { + $requestLogBenefit = RequestLogBenefit::where('request_log_id', $requestLog['id'])->sum('amount_approved'); + $totalBenefit += $requestLogBenefit; + $data = [ + 'id' => $requestLog['id'], + 'code' => $requestLog['code'], + 'submission_date' => Carbon::parse($requestLog['submission_date'])->format('d M Y H:i:s'), + 'provider_name' => $requestLog['organization']['name'], + 'service' => Helper::serviceName($requestLog['service_code']) + ]; + + array_push($dataLog, $data); + } + } + $data = [ 'id' => $this->id, 'member_name' => $this->full_name, @@ -33,6 +78,9 @@ class MemberResource extends JsonResource 'start_date' => $this->members_effective_date, 'corporate_logo' => $_ENV['LMS_APP_STORAGE'] . $this->corporateLogo, 'valid_until' => $this->members_expire_date, + 'total_benefit_usage' => $totalBenefit, + 'service' => $dataServices, + 'histor_log' => $dataLog ]; return $data; diff --git a/app/Models/Person.php b/app/Models/Person.php index 4c61f862..e647f2cd 100644 --- a/app/Models/Person.php +++ b/app/Models/Person.php @@ -23,6 +23,8 @@ class Person extends Model 'birth_date', 'birth_place', 'language', + 'isOnline', + 'review', 'race', 'citizenship', 'current_employment', diff --git a/database/migrations/2024_02_12_133447_add_coloumn_to_benefits_table.php b/database/migrations/2024_02_12_133447_add_coloumn_to_benefits_table.php new file mode 100644 index 00000000..7a031803 --- /dev/null +++ b/database/migrations/2024_02_12_133447_add_coloumn_to_benefits_table.php @@ -0,0 +1,32 @@ +integer('type')->nullable()->default(1)->comment('1=Non COB, 2=Cob'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('benefits', function (Blueprint $table) { + $table->dropColumn('type'); + }); + } +}; diff --git a/database/migrations/2024_04_16_134401_add_column_to_persons_table.php b/database/migrations/2024_04_16_134401_add_column_to_persons_table.php new file mode 100644 index 00000000..06adb0d3 --- /dev/null +++ b/database/migrations/2024_04_16_134401_add_column_to_persons_table.php @@ -0,0 +1,39 @@ +dateTime('start_date_work')->nullable()->after('birth_place'); + $table->integer('isOnline') + ->default(0) + ->comment('0=offline, 1=online') + ->after('start_date_work'); + $table->double('review')->after('isOnline'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('persons', function (Blueprint $table) { + $table->dropColumn('start_date_work'); + $table->dropColumn('isOnline'); + $table->dropColumn('review'); + }); + } +}; diff --git a/database/migrations/2024_04_16_170457_add_column_to_practitioner_roles_table.php b/database/migrations/2024_04_16_170457_add_column_to_practitioner_roles_table.php new file mode 100644 index 00000000..46ce4053 --- /dev/null +++ b/database/migrations/2024_04_16_170457_add_column_to_practitioner_roles_table.php @@ -0,0 +1,32 @@ +integer('price')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('practitioner_roles', function (Blueprint $table) { + $table->dropColumn('price'); + }); + } +}; From b55fcaf235adbb4665d220dbc56ca8b20d3924bd Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Thu, 18 Apr 2024 11:24:16 +0700 Subject: [PATCH 03/52] Testing callback on server --- .../Http/Controllers/Api/DuitkuController.php | 264 ++++++++++++++++++ Modules/Linksehat/Routes/api.php | 10 +- app/Helpers/Helper.php | 11 +- 3 files changed, 280 insertions(+), 5 deletions(-) create mode 100644 Modules/Linksehat/Http/Controllers/Api/DuitkuController.php diff --git a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php new file mode 100644 index 00000000..035cbd1f --- /dev/null +++ b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php @@ -0,0 +1,264 @@ +setSandboxMode(true); + // set sanitizer (default : true) + $duitkuConfig->setSanitizedMode(false); + // set log parameter (default : true) + $duitkuConfig->setDuitkuLogs(false); + return $duitkuConfig; + } + + public function createInvoice(Request $request) + { + $data = [ + 'paymentMethod' => $request->paymentMethod, + 'paymentAmount' => $request->paymentAmount, + 'email' => $request->email, + 'phoneNumber' => $request->phoneNumber, + 'productDetails' => $request->productDetails, + 'merchantOrderId' => $request->merchantOrderId, + 'additionalParam' => $request->additionalParam, + 'merchantUserInfo' => $request->merchantUserInfo, + 'customerVaName' => $request->customerVaName, + // 'callbackUrl' => $request->callbackUrl, + // 'returnUrl' => $request->returnUrl, + // 'expiryPeriod' => $request->expiryPeriod, + 'firstName' => $request->firstName, + 'lastName' => $request->lastName, + 'alamat' => $request->alamat, + 'city' => $request->city, + 'postalCode' => $request->postalCode, + // 'countryCode' => $request->countryCode + ]; + $validator = Validator::make($request->all(), [ + 'paymentMethod' => 'nullable', + 'paymentAmount' => 'required', + 'email' => 'required|email', + 'phoneNumber' => 'nullable', + 'productDetails' => 'required', + 'merchantOrderId' => 'required', + 'additionalParam' => 'nullable', + 'merchantUserInfo' => 'nullable', + 'customerVaName' => 'required', + // 'callbackUrl' => 'required', + // 'returnUrl' => 'nullable', + // 'expiryPeriod' => 'required', + 'firstName' => 'required', + 'lastName' => 'required', + 'alamat' => 'required', + 'city' => 'required', + 'postalCode' => 'required', + // 'countryCode' => 'required' + + ], [ + 'paymentAmount.required' => 'Jumlah pembayaran harus diisi', + 'email.required' => 'Email harus diisi', + 'email.email' => 'Format email salah', + 'productDetails.required' => 'Judul pembayaran harus diisi', + 'merchantOrderId.required' => 'Order ID harus diisi', + 'customerVaName.required' => 'Nama panggilan pelanggan harus diisi', + 'firstName.required' => 'Nama depan pelanggan harus diisi', + 'lastName.required' => 'Nama belakang pelanggan harus diisi', + 'alamat.required' => 'Alamat pelanggan harus diisi', + 'city.required' => 'Kota pelanggan harus diisi', + 'postalCode.required' => 'Kode pos pelanggan harus diisi', + ]); + + if ($validator->fails()) + { + return Helper::responseJson( + data: $data, + status: 'Bad Request', + statusCode: 400, + message: $validator->errors() + ); + } + else + { + #CONTOH DARI DUITKU + // $paymentMethod = ""; // PaymentMethod list => https://docs.duitku.com/pop/id/#payment-method + // $paymentAmount = 10000; // Amount + // $email = "customer@gmail.com"; // your customer email + // $phoneNumber = "081234567890"; // your customer phone number (optional) + // $productDetails = "Test Payment"; + // $merchantOrderId = "2"; // from merchant, unique + // $additionalParam = ''; // optional + // $merchantUserInfo = ''; // optional + // $customerVaName = 'John Doe'; // display name on bank confirmation display + // $callbackUrl = 'http://YOUR_SERVER/callback'; // url for callback + // $returnUrl = 'http://YOUR_SERVER/return'; // url for redirect + // $expiryPeriod = 60; // set the expired time in minutes + + // // Customer Detail + // $firstName = "John"; + // $lastName = "Doe"; + + // // Address + // $alamat = "Jl. Kembangan Raya"; + // $city = "Jakarta"; + // $postalCode = "11530"; + // $countryCode = "ID"; + + $paymentMethod = $request->paymentMethod; // PaymentMethod list => https://docs.duitku.com/pop/id/#payment-method + $paymentAmount = $request->paymentAmount; // Amount + $email = $request->email; // your customer email + $phoneNumber = $request->phoneNumber; // your customer phone number (optional) + $productDetails = $request->productDetails; + $merchantOrderId = $request->merchantOrderId; // from merchant, unique + $additionalParam = $request->additionalParam; // optional + $merchantUserInfo = $request->merchantUserInfo; // optional + $customerVaName = $request->customerVaName; // display name on bank confirmation display + $callbackUrl = env('APP_URL').'/api/linksehat/callback-duitku'; // url for callback + $returnUrl = ''; // url for redirect + $expiryPeriod = 60; // set the expired time in minutes + + // Customer Detail + $firstName = $request->firstName; + $lastName = $request->lastName; + + // Address + $alamat = $request->alamat; + $city = $request->city; + $postalCode = $request->postalCode; + $countryCode = "ID"; + + $address = array( + 'firstName' => $firstName, + 'lastName' => $lastName, + 'address' => $alamat, + 'city' => $city, + 'postalCode' => $postalCode, + 'phone' => $phoneNumber, + 'countryCode' => $countryCode + ); + + $customerDetail = array( + 'firstName' => $firstName, + 'lastName' => $lastName, + 'email' => $email, + 'phoneNumber' => $phoneNumber, + 'billingAddress' => $address, + 'shippingAddress' => $address + ); + + // Item Details + $item1 = array( + 'name' => $productDetails, + 'price' => $paymentAmount, + 'quantity' => 1 + ); + + $itemDetails = array( + $item1 + ); + + $params = array( + 'paymentAmount' => $paymentAmount, + 'merchantOrderId' => $merchantOrderId, + 'productDetails' => $productDetails, + 'additionalParam' => $additionalParam, + 'merchantUserInfo' => $merchantUserInfo, + 'customerVaName' => $customerVaName, + 'email' => $email, + 'phoneNumber' => $phoneNumber, + 'itemDetails' => $itemDetails, + 'customerDetail' => $customerDetail, + 'callbackUrl' => $callbackUrl, + 'returnUrl' => $returnUrl, + 'expiryPeriod' => $expiryPeriod + ); + $duitkuConfig = $this->configuration(); + try { + // createInvoice Request + $responseDuitkuPop = \Duitku\Pop::createInvoice($params, $duitkuConfig); + + header('Content-Type: application/json'); + echo $responseDuitkuPop; + } catch (Exception $e) { + echo $e->getMessage(); + + } + } + } + + public function paymentMethod(Request $request) + { + $duitkuConfig = $this->configuration(); + try { + $paymentAmount = "10000"; //"YOUR_AMOUNT"; + $paymentMethodList = \Duitku\Pop::getPaymentMethod($paymentAmount, $duitkuConfig); + + header('Content-Type: application/json'); + echo $paymentMethodList; + } catch (Exception $e) { + echo $e->getMessage(); + } + } + + public function checkStatus(Request $request) + { + $duitkuConfig = $this->configuration(); + try { + $merchantOrderId = "2"; + $transactionList = \Duitku\Pop::transactionStatus($merchantOrderId, $duitkuConfig); + + header('Content-Type: application/json'); + $transaction = json_decode($transactionList); + + // var_dump($transactionList); + + if ($transaction->statusCode == "00") { + // Action Success + } else if ($transaction->statusCode == "01") { + // Action Pending + } else { + // Action Failed Or Expired + } + echo $transaction->statusCode; + } catch (Exception $e) { + echo $e->getMessage(); + } + } + + public function callback(Request $request) + { + $duitkuConfig = $this->configuration(); + try { + $callback = \Duitku\Pop::callback($duitkuConfig); + + header('Content-Type: application/json'); + $notif = json_decode($callback); + + // var_dump($callback); + + if ($notif->resultCode == "00") { + // Action Success + } else if ($notif->resultCode == "01") { + // Action Failed + } + } catch (Exception $e) { + http_response_code(400); + echo $e->getMessage(); + } + } +} diff --git a/Modules/Linksehat/Routes/api.php b/Modules/Linksehat/Routes/api.php index 8dfff2f5..9a4aaed8 100644 --- a/Modules/Linksehat/Routes/api.php +++ b/Modules/Linksehat/Routes/api.php @@ -5,6 +5,7 @@ use Modules\Linksehat\Http\Controllers\Api\AuthController; use Modules\Linksehat\Http\Controllers\Api\DashboardController; use Modules\Linksehat\Http\Controllers\Api\AutocompleteController; use Modules\Linksehat\Http\Controllers\Api\DoctorController; +use Modules\Linksehat\Http\Controllers\Api\DuitkuController; use Modules\Linksehat\Http\Controllers\Api\HospitalController; use Modules\Linksehat\Http\Controllers\Api\NotificationTokenController; use Modules\Linksehat\Http\Controllers\Api\PersonController; @@ -89,5 +90,12 @@ Route::prefix('linksehat')->group(function () { Route::get('card/{member_id}', [LinkingController::class, 'card']); Route::get('card/{member_id}/{log_id}', [LinkingController::class, 'card_detail']); + + }); -}); + + Route::post('create-invoice-duitku', [DuitkuController::class, 'createInvoice']); + Route::post('payment-method-duitku', [DuitkuController::class, 'paymentMethod']); + Route::post('check-status-duitku', [DuitkuController::class, 'checkStatus']); + Route::post('callback-duitku', [DuitkuController::class, 'callback']); +;}); diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 12892c3d..6643d50b 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -105,7 +105,7 @@ class Helper } else { return 'User not found.'; } - + } public static function serviceName($code) @@ -269,11 +269,14 @@ class Helper * * @param array|object $data * @param int $statusCode - * @param string $message + * @param string|array|object $message * @return JsonResponse */ - public static function responseJson(array|object $data = [], string $status = 'success', int $statusCode = Response::HTTP_OK, string $message = 'Data berhasil di ambil'): JsonResponse + public static function responseJson(array|object $data = [], string $status = 'success', int $statusCode = Response::HTTP_OK, string|array|object $message = 'Data berhasil di ambil'): JsonResponse { + if ($message instanceof \Illuminate\Support\MessageBag) { + $message = $message->first(); + } return response()->json([ 'status' => $status, 'statusCode' => $statusCode, @@ -450,7 +453,7 @@ class Helper } } else { // throw new ImportRowException(__('Format Date Invalid'), 0, null, $date_from_row); - return null; + return null; } } From d242339635b5445335ab85f4af8dc667f81f8a81 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Thu, 18 Apr 2024 13:51:47 +0700 Subject: [PATCH 04/52] Update insert callbak ke api logs --- .../Http/Controllers/Api/DuitkuController.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php index 035cbd1f..e32b0f77 100644 --- a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php +++ b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php @@ -256,6 +256,16 @@ class DuitkuController extends Controller } else if ($notif->resultCode == "01") { // Action Failed } + + DB::table('api_logs') + ->insert([ + 'type' => 'in', + 'target' => env('APP_URL').'/api/linksehat/callback-duitku', + 'request' => $callback, + 'created_by' => auth()->check() ? auth()->user()->id : null, + 'created_at' => date('Y-m-d H:i:s') + ]); + } catch (Exception $e) { http_response_code(400); echo $e->getMessage(); From fe486b15932231ed4f82599a1c8e25d79c531bdc Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Thu, 18 Apr 2024 15:06:57 +0700 Subject: [PATCH 05/52] Update payload check status duitku --- .../Http/Controllers/Api/DuitkuController.php | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php index e32b0f77..6dbb604b 100644 --- a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php +++ b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php @@ -218,25 +218,46 @@ class DuitkuController extends Controller public function checkStatus(Request $request) { $duitkuConfig = $this->configuration(); - try { - $merchantOrderId = "2"; - $transactionList = \Duitku\Pop::transactionStatus($merchantOrderId, $duitkuConfig); + $data = [ + 'merchantOrderId' => $request->merchantOrderId + ]; + $validator = Validator::make($request->all(), [ + 'merchantOrderId' => 'required', + ], [ + 'merchantOrderId.required' => 'Order ID harus diisi', + ]); - header('Content-Type: application/json'); - $transaction = json_decode($transactionList); + if ($validator->fails()) + { + return Helper::responseJson( + data: $data, + status: 'Bad Request', + statusCode: 400, + message: $validator->errors() + ); + } + else + { + try { + $merchantOrderId = $request->merchantOrderId; + $transactionList = \Duitku\Pop::transactionStatus($merchantOrderId, $duitkuConfig); - // var_dump($transactionList); + header('Content-Type: application/json'); + $transaction = json_decode($transactionList); - if ($transaction->statusCode == "00") { - // Action Success - } else if ($transaction->statusCode == "01") { - // Action Pending - } else { - // Action Failed Or Expired + // var_dump($transactionList); + + if ($transaction->statusCode == "00") { + // Action Success + } else if ($transaction->statusCode == "01") { + // Action Pending + } else { + // Action Failed Or Expired + } + echo $transaction->statusCode; + } catch (Exception $e) { + echo $e->getMessage(); } - echo $transaction->statusCode; - } catch (Exception $e) { - echo $e->getMessage(); } } From 5bc4158cfb69906798ef16ea3bb3997f9d2233f9 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Thu, 18 Apr 2024 15:31:32 +0700 Subject: [PATCH 06/52] Update redirect duitku --- .../Http/Controllers/Api/DuitkuController.php | 11 ++++++++++- Modules/Linksehat/Routes/api.php | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php index 6dbb604b..c2ecb497 100644 --- a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php +++ b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php @@ -12,6 +12,7 @@ use Illuminate\Routing\Controller; use Illuminate\Support\Facades\DB; use Exception; use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Facades\Redirect; class DuitkuController extends Controller { @@ -129,7 +130,7 @@ class DuitkuController extends Controller $merchantUserInfo = $request->merchantUserInfo; // optional $customerVaName = $request->customerVaName; // display name on bank confirmation display $callbackUrl = env('APP_URL').'/api/linksehat/callback-duitku'; // url for callback - $returnUrl = ''; // url for redirect + $returnUrl = env('APP_URL').'/api/linksehat/redirect-duitku';; // url for redirect $expiryPeriod = 60; // set the expired time in minutes // Customer Detail @@ -292,4 +293,12 @@ class DuitkuController extends Controller echo $e->getMessage(); } } + + public function redirect(Request $request) + { + $resultCode = $request->input('resultCode'); + $merchantOrderId = $request->input('merchantOrderId'); + $reference = $request->input('reference'); + return Redirect::to('https://linksehat.com/'); + } } diff --git a/Modules/Linksehat/Routes/api.php b/Modules/Linksehat/Routes/api.php index 9a4aaed8..0d080d08 100644 --- a/Modules/Linksehat/Routes/api.php +++ b/Modules/Linksehat/Routes/api.php @@ -98,4 +98,5 @@ Route::prefix('linksehat')->group(function () { Route::post('payment-method-duitku', [DuitkuController::class, 'paymentMethod']); Route::post('check-status-duitku', [DuitkuController::class, 'checkStatus']); Route::post('callback-duitku', [DuitkuController::class, 'callback']); + Route::get('redirect-duitku', [DuitkuController::class, 'redirect']); ;}); From a424721d89431ef8280303e04cbc2b1273b9bad9 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Mon, 22 Apr 2024 11:26:00 +0700 Subject: [PATCH 07/52] payment api --- .../Controllers/Api/LivechatController.php | 235 ++++++++++++++++++ Modules/Linksehat/Routes/api.php | 25 +- .../Transformers/Home/HomeResource.php | 17 +- .../Livechat/LivechatResource.php | 85 +++++++ app/Helpers/DuitkuHelper.php | 33 +++ app/Models/Livechat.php | 37 +++ app/Models/PaymentsMethods.php | 20 ++ ...024_04_19_100246_create_livechat_table.php | 46 ++++ ...2_084927_add_column_to_livechats_table.php | 32 +++ 9 files changed, 506 insertions(+), 24 deletions(-) create mode 100644 Modules/Linksehat/Http/Controllers/Api/LivechatController.php create mode 100644 Modules/Linksehat/Transformers/Livechat/LivechatResource.php create mode 100644 app/Helpers/DuitkuHelper.php create mode 100644 app/Models/Livechat.php create mode 100644 app/Models/PaymentsMethods.php create mode 100644 database/migrations/2024_04_19_100246_create_livechat_table.php create mode 100644 database/migrations/2024_04_22_084927_add_column_to_livechats_table.php diff --git a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php new file mode 100644 index 00000000..c2db0d4e --- /dev/null +++ b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php @@ -0,0 +1,235 @@ +where('nId', $request->id) + ->first(); + return Helper::responseJson([ + 'livechat' => LivechatResource::make($user, $request), + ]); + } + + public function consultation(Request $request) + { + $dataMemberProfile = []; + $user = User::with('detail') + ->where('nId', $request->member_id) + ->first(); + $memberProfile = User::with('detail')->where('nIDUser', $request->member_id)->get()->toArray(); + if (count($memberProfile) > 0){ + $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $user->detail->sImage ?? $urlAvatarDefault; + $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $user->nIDHubunganKeluarga)->first('sHubunganKeluarga'); + + $dataUser = [ + 'id' => $user->nID, + 'name' => $user->sFirstName . ' ' . $user->sLastName, + 'relationship' => $relationship ? $relationship->sHubunganKeluarga : '-', + 'avatar' => $avatarMember + ]; + + array_push($dataMemberProfile, $dataUser); + + foreach($memberProfile as $m){ + $urlAvatarDefault = $m['detail']['nIDJenisKelamin'] == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $m['detail']['sImage'] ?? $urlAvatarDefault; + $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $m['nIDHubunganKeluarga'])->first('sHubunganKeluarga'); + + $data = [ + 'id' => $m['nID'], + 'name' => $m['full_name'], + 'relationship' => $relationship ? $relationship->sHubunganKeluarga : '-', + 'avatar' => $avatarMember, + ]; + + array_push($dataMemberProfile, $data); + } + } else { + $nID = $user->nIDUser ? $user->nIDUser : $user->nID; + if ($nID){ + $memberProfile = User::with('detail')->where('nIDUser', $nID)->get()->toArray(); + + $dataMember = User::with('detail')->where('nID', $nID)->get()->first(); + + if ($user->detail){ + $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + } else { + $urlAvatarDefault = 'https://linksehat.dev/assets/img/users/male-avatar.png'; + } + $avatar = $user->detail->sImage ?? $urlAvatarDefault; + + $avatarMember = $dataMember->detail->sImage ?? $urlAvatarDefault; + $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $user->detail->nIDHubunganKeluarga)->first('sHubunganKeluarga'); + + $dataUser = [ + 'id' => $dataMember->nID, + 'name' => $dataMember->sFirstName . ' ' . $dataMember->sLastName, + 'relationship' => 'Me', + 'avatar' => $avatarMember + ]; + array_push($dataMemberProfile, $dataUser); + + if (count($memberProfile) > 0){ + foreach($memberProfile as $m){ + $urlAvatarDefault = $m['detail']['nIDJenisKelamin'] == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $m['detail']['sImage'] ?? $urlAvatarDefault; + $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $m['nIDHubunganKeluarga'])->first('sHubunganKeluarga'); + + $data = [ + 'id' => $m['nID'], + 'name' => $m['full_name'], + 'relationship' => $relationship->sHubunganKeluarga, + 'avatar' => $avatarMember, + ]; + + array_push( $dataMemberProfile, $data); + } + } + } + } + + return Helper::responseJson([ + 'member' => $dataMemberProfile + ]); + } + + public function consultation_request(Request $request) + { + $data = [ + 'doctor_id' => $request->doctor_id, + 'patient_id' => $request->patient_id, + 'organization_id' => $request->organization_id, + 'descriptions' => $request->descriptions + ]; + + $validator = Validator::make($request->all(), [ + 'doctor_id' => 'required', + 'patient_id' => 'required', + 'descriptions' => 'required', + ], [ + 'doctor_id.required' => 'ID Dokter harus diisi', + 'patient_id.required' => 'ID Dokter harus diisi', + 'descriptions.required' => 'Description harus diisi', + ]); + + if ($validator->fails()){ + return Helper::responseJson( + status: 'Bad Request', + statusCode: 400, + message: $validator->errors() + ); + } else { + // insert table livechat + $timezone = date_default_timezone_get(); + $data['request_date'] = date('Y-m-d H:i:s'); + $data['timezone'] = $timezone; + $data['uuid'] = (string) Str::orderedUuid(); + $livechat = Livechat::create($data); + $doctor = $livechat->doctor; + $data = [ + 'id' => $livechat->id, + 'request_date' => $livechat->request_date, + 'image_path' =>'https' + ]; + + + return Helper::responseJson(data: $data); + } + } + public function consultation_request_show($id){ + $livechat = Livechat::where('id', $id)->with(['doctor', 'practitioner'])->first(); + $practitionerRole = PractitionerRole::where('id',$livechat->practitioner->id)->first(); + + $price = $practitionerRole->price ? $practitionerRole->price : 30000; + $discount = 0; + $adminFee = 5000; + $totalPay = $price + $adminFee - $discount; + $data = [ + 'id' => $livechat->id, + 'code_transaksi' => $livechat->uuid, + 'doctor_id' => $livechat->doctor_id, + 'doctor_name' => $livechat->doctor->name, + 'doctor_specialist' => 'Umum', + 'price' => $price, + 'admin_price' => $adminFee, + 'promo' => [ + [ + 'id' => 1, + 'code' => 'SEHATBERSAMA', + 'discount_percent' => 20 + ], + [ + 'id' => 2, + 'code' => 'MARET MERIAH', + 'discount_percent' => 5 + ], + ], + 'total' => $totalPay + + ]; + return Helper::responseJson(data: $data); + } + public function consultation_payment_choose($id){ + $livechat = Livechat::where('id', $id)->with(['doctor', 'practitioner'])->first(); + $practitionerRole = PractitionerRole::where('id',$livechat->practitioner->id)->first(); + $eWallet = PaymentsMethods::where( + [ + 'active' => 1, + 'config_pmc_id' => 3, + ])->get()->toArray(); + $va = PaymentsMethods::where( + [ + 'active' => 1, + 'config_pmc_id' => 2, + ])->get()->toArray(); + + $payment = DuitkuHelper::paymentMethod(); + + $price = $practitionerRole->price ? $practitionerRole->price : 30000; + $discount = 0; + $adminFee = 5000; + $totalPay = $price + $adminFee - $discount; + $data = [ + 'id' => $livechat->id, + 'code_transaksi' => $livechat->uuid, + 'price' => $price, + 'admin_price' => $adminFee, + 'total' => $totalPay, + 'payment_method' => [ + 'ewallet' => $eWallet, + 'va' => $va + ] + // 'payment_method' => json_decode($payment) + + ]; + return Helper::responseJson(data: $data); + } + + public function consultation_payment(Request $request){ + + $duitku = DuitkuHelper::paymentMethod(); + dd($duitku); + } +} diff --git a/Modules/Linksehat/Routes/api.php b/Modules/Linksehat/Routes/api.php index 9a4aaed8..01de1ffe 100644 --- a/Modules/Linksehat/Routes/api.php +++ b/Modules/Linksehat/Routes/api.php @@ -14,6 +14,7 @@ use Modules\Linksehat\Http\Controllers\Api\SearchController; use Modules\Linksehat\Http\Controllers\Api\SpecialityController; use Modules\Linksehat\Http\Controllers\Api\LinkingController; use Modules\Linksehat\Http\Controllers\Api\HomeController; +use Modules\Linksehat\Http\Controllers\Api\LivechatController; /* |-------------------------------------------------------------------------- @@ -65,12 +66,6 @@ Route::prefix('linksehat')->group(function () { Route::get('doctors/{id}', 'show')->name('doctors.show'); }); - Route::controller(HomeController::class)->group(function () { - Route::get('home', 'index')->name('homes.index'); - Route::get('home/hospital', 'listHospital')->name('homes.listHospital'); - }); - - Route::middleware('auth:sanctum')->group(function () { Route::get('profile/{id}', [ProfileController::class, 'index'])->name('profile'); Route::get('change-profile/{id}', [ProfileController::class, 'changeProfile'])->name('change-profile'); @@ -90,12 +85,26 @@ Route::prefix('linksehat')->group(function () { Route::get('card/{member_id}', [LinkingController::class, 'card']); Route::get('card/{member_id}/{log_id}', [LinkingController::class, 'card_detail']); + Route::controller(HomeController::class)->group(function () { + Route::get('home', 'index')->name('homes.index'); + Route::get('home/hospital', 'listHospital')->name('homes.listHospital'); + }); + + Route::controller(LivechatController::class)->group(function () { + Route::get('livechat', 'index')->name('livechats.index'); + Route::get('livechat/consultation', 'consultation')->name('livechats.consultation'); + Route::post('livechat/consultation-request', 'consultation_request')->name('livechats.consultation-request'); + Route::get('livechat/consultation-request/{id}', 'consultation_request_show'); + Route::get('livechat/consultation-request/consultation_payment_choose/{id}', 'consultation_payment_choose'); + Route::post('livechat/consultation-payment', 'consultation_payment'); + }); + + Route::post('create-invoice-duitku', [DuitkuController::class, 'createInvoice']); + Route::post('check-status-duitku', [DuitkuController::class, 'checkStatus']); }); - Route::post('create-invoice-duitku', [DuitkuController::class, 'createInvoice']); Route::post('payment-method-duitku', [DuitkuController::class, 'paymentMethod']); - Route::post('check-status-duitku', [DuitkuController::class, 'checkStatus']); Route::post('callback-duitku', [DuitkuController::class, 'callback']); ;}); diff --git a/Modules/Linksehat/Transformers/Home/HomeResource.php b/Modules/Linksehat/Transformers/Home/HomeResource.php index 834a7fe9..bfb93dfc 100644 --- a/Modules/Linksehat/Transformers/Home/HomeResource.php +++ b/Modules/Linksehat/Transformers/Home/HomeResource.php @@ -209,6 +209,7 @@ class HomeResource extends JsonResource } } $data = [ + 'id' => $doctor['id'], 'full_name' => $doctor['person']['name'], 'specialist' => $specialist, 'experience' => $year, @@ -270,28 +271,12 @@ class HomeResource extends JsonResource 'message' => $wellcome, 'full_name' => $this->sFirstName . ' '. $this->sLastName, 'avatar' => $avatar, - // 'member_type' => $this->nIDUser ? 'Dependent' : 'Principal', - // 'member_profile' => $dataMemberProfile, 'member_id' => $memberId, 'linking' => $linking, 'doctors_livechat' => [ $doctorsLivechat ], 'hospital' => $hospitalList - - - // [ - // [ - // 'name' => 'PRIMAYA HOSPITAL NORTH BEKASI', - // 'image' => '', - // 'range' => 6 - // ], - // [ - // 'name' => 'PRIMAYA HOSPITAL TANGERANG', - // 'image' => '', - // 'range' => 10 - // ], - // ] ]; } } diff --git a/Modules/Linksehat/Transformers/Livechat/LivechatResource.php b/Modules/Linksehat/Transformers/Livechat/LivechatResource.php new file mode 100644 index 00000000..d77f95fe --- /dev/null +++ b/Modules/Linksehat/Transformers/Livechat/LivechatResource.php @@ -0,0 +1,85 @@ +request = $request; + parent::__construct($resource); + } + + public function toArray($request) + { + // Specialis + $specialists = Speciality::all(); + + // Doctor livechat + $doctors = Practitioner::with('person', 'practitionerRoles.organization', 'practitionerRoles.speciality') + ->whereHas('person', function ($query) { + $query->where('isOnline', 1); // hanya online + }) + ->get() + ->toArray(); + $doctorsLivechat = []; + foreach($doctors as $doctor){ + $specialist = 'Umum'; + $year = 0; + $price = 0; + if (!empty($doctor['person']['start_date_work'])) { + $starExperience = Carbon::parse($doctor['person']['start_date_work'])->format('Y-m-d'); + $experience = Carbon::createFromFormat('Y-m-d', $starExperience); + $year = $experience->diffInYears(Carbon::now()); + } + if ($doctor['practitioner_roles']) { + if ($doctor['practitioner_roles'][0]['speciality']){ + $specialist = $doctor['practitioner_roles'][0]['speciality']['name']; + } + if ($doctor['practitioner_roles'][0]['price']){ + $price = $doctor['practitioner_roles'][0]['price']; + } + } + $data = [ + 'id' => $doctor['id'], + 'full_name' => $doctor['person']['name'], + 'specialist' => $specialist, + 'experience' => $year, + 'review' => $doctor['person']['review'], + 'price' => $price, + 'price_real' => $price + ]; + array_push($doctorsLivechat, $data); + } + + return [ + 'jadwal_weekday' => 'Senin - Jumat (08:00 - 17:30)', + 'jadwal_weekend' => 'Sabtu (08:00 - 12:00)', + 'doctors_livechat' => [ + $doctorsLivechat + ], + 'specialist' => $specialists + ]; + } +} diff --git a/app/Helpers/DuitkuHelper.php b/app/Helpers/DuitkuHelper.php new file mode 100644 index 00000000..f99aa6c7 --- /dev/null +++ b/app/Helpers/DuitkuHelper.php @@ -0,0 +1,33 @@ +setSandboxMode(true); + // set sanitizer (default : true) + $duitkuConfig->setSanitizedMode(false); + // set log parameter (default : true) + $duitkuConfig->setDuitkuLogs(false); + return $duitkuConfig; + } + + public static function paymentMethod() + { + $duitkuConfig = self::configuration(); + try { + $paymentAmount = "10000"; //"YOUR_AMOUNT"; + $paymentMethodList = \Duitku\Pop::getPaymentMethod($paymentAmount, $duitkuConfig); + + header('Content-Type: application/json'); + return $paymentMethodList; + } catch (Exception $e) { + return $e->getMessage(); + } + } +} \ No newline at end of file diff --git a/app/Models/Livechat.php b/app/Models/Livechat.php new file mode 100644 index 00000000..151f0086 --- /dev/null +++ b/app/Models/Livechat.php @@ -0,0 +1,37 @@ +belongsTo(Person::class, 'doctor_id', 'id'); + } + + public function practitioner() { + return $this->belongsTo(Practitioner::class, 'doctor_id', 'id'); + } + + +} diff --git a/app/Models/PaymentsMethods.php b/app/Models/PaymentsMethods.php new file mode 100644 index 00000000..1db98639 --- /dev/null +++ b/app/Models/PaymentsMethods.php @@ -0,0 +1,20 @@ +id(); + $table->foreignId('doctor_id'); + $table->foreignId('patient_id'); + $table->foreignId('organization_id'); + $table->string('timezone')->nullable()->default(null); + $table->text('descriptions'); + $table->string('payment_method'); + $table->dateTime('request_date')->nullable(); + $table->dateTime('accept_date')->nullable(); + $table->dateTime('start_date')->nullable(); + $table->dateTime('end_date')->nullable(); + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('livechats'); + } +}; diff --git a/database/migrations/2024_04_22_084927_add_column_to_livechats_table.php b/database/migrations/2024_04_22_084927_add_column_to_livechats_table.php new file mode 100644 index 00000000..8cfb806b --- /dev/null +++ b/database/migrations/2024_04_22_084927_add_column_to_livechats_table.php @@ -0,0 +1,32 @@ +string('uuid')->after('id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('livechats', function (Blueprint $table) { + $table->dropColumn('uuid'); + }); + } +}; From 1cb3b2d3f059043b393155044b13eeaaaf7e5fbb Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Tue, 23 Apr 2024 16:28:59 +0700 Subject: [PATCH 08/52] API APP DOCTOR --- .../Linksehat/Helpers/Doctor/ApiResponse.php | 21 ++ .../Api/Doctor/AuthDoctorController.php | 264 ++++++++++++++++++ .../Api/Doctor/ProfileDoctorController.php | 175 ++++++++++++ .../Http/Middleware/Doctor/Authentication.php | 65 +++++ .../Http/Middleware/Doctor/Authorization.php | 71 +++++ Modules/Linksehat/Routes/api.php | 33 +++ app/Helpers/Helper.php | 10 +- ..._145224_create_person_educations_table.php | 38 +++ ...0705_add_column_to_table_practitioners.php | 33 +++ ...add_column_to_table_practitioner_roles.php | 33 +++ resources/lang/en/Message.php | 2 + resources/lang/en/Validation.php | 6 +- resources/lang/id/Message.php | 2 + resources/lang/id/Validation.php | 6 +- .../views/email/forgot_password.blade.php | 62 ++++ 15 files changed, 811 insertions(+), 10 deletions(-) create mode 100644 Modules/Linksehat/Helpers/Doctor/ApiResponse.php create mode 100644 Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php create mode 100644 Modules/Linksehat/Http/Controllers/Api/Doctor/ProfileDoctorController.php create mode 100644 Modules/Linksehat/Http/Middleware/Doctor/Authentication.php create mode 100644 Modules/Linksehat/Http/Middleware/Doctor/Authorization.php create mode 100644 database/migrations/2024_04_23_145224_create_person_educations_table.php create mode 100644 database/migrations/2024_04_23_160705_add_column_to_table_practitioners.php create mode 100644 database/migrations/2024_04_23_162213_add_column_to_table_practitioner_roles.php create mode 100644 resources/views/email/forgot_password.blade.php diff --git a/Modules/Linksehat/Helpers/Doctor/ApiResponse.php b/Modules/Linksehat/Helpers/Doctor/ApiResponse.php new file mode 100644 index 00000000..56029c67 --- /dev/null +++ b/Modules/Linksehat/Helpers/Doctor/ApiResponse.php @@ -0,0 +1,21 @@ +first(); + } + return response()->json([ + 'meta' => [ + 'status' => $status, + 'code' => $statusCode, + 'message' => $message + ], + 'data' => $data, + ], $statusCode); + } +} diff --git a/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php b/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php new file mode 100644 index 00000000..3ba2d663 --- /dev/null +++ b/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php @@ -0,0 +1,264 @@ + $request->email, + 'password' => $request->password + ]; + $validator = Validator::make($request->all(), [ + 'email' => 'required|email', + 'password' => 'required' + ], [ + 'email.required' => trans('validation.required',['attribute' => 'Email']), + 'email.email' => trans('validation.email'), + 'password.required' => trans('validation.required',['attribute' => 'Password']), + ]); + + if ($validator->fails()) + { + return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); + } + else + { + $user = User::where('email', $request->email)->first(); + if (!$user) { + return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404); + } + + if (!Hash::check($request->password, $user->password)) { + return ApiResponse::apiResponse('Bad Request', $data, trans('message.password'), 400); + } + + $res_data = [ + 'user' => $user, + 'token' => $user->createToken('app')->plainTextToken + ]; + + return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200); + } + } + + public function logout(Request $request) + { + $request->user()->tokens()->delete(); + + return ApiResponse::apiResponse('Success', [], trans('message.logout'), 200); + } + + public function forgotPassword(Request $request) + { + $data = [ + 'email' => $request->email, + ]; + + $validator = Validator::make($request->all(), [ + 'email' => 'required|email', + ], [ + 'email.required' => trans('validation.required',['attribute' => 'Email']), + 'email.email' => trans('validation.email'), + ]); + + if ($validator->fails()) + { + return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); + } + else + { + $user = User::where('email', $request->email)->first(); + if (!$user) { + return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404); + } + + //send email + // Insert data notifications + $emailTo = $request->email; + $dataNotif = [ + 'user_id' => $user->id, + 'email' => $emailTo, + 'title' => 'Forgot Password', + 'description' => 'Request forgot password from App Doctor', + 'type' => 1, + 'isUnRead' => true, + 'created_by' => auth()->check() ? auth()->user()->id : null, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]; + $sendNotif = Helper::insertNotification($dataNotif); + //Insert data password reset + $token = mt_rand(100000, 999999); // Menghasilkan angka acak antara 100000 dan 999999 + $p_resets = DB::table('password_resets') + ->insert([ + 'email' => $request->email, + 'token' => $token, + 'created_at' => date('Y-m-d H:i:s'), + ]); + // Send Email after insert notifications + if($sendNotif && $p_resets) + { + //send to alarm + $nameTo = 'User'; + $dataEmail = [ + 'email' => $emailTo, + 'name' => $nameTo, + 'subject' => 'Request Forgot Password from App Doctor Date '. date('Y-m-d H:i:s'), + 'body' => View::make('email/forgot_password', ['token' => $token])->render(), + ]; + Helper::sendEmail($dataEmail); + + $res = DB::table('password_resets') + ->where('email', '=', $request->email) + ->where('token', '=', $token) + ->get(); + + return ApiResponse::apiResponse("Success", $res, trans('message.success'), 200); + } + else + { + return ApiResponse::apiResponse("Internal Server Error", $data, trans('message.server_error'), 500); + } + } + } + + public function verifCode(Request $request) + { + $data = [ + 'email' => $request->email, + 'token' => $request->token, + ]; + + $validator = Validator::make($request->all(), [ + 'email' => 'required|email', + 'token' => 'required|numeric', + ], [ + 'email.required' => trans('validation.required',['attribute' => 'Email']), + 'email.email' => trans('validation.email'), + 'token.required' => trans('validation.required',['attribute' => 'Token']), + ]); + + if ($validator->fails()) + { + return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); + } + else + { + //Check Time + $check = DB::table('password_resets') + ->where('email', '=', $request->email) + ->where('token', '=', $request->token) + ->select('created_at') + ->first(); + + if($check) + { + $created_at = strtotime($check->created_at); // Konversi string waktu ke UNIX timestamp + $now = time(); // Waktu sekarang dalam UNIX timestamp + + // Hitung selisih waktu dalam menit + $diffInMinutes = ($now - $created_at) / 60; + + if ($diffInMinutes > 60) { + return ApiResponse::apiResponse('Not Found', $data, trans('message.token_expired'), 404); + } else { + // Lanjutkan dengan proses pemulihan kata sandi + return ApiResponse::apiResponse("Success", $data, trans('message.success'), 200); + } + } + else + { + return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404); + } + } + } + + public function resetPassword(Request $request) + { + $data = [ + 'email' => $request->email, + 'token' => $request->token, + 'new_password' => $request->new_password + ]; + + $validator = Validator::make($request->all(), [ + 'email' => 'required|email', + 'token' => 'required|numeric', + 'new_password' => [ + 'required', + 'min:8', + 'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/' + ] + ], [ + 'email.required' => trans('validation.required',['attribute' => 'Email']), + 'email.email' => trans('validation.email'), + 'token.required' => trans('validation.required',['attribute' => 'Token']), + 'new_password.required' => trans('validation.required',['attribute' => 'New Password']), + 'new_password.min' => trans('validation.min',['attribute' => 'New Password']), + 'new_password.regex' => trans('validation.regex',['attribute' => 'New Password']), + ]); + + if ($validator->fails()) + { + return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); + } + else + { + //Check Time + $check = DB::table('password_resets') + ->where('email', '=', $request->email) + ->where('token', '=', $request->token) + ->select('created_at') + ->first(); + + if($check) + { + $created_at = strtotime($check->created_at); // Konversi string waktu ke UNIX timestamp + $now = time(); // Waktu sekarang dalam UNIX timestamp + + // Hitung selisih waktu dalam menit + $diffInMinutes = ($now - $created_at) / 60; + + if ($diffInMinutes > 60) { + return ApiResponse::apiResponse('Not Found', $data, trans('message.token_expired'), 404); + } else { + // Lanjutkan dengan proses pemulihan kata sandi + $user = User::where('email', $request->email)->first(); + if ($user) + { + $newPassword = Hash::make($request->new_password); + $user->password = $newPassword; + $user->save(); + return ApiResponse::apiResponse("Success", $data, trans('message.success'), 200); + } + else + { + return ApiResponse::apiResponse('Not Found', $data, trans('message.token_expired'), 404); + } + } + } + else + { + return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404); + } + } + } +} diff --git a/Modules/Linksehat/Http/Controllers/Api/Doctor/ProfileDoctorController.php b/Modules/Linksehat/Http/Controllers/Api/Doctor/ProfileDoctorController.php new file mode 100644 index 00000000..e6604fe1 --- /dev/null +++ b/Modules/Linksehat/Http/Controllers/Api/Doctor/ProfileDoctorController.php @@ -0,0 +1,175 @@ + auth()->check() ? auth()->user()->id : null, + ]; + $user_id = auth()->check() ? auth()->user()->id : null; + //Get data Profile + $dataProfile = DB::table('users') + ->join('persons','persons.id', '=', 'users.person_id') + ->leftJoin('person_educations','person_educations.person_id', '=', 'persons.id') + ->leftJoin('practitioners','practitioners.person_id', '=', 'persons.id') + ->leftJoin('practitioner_roles','practitioner_roles.practitioner_id', '=', 'practitioners.id') + ->where('users.id', '=', $user_id) + ->select( + 'persons.name', + DB::raw(' + "Pediatrics" AS specialist + '), + DB::raw(' + "4" AS rating + '), + 'persons.name AS full_name', + 'persons.birth_date as date_of_birth', + 'persons.gender', + 'persons.phone AS mobile_number', + 'persons.email', + 'practitioners.str_number', + 'practitioners.exp_date_str', + 'practitioner_roles.sip_number', + 'practitioner_roles.exp_date_sip' + ) + ->first(); + + //Name + $dataName = [ + 'name' => $dataProfile->name, + 'specialist' => $dataProfile->specialist, + 'rating' => $dataProfile->rating + ]; + $res_data['dataName'] = $dataName; + + // Basic + $dataProfileBasic = [ + 'full_name' => $dataProfile->full_name, + 'date_of_birth' => $dataProfile->date_of_birth ? date('d M Y', strtotime($dataProfile->date_of_birth)) : '', + 'gender' => $dataProfile->gender + ]; + $res_data['dataProfileBasic'] = $dataProfileBasic; + + //Contact + $dataProfileContact = [ + 'mobile_number' => $dataProfile->mobile_number, + 'email' => $dataProfile->email + ]; + $res_data['dataProfileContact'] = $dataProfileContact; + + //Education + $dataEdu = DB::table('users') + ->join('persons','persons.id', '=', 'users.person_id') + ->leftJoin('person_educations','person_educations.person_id', '=', 'persons.id') + ->where('users.id', '=', $user_id) + ->select( + 'person_educations.level_id', + 'person_educations.name', + 'person_educations.start_date', + 'person_educations.end_date', + ) + ->get(); + $dataEducations = []; + foreach($dataEdu as $val) + { + $dataEducations[] = [ + 'level_id' => $val->level_id, + 'name' => $val->name, + 'start_date' => date('d/m/Y', strtotime($val->start_date)), + 'end_date' => date('d/m/Y', strtotime($val->end_date)), + ]; + } + $res_data['dataEducations'] = $dataEducations; + + //Work Experience + $dataWork = DB::table('users') + ->join('persons','persons.id', '=', 'users.person_id') + ->leftJoin('practitioners','practitioners.person_id', '=', 'persons.id') + ->leftJoin('practitioner_roles','practitioner_roles.practitioner_id', '=', 'practitioners.id') + ->leftJoin('organizations','organizations.id', '=', 'practitioner_roles.organization_id') + ->where('users.id', '=', $user_id) + ->select( + 'organizations.name', + 'practitioner_roles.period_start', + 'practitioner_roles.period_end', + ) + ->get(); + $dataWorkExperience = []; + foreach ($dataWork as $val) + { + $dataWorkExperience[] = [ + 'name' => $val->name ? $val->name : '', + 'period' => $this->fWorkExperience($val->period_start, $val->period_end) + ]; + } + $res_data['dataWorkExperience'] = $dataWorkExperience; + + //STR + $dataStr = [ + 'str_number' => $dataProfile->str_number, + 'exp_date_str' => $dataProfile->exp_date_str ? date('d M Y', strtotime($dataProfile->exp_date_str)) : '' + ]; + $res_data['dataStr'] = $dataStr; + + //SIP + $dataSip = [ + 'sip_number' => $dataProfile->sip_number, + 'exp_date_sip' => $dataProfile->exp_date_sip ? date('d M Y', strtotime($dataProfile->exp_date_sip)) : '' + ]; + $res_data['dataSip'] = $dataSip; + + return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200); + + } + public function fWorkExperience($start, $end) + { + $startDateString = $start; // Tanggal dan waktu awal + $endDateString = $end ; // Tanggal dan waktu akhir + + // Mengubah string tanggal ke timestamp UNIX + $startTime = strtotime($startDateString); + $endTime = strtotime($endDateString); + + // Menghitung selisih waktu dalam detik + $timeDifference = $endTime - $startTime; + + // Menghitung jumlah tahun, bulan, dan hari dari selisih waktu + $years = floor($timeDifference / (365 * 24 * 60 * 60)); + $months = floor(($timeDifference - ($years * 365 * 24 * 60 * 60)) / (30 * 24 * 60 * 60)); + $days = floor(($timeDifference - ($years * 365 * 24 * 60 * 60) - ($months * 30 * 24 * 60 * 60)) / (24 * 60 * 60)); + + // Formatkan hasilnya + $experience = ''; + if ($years > 0) { + $experience .= $years . ' years '; + } + if ($months > 0) { + $experience .= $months . ' months '; + } + if ($days > 0) { + $experience .= $days . ' days'; + } + + return $experience; + + } +} diff --git a/Modules/Linksehat/Http/Middleware/Doctor/Authentication.php b/Modules/Linksehat/Http/Middleware/Doctor/Authentication.php new file mode 100644 index 00000000..d2c93b97 --- /dev/null +++ b/Modules/Linksehat/Http/Middleware/Doctor/Authentication.php @@ -0,0 +1,65 @@ +header('Accept'); + $contentType = $request->header('Content-Type'); + $locale = $request->header('Accept-Language'); + + // Add language + if(!$locale) + { + return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Accept-Language']), 401); + } + if($locale !== 'en-US' && $locale !== 'id-ID') + { + return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Accept-Language']), 400); + } + if ($locale === 'en-US') + { + App::setLocale('en'); + } elseif ($locale === 'id-ID') + { + App::setLocale('id'); + } else + { + App::setLocale('en'); + } + + // Validate type accept & content type + if (!$acceptHeader) + { + return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Accept']), 401); + } + if (!$contentType) + { + return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Content-Type']), 401); + } + if ($acceptHeader !== 'application/json') + { + return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Accept']), 400); + } + if($contentType !== 'application/json') + { + return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Content-Type']), 400); + } + return $next($request); + } +} diff --git a/Modules/Linksehat/Http/Middleware/Doctor/Authorization.php b/Modules/Linksehat/Http/Middleware/Doctor/Authorization.php new file mode 100644 index 00000000..6d29b24f --- /dev/null +++ b/Modules/Linksehat/Http/Middleware/Doctor/Authorization.php @@ -0,0 +1,71 @@ +header('Accept'); + $contentType = $request->header('Content-Type'); + $locale = $request->header('Accept-Language'); + $authorization = $request->header('Authorization'); + + // Add language + if(!$locale) + { + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Accept-Language']), 401); + } + if($locale !== 'en-US' && $locale !== 'id-ID') + { + return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Accept-Language']), 400); + } + if ($locale === 'en-US') + { + App::setLocale('en'); + } elseif ($locale === 'id-ID') + { + App::setLocale('id'); + } else + { + App::setLocale('en'); + } + + // Validate authorization + if (empty($authorization) || strpos($authorization, 'Bearer ') !== 0) { + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Authorization']), 401); + } + + // Validate type accept & content type + if (!$acceptHeader) + { + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Accept']), 401); + } + if (!$contentType && $request->isMethod('post')) + { + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Content-Type']), 401); + } + if ($acceptHeader !== 'application/json') + { + return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Accept']), 400); + } + if($contentType !== 'application/json' && $request->isMethod('post')) + { + return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Content-Type']), 400); + } + return $next($request); + } +} diff --git a/Modules/Linksehat/Routes/api.php b/Modules/Linksehat/Routes/api.php index 29d5b260..eb31555a 100644 --- a/Modules/Linksehat/Routes/api.php +++ b/Modules/Linksehat/Routes/api.php @@ -15,6 +15,10 @@ use Modules\Linksehat\Http\Controllers\Api\SpecialityController; use Modules\Linksehat\Http\Controllers\Api\LinkingController; use Modules\Linksehat\Http\Controllers\Api\HomeController; use Modules\Linksehat\Http\Controllers\Api\LivechatController; +use Modules\Linksehat\Http\Middleware\Doctor\Authentication; +use Modules\Linksehat\Http\Middleware\Doctor\Authorization; +use Modules\Linksehat\Http\Controllers\Api\Doctor\AuthDoctorController; +use Modules\Linksehat\Http\Controllers\Api\Doctor\ProfileDoctorController; /* |-------------------------------------------------------------------------- @@ -108,4 +112,33 @@ Route::prefix('linksehat')->group(function () { Route::post('payment-method-duitku', [DuitkuController::class, 'paymentMethod']); Route::post('callback-duitku', [DuitkuController::class, 'callback']); Route::get('redirect-duitku', [DuitkuController::class, 'redirect']); + + //DOCTOR API + Route::prefix('doctor')->group(function() { + //Version 1.0 + Route::prefix('v1')->group(function() { + Route::middleware(Authentication::class)->group(function () { + Route::controller(AuthDoctorController::class)->group(function () { + Route::post('login', 'login'); + }); + }); + Route::middleware('auth:sanctum')->group(function () { + Route::middleware(Authorization::class)->group(function () { + Route::controller(AuthDoctorController::class)->group(function () { + Route::post('logout', 'logout'); + Route::post('forgot-password', 'forgotPassword'); + }); + Route::controller(ProfileDoctorController::class)->group(function () { + Route::get('get-profile', 'getProfile'); + }); + }); + }); + Route::controller(AuthDoctorController::class)->group(function () { + Route::post('forgot-password', 'forgotPassword'); + Route::post('verif-code', 'verifCode'); + Route::post('resend-code', 'forgotPassword'); + Route::post('reset-password', 'resetPassword'); + }); + }); + }); ;}); diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 6643d50b..acccbfc1 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -391,9 +391,7 @@ class Helper $mail->send(); return true; } catch (\Exception $e) { - dd($e); - return ($mail->ErrorInfo); - return false; + return $mail->ErrorInfo; } } @@ -463,16 +461,16 @@ class Helper $lonFrom = deg2rad($longitudeFrom); $latTo = deg2rad($latitudeTo); $lonTo = deg2rad($longitudeTo); - + // Calculate the change in coordinates $deltaLat = $latTo - $latFrom; $deltaLon = $lonTo - $lonFrom; - + // Apply Haversine formula $a = sin($deltaLat / 2) * sin($deltaLat / 2) + cos($latFrom) * cos($latTo) * sin($deltaLon / 2) * sin($deltaLon / 2); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); $distance = $earthRadius * $c; - + return $distance; } diff --git a/database/migrations/2024_04_23_145224_create_person_educations_table.php b/database/migrations/2024_04_23_145224_create_person_educations_table.php new file mode 100644 index 00000000..86faea77 --- /dev/null +++ b/database/migrations/2024_04_23_145224_create_person_educations_table.php @@ -0,0 +1,38 @@ +id(); + $table->bigInteger('person_id'); + $table->integer('level_id')->comment('1=SD, 2= SMP, 3=SMA dst.'); + $table->string('name', 255); + $table->dateTime('start_date'); + $table->dateTime('end_date'); + $table->bigInteger('created_by')->nullable(); + $table->bigInteger('updated_by')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('person_educations'); + } +}; diff --git a/database/migrations/2024_04_23_160705_add_column_to_table_practitioners.php b/database/migrations/2024_04_23_160705_add_column_to_table_practitioners.php new file mode 100644 index 00000000..66ea3ce7 --- /dev/null +++ b/database/migrations/2024_04_23_160705_add_column_to_table_practitioners.php @@ -0,0 +1,33 @@ +string('str_number', 255)->after('person_id')->nullable(); + $table->dateTime('exp_date_str')->after('str_number')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('practitioners', function (Blueprint $table) { + // + }); + } +}; diff --git a/database/migrations/2024_04_23_162213_add_column_to_table_practitioner_roles.php b/database/migrations/2024_04_23_162213_add_column_to_table_practitioner_roles.php new file mode 100644 index 00000000..3c4daa2f --- /dev/null +++ b/database/migrations/2024_04_23_162213_add_column_to_table_practitioner_roles.php @@ -0,0 +1,33 @@ +string('sip_number', 255)->after('period_end')->nullable(); + $table->dateTime('exp_date_sip')->after('sip_number')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('practitioner_roles', function (Blueprint $table) { + // + }); + } +}; diff --git a/resources/lang/en/Message.php b/resources/lang/en/Message.php index 7b8a6c6d..f439e71e 100644 --- a/resources/lang/en/Message.php +++ b/resources/lang/en/Message.php @@ -7,4 +7,6 @@ return [ 'password' => 'Password wrong. Please try again.', 'read_notification' => 'Notification has been read.', 'already_exists' => 'Data already exists.', + 'logout' => 'User logged out successfully.', + 'token_expired' => 'Token has expired. Please re-request the token.' ]; diff --git a/resources/lang/en/Validation.php b/resources/lang/en/Validation.php index 8f242065..ba9e9e01 100644 --- a/resources/lang/en/Validation.php +++ b/resources/lang/en/Validation.php @@ -6,5 +6,7 @@ return [ 'max' => [ 'file' => ':attribute max size is :max.', ], - 'email' => 'Invalid email format.' -]; \ No newline at end of file + 'email' => 'Invalid email format.', + 'regex' => 'The :attribute must contain at least one uppercase letter, one lowercase letter, and one numeric digit.', + 'min' => 'The :attribute must be at least 8 characters.' +]; diff --git a/resources/lang/id/Message.php b/resources/lang/id/Message.php index 475a5731..6589d023 100644 --- a/resources/lang/id/Message.php +++ b/resources/lang/id/Message.php @@ -7,4 +7,6 @@ return [ 'password' => 'Password salah. Silakan coba lagi.', 'read_notification' => 'Notifikasi telah dibaca.', 'already_exists' => 'Data sudah ada.', + 'logout' => 'User berhasil logout.', + 'token_expired' => 'Token telah kedaluwarsa. Silakan minta ulang token.' ]; diff --git a/resources/lang/id/Validation.php b/resources/lang/id/Validation.php index 5a8fdb02..a564bf1e 100644 --- a/resources/lang/id/Validation.php +++ b/resources/lang/id/Validation.php @@ -6,5 +6,7 @@ return [ 'max' => [ 'file' => ':attribute tidak boleh melebihi :max.', ], - 'email' => 'Format email salah.' -]; \ No newline at end of file + 'email' => 'Format email salah.', + 'regex' => ':attribute harus berisi setidaknya satu huruf besar, satu huruf kecil, dan satu digit angka.', + 'min' => ':attribute setidaknya harus terdiri dari 8 karakter.' +]; diff --git a/resources/views/email/forgot_password.blade.php b/resources/views/email/forgot_password.blade.php new file mode 100644 index 00000000..9f484c66 --- /dev/null +++ b/resources/views/email/forgot_password.blade.php @@ -0,0 +1,62 @@ + + + + + + + Verification Code + + + + +
+

Verification Code

+
{{$token}}
+

This code is valid for 60 minutes.

+ +
+ + + From 85544a7f0ec40eff844bfc942666c11ac14931a0 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Wed, 24 Apr 2024 16:47:25 +0700 Subject: [PATCH 09/52] api duitku dan profile add --- .../Controllers/Api/LivechatController.php | 46 +++++- .../Controllers/Api/ProfileController.php | 41 +++++- Modules/Linksehat/Routes/api.php | 4 +- app/Helpers/DuitkuHelper.php | 136 ++++++++++++++++++ app/Models/OLDLMS/User.php | 1 + 5 files changed, 224 insertions(+), 4 deletions(-) diff --git a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php index c2db0d4e..e6b255e3 100644 --- a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php +++ b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php @@ -228,8 +228,50 @@ class LivechatController extends Controller } public function consultation_payment(Request $request){ + + $livechat = Livechat::where('id', $request->consultation_id)->with(['doctor', 'practitioner'])->first(); - $duitku = DuitkuHelper::paymentMethod(); - dd($duitku); + $practitionerRole = PractitionerRole::where('id',$livechat->practitioner->id)->first(); + $price = $practitionerRole->price ? $practitionerRole->price : 30000; + $adminFee = 5000; + $discount = 0; + $totalPay = $price + $adminFee - $discount; + + // From database linksehat + $user = User::with('detail') + ->where('nId', $livechat->patient_id) + ->first(); + + $data['paymentMethod'] = $request->payment_code; + $data['paymentAmount'] = $totalPay; + $data['email'] = $user->sEmail; + $data['phoneNumber'] = $user->sPhone; + $data['productDetails'] = $user->sEmail; + $data['merchantOrderId'] = $livechat->uuid; + $data['additionalParam'] = ''; + $data['merchantUserInfo'] = ''; + $data['customerVaName'] = $user->sFirstName . ' ' . $user->sLastName; + $data['callbackUrl'] = 'htpps://google.com'; + $data['returnUrl'] = 'htpps://linksehat.com'; + $data['expiryPeriod'] = 60; + $data['firstName'] = $user->sFirstName; + $data['lastName'] = $user->sLastName; + + // dd($user); + $data['alamat'] = ''; + $data['city'] = ''; + $data['postalCode'] = ''; + + $duitku = DuitkuHelper::createInvoice($data); + + return $duitku; + } + + public function consultation_check_payment($id){ + $livechat = Livechat::where('id',$id)->with(['doctor', 'practitioner'])->first(); + $duitku = DuitkuHelper::checkStatus($livechat->uuid); + + return $duitku; + } } diff --git a/Modules/Linksehat/Http/Controllers/Api/ProfileController.php b/Modules/Linksehat/Http/Controllers/Api/ProfileController.php index f4be989a..d334dfbf 100644 --- a/Modules/Linksehat/Http/Controllers/Api/ProfileController.php +++ b/Modules/Linksehat/Http/Controllers/Api/ProfileController.php @@ -43,7 +43,46 @@ class ProfileController extends Controller */ public function store(Request $request) { - // + $validator = Validator::make($request->all(), [ + 'id_user' => 'required', + 'first_name' => 'required', + 'last_name' => 'required', + 'date_of_birth' => 'required', + // 'email' => 'required', + + ]); + + if ($validator->fails()) { + return response()->json(['errors' => $validator->errors()], 422); + } else { + $value = [ + 'nIDuser' => $request->id_user, + 'sIPAddress' => $request->ip(), + 'sPassword' => null, + 'sFirstName' => $request->first_name, + 'sLastName' => $request->last_name, + ]; + + $user = User::create($value); + $dataDetail = [ + 'nIDUser' => $user->nID, + 'dTanggalLahir' => $request->date_of_birth, + 'nIDJenisKelamin' => $request->gender + ]; + + $userDetail = UserDetail::create( + $dataDetail + ); + + $data['data'] = [ + 'status' => 200, + 'message' => 'data berhasil di tambahkan', + 'error' => 'false' + + ]; + + return response()->json($data); + } } /** diff --git a/Modules/Linksehat/Routes/api.php b/Modules/Linksehat/Routes/api.php index 29d5b260..124303b3 100644 --- a/Modules/Linksehat/Routes/api.php +++ b/Modules/Linksehat/Routes/api.php @@ -70,6 +70,7 @@ Route::prefix('linksehat')->group(function () { Route::get('profile/{id}', [ProfileController::class, 'index'])->name('profile'); Route::get('change-profile/{id}', [ProfileController::class, 'changeProfile'])->name('change-profile'); Route::post('profile', [ProfileController::class, 'update'])->name('profile.update'); + Route::post('profile-add', [ProfileController::class, 'store'])->name('profile.store'); Route::post('notification-tokens/delete/{id}', [NotificationTokenController::class, 'destroy'])->name('profile.delete.token'); Route::post('notification-tokens', [NotificationTokenController::class, 'store'])->name('profile.store.token'); Route::apiResource('appointment', AppointmentController::class); @@ -95,7 +96,8 @@ Route::prefix('linksehat')->group(function () { Route::get('livechat/consultation', 'consultation')->name('livechats.consultation'); Route::post('livechat/consultation-request', 'consultation_request')->name('livechats.consultation-request'); Route::get('livechat/consultation-request/{id}', 'consultation_request_show'); - Route::get('livechat/consultation-request/consultation_payment_choose/{id}', 'consultation_payment_choose'); + Route::get('livechat/consultation-request/consultation-payment-choose/{id}', 'consultation_payment_choose'); + Route::get('livechat/consultation-request/consultation-payment-check/{id}', 'consultation_check_payment'); Route::post('livechat/consultation-payment', 'consultation_payment'); }); diff --git a/app/Helpers/DuitkuHelper.php b/app/Helpers/DuitkuHelper.php index f99aa6c7..84e9a1f8 100644 --- a/app/Helpers/DuitkuHelper.php +++ b/app/Helpers/DuitkuHelper.php @@ -30,4 +30,140 @@ class DuitkuHelper return $e->getMessage(); } } + + public static function checkStatus($merchantOrderId) + { + $duitkuConfig = self::configuration(); + $data = [ + 'merchantOrderId' => $merchantOrderId + ]; + try { + $transactionList = \Duitku\Pop::transactionStatus($merchantOrderId, $duitkuConfig); + + header('Content-Type: application/json'); + $transaction = json_decode($transactionList); + + // var_dump($transactionList); + + if ($transaction->statusCode == "00") { + // Action Success + } else if ($transaction->statusCode == "01") { + // Action Pending + } else { + // Action Failed Or Expired + } + return $transaction; + // return json_decode($transaction); + } catch (Exception $e) { + return $e->getMessage(); + } + + } + + public static function createInvoice($data) + { + #CONTOH DARI DUITKU + // $paymentMethod = ""; // PaymentMethod list => https://docs.duitku.com/pop/id/#payment-method + // $paymentAmount = 10000; // Amount + // $email = "customer@gmail.com"; // your customer email + // $phoneNumber = "081234567890"; // your customer phone number (optional) + // $productDetails = "Test Payment"; + // $merchantOrderId = "2"; // from merchant, unique + // $additionalParam = ''; // optional + // $merchantUserInfo = ''; // optional + // $customerVaName = 'John Doe'; // display name on bank confirmation display + // $callbackUrl = 'http://YOUR_SERVER/callback'; // url for callback + // $returnUrl = 'http://YOUR_SERVER/return'; // url for redirect + // $expiryPeriod = 60; // set the expired time in minutes + + // // Customer Detail + // $firstName = "John"; + // $lastName = "Doe"; + + // // Address + // $alamat = "Jl. Kembangan Raya"; + // $city = "Jakarta"; + // $postalCode = "11530"; + // $countryCode = "ID"; + + $paymentMethod = $data['paymentMethod']; // PaymentMethod list => https://docs.duitku.com/pop/id/#payment-method + $paymentAmount = $data['paymentAmount']; // Amount + $email = $data['email']; // your customer email + $phoneNumber = $data['phoneNumber']; // your customer phone number (optional) + $productDetails = $data['productDetails']; + $merchantOrderId = $data['merchantOrderId']; // from merchant, unique + $additionalParam = $data['additionalParam']; // optional + $merchantUserInfo = $data['merchantUserInfo']; // optional + $customerVaName = $data['customerVaName']; // display name on bank confirmation display + $callbackUrl = env('APP_URL').'/api/linksehat/callback-duitku'; // url for callback + $returnUrl = env('APP_URL').'/api/linksehat/redirect-duitku';; // url for redirect + $expiryPeriod = 60; // set the expired time in minutes + + // Customer Detail + $firstName = $data['firstName']; + $lastName = $data['lastName']; + + // Address + $alamat = $data['alamat']; + $city = $data['city']; + $postalCode = $data['postalCode']; + $countryCode = "ID"; + + $address = array( + 'firstName' => $firstName, + 'lastName' => $lastName, + 'address' => $alamat, + 'city' => $city, + 'postalCode' => $postalCode, + 'phone' => $phoneNumber, + 'countryCode' => $countryCode + ); + + $customerDetail = array( + 'firstName' => $firstName, + 'lastName' => $lastName, + 'email' => $email, + 'phoneNumber' => $phoneNumber, + 'billingAddress' => $address, + 'shippingAddress' => $address + ); + + // Item Details + $item1 = array( + 'name' => $productDetails, + 'price' => $paymentAmount, + 'quantity' => 1 + ); + + $itemDetails = array( + $item1 + ); + + $params = array( + 'paymentAmount' => $paymentAmount, + 'merchantOrderId' => $merchantOrderId, + 'productDetails' => $productDetails, + 'additionalParam' => $additionalParam, + 'merchantUserInfo' => $merchantUserInfo, + 'customerVaName' => $customerVaName, + 'email' => $email, + 'phoneNumber' => $phoneNumber, + 'itemDetails' => $itemDetails, + 'customerDetail' => $customerDetail, + 'callbackUrl' => $callbackUrl, + 'returnUrl' => $returnUrl, + 'expiryPeriod' => $expiryPeriod + ); + $duitkuConfig = self::configuration(); + try { + // createInvoice Request + $responseDuitkuPop = \Duitku\Pop::createInvoice($params, $duitkuConfig); + header('Content-Type: application/json'); + return json_decode($responseDuitkuPop); + } catch (Exception $e) { + return json_decode($e->getMessage()); + + } + } + } \ No newline at end of file diff --git a/app/Models/OLDLMS/User.php b/app/Models/OLDLMS/User.php index 47a49474..c67e03ee 100644 --- a/app/Models/OLDLMS/User.php +++ b/app/Models/OLDLMS/User.php @@ -38,6 +38,7 @@ class User extends Authenticatable 'sEmail', 'nIDHubunganKeluarga', 'dUpdateOn', + 'sIPAddress', ]; protected function fullName(): Attribute From 60af8f21ce98bc3c968af0ba4e9829da26adda7f Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 25 Apr 2024 17:05:00 +0700 Subject: [PATCH 10/52] tambah field invoice no dan billing no --- .../Controllers/Api/RequestLogController.php | 14 ++++++++ .../Transformers/RequestLogShowResource.php | 3 ++ app/Models/RequestLog.php | 2 ++ ...60416_add_column_to_request_logs_table.php | 34 +++++++++++++++++++ .../Components/DialogEditFinalLOG.tsx | 26 ++++++++++++++ .../pages/CustomerService/FinalLog/Detail.tsx | 8 +++++ 6 files changed, 87 insertions(+) create mode 100644 database/migrations/2024_04_25_160416_add_column_to_request_logs_table.php diff --git a/Modules/Internal/Http/Controllers/Api/RequestLogController.php b/Modules/Internal/Http/Controllers/Api/RequestLogController.php index c434befc..94a4bb3a 100644 --- a/Modules/Internal/Http/Controllers/Api/RequestLogController.php +++ b/Modules/Internal/Http/Controllers/Api/RequestLogController.php @@ -252,6 +252,14 @@ class RequestLogController extends Controller $requestLog->catatan = $request->catatan; } + if (!empty($request->billing_no)) { + $requestLog->billing_no = $request->billing_no; + } + + if (!empty($request->invoice_no)) { + $requestLog->invoice_no = $request->invoice_no; + } + if (!empty($request->reason)) { $requestLog->reason = $request->reason; } @@ -402,6 +410,12 @@ class RequestLogController extends Controller if (!empty($request->catatan)) { $requestLog->catatan = $request->catatan; } + if (!empty($request->billing_no)) { + $requestLog->billing_no = $request->billing_no; + } + if (!empty($request->invoice_no)) { + $requestLog->invoice_no = $request->invoice_no; + } if ($request->discharge_date) { $requestLog->discharge_date = $request->discharge_date; } diff --git a/Modules/Internal/Transformers/RequestLogShowResource.php b/Modules/Internal/Transformers/RequestLogShowResource.php index 89f7e458..88bd4c54 100644 --- a/Modules/Internal/Transformers/RequestLogShowResource.php +++ b/Modules/Internal/Transformers/RequestLogShowResource.php @@ -113,6 +113,9 @@ class RequestLogShowResource extends JsonResource $data = [ 'id' => $requestLog['id'], 'code' => $requestLog['code'], + 'invoice_no' => $requestLog['invoice_no'], + 'billing_no' => $requestLog['billing_no'], + 'code' => $requestLog['code'], 'code_claim' => $claimCode, 'member_id' => $requestLog['member']['member_id'], 'corporate_id' => $corporateId, diff --git a/app/Models/RequestLog.php b/app/Models/RequestLog.php index b5a865fc..5c2e47a8 100644 --- a/app/Models/RequestLog.php +++ b/app/Models/RequestLog.php @@ -20,6 +20,8 @@ class RequestLog extends Model public $fillable = [ 'uuid', + 'invoice_no', + 'billing_no', 'submission_date', 'discharge_date', 'member_id', diff --git a/database/migrations/2024_04_25_160416_add_column_to_request_logs_table.php b/database/migrations/2024_04_25_160416_add_column_to_request_logs_table.php new file mode 100644 index 00000000..46452d52 --- /dev/null +++ b/database/migrations/2024_04_25_160416_add_column_to_request_logs_table.php @@ -0,0 +1,34 @@ +string('invoice_no')->after('code')->nullable(); + $table->string('billing_no')->after('invoice_no')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('request_logs', function (Blueprint $table) { + $table->dropColumn('invoice_no'); + $table->dropColumn('billing_no'); + }); + } +}; diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx index ce60261b..88959ff9 100644 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx @@ -21,6 +21,8 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo const navigate = useNavigate(); const [formData, setFormData] = useState({ + billing_no: requestLog?.billing_no, + invoice_no: requestLog?.invoice_no, discharge_date: requestLog?.discharge_date, id: requestLog?.id, catatan: requestLog?.catatan, @@ -49,6 +51,8 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo if (requestLog) { setFormData({ discharge_date: requestLog.discharge_date, + billing_no: requestLog.billing_no, + invoice_no: requestLog.invoice_no, id: requestLog.id, catatan: requestLog.catatan, icdCodes: requestLog.diagnosis @@ -116,6 +120,8 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo setFormData({ discharge_date: requestLog?.discharge_date, id: requestLog?.id, + billing_no: requestLog?.billing_no, + invoice_no: requestLog?.invoice_no, catatan: requestLog?.catatan, icdCodes: requestLog?.diagnosis ? requestLog?.diagnosis.map(diagnosis => diagnosis.code) @@ -173,6 +179,26 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo + + Invoice Number + handleChange('invoice_no', e.target.value)} + /> + + + Billing Number + handleChange('billing_no', e.target.value)} + /> + Discharge Date ) : null } + + Invoice Number + {requestLog?.invoice_no ? requestLog?.invoice_no : '-'} + + + Billing Number + {requestLog?.billing_no ? requestLog?.billing_no : '-'} + Provider {requestLog?.provider} From 2c4fe723dc85ad5d6ebf08f9d4588279509e4e43 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Fri, 26 Apr 2024 08:57:57 +0700 Subject: [PATCH 11/52] tambah field invoice biiling di client portal --- .../Client/Http/Controllers/Api/ClaimController.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Modules/Client/Http/Controllers/Api/ClaimController.php b/Modules/Client/Http/Controllers/Api/ClaimController.php index 22878143..607f8a8c 100644 --- a/Modules/Client/Http/Controllers/Api/ClaimController.php +++ b/Modules/Client/Http/Controllers/Api/ClaimController.php @@ -296,7 +296,9 @@ class ClaimController extends Controller 'Excess Paid', 'Diagnosis', 'Keterangan', - 'Catatan' + 'Catatan', + 'Invoice No', + 'Billing No' ]; $style = (new StyleBuilder()) ->setFontBold() @@ -336,7 +338,9 @@ class ClaimController extends Controller DB::raw(' (Select SUM(request_log_benefits.amount_approved) as tot_bill FROM request_log_benefits WHERE request_log_benefits.request_log_id = request_logs.id AND request_log_benefits.deleted_at IS NULL LIMIT 1) AS tot_bill - ') + '), + 'request_logs.invoice_no', + 'request_logs.billing_no', ) ->groupBy( 'request_logs.submission_date', @@ -509,6 +513,8 @@ class ClaimController extends Controller !empty($item->diagnosis) ? $item->diagnosis : '', !empty($item->keterangan) ? $item->keterangan : '', !empty($item->catatan) ? $item->catatan : '', + !empty($item->invoice_no) ? $item->invoice_no : '', + !empty($item->billing_no) ? $item->billing_no : '', ]; array_push($dataRow,$rowData); @@ -543,6 +549,8 @@ class ClaimController extends Controller '', '', '', + !empty($item->invoice_no) ? $item->invoice_no : '', + !empty($item->billing_no) ? $item->billing_no : '', ]; array_push($dataRow,$rowData); From 6a13d7aa1fde90f5691ffad42ec466dec1d4b38d Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Fri, 26 Apr 2024 14:03:43 +0700 Subject: [PATCH 12/52] Update case sensitif --- .../Api/Doctor/AuthDoctorController.php | 56 +++++++++---------- .../Api/Doctor/ProfileDoctorController.php | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php b/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php index 3ba2d663..12acbe37 100644 --- a/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php +++ b/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php @@ -30,9 +30,9 @@ class AuthDoctorController extends Controller 'email' => 'required|email', 'password' => 'required' ], [ - 'email.required' => trans('validation.required',['attribute' => 'Email']), - 'email.email' => trans('validation.email'), - 'password.required' => trans('validation.required',['attribute' => 'Password']), + 'email.required' => trans('Validation.required',['attribute' => 'Email']), + 'email.email' => trans('Validation.email'), + 'password.required' => trans('Validation.required',['attribute' => 'Password']), ]); if ($validator->fails()) @@ -43,11 +43,11 @@ class AuthDoctorController extends Controller { $user = User::where('email', $request->email)->first(); if (!$user) { - return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404); + return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); } if (!Hash::check($request->password, $user->password)) { - return ApiResponse::apiResponse('Bad Request', $data, trans('message.password'), 400); + return ApiResponse::apiResponse('Bad Request', $data, trans('Message.password'), 400); } $res_data = [ @@ -55,7 +55,7 @@ class AuthDoctorController extends Controller 'token' => $user->createToken('app')->plainTextToken ]; - return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200); + return ApiResponse::apiResponse("Success", $res_data, trans('Message.success'), 200); } } @@ -63,7 +63,7 @@ class AuthDoctorController extends Controller { $request->user()->tokens()->delete(); - return ApiResponse::apiResponse('Success', [], trans('message.logout'), 200); + return ApiResponse::apiResponse('Success', [], trans('Message.logout'), 200); } public function forgotPassword(Request $request) @@ -75,8 +75,8 @@ class AuthDoctorController extends Controller $validator = Validator::make($request->all(), [ 'email' => 'required|email', ], [ - 'email.required' => trans('validation.required',['attribute' => 'Email']), - 'email.email' => trans('validation.email'), + 'email.required' => trans('Validation.required',['attribute' => 'Email']), + 'email.email' => trans('Validation.email'), ]); if ($validator->fails()) @@ -87,7 +87,7 @@ class AuthDoctorController extends Controller { $user = User::where('email', $request->email)->first(); if (!$user) { - return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404); + return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); } //send email @@ -131,11 +131,11 @@ class AuthDoctorController extends Controller ->where('token', '=', $token) ->get(); - return ApiResponse::apiResponse("Success", $res, trans('message.success'), 200); + return ApiResponse::apiResponse("Success", $res, trans('Message.success'), 200); } else { - return ApiResponse::apiResponse("Internal Server Error", $data, trans('message.server_error'), 500); + return ApiResponse::apiResponse("Internal Server Error", $data, trans('Message.server_error'), 500); } } } @@ -151,9 +151,9 @@ class AuthDoctorController extends Controller 'email' => 'required|email', 'token' => 'required|numeric', ], [ - 'email.required' => trans('validation.required',['attribute' => 'Email']), - 'email.email' => trans('validation.email'), - 'token.required' => trans('validation.required',['attribute' => 'Token']), + 'email.required' => trans('Validation.required',['attribute' => 'Email']), + 'email.email' => trans('Validation.email'), + 'token.required' => trans('Validation.required',['attribute' => 'Token']), ]); if ($validator->fails()) @@ -178,15 +178,15 @@ class AuthDoctorController extends Controller $diffInMinutes = ($now - $created_at) / 60; if ($diffInMinutes > 60) { - return ApiResponse::apiResponse('Not Found', $data, trans('message.token_expired'), 404); + return ApiResponse::apiResponse('Not Found', $data, trans('Message.token_expired'), 404); } else { // Lanjutkan dengan proses pemulihan kata sandi - return ApiResponse::apiResponse("Success", $data, trans('message.success'), 200); + return ApiResponse::apiResponse("Success", $data, trans('Message.success'), 200); } } else { - return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404); + return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); } } } @@ -208,12 +208,12 @@ class AuthDoctorController extends Controller 'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/' ] ], [ - 'email.required' => trans('validation.required',['attribute' => 'Email']), - 'email.email' => trans('validation.email'), - 'token.required' => trans('validation.required',['attribute' => 'Token']), - 'new_password.required' => trans('validation.required',['attribute' => 'New Password']), - 'new_password.min' => trans('validation.min',['attribute' => 'New Password']), - 'new_password.regex' => trans('validation.regex',['attribute' => 'New Password']), + 'email.required' => trans('Validation.required',['attribute' => 'Email']), + 'email.email' => trans('Validation.email'), + 'token.required' => trans('Validation.required',['attribute' => 'Token']), + 'new_password.required' => trans('Validation.required',['attribute' => 'New Password']), + 'new_password.min' => trans('Validation.min',['attribute' => 'New Password']), + 'new_password.regex' => trans('Validation.regex',['attribute' => 'New Password']), ]); if ($validator->fails()) @@ -238,7 +238,7 @@ class AuthDoctorController extends Controller $diffInMinutes = ($now - $created_at) / 60; if ($diffInMinutes > 60) { - return ApiResponse::apiResponse('Not Found', $data, trans('message.token_expired'), 404); + return ApiResponse::apiResponse('Not Found', $data, trans('Message.token_expired'), 404); } else { // Lanjutkan dengan proses pemulihan kata sandi $user = User::where('email', $request->email)->first(); @@ -247,17 +247,17 @@ class AuthDoctorController extends Controller $newPassword = Hash::make($request->new_password); $user->password = $newPassword; $user->save(); - return ApiResponse::apiResponse("Success", $data, trans('message.success'), 200); + return ApiResponse::apiResponse("Success", $data, trans('Message.success'), 200); } else { - return ApiResponse::apiResponse('Not Found', $data, trans('message.token_expired'), 404); + return ApiResponse::apiResponse('Not Found', $data, trans('Message.token_expired'), 404); } } } else { - return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404); + return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); } } } diff --git a/Modules/Linksehat/Http/Controllers/Api/Doctor/ProfileDoctorController.php b/Modules/Linksehat/Http/Controllers/Api/Doctor/ProfileDoctorController.php index e6604fe1..28fdf245 100644 --- a/Modules/Linksehat/Http/Controllers/Api/Doctor/ProfileDoctorController.php +++ b/Modules/Linksehat/Http/Controllers/Api/Doctor/ProfileDoctorController.php @@ -137,7 +137,7 @@ class ProfileDoctorController extends Controller ]; $res_data['dataSip'] = $dataSip; - return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200); + return ApiResponse::apiResponse("Success", $res_data, trans('Message.success'), 200); } public function fWorkExperience($start, $end) From 15295ea963d832bc30bb2305df03a9aa6b149f9f Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Mon, 29 Apr 2024 08:50:49 +0700 Subject: [PATCH 13/52] Update case sensitif --- .../Http/Middleware/Doctor/Authentication.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/Linksehat/Http/Middleware/Doctor/Authentication.php b/Modules/Linksehat/Http/Middleware/Doctor/Authentication.php index d2c93b97..9f1f9c26 100644 --- a/Modules/Linksehat/Http/Middleware/Doctor/Authentication.php +++ b/Modules/Linksehat/Http/Middleware/Doctor/Authentication.php @@ -26,11 +26,11 @@ class Authentication // Add language if(!$locale) { - return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Accept-Language']), 401); + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Accept-Language']), 401); } if($locale !== 'en-US' && $locale !== 'id-ID') { - return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Accept-Language']), 400); + return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Accept-Language']), 400); } if ($locale === 'en-US') { @@ -46,19 +46,19 @@ class Authentication // Validate type accept & content type if (!$acceptHeader) { - return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Accept']), 401); + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Accept']), 401); } if (!$contentType) { - return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Content-Type']), 401); + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Content-Type']), 401); } if ($acceptHeader !== 'application/json') { - return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Accept']), 400); + return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Accept']), 400); } if($contentType !== 'application/json') { - return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Content-Type']), 400); + return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Content-Type']), 400); } return $next($request); } From 2b9553d00389f2abcb48bb0bff582819efe36520 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Mon, 29 Apr 2024 08:52:43 +0700 Subject: [PATCH 14/52] Update --- .../Http/Controllers/Api/Doctor/AuthDoctorController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php b/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php index 12acbe37..4bcc60f1 100644 --- a/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php +++ b/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php @@ -51,7 +51,7 @@ class AuthDoctorController extends Controller } $res_data = [ - 'user' => $user, + // 'user' => $user, 'token' => $user->createToken('app')->plainTextToken ]; From 8aa67c18644d0c574718e08b60faacf6ce2aec4f Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Tue, 30 Apr 2024 10:11:11 +0700 Subject: [PATCH 15/52] update create e-prescription --- .../Http/Controllers/Api/DrugController.php | 62 +- .../Api/EprescriptionController.php | 216 ++++++ .../Api/PrescriptionController.php | 140 +++- .../Controllers/Api/RequestLogController.php | 18 + Modules/Internal/Routes/api.php | 8 + app/Models/OLDLMS/LivechatSummary.php | 40 ++ app/Models/OLDLMS/Prescription.php | 14 + app/Models/OLDLMS/PrescriptionItem.php | 51 ++ frontend/dashboard/src/@types/doctor.tsx | 36 + .../layouts/dashboard/navbar/NavConfig.tsx | 6 +- .../CustomerService/FinalLog/Model/Types.tsx | 2 + .../pages/EPrescription/Livechat/Create.tsx | 93 +++ .../src/pages/EPrescription/Livechat/Form.tsx | 260 ++++++++ .../pages/EPrescription/Livechat/Index.tsx | 30 + .../src/pages/EPrescription/Livechat/List.tsx | 573 ++++++++++++++++ .../src/pages/EPrescription/Livechat/Show.tsx | 52 ++ .../src/pages/EPrescription/Livechat/View.tsx | 614 ++++++++++++++++++ .../src/pages/Master/Doctors/Form.tsx | 30 +- frontend/dashboard/src/routes/index.tsx | 17 + 19 files changed, 2234 insertions(+), 28 deletions(-) create mode 100644 Modules/Internal/Http/Controllers/Api/EprescriptionController.php create mode 100644 app/Models/OLDLMS/LivechatSummary.php create mode 100644 app/Models/OLDLMS/PrescriptionItem.php create mode 100644 frontend/dashboard/src/pages/EPrescription/Livechat/Create.tsx create mode 100644 frontend/dashboard/src/pages/EPrescription/Livechat/Form.tsx create mode 100644 frontend/dashboard/src/pages/EPrescription/Livechat/Index.tsx create mode 100644 frontend/dashboard/src/pages/EPrescription/Livechat/List.tsx create mode 100644 frontend/dashboard/src/pages/EPrescription/Livechat/Show.tsx create mode 100644 frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx diff --git a/Modules/Internal/Http/Controllers/Api/DrugController.php b/Modules/Internal/Http/Controllers/Api/DrugController.php index 97c7bd63..f5237e67 100644 --- a/Modules/Internal/Http/Controllers/Api/DrugController.php +++ b/Modules/Internal/Http/Controllers/Api/DrugController.php @@ -3,6 +3,7 @@ namespace Modules\Internal\Http\Controllers\Api; use App\Models\Drug; +use App\Models\Unit; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; @@ -25,6 +26,37 @@ class DrugController extends Controller return $drugs; } + public function drugList(Request $request){ + $drugs = Drug::query() + ->where([ + 'atc_code' => 'lms', // ini untuk menggunakan list obat yang baru + ]) + ->get(); + + $manipulatedDrugs = $drugs->map(function ($drug) { + // Contoh manipulasi, tambahkan atau ubah properti sesuai kebutuhan + return [ + 'value' => $drug->id, // Ganti dengan properti yang sesuai dari model Icd + 'label' => $drug->name, // Ganti dengan properti yang sesuai dari model Icd + ]; + }); + return Helper::responseJson(data: $manipulatedDrugs); + } + + public function unitList(Request $request){ + $units = Unit::query() + ->get(); + + $manipulatedUnits = $units->map(function ($unit) { + // Contoh manipulasi, tambahkan atau ubah properti sesuai kebutuhan + return [ + 'value' => $unit->id, // Ganti dengan properti yang sesuai dari model Icd + 'label' => $unit->name, // Ganti dengan properti yang sesuai dari model Icd + ]; + }); + return Helper::responseJson(data: $manipulatedUnits); + } + /** * Show the form for creating a new resource. * @return Renderable @@ -123,20 +155,22 @@ class DrugController extends Controller foreach ($processedData as $row) { try { - Drug::create( - [ - 'name' => $row['name'], - 'code' => $row['code'], - 'generic_name' => $row['generic_name'], - 'description' => $row['description'], - 'mims_class' => $row['mims_class'], - 'indications' => $row['indications'], - 'atc_code' => $row['atc_code'], - 'segmentation' => $row['segmentation'], - 'type' => $row['type'], - 'dosage' => $row['dosage'], - 'remark' => $row['remark'], - ] + Drug::updateOrCreate([ + 'code' => $row['code'], + ], + [ + 'name' => $row['name'], + 'code' => $row['code'], + 'generic_name' => $row['generic_name'], + 'description' => $row['description'], + 'mims_class' => $row['mims_class'], + 'indications' => $row['indications'], + 'atc_code' => $row['atc_code'], + 'segmentation' => $row['segmentation'], + 'type' => $row['type'], + 'dosage' => $row['dosage'], + 'remark' => $row['remark'], + ] ); $importedRows++; } catch (\Exception $e) { diff --git a/Modules/Internal/Http/Controllers/Api/EprescriptionController.php b/Modules/Internal/Http/Controllers/Api/EprescriptionController.php new file mode 100644 index 00000000..8aecafe6 --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/EprescriptionController.php @@ -0,0 +1,216 @@ +startDate; + $endDate = $request->endDate; + + $livechat = Livechat::with('doctor.user', 'doctor.speciality', 'appointment.appointmentDetail', 'healthCare'); + // ->where('nIDAppointment', '!=', null) + // ->where('nIDAppointment', '!=', ''); + + + if ($startDate) { + $livechat = $livechat->where('dCreateOn', '>=', $startDate); + } + + if ($endDate) { + $endDate = date('Y-m-d', strtotime($endDate . ' +1 day')); + $livechat = $livechat->where('dCreateOn', '<', $endDate); + } + $livechat = $livechat->latest()->paginate(15); + + return response()->json(Helper::paginateResources(LivechatResource::collection($livechat))); + } + + /** + * Show the specified resource. + * @param int $id + * @return Renderable + */ + public function show($id) + { + $livechat = Livechat::with('doctor.user', 'doctor.speciality', 'appointment.appointmentDetail', 'healthCare') + ->where('nIDAppointment', '!=', null)->where('nIDAppointment', '!=', '') + ->where('nID', $id) + ->first(); + return response()->json(new LivechatResource($livechat)); + } + + public function export(Request $request) + { + $startDate = $request->has('startDate') ? $request->input('startDate') : ''; + $endDate = $request->has('endDate') ? $request->input('endDate') : ''; + + $liveChats = Livechat::with('user:nID,sFirstName,sLastName,sEmail,sPhone,nIDUser', 'doctor:nID,nIDSpesialis,nIDUser', 'doctor.user:nID,sFirstName,sLastName', 'appointment:nID,sPaymentStatus,sPaymentMethod', 'appointment.appointmentDetail:nID,nIDAppointment,dTanggalAppointment,tTimeAppointment', 'healthCare:nID,sHealthCare') + ->where(function (Builder $query) use ($startDate, $endDate) { + // $query->where('nIDAppointment', '!=', null); + // $query->where('nIDAppointment', '!=', ''); + if ($startDate) { + $query->where('dCreateOn', '>=', $startDate); + } + if ($endDate) { + $endDate = date('Y-m-d', strtotime($endDate . ' +1 day')); + $query->where('dCreateOn', '<', $endDate); + } + }) + ->orderBy('nID', 'desc') + ->get(['nID', 'nIDUser', 'nIDDokter', 'nIDHealthCare', 'nIDAppointment', 'sStatus', 'sMediaDokter', 'sMedia', 'dCreateOn']); + + $headers = [ + ['value' => 'No', 'cell' => 'A1', 'mergeCell' => true, 'mergeToCell' => 'A2'], + ['value' => 'Kode TC', 'cell' => 'B1', 'mergeCell' => true, 'mergeToCell' => 'B2'], + ['value' => 'Tanggal', 'cell' => 'C1', 'mergeCell' => true, 'mergeToCell' => 'C2'], + ['value' => 'Waktu', 'cell' => 'D1', 'mergeCell' => true, 'mergeToCell' => 'D2'], + ['value' => 'Faskes', 'cell' => 'E1', 'mergeCell' => true, 'mergeToCell' => 'E2'], + ['value' => 'Nama Dokter', 'cell' => 'F1', 'mergeCell' => true, 'mergeToCell' => 'F2'], + ['value' => 'Spesialis', 'cell' => 'G1', 'mergeCell' => true, 'mergeToCell' => 'G2'], + ['value' => 'Chat Via App/Website', 'cell' => 'H1', 'mergeCell' => true, 'mergeToCell' => 'I1'], + ['value' => 'Pasien', 'cell' => 'H2', 'mergeCell' => false, 'mergeToCell' => ''], + ['value' => 'Dokter', 'cell' => 'I2', 'mergeCell' => false, 'mergeToCell' => ''], + ['value' => 'nIDUser', 'cell' => 'J1', 'mergeCell' => true, 'mergeToCell' => 'J2'], + ['value' => 'Nama Pasien', 'cell' => 'K1', 'mergeCell' => true, 'mergeToCell' => 'K2'], + ['value' => 'No Telepon Pasien', 'cell' => 'L1', 'mergeCell' => true, 'mergeToCell' => 'L2'], + ['value' => 'Email Pasien', 'cell' => 'M1', 'mergeCell' => true, 'mergeToCell' => 'M2'], + ['value' => 'Status', 'cell' => 'N1', 'mergeCell' => true, 'mergeToCell' => 'N2'], + ['value' => 'Record Type', 'cell' => 'O1', 'mergeCell' => true, 'mergeToCell' => 'O2'], + ['value' => 'nID Principal', 'cell' => 'P1', 'mergeCell' => true, 'mergeToCell' => 'P2'], + ['value' => 'Metode Pembayaran', 'cell' => 'Q1', 'mergeCell' => true, 'mergeToCell' => 'Q2'], + ]; + + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + + foreach ($headers as $header) { + $sheet->setCellValue($header['cell'], $header['value']); + + if ($header['mergeCell'] === true) { + $sheet->mergeCells($header['cell'] . ':' . $header['mergeToCell']); + } + + $sheet->getStyle($header['cell'])->getFont()->setBold(true); + $sheet->getStyle($header['cell'])->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER)->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER); + } + + $startFrom = 3; + foreach ($liveChats as $indexLiveChat => $liveChat) { + $phone = $liveChat->user->sPhone ?? '-'; + $status = $liveChat->sStatus; + $nIDUser = $liveChat->user->nIDUser ?? 0; // Principal or Dependent + $paymentMethod = $liveChat->appointment ? Helper::sPaymentMethod($liveChat->appointment->sPaymentMethod) : 'N/A'; + $fullNameDoctor = '-'; + if ($liveChat->doctor->user !== null) { + $fullNameDoctor = ''; + + if ($liveChat->doctor->user->detail !== null) { + if ($liveChat->doctor->user->detail->sTitlePrefix !== null) { + $fullNameDoctor .= $liveChat->doctor->user->detail->sTitlePrefix . '. '; + } + + if ($liveChat->doctor->user->full_name !== null) { + $fullNameDoctor .= $liveChat->doctor->user->full_name . ' '; + } + + if ($liveChat->doctor->user->detail->sTitleSuffix !== null) { + $fullNameDoctor .= $liveChat->doctor->user->detail->sTitleSuffix; + } + } + } + + $recordType = 'P'; + if ($nIDUser){ + $recordType = 'D'; + } + switch ($status) { + case 0: + $statusLivechat = "Request TC"; + break; + case 1: + $statusLivechat = "Accepted by Doctor but User not Payment"; + break; + case 2: + $statusLivechat = "Payment Success"; + break; + case 3: + $statusLivechat = "Decline by Doctor"; + break; + case 4: + $statusLivechat = "Payment Expired"; + break; + default: + $statusLivechat = "Cancel by Patient"; + } + + $sheet->setCellValue('A' . $startFrom, $indexLiveChat + 1); + $sheet->setCellValue('B' . $startFrom, $liveChat->nID ?? '-'); + $sheet->setCellValue('C' . $startFrom, Carbon::parse($liveChat->dCreateOn)->format('d-m-Y')); + $sheet->setCellValue('D' . $startFrom, Carbon::parse($liveChat->dCreateOn)->format('H:i:s')); + // $sheet->setCellValue('D' . $startFrom, Carbon::parse($liveChat->dRequestTime)->format('H:i:s')); + $sheet->setCellValue('E' . $startFrom, $liveChat->healthCare->sHealthCare ?? '-'); + $sheet->setCellValue('F' . $startFrom, $fullNameDoctor); + $sheet->setCellValue('G' . $startFrom, $liveChat->doctor->speciality->sSpesialis ?? '-'); + $sheet->setCellValue('H' . $startFrom, $liveChat->sMedia ?? '-'); + $sheet->setCellValue('I' . $startFrom, $liveChat->sMediaDokter ?? '-'); + $sheet->setCellValue('J' . $startFrom, $liveChat->user->nID ?? '-'); + $sheet->setCellValue('K' . $startFrom, $liveChat->user->full_name ?? '-'); + $sheet->setCellValue('L' . $startFrom, preg_replace('/(\d{3})(\d{4})(\d{3})/', '$1$2$3', $phone)); + $sheet->setCellValue('M' . $startFrom, $liveChat->user->sEmail ?? '-'); + $sheet->setCellValue('N' . $startFrom, $statusLivechat); + $sheet->setCellValue('O' . $startFrom, $recordType); + $sheet->setCellValue('P' . $startFrom, $nIDUser ?? '-'); + $sheet->setCellValue('Q' . $startFrom, $paymentMethod ?? '-'); + $startFrom++; + } + + foreach (['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K', 'L', 'M', 'O', 'P'] as $header) { + if ($header === 'A') { + $spreadsheet->getActiveSheet()->getColumnDimension($header)->setWidth(35, 'px'); + } elseif ($header === 'H' || $header === 'I') { + $spreadsheet->getActiveSheet()->getColumnDimension($header)->setWidth(100, 'px'); + } else { + $spreadsheet->getActiveSheet()->getColumnDimension($header)->setAutoSize(true); + } + } + + $spreadsheet->getActiveSheet()->getStyle('A3:A' . $startFrom)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER)->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER); + + $sheet->getDefaultRowDimension()->setRowHeight(-1); + $sheet->setTitle('Live Chat Report'); + + $writer = new Xlsx($spreadsheet); + ob_start(); + $writer->save('php://output'); + $content = ob_get_contents(); + ob_end_clean(); + + $fileName = 'result-' . now()->getPreciseTimestamp(3) . '-livechat-report.xlsx'; + Storage::disk('public')->put('temp/' . $fileName, $content); + + $fileUrl = url('storage/temp/' . $fileName); + + return Helper::responseJson([ + "file_url" => $fileUrl + ]); + } +} diff --git a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php index a7127855..4d36f639 100644 --- a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php +++ b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php @@ -2,11 +2,30 @@ namespace Modules\Internal\Http\Controllers\Api; - +use App\Helpers\Helper; +use App\Models\OLDLMS\Livechat; +use App\Models\OLDLMS\LivechatSummary; +use App\Models\OLDLMS\Appointment; +use App\Models\OLDLMS\User; +use App\Models\OLDLMS\UserDetail; use App\Models\OLDLMS\Prescription; +use App\Models\OLDLMS\PrescriptionItem; +use App\Models\Icd; +use App\Models\Organization; +use App\Models\Drug; +use App\Models\Unit; +use Illuminate\Contracts\Support\Renderable; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Storage; +use Modules\Internal\Transformers\LivechatResource; +use PhpOffice\PhpSpreadsheet\Spreadsheet; +use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Validator; +use DB; class PrescriptionController extends Controller { @@ -15,21 +34,30 @@ class PrescriptionController extends Controller * @param int|null $id * @return \Illuminate\Http\JsonResponse */ - public function index($id = null) + public function index(Request $request) { - $query = Prescription::query(); - if ($id !== null) { - $query->where('nID', $id); + $startDate = $request->startDate; + $endDate = $request->endDate; + + $livechat = Livechat::with('doctor.user', 'doctor.speciality', 'appointment.appointmentDetail', 'healthCare', 'summary'); + // ->where('nIDAppointment', '!=', null) + // ->where('nIDAppointment', '!=', ''); + if ($startDate) { + $livechat = $livechat->where('dCreateOn', '>=', $startDate); } - - $prescriptions = $query->select('nID','nIDLiveChat', 'nIDLiveChatSummary', 'nIDDokter', 'sDokterName', 'dTanggalResep', 'sSource', 'nIDUser', 'sKodeResep', 'sDiagnose', 'sStatus') - ->get(); - // $prescriptions->toArray(); - // dd($prescriptions); + if ($endDate) { + $endDate = date('Y-m-d', strtotime($endDate . ' +1 day')); + $livechat = $livechat->where('dCreateOn', '<', $endDate); + } - return response()->json($prescriptions); - // return response()->json(Helper::paginateResources(LivechatResource::collection($livechat))); + $livechat = $livechat->whereHas('summary', function ($query) { + $query->whereNotNull('nIDLiveChat'); + }); + + $livechat = $livechat->latest()->paginate(15); + + return response()->json(Helper::paginateResources(LivechatResource::collection($livechat))); } @@ -51,6 +79,94 @@ class PrescriptionController extends Controller */ public function store(Request $request) { + $livechat = Livechat::where('nID', $request->id)->first(); + $livechatSummary = LivechatSummary::where('nIDLivechat', $request->id)->first(); + + $userDokter = User::where('nID', $livechat->nIDDokter)->first(); + $userDetailDokter = UserDetail::where('nIDUser', $userDokter->nID)->first(); + + $dokter = $userDetailDokter->sTitlePrefix . ' ' . $userDokter->sFirstName . ' ' . $userDokter->sLastName . ' ' . $userDetailDokter->sTitleSuffix; + + $kodeResep = 'LMS' . date('ymd') . rand(1,100); + $diagnosis = explode(",",$request->diagnosis); + + if(isset($request->diagnosis) && is_array($diagnosis) && count($diagnosis) > 0) { + foreach($diagnosis as $data){ + $icd = Icd::where('code', $data)->first(); + array_push($diagnosis, $icd->name); + }; + } + $sDiagnosis = implode(", ",$diagnosis); + $hospitalData = Organization::where('id', $request->hospital)->first(); + $hospital = ''; + if ($hospitalData) { + $hospital = $hospitalData->code; + } + + $data = [ + 'nIDLivechat' => $request->id, + 'nIDLivechatSummary' => $livechatSummary->nID, + 'nIDDokter' => $livechat->nIDDokter, + 'sDokterName' => $dokter, + 'dTanggalResep' => date('Y-m-d H:i:s'), + 'sSource' => 'lms', + 'nIDUser' => $livechat->nIDUser, + 'sRegID' => '', + 'sKodeResep' => $kodeResep, + 'sDiagnose' => $sDiagnosis, + 'sKodeRS' => $hospital, + ]; + + $prescription = Prescription::create($data); + + $medicine = $request->medicine; + $customMessages = [ + 'required' => 'Kolom :attribute wajib diisi.', + 'numeric' => 'Kolom :attribute harus berupa angka.', + ]; + + $validator = Validator::make($request->all(), [ + 'medicine' => 'required|array', + 'medicine.*' => 'required', + ], $customMessages); + + if ($validator->fails()) { + return Helper::responseJson([$request->all()],'error', 400, $validator->errors()); + } else { + // BeginTransaction + DB::beginTransaction(); + foreach($medicine as $key => $value){ + $drugData = Drug::where('id', $value['drug_id'])->first(); + $drug = ''; + if ($drugData){ + $drug = $drugData->name; + } + $unitData = Unit::where('id', $value['unit_id'])->first(); + $unit = ''; + if ($unitData) { + $unit = $unitData->name; + } + $data = [ + 'nIDPrescription' => $prescription->id, + 'sItemName' => $drug, + 'nQty' => $value['qty'], + 'sSatuan' => $unit, + 'sSigna' => $value['signa'], + 'sNote' => $value['note'], + ]; + // Insert Data + try { + PrescriptionItem::create($data); + } catch (\Throwable $th) { + DB::rollBack(); + return Helper::responseJson(status: 'failed', statusCode: 500, message: $th->getMessage()); + } + } + DB::commit(); + return Helper::responseJson(status: 'success', statusCode: 201, message: 'success', data: $request->toArray()); + } + + return Helper::responseJson(status: 'success', statusCode: 200, message: 'Resep Online berhasil ajukan!', data: $prescription); } /** diff --git a/Modules/Internal/Http/Controllers/Api/RequestLogController.php b/Modules/Internal/Http/Controllers/Api/RequestLogController.php index 94a4bb3a..36fb8b57 100644 --- a/Modules/Internal/Http/Controllers/Api/RequestLogController.php +++ b/Modules/Internal/Http/Controllers/Api/RequestLogController.php @@ -211,6 +211,24 @@ class RequestLogController extends Controller }); return Helper::responseJson(data: $manipulatedIcds); } + + public function hospitals(){ + $organizations = Organization::query() + ->where([ + 'type' => 'hospital', + 'status' => 'active', + ]) + ->get(); + + $manipulatedOrganizations = $organizations->map(function ($organization) { + // Contoh manipulasi, tambahkan atau ubah properti sesuai kebutuhan + return [ + 'value' => $organization->id, // Ganti dengan properti yang sesuai dari model Icd + 'label' => $organization->name, // Ganti dengan properti yang sesuai dari model Icd + ]; + }); + return Helper::responseJson(data: $manipulatedOrganizations); + } /** * Show the form for editing the specified resource. diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 52bfb748..93abbc6d 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -286,6 +286,9 @@ Route::prefix('internal')->group(function () { // search diagnosis Route::get('diagnosis', [RequestLogController::class, 'diagnosis']); + Route::get('hospitals', [RequestLogController::class, 'hospitals']); + Route::get('drugs', [DrugController::class, 'drugList']); + Route::get('units', [DrugController::class, 'unitList']); // insert benefit Route::post('customer-service/request/insert-benefit', [RequestLogBenefitController::class, 'store']); @@ -303,7 +306,12 @@ Route::prefix('internal')->group(function () { Route::resource('appointments', AppointmentController::class); Route::get('live-chat/export', [LivechatController::class, 'export']); Route::resource('live-chat', LivechatController::class); + Route::get('prescription', [PrescriptionController::class, 'index']); + + Route::post('prescription', [PrescriptionController::class, 'store']); + + Route::get('prescription/{id}', [PrescriptionController::class, 'index']); Route::get('doctorrating', [DoctorRatingController::class, 'index']); Route::get('doctorrating/{id}', [PrescriptionController::class, 'index']); diff --git a/app/Models/OLDLMS/LivechatSummary.php b/app/Models/OLDLMS/LivechatSummary.php new file mode 100644 index 00000000..076fbd58 --- /dev/null +++ b/app/Models/OLDLMS/LivechatSummary.php @@ -0,0 +1,40 @@ + 'Menunggu Konfirmasi', + // 1 => 'Diterima', + // 3 => 'Ditolak', + // 2 => 'Selesai', + // 4 => 'Expired', + // ]; + + public $sStatusNames = [ + 0 => 'Request TC', + 1 => 'Accepted by Doctor but User not Payment', + 3 => 'Decline by Doctor', + 2 => 'Payment Success', + 4 => 'Payment Expired', + 5 => 'Cancel by Patient' + ]; + + const CREATED_AT = 'dCreateOn'; + const UPDATED_AT = 'dUpdateOn'; + const DELETED_AT = 'dDeleteOn'; + + protected $connection = 'oldlms'; + + protected $table = 'tx_livechat_summary'; + + protected $primaryKey = 'nID'; +} diff --git a/app/Models/OLDLMS/Prescription.php b/app/Models/OLDLMS/Prescription.php index 8f75f528..bdb36ef6 100644 --- a/app/Models/OLDLMS/Prescription.php +++ b/app/Models/OLDLMS/Prescription.php @@ -10,6 +10,20 @@ class Prescription extends Model { use HasFactory; + protected $fillable = [ + 'nIDLivechat', + 'nIDLivechatSummary', + 'nIDDokter', + 'sDokterName', + 'dTanggalResep', + 'sSource', + 'nIDUser', + 'sRegID', + 'sKodeResep', + 'sDiagnose', + 'sKodeRS', + ]; + // public $sStatusNames = [ // 0 => 'Menunggu Konfirmasi', diff --git a/app/Models/OLDLMS/PrescriptionItem.php b/app/Models/OLDLMS/PrescriptionItem.php new file mode 100644 index 00000000..7654f78e --- /dev/null +++ b/app/Models/OLDLMS/PrescriptionItem.php @@ -0,0 +1,51 @@ + '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_prescription_items'; + + // protected $appends = [ + // 'status_name', + // ]; + + protected $casts = [ + 'dTanggalResep' => 'datetime', + ]; + +} diff --git a/frontend/dashboard/src/@types/doctor.tsx b/frontend/dashboard/src/@types/doctor.tsx index a635e68c..034365ee 100644 --- a/frontend/dashboard/src/@types/doctor.tsx +++ b/frontend/dashboard/src/@types/doctor.tsx @@ -30,6 +30,13 @@ export type Specialities = { name: string; }; +export type PeriodStart = { + date: string; +}; +export type PeriodEnd = { + date: string; +}; + export type Practitioner = { id: number; name: string; @@ -50,6 +57,35 @@ export type Practitioner = { active: boolean | number; organizations?: Organizations[]; specialities?: Specialities[]; + period_start?: PeriodStart[]; + period_end?: PeriodEnd[]; }; +export type Medicine = { + id: number; + drug_id: number; + unit_id: number; + qty: number; + signa: string; + note: string; +} + +export type Appointment = { + id:number; + name:string; + address:string; + birth_date:string; + gender:string; + description:string; + birth_place:string; + active:number; + avatar_url:string; + doctor_id:number; + organizations:Organizations[]; + specialities:Specialities[]; + diagonis:string; + hospital:string; + medicine: Medicine[]; +} + export type PractitionerType = Practitioner; \ No newline at end of file diff --git a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx index 73c6570f..724bbe82 100644 --- a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx +++ b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx @@ -100,7 +100,7 @@ const navConfig = [ { title: 'Appointment', path: '/report/appointments' }, { title: 'Live Chat', path: '/report/live-chat' }, { title: 'Linksehat Payment', path: '/report/linksehat-payments' }, - { title: 'Prescription', path: '/report/prescription' }, + // { title: 'Prescription', path: '/report/prescription' }, { title: 'Doctor Rating', path: '/report/doctorrating' }, ], @@ -123,6 +123,10 @@ const navConfig = [ title: 'LINKING TOOLS', path: '/linking', }, + { + title: 'E - PRESCRIPTION', + path: '/e-prescription/live-chat', + }, ], }, ]; diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx index 8543d3b6..8b2a0a4f 100644 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx @@ -31,6 +31,8 @@ export type DetailFinalLogType = { id : number, code : string, member_id : string, + invoice_no : string, + billing_no : string, provider : string, policy_number : string, name : string|any, diff --git a/frontend/dashboard/src/pages/EPrescription/Livechat/Create.tsx b/frontend/dashboard/src/pages/EPrescription/Livechat/Create.tsx new file mode 100644 index 00000000..efb7a395 --- /dev/null +++ b/frontend/dashboard/src/pages/EPrescription/Livechat/Create.tsx @@ -0,0 +1,93 @@ +import { useEffect, useState } from 'react'; +import { paramCase } from 'change-case'; +import { useParams, useLocation } from 'react-router-dom'; +// @mui +import { Container, Stack } from '@mui/material'; +import useSettings from '../../../hooks/useSettings'; +import Page from '../../../components/Page'; +import Form from './Form'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import axios from '../../../utils/axios'; +import { Practitioner } from '../../../@types/doctor'; +import ButtonBack from '../../../components/ButtonBack'; + +export default function Create() { + const { themeStretch } = useSettings(); + const { id } = useParams(); + + const isEdit = id ? true : false; + + const [currentPractitioner, setCurrentPractitioner] = useState(); + + useEffect(() => { + if (isEdit) { + axios.get('/doctors/' + id).then((res) => { + setCurrentPractitioner(res.data); + }); + } + }, [id]); + + return ( + + + + {/* */} + + + +
+ + + ); +} +// const pageTitle = 'Create Data Dokter'; +// return ( +// +// +// + +// +// +// +// +// +// +// +// +// +// ); +// } diff --git a/frontend/dashboard/src/pages/EPrescription/Livechat/Form.tsx b/frontend/dashboard/src/pages/EPrescription/Livechat/Form.tsx new file mode 100644 index 00000000..39885db8 --- /dev/null +++ b/frontend/dashboard/src/pages/EPrescription/Livechat/Form.tsx @@ -0,0 +1,260 @@ +import * as Yup from 'yup'; +import { useSnackbar } from 'notistack'; +import { useNavigate } from 'react-router-dom'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import MenuItem from '@mui/material/MenuItem'; + +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import * as React from 'react'; + +// form +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +// @mui +import { styled } from '@mui/material/styles'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Avatar, + Button, + ButtonGroup, + Card, + FormHelperText, + Grid, + Stack, + Typography, + TextField, + Chip, +} from '@mui/material'; + +import CancelIcon from '@mui/icons-material/Cancel'; + +// components +import { + FormProvider, + RHFTextField, + RHFRadioGroup, + RHFUploadAvatar, + RHFSwitch, + RHFEditor, + RHFDatepicker, + RHFMultiCheckbox, + RHFCheckbox, + RHFCustomMultiCheckbox, +} from '../../../components/hook-form'; +import axios from '../../../utils/axios'; +import { fCurrency } from '../../../utils/formatNumber'; +import { Practitioner } from '../../../@types/doctor'; + +import { Label, Rowing } from '@mui/icons-material'; + +const LabelStyle = styled(Typography)(({ theme }) => ({ + ...theme.typography.subtitle2, + color: theme.palette.text.secondary, + marginBottom: theme.spacing(1), +})); + +const HeaderStyle = styled('header')(({ theme }) => ({ + paddingBottom: theme.spacing(5), + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +})); + +const Title = styled(Typography)(({ theme }) => ({ + ...theme.typography.h4, + boxShadow: 'none', + // paddingBottom: theme.spacing(3), + fontWeight: 700, + color: '#005B7F', +})); + +interface FormValuesProps extends Partial { + taxes: boolean; + inStock: boolean; +} + +type Props = { + isEdit: boolean; + currentPractitioner?: Practitioner; +}; + +const Span = styled(Typography)(({ theme }) => ({ + boxShadow: 'none', + paddingBottom: theme.spacing(1), +})); + +const Text = styled(Typography)(({ theme }) => ({ + boxShadow: 'none', + paddingBottom: theme.spacing(3), +})); + +export default function PractitionerForm({ isEdit, currentPractitioner }: Props) { + const navigate = useNavigate(); + const [practitioner_group, setPractitionerGroups] = useState([]); + + // const [ errors, setErrors ] = useState<{ [key: string]: string }>({}); + + const { enqueueSnackbar } = useSnackbar(); + + const NewCorporateSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + // file: Yup.boolean().required('Corporate Status is required'), + }); + + const defaultValues = useMemo( + () => ({ + id: currentPractitioner?.id, + name: currentPractitioner?.name || '', + address: currentPractitioner?.address || '', + birth_date: currentPractitioner?.birth_date || '', + gender: currentPractitioner?.gender || '', + description: currentPractitioner?.description || '', + birth_place: currentPractitioner?.birth_place || '', + active: currentPractitioner?.active === 1 ? true : false, + avatar_url: currentPractitioner?.avatar_url || '', + doctor_id: currentPractitioner?.doctor_id || '', + organizations: currentPractitioner?.organizations || [], + specialities: currentPractitioner?.specialities || [], + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [currentPractitioner] + ); + + console.log('defaultValues', defaultValues); + + function StatusLabel({ value }: { value: boolean }) { + return ( + + ); + } + const methods = useForm({ + resolver: yupResolver(NewCorporateSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const values = watch(); + + useEffect(() => { + if (isEdit && currentPractitioner) { + reset(defaultValues); + } + if (!isEdit) { + reset(defaultValues); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isEdit, currentPractitioner]); + + const handleActivate = (event: React.ChangeEvent) => { + setValue('active', event.target.checked); + + console.log('event.target.checked', event.target.checked); + + const formData = new FormData(); + formData.append('active', event.target.checked ? '1' : '0'); + formData.append('_method', 'PUT'); + axios.post('/doctors/' + currentPractitioner?.id ?? '', formData); + + enqueueSnackbar('active Updated Successfully!', { variant: 'success' }); + }; + + return ( + + + + {/* */} + + + + Data Dokter + + + {/* Status Rumah Sakit */} + + + + + Informasi Umum + + + + + Nama Dokter + {currentPractitioner?.name ? currentPractitioner?.name : '-'} + No Telp + {currentPractitioner?.phone ? currentPractitioner?.phone : '-'} + Tempat Lahir + + {currentPractitioner?.birth_place ? currentPractitioner?.birth_place : '-'} + + Alamat + {currentPractitioner?.address ? currentPractitioner?.address : '-'} + + + Jenis Kelamin + {currentPractitioner?.gender ? currentPractitioner?.gender : '-'} + Email + {currentPractitioner?.email ? currentPractitioner?.email : '-'} + Tanggal Lahir + + {currentPractitioner?.birth_date ? currentPractitioner?.birth_date : '-'} + + + + + + Tempat Praktik + {currentPractitioner?.organizations?.map((item, index) => ( + + + + {item.name} + + + + ))} + + + Spesialisasi + {currentPractitioner?.specialities?.map((item, index) => ( + + + + {item.name} + + + + ))} + + + + + ); +} diff --git a/frontend/dashboard/src/pages/EPrescription/Livechat/Index.tsx b/frontend/dashboard/src/pages/EPrescription/Livechat/Index.tsx new file mode 100644 index 00000000..c5dfb515 --- /dev/null +++ b/frontend/dashboard/src/pages/EPrescription/Livechat/Index.tsx @@ -0,0 +1,30 @@ +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 Doctor() { + const { themeStretch } = useSettings(); + + const { id } = useParams(); + + const pageTitle = 'E-Prescription'; + return ( + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/EPrescription/Livechat/List.tsx b/frontend/dashboard/src/pages/EPrescription/Livechat/List.tsx new file mode 100644 index 00000000..c768e17a --- /dev/null +++ b/frontend/dashboard/src/pages/EPrescription/Livechat/List.tsx @@ -0,0 +1,573 @@ +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, +} 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 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 { Controller } from 'react-hook-form'; + +import SvgIconStyle from '../../../components/SvgIconStyle'; +import { GridSearchIcon } from '@mui/x-data-grid'; +import { Add, 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 { RHFDatepicker } from '@/components/hook-form'; +import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import { fDateOnly } from '@/utils/formatTime'; + +// ---------------------------------------------------------------------- + +export default function List() { + // Generate the every row of the table + + const navigate = useNavigate(); + const { organization_id } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const [searchParamsOrganizations, setSearchParamsOrganizations] = useSearchParams(); + const [searchParamsSpecialities, setSearchParamsSpecialities] = useSearchParams(); + const [searchParamsFilter, setSearchParamsFilter] = useSearchParams(); + + 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); + }; + + useEffect(() => { + // Trigger First Search + setSearchText(searchParams.get('search') ?? ''); + }, []); + + const item = [ + { + id: '', + value: '', + name: 'Semua', + }, + ]; + + return ( + + + + { + if (event.key === 'Enter') { + handleSearchSubmit(event); + } + }} + value={searchText} + InputProps={{ + startAdornment: ( + + + + ), + placeholder: 'Search', + }} + /> + + + + { + try { + if (value && !!Date.parse(value)) { + const date = value ? fDateOnly(value) : ''; + var entries = [...searchParams.entries(), ['startDate', date ?? '']]; + if (!searchParams.get('endDate')) { + entries = [...entries, ['endDate', date ?? '']]; + } + const filter = Object.fromEntries(entries); + + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => } + /> + + + + + { + try { + if (value && !!Date.parse(value)) { + const date = fDateOnly(value); + var entries = [...searchParams.entries(), ['endDate', date ?? '']]; + if (!searchParams.get('startDate')) { + entries = [...entries, ['startDate', date ?? '']]; + } + const filter = Object.fromEntries(entries); + + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => ( + + )} + /> + + + + + + + + ); + } + + function FilterForm(props: any) { + // IMPORT + return ( + + + + + + ); + } + + function createData(doctor: Practitioner): Practitioner { + return { + ...doctor, + }; + } + + 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 ( + + + + setOpen(!open)}> + {open ? : } + + + {row.date_created ? row.date_created : '-'} + {row.time_request ? row.time_request : '-'} + {row.health_care ? row.health_care : '-'} + {row.doctor_name ? row.doctor_name : '-'} + {row.speciality ? row.speciality : '-'} + {row.pasien_name ? row.pasien_name : '-'} + {row.pasien_phone ? row.pasien_phone : '-'} + {/* {row.appointment_media ? row.appointment_media : '-'} */} + {row.patient_media ? row.patient_media : '-'} + {row.doctor_media ? row.doctor_media : '-'} + {/* + {row.status_appointment ? row.status_appointment : '-'} + */} + {row.status_chat ? row.status_chat : '-'} + + + + + + + + + {/* COLLAPSIBLE ROW */} + + + + + + + + + Metode Pembayaran + + + : {row.payment_method ? row.payment_method : '-'} + + + + Jenis Benefit + + + : - + + + Durasi + + + : {row.duration ? row.duration : '-'} + + + Kode + + + : {row.id ? row.id : '-'} + + + + + + + + + + {/* END COLLAPSIBLE ROW */} + { + 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('/prescription', { + params: filter, + }); + setDataTableLoading(false); + setDataTableData(response.data); + }; + + const exportExcel = async () => { + var filter = Object.fromEntries([...searchParams.entries()]); + + await axios + .get('live-chat/export', { params: filter }) + .then((res) => { + enqueueSnackbar('Data berhasil di Export', { + variant: 'success', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }); + + document.location.href = res.data.data.file_url; + }) + .catch((err) => + enqueueSnackbar('Data Gagal di Export', { + variant: 'error', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }) + ); + }; + + // 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 */} + + + + + {/* */} + + + Tanggal + + + Waktu + + + Faskes + + + Nama Dokter + + + Spesialisasi + + + Nama Pasien + + + No Telepon Pasien + + {/* + Appointment Via App/Website + */} + + Chat Via App/Website + + {/* + Status Appointment + */} + + Status + + + + + {/* + Tanggal Booking + + + Tanggal Appointment + + + Faskes + + + Nama Dokter + + + Spesialisasi + */} + + Pasien + + + Dokter + + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} +
+
+ + +
+
+ ); +} diff --git a/frontend/dashboard/src/pages/EPrescription/Livechat/Show.tsx b/frontend/dashboard/src/pages/EPrescription/Livechat/Show.tsx new file mode 100644 index 00000000..3d85a52b --- /dev/null +++ b/frontend/dashboard/src/pages/EPrescription/Livechat/Show.tsx @@ -0,0 +1,52 @@ +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('/live-chat/' + id).then((res) => { + setCurrentAppointment(res.data); + }); + } + }, [id]); + + return ( + + + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx b/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx new file mode 100644 index 00000000..8b4467fa --- /dev/null +++ b/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx @@ -0,0 +1,614 @@ +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 {useFieldArray, 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, + Autocomplete, + 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 RHFTextFieldMoney from "@/components/hook-form/v2/RHFTextFieldMoney"; + +import { Label, Rowing, Spa } from '@mui/icons-material'; +import { border, padding } from '@mui/system'; +import { IconButton } from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +import RemoveIcon from '@mui/icons-material/Remove'; + +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 || [], + diagnosis: currentAppointment?.diagnosis || '', + hospital: currentAppointment?.hospital || '', + medicine : [{ + id: 0, + drug_id: 0, + qty: 0, + signa: '', + unit_id: 0, + note: '', // input to database + }], + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [currentAppointment] + ); + + + const methods = useForm({ + // resolver: yupResolver(NewCorporateSchema), + defaultValues, + }); + + const {fields, append, remove} = useFieldArray({name: 'medicine',control: methods.control}) + + // Autocomplite ICD + const [icdOptions, setIcdOptions] = useState([ + { value: '-', label: '-' } + ]); + const [selectedIcdOptions, setSelectedIcdOptions] = useState([]); + useEffect(() => { + const selectedCodes = icdOptions.filter((icd) => { + // Logika pemilihan sesuai kebutuhan + }); + setSelectedIcdOptions(selectedCodes); + }, [icdOptions]); + + + useEffect(() => { + // Ambil data dari API dan atur opsi ICD + axios.get('diagnosis') + .then((response) => { + setIcdOptions(response.data.data); + }) + .catch((error) => { + console.error('Error fetching ICD options:', error); + }); + }, []); // useEffect dijalankan hanya sekali saat komponen dimount + + + + // Autocomplite Rumah Sakit + const [hospitalOptions, setHospitalOptions] = useState([]); + const [selectedHospitalOption, setSelectedHospitalOption] = useState(null); + + // Ambil data dari API dan atur opsi rumah sakit + useEffect(() => { + axios.get('hospitals') + .then((response) => { + setHospitalOptions(response.data.data); + }) + .catch((error) => { + console.error('Error fetching hospital options:', error); + }); + }, []); // useEffect dijalankan hanya sekali saat komponen dimount + + // Set default value saat hospitalOptions berubah + useEffect(() => { + if (hospitalOptions.length > 0 && !selectedHospitalOption) { + setSelectedHospitalOption({value: '-', label: '-'}); + } + }, [hospitalOptions, selectedHospitalOption]); + + // Autocomplite drugs + const [drugOptions, setDrugsOptions] = useState([]); + const [selectedDrugsOptions, setSelectedDrugsOptions] = useState({}); + + const handleAutocompleteChange = (newValue, index) => { + setSelectedDrugsOptions((prevState) => ({ + ...prevState, + [index]: newValue + })); + setValue(`medicine.${index}.drug_id`, newValue ? newValue.value : ''); + }; + + // Ambil data dari API dan atur opsi rumah sakit + useEffect(() => { + axios.get('drugs') + .then((response) => { + setDrugsOptions(response.data.data); + }) + .catch((error) => { + console.error('Error fetching drug options:', error); + }); + }, []); // useEffect dijalankan hanya sekali saat komponen dimount + + // Autocomplite unit + const [unitOptions, setUnitsOptions] = useState([]); + const [selectedUnitsOptions, setSelectedUnitsOptions] = useState({}); + + const handleAutocompleteChangeUnit = (newValue, index) => { + setSelectedUnitsOptions((prevState) => ({ + ...prevState, + [index]: newValue + })); + setValue(`medicine.${index}.unit_id`, newValue ? newValue.value : ''); + }; + + // Ambil data dari API dan atur opsi rumah sakit + useEffect(() => { + axios.get('units') + .then((response) => { + setUnitsOptions(response.data.data); + }) + .catch((error) => { + console.error('Error fetching unit options:', error); + }); + }, []); // useEffect dijalankan hanya sekali saat komponen dimount + + + 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]); + + const onSubmit = async (data: FormValuesProps) => { + try { + const formData = new FormData(); + formData.append('id', data.id); + formData.append('diagnosis', data.diagnosis); + formData.append('hospital', data.hospital); + + // Iterasi melalui setiap objek dalam array medicine dan menambahkannya ke FormData + data.medicine.forEach((medicineObj, index) => { + // Anda dapat menambahkan setiap properti dari objek medicine ke FormData + formData.append(`medicine[${index}][drug_id]`, medicineObj.drug_id); + formData.append(`medicine[${index}][qty]`, medicineObj.qty); + formData.append(`medicine[${index}][unit_id]`, medicineObj.unit_id); + formData.append(`medicine[${index}][signa]`, medicineObj.signa); + formData.append(`medicine[${index}][note]`, medicineObj.note); + }); + + + const response = await axios.post('/prescription', formData); + reset(); + enqueueSnackbar('Berhasil menambahkan resep', { + variant: 'success', + }); + navigate('/e-prescription/live-chat'); + } catch (error: any) { + console.log(error, 'submit') + enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' }); + } + + const ascent = document?.querySelector('ascent'); + if (ascent != null) { + ascent.innerHTML = ''; + } + }; + return ( + + + + {/* */} + + + + } + spacing={2} + > + Data Live Chat + + + + + + + + + + + Status Appointment : + + + + + + + + Status Chat : + + + + + + + + + + + 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 : '-'} + + Durasi + {currentAppointment?.duration ? currentAppointment?.duration : '-'} + + + 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 + )} + + + + + + E-Prescription + + + + + + Diagnosa + option.label} + fullWidth + value={selectedIcdOptions} + onChange={(e, newValues) => { + const selectedCodes = newValues.map((value) => value.value); + setValue('diagnosis', selectedCodes); + setSelectedIcdOptions(newValues); + }} + renderInput={(params) => ( + + )} + /> + + + + Rumah Sakit + option.label} + fullWidth + value={selectedHospitalOption} + onChange={(e, newValue) => { + setSelectedHospitalOption(newValue); + setValue('hospital', newValue ? newValue.value : ''); // Simpan nilai rumah sakit + }} + renderInput={(params) => ( + + )} + /> + + + + + + Obat + + + + + + {fields.map((field, index) => ( + + + option.label} + fullWidth + value={selectedDrugsOptions[index] || null} + onChange={(e, newValue) => handleAutocompleteChange(newValue, index)} + renderInput={(params) => ( + + )} + /> + + + + + + option.label} + fullWidth + value={selectedUnitsOptions[index] || null} + onChange={(e, newValue) => handleAutocompleteChangeUnit(newValue, index)} + renderInput={(params) => ( + + )} + /> + + + + + + + + + {index !== fields.length - 1 && ( + + remove(index)}> + + + + )} + + ))} + + + + {!isEdit ? 'Save' : 'Save'} + + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Doctors/Form.tsx b/frontend/dashboard/src/pages/Master/Doctors/Form.tsx index 55d27f88..22ab0467 100644 --- a/frontend/dashboard/src/pages/Master/Doctors/Form.tsx +++ b/frontend/dashboard/src/pages/Master/Doctors/Form.tsx @@ -188,6 +188,8 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props) // formData.append('active', data.active ? '1' : '0'); forms.forEach((form, index) => { formData.append(`practices[${index}][organization_id]`, form.organizationId); + formData.append(`practices[${index}][period_start]`, form.periodStart); + formData.append(`practices[${index}][period_end]`, form.periodEnd); form.specialities.forEach((speciality, i) => { formData.append(`practices[${index}][specialities][${i}][speciality_id]`, speciality); }); @@ -225,6 +227,7 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props) const [organizations, setOrganizations] = useState([]); const [specialities, setSpecialities] = useState([]); + const [price, setPrice] = useState([]); useEffect(() => { axios.get(`/search-organizations`).then((response) => { @@ -281,6 +284,8 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props) return { organizationId: practice.organization_id, specialities: practice.specialities.map((s) => s.speciality_id), + periodStart: practice.period_start, + periodEnd: practice.period_end, }; }); setForms(newForms); @@ -289,6 +294,9 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props) { organizationId: '', specialities: [], + periodStart: '', + periodEnd: '', + }, ]); } @@ -333,6 +341,8 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props) ...forms, { organizationId: '', + periodStart: '', + periodEnd: '', specialities: [], }, ]); @@ -397,6 +407,8 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props) setForms(updatedForms); }; + console.log(forms, 'forms') + return ( @@ -492,7 +504,6 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props) value={findValueOrganization(form.organizationId) ?? ''} getOptionLabel={(option) => option.name} isOptionEqualToValue={(option, value) => { - console.log(value, option, 'test') return option.value === value.value } } @@ -541,6 +552,23 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props) )} + + + Period Start + + + + + + Period End + + + ))} diff --git a/frontend/dashboard/src/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index a1e4c8ae..2e1a40f0 100644 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -511,6 +511,18 @@ export default function Router() { path: 'custormer-service/final-log/detail/:id', element: , }, + { + path: 'e-prescription/live-chat', + element: , + }, + { + path: 'e-prescription/live-chat/:id', + element: , + }, + { + path: 'e-prescription/live-chat/:id/show', + element: , + }, ], }, // { @@ -672,6 +684,11 @@ const Livechat = Loadable(lazy(() => import('../pages/Report/Livechat/Index'))); const LivechatCreate = Loadable(lazy(() => import('../pages/Report/Livechat/Create'))); const LivechatShow = Loadable(lazy(() => import('../pages/Report/Livechat/Show'))); + +const EPrescription = Loadable(lazy(() => import('../pages/EPrescription/Livechat/Index'))); +const EPrescriptionCreate = Loadable(lazy(() => import('../pages/EPrescription/Livechat/Create'))); +const EPrescriptionShow = Loadable(lazy(() => import('../pages/EPrescription/Livechat/Show'))); + const LinksehatPayment = Loadable(lazy(() => import('../pages/Report/LinksehatPayments/Index'))); const MasterDrug = Loadable(lazy(() => import('../pages/Master/Drug/Index'))); From 9168af67b6b663a9f92e78a49c97393142fe1db9 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Tue, 30 Apr 2024 17:03:44 +0700 Subject: [PATCH 16/52] fixing add prescription --- .../Api/PrescriptionController.php | 33 +++- .../Transformers/LivechatResource.php | 11 ++ .../Controllers/Api/LivechatController.php | 2 + app/Models/OLDLMS/Livechat.php | 4 + app/Models/OLDLMS/Prescription.php | 2 + app/Models/OLDLMS/PrescriptionItem.php | 1 + app/Models/Prescription.php | 18 ++ app/Models/PrescriptionItem.php | 19 +++ ...0705_add_column_to_table_practitioners.php | 3 +- ...add_column_to_table_practitioner_roles.php | 3 +- ...04_30_132417_create_prescription_table.php | 34 ++++ ...132747_create_prescription_items_table.php | 36 ++++ ...add_column_to_prescription_items_table.php | 32 ++++ frontend/dashboard/src/@types/doctor.tsx | 2 +- .../src/pages/EPrescription/Livechat/View.tsx | 156 +++++++++++------- 15 files changed, 294 insertions(+), 62 deletions(-) create mode 100644 app/Models/Prescription.php create mode 100644 app/Models/PrescriptionItem.php create mode 100644 database/migrations/2024_04_30_132417_create_prescription_table.php create mode 100644 database/migrations/2024_04_30_132747_create_prescription_items_table.php create mode 100644 database/migrations/2024_04_30_150511_add_column_to_prescription_items_table.php diff --git a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php index 4d36f639..e8b52477 100644 --- a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php +++ b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php @@ -10,6 +10,9 @@ use App\Models\OLDLMS\User; use App\Models\OLDLMS\UserDetail; use App\Models\OLDLMS\Prescription; use App\Models\OLDLMS\PrescriptionItem; + +use App\Models\Prescription as PrescriptionAso; +use App\Models\PrescriptionItem as PrescriptionItemAso; use App\Models\Icd; use App\Models\Organization; use App\Models\Drug; @@ -79,6 +82,17 @@ class PrescriptionController extends Controller */ public function store(Request $request) { + // Insert atau Update ke table prescription di ASO + $data = [ + 'livechat_id' => $request->id, + 'organization_id' => $request->hospital, + 'icd_code' => $request->diagnosis, + ]; + $prescriptionAso = PrescriptionAso::updateOrCreate([ + 'livechat_id' => $request->id + ], $data); + + // Insert ke table tx_prescription di Linksehat $livechat = Livechat::where('nID', $request->id)->first(); $livechatSummary = LivechatSummary::where('nIDLivechat', $request->id)->first(); @@ -117,7 +131,9 @@ class PrescriptionController extends Controller 'sKodeRS' => $hospital, ]; - $prescription = Prescription::create($data); + $prescription = Prescription::updateOrCreate([ + 'nIDLivechat' => $request->id + ],$data); $medicine = $request->medicine; $customMessages = [ @@ -134,7 +150,10 @@ class PrescriptionController extends Controller return Helper::responseJson([$request->all()],'error', 400, $validator->errors()); } else { // BeginTransaction + // delete item DB::beginTransaction(); + PrescriptionItemAso::where('prescription_id', $prescriptionAso->id)->delete(); + PrescriptionItem::where('nIDPrescription', $prescriptionAso->id)->delete(); foreach($medicine as $key => $value){ $drugData = Drug::where('id', $value['drug_id'])->first(); $drug = ''; @@ -154,8 +173,20 @@ class PrescriptionController extends Controller 'sSigna' => $value['signa'], 'sNote' => $value['note'], ]; + + $dataAso = [ + 'prescription_id' => $prescriptionAso->id, + 'drug_id' => $value['drug_id'], + 'qty' => $value['qty'], + 'unit_id' => $value['unit_id'], + 'signa' => $value['signa'], + 'note' => $value['note'] + ]; // Insert Data try { + // Insert to ASO + PrescriptionItemAso::create($dataAso); + // Insert to Linksehat PrescriptionItem::create($data); } catch (\Throwable $th) { DB::rollBack(); diff --git a/Modules/Internal/Transformers/LivechatResource.php b/Modules/Internal/Transformers/LivechatResource.php index 1dd3954e..3c2585d9 100644 --- a/Modules/Internal/Transformers/LivechatResource.php +++ b/Modules/Internal/Transformers/LivechatResource.php @@ -5,6 +5,8 @@ namespace Modules\Internal\Transformers; use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; use App\Helpers\Helper; +use App\Models\Prescription; +use App\Models\PrescriptionItem; class LivechatResource extends JsonResource { @@ -16,6 +18,12 @@ class LivechatResource extends JsonResource */ public function toArray($request) { + $prescription = Prescription::where('livechat_id', $this->nID)->first(); + $diagnosis = $prescription ? $prescription->icd_code : ''; + $hospital = $prescription ? $prescription->organization_id : ''; + + $prescriptionItem = $prescription ? PrescriptionItem::where('prescription_id', $prescription->id)->get() : []; + $livechat = [ 'id' => $this->nID, 'doctor_name' => isset($this->doctor->user->sFirstName) ? $this->doctor->user->detail->sTitlePrefix . '. ' . $this->doctor->user->sFirstName . ' ' . $this->doctor->user->sLastName . ' ' . $this->doctor->user->detail->sTitleSuffix : null, @@ -36,6 +44,9 @@ class LivechatResource extends JsonResource 'appointment_media' => $this->appointment->sMedia ?? null, 'status_chat' => $this->status_name ?? null, 'payment_method' => $this->appointment->payment_method ?? null, + 'diagnosis' => $diagnosis, + 'hospital' => $hospital, + 'medicine' => $prescriptionItem ]; $start_time = $this->dStartTime; diff --git a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php index e6b255e3..7671e376 100644 --- a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php +++ b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php @@ -15,6 +15,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Http; use Modules\Linksehat\Transformers\Livechat\LivechatResource; use Illuminate\Support\Facades\Validator; +use App\Http\Controllers\DuitkuController; use DB; use Str; @@ -191,6 +192,7 @@ class LivechatController extends Controller ]; return Helper::responseJson(data: $data); } + public function consultation_payment_choose($id){ $livechat = Livechat::where('id', $id)->with(['doctor', 'practitioner'])->first(); $practitionerRole = PractitionerRole::where('id',$livechat->practitioner->id)->first(); diff --git a/app/Models/OLDLMS/Livechat.php b/app/Models/OLDLMS/Livechat.php index 5c0ae646..19b35fa7 100644 --- a/app/Models/OLDLMS/Livechat.php +++ b/app/Models/OLDLMS/Livechat.php @@ -70,4 +70,8 @@ class Livechat extends Model { return $this->belongsTo(Healthcare::class, 'nIDHealthCare', 'nID'); } + + public function summary(){ + return $this->belongsTo(LivechatSummary::class, 'nID', 'nIDLivechat'); + } } diff --git a/app/Models/OLDLMS/Prescription.php b/app/Models/OLDLMS/Prescription.php index bdb36ef6..5652aa73 100644 --- a/app/Models/OLDLMS/Prescription.php +++ b/app/Models/OLDLMS/Prescription.php @@ -41,6 +41,8 @@ class Prescription extends Model protected $table = 'tx_prescriptions'; + protected $primaryKey = 'nID'; + // protected $appends = [ // 'status_name', // ]; diff --git a/app/Models/OLDLMS/PrescriptionItem.php b/app/Models/OLDLMS/PrescriptionItem.php index 7654f78e..4851e29c 100644 --- a/app/Models/OLDLMS/PrescriptionItem.php +++ b/app/Models/OLDLMS/PrescriptionItem.php @@ -48,4 +48,5 @@ class PrescriptionItem extends Model 'dTanggalResep' => 'datetime', ]; + protected $primaryKey = 'nID'; } diff --git a/app/Models/Prescription.php b/app/Models/Prescription.php new file mode 100644 index 00000000..27c48ffe --- /dev/null +++ b/app/Models/Prescription.php @@ -0,0 +1,18 @@ +dropColumn('str_number'); + $table->dropColumn('exp_date_str'); }); } }; diff --git a/database/migrations/2024_04_23_162213_add_column_to_table_practitioner_roles.php b/database/migrations/2024_04_23_162213_add_column_to_table_practitioner_roles.php index 3c4daa2f..d80eb1b4 100644 --- a/database/migrations/2024_04_23_162213_add_column_to_table_practitioner_roles.php +++ b/database/migrations/2024_04_23_162213_add_column_to_table_practitioner_roles.php @@ -27,7 +27,8 @@ return new class extends Migration public function down() { Schema::table('practitioner_roles', function (Blueprint $table) { - // + $table->dropColumn('sip_number'); + $table->dropColumn('exp_date_sip'); }); } }; diff --git a/database/migrations/2024_04_30_132417_create_prescription_table.php b/database/migrations/2024_04_30_132417_create_prescription_table.php new file mode 100644 index 00000000..69b52c24 --- /dev/null +++ b/database/migrations/2024_04_30_132417_create_prescription_table.php @@ -0,0 +1,34 @@ +id(); + $table->foreignId('livechat_id'); + $table->foreignId('organization_id'); + $table->string('icd_code'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('prescriptions'); + } +}; diff --git a/database/migrations/2024_04_30_132747_create_prescription_items_table.php b/database/migrations/2024_04_30_132747_create_prescription_items_table.php new file mode 100644 index 00000000..57585342 --- /dev/null +++ b/database/migrations/2024_04_30_132747_create_prescription_items_table.php @@ -0,0 +1,36 @@ +id(); + $table->foreignId('prescription_id'); + $table->foreignId('drug_id'); + $table->integer('qty'); + $table->foreignId('unit_id'); + $table->string('note'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('prescription_items'); + } +}; diff --git a/database/migrations/2024_04_30_150511_add_column_to_prescription_items_table.php b/database/migrations/2024_04_30_150511_add_column_to_prescription_items_table.php new file mode 100644 index 00000000..ec8ec019 --- /dev/null +++ b/database/migrations/2024_04_30_150511_add_column_to_prescription_items_table.php @@ -0,0 +1,32 @@ +string('signa'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('prescription_items', function (Blueprint $table) { + $table->dropColumn('signa'); + }); + } +}; diff --git a/frontend/dashboard/src/@types/doctor.tsx b/frontend/dashboard/src/@types/doctor.tsx index 034365ee..d672f44e 100644 --- a/frontend/dashboard/src/@types/doctor.tsx +++ b/frontend/dashboard/src/@types/doctor.tsx @@ -83,7 +83,7 @@ export type Appointment = { doctor_id:number; organizations:Organizations[]; specialities:Specialities[]; - diagonis:string; + diagnosis:string; hospital:string; medicine: Medicine[]; } diff --git a/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx b/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx index 8b4467fa..b3f3e4fa 100644 --- a/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx +++ b/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx @@ -124,15 +124,16 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { organizations: currentAppointment?.organizations || [], specialities: currentAppointment?.specialities || [], diagnosis: currentAppointment?.diagnosis || '', - hospital: currentAppointment?.hospital || '', - medicine : [{ - id: 0, - drug_id: 0, - qty: 0, - signa: '', - unit_id: 0, - note: '', // input to database - }], + hospital: currentAppointment?.hospital || null, + medicine : currentAppointment?.medicine || [ + { + drug_id: 0, + qty: 0, + signa: '', + unit_id: 0, + note: '', // input to database + } + ], }), // eslint-disable-next-line react-hooks/exhaustive-deps [currentAppointment] @@ -150,14 +151,17 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { const [icdOptions, setIcdOptions] = useState([ { value: '-', label: '-' } ]); + const codes = defaultValues.diagnosis.split(','); const [selectedIcdOptions, setSelectedIcdOptions] = useState([]); useEffect(() => { const selectedCodes = icdOptions.filter((icd) => { - // Logika pemilihan sesuai kebutuhan + return codes.includes(icd.value); }); setSelectedIcdOptions(selectedCodes); - }, [icdOptions]); + setValue('diagnosis', selectedCodes); + }, [icdOptions, defaultValues]); + useEffect(() => { // Ambil data dari API dan atur opsi ICD @@ -189,10 +193,14 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { // Set default value saat hospitalOptions berubah useEffect(() => { - if (hospitalOptions.length > 0 && !selectedHospitalOption) { - setSelectedHospitalOption({value: '-', label: '-'}); - } - }, [hospitalOptions, selectedHospitalOption]); + const selectedId = hospitalOptions.find((hospital) => { + + return hospital.value == defaultValues.hospital + }); + setSelectedHospitalOption(selectedId); + setValue('hospital', defaultValues.hospital) + + }, [hospitalOptions, defaultValues]); // Autocomplite drugs const [drugOptions, setDrugsOptions] = useState([]); @@ -240,6 +248,26 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { }); }, []); // useEffect dijalankan hanya sekali saat komponen dimount + useEffect(() => { + if (defaultValues.medicine.length > 0) { + defaultValues.medicine.map((med, index) => { + const selectedDrugId = drugOptions.find((drug) => { + return drug.value == med.drug_id + }); + handleAutocompleteChange(selectedDrugId,index) + + const selectedUnitId = unitOptions.find((unit) => { + return unit.value == med.unit_id + }); + handleAutocompleteChangeUnit(selectedUnitId,index) + + // Contoh: Lakukan tindakan lainnya sesuai kebutuhan Anda + }); + } else { + console.log('Medicine is empty'); + } + }, [defaultValues.medicine]); + const { reset, @@ -265,40 +293,40 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isEdit, currentAppointment]); - const onSubmit = async (data: FormValuesProps) => { - try { - const formData = new FormData(); - formData.append('id', data.id); - formData.append('diagnosis', data.diagnosis); - formData.append('hospital', data.hospital); - - // Iterasi melalui setiap objek dalam array medicine dan menambahkannya ke FormData - data.medicine.forEach((medicineObj, index) => { - // Anda dapat menambahkan setiap properti dari objek medicine ke FormData - formData.append(`medicine[${index}][drug_id]`, medicineObj.drug_id); - formData.append(`medicine[${index}][qty]`, medicineObj.qty); - formData.append(`medicine[${index}][unit_id]`, medicineObj.unit_id); - formData.append(`medicine[${index}][signa]`, medicineObj.signa); - formData.append(`medicine[${index}][note]`, medicineObj.note); - }); + const onSubmit = async (data: FormValuesProps) => { + try { + const formData = new FormData(); + formData.append('id', data.id); + formData.append('diagnosis', data.diagnosis); + formData.append('hospital', data.hospital); + + // Iterasi melalui setiap objek dalam array medicine dan menambahkannya ke FormData + data.medicine.forEach((medicineObj, index) => { + // Anda dapat menambahkan setiap properti dari objek medicine ke FormData + formData.append(`medicine[${index}][drug_id]`, medicineObj.drug_id); + formData.append(`medicine[${index}][qty]`, medicineObj.qty); + formData.append(`medicine[${index}][unit_id]`, medicineObj.unit_id); + formData.append(`medicine[${index}][signa]`, medicineObj.signa); + formData.append(`medicine[${index}][note]`, medicineObj.note); + }); - const response = await axios.post('/prescription', formData); - reset(); - enqueueSnackbar('Berhasil menambahkan resep', { - variant: 'success', - }); - navigate('/e-prescription/live-chat'); - } catch (error: any) { - console.log(error, 'submit') - enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' }); - } + const response = await axios.post('/prescription', formData); + reset(); + enqueueSnackbar('Berhasil menambahkan resep', { + variant: 'success', + }); + navigate('/e-prescription/live-chat'); + } catch (error: any) { + console.log(error, 'submit') + enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' }); + } - const ascent = document?.querySelector('ascent'); - if (ascent != null) { - ascent.innerHTML = ''; - } - }; + const ascent = document?.querySelector('ascent'); + if (ascent != null) { + ascent.innerHTML = ''; + } + }; return ( @@ -508,9 +536,6 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { Obat - @@ -582,14 +607,29 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { fullWidth /> - - {index !== fields.length - 1 && ( - - remove(index)}> - - - - )} + { + index === 0 ? ( + + append({medicine_name: '', medicine_price: 0, request_log_id: 1 })}> + + + + ) : ( + index == (fields.length-1) ? ( + + remove(index)}> + + + + ) : ( + + append({medicine_name: '', medicine_price: 0, request_log_id: 1 })}> + + + + ) + ) + } ))} @@ -601,7 +641,7 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { size="large" loading={isSubmitting} > - {!isEdit ? 'Save' : 'Save'} + {!isEdit ? 'Save' : 'Update'} From 04b0b2943d3002a65dfd7fe976e372979ba7f78a Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 2 May 2024 08:21:56 +0700 Subject: [PATCH 17/52] update --- Modules/Internal/Transformers/LivechatResource.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Modules/Internal/Transformers/LivechatResource.php b/Modules/Internal/Transformers/LivechatResource.php index 3c2585d9..28018765 100644 --- a/Modules/Internal/Transformers/LivechatResource.php +++ b/Modules/Internal/Transformers/LivechatResource.php @@ -22,7 +22,15 @@ class LivechatResource extends JsonResource $diagnosis = $prescription ? $prescription->icd_code : ''; $hospital = $prescription ? $prescription->organization_id : ''; - $prescriptionItem = $prescription ? PrescriptionItem::where('prescription_id', $prescription->id)->get() : []; + $prescriptionItem = $prescription ? PrescriptionItem::where('prescription_id', $prescription->id)->get() : [ + [ + 'drug_id' => 0, + 'qty' => 0, + 'signa' => '', + 'unit_id' => 0, + 'note' => '', // input to database + ] + ]; $livechat = [ 'id' => $this->nID, From 3ed024296392c7cbfba3cbf2cdc918df9abbd12d Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 2 May 2024 10:25:34 +0700 Subject: [PATCH 18/52] update --- .../Api/PrescriptionController.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php index e8b52477..52036b06 100644 --- a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php +++ b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php @@ -153,7 +153,7 @@ class PrescriptionController extends Controller // delete item DB::beginTransaction(); PrescriptionItemAso::where('prescription_id', $prescriptionAso->id)->delete(); - PrescriptionItem::where('nIDPrescription', $prescriptionAso->id)->delete(); + PrescriptionItem::where('nIDPrescription', $prescription->nID)->delete(); foreach($medicine as $key => $value){ $drugData = Drug::where('id', $value['drug_id'])->first(); $drug = ''; @@ -165,15 +165,8 @@ class PrescriptionController extends Controller if ($unitData) { $unit = $unitData->name; } - $data = [ - 'nIDPrescription' => $prescription->id, - 'sItemName' => $drug, - 'nQty' => $value['qty'], - 'sSatuan' => $unit, - 'sSigna' => $value['signa'], - 'sNote' => $value['note'], - ]; - + + // Insert Data $dataAso = [ 'prescription_id' => $prescriptionAso->id, 'drug_id' => $value['drug_id'], @@ -182,7 +175,14 @@ class PrescriptionController extends Controller 'signa' => $value['signa'], 'note' => $value['note'] ]; - // Insert Data + $data = [ + 'nIDPrescription' => $prescription->nID, + 'sItemName' => $drug, + 'nQty' => $value['qty'], + 'sSatuan' => $unit, + 'sSigna' => $value['signa'], + 'sNote' => $value['note'], + ]; try { // Insert to ASO PrescriptionItemAso::create($dataAso); From 9f557835081910c2ec7c2ae1d5b54959732445cb Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 2 May 2024 10:56:23 +0700 Subject: [PATCH 19/52] update --- .../Internal/Http/Controllers/Api/PrescriptionController.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php index 52036b06..0e6a84d8 100644 --- a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php +++ b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php @@ -6,6 +6,7 @@ use App\Helpers\Helper; use App\Models\OLDLMS\Livechat; use App\Models\OLDLMS\LivechatSummary; use App\Models\OLDLMS\Appointment; +use App\Models\OLDLMS\Dokter; use App\Models\OLDLMS\User; use App\Models\OLDLMS\UserDetail; use App\Models\OLDLMS\Prescription; @@ -96,6 +97,8 @@ class PrescriptionController extends Controller $livechat = Livechat::where('nID', $request->id)->first(); $livechatSummary = LivechatSummary::where('nIDLivechat', $request->id)->first(); + $dokterData = Dokter::where('nIDUser', $livechat->nIDDokter)->first(); + $nIDDokter = $dokterData ? $dokterData->nID : $livechat->nIDDokter; $userDokter = User::where('nID', $livechat->nIDDokter)->first(); $userDetailDokter = UserDetail::where('nIDUser', $userDokter->nID)->first(); @@ -120,7 +123,7 @@ class PrescriptionController extends Controller $data = [ 'nIDLivechat' => $request->id, 'nIDLivechatSummary' => $livechatSummary->nID, - 'nIDDokter' => $livechat->nIDDokter, + 'nIDDokter' => $nIDDokter, 'sDokterName' => $dokter, 'dTanggalResep' => date('Y-m-d H:i:s'), 'sSource' => 'lms', From 8cb30ddeddb95bbced5b45a8fcb3fd9cdbaeca4a Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 2 May 2024 11:33:13 +0700 Subject: [PATCH 20/52] update --- .../Internal/Http/Controllers/Api/PrescriptionController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php index 0e6a84d8..7b1ffb0f 100644 --- a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php +++ b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php @@ -160,8 +160,10 @@ class PrescriptionController extends Controller foreach($medicine as $key => $value){ $drugData = Drug::where('id', $value['drug_id'])->first(); $drug = ''; + $drugCode = ''; if ($drugData){ $drug = $drugData->name; + $drugCode = $drugData->code; } $unitData = Unit::where('id', $value['unit_id'])->first(); $unit = ''; @@ -181,6 +183,8 @@ class PrescriptionController extends Controller $data = [ 'nIDPrescription' => $prescription->nID, 'sItemName' => $drug, + 'sItemCode' => $drug, + 'sOriginCode' => $drugCode, 'nQty' => $value['qty'], 'sSatuan' => $unit, 'sSigna' => $value['signa'], From a7bce13ec31f680dcab0e32bb7d19df389314891 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 2 May 2024 15:08:49 +0700 Subject: [PATCH 21/52] filter dokter only yang bisa merespkan --- .../src/layouts/dashboard/navbar/NavConfig.tsx | 4 +++- .../layouts/dashboard/navbar/NavbarVertical.tsx | 14 +++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx index 724bbe82..df8ffd5d 100644 --- a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx +++ b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx @@ -19,6 +19,8 @@ export const accessGroup = { admin: ["/report/logs"] } + + const navConfig = [ // GENERAL // ---------------------------------------------------------------------- @@ -124,7 +126,7 @@ const navConfig = [ path: '/linking', }, { - title: 'E - PRESCRIPTION', + title: 'E-PRESCRIPTION', path: '/e-prescription/live-chat', }, ], diff --git a/frontend/dashboard/src/layouts/dashboard/navbar/NavbarVertical.tsx b/frontend/dashboard/src/layouts/dashboard/navbar/NavbarVertical.tsx index a356edf3..6f7bd107 100644 --- a/frontend/dashboard/src/layouts/dashboard/navbar/NavbarVertical.tsx +++ b/frontend/dashboard/src/layouts/dashboard/navbar/NavbarVertical.tsx @@ -19,6 +19,7 @@ import navConfig from './NavConfig'; import NavbarDocs from './NavbarDocs'; import NavbarAccount from './NavbarAccount'; import CollapseButton from './CollapseButton'; +import useAuth from '@/hooks/useAuth'; // ---------------------------------------------------------------------- @@ -42,6 +43,7 @@ export default function NavbarVertical({ isOpenSidebar, onCloseSidebar }: Props) const theme = useTheme(); const { pathname } = useLocation(); + const {user} = useAuth() const isDesktop = useResponsive('up', 'lg'); @@ -55,6 +57,16 @@ export default function NavbarVertical({ isOpenSidebar, onCloseSidebar }: Props) // eslint-disable-next-line react-hooks/exhaustive-deps }, [pathname]); + const filteredItems = navConfig.filter(section => { + return section.items.some(item => { + return item.title === 'E-PRESCRIPTION'; + }); + }); + + const filteredData = filteredItems.map(section => ({ + items: section.items.filter(item => item.title === "E-PRESCRIPTION") + })); + const renderContent = ( - + From 738bba88a5c9aa5051512dbf5a2f2f4049e90ec0 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Fri, 3 May 2024 14:22:39 +0700 Subject: [PATCH 22/52] livechat --- Modules/Linksehat/Transformers/Livechat/LivechatResource.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Linksehat/Transformers/Livechat/LivechatResource.php b/Modules/Linksehat/Transformers/Livechat/LivechatResource.php index d77f95fe..e1d974d1 100644 --- a/Modules/Linksehat/Transformers/Livechat/LivechatResource.php +++ b/Modules/Linksehat/Transformers/Livechat/LivechatResource.php @@ -76,9 +76,9 @@ class LivechatResource extends JsonResource return [ 'jadwal_weekday' => 'Senin - Jumat (08:00 - 17:30)', 'jadwal_weekend' => 'Sabtu (08:00 - 12:00)', - 'doctors_livechat' => [ + 'doctors_livechat' => $doctorsLivechat - ], + , 'specialist' => $specialists ]; } From 7516e475d632ad1a5eaffd0637f4c1b419a082bd Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Mon, 6 May 2024 08:15:58 +0700 Subject: [PATCH 23/52] update home dokter --- Modules/Linksehat/Transformers/Home/HomeResource.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Modules/Linksehat/Transformers/Home/HomeResource.php b/Modules/Linksehat/Transformers/Home/HomeResource.php index bfb93dfc..274ca362 100644 --- a/Modules/Linksehat/Transformers/Home/HomeResource.php +++ b/Modules/Linksehat/Transformers/Home/HomeResource.php @@ -273,9 +273,7 @@ class HomeResource extends JsonResource 'avatar' => $avatar, 'member_id' => $memberId, 'linking' => $linking, - 'doctors_livechat' => [ - $doctorsLivechat - ], + 'doctors_livechat' => $doctorsLivechat, 'hospital' => $hospitalList ]; } From 15df89e703108ed8f62d782f606abe10233a088d Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Mon, 6 May 2024 10:46:26 +0700 Subject: [PATCH 24/52] update eprescription --- .../Api/EprescriptionController.php | 216 ------------------ .../Api/PrescriptionController.php | 56 +++++ Modules/Internal/Routes/api.php | 1 + frontend/dashboard/src/@types/doctor.tsx | 2 +- .../src/pages/EPrescription/Livechat/Show.tsx | 1 + .../src/pages/EPrescription/Livechat/View.tsx | 34 ++- 6 files changed, 88 insertions(+), 222 deletions(-) delete mode 100644 Modules/Internal/Http/Controllers/Api/EprescriptionController.php diff --git a/Modules/Internal/Http/Controllers/Api/EprescriptionController.php b/Modules/Internal/Http/Controllers/Api/EprescriptionController.php deleted file mode 100644 index 8aecafe6..00000000 --- a/Modules/Internal/Http/Controllers/Api/EprescriptionController.php +++ /dev/null @@ -1,216 +0,0 @@ -startDate; - $endDate = $request->endDate; - - $livechat = Livechat::with('doctor.user', 'doctor.speciality', 'appointment.appointmentDetail', 'healthCare'); - // ->where('nIDAppointment', '!=', null) - // ->where('nIDAppointment', '!=', ''); - - - if ($startDate) { - $livechat = $livechat->where('dCreateOn', '>=', $startDate); - } - - if ($endDate) { - $endDate = date('Y-m-d', strtotime($endDate . ' +1 day')); - $livechat = $livechat->where('dCreateOn', '<', $endDate); - } - $livechat = $livechat->latest()->paginate(15); - - return response()->json(Helper::paginateResources(LivechatResource::collection($livechat))); - } - - /** - * Show the specified resource. - * @param int $id - * @return Renderable - */ - public function show($id) - { - $livechat = Livechat::with('doctor.user', 'doctor.speciality', 'appointment.appointmentDetail', 'healthCare') - ->where('nIDAppointment', '!=', null)->where('nIDAppointment', '!=', '') - ->where('nID', $id) - ->first(); - return response()->json(new LivechatResource($livechat)); - } - - public function export(Request $request) - { - $startDate = $request->has('startDate') ? $request->input('startDate') : ''; - $endDate = $request->has('endDate') ? $request->input('endDate') : ''; - - $liveChats = Livechat::with('user:nID,sFirstName,sLastName,sEmail,sPhone,nIDUser', 'doctor:nID,nIDSpesialis,nIDUser', 'doctor.user:nID,sFirstName,sLastName', 'appointment:nID,sPaymentStatus,sPaymentMethod', 'appointment.appointmentDetail:nID,nIDAppointment,dTanggalAppointment,tTimeAppointment', 'healthCare:nID,sHealthCare') - ->where(function (Builder $query) use ($startDate, $endDate) { - // $query->where('nIDAppointment', '!=', null); - // $query->where('nIDAppointment', '!=', ''); - if ($startDate) { - $query->where('dCreateOn', '>=', $startDate); - } - if ($endDate) { - $endDate = date('Y-m-d', strtotime($endDate . ' +1 day')); - $query->where('dCreateOn', '<', $endDate); - } - }) - ->orderBy('nID', 'desc') - ->get(['nID', 'nIDUser', 'nIDDokter', 'nIDHealthCare', 'nIDAppointment', 'sStatus', 'sMediaDokter', 'sMedia', 'dCreateOn']); - - $headers = [ - ['value' => 'No', 'cell' => 'A1', 'mergeCell' => true, 'mergeToCell' => 'A2'], - ['value' => 'Kode TC', 'cell' => 'B1', 'mergeCell' => true, 'mergeToCell' => 'B2'], - ['value' => 'Tanggal', 'cell' => 'C1', 'mergeCell' => true, 'mergeToCell' => 'C2'], - ['value' => 'Waktu', 'cell' => 'D1', 'mergeCell' => true, 'mergeToCell' => 'D2'], - ['value' => 'Faskes', 'cell' => 'E1', 'mergeCell' => true, 'mergeToCell' => 'E2'], - ['value' => 'Nama Dokter', 'cell' => 'F1', 'mergeCell' => true, 'mergeToCell' => 'F2'], - ['value' => 'Spesialis', 'cell' => 'G1', 'mergeCell' => true, 'mergeToCell' => 'G2'], - ['value' => 'Chat Via App/Website', 'cell' => 'H1', 'mergeCell' => true, 'mergeToCell' => 'I1'], - ['value' => 'Pasien', 'cell' => 'H2', 'mergeCell' => false, 'mergeToCell' => ''], - ['value' => 'Dokter', 'cell' => 'I2', 'mergeCell' => false, 'mergeToCell' => ''], - ['value' => 'nIDUser', 'cell' => 'J1', 'mergeCell' => true, 'mergeToCell' => 'J2'], - ['value' => 'Nama Pasien', 'cell' => 'K1', 'mergeCell' => true, 'mergeToCell' => 'K2'], - ['value' => 'No Telepon Pasien', 'cell' => 'L1', 'mergeCell' => true, 'mergeToCell' => 'L2'], - ['value' => 'Email Pasien', 'cell' => 'M1', 'mergeCell' => true, 'mergeToCell' => 'M2'], - ['value' => 'Status', 'cell' => 'N1', 'mergeCell' => true, 'mergeToCell' => 'N2'], - ['value' => 'Record Type', 'cell' => 'O1', 'mergeCell' => true, 'mergeToCell' => 'O2'], - ['value' => 'nID Principal', 'cell' => 'P1', 'mergeCell' => true, 'mergeToCell' => 'P2'], - ['value' => 'Metode Pembayaran', 'cell' => 'Q1', 'mergeCell' => true, 'mergeToCell' => 'Q2'], - ]; - - $spreadsheet = new Spreadsheet(); - $sheet = $spreadsheet->getActiveSheet(); - - foreach ($headers as $header) { - $sheet->setCellValue($header['cell'], $header['value']); - - if ($header['mergeCell'] === true) { - $sheet->mergeCells($header['cell'] . ':' . $header['mergeToCell']); - } - - $sheet->getStyle($header['cell'])->getFont()->setBold(true); - $sheet->getStyle($header['cell'])->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER)->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER); - } - - $startFrom = 3; - foreach ($liveChats as $indexLiveChat => $liveChat) { - $phone = $liveChat->user->sPhone ?? '-'; - $status = $liveChat->sStatus; - $nIDUser = $liveChat->user->nIDUser ?? 0; // Principal or Dependent - $paymentMethod = $liveChat->appointment ? Helper::sPaymentMethod($liveChat->appointment->sPaymentMethod) : 'N/A'; - $fullNameDoctor = '-'; - if ($liveChat->doctor->user !== null) { - $fullNameDoctor = ''; - - if ($liveChat->doctor->user->detail !== null) { - if ($liveChat->doctor->user->detail->sTitlePrefix !== null) { - $fullNameDoctor .= $liveChat->doctor->user->detail->sTitlePrefix . '. '; - } - - if ($liveChat->doctor->user->full_name !== null) { - $fullNameDoctor .= $liveChat->doctor->user->full_name . ' '; - } - - if ($liveChat->doctor->user->detail->sTitleSuffix !== null) { - $fullNameDoctor .= $liveChat->doctor->user->detail->sTitleSuffix; - } - } - } - - $recordType = 'P'; - if ($nIDUser){ - $recordType = 'D'; - } - switch ($status) { - case 0: - $statusLivechat = "Request TC"; - break; - case 1: - $statusLivechat = "Accepted by Doctor but User not Payment"; - break; - case 2: - $statusLivechat = "Payment Success"; - break; - case 3: - $statusLivechat = "Decline by Doctor"; - break; - case 4: - $statusLivechat = "Payment Expired"; - break; - default: - $statusLivechat = "Cancel by Patient"; - } - - $sheet->setCellValue('A' . $startFrom, $indexLiveChat + 1); - $sheet->setCellValue('B' . $startFrom, $liveChat->nID ?? '-'); - $sheet->setCellValue('C' . $startFrom, Carbon::parse($liveChat->dCreateOn)->format('d-m-Y')); - $sheet->setCellValue('D' . $startFrom, Carbon::parse($liveChat->dCreateOn)->format('H:i:s')); - // $sheet->setCellValue('D' . $startFrom, Carbon::parse($liveChat->dRequestTime)->format('H:i:s')); - $sheet->setCellValue('E' . $startFrom, $liveChat->healthCare->sHealthCare ?? '-'); - $sheet->setCellValue('F' . $startFrom, $fullNameDoctor); - $sheet->setCellValue('G' . $startFrom, $liveChat->doctor->speciality->sSpesialis ?? '-'); - $sheet->setCellValue('H' . $startFrom, $liveChat->sMedia ?? '-'); - $sheet->setCellValue('I' . $startFrom, $liveChat->sMediaDokter ?? '-'); - $sheet->setCellValue('J' . $startFrom, $liveChat->user->nID ?? '-'); - $sheet->setCellValue('K' . $startFrom, $liveChat->user->full_name ?? '-'); - $sheet->setCellValue('L' . $startFrom, preg_replace('/(\d{3})(\d{4})(\d{3})/', '$1$2$3', $phone)); - $sheet->setCellValue('M' . $startFrom, $liveChat->user->sEmail ?? '-'); - $sheet->setCellValue('N' . $startFrom, $statusLivechat); - $sheet->setCellValue('O' . $startFrom, $recordType); - $sheet->setCellValue('P' . $startFrom, $nIDUser ?? '-'); - $sheet->setCellValue('Q' . $startFrom, $paymentMethod ?? '-'); - $startFrom++; - } - - foreach (['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K', 'L', 'M', 'O', 'P'] as $header) { - if ($header === 'A') { - $spreadsheet->getActiveSheet()->getColumnDimension($header)->setWidth(35, 'px'); - } elseif ($header === 'H' || $header === 'I') { - $spreadsheet->getActiveSheet()->getColumnDimension($header)->setWidth(100, 'px'); - } else { - $spreadsheet->getActiveSheet()->getColumnDimension($header)->setAutoSize(true); - } - } - - $spreadsheet->getActiveSheet()->getStyle('A3:A' . $startFrom)->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER)->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER); - - $sheet->getDefaultRowDimension()->setRowHeight(-1); - $sheet->setTitle('Live Chat Report'); - - $writer = new Xlsx($spreadsheet); - ob_start(); - $writer->save('php://output'); - $content = ob_get_contents(); - ob_end_clean(); - - $fileName = 'result-' . now()->getPreciseTimestamp(3) . '-livechat-report.xlsx'; - Storage::disk('public')->put('temp/' . $fileName, $content); - - $fileUrl = url('storage/temp/' . $fileName); - - return Helper::responseJson([ - "file_url" => $fileUrl - ]); - } -} diff --git a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php index 7b1ffb0f..75cad7a6 100644 --- a/Modules/Internal/Http/Controllers/Api/PrescriptionController.php +++ b/Modules/Internal/Http/Controllers/Api/PrescriptionController.php @@ -29,6 +29,8 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Validator; +use Dompdf\Dompdf; +use Dompdf\Options; use DB; class PrescriptionController extends Controller @@ -247,4 +249,58 @@ class PrescriptionController extends Controller { // } + + public function downloadPrescription($id){ + $pdf = new Dompdf(); + + $options = new Options(); + $options->set('isHtml5ParserEnabled', true); + $options->set('isPhpEnabled', true); + $options->set(['isRemoteEnabled' => true]); + $pdf->setOptions($options); + + $pdf->setPaper('A4', 'portrait'); + + + $livechat = Livechat::with('doctor.user', 'doctor.speciality', 'appointment.appointmentDetail', 'healthCare') + ->where('nIDAppointment', '!=', null)->where('nIDAppointment', '!=', '') + ->where('nID', $id) + ->first(); + + $prescription = Prescription::where('nIDLivechat', $id)->first(); + $valid_date = date('d-m-Y', strtotime($prescription->dTanggalResep . ' +3 days')); + $prescriptionItem = PrescriptionItem::where('nIDPrescription', $prescription->nID)->get(); + + $user = User::where('nID', $livechat->nIDUser)->first(); + $doctor = Dokter::where('nIDUser', $livechat->nIDDokter)->first(); + + $patient = [ + 'name' => $user->sFirstName. ' '. $user->sMiddleName. ' '. $user->sLastName, + 'tgl_lahir' => date('d-m-Y', strtotime($user->dTanggalLahir)), + 'kelamin' => $user->nIDJenisKelamin == 1 ? 'M' : 'F', + 'umur' => Helper::calculateAge($user->dTanggalLahir) + ]; + + // Memuat view pdf_view.php ke dalam variabel + $data = [ + 'doctor' => $doctor, + 'items' => $prescriptionItem, + 'tanggal_resep' => date('d-m-Y', strtotime($prescription->dTanggalResep)), + 'pasien' => $patient, + 'valid_date' => $valid_date, + ]; + // Halaman 1 + $html1 = view('pdf.prescription', $data); + $htmlCombined = $html1 ; + + $pdf->loadHtml($htmlCombined); + $pdf->render(); + + $headers = [ + 'Content-Type' => 'application/pdf', + 'Content-Disposition' => 'inline; filename="file.pdf"', + ]; + + return response($pdf->output(), 200, $headers); + } } diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 93abbc6d..56fc9861 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -310,6 +310,7 @@ Route::prefix('internal')->group(function () { Route::get('prescription', [PrescriptionController::class, 'index']); Route::post('prescription', [PrescriptionController::class, 'store']); + Route::get('prescription-download/{id}', [PrescriptionController::class, 'downloadPrescription']); Route::get('prescription/{id}', [PrescriptionController::class, 'index']); diff --git a/frontend/dashboard/src/@types/doctor.tsx b/frontend/dashboard/src/@types/doctor.tsx index d672f44e..4a1dd106 100644 --- a/frontend/dashboard/src/@types/doctor.tsx +++ b/frontend/dashboard/src/@types/doctor.tsx @@ -71,7 +71,7 @@ export type Medicine = { } export type Appointment = { - id:number; + id:number|undefined; name:string; address:string; birth_date:string; diff --git a/frontend/dashboard/src/pages/EPrescription/Livechat/Show.tsx b/frontend/dashboard/src/pages/EPrescription/Livechat/Show.tsx index 3d85a52b..25cb9c15 100644 --- a/frontend/dashboard/src/pages/EPrescription/Livechat/Show.tsx +++ b/frontend/dashboard/src/pages/EPrescription/Livechat/Show.tsx @@ -44,6 +44,7 @@ export default function Create() {
diff --git a/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx b/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx index b3f3e4fa..14533ed0 100644 --- a/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx +++ b/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx @@ -84,6 +84,7 @@ interface FormValuesProps extends Partial { type Props = { isEdit: boolean; + id: number; currentAppointment?: Appointment; }; @@ -97,7 +98,7 @@ const Text = styled(Typography)(({ theme }) => ({ paddingBottom: theme.spacing(3), })); -export default function AppointmentForm({ isEdit, currentAppointment }: Props) { +export default function AppointmentForm({ isEdit, id, currentAppointment }: Props) { const navigate = useNavigate(); // const [ errors, setErrors ] = useState<{ [key: string]: string }>({}); @@ -139,7 +140,8 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { [currentAppointment] ); - + console.log() + const methods = useForm({ // resolver: yupResolver(NewCorporateSchema), defaultValues, @@ -159,7 +161,7 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { }); setSelectedIcdOptions(selectedCodes); setValue('diagnosis', selectedCodes); - }, [icdOptions, defaultValues]); + }, [defaultValues.diagnosis]); @@ -327,6 +329,20 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { ascent.innerHTML = ''; } }; + + const handleDownloadEPrescription = (id: number) => { + axios + .get(`prescription-download/${id}`, { + responseType: 'blob', + }) + .then((response) => { + window.open(URL.createObjectURL(response.data)); + }) + .catch((response) => { + enqueueSnackbar(response.message, { variant: 'error' }); + }); +} + return ( @@ -510,7 +526,7 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { /> - + {/* Rumah Sakit - + */} @@ -634,6 +650,14 @@ export default function AppointmentForm({ isEdit, currentAppointment }: Props) { ))} + Date: Mon, 6 May 2024 10:46:49 +0700 Subject: [PATCH 25/52] update --- app/Helpers/Helper.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index acccbfc1..d9fca859 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -474,4 +474,20 @@ class Helper return $distance; } + public static function calculateAge($date_brith_day){ + // Konversi tanggal lahir ke dalam format UNIX timestamp + $dob = strtotime($date_brith_day); + + // Hitung umur berdasarkan tanggal lahir + $umur = date('Y') - date('Y', $dob); + + // Periksa apakah ulang tahun sudah lewat atau belum + if (date('md', $dob) > date('md')) { + $umur--; + } + + // Mengembalikan umur dalam format "x years old" + return $umur . ' years old'; + } + } From 5dd0f24af92f918282fd86d9609d61ee8364a07a Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Mon, 6 May 2024 10:58:05 +0700 Subject: [PATCH 26/52] update --- resources/views/pdf/prescription.blade.php | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 resources/views/pdf/prescription.blade.php diff --git a/resources/views/pdf/prescription.blade.php b/resources/views/pdf/prescription.blade.php new file mode 100644 index 00000000..40c40fff --- /dev/null +++ b/resources/views/pdf/prescription.blade.php @@ -0,0 +1,129 @@ + + + + E-Prescription + + + +
+ '; + ?> +
+

E-PRESCRIPTION

+
+

+

user->detail->sTitlePrefix . ' ' . $doctor->user->sFirstName . ' ' . $doctor->user->sLastName . ' ' . $doctor->user->detail->sTitleSuffix : $prescription->sDokterName; ?>

+

speciality->sSpesialis : '-'; ?>

+

Sip : jadwalDokter[0]->sSIP : '-'; ?>

+
+

Patient Details

+ + + + + + + + + + + + + + + +
(, )
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
R/sItemName;?>@nQty;?> Per sSatuan; ?>
sSigna ? '('.$item->sSigna.')' : ''; ?> per day
sNote; ?>
+ +
+

Valid Until

+
+

+

+

+
+

+

+

+ + + From 9d79118e47d3b551b3e525b3a6e8db88d9b84b6a Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Mon, 6 May 2024 11:16:04 +0700 Subject: [PATCH 27/52] update --- frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx b/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx index 14533ed0..c1dc31c7 100644 --- a/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx +++ b/frontend/dashboard/src/pages/EPrescription/Livechat/View.tsx @@ -341,7 +341,7 @@ export default function AppointmentForm({ isEdit, id, currentAppointment }: Prop .catch((response) => { enqueueSnackbar(response.message, { variant: 'error' }); }); -} + } return ( @@ -652,7 +652,7 @@ export default function AppointmentForm({ isEdit, id, currentAppointment }: Prop + + {data?.status == 1 ? + + : } +
+
*/} + {content} + + + + ); +}; + +export default DialogUpdateStatus; diff --git a/frontend/client-portal/src/contexts/ConfiguredCorporateContext.tsx b/frontend/client-portal/src/contexts/ConfiguredCorporateContext.tsx new file mode 100644 index 00000000..59270cf0 --- /dev/null +++ b/frontend/client-portal/src/contexts/ConfiguredCorporateContext.tsx @@ -0,0 +1,46 @@ +import { Corporate } from '@/@types/corporates'; +import axios from '@/utils/axios'; +import { ReactNode, createContext, useState, useEffect } from 'react'; +import { useParams } from 'react-router'; +// hooks +import useResponsive from '../hooks/useResponsive'; + +// ---------------------------------------------------------------------- + +export type ConfiguredCorporateContextProps = { + currentCorporate?: Corporate | null; +}; + +const initialState: ConfiguredCorporateContextProps = { + currentCorporate: null, +}; + +const ConfiguredCorporateContext = createContext(initialState); + +type ConfiguredCorporateProviderProps = { + children: ReactNode; +}; + +function ConfiguredCorporateProvider({ children }: ConfiguredCorporateProviderProps) { + const { corporate_id } = useParams(); + const [corporate, setCorporate] = useState(null); + + useEffect(() => { + axios.get(`corporates/${corporate_id}`) + .then((res) => { + setCorporate(res.data) + }) + }, []); + + return ( + + {children} + + ); +} + +export { ConfiguredCorporateProvider, ConfiguredCorporateContext }; diff --git a/frontend/client-portal/src/layouts/dashboard/navbar/NavConfig.tsx b/frontend/client-portal/src/layouts/dashboard/navbar/NavConfig.tsx index b910d105..4ea27c39 100644 --- a/frontend/client-portal/src/layouts/dashboard/navbar/NavConfig.tsx +++ b/frontend/client-portal/src/layouts/dashboard/navbar/NavConfig.tsx @@ -32,6 +32,10 @@ const navConfig = [ title: 'Alarm Center', path: '/alarm-center', }, + { + title: 'Formularium', + path: '/master/formularium-template-v2', + } // { // title: 'Claim Submit', // path: '/claim-submit', diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/CreateUpdate.tsx b/frontend/client-portal/src/pages/Master/FormulariumV2/CreateUpdate.tsx new file mode 100644 index 00000000..2e1aa7f7 --- /dev/null +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/CreateUpdate.tsx @@ -0,0 +1,69 @@ +import { useNavigate, useParams } from "react-router-dom"; +import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs"; +import Page from "../../../components/Page"; +import useSettings from "../../../hooks/useSettings"; +import {useContext, useEffect, useMemo, useState } from 'react'; +import axios from '../../../utils/axios'; +import { useSnackbar } from 'notistack'; +import { ConfiguredCorporateContext } from "@/contexts/ConfiguredCorporateContext"; +import { Corporate, CorporatePlan } from "@/@types/corporates"; +import { MasterFormularium } from "./Type"; +import CreateUpdateForm from "./CreateUpdateForm"; +import Typography from "@/theme/overrides/Typography"; + +export default function CreateUpdate() { + const navigate = useNavigate(); + const { corporate_id, id } = useParams(); + const [corporate, setCorporate] = useState(); + const configuredCorporateContext = useContext(ConfiguredCorporateContext) + + useEffect(() => { + setCorporate(configuredCorporateContext.currentCorporate); + }, [ConfiguredCorporateContext]) + + const [ currentCorporatePlan, setCurrentCorporatePlan ] = useState(); + const [ currentMasterForm, setCurrentMasterForm ] = useState(); + const isEdit = !!id; + + useEffect(() => { + if (isEdit) { + axios.get(`/master/formularium-template/${id}/edit`) + .then((res) => { + // setCurrentCorporatePlan(res.data); + setCurrentMasterForm(res.data); + }) + .catch((err) => { + if (err.response.status === 404) { + navigate('/404'); + } + }) + } + }, [corporate_id, id]) + + const pageType = !isEdit ? "Create" : "Edit" + const pageTitle = `Master Formularium ${pageType}` + return ( + + + + + + + ) +} \ No newline at end of file diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/CreateUpdateForm.tsx b/frontend/client-portal/src/pages/Master/FormulariumV2/CreateUpdateForm.tsx new file mode 100644 index 00000000..b93d3bfd --- /dev/null +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/CreateUpdateForm.tsx @@ -0,0 +1,143 @@ +import * as Yup from 'yup'; +import { LoadingButton } from "@mui/lab"; +import { Button, Card, Grid, Stack, Typography } from "@mui/material"; +import { CorporatePlan } from "../../../@types/corporates"; +import { FormProvider, RHFSwitch, RHFTextField } from "../../../components/hook-form"; +import { useEffect, useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { useSnackbar } from 'notistack'; +import { useNavigate, useParams } from 'react-router-dom'; +import axios from '../../../utils/axios'; +import { MasterFormularium } from "./Type"; + + +type Props = { + isEdit: boolean; + currentMasterForm?: MasterFormularium; +}; + +export default function CreateUpdateForm({ isEdit, currentMasterForm }: Props) { + const navigate = useNavigate(); + const { enqueueSnackbar } = useSnackbar(); + + const NewCorporatePlanSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + }); + + const defaultValues = useMemo( + () => ({ + name: currentMasterForm?.name || '', + description: currentMasterForm?.description || '', + }), + [currentMasterForm] + ) + + useEffect(() => { + if (isEdit && currentMasterForm) { + reset(defaultValues); + } + if (!isEdit) { + reset(defaultValues); + } + }, [isEdit, currentMasterForm]) + + const methods = useForm({ + resolver: yupResolver(NewCorporatePlanSchema), + defaultValues, + }) + + const { + reset, + setError, + handleSubmit, + formState: { isSubmitting } + } = methods; + + const onSubmit = async (data: any) => { + if (!isEdit) { + await axios + .post('/master/formularium-template/store', data) + .then((res) => { + enqueueSnackbar('Formularium created succesfully', {variant: 'success'}); + }) + .then((res) => { + navigate('/master/formularium-template-v2', { replace: true }); + }) + .catch(({ response }) => { + if (response.status === 422) { + for (const [key, value] of Object.entries(response.data.errors)) { + setError(key, { message: value[0] }); + enqueueSnackbar(value[0] ?? 'Failed Processing Reques', { variant: 'error' }); + } + } + else { + enqueueSnackbar('Create Failed : ' + response.data.message, {variant: 'error'}); + } + }); + } else { + await axios + .put(`/master/formularium-template/${currentMasterForm?.id}/update`, data) + .then((res) => { + enqueueSnackbar('Formularium updated successfully', { variant: 'success' }); + }) + .then((res) => { + navigate('/master/formularium-template-v2', { replace: true }); + }) + .catch(({ response }) => { + enqueueSnackbar(`Update Failed : ${response.data.message}`, { variant: 'error' }) + }) + } + } + + return ( + + + + + + Detail + Name* + + Description* + + + + + + + + { isEdit? 'Update' : 'Create' } + + + + + + + ) +} \ No newline at end of file diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/Detail/DetailFormularium.tsx b/frontend/client-portal/src/pages/Master/FormulariumV2/Detail/DetailFormularium.tsx new file mode 100644 index 00000000..049b3c3a --- /dev/null +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/Detail/DetailFormularium.tsx @@ -0,0 +1,102 @@ +// @mui +import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Grid, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Pagination } from '@mui/material'; +import { LoadingButton } from "@mui/lab"; +import { CachedOutlined, FindInPageOutlined } from '@mui/icons-material'; +// hooks +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../../hooks/useSettings'; +import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; +import { RHFSelect, FormProvider } from '@/components/hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { useForm } from 'react-hook-form'; +// components +import axios from '../../../../utils/axios'; +import Label from '@/components/Label'; +import TableMoreMenu from '@/components/table/TableMoreMenu'; +import DialogUpdateStatus from '@/components/DialogUpdateStatus' +import * as Yup from 'yup'; +import { FormulariumData } from "../Type"; + + +type Props = { + props: FormulariumData, + isActive: number, +} + +export default function DetailFormularium({props, isActive} : Props) { + const [open, setOpen] = React.useState(false); + + + return ( + + + + {props.code} + {props.atc_code} + {props.name} + {props.category_name} + {props.uom} + + {isActive == 1 ? ( + + ) : ( + + )} + + + + setOpen(!open)}> + + Detail + + + } /> + + + + + + + + + + Detail + + Description + : {props.description} + + General Indication + : {props.general_indication} + + Composition + : {props.composition} + + Kategori Obat + : {props.kategori_obat} + + BPOM Registration + : {props.bpom_registration} + + Classification + : {props.classifications} + + Cat For + : {props.cat_for} + + Class + : {props.class} + + Manufacturer + : {props.manufacturer} + + + + + + + + + + ) +} \ No newline at end of file diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/Detail/Formularium.tsx b/frontend/client-portal/src/pages/Master/FormulariumV2/Detail/Formularium.tsx new file mode 100644 index 00000000..13e09315 --- /dev/null +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/Detail/Formularium.tsx @@ -0,0 +1,316 @@ +// @mui +import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Grid, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Pagination } from '@mui/material'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import AddIcon from '@mui/icons-material/Add'; +import UploadIcon from '@mui/icons-material/Upload'; +import CancelIcon from '@mui/icons-material/Cancel'; +// hooks +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../../hooks/useSettings'; +import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'; +// components +import axios from '../../../../utils/axios'; +import { LaravelPaginatedData } from '../../../../@types/paginated-data'; +import { Icd } from '../../../../@types/diagnosis'; +import BasePagination from '../../../../components/BasePagination'; +import { enqueueSnackbar } from 'notistack'; +import DetailFormularium from "./DetailFormularium"; +import { FormulariumData } from '../Type'; + + +export default function List() { + const navigate = useNavigate(); + const { id: formularium_template_id } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const [importResult, setImportResult] = useState(null); + const { isActive } = useLocation().state as { isActive: number } + + function SearchInput(props: any) { + const searchInput = useRef(null); + const [searchText, setSearchText] = useState(""); + + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? '' + setSearchText(newSearchText); + } + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + props.onSearch(searchText); + } + + useEffect(() => { + setSearchText(searchParams.get('search') ?? ''); + }, [searchParams]) + + return ( +
+ + + ); + } + + function ImportForm( props: any ) { + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); + const importForm = useRef(null) + const [currentImportFileName, setCurrentImportFileName] = useState(null) + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const handleImportButton = () => { + if (importForm?.current) {; + handleClose(); + importForm.current ? importForm.current.click() : console.log('No File selected'); + } else { + alert('No file selected') + } + } + + const handleCancelImportButton = () => { + importForm.current.value = ''; + importForm.current?.dispatchEvent(new Event('change', { bubbles: true })) + } + + const handleGetTemplate = (type: string) => { + axios.get('corporates/import-document-example/' + type) + .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 handleImportChange = (event: any) => { + if (event.target.files[0]) { + setCurrentImportFileName(event.target.files[0].name) + } else { + setCurrentImportFileName(null); + } + } + + const handleFormulariumList = async (appliedFilter = null) => { + axios.get(`master/formulariums/${formularium_template_id}/list`) + .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(); + enqueueSnackbar('Download Success', { variant: 'success' }) + }) + .catch(response => { + enqueueSnackbar('Looks like something went wrong. Please check your data and try again. ' + response.message, { variant: 'error' }) + }); + } + + const handleUpload = () => { + if (importForm.current?.files?.length) { + const formData = new FormData(); + formData.append('file', importForm.current?.files[0]) + axios.post(`master/formulariums/${formularium_template_id}/import`, formData ) + .then(response => { + handleCancelImportButton(); + loadDataTableData(); + setImportResult(response.data); + }) + .catch(response => { + enqueueSnackbar('Looks like something went wrong. Please check your data and try again. ' + response.message, { variant: 'error' }) + }) + } else { + enqueueSnackbar(`No File Selected`, { variant: 'warning' }) + } + } + + return ( +
+ + {(!currentImportFileName && + + + + Import + handleGetTemplate('master-formularium')}>Download Template + handleFormulariumList()}>Download Formularium + + + )} + {( currentImportFileName && + + + + + + + )} + {( importResult && + + Last Import Result Report : {importResult.result_file?.name ?? "-"} + + )} +
+ ); + } + + // Default data + const [dataTableRow, setDataTableRow] = useState(null) + const [dataTableIsLoading, setDataTableLoading] = useState(true); + 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 loadDataTableData = async (appliedFilter : any | null = null) => { + setDataTableLoading(true); + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + const response = await axios.get(`/master/formulariums/${formularium_template_id}`, { params: filter }); + setDataTableLoading(false); + + console.log(formularium_template_id) + console.log(response) + setDataTableData(response.data) + setDataTableRow(response.data.data) + } + + const applyFilter = async (searchFilter: string) => { + await loadDataTableData({ 'search': searchFilter }); + setSearchParams({ 'search' : searchFilter }); + } + + const handlePageChange = (event : ChangeEvent, value: number) => { + const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); + loadDataTableData(filter); + setSearchParams(filter); + } + + useEffect(() => { + loadDataTableData(); + }, []) + + const headStyle = { + fontWeight: 'bold', + }; + + return ( + + + + + + + + + Code + ATC Code + Name + Category Name + UOM + Status + + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + + + + ) : ( + dataTableRow?.map(item => ( + + )) + )} +
+
+ + +
+
+ ) +} \ No newline at end of file diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/Detail/Index.tsx b/frontend/client-portal/src/pages/Master/FormulariumV2/Detail/Index.tsx new file mode 100644 index 00000000..381283ef --- /dev/null +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/Detail/Index.tsx @@ -0,0 +1,37 @@ +import { Card, Grid } 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 "./Formularium"; + + +export default function Formularium() { + const pageTitle = "Formularium" + const { id: formularium_template_id } = useParams(); + + return ( + + + + + + + ) +} \ No newline at end of file diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/FormulariumRow.tsx b/frontend/client-portal/src/pages/Master/FormulariumV2/FormulariumRow.tsx new file mode 100644 index 00000000..47c90524 --- /dev/null +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/FormulariumRow.tsx @@ -0,0 +1,211 @@ +import * as Yup from 'yup'; +import TableMoreMenu from "@/components/table/TableMoreMenu" +import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; +import CachedOutlinedIcon from '@mui/icons-material/CachedOutlined'; +import HistoryIcon from '@mui/icons-material/History'; +import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined'; +import { Button, Card, Collapse, Grid, MenuItem, Paper, Stack, Table, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material" +import React, { Fragment, useEffect, useState } from "react"; +import axios from "@/utils/axios"; +import { useNavigate, useParams } from "react-router-dom"; +import { RHFSelect, FormProvider } from '@/components/hook-form'; +import DialogUpdateStatus from '@/components/DialogUpdateStatus' +import { MasterFormularium } from "./Type"; +import { LoadingButton } from "@mui/lab"; +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; + +type Props = { + props: MasterFormularium +} + +export default function FormulariumRow({props} : Props) { + const navigate = useNavigate(); + + const [isDialogOpen, setDialogOpen] = useState(false); + const [dataValue, setDataValue] = useState(null); + const [dataDescription, setDescriptionValue] = useState(''); + const [url, setUrl] = useState(''); + let titles = { + name: 'Update Status', + icon: '-' + } + type FormValuesProps = { + value: string; + active: boolean; + }; + + const handleActivate = (isOpen: boolean, dataValue: MasterFormularium) => { + setDialogOpen(isOpen); + setDataValue(dataValue); + setDescriptionValue("Are you sure to inactive this formularium ? "); + setUrl(url); + }; + + const NewMasterFormSchema = Yup.object().shape({ + reason: Yup.string().required('Reason edit is required') + }); + + const methods = useForm({ + resolver: yupResolver(NewMasterFormSchema) + }); + + const onSubmit = async (row: any) => { + try { + handleUpdate(dataValue?.active, dataValue?.id, row.reason) + } catch (error: any) { + console.log('data gagal'); + } + + const ascent = document?.querySelector('ascent'); + if (ascent != null) { + ascent.innerHTML = ''; + } + }; + + const handleUpdate = (active: number, id: number, reason: string) => { + + if (active == 1) { + active = 0 + } else { + active = 1 + } + + axios + .put(`/master/formularium-template/${id}/activation`, { + active: active, + }) + .then((res) => { + window.location.reload(); + }) + } + + const { + reset, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const getContent = () => ( + <> + + Are you sure to Change this Formularium + + + + + Formularium name + + + {dataValue?.name} + + + + + Reason for update + + + + + + + + + + + + {dataValue?.active == 1 ? + + Inactive + : + + Active + + } + + + + + + + ) + + return ( + + + + {props.name} + {props.description} + + + navigate(`/master/formularium-template-v2/${props.id}/detail`, {state: { isActive: props.active }})}> + + Detail + + navigate(`/master/formularium-template-v2/${props.id}/edit`)}> + + Edit + + handleActivate(true, { + id: props.id, + name: props.name, + description: props.description, + active: props.active + }) + }> + + Update Status + + navigate(`/master/formularium-template-v2/${props.id}/history`)}> + + History + + + } /> + + + + + ) +} diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/History.tsx b/frontend/client-portal/src/pages/Master/FormulariumV2/History.tsx new file mode 100644 index 00000000..ca991be6 --- /dev/null +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/History.tsx @@ -0,0 +1,211 @@ +// @mui +import { + Box, + Button, + Card, + Collapse, + Container, + FormControl, + Grid, + IconButton, + InputLabel, + MenuItem, + OutlinedInput, + Paper, + Select, + SelectChangeEvent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Typography, + Badge, + Stack, + } from '@mui/material'; + import * as React from 'react'; +import { useParams } from 'react-router-dom'; +import { styled } from '@mui/material/styles'; +import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp'; +import MuiAccordion, { AccordionProps } from '@mui/material/Accordion'; +import { useContext, useEffect, useState } from 'react'; +import MuiAccordionSummary, { + AccordionSummaryProps, +} from '@mui/material/AccordionSummary'; +import useSettings from '../../../hooks/useSettings'; +import axios from '../../../utils/axios'; +import { ConfiguredCorporateContext } from '@/contexts/ConfiguredCorporateContext'; +import MuiAccordionDetails from '@mui/material/AccordionDetails'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import { Corporate } from '@/@types/corporates'; +import { fDate, fDateTime } from '@/utils/formatTime'; + + +const Accordion = styled((props: AccordionProps) => ( + +))(({ theme }) => ({ + border: `1px solid ${theme.palette.divider}`, + "&:not(:last-child)": { + borderBottom: 0, + }, + "&:before": { + display: 'none' + }, +})); + +const AccordionSummary = styled((props: AccordionSummaryProps) => ( + } + {...props} + /> +))(({ theme }) => ({ + backgroundColor: theme.palette.mode === 'dark' + ? 'rgba(255,255,255,0.5)' + : 'rgba(0,0,0,.03)', + flexDirection: 'row-reverse', + "& .MuiAccordionSummary-expandIconWrapper.Mui-expanded": { + transform: 'rotate(90dg)', + }, + "& .MuiAccordionSummary=content": { + marginLeft: theme.spacing(1), + }, +})); + +const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({ + padding: theme.spacing(2), + borderTop: '1px solid rgba(0,0,0,.125)', +})); + +export default function MasterFormulariumHistory() { + const [expanded, setExpanded] = React.useState('panel1'); + + const handleChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => { + setExpanded(newExpanded ? panel : false); + }; + const pageTitle = 'Master Formularium History' + const { id } = useParams(); + const { corporate_id } = useParams(); + const [corporate, setCorporate] = useState(); + const [currentCorporate, setCurrentCorporate] = useState(); + const configuredCorporateContext = useContext(ConfiguredCorporateContext); + + useEffect(() => { + setCorporate(configuredCorporateContext.currentCorporate); + const model = 'App\\Models\\FormulariumTemplate'; + const url = `/audittrail/${id}?model=${model}`; + axios.get(url) + .then((res) => { + setCurrentCorporate(res.data); + }) + .catch((error) => { + console.error('Terjadi kesalahan: ', error); + }); + }, [configuredCorporateContext]) + + return ( +
+ + {currentCorporate?.data.map((item, index) => ( + + + {`Data has ${item.action} by ${item.user_id} on ${fDateTime(item.updated_at)}`} + + + + + + + Field + Old Value + New Values + + + + {Object.entries(item.old_values).map(([key, value]) => { + let renderedValue; + if (key === 'deleted_by' || + key === 'deleted_at' || + key === 'created_by' || + key === 'created_at' || + key === 'updated_by' || + key === 'description' + ) { + return null; // Melewati iterasi saat key adalah 'deleted_by' + } + switch (key) { + case 'welcome_message': + renderedValue = item.new_values[key].replace(/<[^>]*>/g, ''); + value = value.replace(/<[^>]*>/g, ''); + break; + case 'help_text': + renderedValue = item.new_values[key].replace(/<[^>]*>/g, ''); + value = value.replace(/<[^>]*>/g, ''); + break; + case 'active': + renderedValue = item.new_values[key] == 1 ? 'Active' : 'Inactive'; + value = value == 1 ? 'Active' : 'Inactive'; + break; + case 'created_at': + renderedValue = fDateTime(item.new_values[key]); + value = fDateTime(value); + break; + case 'updated_at': + renderedValue = fDateTime(item.new_values[key]); + value = fDateTime(value); + break; + case 'delete_at': + renderedValue = fDateTime(item.new_values[key]); + value = fDateTime(value); + break; + default: + renderedValue = item.new_values[key]; + break; + } + + const field = key.charAt(0).toUpperCase() + key.slice(1); + if (value == renderedValue) { + return null + } else { + return ( + + {`${field}`} + {`${value}`} + {renderedValue} + + ); + } + })} + +
+
+
+
+ ))} +
+ ) +} \ No newline at end of file diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/Index.tsx b/frontend/client-portal/src/pages/Master/FormulariumV2/Index.tsx new file mode 100644 index 00000000..2a25563b --- /dev/null +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/Index.tsx @@ -0,0 +1,33 @@ +import { Card, Grid } 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 MasterFormularium() { + const pageTitle = "Master Formularium" + + return ( + + + + + + + + + ) +} diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/List.tsx b/frontend/client-portal/src/pages/Master/FormulariumV2/List.tsx new file mode 100644 index 00000000..25dae159 --- /dev/null +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/List.tsx @@ -0,0 +1,179 @@ +// @mui +import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Grid, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Pagination } from '@mui/material'; +import AddIcon from '@mui/icons-material/Add'; +// hooks +import React, { ChangeEvent, Component, useEffect, useContext, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom'; +// components +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import BasePagination from '../../../components/BasePagination'; +import FormulariumRow from "./FormulariumRow"; +import { MasterFormularium } from "./Type"; +import { UserCurrentCorporateContext } from '../../../contexts/UserCurrentCorporate'; + +export default function List() { + const navigate = useNavigate(); + const [searchParams, setSearchParams] = useSearchParams(); + + // Default data + const [dataTableRow, setDataTableRow] = useState(null) + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const { corporateValue } = useContext(UserCurrentCorporateContext); + 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 loadDataTableData =async (appliedFilter: any | null = null) => { + setDataTableLoading(true); + + const filter = { + ...Object.fromEntries([...searchParams.entries()]), // Mengambil parameter dari searchParams + corporate_id: corporateValue, // Menambahkan corporate_id + ...(appliedFilter ? appliedFilter : {}) // Menggabungkan dengan appliedFilter jika ada + }; + const response = await axios.get('/master/formularium-template', { params: filter }); + console.log(response.data); + console.log(response.data.data) + setDataTableLoading(false); + + setDataTableData(response.data); + setDataTableRow(response.data.data); + } + + const applyFilter = async (searchFilter: string) => { + await loadDataTableData({'search' : searchFilter }); + setSearchParams({ 'search' : searchFilter }) + } + + const handlePageChange = (event : ChangeEvent, value: number) => { + const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); + loadDataTableData(filter); + setSearchParams(filter); + } + + useEffect(() => { + loadDataTableData(); + }, []) + + function SearchInput(props: any) { + const searchInput = useRef(null); + const [searchText, setSearchText] = useState(""); + + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? "" + setSearchText(newSearchText) + } + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + props.onSearch(searchText); + } + + useEffect(() => { + setSearchText(searchParams.get('search') ?? ''); + }, [searchParams]) + + return ( +
+ + + ) + } + + function SearchCreate(props: any) { + return ( +
+ + + {/* */} + +
+ ) + } + + const headStyle = { + fontWeight: 'bold' + } + + return ( + + + + + + + + + + Name + Description + + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableRow?.map(item => ( + + ))} + + )} +
+
+ +
+
+
+ ) +} diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/Type.ts b/frontend/client-portal/src/pages/Master/FormulariumV2/Type.ts new file mode 100644 index 00000000..3e16e32e --- /dev/null +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/Type.ts @@ -0,0 +1,32 @@ +export type MasterFormularium = { + id: number + name: string + description: string + active: number +} + + +export type FormulariumData = { + id: number + code: string + name: string + description: string + manufacturer: string + category_name: string + kategori_obat: string + uom: string + general_indication: string + composition: string + atc_code: string + class: string + bpom_registration: string + classifications: string + cat_for: string + created_at: string + updated_at: string + deleted_at: any + created_by: number + updated_by: number + deleted_by: any + formularium_template_id: number + } \ No newline at end of file diff --git a/frontend/client-portal/src/routes/index.tsx b/frontend/client-portal/src/routes/index.tsx index a43eba44..4bf282dd 100644 --- a/frontend/client-portal/src/routes/index.tsx +++ b/frontend/client-portal/src/routes/index.tsx @@ -234,6 +234,86 @@ export default function Router() { { path: '*', element: }, ], }, + { + path: 'master/formularium-template-v2', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, + { + path: 'master/formularium-template-v2/:id/detail', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, + { + path: 'master/formularium-template-v2/create', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, + { + path: 'master/formularium-template-v2/:id/edit', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, + { + path: 'master/formularium-template-v2/:id/history', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, { path: '*', element: }, ]); } @@ -275,3 +355,9 @@ const ClaimRequest = Loadable(lazy(() => import('../pages/ClaimSubmit/DialogDeta const Corporate = Loadable(lazy(() => import('../pages/Corporate/Index'))); const CorporateEdit = Loadable(lazy(() => import('../pages/Corporate/Form'))); const CorporateShow = Loadable(lazy(() => import('../pages/Corporate/Show'))); + +// Formularium +const MasterFormulariumTemplateV2 = Loadable(lazy(() => import('../pages/Master/FormulariumV2/Index'))); +const MasterFormulariumTemplateCreateV2 = Loadable(lazy(() => import('../pages/Master/FormulariumV2/CreateUpdate'))); +const MasterFormulariumTemplateHistoriesV2 = Loadable(lazy(() => import('../pages/Master/FormulariumV2/History'))); +const MasterFormulariumTemplateDetailV2 = Loadable(lazy(() => import('../pages/Master/FormulariumV2/Detail/Index'))); From 4cc3df9859c0e93eda36d524dcdbc6203f754b00 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Wed, 15 May 2024 14:58:25 +0700 Subject: [PATCH 46/52] Update --- .../Http/Controllers/Api/FormulariumTemplateController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php b/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php index 18520429..2bc5bd56 100644 --- a/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php +++ b/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php @@ -33,13 +33,14 @@ class FormulariumTemplateController extends Controller }) ->join('corporate_formulariums', 'formularium_templates.id', '=', 'corporate_formulariums.formularium_template_id') ->where('corporate_formulariums.corporate_id', '=', $request->corporate_id) + ->orderBy('formularium_templates.id', 'ASC') ->paginate(15); } else { $diagnosisTemplate = FormulariumTemplate::query() // ->filter($request->toArray()) ->join('corporate_formulariums', 'formularium_templates.id', '=', 'corporate_formulariums.formularium_template_id') ->where('corporate_formulariums.corporate_id', '=', $request->corporate_id) - ->orderBy('name', 'ASC') + ->orderBy('formularium_templates.id', 'ASC') ->paginate(15); return $diagnosisTemplate; } From fa160aa2de945a8fce85ba36eeb6af412e8eba5e Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Wed, 15 May 2024 15:13:10 +0700 Subject: [PATCH 47/52] Update --- .../Http/Controllers/Api/FormulariumTemplateController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php b/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php index 2bc5bd56..1f3e8236 100644 --- a/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php +++ b/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php @@ -33,6 +33,7 @@ class FormulariumTemplateController extends Controller }) ->join('corporate_formulariums', 'formularium_templates.id', '=', 'corporate_formulariums.formularium_template_id') ->where('corporate_formulariums.corporate_id', '=', $request->corporate_id) + ->select('formularium_templates.*', 'corporate_formulariums.corporate_id') ->orderBy('formularium_templates.id', 'ASC') ->paginate(15); } else { @@ -40,6 +41,7 @@ class FormulariumTemplateController extends Controller // ->filter($request->toArray()) ->join('corporate_formulariums', 'formularium_templates.id', '=', 'corporate_formulariums.formularium_template_id') ->where('corporate_formulariums.corporate_id', '=', $request->corporate_id) + ->select('formularium_templates.*', 'corporate_formulariums.corporate_id') ->orderBy('formularium_templates.id', 'ASC') ->paginate(15); return $diagnosisTemplate; From 0ac3e4d4518e8a504e825d14dee6aa62717cfa78 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 16 May 2024 17:12:29 +0700 Subject: [PATCH 48/52] api chat, send summary, health sertificate, callback duitku --- .../Http/Controllers/Api/ChatController.php | 142 +++++++++++-- .../Api/Doctor/ChatDoctorController.php | 188 +++++++++++++++--- .../Http/Controllers/Api/DuitkuController.php | 54 ++++- .../Controllers/Api/LivechatController.php | 92 ++++++--- Modules/Linksehat/Routes/api.php | 17 +- app/Helpers/Helper.php | 9 + app/Models/Livechat.php | 7 + app/Models/PrescriptionItem.php | 3 +- ...5_111627_add_column_to_livechats_table.php | 44 ++++ ...add_column_to_prescription_items_table.php | 32 +++ 10 files changed, 504 insertions(+), 84 deletions(-) create mode 100644 database/migrations/2024_05_15_111627_add_column_to_livechats_table.php create mode 100644 database/migrations/2024_05_16_133343_add_column_to_prescription_items_table.php diff --git a/Modules/Linksehat/Http/Controllers/Api/ChatController.php b/Modules/Linksehat/Http/Controllers/Api/ChatController.php index 9f2ee112..48d86997 100644 --- a/Modules/Linksehat/Http/Controllers/Api/ChatController.php +++ b/Modules/Linksehat/Http/Controllers/Api/ChatController.php @@ -2,17 +2,22 @@ namespace Modules\Linksehat\Http\Controllers\Api; +use App\Helpers\Helper; use App\Models\Channel; use App\Events\ChatMessageSent; use App\Models\UserChannel; use App\Models\Message; use App\Models\File; +use App\Models\Livechat; use App\Models\Person; use App\Models\OLDLMS\User; use App\Models\OLDLMS\UserDetail; use Illuminate\Http\Request; use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\DB; +use Dompdf\Dompdf; +use Dompdf\Options; use Pusher\Pusher; class ChatController extends Controller @@ -36,7 +41,6 @@ class ChatController extends Controller 'type' => $request->type, 'member_id' => $request->member_id, 'doctor_id' => $request->doctor_id, - // Jika ada kolom tambahan, tambahkan di sini ]); // Menggunakan updateOrCreate untuk menambahkan data UserChannel untuk member_id @@ -166,34 +170,144 @@ class ChatController extends Controller public function getMessage(Request $request) { // Buat instance Pusher dengan konfigurasi yang sesuai - $pusher = new Pusher( - env('PUSHER_APP_KEY'), - env('PUSHER_APP_SECRET'), - env('PUSHER_APP_ID'), - [ - 'cluster' => env('PUSHER_APP_CLUSTER'), - 'useTLS' => true, - ] - ); $channel = Channel::where('id',$request->channel_id)->first(); + $livechat = Livechat::where([ + 'doctor_id' => $channel->doctor_id, + 'patient_id' => $channel->member_id, + ])->latest('created_at')->first(); + if($channel->member_id == $request->user_id){ // Get nama dokter $person = Person::where('id', $channel->doctor_id)->first(); $name = $person->name; + $avatar = ''; + $age = ''; + $gender = ''; + $question = ''; + + $consultationStart = $livechat->start_date; + $consultationEnd = $livechat->end_date; + $work = ''; + $address = ''; } else { // Get nama pasien - $person = User::where('nID', $channel->member_id)->first(); - $name = $person->sFirstName . ' ' . $person->sLastName; + $user = User::where('nID', $channel->member_id)->with('detail')->first(); + $name = $user->sFirstName . ' ' . $user->sLastName; + $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatar = $user->detail->sImage ?? $urlAvatarDefault; + $gender = DB::connection('oldlms')->table('tm_jenis_kelamin')->where('nID', $user->detail->nIDJenisKelamin)->first('sJenisKelamin'); + if ($gender){ + $gender = $gender->sJenisKelamin; + } + $age = Helper::calculateAge($user->detail->dTanggalLahir); + $question = $livechat->descriptions; + $consultationStart = $livechat->start_date; + $consultationEnd = $livechat->end_date; + $work = DB::connection('oldlms')->table('tm_pekerjaan')->where('nID', $user->detail->nIDPekerjaan)->first('sPekerjaan'); + + if($work){ + $work = $work->sPekerjaan; + } + $address = DB::connection('oldlms')->table('tm_users_address')->where('nIDUser', $user->nID)->first('sAlamat'); + if($address){ + $address = $address->sAlamat; + } + + } + + // Ini Untul Chat + $data = Message::where('channel_id', $request->channel_id)->get()->toArray(); + + // Data Consultation Summary + $consultationSummary = [ + 'subject' => $livechat->subject, + 'object' => $livechat->object, + 'assessment' => $livechat->assessment, + 'plan' => $livechat->plan, + ]; + + $healthSertificate = false; + if ($livechat->health_certificate_start && $livechat->health_certificate_end){ + $healthSertificate = True; } - $data = Message::where('channel_id', $request->channel_id)->get()->toArray(); // Berikan respons yang sesuai ke klien return response()->json([ 'message' => 'Message sent successfully', 'data' => [ 'header' => $name, - 'chat' => $data + 'avatar' => $avatar, + 'gender' => $gender, + 'age' => $age, + 'question' => $question, + 'start' => $consultationStart, + 'end' => $consultationEnd, + 'work' => $work, + 'address' => $address, + 'chat' => $data, + 'summary' => $consultationSummary, + 'livechat_id' => $livechat->id, + 'health_sertificate' => $healthSertificate ] ]); } + + public function downloadHealtcare($id){ + $pdf = new Dompdf(); + + $options = new Options(); + $options->set('isHtml5ParserEnabled', true); + $options->set('isPhpEnabled', true); + $options->set(['isRemoteEnabled' => true]); + $pdf->setOptions($options); + + $pdf->setPaper('A4', 'portrait'); + + $livechat = Livechat::where([ + 'id' => $id + ])->latest('created_at')->first(); + + $user = User::where('nID', $livechat->patient_id)->with('detail')->first(); + $name = $user->sFirstName . ' ' . $user->sLastName; + $age = Helper::calculateAge($user->detail->dTanggalLahir); + + $person = Person::where('id', $livechat->doctor_id)->first(); + $doctorName = $person->name; + + $work = DB::connection('oldlms')->table('tm_pekerjaan')->where('nID', $user->detail->nIDPekerjaan)->first('sPekerjaan'); + if($work){ + $work = $work->sPekerjaan; + } + $address = DB::connection('oldlms')->table('tm_users_address')->where('nIDUser', $user->nID)->first('sAlamat'); + if($address){ + $address = $address->sAlamat; + } + // Memuat view pdf_view.php ke dalam variabel + $calculateDate = Helper::calculateDateDifference($livechat->health_certificate_start, $livechat->health_certificate_end); + + $data = [ + 'name' => $name, + 'age' => $age, + 'work' => $work, + 'address' => $address, + 'doctor_name' => $doctorName, + 'date' => $livechat->created_at, + 'start_date' => $livechat->health_certificate_start, + 'end_date' => $livechat->health_certificate_end, + 'calculate_date' => $calculateDate + ]; + // Halaman 1 + $html1 = view('pdf.health_sertificate', $data); + $htmlCombined = $html1 ; + + $pdf->loadHtml($htmlCombined); + $pdf->render(); + + $headers = [ + 'Content-Type' => 'application/pdf', + 'Content-Disposition' => 'inline; filename="file.pdf"', + ]; + + return response($pdf->output(), 200, $headers); + } } diff --git a/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php b/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php index 9a18c016..4e39f144 100644 --- a/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php +++ b/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php @@ -4,7 +4,12 @@ namespace Modules\Linksehat\Http\Controllers\Api\Doctor; use App\Http\Controllers\Controller; use App\Models\User; +use App\Models\OLDLMS\User as UserLMS; use App\Models\Livechat; +use App\Models\Channel; +use App\Models\Message; +use App\Models\Prescription; +use App\Models\PrescriptionItem; use Crypt; use Error; use Illuminate\Http\Request; @@ -32,42 +37,165 @@ class ChatDoctorController extends Controller $chat = Livechat::where([ 'doctor_id'=> $user->person_id, 'accept_date'=> null, - ])->get()->toArray(); - dd($chat); + 'status' => 1 + ])->get(); - return ApiResponse::apiResponse("Success", $res_data, trans('Message.success'), 200); + $dataIncomingChat = []; + if($chat) { + foreach($chat as $c){ + $patient = UserLMS::where('nID',$c->patient_id)->with('detail')->first(); + $urlAvatarDefault = $patient->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $patient->detail->sImage ?? $urlAvatarDefault; + $arr['id'] = $c->id; + $arr['patient_id'] = $patient->nID; + $arr['avatar'] = $avatarMember; + $arr['name'] = $patient->sFirstName .' '.$patient->sLastName; ; + array_push($dataIncomingChat, $arr); + } + } + + $dataChannel = Channel::where('doctor_id',$user->person_id)->get()->toArray(); + $dataOnGoing = []; + if ($dataChannel){ + foreach($dataChannel as $d){ + $user = UserLMS::with('detail')->where('nID', $d['member_id'])->first(); + $lastMessage = Message::where('channel_id', $d['id']) + ->latest('created_at') + ->first(); + $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $user->detail->sImage ?? $urlAvatarDefault; + + $arr['id'] = $d['id']; + $arr['avatar'] = $avatarMember; + $arr['name'] = $user->sFirstName .' '.$user->sLastName; + $arr['last_message'] = $lastMessage; + + array_push($dataOnGoing, $arr); + } + } + $channel = $data; + + $data = [ + 'incoming_chat' => $dataIncomingChat, + 'ongoing_chat' => $dataOnGoing + ]; + + return ApiResponse::apiResponse("Success", $data, trans('Message.success'), 200); } - public function fWorkExperience($start, $end) + + public function getChatDetail($id){ + $livechat = Livechat::find($id); + $user = UserLMS::with('detail')->where('nID', $livechat->patient_id)->first(); + $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $user->detail->sImage ?? $urlAvatarDefault; + $gender = DB::connection('oldlms')->table('tm_jenis_kelamin')->where('nID', $user->detail->nIDJenisKelamin)->first('sJenisKelamin'); + $maritalStaus = DB::connection('oldlms')->table('tm_status_pernikahan')->where('nID', $user->detail->sMartialStatus)->first('sStatusPernikahan'); + $data = []; + if ($livechat->status != 2){ + $data = [ + 'id' => $user->nID, + 'name' => $user->sFirstName . ' ' . $user->sLastName, + 'avatar' => $avatarMember, + 'gender' => $gender->sJenisKelamin, + 'marital_status' => $maritalStaus->sStatusPernikahan, + 'age' => Helper::calculateAge($user->detail->dTanggalLahir), + 'weight' => $user->detail->sWeight, + 'height' => $user->detail->sHeight, + 'question' => $livechat->descriptions, + 'diseases' => [], + 'medications' => [], + 'allergy' => [], + 'family_history' => [] + ]; + } else if ($livechat->status == 2){ // sudah accept, tinggal tunggu bayar pasient + $data = [ + 'message' => 'waiting payment' + ]; + } + return ApiResponse::apiResponse("Success", $data, trans('Message.success'), 200); + } + + public function declineChat(Request $request) { - $startDateString = $start; // Tanggal dan waktu awal - $endDateString = $end ; // Tanggal dan waktu akhir - - // Mengubah string tanggal ke timestamp UNIX - $startTime = strtotime($startDateString); - $endTime = strtotime($endDateString); - - // Menghitung selisih waktu dalam detik - $timeDifference = $endTime - $startTime; - - // Menghitung jumlah tahun, bulan, dan hari dari selisih waktu - $years = floor($timeDifference / (365 * 24 * 60 * 60)); - $months = floor(($timeDifference - ($years * 365 * 24 * 60 * 60)) / (30 * 24 * 60 * 60)); - $days = floor(($timeDifference - ($years * 365 * 24 * 60 * 60) - ($months * 30 * 24 * 60 * 60)) / (24 * 60 * 60)); - - // Formatkan hasilnya - $experience = ''; - if ($years > 0) { - $experience .= $years . ' years '; - } - if ($months > 0) { - $experience .= $months . ' months '; - } - if ($days > 0) { - $experience .= $days . ' days'; + $livechat = Livechat::find($request->id); + if ($livechat) { + // Memperbarui atribut model + $livechat->status = 3; // Decline + // Menyimpan perubahan ke database + $livechat->save(); + return ApiResponse::apiResponse("Success",['message' => 'Livechat updated successfully'], trans('Message.success'), 200); + } else { + return response()->json(['message' => 'Livechat not found'], 404); } + } - return $experience; + public function approveChat(Request $request) + { + $livechat = Livechat::find($request->id); + if ($livechat) { + // Memperbarui atribut model + $livechat->status = 2; // Accept + $livechat->accept_date = date('Y-m-d H:i:s'); // Accept + // Menyimpan perubahan ke database + $livechat->save(); + return ApiResponse::apiResponse("Success",['message' => 'Livechat updated successfully'], trans('Message.success'), 200); + } else { + return response()->json(['message' => 'Livechat not found'], 404); + } + } + public function endChat(Request $request) + { + $livechat = Livechat::find($request->id); + if ($livechat) { + // Memperbarui atribut model + $livechat->status = 6; // End Chat + $livechat->end_date = date('Y-m-d H:i:s'); // Accept + // Menyimpan perubahan ke database + $livechat->save(); + return ApiResponse::apiResponse("Success",['message' => 'Livechat updated successfully'], trans('Message.success'), 200); + } else { + return response()->json(['message' => 'Livechat not found'], 404); + } + } + + public function summaryChat(Request $request) + { + + $livechat = Livechat::find($request->id); + if ($livechat) { + // Memperbarui atribut model + $livechat->subject = $request->subject; // Subject + $livechat->object = $request->object; // Object + $livechat->assessment = $request->assessment; // Assessment + $livechat->plan = $request->plan; // Plan + + $livechat->health_certificate_start = $request->health_certificate_start; // start + $livechat->health_certificate_end = $request->health_certificate_end; // end + // Menyimpan perubahan ke database + $livechat->save(); + + $prescriptions = Prescription::create([ + 'livechat_id' => $livechat->id, + 'organization_id' => $livechat->organization_id, + ]); + + if ($request->prescriptions) { + foreach ($request->prescriptions as $prescription) { + $prescriptionItem = PrescriptionItem::create([ + 'prescription_id' => $prescriptions->id, + 'drug_id' => $prescription['medicine'], + 'signa' => $prescription['dosis'], + 'direction' => $prescription['direction'], + 'note' => $prescription['note'], + ]); + } + } + + return ApiResponse::apiResponse("Success",['message' => 'Livechat updated successfully'], trans('Message.success'), 200); + } else { + return response()->json(['message' => 'Livechat not found'], 404); + } } } diff --git a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php index 33285b04..e22ff031 100644 --- a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php +++ b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php @@ -5,6 +5,9 @@ namespace Modules\Linksehat\Http\Controllers\Api; use App\Helpers\Helper; use App\Models\Organization; use App\Models\Speciality; +use App\Models\Livechat; +use App\Models\Channel; +use App\Models\UserChannel; use Illuminate\Contracts\Support\Renderable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; @@ -271,6 +274,8 @@ class DuitkuController extends Controller header('Content-Type: application/json'); $notif = json_decode($callback); + // $notif = $request; ini untuk di local + DB::table('api_logs') ->insert([ 'type' => 'in', @@ -280,12 +285,57 @@ class DuitkuController extends Controller 'created_at' => date('Y-m-d H:i:s') ]); - if ($notif->resultCode == "00") { // Action Success - return Redirect::to('https://linksehat.com/'); + $livechat = Livechat::where('uuid', $notif->merchantOrderId)->first(); + // Update status pembayaran + $livechat->payment_method = $notif->paymentCode; + $livechat->status = 5; // success payment + $livechat->save(); + + // Update start chat + $livechat->start_date = date('Y-m-d H:i:s'); + // Buat dan simpan data channel ke dalam tabel + $channel = Channel::updateOrCreate([ + 'name' => $livechat->patient_id .'_' . $request->doctor_id, + ], + [ + 'name' => $livechat->patient_id .'_' . $livechat->doctor_id, + 'type' => 'Private', + 'member_id' => $livechat->patient_id, + 'doctor_id' => $livechat->doctor_id, + ]); + + // Menggunakan updateOrCreate untuk menambahkan data UserChannel untuk member_id + $userChannelMember = UserChannel::updateOrCreate( + [ + 'user_id' => $livechat->patient_id, + 'channel_id' => $channel->id + ], + [ + 'user_id' => $livechat->patient_id, + 'channel_id' => $channel->id + ] + ); + + // Menggunakan updateOrCreate untuk menambahkan data UserChannel untuk doctor_id + $userChannelDoctor = UserChannel::updateOrCreate( + [ + 'user_id' => $livechat->doctor_id, + 'channel_id' => $channel->id + ], + [ + 'user_id' => $livechat->doctor_id, + 'channel_id' => $channel->id + ] + ); + + // Berikan respons yang sesuai ke klien + return response()->json(['message' => 'Channel created successfully', 'channel' => $channel]); + } else if ($notif->resultCode == "01") { // Action Failed + return response()->json(['message' => 'User Gagal melakukan pembayaran']); } } catch (Exception $e) { diff --git a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php index 7671e376..23ca6fca 100644 --- a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php +++ b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php @@ -143,10 +143,17 @@ class LivechatController extends Controller ); } else { // insert table livechat + + /** + * Status Livechat + * 1=Request, 2=Accept, 3=Decline, 4=Waiting Payment, 5=Success Payment, 6 = End Chat + */ + $timezone = date_default_timezone_get(); $data['request_date'] = date('Y-m-d H:i:s'); $data['timezone'] = $timezone; $data['uuid'] = (string) Str::orderedUuid(); + $data['status'] = 1; $livechat = Livechat::create($data); $doctor = $livechat->doctor; $data = [ @@ -159,6 +166,7 @@ class LivechatController extends Controller return Helper::responseJson(data: $data); } } + public function consultation_request_show($id){ $livechat = Livechat::where('id', $id)->with(['doctor', 'practitioner'])->first(); $practitionerRole = PractitionerRole::where('id',$livechat->practitioner->id)->first(); @@ -229,44 +237,62 @@ class LivechatController extends Controller return Helper::responseJson(data: $data); } - public function consultation_payment(Request $request){ + public function consultation_payment(Request $request) + { + try { + // Mengambil data Livechat dengan relasi doctor dan practitioner + $livechat = Livechat::with(['doctor', 'practitioner'])->find($request->consultation_id); - $livechat = Livechat::where('id', $request->consultation_id)->with(['doctor', 'practitioner'])->first(); - - $practitionerRole = PractitionerRole::where('id',$livechat->practitioner->id)->first(); - $price = $practitionerRole->price ? $practitionerRole->price : 30000; - $adminFee = 5000; - $discount = 0; - $totalPay = $price + $adminFee - $discount; + if (!$livechat) { + return response()->json(['success' => false, 'message' => 'Consultation not found'], 404); + } - // From database linksehat - $user = User::with('detail') - ->where('nId', $livechat->patient_id) - ->first(); - - $data['paymentMethod'] = $request->payment_code; - $data['paymentAmount'] = $totalPay; - $data['email'] = $user->sEmail; - $data['phoneNumber'] = $user->sPhone; - $data['productDetails'] = $user->sEmail; - $data['merchantOrderId'] = $livechat->uuid; - $data['additionalParam'] = ''; - $data['merchantUserInfo'] = ''; - $data['customerVaName'] = $user->sFirstName . ' ' . $user->sLastName; - $data['callbackUrl'] = 'htpps://google.com'; - $data['returnUrl'] = 'htpps://linksehat.com'; - $data['expiryPeriod'] = 60; - $data['firstName'] = $user->sFirstName; - $data['lastName'] = $user->sLastName; + // Update status + $livechat->status = 4; + $livechat->save(); - // dd($user); - $data['alamat'] = ''; - $data['city'] = ''; - $data['postalCode'] = ''; + $practitionerRole = PractitionerRole::find($livechat->practitioner->id); + $price = $practitionerRole->price ?? 30000; // Gunakan null coalescing operator + $adminFee = 5000; + $discount = 0; + $totalPay = $price + $adminFee - $discount; - $duitku = DuitkuHelper::createInvoice($data); + // Mengambil user dari database + $user = User::with('detail')->where('nId', $livechat->patient_id)->first(); - return $duitku; + if (!$user) { + return response()->json(['success' => false, 'message' => 'User not found'], 404); + } + + // Menyiapkan data untuk invoice + $data = [ + 'paymentMethod' => $request->payment_code, + 'paymentAmount' => $totalPay, + 'email' => $user->sEmail, + 'phoneNumber' => $user->sPhone, + 'productDetails' => 'Telekonsul Livechat LMS', + 'merchantOrderId' => $livechat->uuid, + 'additionalParam' => '', + 'merchantUserInfo' => '', + 'customerVaName' => $user->sFirstName . ' ' . $user->sLastName, + 'callbackUrl' => 'https://google.com', + 'returnUrl' => 'https://linksehat.com', + 'expiryPeriod' => 60, + 'firstName' => $user->sFirstName, + 'lastName' => $user->sLastName, + 'alamat' => '', + 'city' => '', + 'postalCode' => '' + ]; + + // Membuat invoice menggunakan DuitkuHelper + $duitku = DuitkuHelper::createInvoice($data); + + return response()->json(['success' => true, 'data' => $duitku], 200); + } catch (Exception $e) { + // Menangkap error dan mengembalikan respon error + return response()->json(['success' => false, 'message' => $e->getMessage()], 500); + } } public function consultation_check_payment($id){ diff --git a/Modules/Linksehat/Routes/api.php b/Modules/Linksehat/Routes/api.php index 702f246b..e0fa3969 100644 --- a/Modules/Linksehat/Routes/api.php +++ b/Modules/Linksehat/Routes/api.php @@ -107,11 +107,15 @@ Route::prefix('linksehat')->group(function () { Route::post('livechat/consultation-payment', 'consultation_payment'); }); + + Route::controller(ChatController::class)->group(function () { + Route::post('livechat/send-message', 'sendMessage'); + Route::get('livechat/get-message', 'getMessage'); + Route::post('livechat/channel','createChannel'); + Route::get('livechat/channel','listChannel'); + Route::get('livechat/{id}/health-sertificate','downloadHealtcare'); + }); - Route::post('livechat/send-message', [ChatController::class, 'sendMessage']); - Route::get('livechat/get-message', [ChatController::class, 'getMessage']); - Route::post('livechat/channel',[ChatController::class, 'createChannel']); - Route::get('livechat/channel',[ChatController::class, 'listChannel']); Route::post('create-invoice-duitku', [DuitkuController::class, 'createInvoice']); Route::post('check-status-duitku', [DuitkuController::class, 'checkStatus']); @@ -142,6 +146,11 @@ Route::prefix('linksehat')->group(function () { }); Route::controller(ChatDoctorController::class)->group(function () { Route::get('chat', 'getChat'); + Route::post('decline', 'declineChat'); + Route::post('approve', 'approveChat'); + Route::post('end', 'endChat'); + Route::post('summary', 'summaryChat'); + Route::get('chat/{id}', 'getChatDetail'); }); }); }); diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index d9fca859..11f8d42f 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -490,4 +490,13 @@ class Helper return $umur . ' years old'; } + public static function calculateDateDifference($startDate, $endDate) + { + $start = Carbon::parse($startDate); + $end = Carbon::parse($endDate); + + return $start->diffInDays($end) + 1; + } + + } diff --git a/app/Models/Livechat.php b/app/Models/Livechat.php index 151f0086..82536b15 100644 --- a/app/Models/Livechat.php +++ b/app/Models/Livechat.php @@ -23,6 +23,13 @@ class Livechat extends Model 'accept_date', 'start_date', 'end_date', + 'status', + 'subject', + 'object', + 'assessment', + 'plan', + 'health_certificate_start', + 'health_certificate_end', ]; public function doctor() { diff --git a/app/Models/PrescriptionItem.php b/app/Models/PrescriptionItem.php index 6e4eb2f4..3c6e9145 100644 --- a/app/Models/PrescriptionItem.php +++ b/app/Models/PrescriptionItem.php @@ -14,6 +14,7 @@ class PrescriptionItem extends Model 'qty', 'unit_id', 'note', - 'signa' + 'signa', + 'direction' ]; } diff --git a/database/migrations/2024_05_15_111627_add_column_to_livechats_table.php b/database/migrations/2024_05_15_111627_add_column_to_livechats_table.php new file mode 100644 index 00000000..da35e42a --- /dev/null +++ b/database/migrations/2024_05_15_111627_add_column_to_livechats_table.php @@ -0,0 +1,44 @@ +integer('status')->after('end_date')->comment('1=Request, 2=Accept, 3=Decline, 4=Waiting Payment, 5=Success Payment, 6 = End Chat'); + $table->string('subject')->after('status')->nullable(); + $table->string('object')->after('subject')->nullable(); + $table->string('assessment')->after('object')->nullable(); + $table->text('plan')->after('assessment')->nullable(); + $table->date('health_certificate_start')->after('end_date')->nullable(); + $table->date('health_certificate_end')->after('health_certificate_start')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('livechats', function (Blueprint $table) { + $table->dropColumn('status'); + $table->dropColumn('subject'); + $table->dropColumn('object'); + $table->dropColumn('assessment'); + $table->dropColumn('plan'); + $table->dropColumn('health_certificate_start'); + $table->dropColumn('health_certificate_end'); + }); + } +}; diff --git a/database/migrations/2024_05_16_133343_add_column_to_prescription_items_table.php b/database/migrations/2024_05_16_133343_add_column_to_prescription_items_table.php new file mode 100644 index 00000000..1c6a55b5 --- /dev/null +++ b/database/migrations/2024_05_16_133343_add_column_to_prescription_items_table.php @@ -0,0 +1,32 @@ +string('direction')->after('signa')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('prescription_items', function (Blueprint $table) { + $table->dropColumn('direction'); + }); + } +}; From f838b370452b5077c9f2afa5704f796e3f05cf31 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Thu, 16 May 2024 17:12:51 +0700 Subject: [PATCH 49/52] update --- .../views/pdf/health_sertificate.blade.php | 297 ++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 resources/views/pdf/health_sertificate.blade.php diff --git a/resources/views/pdf/health_sertificate.blade.php b/resources/views/pdf/health_sertificate.blade.php new file mode 100644 index 00000000..c45474a9 --- /dev/null +++ b/resources/views/pdf/health_sertificate.blade.php @@ -0,0 +1,297 @@ + + + + + + + @php + $dynamicVariableName = 'font_size_' . now()->timestamp; + $$dynamicVariableName = 10; + @endphp + + + + +
+
+
+
+ The Future Of Healthcare At Your Fingertips +
+ + +
+
+ SURAT KETERANGAN SAKIT +
+
+
+
+
+ Menerangkan Bahwa : +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
Nama:{{ $name }}
Umur:{{ $age }}
Pekerjaan:{{ $work }}
Alamat:{{ $address }}
+
+
+
+ Setelah diperiksa kesehatannya ternyata pada saat ini dalam keadaan SAKIT dan memerlukan istirahat selama {{ $calculate_date }} hari, terhitung dari tanggal {{ \Carbon\Carbon::parse($start_date)->format('d M Y') }} sampai {{ \Carbon\Carbon::parse($end_date)->format('d M Y') }}. +
+ Demikianlah Surat Keterangan ini untuk dapat dipergunakan seperlunya. +
+ +
+ {{ \Carbon\Carbon::parse($date)->format('d M Y') }} +
+ Pemeriksa, +
+
+ +
+
+ ( {{ $doctor_name }} ) +
+
+ PT Link Medis Sehat
+ Primaya Hospital Corporate
+ Graha Cempaka Mas Blok D5-6
+ Jl. Let. Jend. Suprapto, Jakarta Pusat 10640, Indonesia
+ Telp (021) 4217746/47 +
+
+ + + From ba5586620378d9195d9b6d6fbff1b2c56e1c6d7d Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Fri, 17 May 2024 14:00:24 +0700 Subject: [PATCH 50/52] update pagination livechat --- .../Http/Controllers/Api/ChatController.php | 26 +++++++++++++------ .../Http/Controllers/Api/DuitkuController.php | 6 +++++ .../Controllers/Api/LivechatController.php | 15 ++++++----- app/Helpers/DuitkuHelper.php | 2 +- app/Services/Duitku.php | 6 ++--- 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/Modules/Linksehat/Http/Controllers/Api/ChatController.php b/Modules/Linksehat/Http/Controllers/Api/ChatController.php index 48d86997..a6a3307b 100644 --- a/Modules/Linksehat/Http/Controllers/Api/ChatController.php +++ b/Modules/Linksehat/Http/Controllers/Api/ChatController.php @@ -141,8 +141,6 @@ class ChatController extends Controller 'type' => 'file', ]); } - - $data = Message::where('channel_id', $request->channel_id)->get()->toArray(); // Berikan respons yang sesuai ke klien $channel = Channel::where('id',$request->channel_id)->first(); @@ -162,7 +160,6 @@ class ChatController extends Controller 'message' => 'Message sent successfully', 'data' => [ 'header' => $name, - 'chat' => $data ] ]); } @@ -216,8 +213,14 @@ class ChatController extends Controller } // Ini Untul Chat - $data = Message::where('channel_id', $request->channel_id)->get()->toArray(); + $perPage = $request->input('per_page', 10); // Default 10 pesan per halaman + $page = $request->input('page', 1); // Default halaman 1 + $data = Message::where('channel_id', $request->channel_id) + ->where('type', '!=', 'trigger') + ->orderBy('created_at', 'desc') // Urutkan berdasarkan created_at secara descending + ->paginate($perPage, ['*'], 'page', $page); + // Data Consultation Summary $consultationSummary = [ 'subject' => $livechat->subject, @@ -230,10 +233,9 @@ class ChatController extends Controller if ($livechat->health_certificate_start && $livechat->health_certificate_end){ $healthSertificate = True; } - // Berikan respons yang sesuai ke klien return response()->json([ - 'message' => 'Message sent successfully', + 'message' => 'Message sent successfully', 'data' => [ 'header' => $name, 'avatar' => $avatar, @@ -244,10 +246,18 @@ class ChatController extends Controller 'end' => $consultationEnd, 'work' => $work, 'address' => $address, - 'chat' => $data, + 'chat' => $data->items(), + 'pagination' => [ + 'total' => $data->total(), + 'per_page' => $data->perPage(), + 'current_page' => $data->currentPage(), + 'last_page' => $data->lastPage(), + 'from' => $data->firstItem(), + 'to' => $data->lastItem(), + ], 'summary' => $consultationSummary, 'livechat_id' => $livechat->id, - 'health_sertificate' => $healthSertificate + 'health_sertificate' => $healthSertificate, ] ]); } diff --git a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php index e22ff031..4f5159b5 100644 --- a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php +++ b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php @@ -335,6 +335,12 @@ class DuitkuController extends Controller } else if ($notif->resultCode == "01") { // Action Failed + $livechat = Livechat::where('uuid', $notif->merchantOrderId)->first(); + // Update status pembayaran + $livechat->payment_method = $notif->paymentCode; + $livechat->status = 7; // failed payment + $livechat->save(); + return response()->json(['message' => 'User Gagal melakukan pembayaran']); } diff --git a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php index 23ca6fca..b1e2d7e0 100644 --- a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php +++ b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php @@ -4,8 +4,10 @@ namespace Modules\Linksehat\Http\Controllers\Api; use App\Helpers\Helper; use App\Helpers\DuitkuHelper; +use App\Services\Duitku; use App\Models\Organization; use App\Models\PractitionerRole; +use App\Models\Invoice; use App\Models\PaymentsMethods; use App\Models\Livechat; use App\Models\OLDLMS\User; @@ -259,7 +261,10 @@ class LivechatController extends Controller // Mengambil user dari database $user = User::with('detail')->where('nId', $livechat->patient_id)->first(); - + $address = DB::connection('oldlms')->table('tm_users_address')->where('nIDUser', $user->nID)->first('sAlamat'); + if($address){ + $address = $address->sAlamat; + } if (!$user) { return response()->json(['success' => false, 'message' => 'User not found'], 404); } @@ -270,17 +275,14 @@ class LivechatController extends Controller 'paymentAmount' => $totalPay, 'email' => $user->sEmail, 'phoneNumber' => $user->sPhone, - 'productDetails' => 'Telekonsul Livechat LMS', + 'productDetails' => 'INV-' . date('Ymd') . '-' . rand(100, 999), 'merchantOrderId' => $livechat->uuid, 'additionalParam' => '', 'merchantUserInfo' => '', 'customerVaName' => $user->sFirstName . ' ' . $user->sLastName, - 'callbackUrl' => 'https://google.com', - 'returnUrl' => 'https://linksehat.com', - 'expiryPeriod' => 60, 'firstName' => $user->sFirstName, 'lastName' => $user->sLastName, - 'alamat' => '', + 'alamat' => $address, 'city' => '', 'postalCode' => '' ]; @@ -288,6 +290,7 @@ class LivechatController extends Controller // Membuat invoice menggunakan DuitkuHelper $duitku = DuitkuHelper::createInvoice($data); + return response()->json(['success' => true, 'data' => $duitku], 200); } catch (Exception $e) { // Menangkap error dan mengembalikan respon error diff --git a/app/Helpers/DuitkuHelper.php b/app/Helpers/DuitkuHelper.php index 48a31e05..68237430 100644 --- a/app/Helpers/DuitkuHelper.php +++ b/app/Helpers/DuitkuHelper.php @@ -93,7 +93,7 @@ class DuitkuHelper $additionalParam = $data['additionalParam']; // optional $merchantUserInfo = $data['merchantUserInfo']; // optional $customerVaName = $data['customerVaName']; // display name on bank confirmation display - $callbackUrl = env('APP_URL').'/api/linksehat/callback-duitku'; // url for callback + $callbackUrl = env('DUITKU_PAYMENT_CALLBACK_URL'); // url for callback $returnUrl = env('APP_URL').'/api/linksehat/redirect-duitku';; // url for redirect $expiryPeriod = 60; // set the expired time in minutes diff --git a/app/Services/Duitku.php b/app/Services/Duitku.php index da849ab9..4b60cc56 100644 --- a/app/Services/Duitku.php +++ b/app/Services/Duitku.php @@ -44,7 +44,7 @@ class Duitku $merchantUserInfo = ''; // optional $customerVaName = $patient->name ?? ''; // display name on bank confirmation display $callbackUrl = env('DUITKU_PAYMENT_CALLBACK_URL'); // url for callback - $returnUrl = 'https://dev-superapp.primaya.id'; + $returnUrl = env(''); $expiryPeriod = $paymentMethods->timeout; // set the expired time in minutes // Customer Detail @@ -53,9 +53,9 @@ class Duitku $lastName = $patient->last_name ?? ''; // Address - $alamat = $patient->address->first()->line ?? ''; + $alamat = $patient->address?? ''; // dd($alamat); - $city = $patient->address->first()->city->name ?? ''; + $city = $patient->city ?? ''; // dd($city); $postalCode = $patient->postal_code ?? ''; $countryCode = "ID"; From 664f015c941948aac637c07423654e0f3048d6e1 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Fri, 17 May 2024 15:40:27 +0700 Subject: [PATCH 51/52] update autocomplite --- .../Api/AutocompleteController.php | 49 +++++++++++++++++++ Modules/Linksehat/Routes/api.php | 3 ++ 2 files changed, 52 insertions(+) diff --git a/Modules/Linksehat/Http/Controllers/Api/AutocompleteController.php b/Modules/Linksehat/Http/Controllers/Api/AutocompleteController.php index 3af9e00b..633d50e8 100644 --- a/Modules/Linksehat/Http/Controllers/Api/AutocompleteController.php +++ b/Modules/Linksehat/Http/Controllers/Api/AutocompleteController.php @@ -3,6 +3,9 @@ namespace Modules\Linksehat\Http\Controllers\Api; use App\Helpers\Helper; use App\Models\OLDLMS\User; +use App\Models\Icd; +use App\Models\Drug; +use App\Models\Unit; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; @@ -93,5 +96,51 @@ class AutocompleteController extends Controller { } return Helper::responseJson($data); } + + public function diagnosis(){ + $icds = Icd::query() + ->get(); + + $manipulatedIcds = $icds->map(function ($icd) { + // Contoh manipulasi, tambahkan atau ubah properti sesuai kebutuhan + return [ + 'value' => $icd->code, // Ganti dengan properti yang sesuai dari model Icd + 'label' => $icd->code . ' - ' .$icd->name, // Ganti dengan properti yang sesuai dari model Icd + ]; + }); + return Helper::responseJson(data: $manipulatedIcds); + } + + public function drugList(Request $request){ + $drugs = Drug::query() + ->where([ + 'atc_code' => 'lms', // ini untuk menggunakan list obat yang baru + ]) + ->get(); + + $manipulatedDrugs = $drugs->map(function ($drug) { + // Contoh manipulasi, tambahkan atau ubah properti sesuai kebutuhan + return [ + 'value' => $drug->id, // Ganti dengan properti yang sesuai dari model Icd + 'label' => $drug->name, // Ganti dengan properti yang sesuai dari model Icd + ]; + }); + return Helper::responseJson(data: $manipulatedDrugs); + } + + public function unitList(Request $request){ + $units = Unit::query() + ->get(); + + $manipulatedUnits = $units->map(function ($unit) { + // Contoh manipulasi, tambahkan atau ubah properti sesuai kebutuhan + return [ + 'value' => $unit->id, // Ganti dengan properti yang sesuai dari model Icd + 'label' => $unit->name, // Ganti dengan properti yang sesuai dari model Icd + ]; + }); + return Helper::responseJson(data: $manipulatedUnits); + } + } diff --git a/Modules/Linksehat/Routes/api.php b/Modules/Linksehat/Routes/api.php index e0fa3969..9344de6f 100644 --- a/Modules/Linksehat/Routes/api.php +++ b/Modules/Linksehat/Routes/api.php @@ -86,6 +86,9 @@ Route::prefix('linksehat')->group(function () { Route::get('autocomplete/blood_type', [AutocompleteController::class, 'bloodType']); Route::get('autocomplete/relationship', [AutocompleteController::class, 'relationship']); Route::get('autocomplete/corporate', [AutocompleteController::class, 'corporate']); + Route::get('autocomplete/drugs', [AutocompleteController::class, 'drugList']); + Route::get('autocomplete/units', [AutocompleteController::class, 'unitList']); + Route::get('autocomplete/diagnosis', [AutocompleteController::class, 'diagnosis']); Route::post('manual-linking', [LinkingController::class, 'linkingValidate']); From f83a7954aa30bef38a5eda822dff8232e35f34f6 Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Fri, 17 May 2024 15:59:51 +0700 Subject: [PATCH 52/52] update --- .../Http/Controllers/Api/Doctor/ChatDoctorController.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php b/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php index 4e39f144..1809f0ac 100644 --- a/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php +++ b/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php @@ -62,8 +62,12 @@ class ChatDoctorController extends Controller $lastMessage = Message::where('channel_id', $d['id']) ->latest('created_at') ->first(); - $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; - $avatarMember = $user->detail->sImage ?? $urlAvatarDefault; + if ($user->detail){ + $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $user->detail->sImage ?? $urlAvatarDefault; + } else { + $avatarMember = 'https://linksehat.dev/assets/img/users/male-avatar.png'; + } $arr['id'] = $d['id']; $arr['avatar'] = $avatarMember;