From 8aa67c18644d0c574718e08b60faacf6ce2aec4f Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Tue, 30 Apr 2024 10:11:11 +0700 Subject: [PATCH] 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')));