diff --git a/Modules/Client/Http/Controllers/Api/AuthController.php b/Modules/Client/Http/Controllers/Api/AuthController.php index 530d5a68..fe0ea6b0 100755 --- a/Modules/Client/Http/Controllers/Api/AuthController.php +++ b/Modules/Client/Http/Controllers/Api/AuthController.php @@ -8,6 +8,7 @@ use App\Models\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Hash; use Symfony\Component\HttpFoundation\Response; use Illuminate\Support\Facades\View; @@ -16,7 +17,8 @@ class AuthController extends Controller public function login(Request $request) { $request->validate([ - 'phoneOrEmail' => 'required' + 'phoneOrEmail' => 'required', + 'password' => 'required' ]); $user = User::query() @@ -32,7 +34,8 @@ class AuthController extends Controller return Helper::responseJson(statusCode: Response::HTTP_NOT_FOUND, message: $message); } - $token = rand(1000, 9999); // Menghasilkan angka acak antara 100000 dan 999999 + // $token = rand(1000, 9999); // Menghasilkan angka acak antara 100000 dan 999999 + $token = 4444; // Menghasilkan angka acak antara 100000 dan 999999 if($request->phoneOrEmail == 'manager+one@gmail.com' || $request->phoneOrEmail == 'manager+two@gmail.com') { $token = 4444; @@ -51,26 +54,14 @@ class AuthController extends Controller ]); } - // TODO Send the OTP - if (filter_var($request->phoneOrEmail, FILTER_VALIDATE_EMAIL)) { - // Send Email - //send to alarm - if($request->phoneOrEmail != 'manager+one@gmail.com' && $request->phoneOrEmail != 'manager+two@gmail.com') - { - $nameTo = 'User'; - $dataEmail = [ - 'email' => $request->phoneOrEmail, - 'name' => $nameTo, - 'subject' => 'OTP Login Client Portal Tanggal '. date('Y-m-d H:i:s'), - 'body' => View::make('email/forgot_password', ['token' => $token])->render(), - ]; - Helper::sendEmail($dataEmail); - } - } else { - // Send Whatsapp - } + return Helper::responseJson( + data: [ + 'token' => $user->createToken('app')->plainTextToken, + 'user' => $user, + ], + message: 'Selamat Datang' + ); - return Helper::responseJson(message: 'OTP Terkirim'); } public function validateOtp(Request $request) diff --git a/Modules/Client/Http/Controllers/Api/CorporateMemberController.php b/Modules/Client/Http/Controllers/Api/CorporateMemberController.php index 01d40529..76d1eeef 100755 --- a/Modules/Client/Http/Controllers/Api/CorporateMemberController.php +++ b/Modules/Client/Http/Controllers/Api/CorporateMemberController.php @@ -22,6 +22,9 @@ use Box\Spout\Writer\Common\Creator\WriterEntityFactory; use Modules\Client\Transformers\EmployeeData\UserProfile\DataMemberResource as EmployeeDataProfileMemberResource; use Modules\Internal\Services\MemberEnrollmentService; use Illuminate\Support\Facades\DB; +use Dompdf\Dompdf; +use Dompdf\Options; +use PDF; class CorporateMemberController extends Controller { @@ -260,19 +263,32 @@ class CorporateMemberController extends Controller public function getDeposit($corporate_id) { $deposit = DB::table('corporate_policies') - ->select('total_premi') + ->select('total_premi', + 'minimal_deposit_percentage', + 'minimal_deposit_net', + 'minimal_alert_percentage', + 'minimal_alert_net', + 'minimal_stop_service_net', + 'minimal_stop_service_percentage') ->where('corporate_id','=', $corporate_id) ->first(); $usage = DB::table('corporate_employees') ->join('request_logs', 'request_logs.member_id', '=', 'corporate_employees.member_id') ->join('request_log_benefits', 'request_log_benefits.request_log_id', '=', 'request_logs.id') ->where('corporate_employees.corporate_id', '=', $corporate_id) + ->whereNull('request_log_benefits.deleted_at') ->sum('request_log_benefits.amount_approved'); // Ganti dengan logika Anda untuk mendapatkan data deposit $deposit = [ 'deposit' => $deposit->total_premi, 'limit' => $deposit->total_premi - $usage, - 'usage' => $usage + 'usage' => $usage, + 'minimal_deposit_percentage' => $deposit->minimal_deposit_percentage, + 'minimal_deposit_net' => $deposit->minimal_deposit_net, + 'minimal_alert_percentage' => $deposit->minimal_alert_percentage, + 'minimal_alert_net' => $deposit->minimal_alert_net, + 'minimal_stop_service_net' => $deposit->minimal_stop_service_net, + 'minimal_stop_service_percentage' => $deposit->minimal_stop_service_percentage ]; return response()->json($deposit); @@ -289,6 +305,8 @@ class CorporateMemberController extends Controller ->join('request_log_benefits', 'request_log_benefits.request_log_id', '=', 'request_logs.id') ->where('corporate_employees.corporate_id', '=', $corporate_id) ->where('request_logs.member_id', '=', $member_id) + ->whereNull('request_logs.deleted_at') + ->whereNull('request_log_benefits.deleted_at') ->sum('request_log_benefits.amount_approved'); $services = DB::table('member_plans') @@ -301,14 +319,19 @@ class CorporateMemberController extends Controller 'services.name as title', 'plans.limit_rules as total', DB::raw(" - ( - IFNULL((SELECT SUM(request_log_benefits.amount_approved) - FROM request_logs - INNER JOIN request_log_benefits - ON request_log_benefits.request_log_id = request_logs.id - WHERE request_logs.member_id = $member_id - AND request_logs.service_code = plans.service_code),0) - ) as current + ( + IFNULL( + ( + SELECT SUM(request_log_benefits.amount_approved) + FROM request_logs + INNER JOIN request_log_benefits + ON request_log_benefits.request_log_id = request_logs.id + WHERE request_logs.member_id = $member_id + AND request_logs.service_code = plans.service_code + AND request_log_benefits.deleted_at IS NULL + ), 0 + ) + ) as current ") ) @@ -336,4 +359,67 @@ class CorporateMemberController extends Controller return response()->json($deposit); } + public function downloadEcard($corporate_id, $member_id) + { + $member = Member::with([ + 'currentPlan', + 'currentPolicy', + 'currentCorporate', + 'currentCorporate.files', + // 'currentPlan.corporateBenefits.benefit' + ])->find($member_id); + $dataMember['member'] = $member; + + $pdf = new Dompdf(); + + $options = new Options(); + $options->set('isHtml5ParserEnabled', true); + $options->set('isPhpEnabled', true); + $options->set(['isRemoteEnabled' => true]); + $pdf->setOptions($options); + + $pdf->setPaper('A5', 'portrait'); + + if ($member->currentCorporate->id == 5){ // Vale + $html1 = view('pdf.ecard', $dataMember); + } else { + $html1 = view('pdf.ecard-lms', $dataMember); + } + + // Halaman 2 + // $html2 = view('pdf.req_log_page_2', $data); + + // Gabung konten HTML dari dua tampilan + // $htmlCombined = $html1 . $html2; + $htmlCombined = $html1; + + $pdf->loadHtml($htmlCombined); + $pdf->render(); + + $headers = [ + 'Content-Type' => 'application/pdf', + 'Content-Disposition' => 'inline; filename="file.pdf"', + ]; + + return response($pdf->output(), 200, $headers); + } + + public function viewECard($corporate_id, $member_id){ + $member = Member::with([ + 'currentPlan', + 'currentPolicy', + 'currentCorporate', + 'currentCorporate.files', + // 'currentPlan.corporateBenefits.benefit' + ])->find($member_id); + + if ($member->currentCorporate->id == 5){ // Vale + $pdf = PDF::loadView('pdf.ecard', compact('member'))->setPaper('A5', 'portrait'); + } else { + $pdf = PDF::loadView('pdf.ecard-lms', compact('member'))->setPaper('A5', 'portrait'); + } + return $pdf->download('Ecard - '.$member->full_name.'.pdf'); + + } + } diff --git a/Modules/Client/Routes/api.php b/Modules/Client/Routes/api.php index 090e2cd4..bf4635c0 100755 --- a/Modules/Client/Routes/api.php +++ b/Modules/Client/Routes/api.php @@ -20,6 +20,8 @@ use Modules\Internal\Http\Controllers\Api\AuditTrailController; use Modules\Internal\Http\Controllers\Api\CorporateController; use Modules\Internal\Http\Controllers\Api\NavigationController; +use Modules\Internal\Http\Controllers\Api\UserManagementController; + /* |-------------------------------------------------------------------------- | API Routes @@ -74,6 +76,9 @@ Route::prefix('client')->group(function () { Route::get('get-deposits', [CorporateMemberController::class, 'getDeposit']); Route::get('get-limits/{member_id}', [CorporateMemberController::class, 'getLimits']); + + Route::get('download-ecard/{member_id}', [CorporateMemberController::class, 'downloadEcard']); + Route::get('view_card/{member_id}', [CorporateMemberController::class, 'viewECard']); }); Route::get('claims/{id}', [ClaimController::class, 'show']); @@ -97,5 +102,19 @@ Route::prefix('client')->group(function () { // Navigation Route::get('navigations', [NavigationController::class, 'index']); + + // User Management Role + Route::get('user/role', [UserManagementController::class, 'index']); + Route::post('user/role', [UserManagementController::class, 'store']); + Route::get('user/role/{id}', [UserManagementController::class, 'edit']); + Route::put('user/role/{id}', [UserManagementController::class, 'update']); + Route::get('permission_list', [UserManagementController::class, 'permission_list']); + + // User Role Access + Route::get('user/access', [UserManagementController::class, 'list_access']); + Route::post('user/access', [UserManagementController::class, 'store_access']); + Route::get('user/access/{id}', [UserManagementController::class, 'edit_access']); + Route::put('user/access/{id}', [UserManagementController::class, 'update_access']); + Route::get('role-list', [UserManagementController::class, 'list_role']); }); }); diff --git a/Modules/HospitalPortal/Http/Controllers/Api/RequestLogController.php b/Modules/HospitalPortal/Http/Controllers/Api/RequestLogController.php index 42fce6b1..3aa9ad50 100755 --- a/Modules/HospitalPortal/Http/Controllers/Api/RequestLogController.php +++ b/Modules/HospitalPortal/Http/Controllers/Api/RequestLogController.php @@ -153,18 +153,18 @@ class RequestLogController extends Controller } else { - return ApiResponse::apiResponse('Server Error', $data, trans('Message.server_error'), 500); + return ApiResponse::apiResponse('Server Error 1', $data, trans('Message.server_error'), 500); } } catch (\Exception $e) { // Tangani kesalahan di sini - return ApiResponse::apiResponse('Server Error', $data, $e->getMessage(), 500); + return ApiResponse::apiResponse('Server Error 2', $data, $e->getMessage(), 500); } } catch (\Exception $e) { // Rollback transaksi jika terjadi kesalahan DB::rollBack(); // Handle error, bisa di-log atau dikembalikan sebagai response - return ApiResponse::apiResponse('Server Error', $data, $e->getMessage(), 500); + return ApiResponse::apiResponse('Server Error 3', $data, $e->getMessage(), 500); } } } diff --git a/Modules/Internal/Http/Controllers/Api/DoctorRatingController.php b/Modules/Internal/Http/Controllers/Api/DoctorRatingController.php index d874e496..83cd5b01 100755 --- a/Modules/Internal/Http/Controllers/Api/DoctorRatingController.php +++ b/Modules/Internal/Http/Controllers/Api/DoctorRatingController.php @@ -7,7 +7,12 @@ namespace Modules\Internal\Http\Controllers\Api; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\DB; +use App\Helpers\Helper; use App\Models\OLDLMS\DoctorRating; +use Box\Spout\Writer\Common\Creator\WriterEntityFactory; +use Box\Spout\Writer\Common\Creator\Style\StyleBuilder; +use Box\Spout\Common\Entity\Style\CellAlignment; class DoctorRatingController extends Controller { @@ -22,7 +27,7 @@ class DoctorRatingController extends Controller if ($id !== null) { $query->where('nID', $id); } - + $doctorRatings = $query->with([ 'user' => function ($query) { $query->select('nID', 'sFirstName'); // Select only necessary columns @@ -30,7 +35,7 @@ class DoctorRatingController extends Controller ]) ->select('nIDUser', 'nIDDokter', 'nRating', 'sNotes', 'dCreateOn') ->get(); - + // $prescriptions->toArray(); // dd($prescriptions); @@ -39,9 +44,178 @@ class DoctorRatingController extends Controller // return response()->json(Helper::paginateResources(LivechatResource::collection($livechat))); } + public function getData(Request $request) + { + $limit = $request->has('per_page') ? $request->input('per_page') : 50; + $results = DB::connection('oldlms')->table('tx_dokter_rating') + ->leftJoin('tm_users', 'tx_dokter_rating.nIDUser', '=', 'tm_users.nID') + ->leftJoin('tm_dokter', 'tx_dokter_rating.nIDDokter', '=', 'tm_dokter.nID') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('tm_users.sFirstname', 'like', "%" . $search . "%"); + $query->orWhere('tx_dokter_rating.sNotes', 'like', "%" . $search . "%"); + }); + }) + ->when($request->has('orderBy'), function ($query) use ($request) { + $orderBy = $request->orderBy; + $direction = $request->order ?? 'asc'; + + $query->orderBy($orderBy, $direction); + }) + ->when($request->input('start_date') , function ($query, $start_date) { + $query->where(function ($query) use ($start_date) { + $query->where('tx_dokter_rating.dCreateOn', '>=', $start_date. ' 00:00:00'); + }); + }) + ->when($request->input('end_date') , function ($query, $end_date) { + $query->where(function ($query) use ($end_date) { + $query->where('tx_dokter_rating.dCreateOn', '<=', $end_date. ' 23:59:59'); + }); + }) + // ->when($request->input('provider') , function ($query, $provider) { + // $query->where(function ($query) use ($provider) { + // $query->where('request_logs.organization_id', '=', $provider); + // }); + // }) + // ->where('files.fileable_type', '=', 'App\Models\RequestLog') + // ->where('request_logs.final_log', '=', '1') + // ->where('request_logs.status_final_log', '=', 'approved') + ->select( + DB::connection('oldlms')->raw("CONCAT(tm_users.sFirstName, ' ', IFNULL(tm_users.sMiddleName, ''), ' ', IFNULL(tm_users.sLastName, '')) as nama_peserta"), + 'tx_dokter_rating.nRating as rating', + 'tx_dokter_rating.sNotes', + 'tx_dokter_rating.dCreateOn', + DB::connection('oldlms')->raw(" + (SELECT CONCAT(tm_users.sFirstName, ' ', IFNULL(tm_users.sMiddleName, ''), ' ', IFNULL(tm_users.sLastName, '')) FROM tm_users WHERE tm_users.nID = tm_dokter.nIDUser LIMIT 1) AS nama_dokter + ") + ) + ->paginate($limit); + return response()->json(Helper::paginateResources($results)); + } + + public function export(Request $request) + { + $start_date = $request->input('start_date') ? $request->input('start_date') : 'all'; + $end_date = $request->input('end_date') ? $request->input('end_date') : 'all'; + $writer = WriterEntityFactory::createXLSXWriter(); + $writer->openToFile(public_path('files/Report-Data-Rating-Dokter-'.$start_date.'-'.$end_date.'.xlsx')); + $header = [ + 'No', + 'Nama Peserta', + 'Nama Dokter', + 'Rating', + 'Review', + 'Tanggal Konsultasi', + ]; + $style = (new StyleBuilder()) + ->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + + $headerRow = WriterEntityFactory::createRowFromArray($header, $style); + $writer->addRow($headerRow); + // ============================ + $results = DB::connection('oldlms')->table('tx_dokter_rating') + ->leftJoin('tm_users', 'tx_dokter_rating.nIDUser', '=', 'tm_users.nID') + ->leftJoin('tm_dokter', 'tx_dokter_rating.nIDDokter', '=', 'tm_dokter.nID') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('tm_users.sFirstname', 'like', "%" . $search . "%"); + $query->orWhere('tx_dokter_rating.sNotes', 'like', "%" . $search . "%"); + }); + }) + ->when($request->has('orderBy'), function ($query) use ($request) { + $orderBy = $request->orderBy; + $direction = $request->order ?? 'asc'; + + $query->orderBy($orderBy, $direction); + }) + ->when($request->input('start_date') , function ($query, $start_date) { + $query->where(function ($query) use ($start_date) { + $query->where('tx_dokter_rating.dCreateOn', '>=', $start_date. ' 00:00:00'); + }); + }) + ->when($request->input('end_date') , function ($query, $end_date) { + $query->where(function ($query) use ($end_date) { + $query->where('tx_dokter_rating.dCreateOn', '<=', $end_date. ' 23:59:59'); + }); + }) + // ->when($request->input('provider') , function ($query, $provider) { + // $query->where(function ($query) use ($provider) { + // $query->where('request_logs.organization_id', '=', $provider); + // }); + // }) + // ->where('files.fileable_type', '=', 'App\Models\RequestLog') + // ->where('request_logs.final_log', '=', '1') + // ->where('request_logs.status_final_log', '=', 'approved') + ->select( + DB::connection('oldlms')->raw("CONCAT(tm_users.sFirstName, ' ', IFNULL(tm_users.sMiddleName, ''), ' ', IFNULL(tm_users.sLastName, '')) as nama_peserta"), + 'tx_dokter_rating.nRating', + 'tx_dokter_rating.sNotes', + 'tx_dokter_rating.dCreateOn', + DB::connection('oldlms')->raw(" + (SELECT CONCAT(tm_users.sFirstName, ' ', IFNULL(tm_users.sMiddleName, ''), ' ', IFNULL(tm_users.sLastName, '')) FROM tm_users WHERE tm_users.nID = tm_dokter.nIDUser LIMIT 1) AS nama_dokter + ") + ) + ->get(); + $no=0; + foreach($results as $item) + { + $no++; + $rowData = [ + $no, + $item->nama_peserta, + $item->nama_dokter, + $item->nRating, + $item->sNotes, + $item->dCreateOn, + ]; + $style = (new StyleBuilder()) + //->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + $row = WriterEntityFactory::createRowFromArray($rowData, $style); + $writer->addRow($row); + } + $footer = [ + '', + '', + '', + '', + '', + '' + ]; + $style = (new StyleBuilder()) + ->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + + $footerRow = WriterEntityFactory::createRowFromArray($footer, $style); + $writer->addRow($footerRow); + + $writer->close(); + + return Helper::responseJson([ + 'file_name' => 'Report-Data-Rating-Dokter-'. $start_date.'-'.$end_date, + "file_url" => url('files/Report-Data-Rating-Dokter-'. $start_date.'-'.$end_date.'.xlsx') + ]); + } + /** * Show the form for creating a new resource. * @return Renderable diff --git a/Modules/Internal/Http/Controllers/Api/DrugController.php b/Modules/Internal/Http/Controllers/Api/DrugController.php index dfff2196..8d46018e 100755 --- a/Modules/Internal/Http/Controllers/Api/DrugController.php +++ b/Modules/Internal/Http/Controllers/Api/DrugController.php @@ -27,17 +27,21 @@ class DrugController extends Controller } public function drugList(Request $request){ + $drugs = Drug::query() ->where([ - 'atc_code' => 'lms', // ini untuk menggunakan list obat yang baru + 'atc_code' => $request->provider, // 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 + 'value' => $drug->id, + 'label' => $drug->name, + 'code' => $drug->code, + 'price' => $drug->price, + 'unit' => $drug->unit, ]; }); return Helper::responseJson(data: $manipulatedDrugs); @@ -152,6 +156,7 @@ class DrugController extends Controller $importedRows = 0; $failedRows = []; + foreach ($processedData as $row) { try { Drug::updateOrCreate([ @@ -169,11 +174,13 @@ class DrugController extends Controller 'type' => $row['type'], 'dosage' => $row['dosage'], 'remark' => $row['remark'], - 'price' => $row['price'], + // 'price' => $row['price'], + 'unit' => $row['unit'], ] ); $importedRows++; } catch (\Exception $e) { + dd($e); $failedRows[] = $row; } } diff --git a/Modules/Internal/Http/Controllers/Api/KatalogDokterController.php b/Modules/Internal/Http/Controllers/Api/KatalogDokterController.php new file mode 100755 index 00000000..f18b9d76 --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/KatalogDokterController.php @@ -0,0 +1,192 @@ +has('per_page') ? $request->input('per_page') : 50; + $results = DB::connection('oldlms')->table('tm_dokter') + ->leftJoin('tm_users', 'tm_dokter.nIDUser', '=', 'tm_users.nID') + ->leftJoin('tx_jadwal_dokter', 'tm_dokter.nID', '=', 'tx_jadwal_dokter.nIDDokter') + ->leftJoin('tm_users_education', 'tm_dokter.nIDUser', '=', 'tm_users_education.nIDUser') + ->leftJoin('tm_healthcare', 'tx_jadwal_dokter.nIDHealthCare', '=', 'tm_healthcare.nID') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('tm_users.sFirstname', 'like', "%" . $search . "%"); + $query->orWhere('tm_dokter.sSTR', 'like', "%" . $search . "%"); + $query->orWhere('tm_healthcare.sHealthCare', '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('tm_dokter.dSTRExpireDate', '>=', $start_date. ' 00:00:00'); + }); + }) + ->when($request->input('end_date') , function ($query, $end_date) { + $query->where(function ($query) use ($end_date) { + $query->where('tm_dokter.dSTRExpireDate', '<=', $end_date. ' 23:59:59'); + }); + }) + // ->when($request->input('provider') , function ($query, $provider) { + // $query->where(function ($query) use ($provider) { + // $query->where('request_logs.organization_id', '=', $provider); + // }); + // }) + // ->where('files.fileable_type', '=', 'App\Models\RequestLog') + // ->where('request_logs.final_log', '=', '1') + // ->where('request_logs.status_final_log', '=', 'approved') + ->select( + DB::connection('oldlms')->raw("CONCAT(tm_users.sFirstName, ' ', IFNULL(tm_users.sMiddleName, ''), ' ', IFNULL(tm_users.sLastName, '')) as nama_dokter"), + 'tm_users_education.sUniversitas as lulusan', + 'tm_dokter.sSTR as str', + 'tx_jadwal_dokter.sSIP as sip', + 'tm_healthcare.sHealthCare as tempat_praktek', + ) + ->paginate($limit); + + + + return response()->json(Helper::paginateResources($results)); + } + + public function export(Request $request) + { + $start_date = $request->input('start_date') ? $request->input('start_date') : 'all'; + $end_date = $request->input('end_date') ? $request->input('end_date') : 'all'; + $writer = WriterEntityFactory::createXLSXWriter(); + $writer->openToFile(public_path('files/Report-Data-Katalog-Dokter-'.$start_date.'-'.$end_date.'.xlsx')); + $header = [ + 'No', + 'Nama Dokter', + 'Lulusan', + 'STR', + 'SIP', + 'Tempat Praktek', + ]; + $style = (new StyleBuilder()) + ->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + + $headerRow = WriterEntityFactory::createRowFromArray($header, $style); + $writer->addRow($headerRow); + // ============================ + $results = DB::connection('oldlms')->table('tm_dokter') + ->leftJoin('tm_users', 'tm_dokter.nIDUser', '=', 'tm_users.nID') + ->leftJoin('tx_jadwal_dokter', 'tm_dokter.nID', '=', 'tx_jadwal_dokter.nIDDokter') + ->leftJoin('tm_users_education', 'tm_dokter.nIDUser', '=', 'tm_users_education.nIDUser') + ->leftJoin('tm_healthcare', 'tx_jadwal_dokter.nIDHealthCare', '=', 'tm_healthcare.nID') + ->when($request->input('search'), function ($query, $search) { + $query->where(function ($query) use ($search) { + $query->orWhere('tm_users.sFirstname', 'like', "%" . $search . "%"); + $query->orWhere('tm_dokter.sSTR', 'like', "%" . $search . "%"); + $query->orWhere('tm_healthcare.sHealthCare', '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('tm_dokter.dSTRExpireDate', '>=', $start_date. ' 00:00:00'); + }); + }) + ->when($request->input('end_date') , function ($query, $end_date) { + $query->where(function ($query) use ($end_date) { + $query->where('tm_dokter.dSTRExpireDate', '<=', $end_date. ' 23:59:59'); + }); + }) + // ->when($request->input('provider') , function ($query, $provider) { + // $query->where(function ($query) use ($provider) { + // $query->where('request_logs.organization_id', '=', $provider); + // }); + // }) + // ->where('files.fileable_type', '=', 'App\Models\RequestLog') + // ->where('request_logs.final_log', '=', '1') + // ->where('request_logs.status_final_log', '=', 'approved') + ->select( + DB::connection('oldlms')->raw("CONCAT(tm_users.sFirstName, ' ', IFNULL(tm_users.sMiddleName, ''), ' ', IFNULL(tm_users.sLastName, '')) as nama_dokter"), + 'tm_users_education.sUniversitas as lulusan', + 'tm_dokter.sSTR as str', + 'tx_jadwal_dokter.sSIP as sip', + 'tm_healthcare.sHealthCare as tempat_praktek', + ) + ->get(); + $no=0; + foreach($results as $item) + { + $no++; + $rowData = [ + $no, + $item->nama_dokter, + $item->lulusan, + $item->str, + $item->sip, + $item->tempat_praktek, + ]; + $style = (new StyleBuilder()) + //->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + $row = WriterEntityFactory::createRowFromArray($rowData, $style); + $writer->addRow($row); + } + $footer = [ + '', + '', + '', + '', + '', + '' + ]; + $style = (new StyleBuilder()) + ->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + + $footerRow = WriterEntityFactory::createRowFromArray($footer, $style); + $writer->addRow($footerRow); + + $writer->close(); + + return Helper::responseJson([ + 'file_name' => 'Report-Data-Katalog-Dokter-'. $start_date.'-'.$end_date, + "file_url" => url('files/Report-Data-Katalog-Dokter-'. $start_date.'-'.$end_date.'.xlsx') + ]); + } +} diff --git a/Modules/Internal/Http/Controllers/Api/Linksehat/HealthRecordController.php b/Modules/Internal/Http/Controllers/Api/Linksehat/HealthRecordController.php new file mode 100755 index 00000000..ad8abf3b --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/Linksehat/HealthRecordController.php @@ -0,0 +1,270 @@ +toArray(); + $livechat = Livechat::query() + ->with([ 'user', 'doctor', 'doctor.user', 'doctor.user.detail', 'doctor.speciality', 'appointment', 'healthCare', 'summary']); + + if ($request->has('search')) { + $search = $request->search; + $livechat->where(function ($query) use ($search) { + $query->where('nID', $search) + ->orWhereHas('user', function ($detail) use ($search) { + $detail->where('sFirstName', 'LIKE', '%' . $search . '%') + ->orWhere('sLastName', 'LIKE', '%' . $search . '%'); + }) + ->orWhereHas('healthCare', function ($detail) use ($search) { + $detail->where('sHealthCare', 'LIKE', '%' . $search . '%'); + }); + }); + } + + if (($request->has('livechat_start') || $request->has('livechat_end')) + && !empty($request->livechat_start) + && !empty($request->livechat_end) + ) { + + + $livechat = $livechat->where(function($q) use ($request) { + $q->where('dCreateOn', '>=', $request->livechat_start) + ->where('dCreateOn', '<=', $request->livechat_end); + }); + } + + if ($request->has('healthcare_id') && !empty($request->healthcare_id)) { + $livechat->where('nIDHealthCare', $request->healthcare_id); + } + + $livechats = $livechat->orderBy('dUpdateOn', 'DESC') + ->paginate(); + + return Helper::responseJson(Helper::paginateResources(ReportPhrResource::collection($livechats))); + } + + /** + * Show the form for creating a new resource. + * @return Renderable + */ + public function create() + { + return view('internal::create'); + } + + /** + * Store a newly created resource in storage. + * @param Request $request + * @return Renderable + */ + public function store(Request $request) + { + // + } + + /** + * Show the specified resource. + * @param int $id + * @return Renderable + */ + public function show($id) + { + return view('internal::show'); + } + + /** + * Show the form for editing the specified resource. + * @param int $id + * @return Renderable + */ + public function edit($id) + { + return view('internal::edit'); + } + + /** + * Update the specified resource in storage. + * @param Request $request + * @param int $id + * @return Renderable + */ + public function update(Request $request, $id) + { + // + } + + /** + * Remove the specified resource from storage. + * @param int $id + * @return Renderable + */ + public function destroy($id) + { + // + } + + // Function to determine if a string is serialized + private function is_serialized($string) { + // Check if string is empty + if (empty($string)) { + return false; + } + // Check for common serialized string patterns + return ($string == 'b:0;' || @unserialize($string) !== false); + } + + // Function to determine if a string is JSON + private function is_json($string) { + // Check if string is empty + if (empty($string)) { + return false; + } + // Decode JSON and check for errors + json_decode($string); + return (json_last_error() == JSON_ERROR_NONE); + } + + // Function to safely process the plan + private function processPlan($sPlan) { + // Check if the string is serialized + if ($this->is_serialized($sPlan)) { + $unserializedPlan = @unserialize($sPlan); + if ($unserializedPlan !== false || $sPlan === 'b:0;') { + return $unserializedPlan; + } + } + // Check if the string is JSON + elseif ($this->is_json($sPlan)) { + $jsonPlan = json_decode($sPlan, true); + if (json_last_error() == JSON_ERROR_NONE) { + return $jsonPlan; + } + } + // Treat as plain text if not serialized or JSON + return $sPlan; + } + + public function generateExcel(Request $request){ + Helper::setCustomPHPIniSettings(); + + $file_name = 'Data Report Riwayat Rekam Medis'; + // Membuat penulis entitas Spout + $writer = WriterEntityFactory::createXLSXWriter(); + // Membuka penulis untuk menulis ke file + $writer->openToFile(public_path('files/Report-Riwayat-Rekam-Medis.xlsx')); + $headerArray = [ + 'Healthcare', + 'Patient', + 'Doctor', + 'Speciality', + 'Date', + 'Keluhan (Subjective)', + 'Pemerikasan Fisik Online (Objective)', + 'Diagnosa (Assessment)', + 'Tata Laksana (Plan)', + ]; + // Sheet 1 + $writer->getCurrentSheet()->setName('Data'); + $headers_map_to_table_fields = $headerArray; + $headerRow = WriterEntityFactory::createRowFromArray($headers_map_to_table_fields); + $writer->addRow($headerRow); + $livechats = Livechat::query() + ->with([ 'user', 'doctor', 'doctor.user', 'doctor.user.detail', 'doctor.speciality', 'appointment', 'healthCare', 'summary']); + + if ($request->has('search')) { + $search = $request->search; + $livechats->where(function ($query) use ($search) { + $query->where('nID', $search) + ->orWhereHas('user', function ($detail) use ($search) { + $detail->where('sFirstName', 'LIKE', '%' . $search . '%') + ->orWhere('sLastName', 'LIKE', '%' . $search . '%'); + }) + ->orWhereHas('healthCare', function ($detail) use ($search) { + $detail->where('sHealthCare', 'LIKE', '%' . $search . '%'); + }); + }); + } + + if (($request->has('livechat_start') || $request->has('livechat_end')) + && !empty($request->livechat_start) + && !empty($request->livechat_end) + ) { + $livechats = $livechats->where(function($q) use ($request) { + $q->where('dCreateOn', '>=', $request->livechat_start) + ->where('dCreateOn', '<=', $request->livechat_end); + }); + } + + $livechats = $livechats->get(); + + if ($livechats){ + foreach ($livechats as $index => $row) { + $doctor_name = '-'; + if ($row->doctor && $row->doctor->user && $row->doctor->user->detail) { + $doctor_name = $row->doctor->user->detail->sTitlePrefix . ' ' . $row->doctor->user->fullname; + } + $speciality = $row->doctor->speciality->sSpesialis ?? '-'; + // Check if $row->summary and $row->summary->sPlan are set and not null + if (isset($row->summary) && isset($row->summary->sPlan)) { + $plan = $this->processPlan($row->summary->sPlan); + + if (is_array($plan)) { + // Mengubah array menjadi string dengan format yang diinginkan + $plans = implode(', ', array_map(function($item) { + return !empty($item['note']) ? $item['note'] : '-'; + }, $plan)); + } else { + // Jika $plan bukan array, set $plans menjadi tanda '-' + $plans = '-'; + } + } else { + // If $row->summary or $row->summary->sPlan are not set, set $plans to '-' + $plans = '-'; + } + + $rowData = [ + $row->healthCare ? $row->healthCare->sHealthCare : '-', + $row->user ? $row->user->sFirstName : '-', + $doctor_name, + $speciality, + $row->summary ? Carbon::parse($row->dCreateOn)->format('Y-m-d H:i:s') : '-', + $row->summary ? $row->summary->sSubjective : '-', + $row->summary ? $row->summary->sObjective : '-', + $row->summary ? $row->summary->sAssessment : '-', + // is_array($plan) ? implode(', ', $plan[0]) : $plan, // Handle arrays from unserialized or JSON data + $row->summary ? $plans : '-', + ]; + + // Create a row from the array and add it to the writer + $rowEntity = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($rowEntity); + } + } + $writer->close(); + return Helper::responseJson([ + 'file_name' => "Data Riwayat Log " . date('Y-m-d h:i:s'), + "file_url" => url('files/Report-Riwayat-Rekam-Medis.xlsx') + ]); + } +} diff --git a/Modules/Internal/Http/Controllers/Api/Linksehat/PrescriptionController.php b/Modules/Internal/Http/Controllers/Api/Linksehat/PrescriptionController.php new file mode 100644 index 00000000..91d50556 --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/Linksehat/PrescriptionController.php @@ -0,0 +1,240 @@ +toArray(); + $prescription = Prescription::query() + ->with(['livechat', 'user', 'items']); + if ($request->has('search')) { + $search = $request->search; + $prescription->where(function ($query) use ($search) { + $query->where('sDokterName', 'LIKE', '%' . $search . "%") + ->orWhere('sKodeResep', 'LIKE', '%' . $search . "%"); + }); + } + + if (($request->has('prescription_start') || $request->has('prescription_end')) + && !empty($request->prescription_start) + && !empty($request->prescription_end) + ) { + + + $prescription = $prescription->where(function($q) use ($request) { + $q->where('dTanggalResep', '>=', $request->prescription_start) + ->where('dTanggalResep', '<=', $request->prescription_end); + }); + } + + $prescriptions = $prescription->orderBy('dUpdateOn', 'DESC') + ->paginate(); + + return Helper::responseJson(Helper::paginateResources(ReportPrescriptionResource::collection($prescriptions))); + } + + /** + * Show the form for creating a new resource. + * @return Renderable + */ + public function create() + { + return view('internal::create'); + } + + /** + * Store a newly created resource in storage. + * @param Request $request + * @return Renderable + */ + public function store(Request $request) + { + // + } + + /** + * Show the specified resource. + * @param int $id + * @return Renderable + */ + public function show($id) + { + return view('internal::show'); + } + + /** + * Show the form for editing the specified resource. + * @param int $id + * @return Renderable + */ + public function edit($id) + { + return view('internal::edit'); + } + + /** + * Update the specified resource in storage. + * @param Request $request + * @param int $id + * @return Renderable + */ + public function update(Request $request, $id) + { + // + } + + /** + * Remove the specified resource from storage. + * @param int $id + * @return Renderable + */ + public function destroy($id) + { + // + } + + // Function to determine if a string is serialized + private function is_serialized($string) { + return ($string == 'b:0;' || @unserialize($string) !== false); + } + + // Function to determine if a string is JSON + private function is_json($string) { + json_decode($string); + return (json_last_error() == JSON_ERROR_NONE); + } + + // Function to safely process the plan + private function processPlan($sPlan) { + if ($this->is_serialized($sPlan)) { + $unserializedPlan = @unserialize($sPlan); + if ($unserializedPlan !== false || $sPlan === 'b:0;') { + return $unserializedPlan; + } + } elseif ($this->is_json($sPlan)) { + $jsonPlan = json_decode($sPlan, true); + if (json_last_error() == JSON_ERROR_NONE) { + return $jsonPlan; + } + } + return $sPlan; // Treat as plain text if not serialized or JSON + } + + public function generateExcel(Request $request) + { + Helper::setCustomPHPIniSettings(); + + $file_name = 'Data Report Resep Online'; + // Membuat penulis entitas Spout + $writer = WriterEntityFactory::createXLSXWriter(); + // Membuka penulis untuk menulis ke file + $writer->openToFile(public_path('files/Report-Resep-Online.xlsx')); + + $headerArray = [ + 'No', + 'Prescription Code', + 'Date Consultation', + 'Patient', + 'Doctor', + 'Jenis Obat (Drugs)', + 'Jumlah Obat (QTY)', + 'Cara Minum Obat', + ]; + + // Sheet 1 + $writer->getCurrentSheet()->setName('Data'); + $headerRow = WriterEntityFactory::createRowFromArray($headerArray); + $writer->addRow($headerRow); + + // Query prescription data + $prescriptionQuery = Prescription::query() + ->with(['livechat', 'user', 'items']); + + if ($request->has('search')) { + $search = $request->search; + $prescriptionQuery->where(function ($query) use ($search) { + $query->where('sDokterName', 'LIKE', '%' . $search . '%') + ->orWhere('sKodeResep', 'LIKE', '%' . $search . '%'); + }); + } + + if ($request->has('prescription_start') && $request->has('prescription_end') && + !empty($request->prescription_start) && !empty($request->prescription_end)) { + $prescriptionQuery->whereBetween('dTanggalResep', [$request->prescription_start, $request->prescription_end]); + } + + $prescriptions = $prescriptionQuery->get(); + + if ($prescriptions->isNotEmpty()) { + $no = 1; + foreach ($prescriptions as $index => $row) { + if ($row->items->isNotEmpty()) { + $rowData = [ + $no++, + $row->sKodeResep ?? '-', + $row->dTanggalResep ? Carbon::parse($row->dTanggalResep)->format('Y-m-d') : '-', + $row->user->name ?? '-', + $row->sDokterName ?? '-', + ]; + + // Create a row from the array and add it to the writer + $rowEntity = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($rowEntity); + foreach ($row->items as $item) { + $rowSubData = [ + '', + '', + '', + '', + '', + $item->sItemName ?? '-', + $item->nQty ?? '-', + $item->sSigna ?? '-' + ]; + $subData = WriterEntityFactory::createRowFromArray($rowSubData); + $writer->addRow($subData); + } + } else { + $rowData = [ + $no++, + $row->sKodeResep ?? '-', + $row->dTanggalResep ? Carbon::parse($row->dTanggalResep)->format('Y-m-d') : '-', + $row->user->name ?? '-', + $row->sDokterName ?? '-', + ]; + // Create a row from the array and add it to the writer + $rowEntity = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($rowEntity); + } + } + } + + $writer->close(); + + return Helper::responseJson([ + 'file_name' => "Data Resep Online " . date('Y-m-d h:i:s'), + 'file_url' => url('files/Report-Resep-Online.xlsx') + ]); + } + +} diff --git a/Modules/Internal/Http/Controllers/Api/Linksehat/RujukanController.php b/Modules/Internal/Http/Controllers/Api/Linksehat/RujukanController.php new file mode 100644 index 00000000..a43414c9 --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/Linksehat/RujukanController.php @@ -0,0 +1,243 @@ +toArray(); + $rujukan = Rujukan::query() + ->with(['livechat', 'livechat.user']); + + if ($request->has('search')) { + $search = $request->search; + $rujukan->where(function ($query) use ($search) { + $query->where('nID', $search) + ->orWhereHas('user', function ($detail) use ($search) { + $detail->where('sFirstName', 'LIKE', '%' . $search . '%') + ->orWhere('sLastName', 'LIKE', '%' . $search . '%'); + }) + ->orWhereHas('healthCare', function ($detail) use ($search) { + $detail->where('sHealthCare', 'LIKE', '%' . $search . '%'); + }); + }); + } + + if (($request->has('rujukan_start') || $request->has('rujukan_end')) + && !empty($request->rujukan_start) + && !empty($request->rujukan_end) + ) { + + + $rujukan = $rujukan->where(function($q) use ($request) { + $q->where('dCreateOn', '>=', $request->rujukan_start) + ->where('dCreateOn', '<=', $request->rujukan_end); + }); + } + + if ($request->has('healthcare_id') && !empty($request->healthcare_id)) { + $rujukan->where('nIDHealthCare', $request->healthcare_id); + } + + $rujukans = $rujukan->orderBy('dUpdateOn', 'DESC') + ->paginate(); + + return Helper::responseJson(Helper::paginateResources(ReportRujukanResource::collection($rujukans))); + } + + /** + * Show the form for creating a new resource. + * @return Renderable + */ + public function create() + { + return view('internal::create'); + } + + /** + * Store a newly created resource in storage. + * @param Request $request + * @return Renderable + */ + public function store(Request $request) + { + // + } + + /** + * Show the specified resource. + * @param int $id + * @return Renderable + */ + public function show($id) + { + return view('internal::show'); + } + + /** + * Show the form for editing the specified resource. + * @param int $id + * @return Renderable + */ + public function edit($id) + { + return view('internal::edit'); + } + + /** + * Update the specified resource in storage. + * @param Request $request + * @param int $id + * @return Renderable + */ + public function update(Request $request, $id) + { + // + } + + /** + * Remove the specified resource from storage. + * @param int $id + * @return Renderable + */ + public function destroy($id) + { + // + } + + // Function to determine if a string is serialized + private function is_serialized($string) { + return ($string == 'b:0;' || @unserialize($string) !== false); + } + + // Function to determine if a string is JSON + private function is_json($string) { + json_decode($string); + return (json_last_error() == JSON_ERROR_NONE); + } + + // Function to safely process the plan + private function processPlan($sPlan) { + if ($this->is_serialized($sPlan)) { + $unserializedPlan = @unserialize($sPlan); + if ($unserializedPlan !== false || $sPlan === 'b:0;') { + return $unserializedPlan; + } + } elseif ($this->is_json($sPlan)) { + $jsonPlan = json_decode($sPlan, true); + if (json_last_error() == JSON_ERROR_NONE) { + return $jsonPlan; + } + } + return $sPlan; // Treat as plain text if not serialized or JSON + } + + public function generateExcel(Request $request){ + Helper::setCustomPHPIniSettings(); + + $file_name = 'Data Report Riwayat Rekam Medis'; + // Membuat penulis entitas Spout + $writer = WriterEntityFactory::createXLSXWriter(); + // Membuka penulis untuk menulis ke file + $writer->openToFile(public_path('files/Report-Riwayat-Rekam-Medis.xlsx')); + $headerArray = [ + 'Healthcare', + 'Patient', + 'Doctor', + 'Speciality', + 'Date', + 'Keluhan (Subjective)', + 'Pemerikasan Fisik Online (Objective)', + 'Diagnosa (Assessment)', + 'Tata Laksana (Plan)', + ]; + // Sheet 1 + $writer->getCurrentSheet()->setName('Data'); + $headers_map_to_table_fields = $headerArray; + $headerRow = WriterEntityFactory::createRowFromArray($headers_map_to_table_fields); + $writer->addRow($headerRow); + $rujukans = Livechat::query() + ->with([ 'user', 'doctor', 'doctor.user', 'doctor.user.detail', 'doctor.speciality', 'appointment', 'healthCare', 'summary']); + + if ($request->has('search')) { + $search = $request->search; + $rujukans->where(function ($query) use ($search) { + $query->where('nID', $search) + ->orWhereHas('user', function ($detail) use ($search) { + $detail->where('sFirstName', 'LIKE', '%' . $search . '%') + ->orWhere('sLastName', 'LIKE', '%' . $search . '%'); + }) + ->orWhereHas('healthCare', function ($detail) use ($search) { + $detail->where('sHealthCare', 'LIKE', '%' . $search . '%'); + }); + }); + } + + if (($request->has('rujukan_start') || $request->has('rujukan_end')) + && !empty($request->rujukan_start) + && !empty($request->rujukan_end) + ) { + $rujukans = $rujukans->where(function($q) use ($request) { + $q->where('dCreateOn', '>=', $request->rujukan_start) + ->where('dCreateOn', '<=', $request->rujukan_end); + }); + } + + $rujukans = $rujukans->get(); + + if ($rujukans){ + foreach ($rujukans as $index => $row) { + $doctor_name = '-'; + if ($row->doctor && $row->doctor->user && $row->doctor->user->detail) { + $doctor_name = $row->doctor->user->detail->sTitlePrefix . ' ' . $row->doctor->user->fullname; + } + $speciality = $row->doctor->speciality->sSpesialis ?? '-'; + // Process the plan + $plan = '-'; + if ($row->summary && $row->summary->sPlan) { + $plan = $this->processPlan($row->summary->sPlan); + } + + $rowData = [ + $row->healthCare ? $row->healthCare->sHealthCare : '-', + $row->user ? $row->user->sFirstName : '-', + $doctor_name, + $speciality, + $row->summary ? Carbon::parse($row->dCreateOn)->format('Y-m-d H:i:s') : '-', + $row->summary ? $row->summary->sSubjective : '-', + $row->summary ? $row->summary->sObjective : '-', + $row->summary ? $row->summary->sAssessment : '-', + is_array($plan) ? implode(', ', $plan) : $plan, // Handle arrays from unserialized or JSON data + ]; + + // Create a row from the array and add it to the writer + $rowEntity = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($rowEntity); + } + } + $writer->close(); + return Helper::responseJson([ + 'file_name' => "Data Riwayat Log " . date('Y-m-d h:i:s'), + "file_url" => url('files/Report-Riwayat-Rekam-Medis.xlsx') + ]); + } +} diff --git a/Modules/Internal/Http/Controllers/Api/RequestLogController.php b/Modules/Internal/Http/Controllers/Api/RequestLogController.php index 41364961..ce63e4f9 100755 --- a/Modules/Internal/Http/Controllers/Api/RequestLogController.php +++ b/Modules/Internal/Http/Controllers/Api/RequestLogController.php @@ -109,12 +109,11 @@ class RequestLogController extends Controller 'member_id' => 'required', 'service_code' => 'required', ]); - if ($request->member_id){ try { $code = !empty($this->getNextCode($request)) ? $this->getNextCode($request) : null; + $member = Member::find($request->member_id); - $memberValid = false; if ($member){ if (($member->members_effective_date <= date('Y-m-d')) && @@ -203,9 +202,13 @@ class RequestLogController extends Controller return Helper::responseJson(data: RequestLogShowResource::make($claimRequest)); } - public function diagnosis(){ + public function diagnosis(Request $request){ $icds = Icd::query() - ->get(); + ->when($request->search, function ($q, $search) { + $q->where('code', 'LIKE', "%".$search."%"); + $q->orWhere('name', 'LIKE', "%".$search."%"); + }) + ->paginate(); $manipulatedIcds = $icds->map(function ($icd) { // Contoh manipulasi, tambahkan atau ubah properti sesuai kebutuhan @@ -447,9 +450,17 @@ class RequestLogController extends Controller $requestLog->discharge_date = $request->discharge_date; } if (!empty($request->icdCodes)) { - $diagnosis = implode(',', $request->icdCodes); + $data = []; + if (count($request->icdCodes)>0){ + foreach($request->icdCodes as $code){ + array_push($data, $code['value']); + } + } + $diagnosis = implode(',', $data); $requestLog->diagnosis = $diagnosis; + } else { + $requestLog->diagnosis = ''; } if (!empty($request->status)) { $requestLog->status_final_log = $status; @@ -698,8 +709,8 @@ class RequestLogController extends Controller ]); if ($affectedRows === 0) { - $row['code_error'] = '500'; - $row['error'] = 'Gagal update karena data sudah ada '; + $row['code_error'] = '200'; + $row['error'] = 'Tidak ada data yang diedit'; $result_rows[] = $row; $failedRows[] = $row; } else { @@ -1038,12 +1049,12 @@ class RequestLogController extends Controller // $last_number = RequestLog::max('code'); // $next_number = empty($last_number) ? 1 : ((int) explode('-', $last_number)[2] + 1); // return self::makeCode($next_number); - $source = $request->source == 'client-portal' ? 'C' : 'H'; $organization = Organization::where(['id' => $request->organization_id, 'type' => 'hospital'])->first('code'); $provideCode = $organization ? $organization->code : ''; - $member = Member::with('currentCorporate')->where(['id' => $request->member_id])->first(); - + $member = Member::with(['currentCorporate','currentPolicy' ])->where(['id' => $request->member_id])->first(); + + $data = [ 'source' => $source, 'provideCode' => $provideCode, @@ -1052,11 +1063,12 @@ class RequestLogController extends Controller 'member_code' => $member->member_id, ]; + + $last_numeric_code = RequestLog::select(DB::raw('MAX(CAST(SUBSTRING_INDEX(code, ".", -1) AS SIGNED)) as max_numeric_code')) ->whereRaw('SUBSTRING_INDEX(code, ".", -1) REGEXP "^[0-9]+$"') ->value('max_numeric_code'); // $next_number = 1; - if ($last_numeric_code) { // // Jika ada kode sebelumnya, pecah kode dan tambahkan 1 ke angka terakhir // $parts = explode('-', $last_code); @@ -1155,4 +1167,10 @@ class RequestLogController extends Controller // Jika file tidak ditemukan di penyimpanan, kirim respons JSON gagal return Helper::responseJson(data: $request->toArray(), message: 'File deletion failed'); } + + public function cekphp(){ + phpinfo(); + } + + } diff --git a/Modules/Internal/Http/Controllers/Api/UserManagementController.php b/Modules/Internal/Http/Controllers/Api/UserManagementController.php index 98c3a97d..e2448a3e 100755 --- a/Modules/Internal/Http/Controllers/Api/UserManagementController.php +++ b/Modules/Internal/Http/Controllers/Api/UserManagementController.php @@ -22,6 +22,11 @@ class UserManagementController extends Controller $search = $request->get('search'); $query->where('name', 'like', "%{$search}%"); } + if($request->has('guard_name')) + { + $guard_name = $request->get('guard_name'); + $query->where('guard_name', '=', $guard_name); + } $userRole = $query->paginate(10); return Helper::paginateResources($userRole); } @@ -133,6 +138,13 @@ class UserManagementController extends Controller $search = $request->get('search'); $userAccess->where('name', 'like', "%{$search}%"); } + if($request->has('guard_name')) + { + $guard_name = $request->get('guard_name'); + $userAccess->whereHas('role', function ($query) use ($guard_name) { + $query->where('guard_name', $guard_name); + }); + } $userAccess = $userAccess->paginate(10); return Helper::paginateResources($userAccess); } diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index b93c14fb..f64000c2 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -8,6 +8,7 @@ use Modules\Internal\Http\Controllers\Api\BenefitController; use Modules\Internal\Http\Controllers\Api\CityController; use Modules\Internal\Http\Controllers\Api\ClaimController; use Modules\Internal\Http\Controllers\Api\ClaimRequestController; +use Modules\Internal\Http\Controllers\Api\KatalogDokterController; use Modules\Internal\Http\Controllers\Api\RequestLogController; use Modules\Internal\Http\Controllers\Api\RequestLogBenefitController; use Modules\Internal\Http\Controllers\Api\RequestLogMedicineController; @@ -29,6 +30,9 @@ use Modules\Internal\Http\Controllers\Api\DrugController; use Modules\Internal\Http\Controllers\Api\FormulariumController; use Modules\Internal\Http\Controllers\Api\FormulariumTemplateController; use Modules\Internal\Http\Controllers\Api\Linksehat\PaymentController; +use Modules\Internal\Http\Controllers\Api\Linksehat\HealthRecordController; +use Modules\Internal\Http\Controllers\Api\Linksehat\RujukanController; +use Modules\Internal\Http\Controllers\Api\Linksehat\PrescriptionController as PrescriptionControllerReport; use Modules\Internal\Http\Controllers\Api\LivechatController; use Modules\Internal\Http\Controllers\Api\MemberController; use Modules\Internal\Http\Controllers\Api\OptionController; @@ -72,14 +76,26 @@ Route::prefix('internal')->group(function () { Route::get('linksehat/payments', [PaymentController::class, 'index']); Route::get('linksehat/payments/generate-excel', [PaymentController::class, 'generateExcel']); + + + Route::get('diagnosis', [RequestLogController::class, 'diagnosis']); Route::get('drugs', [DrugController::class, 'drugList']); Route::get('units', [DrugController::class, 'unitList']); + Route::get('cekphp', [RequestLogController::class, 'cekphp']); Route::middleware('auth:sanctum')->group(function () { + // Report LMS + Route::get('linksehat/phr', [HealthRecordController::class, 'index']); + Route::get('linksehat/phr/generate-excel', [HealthRecordController::class, 'generateExcel']); + Route::get('linksehat/prescription', [PrescriptionControllerReport::class, 'index']); + Route::get('linksehat/prescription/generate-excel', [PrescriptionControllerReport::class, 'generateExcel']); + Route::get('linksehat/rujukan', [RujukanController::class, 'index']); + Route::get('linksehat/rujukan/generate-excel', [RujukanController::class, 'generateExcel']); + Route::post('logout', [AuthController::class, 'logout'])->name('logout'); Route::get('/user', function (Request $request) { return $request->user(); @@ -326,6 +342,12 @@ Route::prefix('internal')->group(function () { Route::get('doctorrating', [DoctorRatingController::class, 'index']); Route::get('doctorrating/{id}', [PrescriptionController::class, 'index']); + Route::get('get-doctorrating', [DoctorRatingController::class, 'getData']); + Route::get('export-doctorrating', [DoctorRatingController::class, 'export']); + + Route::get('get-dokter-katalog', [KatalogDokterController::class, 'getData']); + Route::get('export-dokter-katalog', [KatalogDokterController::class, 'export']); + Route::resource('doctors', DoctorController::class); Route::post('generate-log/{member_id}', [CorporateMemberController::class, 'generateLog']); diff --git a/Modules/Internal/Services/MemberEnrollmentService.php b/Modules/Internal/Services/MemberEnrollmentService.php index 1e4cff52..300f0917 100755 --- a/Modules/Internal/Services/MemberEnrollmentService.php +++ b/Modules/Internal/Services/MemberEnrollmentService.php @@ -1221,7 +1221,11 @@ class MemberEnrollmentService }; - $userLms = User::create( + $userLms = User::updateOrCreate( + [ + 'sPhone' => $row['telephone_mobile'], + 'sEmail' => str_replace(' ', '', $row['email']), + ], [ 'sFirstName' => $first_name, 'sLastName' => $middle_name . ' ' . $last_name, // Ubah ini dengan variabel yang sesuai dengan nama belakang (last name) @@ -1233,7 +1237,8 @@ class MemberEnrollmentService ); $nIDUser = $userLms->nID; - $userLmsDetail = UserDetail::create( + $userLmsDetail = UserDetail::updateOrCreate( + ['nIDUser' => $nIDUser], [ 'nIDUser' => $nIDUser, // 'dTanggalLahir' => $row['date_of_birth'], diff --git a/Modules/Internal/Transformers/ReportPhrResource.php b/Modules/Internal/Transformers/ReportPhrResource.php new file mode 100755 index 00000000..dde2d451 --- /dev/null +++ b/Modules/Internal/Transformers/ReportPhrResource.php @@ -0,0 +1,72 @@ +doctor && $this->doctor->user && $this->doctor->user->detail) { + $doctor_name = $this->doctor->user->detail->sTitlePrefix . ' ' . $this->doctor->user->fullname; + } + + // Process the plan + $plan = []; + if ($this->summary && $this->summary->sPlan) { + $plan = $this->processPlan($this->summary->sPlan); + } + + $data = [ + 'id' => $this->nID, + 'healthcare' => $this->healthCare ? $this->healthCare->sHealthCare : null, + 'patient_name' => $this->user ? $this->user->sFirstName : null, + 'doctor_name' => $doctor_name, + 'specialis' => $this->doctor ? $this->doctor->speciality->sSpesialis : null, + 'date_consultation' => $this->summary ? Carbon::parse($this->dCreateOn)->format('Y-m-d H:i:s') : null , + 'subject' => $this->summary ? $this->summary->sSubjective : null, + 'object' => $this->summary ? $this->summary->sObjective : null, + 'assessment' => $this->summary ? $this->summary->sAssessment : null, + 'plan' => $plan, + ]; + + return $data; + } + + // Function to determine if a string is serialized + private function is_serialized($string) { + return ($string == 'b:0;' || @unserialize($string) !== false); + } + + // Function to determine if a string is JSON + private function is_json($string) { + json_decode($string); + return (json_last_error() == JSON_ERROR_NONE); + } + + // Function to safely process the plan + private function processPlan($sPlan) { + if ($this->is_serialized($sPlan)) { + $unserializedPlan = @unserialize($sPlan); + if ($unserializedPlan !== false || $sPlan === 'b:0;') { + return $unserializedPlan; + } + } elseif ($this->is_json($sPlan)) { + $jsonPlan = json_decode($sPlan, true); + if (json_last_error() == JSON_ERROR_NONE) { + return $jsonPlan; + } + } + return $sPlan; // Treat as plain text if not serialized or JSON + } +} diff --git a/Modules/Internal/Transformers/ReportPrescriptionResource.php b/Modules/Internal/Transformers/ReportPrescriptionResource.php new file mode 100644 index 00000000..baefd72f --- /dev/null +++ b/Modules/Internal/Transformers/ReportPrescriptionResource.php @@ -0,0 +1,32 @@ +user ? $this->user->sFirstName .' '. $this->user->sLastName : '-'; + + $data = [ + 'id' => $this->nID, + 'patient_name' => $patientName, + 'livechat' => $this->livechat, + 'prescription_code' => $this->sKodeResep, + 'date_consultation' => $this->dTanggalResep ? Carbon::parse($this->dTanggalResep)->format('Y-m-d H:i:s') : null, + 'doctor_name' => $this->sDokterName ? $this->sDokterName : '-', + 'items' => $this->items ? $this->items : [], + ]; + + return $data; + } +} diff --git a/Modules/Internal/Transformers/ReportRujukanResource.php b/Modules/Internal/Transformers/ReportRujukanResource.php new file mode 100644 index 00000000..3ad38f6b --- /dev/null +++ b/Modules/Internal/Transformers/ReportRujukanResource.php @@ -0,0 +1,46 @@ +livechat->doctor && $this->livechat->doctor->user && $this->livechat->doctor->user->detail) { + $doctor_name = $this->livechat->doctor->user->detail->sTitlePrefix . ' ' . $this->livechat->doctor->user->fullname; + } + + $diagnosis = null; + $plan = null; + + if ($this->livechat->summary){ + $diagnosis = $this->livechat->summary->sAssessment; + $plan = $this->livechat->summary->sPlan ? unserialize($this->livechat->summary->sPlan) : $this->livechat->summary->sPlan; + } + // dd($this->livechat->summary); + $data = [ + 'id' => $this->nID, + 'no_rujukan' => $this->sNoRujukan ? $this->sNoRujukan : null, + 'patient_name' => $this->livechat ? $this->livechat->user->sFirstName : null, + 'healthcare' => $this->healthcare ? $this->healthcare->sHealthCare : null, + 'doctor_name' => $doctor_name, + 'departement' => $this->sDepartement ? $this->sDepartement : null, + 'date_rujukan' => $this->dCreatedOn ? Carbon::parse($this->dCreateOn)->format('Y-m-d H:i:s') : null , + 'diagnosa' => $diagnosis, + 'plan' => $plan, + ]; + + return $data; + } +} diff --git a/Modules/Internal/Transformers/RequestLogShowResource.php b/Modules/Internal/Transformers/RequestLogShowResource.php index 88bd4c54..dc46b978 100755 --- a/Modules/Internal/Transformers/RequestLogShowResource.php +++ b/Modules/Internal/Transformers/RequestLogShowResource.php @@ -9,6 +9,7 @@ use App\Models\CorporateService; use App\Models\RequestLogBenefit; use App\Models\RequestLogMedicine; use App\Models\Organization; +use App\Models\Benefit; use App\Models\Exclusion; use App\Models\ClaimRequest; use App\Models\Icd; @@ -33,7 +34,7 @@ class RequestLogShowResource extends JsonResource $planMember = MemberPlan::where('member_id', $member_id)->get('plan_id'); $planId = Plan::whereIn('id', $planMember)->where('service_code', $requestLog['service_code'])->first(); - $benefit = CorporateBenefit::with('benefit')->where('plan_id', $planId->id)->get()->toArray(); + $benefit = CorporateBenefit::with(['benefit', 'plan'])->where('plan_id', $planId->id)->get()->toArray(); $benefitDetailLog = RequestLogBenefit::with('benefit')->where('request_log_id', $requestLog['id'])->get()->toArray(); $medicineDetailLog = RequestLogMedicine::where('request_log_id', $requestLog['id'])->get()->toArray(); $provider = Organization::where('id', $requestLog['organization_id'])->first(); @@ -61,14 +62,19 @@ class RequestLogShowResource extends JsonResource $providerName = '-'; } + // Benefit Data $benefitData = []; - if (count($benefit)){ foreach($benefit as $data){ + $data['benefit']['plan_id'] = $data['plan_id']; + $data['benefit']['limit_amount'] = $data['limit_amount']; + $data['benefit']['family_plan'] = $planId->family_plan; + $data['benefit']['max_frequency_period'] = $data['max_frequency_period']; + $data['benefit']['limit_amount_plan'] = $data['plan']['limit_rules']; + $data['benefit']['family_plan_plans'] = $data['plan']['family_plan']; array_push($benefitData, $data['benefit']); } } - // Medicine $medicineData = []; if (count($medicineDetailLog)){ @@ -106,10 +112,12 @@ class RequestLogShowResource extends JsonResource $diagnosis = explode(',', $requestLog['diagnosis']); $icd = Icd::query() ->whereIn('code', $diagnosis) - ->select('code', 'name') + ->select('code as value', 'name as label') ->get(); } + $memberUsage = Helper::getUsageMember($corporateId, $requestLog['member']['id'], $benefitData); + // dd($memberLimitUsage); $data = [ 'id' => $requestLog['id'], 'code' => $requestLog['code'], @@ -125,6 +133,7 @@ class RequestLogShowResource extends JsonResource 'gender' => $requestLog['member']['gender'], 'marital_status' => $requestLog['member']['status_marital'], 'member_type' => Helper::memberType($requestLog['member']['record_type']), + 'member_usage_benefit' => json_decode($memberUsage), 'principal_id' => $requestLog['member']['principal_id'] ? $requestLog['member']['principal_id'] : '-', 'principal_name' => $requestLog['member']['principal_id'] ? Helper::principalName($requestLog['member']['principal_id']) : '-', 'relation_with_principal' => Helper::relationWithPrincipal($requestLog['member']['relation_with_principal']), @@ -151,6 +160,7 @@ class RequestLogShowResource extends JsonResource 'reason' => $requestLog['reason'], 'diagnosis' => $icd, 'is_reversal' => $isReversal, // untuk penjagaan, jika true tidak bisa di edit/hapus lagi + ]; diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 10ab74d7..db64b947 100755 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -9,7 +9,9 @@ use Symfony\Component\HttpFoundation\Response; use PHPMailer\PHPMailer\PHPMailer; use Illuminate\Support\Facades\DB; use App\Models\Member; +use App\Models\RequestLogBenefit; use App\Models\User; +use App\Models\CorporatePolicy; use App\Models\Service; use App\Models\Icd; use DateTime; @@ -534,5 +536,138 @@ class Helper } } + public static function getUsageMember($corporateId, $id, $benefitData) { + // Menghitung jumlah total approved dan mengkategorikan berdasarkan benefit_id + $amountApprovedByBenefit = []; + foreach($benefitData as $benefit) { + if ($benefit['limit_amount'] != 999999999 || $benefit['limit_amount_plan'] != 999999999) { + if ($benefit['family_plan'] == 'S' || $benefit['family_plan'] == 'F'){ // Menghitung usage per family + $memberFamilys = Helper::getMemberFamily($id); + $usageLogs = 0; + + $today = date('Y-m-d'); + $periods = [ + 0 => [ + 'start' => '2024-01-05', + 'end' => '2025-01-05' + ], + 1 => [ + 'start' => $today, + 'end' => $today + ] + // Tambahkan kondisi lainnya jika diperlukan + ]; + + // if (array_key_exists($benefit['max_frequency_period'], $periods)) { + $usageLogs = RequestLogBenefit::query() + ->whereHas('requestLog', function ($query) use ($benefit, $memberFamilys, $periods) { + // $period = $periods[$benefit['max_frequency_period']]; + $query->where('benefit_id', $benefit['id']) + // ->where('submission_date', '>=', $period['start']) + // ->where('submission_date', '<=', $period['end']) + ->whereIn('member_id', $memberFamilys); + }) + ->sum('amount_approved'); + // } + // Mengkategorikan sum berdasarkan benefit_id + $amountApprovedByBenefit[$benefit['id']] = $usageLogs; + } else { // Menghitung usage permember + $usageLog = RequestLogBenefit::query() + ->whereHas('requestLog', function ($query) use ($benefit, $id) { + $query->where('benefit_id', $benefit['id']) + ->where('member_id', $id); + }) + ->sum('amount_approved'); + + // Mengkategorikan sum berdasarkan benefit_id + $amountApprovedByBenefit[$benefit['id']] = $usageLog; + } + } + } + + return json_encode($amountApprovedByBenefit); + + } + + public static function getLimitMemberService($corporateId, $id, $benefitData) { + // Menghitung jumlah total approved dan mengkategorikan berdasarkan benefit_id + $amountApprovedByBenefit = []; + foreach($benefitData as $benefit) { + if ($benefit['limit_amount'] != 999999999) { + if ($benefit['family_plan'] == 'S' || $benefit['family_plan'] == 'F'){ // Menghitung usage per family + $memberFamilys = Helper::getMemberFamily($id); + $usageLogs = 0; + + $today = date('Y-m-d'); + $periods = [ + 0 => [ + 'start' => '2024-01-05', + 'end' => '2025-01-05' + ], + 1 => [ + 'start' => $today, + 'end' => $today + ] + // Tambahkan kondisi lainnya jika diperlukan + ]; + + if (array_key_exists($benefit['max_frequency_period'], $periods)) { + $usageLogs = RequestLogBenefit::query() + ->whereHas('requestLog', function ($query) use ($benefit, $memberFamilys, $periods) { + $period = $periods[$benefit['max_frequency_period']]; + $query->where('benefit_id', $benefit['id']) + // ->where('submission_date', '>=', $period['start']) + // ->where('submission_date', '<=', $period['end']) + ->whereIn('member_id', $memberFamilys); + }) + ->sum('amount_approved'); + } + // Mengkategorikan sum berdasarkan benefit_id + $amountApprovedByBenefit[$benefit['id']] = $usageLogs; + } else { // Menghitung usage permember + $usageLog = RequestLogBenefit::query() + ->whereHas('requestLog', function ($query) use ($benefit, $id) { + $query->where('benefit_id', $benefit['id']) + ->where('member_id', $id); + }) + ->sum('amount_approved'); + + // Mengkategorikan sum berdasarkan benefit_id + $amountApprovedByBenefit[$benefit['id']] = $usageLog; + } + } + } + + return json_encode($amountApprovedByBenefit); + + } + + public static function getMemberFamily($memberId){ + // Mengambil data member berdasarkan ID + $member = Member::where('id', $memberId)->first(); + + // Memastikan member ditemukan + if ($member) { + // Mengambil keluarga berdasarkan member_id atau principal_id + if (!$member->principal_id){ + $memberFamily = Member::where(function($query) use ($member) { + $query->where('principal_id', $member->member_id) + ->orWhere('member_id', $member->member_id); + })->pluck('id')->toArray(); + } else { + $memberFamily = Member::where(function($query) use ($member) { + $query->where('principal_id', $member->principal_id) + ->orWhere('member_id', $member->principal_id) + ->orWhere('member_id', $member->member_id); + })->pluck('id')->toArray(); + } + // Mengembalikan data keluarga + return $memberFamily; + } + + // Jika member tidak ditemukan, mengembalikan array kosong + return []; + } + } diff --git a/app/Models/Corporate.php b/app/Models/Corporate.php index acf73614..ea1b69ed 100755 --- a/app/Models/Corporate.php +++ b/app/Models/Corporate.php @@ -23,7 +23,10 @@ class Corporate extends Model 'help_text', 'active', 'linking_rules', - 'automatic_linking' + 'automatic_linking', + 'phone', + 'phone_alarm_canter', + 'description_information', ]; protected $casts = [ diff --git a/app/Models/Drug.php b/app/Models/Drug.php index 84028837..1d6d77f0 100755 --- a/app/Models/Drug.php +++ b/app/Models/Drug.php @@ -28,6 +28,7 @@ class Drug extends Model 'status', 'price', 'active', + 'unit' ]; public function categories() diff --git a/app/Models/OLDLMS/Livechat.php b/app/Models/OLDLMS/Livechat.php index 19b35fa7..7f734c68 100755 --- a/app/Models/OLDLMS/Livechat.php +++ b/app/Models/OLDLMS/Livechat.php @@ -55,6 +55,12 @@ class Livechat extends Model return $this->belongsTo(User::class, 'nIDUser', 'nID'); } + public function userInsurance() + { + return $this->belongsTo(UserInsurance::class, 'nIDUser', 'nIDUser'); + } + + public function doctor() { return $this->belongsTo(Dokter::class, 'nIDDokter', 'nIDUser'); @@ -74,4 +80,12 @@ class Livechat extends Model public function summary(){ return $this->belongsTo(LivechatSummary::class, 'nID', 'nIDLivechat'); } + + public function prescription(){ + return $this->belongsTo(Prescription::class, 'nID', 'nIDLivechat'); + } + + public function rujukan(){ + return $this->belongsTo(Rujukan::class, 'nID', 'nIDLivechat'); + } } diff --git a/app/Models/OLDLMS/Prescription.php b/app/Models/OLDLMS/Prescription.php index 9c3252d8..0b82e8f1 100755 --- a/app/Models/OLDLMS/Prescription.php +++ b/app/Models/OLDLMS/Prescription.php @@ -52,4 +52,28 @@ class Prescription extends Model 'dTanggalResep' => 'datetime', ]; + public function user() + { + return $this->belongsTo(User::class, 'nIDUser', 'nID'); + } + + public function items() + { + return $this->hasMany(PrescriptionItem::class, 'nIDPrescription', 'nID'); + } + + public function livechat(){ + return $this->belongsTo(Livechat::class, 'nIDLivechat', 'nID'); + } + + public function order() + { + return $this->hasMany(PrescriptionItem::class, 'nIDPrescription', 'nID'); + } + + public function payment() + { + return $this->hasOne(PrescriptionOrder::class, 'nIDPrescription', 'nID'); + } + } diff --git a/app/Models/OLDLMS/PrescriptionItem.php b/app/Models/OLDLMS/PrescriptionItem.php index 5c289e28..d05d7942 100755 --- a/app/Models/OLDLMS/PrescriptionItem.php +++ b/app/Models/OLDLMS/PrescriptionItem.php @@ -50,4 +50,10 @@ class PrescriptionItem extends Model ]; protected $primaryKey = 'nID'; + + public function prescription() + { + return $this->belongsTo(Prescription::class, 'nIDPrescription', 'nID'); + } + } diff --git a/app/Models/OLDLMS/PrescriptionOrder.php b/app/Models/OLDLMS/PrescriptionOrder.php new file mode 100755 index 00000000..39011446 --- /dev/null +++ b/app/Models/OLDLMS/PrescriptionOrder.php @@ -0,0 +1,59 @@ + '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_orders'; + + // protected $appends = [ + // 'status_name', + // ]; + + protected $casts = [ + 'dTanggalResep' => 'datetime', + ]; + + protected $primaryKey = 'nID'; + + public function order() + { + return $this->belongsTo(Prescription::class, 'nIDPrescription', 'nIDPrescription'); + } + +} diff --git a/app/Models/OLDLMS/Relationship.php b/app/Models/OLDLMS/Relationship.php new file mode 100755 index 00000000..a3d30ef3 --- /dev/null +++ b/app/Models/OLDLMS/Relationship.php @@ -0,0 +1,25 @@ + 'Menunggu Konfirmasi', + // 1 => 'Diterima', + // 2 => 'Ditolak', + // 3 => 'Selesai', + // 4 => 'Expired', + // ]; + + const CREATED_AT = 'dCreateOn'; + const UPDATED_AT = 'dUpdateOn'; + const DELETED_AT = 'dDeleteOn'; + + protected $connection = 'oldlms'; + + protected $table = 'tx_rujukan'; + + protected $primaryKey = 'nID'; + + // protected $appends = [ + // 'status_name', + // ]; + + public function livechat(){ + return $this->belongsTo(Livechat::class, 'nIDLivechat', 'nID'); + } + + public function healthcare(){ + return $this->belongsTo(Healthcare::class, 'nIDHealthcare', 'nID'); + } + + +} diff --git a/app/Models/OLDLMS/User.php b/app/Models/OLDLMS/User.php index eae89864..73f785af 100755 --- a/app/Models/OLDLMS/User.php +++ b/app/Models/OLDLMS/User.php @@ -64,6 +64,11 @@ class User extends Authenticatable return $this->hasOne(UserDetail::class, 'nIDUser', 'nID'); } + public function relation() + { + return $this->hasOne(Relationship::class, 'nID', 'nIDHubunganKeluarga'); + } + public function insurances() { return $this->hasMany(UserInsurance::class, 'nIDUser', 'nID'); diff --git a/app/Models/Organization.php b/app/Models/Organization.php index 7a705843..6c3f7c73 100755 --- a/app/Models/Organization.php +++ b/app/Models/Organization.php @@ -19,6 +19,8 @@ class Organization extends Model 'part_of', 'main_address_id', 'corporate_id_partner', + 'phone', + 'email', ]; // public $with = [ diff --git a/app/Models/PrescriptionOrder.php b/app/Models/PrescriptionOrder.php new file mode 100644 index 00000000..4596cafb --- /dev/null +++ b/app/Models/PrescriptionOrder.php @@ -0,0 +1,11 @@ +leftJoin('members', 'request_logs.member_id', '=', 'members.id') + ->leftJoin('corporate_employees', 'request_logs.member_id', '=', 'corporate_employees.member_id') // ->joinMemberPlans('left') // ->joinPlans('left') // ->with(['currentPlan', 'person']) @@ -215,6 +216,7 @@ class CorporateMemberService // ->whereHas('currentCorporate', function (Builder $query) use ($corporateId) { // // $query->where('corporate_id', $corporateId); // }) + ->where('corporate_employees.corporate_id', $corporateId) ->when($request->input('search'), function (Builder $query, $search) { $query->where(function (Builder $query) use ($search) { $query->orWhere('members.member_id', 'like', "%" . $search . "%") @@ -261,8 +263,6 @@ class CorporateMemberService // ->selectRaw("(select sum(`claims`.`total_claim`) from `claims` where `members`.`id` = `claims`.`member_id` AND `claims`.`deleted_at` IS NULL) AS `claims_sum_total_claim`") ->groupBy('member_id') ->paginate($limit); - - } public function getAllMemberEmployeeData(int $corporateId, Request $request) diff --git a/database/migrations/2024_07_08_085748_add_column_to_corporates.php b/database/migrations/2024_07_08_085748_add_column_to_corporates.php new file mode 100755 index 00000000..51c19354 --- /dev/null +++ b/database/migrations/2024_07_08_085748_add_column_to_corporates.php @@ -0,0 +1,36 @@ +string('phone')->nullable(); + $table->string('phone_alarm_canter')->nullable(); + $table->text('description_information')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('corporates', function (Blueprint $table) { + $table->dropColumn('phone'); + $table->dropColumn('phone_alarm_canter'); + $table->dropColumn('description_information'); + }); + } +}; diff --git a/database/migrations/2024_08_21_114955_add_unit_to_drugs_table.php b/database/migrations/2024_08_21_114955_add_unit_to_drugs_table.php new file mode 100644 index 00000000..344e5cdf --- /dev/null +++ b/database/migrations/2024_08_21_114955_add_unit_to_drugs_table.php @@ -0,0 +1,32 @@ +string('unit')->nullable()->after('generic_name'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('drugs', function (Blueprint $table) { + $table->dropColumn('unit'); + }); + } +}; diff --git a/database/migrations/2024_08_28_171751_add_column_to_organizations.php b/database/migrations/2024_08_28_171751_add_column_to_organizations.php new file mode 100644 index 00000000..399c5209 --- /dev/null +++ b/database/migrations/2024_08_28_171751_add_column_to_organizations.php @@ -0,0 +1,34 @@ +string('phone')->nullable()->after('main_address_id'); + $table->string('email')->nullable()->after('phone'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('organizations', function (Blueprint $table) { + $table->dropColumn('phone'); + $table->dropColumn('email'); + }); + } +}; diff --git a/database/seeders/ApotekMandiriInhealtSeeder.php b/database/seeders/ApotekMandiriInhealtSeeder.php new file mode 100644 index 00000000..ca5158af --- /dev/null +++ b/database/seeders/ApotekMandiriInhealtSeeder.php @@ -0,0 +1,173 @@ +open($file_path); + + $cell_map = [ // Urutan kolom di excel + 'No', + 'Nama KOP', + 'Nama KLY', + 'Kode Provider', + 'Nama Provider', + 'Jalan', + 'KODEPOS', + 'OPERASIONAL DAY', + 'OPERASIONAL HOUR', + 'AVAILABLE Brozo', + 'Geo Tag' + ]; + + foreach ($reader->getSheetIterator() as $sheet) { + $previousCells = null; + foreach ($sheet->getRowIterator() as $index => $row) { + if ($index >= 4) { + $cells = $row->getCells(); + $data = []; + + // Check Merging + if ($cells[0]->getValue() == '' && $cells[1]->getValue() == '' && $cells[2]->getValue() == '' && $cells[3]->getValue() == '' && $cells[4]->getValue() == '' && $cells[5]->getValue() == '' && $cells[6]->getValue() == '' && $cells[9]->getValue() == '' && $cells[10]->getValue() == '' && $cells[11]->getValue() == '' && $cells[12]->getValue() == '') { + $data = [ + 'code' => $previousCells[3]->getValue(), + 'name' => $previousCells[4]->getValue(), + 'type' => 'hospital', + 'corporate_id_partner' => 8, // Mandiri Inhealth + 'phone' => $previousCells[11]->getValue(), + 'email' => isset($previousCells[12]) ? $previousCells[12]->getValue() : null, + 'operational_day' => $this->expandDaysRange($previousCells[7]->getValue(), [$cells[7]->getValue()]), + 'operational_hour' => $previousCells[8]->getValue() == "24 jam" ? "00:00-24:00" : $previousCells[8]->getValue(), + 'operational_day_other' => $cells[7]->getValue(), + 'operational_hour_other' => $cells[8]->getValue() + ]; + + // Update or create organization + $organization = Organization::updateOrCreate( + ['code' => $previousCells[3]->getValue()], + $data + ); + + + // Update or create address + $geo = str_replace("'", "", $previousCells[9]->getValue()); // Hilangkan tanda petik tunggal + $datageo = explode(",", $geo); + + // Pastikan $datageo memiliki dua elemen sebelum mencoba mengaksesnya + $lat = isset($datageo[0]) ? $datageo[0] : null; + $lng = isset($datageo[1]) ? $datageo[1] : null; + + $address = $organization->addresses()->updateOrCreate( + ['addressable_id' => $organization->id], + [ + 'use' => 'both', + 'type' => 'physical', + 'text' => $previousCells[5]->getValue(), + 'postal_code' => $previousCells[6]->getValue(), + 'lat' => $lat, + 'lng' => $lng, + ] + ); + + // Set main address id + $organization->main_address_id = $address->id; + $organization->save(); + } else { + $data = [ + 'code' => $cells[3]->getValue(), + 'name' => $cells[4]->getValue(), + 'type' => 'hospital', + 'corporate_id_partner' => 8, // Mandiri Inhealth + 'phone' => $cells[11]->getValue(), + 'email' => isset($cells[12]) ? $cells[12]->getValue() : null, + 'operational_day' => $this->expandDaysRange($cells[7]->getValue()), + 'operational_hour' => $cells[8]->getValue() == "24 jam" ? "00:00-24:00" : $cells[8]->getValue(), + ]; + + // Update or create organization + $organization = Organization::updateOrCreate( + ['code' => $cells[3]->getValue()], + $data + ); + + + // Update or create address + $geo = str_replace("'", "", $cells[9]->getValue()); // Hilangkan tanda petik tunggal + $datageo = explode(",", $geo); + + // Pastikan $datageo memiliki dua elemen sebelum mencoba mengaksesnya + $lat = isset($datageo[0]) ? $datageo[0] : null; + $lng = isset($datageo[1]) ? $datageo[1] : null; + + $address = $organization->addresses()->updateOrCreate( + ['addressable_id' => $organization->id], + [ + 'use' => 'both', + 'type' => 'physical', + 'text' => $cells[5]->getValue(), + 'postal_code' => $cells[6]->getValue(), + 'lat' => $lat, + 'lng' => $lng, + ] + ); + + // Set main address id + $organization->main_address_id = $address->id; + $organization->save(); + } + + $previousCells = $cells; + } + } + } + + $reader->close(); + + } + + private function expandDaysRange($input, $excludedDays = []) { + // Daftar hari dalam seminggu + $daysOfWeek = ["Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"]; + + // Pisahkan input berdasarkan tanda "-" + $range = array_map('trim', explode('-', $input)); + + if (count($range) == 2) { + // Cari posisi hari pertama dan hari terakhir di dalam array $daysOfWeek + $startIndex = array_search($range[0], $daysOfWeek); + $endIndex = array_search($range[1], $daysOfWeek); + + // Jika ditemukan, ambil rentang hari + if ($startIndex !== false && $endIndex !== false && $startIndex <= $endIndex) { + // Ambil hari-hari dalam rentang tersebut + $daysRange = array_slice($daysOfWeek, $startIndex, ($endIndex - $startIndex + 1)); + + // Jika ada hari yang ingin di-exclude, hapus dari $daysRange + if (!empty($excludedDays)) { + $daysRange = array_diff($daysRange, $excludedDays); + } + + // Gabungkan menjadi string dengan koma + return implode(',', $daysRange); + } else { + return "Hari tidak valid atau rentang tidak benar."; + } + } else { + return "Format input tidak sesuai."; + } + } +} diff --git a/database/seeders/NavigationSeeder.php b/database/seeders/NavigationSeeder.php index b5a38158..8581a634 100755 --- a/database/seeders/NavigationSeeder.php +++ b/database/seeders/NavigationSeeder.php @@ -170,9 +170,14 @@ class NavigationSeeder extends Seeder // ['title' => 'Prescription', 'path' => '/report/prescription'], [ 'title' => 'Doctor Rating', - 'path' => '/report/doctorrating', + 'path' => '/report/doctor-rating', 'permission' => 'report-doctor-rating' ], + [ + 'title' => 'Katalog Dokter', + 'path' => '/report/katalog-dokter', + 'permission' => 'report-katalog-dokter' + ] ], 'permission' => null ], @@ -261,30 +266,46 @@ class NavigationSeeder extends Seeder ], 'permission' => 'case-management-client-portal' ], + [ + 'title' => 'User Management', + 'children' => [ + [ + 'title' => 'User Role', + 'path' => '/user-role', + 'permission' => 'user-role-list-client-portal' + ], + [ + 'title' => 'User Access', + 'path' => '/user-access', + 'permission' => 'user-access-list-client-portal' + ] + ], + 'permission' => 'user-management-client-portal' + ] ]; foreach ($menuItems as $menuItemData) { $menuItem = Navigations::updateOrCreate([ 'title' => $menuItemData['title'], - 'permission' => $menuItemData['permission'] + 'permission' => $menuItemData['permission'] ?? null ], [ 'title' => $menuItemData['title'], 'path' => $menuItemData['path'] ?? null, - 'permission' => $menuItemData['permission'] + 'permission' => $menuItemData['permission'] ?? null ]); if (isset($menuItemData['children'])) { foreach ($menuItemData['children'] as $childData) { $menuItemChildren = Navigations::updateOrCreate([ 'title' => $childData['title'], - 'permission' => $childData['permission'] + 'permission' => $childData['permission'] ?? null ], [ 'title' => $childData['title'], 'path' => $childData['path'] ?? null, 'parent_id' => $menuItem->id, - 'permission' => $childData['permission'] + 'permission' => $childData['permission'] ?? null ]); } } diff --git a/database/seeders/PermissionTableSeeder.php b/database/seeders/PermissionTableSeeder.php index fa57a98e..232d8e91 100755 --- a/database/seeders/PermissionTableSeeder.php +++ b/database/seeders/PermissionTableSeeder.php @@ -70,7 +70,8 @@ class PermissionTableSeeder extends Seeder 'report-livechat-payment', 'report-doctor-rating', 'user-role-list', - 'user-access-list' + 'user-access-list', + 'report-katalog-dokter' ] ], ####################### CLIENT PORTAL ######################### @@ -86,6 +87,9 @@ class PermissionTableSeeder extends Seeder 'formularium-list-client-portal', 'case-management-client-portal', 'service-monitoring-limit-client-portal', + 'user-management-client-portal', + 'user-role-list-client-portal', + 'user-access-list-client-portal' ] ] ]; diff --git a/frontend/client-portal/src/@types/auth.ts b/frontend/client-portal/src/@types/auth.ts index a5d2236c..680ca4d7 100755 --- a/frontend/client-portal/src/@types/auth.ts +++ b/frontend/client-portal/src/@types/auth.ts @@ -26,7 +26,7 @@ export type JWTContextType = { isInitialized: boolean; user: AuthUser; method: 'jwt'; - login: (phoneOrEmail: string) => Promise; + login: (phoneOrEmail: string, password: string, rememberMe: boolean) => Promise; validateOtp: (phoneOrEmail: string, otp: string) => Promise logout: () => void; }; diff --git a/frontend/client-portal/src/components/nav-section/vertical/NavItem.tsx b/frontend/client-portal/src/components/nav-section/vertical/NavItem.tsx index 44826f7b..178cee99 100755 --- a/frontend/client-portal/src/components/nav-section/vertical/NavItem.tsx +++ b/frontend/client-portal/src/components/nav-section/vertical/NavItem.tsx @@ -21,7 +21,7 @@ export function NavItemRoot({ const renderContent = ( <> - + {/* */} {!isCollapse && ( <> diff --git a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx index 0ce83544..b0c9a8f0 100755 --- a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx +++ b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx @@ -1,11 +1,12 @@ import { createContext, ReactNode, useEffect, useReducer } from 'react'; // utils import axios from '../utils/axios'; -import { setSession, getSession } from '../utils/token'; +import { setSession, getSession, setUser, getUser, getCookie } from '../utils/token'; // @types import { ActionMap, AuthState, AuthUser, JWTContextType } from '../@types/auth'; // ---------------------------------------------------------------------- +import { Navigate, useLocation } from 'react-router-dom'; enum Types { Initial = 'INITIALIZE', @@ -19,7 +20,9 @@ type JWTAuthPayload = { isAuthenticated: boolean; user: AuthUser; }; - [Types.Login]: undefined; + [Types.Login]: { + user: AuthUser; + }; [Types.ValidateOtp]: { user: AuthUser; }; @@ -45,8 +48,8 @@ const JWTReducer = (state: AuthState, action: JWTActions) => { case 'LOGIN': return { ...state, - isAuthenticated: false, - user: null, + isAuthenticated: true, + user: action.payload.user, }; case 'VALIDATE-OTP': return { @@ -75,7 +78,46 @@ type AuthProviderProps = { function AuthProvider({ children }: AuthProviderProps) { const [state, dispatch] = useReducer(JWTReducer, initialState); + let location = useLocation(); const accessToken = getSession(); + // useEffect(() => { + // (async () => { + // try { + // // const accessToken = getSession(); + + // if (accessToken) { + // setSession(accessToken); + + // const response = await axios.get('/user'); + // const user = response.data; + + // dispatch({ + // type: Types.Initial, + // payload: { + // isAuthenticated: true, + // user, + // }, + // }); + // } else { + // dispatch({ + // type: Types.Initial, + // payload: { + // isAuthenticated: false, + // user: null, + // }, + // }); + // } + // } catch (err) { + // dispatch({ + // type: Types.Initial, + // payload: { + // isAuthenticated: false, + // user: null, + // }, + // }); + // } + // })(); + // }, [accessToken]); useEffect(() => { (async () => { @@ -116,12 +158,28 @@ function AuthProvider({ children }: AuthProviderProps) { })(); }, [accessToken]); - const login = async (phoneOrEmail: string) => + + const headers = { + headers: { + 'Accept': 'application/json', + 'Content-Type' : 'application/json', + 'Accept-Language': localStorage.getItem('currentLocale') ?? 'id-ID', + }, + }; + + const login = async (phoneOrEmail: string, password: string, remember:boolean) => axios - .post('/login', { phoneOrEmail }) - .then(() => { + .post('/login', { phoneOrEmail, password }, headers) + .then((response) => { + const { user, token } = response.data.data; + setSession(token); + setUser(user); + dispatch({ type: Types.Login, + payload: { + user, + } }); }) .catch((error) => { diff --git a/frontend/client-portal/src/pages/Dashboard/Index.tsx b/frontend/client-portal/src/pages/Dashboard/Index.tsx index 8bae1f72..f967e650 100755 --- a/frontend/client-portal/src/pages/Dashboard/Index.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Index.tsx @@ -1,5 +1,5 @@ // @mui -import { Box,CardContent,Button, Container, Grid, styled, Typography, Card, Stack } from '@mui/material'; +import { Box, Button, Container, Grid, styled, Typography, Card, Stack } from '@mui/material'; // hooks import useSettings from '../../hooks/useSettings'; // components @@ -9,6 +9,7 @@ import useAuth from '../../hooks/useAuth'; import SomethingUsage from '../../sections/dashboard/SomethingUsage'; import { fCurrency } from '../../utils/formatNumber'; import AccountBalanceWalletIcon from '@mui/icons-material/AccountBalanceWallet'; +import SendIcon from '@mui/icons-material/Send'; import TrendingUpIcon from '@mui/icons-material/TrendingUp'; import MonetizationOnIcon from '@mui/icons-material/MonetizationOn'; import { useContext, useEffect, useState } from 'react'; @@ -20,32 +21,29 @@ import { useNavigate, useParams } from 'react-router-dom'; // ---------------------------------------------------------------------- export default function Dashboard() { - - const navigate = useNavigate(); + const navigate = useNavigate(); const { themeStretch } = useSettings(); const { user } = useAuth(); - - const checkIfNameExists = (name) => { - return user.user.permissions.some(item => item.name === name); - }; + return user.user.permissions.some((item) => item.name === name); + }; - const nameToCheck = 'dashboard-list-client-portal'; + const nameToCheck = 'dashboard-list-client-portal'; + const doesNameExist = checkIfNameExists(nameToCheck); + useEffect(() => { const doesNameExist = checkIfNameExists(nameToCheck); - useEffect(() => { - const doesNameExist = checkIfNameExists(nameToCheck); - if (!doesNameExist) { - navigate('/corporate'); - } - }, [nameToCheck, user, navigate]); -// const loadSomething = () => { -// axios.get('/user') -// }; + if (!doesNameExist) { + navigate('/corporate'); + } + }, [nameToCheck, user, navigate]); + // const loadSomething = () => { + // axios.get('/user') + // }; const Wallet = styled(AccountBalanceWalletIcon)(({ theme }) => ({ - color: 'orange', + color: 'rgba(249, 131, 124, 1)', marginRight: theme.spacing(1), })); @@ -54,8 +52,8 @@ export default function Dashboard() { marginRight: theme.spacing(1), })); - const Monet = styled(MonetizationOnIcon)(({ theme }) => ({ - color: 'orange', + const Send = styled(SendIcon)(({ theme }) => ({ + color: 'rgba(243, 204, 92, 1)', marginRight: theme.spacing(1), })); @@ -74,32 +72,50 @@ export default function Dashboard() { })); const DefaultCard = styled(Card)(({ theme }) => ({ + boxShadow: theme.shadows[3], // Menggunakan bayangan standar dari tema + padding: theme.spacing(3), + color: theme.palette.text.primary, + backgroundColor: theme.palette.background.neutral, // Latar belakang putih + })); + + const CardContent = styled(Card)(({ theme }) => ({ boxShadow: theme.shadows[3], // Menggunakan bayangan standar dari tema padding: theme.spacing(3), color: theme.palette.text.primary, backgroundColor: theme.palette.background.paper, // Latar belakang putih -})); -const { corporateValue } = useContext(UserCurrentCorporateContext); + })); -const [depositData, setDepositData] = useState({ deposit: 0, limit: 0, usage: 0 }); + const { corporateValue } = useContext(UserCurrentCorporateContext); - useEffect(() => { - const fetchDepositData = async () => { - try { - const response = await axios.get(`${corporateValue}/get-deposits`); - setDepositData(response.data); - } catch (error) { - console.error('Failed to fetch deposit data:', error); - } - }; + const [depositData, setDepositData] = useState({ + deposit: 0, + limit: 0, + usage: 0, + minimal_deposit_percentage: 0, + minimal_deposit_net: 0, + minimal_alert_percentage: 0, + minimal_alert_net: 0, + minimal_stop_service_net: 0, + minimal_stop_service_percentage: 0, + }); - fetchDepositData(); - }, [corporateValue]); + useEffect(() => { + const fetchDepositData = async () => { + try { + const response = await axios.get(`${corporateValue}/get-deposits`); + setDepositData(response.data); + } catch (error) { + console.error('Failed to fetch deposit data:', error); + } + }; - const handleGoBack = () => { - // Logic untuk kembali ke halaman sebelumnya atau halaman utama - navigate('/corporate') - }; + fetchDepositData(); + }, [corporateValue]); + + const handleGoBack = () => { + // Logic untuk kembali ke halaman sebelumnya atau halaman utama + navigate('/corporate'); + }; return ( @@ -108,67 +124,143 @@ const [depositData, setDepositData] = useState({ deposit: 0, limit: 0, usage: 0 Dashboard {doesNameExist ? ( - - - {/* */} - + + + + + - - - {fCurrency(depositData.deposit)} - - - Deposit + + + {fCurrency(depositData.deposit)} + + + Deposit + + + + + • Minimal Deposit {depositData.minimal_deposit_percentage}% + + + {fCurrency(depositData.minimal_deposit_net)} + + + + + • Stop Service {depositData.minimal_stop_service_percentage}% + + + {fCurrency(depositData.minimal_stop_service_net)} + + + + + • Reminder Alert Level {depositData.minimal_alert_percentage}% + + + {fCurrency(depositData.minimal_alert_net)} + + + + - - - - + + - - - {fCurrency(depositData.limit)} - - - Limit + + + {fCurrency(depositData.usage)} + + + This Year Usage + + - - - - - - - - {fCurrency(depositData.usage)} - - - This Year Usage - - - + + - ):( - - - Maaf, halaman ini tidak bisa diakses atau tidak ada. - - - + + ) : ( + + + Maaf, halaman ini tidak bisa diakses atau tidak ada. + + + )} - ); diff --git a/frontend/client-portal/src/pages/EmployeeData/Index.tsx b/frontend/client-portal/src/pages/EmployeeData/Index.tsx index e69a3176..5592621b 100755 --- a/frontend/client-portal/src/pages/EmployeeData/Index.tsx +++ b/frontend/client-portal/src/pages/EmployeeData/Index.tsx @@ -16,7 +16,7 @@ export default function Drugs() { diff --git a/frontend/client-portal/src/pages/EmployeeData/List.tsx b/frontend/client-portal/src/pages/EmployeeData/List.tsx index 8b5ed293..03d8d7d2 100755 --- a/frontend/client-portal/src/pages/EmployeeData/List.tsx +++ b/frontend/client-portal/src/pages/EmployeeData/List.tsx @@ -17,6 +17,8 @@ import Typography from '@mui/material/Typography'; import TableMoreMenu from '../../components/table/TableMoreMenu'; import VisibilityOutlinedIcon from '@mui/icons-material/VisibilityOutlined'; import Label from '../../components/Label'; + +import DownloadIcon from '@mui/icons-material/Download'; import { enqueueSnackbar } from 'notistack'; export default function List() { @@ -167,6 +169,37 @@ export default function List() { }, ]; /* -------------------------------------------------------------------------- */ + // Download E-Card + async function handleDownloadEcard(member_id: any, fullName:any) { + axios + .get(corporateValue+`/view_card/${member_id}`, { + responseType: 'blob', + }) + .then((response) => { + window.open(URL.createObjectURL(response.data)); + }) + .catch((response) => { + enqueueSnackbar(response.message, { variant: 'error' }); + }); + + // return axios + // .get(corporateValue+`/download-ecard/${member_id}`, { + // responseType: 'blob', + // }) + // .then((response) => { + // const namaFile = 'Ecard - '+fullName+".pdf"; + // const url = URL.createObjectURL(response.data); + // const link = document.createElement('a'); + // link.href = url; + // link.setAttribute('download', namaFile); + // document.body.appendChild(link); + // link.click(); + // document.body.removeChild(link); + // }) + // .catch((error) => { + // enqueueSnackbar(error.message, { variant: 'error' }); + // }); + } useEffect(() => { (async () => { @@ -182,7 +215,6 @@ export default function List() { const response = await axios.get(`${corporateValue}/members?type=employee-data`, { params: { ...parameters }, }); - setSearchParams(parameters); setData( response.data.data.map((obj: any) => ({ @@ -205,6 +237,10 @@ export default function List() { View + handleDownloadEcard(obj.id, obj.fullName)}> + + Download E-card + } /> diff --git a/frontend/client-portal/src/pages/Master/FormulariumV2/Index.tsx b/frontend/client-portal/src/pages/Master/FormulariumV2/Index.tsx index 2a25563b..7f81542c 100755 --- a/frontend/client-portal/src/pages/Master/FormulariumV2/Index.tsx +++ b/frontend/client-portal/src/pages/Master/FormulariumV2/Index.tsx @@ -14,7 +14,7 @@ export default function MasterFormularium() { heading={pageTitle} links={[ { - name: "Master", + name: "Case Management", href: "/master/formularium-template-v2" }, { diff --git a/frontend/client-portal/src/pages/UserManagement/UserAccess/CreateUpdate.tsx b/frontend/client-portal/src/pages/UserManagement/UserAccess/CreateUpdate.tsx new file mode 100755 index 00000000..44498f35 --- /dev/null +++ b/frontend/client-portal/src/pages/UserManagement/UserAccess/CreateUpdate.tsx @@ -0,0 +1,63 @@ + +import { useNavigate, useParams } from "react-router-dom"; +import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs"; +import Page from "../../../components/Page"; +import {useContext, useEffect, useMemo, useState } from 'react'; +import axios from '../../../utils/axios'; +import UserAccessForm from './Form'; +import { Role, UserAccess } from '../../../@types/user'; + + + +export default function UserAccessCreate() { + const { id } = useParams(); + const [ currentUserAccess, setCurrentUserAccess ] = useState(); + const [ roles, setRole ] = useState(); + + + const navigate = useNavigate(); + + const isEdit = !!id; + + useEffect(() => { + if (isEdit) { + axios.get('/user/access/'+id) + .then((res) => { + setCurrentUserAccess(res.data); + }) + .catch((err) => { + if (err.response.status === 404) { + navigate('/404'); + } + }) + } + axios.get('/role-list') + .then((res)=> { + setRole(res.data) + }) + .catch((err) => { + if (err.response.status === 404) { + navigate('/404'); + } + }) + + }, [id]); + + + return ( + + + + + + ); +} diff --git a/frontend/client-portal/src/pages/UserManagement/UserAccess/Form.tsx b/frontend/client-portal/src/pages/UserManagement/UserAccess/Form.tsx new file mode 100755 index 00000000..015f3cc4 --- /dev/null +++ b/frontend/client-portal/src/pages/UserManagement/UserAccess/Form.tsx @@ -0,0 +1,147 @@ +import * as Yup from 'yup'; +import { LoadingButton } from "@mui/lab"; +import { Box, Card, Grid, Stack, Typography } from "@mui/material"; +import { Role, UserAccess } from "../../../@types/user"; +import { FormProvider, RHFSelect, 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 palette from '@/theme/palette'; + +type Props = { + isEdit: boolean; + currentUserAccess?: UserAccess; + roles: Role +}; + +export default function AccsessForm({ isEdit, currentUserAccess, roles }: Props) { + + const { enqueueSnackbar } = useSnackbar(); + const navigate = useNavigate(); + const { id } = useParams(); + + const NewCorporatePlanSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + }); + + console.log(currentUserAccess, 'test') + const defaultValues = useMemo( + () => ({ + name: currentUserAccess?.person?.name || '', + username: currentUserAccess?.username || '', + email: currentUserAccess?.email || '', + roles: currentUserAccess?.role?.id || [], + password: '', + }), + [currentUserAccess] + ); + + useEffect(() => { + if (isEdit && currentUserAccess) { + reset(defaultValues); + } + if (!isEdit) { + reset(defaultValues); + } + }, [isEdit, currentUserAccess]); + + const methods = useForm({ + resolver: yupResolver(NewCorporatePlanSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + + const onSubmit = async (data: any) => { + + console.log(data); + if (!isEdit) { + await axios + .post('/user/access', data) + .then((res) => { + enqueueSnackbar('User created successfully', { variant: 'success' }); + }) + .then((res) => { + navigate('/user-access', { 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 Request', { variant: 'error' }); + } + } + else { + enqueueSnackbar('Create Failed : '+ response.data.message, { variant: 'error' }); + } + }); + } else { + await axios + .put('/user/access/' + currentUserAccess?.id, data) + .then((res) => { + enqueueSnackbar('User updated successfully', { variant: 'success' }); + }) + .then((res) => { + navigate('/user-access' , { replace: true }); + }) + .catch(({ response }) => { + enqueueSnackbar('Update Failed : '+ response.data.message, { variant: 'error' }); + }); + } + }; + + const optionsRoles = roles?.data?.map(item => ({ + value: item.id, + label: item.name + })) ?? []; + + if (optionsRoles.length > 0) { + optionsRoles.unshift({ value: '', label: '' }); + } + + return ( + + + + + + + User Access + + + + + + {optionsRoles.map((option, index) => ( + + ))} + + + + + { isEdit? 'Update' : 'Create' } + + + + + + + + + ); +} diff --git a/frontend/client-portal/src/pages/UserManagement/UserAccess/History.tsx b/frontend/client-portal/src/pages/UserManagement/UserAccess/History.tsx new file mode 100755 index 00000000..8d733ca7 --- /dev/null +++ b/frontend/client-portal/src/pages/UserManagement/UserAccess/History.tsx @@ -0,0 +1,218 @@ +// @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, .05)' + : 'rgba(0, 0, 0, .03)', + flexDirection: 'row-reverse', + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(90deg)', + }, + '& .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 CustomizedAccordions() { + const [expanded, setExpanded] = React.useState('panel1'); + + const handleChange = + (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => { + setExpanded(newExpanded ? panel : false); + }; + const pageTitle = 'Diagnosis Template History'; + + const { themeStretch } = useSettings(); + + const { id } = useParams(); + + const [corporate, setCorporate] = useState(); + const [ currentCorporate, setCurrentCorporate ] = useState(); + + const configuredCorporateContext = useContext(ConfiguredCorporateContext); + + useEffect(() => { + setCorporate(configuredCorporateContext.currentCorporate); + const model = 'App\\Models\\IcdTemplate'; + 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 '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} + + ); + } + })} + + + + + ))} +
+ ); +} diff --git a/frontend/client-portal/src/pages/UserManagement/UserAccess/Index.tsx b/frontend/client-portal/src/pages/UserManagement/UserAccess/Index.tsx new file mode 100755 index 00000000..a5f6e278 --- /dev/null +++ b/frontend/client-portal/src/pages/UserManagement/UserAccess/Index.tsx @@ -0,0 +1,37 @@ +import { Container,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 Divisions() { + const { themeStretch } = useSettings(); + + const { corporate_id } = useParams(); + + const pageTitle = 'User Access'; + return ( + + + + + + + + + + + ); +} diff --git a/frontend/client-portal/src/pages/UserManagement/UserAccess/List.tsx b/frontend/client-portal/src/pages/UserManagement/UserAccess/List.tsx new file mode 100755 index 00000000..c4b5d66a --- /dev/null +++ b/frontend/client-portal/src/pages/UserManagement/UserAccess/List.tsx @@ -0,0 +1,443 @@ +// @mui +import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Pagination, Grid, Autocomplete, DialogActions } 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'; +import HistoryIcon from '@mui/icons-material/History'; +// hooks +import { Link, NavLink as RouterLink, useNavigate } from 'react-router-dom'; +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +import { useParams, useSearchParams } from 'react-router-dom'; +// components +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import { UserAccess } from '../../../@types/user'; +import BasePagination from '../../../components/BasePagination'; +import { enqueueSnackbar } from 'notistack'; +import TableMoreMenu from '@/components/table/TableMoreMenu'; +import { Delete, EditOutlined, FindInPageOutlined } from '@mui/icons-material'; +import MuiDialog from '@/components/MuiDialog'; + +export default function List() { + const navigate = useNavigate(); + const { themeStretch } = useSettings(); + const { corporate_id } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const [importResult, setImportResult] = useState(null); + + function SearchInput(props: any) { + // SEARCH + 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); // Trigger to Parent + } + + useEffect(() => { // Trigger First Search + setSearchText(searchParams.get('search') ?? ''); + }, [searchParams]) + + return ( +
+ + + ); + } + + function ImportForm(props: any) { + // IMPORT + // Create Button Menu + 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 handleICDList = async (appliedFilter = null) => { + axios.get('master/diagnosis/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(); + }); + } + + const handleCancelImportButton = () => { + importForm.current.value = ""; + importForm.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 (importForm.current?.files.length) { + const formData = new FormData(); + formData.append("file", importForm.current?.files[0]) + axios.post(`master/diagnosis/import`, formData ) + .then(response => { + handleCancelImportButton(); + loadDataTableData(); + setImportResult(response.data) + // alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows'); + }) + .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' }) + } + } + + 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(); + }) + } + + + + return ( +
+ + {( !currentImportFileName && + + {/*

kjasndkjandskjasndkjansdkjansd

*/} + + + + + + +
+ )} +
+ ); + } + + // Called on every row to map the data to the columns + function createData( userAccess: UserAccess ): UserAccess { + return { + ...userAccess, + } + } + + // Generate the every row of the table + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + + const handleActivate = (model: any, status: string) => { + axios + .put(`/master/diagnosis-template/${row.id}/activation`, { + // service_code: service.service_code, + active: status == 'active', + }) + .then((res) => { + setDataTableData({ + ...dataTableData, + data: dataTableData.data.map((model) => { + let updatedModel = model; + if (row.id == model.id) { + updatedModel.active = res.data.icd.active; + } + return updatedModel; + }), + }); + }) + .catch((error) => { + // console.log('asdasd', error.response.data.message) + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; + + return ( + + *': { borderBottom: '1' } }}> + + {row.person?.name ?? '-'} + {row.email ?? '-'} + {row.role?.name ?? '-'} + + + + {/* navigate(`/master/diagnosis/${row.id}`)}> + + Detail + */} + navigate(`/user/access/${row.id}/edit`)} > + + Edit + + {/* setOpenDialogDelete(true)}> + + Delete + */} + + } /> + + + + + ); + } + // Delete + const reasons = [ + { value: 'agreement', label: 'Agreement changed' }, + { value: 'endorsement', label: 'Endorsement' }, + { value: 'renewal', label: 'Renewal' }, + { value: 'wrong_setting', label: 'Wrong Setting' }, + // Add more options as needed + ]; + + const [isReasonSelected, setIsReasonSelected] = useState(false); + const [formData, setFormData] = useState({ + reason: null + }); + + const marginBottom2 = { + marginBottom: 2, + } + + const style1 = { + color: '#919EAB', + width: '30%' + } + + const handleCloseDialog = () => { + setOpenDialogDelete(false); + resetForm(); + } + + const resetForm = () => { + setFormData({ + reason: null + }); + }; + const handleChange = (field, value) => { + setFormData((prevData) => ({ + ...prevData, + [field]: value, + })); + if (field === 'reason') { + setIsReasonSelected(!!value); + } + } + + const handleSubmit = () => { + if (isReasonSelected && formData.reason !== '') { + alert('zsd.'); + } else { + setIsReasonSelected(false); + } + + } + + // Dialog + const getContent = () => ( + + Are you sure to delete this User? + + + + Reason* + option.label} + fullWidth + value={reasons.find((r) => r.value === formData.reason) || null} // Use find to match the default value + onChange={(e, newValue) => handleChange('reason', newValue?.value)} + renderInput={(params) => ( + + )} + /> + + + + + + + + + + ); + + // 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('/user/access?guard_name=client-portal&', { params: filter }); + console.log(response.data); + setDataTableLoading(false); + + setDataTableData(response.data); + } + + const headStyle = { + fontWeight: 'bold', + }; + + 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); + } + + const [openDialogDelete, setOpenDialogDelete] = React.useState(false); + + useEffect(() => { + loadDataTableData(); + }, []) + + return ( + + + + {/* The Main Table */} + + + + + + + + + + + + + Name + Email + Role Access + + + + {dataTableIsLoading ? + ( + + + Loading + + + ) : ( + dataTableData.data.length == 0 ? + ( + + + No Data + + + ) : ( + + {dataTableData.data.map(row => ( + + ))} + + ) + )} +
+
+ + +
+ ); +} diff --git a/frontend/client-portal/src/pages/UserManagement/UserRole/CreateUpdate.tsx b/frontend/client-portal/src/pages/UserManagement/UserRole/CreateUpdate.tsx new file mode 100755 index 00000000..c6366d9d --- /dev/null +++ b/frontend/client-portal/src/pages/UserManagement/UserRole/CreateUpdate.tsx @@ -0,0 +1,82 @@ + +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 UserRoleForm from './Form'; +import { Role } from '../../../@types/user'; +import { Corporate } from "@/@types/corporates"; +import { ConfiguredCorporateContext } from "@/contexts/ConfiguredCorporateContext"; + + + +export default function PlanCreate() { + const { themeStretch } = useSettings(); + const { corporate_id, id } = useParams(); + const [corporate, setCorporate] = useState(); + const configuredCorporateContext = useContext(ConfiguredCorporateContext); + + useEffect(() => { + setCorporate(configuredCorporateContext.currentCorporate); + }, [configuredCorporateContext]) + + const [ currentUserRole, setCurrentUserRole ] = useState(); + + + const navigate = useNavigate(); + + const isEdit = !!id; + + const [permissions, setPermissions] = useState([]); + + useEffect(() => { + if (isEdit) { + axios.get('/user/role/'+id) + .then((res) => { + setCurrentUserRole(res.data); + axios.get('/permission_list?guard_name='+res.data.guard_name) + .then((res) => { + setPermissions(res.data); + }) + .catch((err) => { + if (err.response && err.response.status === 404) { + navigate('/404'); + } else { + console.error('Error fetching permissions:', err); + } + }); + }) + .catch((err) => { + if (err.response.status === 404) { + navigate('/404'); + } + }) + } + + + }, [corporate_id, id]); + + return ( + + + + + + ); +} diff --git a/frontend/client-portal/src/pages/UserManagement/UserRole/Form.tsx b/frontend/client-portal/src/pages/UserManagement/UserRole/Form.tsx new file mode 100755 index 00000000..46195612 --- /dev/null +++ b/frontend/client-portal/src/pages/UserManagement/UserRole/Form.tsx @@ -0,0 +1,193 @@ +import * as Yup from 'yup'; +import { LoadingButton } from "@mui/lab"; +import {Box, Card, FormControlLabel, Grid, Stack, Typography } from "@mui/material"; +import Autocomplete from '@mui/material/Autocomplete'; +import TextField from '@mui/material/TextField'; +import { Role } from '../../../@types/user'; +import { Permisions } from '../../../@types/user'; +import { FormProvider, RHFSelect, RHFSwitch, RHFTextField } from "../../../components/hook-form"; +import { useEffect, useMemo, useState } from 'react'; +import { useForm, Controller } 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 palette from '@/theme/palette'; +import { Checkbox } from '@mui/material'; +import Label from '@/components/Label'; + +type Props = { + isEdit: boolean; + currentUserRole?: Role; + permissions?: Permisions; +}; + +export default function UserRoleForm({ isEdit, currentUserRole, permissions }: Props) { + + const { enqueueSnackbar } = useSnackbar(); + const navigate = useNavigate(); + const { corporate_id } = useParams(); + const [guardName, setGuardName] = useState(currentUserRole?.guard_name || ''); + const [filteredPermissions, setFilteredPermissions] = useState(permissions); + + const NewUserRoleSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + }); + + const defaultValues = useMemo( + () => ({ + name: currentUserRole?.name || '', + guard_name: currentUserRole?.guard_name || '', + permission_check: currentUserRole?.permissions?.map(permission => permission.id) || [] + + }), + [currentUserRole, permissions] + ); + + useEffect(() => { + if (isEdit && currentUserRole) { + reset(defaultValues); + } + if (!isEdit) { + reset(defaultValues); + } + }, [isEdit, currentUserRole]); + + const methods = useForm({ + resolver: yupResolver(NewUserRoleSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + + const onSubmit = async (data: any) => { + console.log(data, 'test1') + if (!isEdit) { + await axios + .post('/user/role', data) + .then((res) => { + enqueueSnackbar('User Role created successfully', { variant: 'success' }); + }) + .then((res) => { + navigate('/user-role', { 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 Request', { variant: 'error' }); + } + } + else { + enqueueSnackbar('Create Failed : '+ response.data.message, { variant: 'error' }); + } + }); + } else { + await axios + .put('/user/role/' + currentUserRole?.id, data) + .then((res) => { + enqueueSnackbar('User Role updated successfully', { variant: 'success' }); + }) + .then((res) => { + navigate('/user-role' , { replace: true }); + }) + .catch(({ response }) => { + enqueueSnackbar('Update Failed : '+ response.data.message, { variant: 'error' }); + }); + } + }; + + const guard_name_options = [ + { value: '', label: '' }, + // { value: 'web', label: 'Primecenter' }, + { value: 'client-portal', label: 'Client Portal' }, + // { value: 'hospital-portal', label: 'Hospital Portal' } + ]; + + // Buat fungsi handleCheckboxClick di luar komponen utama (UserRoleForm) + const handleCheckboxClick = (permissionId, checked) => { + const currentPermissions = getValues('permission_check') || []; + if (checked) { + setValue('permission_check', [...currentPermissions, permissionId]); + } else { + setValue('permission_check', currentPermissions.filter(id => id !== permissionId)); + } + }; + + useEffect(() => { + // Fetch permissions based on guard_name + if (guardName) { + axios.get(`/permission_list?guard_name=${guardName}`) + .then((res) => { + setFilteredPermissions(res.data); + }) + .catch((err) => { + console.error('Error fetching permissions:', err); + }); + } else { + setFilteredPermissions(permissions); + } + }, [guardName,permissions]); + + const handleGuardNameChange = (event) => { + console.log("ivan") + setGuardName(event.target.value); + setValue('guard_name', event.target.value); + }; + + return ( + + + + + + + User Role + + + {guard_name_options.map((option, index) => ( + + ))} + + Permission + + {filteredPermissions?.map((permission, index) => ( + + handleCheckboxClick(permission.id, e.target.checked)} + /> + } + label={permission.name} + /> + + ))} + + + { isEdit? 'Update' : 'Create' } + + + + + + + + + ); +} diff --git a/frontend/client-portal/src/pages/UserManagement/UserRole/History.tsx b/frontend/client-portal/src/pages/UserManagement/UserRole/History.tsx new file mode 100755 index 00000000..8d733ca7 --- /dev/null +++ b/frontend/client-portal/src/pages/UserManagement/UserRole/History.tsx @@ -0,0 +1,218 @@ +// @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, .05)' + : 'rgba(0, 0, 0, .03)', + flexDirection: 'row-reverse', + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(90deg)', + }, + '& .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 CustomizedAccordions() { + const [expanded, setExpanded] = React.useState('panel1'); + + const handleChange = + (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => { + setExpanded(newExpanded ? panel : false); + }; + const pageTitle = 'Diagnosis Template History'; + + const { themeStretch } = useSettings(); + + const { id } = useParams(); + + const [corporate, setCorporate] = useState(); + const [ currentCorporate, setCurrentCorporate ] = useState(); + + const configuredCorporateContext = useContext(ConfiguredCorporateContext); + + useEffect(() => { + setCorporate(configuredCorporateContext.currentCorporate); + const model = 'App\\Models\\IcdTemplate'; + 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 '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} + + ); + } + })} + + + + + ))} +
+ ); +} diff --git a/frontend/client-portal/src/pages/UserManagement/UserRole/Index.tsx b/frontend/client-portal/src/pages/UserManagement/UserRole/Index.tsx new file mode 100755 index 00000000..c1971074 --- /dev/null +++ b/frontend/client-portal/src/pages/UserManagement/UserRole/Index.tsx @@ -0,0 +1,37 @@ +import { Container,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 Divisions() { + const { themeStretch } = useSettings(); + + const { corporate_id } = useParams(); + + const pageTitle = 'User Role'; + return ( + + + + + + + + + + + ); +} diff --git a/frontend/client-portal/src/pages/UserManagement/UserRole/List.tsx b/frontend/client-portal/src/pages/UserManagement/UserRole/List.tsx new file mode 100755 index 00000000..c3a577fd --- /dev/null +++ b/frontend/client-portal/src/pages/UserManagement/UserRole/List.tsx @@ -0,0 +1,442 @@ +// @mui +import { Box, Button, Card, MenuItem, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Stack, Menu, Grid, DialogActions } from '@mui/material'; +import { Autocomplete } from "@mui/material"; +import AddIcon from '@mui/icons-material/Add'; +// hooks +import { Link, NavLink as RouterLink, useNavigate } from 'react-router-dom'; +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +import { useParams, useSearchParams } from 'react-router-dom'; +// components +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import { Role } from '../../../@types/user'; +import BasePagination from '../../../components/BasePagination'; +import { enqueueSnackbar } from 'notistack'; +import TableMoreMenu from '@/components/table/TableMoreMenu'; +import { Delete, EditOutlined, FindInPageOutlined } from '@mui/icons-material'; +import MuiDialog from '@/components/MuiDialog'; + +export default function List() { + const navigate = useNavigate(); + const [searchParams, setSearchParams] = useSearchParams(); + + function SearchInput(props: any) { + // SEARCH + 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); // Trigger to Parent + } + + useEffect(() => { // Trigger First Search + setSearchText(searchParams.get('search') ?? ''); + }, [searchParams]) + + return ( +
+ + + ); + } + + function ImportForm(props: any) { + // IMPORT + // Create Button Menu + 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 handleICDList = async (appliedFilter = null) => { + axios.get('master/diagnosis/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(); + }); + } + + const handleCancelImportButton = () => { + importForm.current.value = ""; + importForm.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 (importForm.current?.files.length) { + const formData = new FormData(); + formData.append("file", importForm.current?.files[0]) + axios.post(`master/diagnosis/import`, formData ) + .then(response => { + handleCancelImportButton(); + loadDataTableData(); + setImportResult(response.data) + // alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows'); + }) + .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' }) + } + } + + 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(); + }) + } + + + + return ( +
+ + {( !currentImportFileName && + + {/*

kjasndkjandskjasndkjansdkjansd

*/} + + + + + + +
+ )} +
+ ); + } + + // Called on every row to map the data to the columns + function createData( userManamgent: Role ): Role { + return { + ...userManamgent, + } + } + + const [id, setId] = useState(null) + + // Generate the every row of the table + function Row(props: { row: ReturnType }) { + const { row } = props; + const handleActivate = (model: any, status: string) => { + axios + .put(`/master/diagnosis-template/${row.id}/activation`, { + // service_code: service.service_code, + active: status == 'active', + }) + .then((res) => { + setDataTableData({ + ...dataTableData, + data: dataTableData.data.map((model) => { + let updatedModel = model; + if (row.id == model.id) { + updatedModel.active = res.data.icd.active; + } + return updatedModel; + }), + }); + }) + .catch((error) => { + // console.log('asdasd', error.response.data.message) + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; + + return ( + + *': { borderBottom: '1' } }}> + + {row.id} + {row.name ?? '-'} + {row.guard_name ?? '-'} + + + + {/* navigate(`/user/role/${row.id}`)}> + + Detail + */} + navigate(`/user/role/${row.id}/edit`)} > + + Edit + + {/* { setOpenDialogDelete(true); setId(row.id); }}> + + Delete + */} + {/* navigate(`/user/role/${row.id}/history`)}> + + History + */} + + } /> + + + + + ); + } + + // Delete + const reasons = [ + { value: 'agreement', label: 'Agreement changed' }, + { value: 'endorsement', label: 'Endorsement' }, + { value: 'renewal', label: 'Renewal' }, + { value: 'wrong_setting', label: 'Wrong Setting' }, + // Add more options as needed + ]; + + const [isReasonSelected, setIsReasonSelected] = useState(false); + const [formData, setFormData] = useState({ + reason: null + }); + + const marginBottom2 = { + marginBottom: 2, + } + + const style1 = { + color: '#919EAB', + width: '30%' + } + + const handleCloseDialog = () => { + setOpenDialogDelete(false); + resetForm(); + } + + const resetForm = () => { + setFormData({ + reason: null + }); + }; + + const handleChange = (field, value) => { + setFormData((prevData) => ({ + ...prevData, + [field]: value, + })); + if (field === 'reason') { + setIsReasonSelected(!!value); + } + } + + const handleSubmit = () => { + if (isReasonSelected && formData.reason !== '') { + console.log(formData, 'test') + } else { + setIsReasonSelected(false); + } + } + + // Dialog + const getContent = () => ( + + Are you sure to delete this User Role? + + + + Reason* + option.label} + fullWidth + value={reasons.find((r) => r.value === formData.reason) || null} // Use find to match the default value + onChange={(e, newValue) => handleChange('reason', newValue?.value)} + renderInput={(params) => ( + + )} + /> + + + + + + + + + + ); + + // 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('/user/role?guard_name=client-portal&', { params: filter }); + console.log(response.data); + setDataTableLoading(false); + + setDataTableData(response.data); + } + + const headStyle = { + fontWeight: 'bold', + }; + + 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); + } + + const [openDialogDelete, setOpenDialogDelete] = React.useState(false); + + useEffect(() => { + loadDataTableData(); + }, []) + + return ( + + + + {/* The Main Table */} + + + + + + + + + + + + + ID + Name + Guard Name + + + + {dataTableIsLoading ? + ( + + + Loading + + + ) : ( + dataTableData.data.length == 0 ? + ( + + + No Data + + + ) : ( + + {dataTableData.data.map(row => ( + + ))} + + ) + )} +
+
+ + + + +
+ ); +} diff --git a/frontend/client-portal/src/pages/auth/Login.tsx b/frontend/client-portal/src/pages/auth/Login.tsx index ccea33d6..a0b84ebc 100755 --- a/frontend/client-portal/src/pages/auth/Login.tsx +++ b/frontend/client-portal/src/pages/auth/Login.tsx @@ -97,7 +97,7 @@ export default function Login() {
- {loginOrVerifyCode && emailOrPhone ? ( + {/* {loginOrVerifyCode && emailOrPhone ? ( <> @@ -138,7 +138,7 @@ export default function Login() { >Kirim Ulang Kode OTP - ) : ( + ) : ( */} <> @@ -152,19 +152,16 @@ export default function Login() { - {emailOrPhoneForm ? ( + {/* {emailOrPhoneForm ? ( - ) : ( - - )} + ) : ( */} + + {/* )} */} - )} + {/* )} */} {/* Atau diff --git a/frontend/client-portal/src/routes/index.tsx b/frontend/client-portal/src/routes/index.tsx index 4bf282dd..d3d4bc87 100755 --- a/frontend/client-portal/src/routes/index.tsx +++ b/frontend/client-portal/src/routes/index.tsx @@ -314,6 +314,102 @@ export default function Router() { }, ], }, + { + path: 'user-role', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, + { + path: 'user-role/create', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, + { + path: 'user/role/:id/edit', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, + { + path: 'user-access', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, + { + path: 'user-access/create', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, + { + path: 'user/access/:id/edit', + element: ( + + + + + + ), + children: [ + { + element: , + index: true, + }, + ], + }, { path: '*', element: }, ]); } @@ -361,3 +457,9 @@ const MasterFormulariumTemplateV2 = Loadable(lazy(() => import('../pages/Master/ 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'))); + +// User Management +const UserRole = Loadable(lazy(() => import('../pages/UserManagement/UserRole/Index'))); +const UserRoleCreate = Loadable(lazy(() => import('../pages/UserManagement/UserRole/CreateUpdate'))); +const UserAccess = Loadable(lazy(() => import('../pages/UserManagement/UserAccess/Index'))); +const UserAccessCreate = Loadable(lazy(() => import('../pages/UserManagement/UserAccess/CreateUpdate'))); diff --git a/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx b/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx index 17259d4c..75d7e7e2 100755 --- a/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx @@ -1,42 +1,52 @@ /* ----------------------------------- yup ---------------------------------- */ import * as Yup from 'yup'; +import React, { useContext, useRef, useState, useEffect } from 'react'; /* ---------------------------------- form ---------------------------------- */ import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; /* ---------------------------------- @mui ---------------------------------- */ -import { Stack, Alert } from '@mui/material'; import { LoadingButton } from '@mui/lab'; +import { Link, Stack, Alert, IconButton, InputAdornment } from '@mui/material'; /* ---------------------------------- hooks --------------------------------- */ import useAuth from '../../../hooks/useAuth'; import useIsMountedRef from '../../../hooks/useIsMountedRef'; /* ------------------------------- components ------------------------------- */ -import { FormProvider, RHFTextField } from '../../../components/hook-form'; +import Iconify from '../../../components/Iconify'; +import { FormProvider, RHFTextField, RHFCheckbox } from '../../../components/hook-form'; import { enqueueSnackbar } from 'notistack'; +import { useNavigate } from 'react-router-dom'; /* ---------------------------------- types --------------------------------- */ -type LoginFormProps = { - setEmailOrPhone: Function; - setLoginOrVerifyCode: Function; -}; +// type LoginFormProps = { +// setEmailOrPhone: Function; +// setLoginOrVerifyCode: Function; +// }; type FormValuesProps = { email: string; + password: string; + remember: boolean; afterSubmit?: string; }; /* -------------------------------------------------------------------------- */ -export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: LoginFormProps) { +export default function LoginForm() { const { login } = useAuth(); + const navigate = useNavigate(); const isMountedRef = useIsMountedRef(); + const [showPassword, setShowPassword] = useState(false); const LoginSchema = Yup.object().shape({ - email: Yup.string().email('Email must be a valid email address').required('Email is required'), + email: Yup.string().email('Format email tidak valid').required('Email harus diisi'), + password: Yup.string().required('Password harus diisi'), }); - const defaultValues = { + const defaultValues = { email: '', + password: '', + remember: true, }; const methods = useForm({ @@ -51,17 +61,34 @@ export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: Log formState: { errors, isSubmitting }, } = methods; + // const onSubmit = async (data: FormValuesProps) => { + // try { + // const loginResult = await login(data.email, data.password, data.remember); + // // setEmailOrPhone(data.email); + // // setLoginOrVerifyCode(true); + // // reset(); + // console.log('test'); + // navigate('/dashboard'); + // // enqueueSnackbar('Kode OTP telah dikirim, silahkan cek email dan spam folder', { + // // variant: 'success', + // // autoHideDuration: 5000, + // // }); + // } catch (error: any) { + // reset(); + // console.log(error, 'test'); + + // if (isMountedRef.current) { + // setError('afterSubmit', { ...error, message: error.data.message }); + // } + // } + // }; + const onSubmit = async (data: FormValuesProps) => { try { - await login(data.email); - setEmailOrPhone(data.email); - setLoginOrVerifyCode(true); - reset(); - enqueueSnackbar('Kode OTP telah dikirim, silahkan cek email dan spam folder', { - variant: 'success', - autoHideDuration: 5000, - }); - } catch (error: any) { + const loginResult = await login(data.email, data.password, data.remember); + navigate('/dashboard'); + } catch (error) { + reset(); if (isMountedRef.current) { @@ -73,10 +100,25 @@ export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: Log return ( - Masukkan akun yang telah terdaftar + Masukan Email atau Username dan Password {!!errors.afterSubmit && {errors.afterSubmit.message}} + + setShowPassword(!showPassword)} edge="end"> + + + + ), + }} + required + /> Corporate Name* + Corporate Phone + + + Alarm Center Phone + + Payor ID* @@ -560,6 +572,13 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) { + + + Description Letter of Guarantee + + + + {/*
Images {row.code} {row.type} - {row.limit_rules} + {row.limit_rules ? fNumber(row.limit_rules) : row.limit_rules} {row.active == 1 ? diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogBenefit.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogBenefit.tsx index b7855726..a4176042 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogBenefit.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogBenefit.tsx @@ -40,6 +40,11 @@ type BenefitSelected = { id: number, description: string, benefit_id: number, + family_plan: string, + family_plan_plans: string, + limit_amount: number, + limit_amount_plan: number, + max_frequency_period: number, } @@ -54,6 +59,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl const [valBenefitNameError, setValBenefitNameError] = useState(''); const benefitNameData = requestLog?.benefit; const [benefitSelected, setBenefitSelected] = useState([]); + const [isDisabled, setisDisable] = useState(false); const handleConditionChangeService = (event) => { const selectedItem = event.target.value; @@ -186,15 +192,125 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl } } + const totalUsage = () => { + let realTimeUsageMember = 0 + for (let key in requestLog?.member_usage_benefit) { + if (requestLog?.member_usage_benefit.hasOwnProperty(key)) { + let value = requestLog?.member_usage_benefit[key]; + // Menggunakan parseFloat() untuk mengonversi nilai menjadi angka + let numericValue = parseFloat(value); + // Memeriksa apakah numericValue adalah angka yang valid + if (!isNaN(numericValue)) { + realTimeUsageMember += numericValue; + } + } + } + + let totalAmountMember = 0 + for (let key in benefitData) { + // Menambahkan nilai amount_approved ke totalAmount + totalAmountMember += Number(benefitData[key].amount_approved); + } + + return realTimeUsageMember+totalAmountMember + } + const handleOnChangeNominal = (key) => { - if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){ - // setValue(`benefit_data.${key}.amount_approved`, 0); - setError(`benefit_data.${key}.amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'}); + if (benefitSelected[key].family_plan == 'S' || benefitSelected[key].family_plan == 'F'){ + if (requestLog?.member_usage_benefit && benefitSelected[key] && benefitData[key]) { + let limitAmount = Number(benefitSelected[key].limit_amount) || 0; + let limitAmountPlan = Number(benefitSelected[key].limit_amount_plan) || 0; + // Periksa apakah limitAmount Benefit lebih besar dari realTimeUsage + if (limitAmountPlan != 999999999){ + let realTimeUsage = 0; + let value = 0; + for (let key in requestLog?.member_usage_benefit) { + if (requestLog?.member_usage_benefit.hasOwnProperty(key)) { + let value = requestLog?.member_usage_benefit[key]; + // Menggunakan parseFloat() untuk mengonversi nilai menjadi angka + let numericValue = parseFloat(value); + // Memeriksa apakah numericValue adalah angka yang valid + if (!isNaN(numericValue)) { + realTimeUsage += numericValue; + } + } + } + // console.log(benefitData, 'test') + let totalAmount = 0; + + for (let key in benefitData) { + // Menambahkan nilai amount_approved ke totalAmount + totalAmount += Number(benefitData[key].amount_approved); + } + + // Hitung penggunaan waktu nyata + realTimeUsage += totalAmount; + let excess = realTimeUsage - limitAmountPlan + let incurred = Number(benefitData[key].amount_incurred); + + if (realTimeUsage === limitAmountPlan){ + setisDisable(true) + setValue(`benefit_data.${key}.amount_not_approved`, incurred); + } else { + setisDisable(false) + } + if (limitAmountPlan < realTimeUsage) { + setValue(`benefit_data.${key}.amount_not_approved`, excess); + setValue(`benefit_data.${key}.amount_approved`, incurred - excess); + setError(`benefit_data.${key}.amount_approved`, { message: `Total Amount Approve sudah melebihi limit ${ fNumber(limitAmountPlan) } , silakan isikan di Amount Excess` }); + } else if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred) { + setError(`benefit_data.${key}.amount_approved`, { message: 'Total Amount Approve tidak boleh lebih dari Total Amount Incurred' }); + } else { + clearErrors(`benefit_data.${key}.amount_approved`); + } + } else if (limitAmount != 999999999) { // Periksa apakah limitAmount Benefit lebih besar dari realTimeUsage + // Konversi nilai ke angka dengan aman + let memberUsage = Number(requestLog.member_usage_benefit[benefitSelected[key].id]) || 0; + let amountApproved = Number(benefitData[key].amount_approved) || 0; + // Hitung penggunaan waktu nyata + let realTimeUsage = memberUsage + amountApproved; + let value = realTimeUsage - limitAmount + + if (limitAmount < realTimeUsage) { + setValue(`benefit_data.${key}.amount_not_approved`, value); + setError(`benefit_data.${key}.amount_approved`, { message: `Total Amount Approve sudah melebihi limit ${ fNumber(limitAmount) } , silakan isikan di Amount Excess` }); + } else if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred) { + setError(`benefit_data.${key}.amount_approved`, { message: 'Total Amount Approve tidak boleh lebih dari Total Amount Incurred' }); + } else { + clearErrors(`benefit_data.${key}.amount_approved`); + } + } else { + if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred) { + setError(`benefit_data.${key}.amount_approved`, { message: 'Total Amount Approve tidak boleh lebih dari Total Amount Incurred' }); + } else { + clearErrors(`benefit_data.${key}.amount_approved`); + } + } + + if (totalAll().totalAmountApproved + totalAll().totalAmountNotApproved === totalAll().totalAmountIncurred) { + clearErrors(`benefit_data.${key}.excess_paid`); + } else { + setError(`benefit_data.${key}.excess_paid`, { message: 'Total Amount Excess tidak sama dengan Total Amount Incurred' }); + } + } } else { - clearErrors(`benefit_data.${key}.amount_approved`); + if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){ + setError(`benefit_data.${key}.amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'}); + } else { + clearErrors(`benefit_data.${key}.amount_approved`); + } } } + const handleOnChangeNotApprove = (key, value) => { + setValue(`benefit_data.${key}.excess_paid`, value); + if (totalAll().totalAmountApproved + totalAll().totalAmountNotApproved === totalAll().totalAmountIncurred) { + clearErrors(`benefit_data.${key}.excess_paid`); + } else { + setError(`benefit_data.${key}.excess_paid`, { message: 'Total Amount Excess tidak sama dengan Total Amount Incurred' }); + } + }; + // Submit Form // ===================================== const submitHandler = async (data: BenefitConfigurationListType) => { @@ -318,6 +434,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl setValue(`benefit_data.${index}.amount_approved`, event.target.value) handleOnChangeNominal(index)} } + disabled={isDisabled} /> @@ -338,6 +455,10 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl name={`benefit_data.${index}.amount_not_approved`} placeholder='Amount Not Approved' required + onChange={(event) => { + setValue(`benefit_data.${index}.amount_not_approved`, event.target.value) + handleOnChangeNotApprove(index, event.target.value)} + } /> @@ -432,7 +553,7 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl - Total Benefit + Total Benefit @@ -507,7 +628,21 @@ export default function DialogBenefit({requestLog, setOpenDialog, openDialog, cl - + + + + + Total Usage / Limit + + + + + {fNumber(totalUsage())} / {fNumber(benefitSelected[0].limit_amount_plan) } + + + + + diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogConfirmation copy.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogConfirmation copy.tsx new file mode 100755 index 00000000..4ff76dd6 --- /dev/null +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogConfirmation copy.tsx @@ -0,0 +1,206 @@ +import MuiDialog from "@/components/MuiDialog"; +import { Autocomplete, Button, Card, Checkbox, DialogActions, Grid, TextField, Typography } from "@mui/material"; +import { Paper } from "@mui/material"; +import { Stack } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { DetailFinalLogType } from "../Model/Types"; +import { fDateOnly, fDateTimesecond, toTitleCase } from "@/utils/formatTime"; +import axios from "@/utils/axios"; +import { enqueueSnackbar } from "notistack"; +import { useNavigate } from "react-router"; + + +type DialogConfirmationType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + approve: string; + requestLog: DetailFinalLogType|undefined; +} + +export default function DialogConfirmation({requestLog, setOpenDialog, openDialog, approve, onSubmit} : DialogConfirmationType ) { + + const navigate = useNavigate(); + const [formData, setFormData] = useState({ + discharge_date: requestLog?.discharge_date, + id: requestLog?.id, + status: approve || '', + catatan: '', + icdCodes: requestLog?.diagnosis.length ? requestLog.diagnosis.map(diagnosis => ({ value: diagnosis.id, label: diagnosis.name })) : [] + }); + + const [icdOptions, setIcdOptions] = useState([ + { value: '-', label: '-' } + ]); + + 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 + + + useEffect(() => { + // Update formData setiap kali approve berubah + setFormData(prevData => ({ + ...prevData, + status: approve || '', + })); + }, [approve]); + + const handleChange = (field, value) => { + setFormData((prevData) => ({ + ...prevData, + [field]: value, + })); + + }; + + const handleApprove = () => { + setFormData((prevData) => ({ + ...prevData, + status: approve, + })); + handleSubmit(); + }; + + + const handleSubmit = () => { + axios + .post(`customer-service/request/final-log`, formData) + .then((response) => { + enqueueSnackbar('Verification Request LOG Success', { variant: 'success' }); + setOpenDialog(false); + if (requestLog?.service_type == 'Inpatient'){ + navigate('/case_management/inpatient_monitoring'); + } else { + navigate('/custormer-service/final-log'); + } + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' }); + }); + } + + + const style1 = { + color: '#919EAB', + width: '30%' + } + const style2 = { + width: '70%' + } + const marginBottom1 = { + marginBottom: 1, + } + const marginBottom2 = { + marginBottom: 2, + } + const handleCloseDialog = () => { + setOpenDialog(false); + } + + + + const getContent = () => ( + + Are you sure to {approve == 'approved' ? 'approve' : 'deciline'} this final log ? + + + + Member ID + {requestLog?.member_id} + + + Policy Number + {requestLog?.policy_number} + + + Name + {requestLog?.name} + + + Submission Date + {requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'} + + + Claim Method + {requestLog?.claim_method ? toTitleCase(requestLog?.claim_method) : '-'} + + + Service Type + {requestLog?.service_type} + + + + + + Discharge Date + handleChange('discharge_date', e.target.value)} + /> + + + Catatan + handleChange('catatan', e.target.value)} + /> + + + Diagnosis ICD - X + option.label} + fullWidth + value={icdOptions.filter((icd) => formData.icdCodes.includes(icd.value))} + onChange={(e, newValues) => handleChange('icdCodes', newValues.map((value) => value.value))} + renderInput={(params) => ( + + )} + /> + + + + + + + {approve == 'approved' ? ( + + ) : ( + + ) } + + + + ); + + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogConfirmation.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogConfirmation.tsx index 4ff76dd6..75134e54 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogConfirmation.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogConfirmation.tsx @@ -1,6 +1,5 @@ import MuiDialog from "@/components/MuiDialog"; -import { Autocomplete, Button, Card, Checkbox, DialogActions, Grid, TextField, Typography } from "@mui/material"; -import { Paper } from "@mui/material"; +import { Autocomplete, Button, Card, DialogActions, Grid, TextField, Typography } from "@mui/material"; import { Stack } from '@mui/material'; import React, { useEffect, useState } from 'react'; import { DetailFinalLogType } from "../Model/Types"; @@ -26,25 +25,15 @@ export default function DialogConfirmation({requestLog, setOpenDialog, openDialo id: requestLog?.id, status: approve || '', catatan: '', - icdCodes: requestLog?.diagnosis.length ? requestLog.diagnosis.map(diagnosis => ({ value: diagnosis.id, label: diagnosis.name })) : [] + icdCodes: requestLog?.diagnosis.length ? requestLog.diagnosis.map(diagnosis => ({ value: diagnosis.value, label: diagnosis.label })) : [] }); const [icdOptions, setIcdOptions] = useState([ { value: '-', label: '-' } ]); - 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 - - + const [searchIcd, setSearchIcd] = useState(''); + useEffect(() => { // Update formData setiap kali approve berubah setFormData(prevData => ({ @@ -58,7 +47,6 @@ export default function DialogConfirmation({requestLog, setOpenDialog, openDialo ...prevData, [field]: value, })); - }; const handleApprove = () => { @@ -68,7 +56,17 @@ export default function DialogConfirmation({requestLog, setOpenDialog, openDialo })); handleSubmit(); }; - + + const handleSearch = (search) => { + setSearchIcd(search); + axios.get('diagnosis?search=' + search) + .then((response) => { + setIcdOptions(response.data.data); + }) + .catch((error) => { + console.error('Error fetching ICD options:', error); + }); + } const handleSubmit = () => { axios @@ -76,7 +74,7 @@ export default function DialogConfirmation({requestLog, setOpenDialog, openDialo .then((response) => { enqueueSnackbar('Verification Request LOG Success', { variant: 'success' }); setOpenDialog(false); - if (requestLog?.service_type == 'Inpatient'){ + if (requestLog?.service_type === 'Inpatient') { navigate('/case_management/inpatient_monitoring'); } else { navigate('/custormer-service/final-log'); @@ -87,120 +85,117 @@ export default function DialogConfirmation({requestLog, setOpenDialog, openDialo }); } - const style1 = { color: '#919EAB', width: '30%' - } + }; const style2 = { width: '70%' - } + }; const marginBottom1 = { marginBottom: 1, - } + }; const marginBottom2 = { marginBottom: 2, - } + }; const handleCloseDialog = () => { setOpenDialog(false); - } - - + }; const getContent = () => ( - Are you sure to {approve == 'approved' ? 'approve' : 'deciline'} this final log ? + Are you sure to {approve === 'approved' ? 'approve' : 'decline'} this final log? - - - Member ID - {requestLog?.member_id} - - - Policy Number - {requestLog?.policy_number} - - - Name - {requestLog?.name} - - - Submission Date - {requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'} - - - Claim Method - {requestLog?.claim_method ? toTitleCase(requestLog?.claim_method) : '-'} - - - Service Type - {requestLog?.service_type} - - + + + Member ID + {requestLog?.member_id} + + + Policy Number + {requestLog?.policy_number} + + + Name + {requestLog?.name} + + + Submission Date + {requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'} + + + Claim Method + {requestLog?.claim_method ? toTitleCase(requestLog?.claim_method) : '-'} + + + Service Type + {requestLog?.service_type} + + - - - Discharge Date - handleChange('discharge_date', e.target.value)} - /> - - - Catatan - handleChange('catatan', e.target.value)} - /> - - - Diagnosis ICD - X - option.label} - fullWidth - value={icdOptions.filter((icd) => formData.icdCodes.includes(icd.value))} - onChange={(e, newValues) => handleChange('icdCodes', newValues.map((value) => value.value))} - renderInput={(params) => ( - - )} - /> - - - - - + + + Discharge Date + handleChange('discharge_date', e.target.value)} + /> + + + Catatan + handleChange('catatan', e.target.value)} + /> + + + Diagnosis ICD - X + option.label} + fullWidth + value={formData.icdCodes} + onChange={(e, newValues) => handleChange('icdCodes', newValues)} + inputValue={searchIcd} + onInputChange={(e, newInputValue) => handleSearch(newInputValue)} + renderInput={(params) => ( + + )} + /> + + + + + - {approve == 'approved' ? ( - - ) : ( - - ) } - - + {approve === 'approved' ? ( + + ) : ( + + )} + - ); - + ); return ( - ); -} \ No newline at end of file + ); +} diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditBenefit.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditBenefit.tsx index 11f0b03f..8af53a7e 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditBenefit.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditBenefit.tsx @@ -31,10 +31,19 @@ type DialogDeleteType = { total: any } +type BenefitSelected = { + id: number, + description: string, + benefit_id: number, + family_plan: string, + limit_amount: number, +} + export default function DialogEditBenefit({id, data, setOpenDialog, openDialog, onSubmit, total} : DialogDeleteType ) { const handleCloseDialog = () => { setOpenDialog(false); } + const [benefitSelected, setBenefitSelected] = useState([]); // setup form // ==================================== @@ -85,15 +94,70 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog, } } + const findItemById = (id) => { + return total.benefit.find(item => item.id === id); + } + const handleOnChangeNominal = (key) => { - if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){ - // setValue(`benefit_data.${key}.amount_approved`, 0); - setError(`amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'}); + let benefitData = findItemById(data?.benefit_id) + if (benefitData.family_plan == 'S' || benefitData.family_plan == 'F'){ + // Konversi nilai ke angka dengan aman + let limitAmount = Number(benefitData.limit_amount) || 0; + let limitAmountPlan = Number(benefitData.limit_amount_plan) || 0; + + if (limitAmountPlan != 999999999){ + let realTimeUsage = totalAll().totalAmountApproved; + console.log(limitAmountPlan, 'test') + if (limitAmountPlan < realTimeUsage) { + setError(`amount_approved`, { message: `Total Amount Approve sudah melebihi limit ${ fNumber(limitAmountPlan) } , silakan isikan di Amount Excess` }); + } else if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred) { + setError(`amount_approved`, { message: 'Total Amount Approve tidak boleh lebih dari Total Amount Incurred' }); + } else { + clearErrors(`amount_approved`); + } + } else if (limitAmount != 999999999) { + let memberUsage = Number(total.totalLimit[benefitData.id]) || 0; + let amountApproved = Number(parseFloat(watch('amount_approved'))) || 0; + // Hitung penggunaan waktu nyata + let realTimeUsage = memberUsage + amountApproved; + // Periksa apakah limitAmount lebih besar dari realTimeUsage + if (limitAmount < realTimeUsage) { + setError(`amount_approved`, { message: `Total Amount Approve sudah melebihi limit ${ fNumber(limitAmount) } , silakan isikan di Amount Excess` }); + } else if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){ + // setValue(`benefit_data.${key}.amount_approved`, 0); + setError(`amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'}); + } else { + clearErrors(`amount_approved`); + } + } else { + if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred) { + setError(`amount_approved`, { message: 'Total Amount Approve tidak boleh lebih dari Total Amount Incurred' }); + } else { + clearErrors(`amount_approved`); + } + } } else { - clearErrors(`amount_approved`); + if (totalAll().totalAmountApproved > totalAll().totalAmountIncurred){ + setError(`amount_approved`, {message: 'Amount Approve tidak boleh lebih dari Amount Incurred'}); + } else { + clearErrors(`amount_approved`); + } } } + const handleOnChangeNotApprove = (key, value) => { + let amountApproved = Number(parseFloat(watch('amount_approved'))) || 0; + let amountNotApproved = Number(parseFloat(watch('amount_not_approved'))) || 0; + let amountIncurred = Number(parseFloat(watch('amount_incurred'))) || 0; + setValue(`excess_paid`, value); + console.log(amountApproved + amountNotApproved, amountIncurred, 'test') + if ((amountApproved + amountNotApproved) !== amountIncurred) { + setError(`amount_not_approved`, {message: 'Amount Not Approve tidak sama dengan total Amount Incurred'}); + } else { + clearErrors(`amount_not_approved`); + } + }; + // if (totalAmountIncurred !== (totalAmountApproved+totalAmountNotApproved)){ // // alert('Total Incurred tidak sama dengan Total Approve + Total Not Approve') // // setValue('amount_approved', data?.amount_approved) @@ -203,6 +267,10 @@ export default function DialogEditBenefit({id, data, setOpenDialog, openDialog, name={`amount_not_approved`} placeholder='Amount Not Approved' required + onChange={(event) => { + setValue(`amount_not_approved`, event.target.value) + handleOnChangeNotApprove(id, event.target.value)} + } /> diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG copy.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG copy.tsx new file mode 100755 index 00000000..88959ff9 --- /dev/null +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG copy.tsx @@ -0,0 +1,284 @@ +import MuiDialog from "@/components/MuiDialog"; +import { Autocomplete, Button, Card, Checkbox, DialogActions, Grid, TextField, Typography } from "@mui/material"; +import { Paper } from "@mui/material"; +import { Stack } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { DetailFinalLogType } from "../Model/Types"; +import { fDateOnly, fDateTimesecond, toTitleCase } from "@/utils/formatTime"; +import axios from "@/utils/axios"; +import { enqueueSnackbar } from "notistack"; +import { useNavigate } from "react-router"; + + +type DialogConfirmationType = { + openDialog: boolean; + setOpenDialog: any; + onSubmit?: void; + requestLog: DetailFinalLogType|undefined; +} + +export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialog, onSubmit} : DialogConfirmationType ) { + + 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, + icdCodes: requestLog?.diagnosis + ? requestLog?.diagnosis.map(diagnosis => diagnosis.code) + : [], + reason: requestLog?.reason + }); + + const [icdOptions, setIcdOptions] = useState([ + { value: '-', label: '-' } + ]); + + 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 + + useEffect(() => { + 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 + ? requestLog.diagnosis.map(diagnosis => diagnosis.code) + : [], + reason: requestLog.reason + }); + } + }, [requestLog]); + + const handleChange = (field, value) => { + setFormData((prevData) => ({ + ...prevData, + [field]: value, + })); + if (field === 'reason') { + setIsReasonSelected(!!value); + } + + }; + + const handleApprove = () => { + setFormData((prevData) => ({ + ...prevData, + })); + handleSubmit(); + }; + + + const handleSubmit = () => { + if (isReasonSelected && formData.reason !== '') { + axios + .post(`customer-service/request/final-log`, formData) + .then((response) => { + enqueueSnackbar('Verification Request LOG Success', { variant: 'success' }); + setOpenDialog(false); + navigate('/custormer-service/final-log/detail/' + requestLog?.id) + window.location.reload() + }) + .catch(({ response }) => { + enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' }); + }); + } else { + setIsReasonSelected(false); + alert('Silakan pilih alasan sebelum mengirimkan data.'); + } + } + + + const style1 = { + color: '#919EAB', + width: '30%' + } + const style2 = { + width: '70%' + } + const marginBottom1 = { + marginBottom: 1, + } + const marginBottom2 = { + marginBottom: 2, + } + + const resetForm = () => { + 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) + : [], + reason: requestLog?.reason + }); + }; + + const [isReasonSelected, setIsReasonSelected] = useState(true); + + const handleCloseDialog = () => { + setOpenDialog(false); + resetForm(); + } + + const reasons = [ + { value: 'agreement', label: 'Agreement changed' }, + { value: 'endorsement', label: 'Endorsement' }, + { value: 'renewal', label: 'Renewal' }, + { value: 'wrong_setting', label: 'Wrong Setting' }, + // Add more options as needed + ]; + + + + const getContent = () => ( + + Are you sure to edit this final log ? + + + + Member ID + {requestLog?.member_id} + + + Policy Number + {requestLog?.policy_number} + + + Name + {requestLog?.name} + + + Submission Date + {requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'} + + + Claim Method + {requestLog?.claim_method ? toTitleCase(requestLog?.claim_method) : '-'} + + + Service Type + {requestLog?.service_type} + + + + + + Invoice Number + handleChange('invoice_no', e.target.value)} + /> + + + Billing Number + handleChange('billing_no', e.target.value)} + /> + + + Discharge Date + handleChange('discharge_date', e.target.value)} + /> + + + Catatan + handleChange('catatan', e.target.value)} + /> + + + Diagnosis ICD - X + option.label} + fullWidth + value={icdOptions.filter((icd) => formData.icdCodes.includes(icd.value))} + onChange={(e, newValues) => { + const selectedCodes = newValues.map((value) => value.value); + setFormData({ ...formData, icdCodes: selectedCodes }); + }} + renderInput={(params) => ( + + )} + /> + + + Reason* + option.label} + fullWidth + value={reasons.find((r) => r.value === formData.reason) || null} // Use find to match the default value + onChange={(e, newValue) => handleChange('reason', newValue?.value)} + renderInput={(params) => ( + + )} + /> + + + + + + + + + ); + + + return ( + + ); +} \ No newline at end of file diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx index 88959ff9..3da8b4dc 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Components/DialogEditFinalLOG.tsx @@ -26,9 +26,7 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo discharge_date: requestLog?.discharge_date, id: requestLog?.id, catatan: requestLog?.catatan, - icdCodes: requestLog?.diagnosis - ? requestLog?.diagnosis.map(diagnosis => diagnosis.code) - : [], + icdCodes: requestLog?.diagnosis, reason: requestLog?.reason }); @@ -36,8 +34,10 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo { value: '-', label: '-' } ]); + const [searchIcd, setSearchIcd] = useState(''); + useEffect(() => { - // Ambil data dari API dan atur opsi ICD + // Ambil data dari API dan atur opsi ICD axios.get('diagnosis') .then((response) => { setIcdOptions(response.data.data); @@ -45,23 +45,23 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo .catch((error) => { console.error('Error fetching ICD options:', error); }); + }, []); // useEffect dijalankan hanya sekali saat komponen dimount useEffect(() => { - 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 - ? requestLog.diagnosis.map(diagnosis => diagnosis.code) - : [], - reason: requestLog.reason - }); - } - }, [requestLog]); + 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, + reason: requestLog.reason + }); + } + }, [requestLog]); + const handleChange = (field, value) => { setFormData((prevData) => ({ @@ -80,6 +80,17 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo })); handleSubmit(); }; + + const handleSearch = (search) => { + setSearchIcd(search); + axios.get('diagnosis?search=' + search) + .then((response) => { + setIcdOptions(response.data.data); + }) + .catch((error) => { + console.error('Error fetching ICD options:', error); + }); + } const handleSubmit = () => { @@ -100,8 +111,7 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo alert('Silakan pilih alasan sebelum mengirimkan data.'); } } - - + const style1 = { color: '#919EAB', width: '30%' @@ -123,13 +133,10 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo billing_no: requestLog?.billing_no, invoice_no: requestLog?.invoice_no, catatan: requestLog?.catatan, - icdCodes: requestLog?.diagnosis - ? requestLog?.diagnosis.map(diagnosis => diagnosis.code) - : [], + icdCodes: requestLog?.diagnosis, reason: requestLog?.reason }); }; - const [isReasonSelected, setIsReasonSelected] = useState(true); const handleCloseDialog = () => { @@ -227,11 +234,10 @@ export default function DialogEditFinalLOG({requestLog, setOpenDialog, openDialo options={icdOptions} getOptionLabel={(option) => option.label} fullWidth - value={icdOptions.filter((icd) => formData.icdCodes.includes(icd.value))} - onChange={(e, newValues) => { - const selectedCodes = newValues.map((value) => value.value); - setFormData({ ...formData, icdCodes: selectedCodes }); - }} + value={formData.icdCodes} + onChange={(e, newValues) => handleChange('icdCodes', newValues)} + inputValue={searchIcd} + onInputChange={(e, newInputValue) => handleSearch(newInputValue)} renderInput={(params) => ( option.label} fullWidth - value={reasons.find((r) => r.value === formData.reason) || null} // Use find to match the default value + value={reasons.find((r) => r.value == formData.reason) || null} // Use find to match the default value onChange={(e, newValue) => handleChange('reason', newValue?.value)} renderInput={(params) => ( 0 ? (
    {requestLog.diagnosis.map((diagnosisItem, index) => ( -
  • {diagnosisItem.code} - {diagnosisItem.name}
  • +
  • {diagnosisItem.value} - {diagnosisItem.label}
  • // Replace 'name' with the property you want to display ))}
diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx index 3ce0da8d..9d997ac4 100755 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/Model/Types.tsx @@ -61,12 +61,13 @@ export type DetailFinalLogType = { exclusion : Exclusion[], medicine : Medicine[], files : file[], + member_usage_benefit : number } export type Diagnosis = { id : number, - name : string, - code : string + value : string, + label : string } export type BenefitData = { @@ -85,6 +86,7 @@ export type BenefitData = { export type BenefitConfigurationListType = { request_log_id: number|undefined, benefit_name: string, + benefit_id: number, benefit: { description: string }, diff --git a/frontend/dashboard/src/pages/Report/DoctorRating_v2/Index.tsx b/frontend/dashboard/src/pages/Report/DoctorRating_v2/Index.tsx new file mode 100755 index 00000000..5fdb052a --- /dev/null +++ b/frontend/dashboard/src/pages/Report/DoctorRating_v2/Index.tsx @@ -0,0 +1,35 @@ +import { Card, Grid, Container } from '@mui/material'; +import { useParams } from 'react-router-dom'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import Page from '../../../components/Page'; +import useSettings from '../../../hooks/useSettings'; +import List from './List'; + +export default function Index() { + const { themeStretch } = useSettings(); + + const { id } = useParams(); + + const pageTitle = 'Doctor Ratings'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/DoctorRating_v2/List.tsx b/frontend/dashboard/src/pages/Report/DoctorRating_v2/List.tsx new file mode 100755 index 00000000..0a28c53c --- /dev/null +++ b/frontend/dashboard/src/pages/Report/DoctorRating_v2/List.tsx @@ -0,0 +1,1026 @@ +// @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('dCreateOn'); + const [perPage, setPerPage] = useState(0); + + const handleChange = (event, newValue) => { + // Jika newValue tidak undefined, atur nilai dataProvider + if (newValue !== undefined) { + setDataProvider(newValue.service_code); + } else { + // Jika tidak ada yang dipilih, set dataProvider menjadi string kosong + setDataProvider(null); + } + }; + // Dummy data + const dummyServices = [ + { service_code: '1', name: 'Service 1' }, + { service_code: '2', name: 'Service 2' }, + { service_code: '3', name: 'Service 3' }, + // tambahkan data lain sesuai kebutuhan + ]; + + + + const handleSelectAll = () => { + setSelectAll(!selectAll); + if (!selectAll) { + const requestedIds = dataTableData.data + .filter(row => row.status === 'approved') // Memfilter baris dengan status 'requested' + .map(row => row.id); // Mengambil hanya ID dari baris-baris yang memenuhi kondisi + setSelectedRows(requestedIds); + } else { + setSelectedRows([]); + } + }; + + const handleRowSelect = (id) => { + if (selectedRows.includes(id)) { + setSelectedRows(selectedRows.filter(rowId => rowId !== id)); + } else { + setSelectedRows([...selectedRows, id]); + } + }; + + const [searchParams, setSearchParams] = useSearchParams(); + const [startDate, setStartDate] = useState(null); + const [searchText, setSearchText] = useState(''); + const [endDate, setEndDate] = useState(null); + const navigate = useNavigate(); + const [dataProvider, setDataProvider] = useState(null); + + useEffect(() => { + if (startDate !== null || endDate !== null || dataProvider !== null + || order !== null || orderBy !== null || perPage !== 0) { + loadDataTableData(); + getProvider(); + } + }, [startDate, endDate, dataProvider, order, orderBy, perPage]); + + const [isLoading, setIsLoading] = useState(false); + const [isLoadingImport, setIsLoadingImport] = useState(false); + const handleExportReport = async () => { + + + const year = startDate?.getFullYear(); + const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate = year && month && day ? `${year}-${month}-${day}` : ''; + + const year1 = endDate?.getFullYear(); + const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : ''; + + + + var filter = Object.fromEntries([...searchParams.entries()]); + setIsLoading(true) + await axios + .get('/export-doctorrating',{ + params: { + search: searchText, + start_date: formattedDate ? formattedDate : null, + end_date:formattedDate1, + provider: dataProvider, + order: order, + orderBy: orderBy, + page: perPage, + } + }) + .then((res) => { + enqueueSnackbar('Data berhasil di Export', { + variant: 'success', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }); + setIsLoading(false) + + document.location.href = res.data.data.file_url; + }) + .catch((err) => + enqueueSnackbar('Data Gagal di Export', { + variant: 'error', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }) + + ); + }; + + + function SearchInput(props: any) { + // SEARCH + const searchInput = useRef(null); + + + + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + props.onSearch({ search: searchText }); // Trigger to Parent + }; + + const handleGetData = (type :string) => { + axios.get(`claims/1/data-claim`) + .then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + }) + } + + useEffect(() => { + // Trigger First Search + // setSearchText(searchParams.get('search') ?? ''); + }, []); + + return ( +
+ + + + +
+ ); + } + + function ImportForm(props: any) { + // IMPORT + // Create Button Menu + const [anchorEl, setAnchorEl] = React.useState(null); + + return ( +
+ + + {/* */} + +
+ ); + } + + const searchInput = useRef(null); + + + //handle search + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + loadDataTableData(); + }; + + + + useEffect(() => { + // Trigger First Search + //setSearchText(searchText); + }, []); + + const item = [ + { + id: '', + value: '', + name: 'Semua', + }, + ]; + + // const handleClick = () => { + + // } + + + + // Dummy Default Data + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const [dataTableData, setDataTableData] = useState( + LaravelPaginatedDataDefault + ); + + + + const loadDataTableData = async (appliedFilter: any | null = null) => { + setDataTableLoading(true); + const year = startDate?.getFullYear(); + const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate = year && month && day ? `${year}-${month}-${day}` : ''; + + const year1 = endDate?.getFullYear(); + const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : ''; + + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + const response = await axios.get('/get-doctorrating', { + params: { + search: searchText, + start_date: formattedDate ? formattedDate : null, + end_date:formattedDate1, + provider: dataProvider, + order: order, + orderBy: orderBy, + page: perPage, + } + }); + + setDataTableLoading(false); + + setDataTableData(response.data); + }; + + const getProvider = async () => { + const response = await axios.get('/claims/get-provider'); + setProviders(response.data) + } + + const applyFilter = async (searchFilter: { search: string }) => { + await loadDataTableData(searchFilter); + setSearchParams(searchFilter); + }; + + const handlePageChange = (event: ChangeEvent, value: number): void => { + setPerPage(value); + }; + + const [openDialogSubmit, setOpenDialogSubmit] = useState(false); + const handleCloseDialogSubmit = () => { + setOpenDialogSubmit(false); + } + + function toTitleCase(str: string | null) { + return str.replace(/\w\S*/g, function(txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); + } + + const [approve, setApprove] = useState(''); + + const [reasonDecline, setReasonDecline] = useState(''); + + const handleReasonDeclineChange = (event) => { + setReasonDecline(event.target.value); + // Tambahkan logika yang diperlukan di sini + }; + + const handleSubmitData = async () => { + try { + const response = await axios.post('download-zip', { selectedRows: selectedRows }); + const fileUrl = response.data.file_url; // Perbaikan disini + enqueueSnackbar('Data berhasil di download', { variant: 'success' }); + window.open(fileUrl, '_blank'); + setOpenDialogSubmit(false); + setTimeout(() => { + window.location.reload(); + }, 5000); // Reload the page after 5 seconds + } catch (error) { + enqueueSnackbar('Data Gagal di download', { variant: 'error' }); + } + }; + + const handleSubmitData1 = () => { + //approve or decline + if (!reasonDecline && approve == 'decline') { + enqueueSnackbar('Mohon isi alasan', { variant: 'warning' }); + return false; + } + Promise.all(selectedRows.map(send_bulk)) + .then(() => { + enqueueSnackbar('All requests processed successfully', { variant: 'success' }); + setOpenDialogSubmit(false); + setTimeout(() => { + window.location.reload(); + }, 5000); // Reload the page after 5 seconds + }) + .catch((error) => { + enqueueSnackbar(error.response?.data?.message ?? 'Something went wrong!', { variant: 'error' }); + }); + }; + + function send_bulk(id) { + return axios.post(`claims/${id}/${approve}`, { reasonDecline: reasonDecline }); + } + + + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); + const importClaimManagement = useRef(null); + const [currentImportFileName, setCurrentImportFileName] = useState(null); + const [importLoading, setImportLoading] = useState(false); + const [importResult, setImportResult] = useState(null); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const handleImportButton = () => { + if (importClaimManagement?.current) { + handleClose(); + importClaimManagement.current ? importClaimManagement.current.click() : console.log('No File selected'); + } else { + alert('No file selected'); + } + }; + const handleCancelImportButton = () => { + if(importClaimManagement.current) + { + importClaimManagement.current.value = ''; + importClaimManagement.current.dispatchEvent(new Event('change', { bubbles: true })); + } + }; + const handleImportChange = (event: any) => { + if (event.target.files[0]) { + setCurrentImportFileName(event.target.files[0].name); + } else { + setCurrentImportFileName(null); + } + }; + const handleUpload = () => { + if(importClaimManagement.current && importClaimManagement.current.files) + { + if (importClaimManagement.current?.files.length) { + const formData = new FormData(); + formData.append('file', importClaimManagement.current?.files[0]); + setImportLoading(true); + axios + .post('claims/import', formData) + .then((response) => { + handleCancelImportButton(); + loadDataTableData(); + setImportResult(response.data); + setImportLoading(false); + enqueueSnackbar('Success Import Claim Managemenet', { variant: 'success' }); + }) + .catch((response) => { + enqueueSnackbar( + 'Looks like something went wrong. Please check your data and try again. ' + + response.message, + { variant: 'error' } + ); + setImportLoading(false); + }); + } else { + enqueueSnackbar('No File Selected', { variant: 'warning' }); + } + } + }; + const handleGetTemplate = () => { + axios.get('claims/download-template').then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + }); + + + }; + + const handleExportReportFiled = async () => { + + await axios + .post('claims/exportFiled', { params: importResult?.data.result_rows }) + .then((res) => { + enqueueSnackbar('Data berhasil di Export', { + variant: 'success', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }); + setIsLoading(false) + + document.location.href = res.data.data.file_url; + }) + .catch((err) => + enqueueSnackbar('Data Gagal di Export', { + variant: 'error', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }) + + ); + }; + + + // useEffect(() => { + // loadDataTableData(); + // getProvider(); + // }, []); + + const headStyle = { + fontWeight: 'bold', + }; + // const headCells = [ + // { + // id: 'dCreateOn', + // align: 'left', + // label: 'Date Submission', + // isSort: true, + // }, + // { + // id: 'code', + // align: 'left', + // label: 'Code', + // isSort: true, + // }, + // { + // id: 'name', + // align: 'left', + // label: 'Name', + // isSort: false, + // }, + // { + // id: 'provider', + // align: 'left', + // label: 'Provider', + // isSort: false, + // }, + // { + // id: 'files', + // align: 'left', + // label: 'Nama File', + // isSort: false, + // }, + // ]; + const headCells = [ + { + id: 'nama_peserta', + align: 'left', + label: 'Nama Peserta', + isSort: true, + }, + { + id: 'nama_dokter', + align: 'left', + label: 'Nama Dokter', + isSort: true, + }, + { + id: 'rating', + align: 'left', + label: 'Rating', + isSort: true, + }, + { + id: 'review', + align: 'left', + label: 'Review', + isSort: false, + }, + { + id: 'dCreateOn', + align: 'left', + label: 'Tanggal Konsultasi', + isSort: true, + }, + ]; + + const orders = { + order: order, + setOrder: setOrder, + orderBy: orderBy, + setOrderBy: setOrderBy, + }; + const createSortHandler = (property: string) => (event: React.MouseEvent) => { + handleRequestSort(event, property); + }; + const handleRequestSort = async (event: React.MouseEvent, property: string) => { + const isAsc = orders?.orderBy === property && orders?.order === 'asc'; + + orders?.setOrder(isAsc ? 'desc' : 'asc'); + orders?.setOrderBy(property); + }; + // Called on every row to map the data to the columns + function createData(data: Claims): Claims { + return { + ...data, + }; + } + + { + /* ------------------ TABLE ROW ------------------ */ + } + function Row(props: { row: ReturnType, isSelected: boolean, onSelect: (id: string) => void }) { + const { row, isSelected, onSelect } = props; + // Memperbaiki destrukturisasi props + + const handleRowCheckboxChange = () => { + onSelect(row.id); // Panggil fungsi onSelect dari komponen induk dengan id baris saat checkbox di baris diklik + }; + + const [open, setOpen] = React.useState(false); + + const test = 1000; + + return ( + + *': { borderBottom: 'unset' } }}> + {/* + setOpen(!open)}> + {open ? : } + + */} + {/* + + */} + {row?.nama_peserta} + {row?.nama_dokter} + {row?.rating} + {row?.sNotes} + {row?.dCreateOn ? fDateTime(row?.dCreateOn) : ''} + {/* {row?.dCreateOn ? fDateTime(row?.dCreateOn) : ''} */} + {/* {row?.code} */} + {/* {row.code} */} + {/* {row?.provider} */} + + {/* COLLAPSIBLE ROW */} + + + + {/* + + Description : {row.description} + + */} + + + + + ); + } + { + /* ------------------ END TABLE ROW ------------------ */ + } + + + + function TableContent() { + return ( + + {/* ------------------ TABLE HEADER ------------------ */} + + + {selectedRows.length > 0 ? ( + <> + + + + {selectedRows.length > 0 ? selectedRows.length : '0'}  Selected + + + + + + {/* + + */} + + + + + + ) : ( + <> + {/* + + */} + {headCells && + headCells.map((headCell, index) => ( + + {headCell.isSort ? ( + + {headCell.label} + {orders?.orderBy === headCell.id ? ( + + {orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + ) : ( + headCell.label + )} + + ))} + + + )} + + + + + {/* ------------------ END TABLE HEADER ------------------ */} + + {/* ------------------ TABLE ROW ------------------ */} + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length === 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} + {/* ------------------ END TABLE ROW ------------------ */} +
+ ); + } + + return ( + + + +
+ + + {!currentImportFileName && ( + <> + + { + if (event.key === 'Enter') { + handleSearchSubmit(event); + } + }} + InputProps={{ + startAdornment: ( + + + + ), + placeholder: 'Search Name', + }} + /> + + + + { + + // loadDataTableData(); + setStartDate(value); + }} + renderInput={(params) => } + /> + + + + { + setEndDate(value); + }} + renderInput={(params) => ( + + )} + /> + + + {/* + { + providers ? ( + option.name || ''} + value={providers.find((item) => item.id === dataProvider) || null} + onChange={(event, value) => { + if (value) { + setDataProvider(value.id); + } else { + setDataProvider(null); + } + }} + renderInput={(params) => ( + + )} + /> + ):( + <> + Loading... + + ) + } + */} + + {/* + } + sx={{ p: 1.8 }} + loading={isLoadingImport} + onClick={handleClick} + > + + Import + + + + + Import + + { + handleGetTemplate(); + }} + > + Download Template + + + */} + + } + sx={{ p: 1.8 }} + onClick={handleExportReport} + loading={isLoading} + > + + Export to Excel + + + + + + )} + {currentImportFileName && ( + + + + + + + + } + sx={{ p: 1.8 }} + onClick={handleUpload} + loading={importLoading} + > + Upload + + + + )} + {importResult && ( + + + Last Import Result :{' '} + + {importResult.data.total_success_row ?? 0} + {' '} + Row Processed,{' '} + + {importResult.data.total_failed_row} + {' '} + Failed, + {/* {importResult.data.failed_rows.map((row, index) => ( + [Code={row.code ? row.code : 'Required'}] + ))} */} +  Report: +  Download Data Result Import + + + )} + +
+
+
+ + } + /> + + + + + Confirmation + + + + + + + + + + Are you sure to Download this files selected ? + {approve == "decline" ? ( + + + + ): ''} + + + + + + + +
+ ); + } diff --git a/frontend/dashboard/src/pages/Report/KatalogDokter/Index.tsx b/frontend/dashboard/src/pages/Report/KatalogDokter/Index.tsx new file mode 100755 index 00000000..54f4553e --- /dev/null +++ b/frontend/dashboard/src/pages/Report/KatalogDokter/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 = 'Katalog Dokter & Profile'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/KatalogDokter/List.tsx b/frontend/dashboard/src/pages/Report/KatalogDokter/List.tsx new file mode 100755 index 00000000..65e4f327 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/KatalogDokter/List.tsx @@ -0,0 +1,1027 @@ +// @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('dSTRExpireDate'); + const [perPage, setPerPage] = useState(0); + + const handleChange = (event, newValue) => { + // Jika newValue tidak undefined, atur nilai dataProvider + if (newValue !== undefined) { + setDataProvider(newValue.service_code); + } else { + // Jika tidak ada yang dipilih, set dataProvider menjadi string kosong + setDataProvider(null); + } + }; + // Dummy data + const dummyServices = [ + { service_code: '1', name: 'Service 1' }, + { service_code: '2', name: 'Service 2' }, + { service_code: '3', name: 'Service 3' }, + // tambahkan data lain sesuai kebutuhan + ]; + + + + const handleSelectAll = () => { + setSelectAll(!selectAll); + if (!selectAll) { + const requestedIds = dataTableData.data + .filter(row => row.status === 'approved') // Memfilter baris dengan status 'requested' + .map(row => row.id); // Mengambil hanya ID dari baris-baris yang memenuhi kondisi + setSelectedRows(requestedIds); + } else { + setSelectedRows([]); + } + }; + + const handleRowSelect = (id) => { + if (selectedRows.includes(id)) { + setSelectedRows(selectedRows.filter(rowId => rowId !== id)); + } else { + setSelectedRows([...selectedRows, id]); + } + }; + + const [searchParams, setSearchParams] = useSearchParams(); + const [startDate, setStartDate] = useState(null); + const [searchText, setSearchText] = useState(''); + const [endDate, setEndDate] = useState(null); + const navigate = useNavigate(); + const [dataProvider, setDataProvider] = useState(null); + + useEffect(() => { + if (startDate !== null || endDate !== null || dataProvider !== null + || order !== null || orderBy !== null || perPage !== 0) { + loadDataTableData(); + getProvider(); + } + }, [startDate, endDate, dataProvider, order, orderBy, perPage]); + + const [isLoading, setIsLoading] = useState(false); + const [isLoadingImport, setIsLoadingImport] = useState(false); + const handleExportReport = async () => { + + + const year = startDate?.getFullYear(); + const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate = year && month && day ? `${year}-${month}-${day}` : ''; + + const year1 = endDate?.getFullYear(); + const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : ''; + + + + var filter = Object.fromEntries([...searchParams.entries()]); + setIsLoading(true) + await axios + .get('/export-dokter-katalog',{ + params: { + search: searchText, + start_date: formattedDate ? formattedDate : null, + end_date:formattedDate1, + provider: dataProvider, + order: order, + orderBy: orderBy, + page: perPage, + } + }) + .then((res) => { + enqueueSnackbar('Data berhasil di Export', { + variant: 'success', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }); + setIsLoading(false) + + document.location.href = res.data.data.file_url; + }) + .catch((err) => + enqueueSnackbar('Data Gagal di Export', { + variant: 'error', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }) + + ); + }; + + + function SearchInput(props: any) { + // SEARCH + const searchInput = useRef(null); + + + + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + props.onSearch({ search: searchText }); // Trigger to Parent + }; + + const handleGetData = (type :string) => { + axios.get(`claims/1/data-claim`) + .then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + }) + } + + useEffect(() => { + // Trigger First Search + // setSearchText(searchParams.get('search') ?? ''); + }, []); + + return ( +
+ + + + +
+ ); + } + + function ImportForm(props: any) { + // IMPORT + // Create Button Menu + const [anchorEl, setAnchorEl] = React.useState(null); + + return ( +
+ + + {/* */} + +
+ ); + } + + const searchInput = useRef(null); + + + //handle search + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + loadDataTableData(); + }; + + + + useEffect(() => { + // Trigger First Search + //setSearchText(searchText); + }, []); + + const item = [ + { + id: '', + value: '', + name: 'Semua', + }, + ]; + + // const handleClick = () => { + + // } + + + + // Dummy Default Data + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const [dataTableData, setDataTableData] = useState( + LaravelPaginatedDataDefault + ); + + + + const loadDataTableData = async (appliedFilter: any | null = null) => { + setDataTableLoading(true); + const year = startDate?.getFullYear(); + const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate = year && month && day ? `${year}-${month}-${day}` : ''; + + const year1 = endDate?.getFullYear(); + const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit + const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit + + const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : ''; + + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + const response = await axios.get('/get-dokter-katalog', { + params: { + search: searchText, + start_date: formattedDate ? formattedDate : null, + end_date:formattedDate1, + provider: dataProvider, + order: order, + orderBy: orderBy, + page: perPage, + } + }); + + setDataTableLoading(false); + + setDataTableData(response.data); + }; + + const getProvider = async () => { + const response = await axios.get('/claims/get-provider'); + setProviders(response.data) + } + + const applyFilter = async (searchFilter: { search: string }) => { + await loadDataTableData(searchFilter); + setSearchParams(searchFilter); + }; + + const handlePageChange = (event: ChangeEvent, value: number): void => { + setPerPage(value); + }; + + const [openDialogSubmit, setOpenDialogSubmit] = useState(false); + const handleCloseDialogSubmit = () => { + setOpenDialogSubmit(false); + } + + function toTitleCase(str: string | null) { + return str.replace(/\w\S*/g, function(txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); + } + + const [approve, setApprove] = useState(''); + + const [reasonDecline, setReasonDecline] = useState(''); + + const handleReasonDeclineChange = (event) => { + setReasonDecline(event.target.value); + // Tambahkan logika yang diperlukan di sini + }; + + const handleSubmitData = async () => { + try { + const response = await axios.post('download-zip', { selectedRows: selectedRows }); + const fileUrl = response.data.file_url; // Perbaikan disini + enqueueSnackbar('Data berhasil di download', { variant: 'success' }); + window.open(fileUrl, '_blank'); + setOpenDialogSubmit(false); + setTimeout(() => { + window.location.reload(); + }, 5000); // Reload the page after 5 seconds + } catch (error) { + enqueueSnackbar('Data Gagal di download', { variant: 'error' }); + } + }; + + const handleSubmitData1 = () => { + //approve or decline + if (!reasonDecline && approve == 'decline') { + enqueueSnackbar('Mohon isi alasan', { variant: 'warning' }); + return false; + } + Promise.all(selectedRows.map(send_bulk)) + .then(() => { + enqueueSnackbar('All requests processed successfully', { variant: 'success' }); + setOpenDialogSubmit(false); + setTimeout(() => { + window.location.reload(); + }, 5000); // Reload the page after 5 seconds + }) + .catch((error) => { + enqueueSnackbar(error.response?.data?.message ?? 'Something went wrong!', { variant: 'error' }); + }); + }; + + function send_bulk(id) { + return axios.post(`claims/${id}/${approve}`, { reasonDecline: reasonDecline }); + } + + + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); + const importClaimManagement = useRef(null); + const [currentImportFileName, setCurrentImportFileName] = useState(null); + const [importLoading, setImportLoading] = useState(false); + const [importResult, setImportResult] = useState(null); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const handleImportButton = () => { + if (importClaimManagement?.current) { + handleClose(); + importClaimManagement.current ? importClaimManagement.current.click() : console.log('No File selected'); + } else { + alert('No file selected'); + } + }; + const handleCancelImportButton = () => { + if(importClaimManagement.current) + { + importClaimManagement.current.value = ''; + importClaimManagement.current.dispatchEvent(new Event('change', { bubbles: true })); + } + }; + const handleImportChange = (event: any) => { + if (event.target.files[0]) { + setCurrentImportFileName(event.target.files[0].name); + } else { + setCurrentImportFileName(null); + } + }; + const handleUpload = () => { + if(importClaimManagement.current && importClaimManagement.current.files) + { + if (importClaimManagement.current?.files.length) { + const formData = new FormData(); + formData.append('file', importClaimManagement.current?.files[0]); + setImportLoading(true); + axios + .post('claims/import', formData) + .then((response) => { + handleCancelImportButton(); + loadDataTableData(); + setImportResult(response.data); + setImportLoading(false); + enqueueSnackbar('Success Import Claim Managemenet', { variant: 'success' }); + }) + .catch((response) => { + enqueueSnackbar( + 'Looks like something went wrong. Please check your data and try again. ' + + response.message, + { variant: 'error' } + ); + setImportLoading(false); + }); + } else { + enqueueSnackbar('No File Selected', { variant: 'warning' }); + } + } + }; + const handleGetTemplate = () => { + axios.get('claims/download-template').then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + }); + + + }; + + const handleExportReportFiled = async () => { + + await axios + .post('claims/exportFiled', { params: importResult?.data.result_rows }) + .then((res) => { + enqueueSnackbar('Data berhasil di Export', { + variant: 'success', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }); + setIsLoading(false) + + document.location.href = res.data.data.file_url; + }) + .catch((err) => + enqueueSnackbar('Data Gagal di Export', { + variant: 'error', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }) + + ); + }; + + + // useEffect(() => { + // loadDataTableData(); + // getProvider(); + // }, []); + + const headStyle = { + fontWeight: 'bold', + }; + // const headCells = [ + // { + // id: 'dCreateOn', + // align: 'left', + // label: 'Date Submission', + // isSort: true, + // }, + // { + // id: 'code', + // align: 'left', + // label: 'Code', + // isSort: true, + // }, + // { + // id: 'name', + // align: 'left', + // label: 'Name', + // isSort: false, + // }, + // { + // id: 'provider', + // align: 'left', + // label: 'Provider', + // isSort: false, + // }, + // { + // id: 'files', + // align: 'left', + // label: 'Nama File', + // isSort: false, + // }, + // ]; + const headCells = [ + { + id: 'nama_dokter', + align: 'left', + label: 'Nama Dokter', + isSort: true, + }, + { + id: 'lulusan', + align: 'left', + label: 'Lulusan', + isSort: true, + }, + { + id: 'str', + align: 'left', + label: 'STR', + isSort: false, + }, + { + id: 'sip', + align: 'left', + label: 'SIP', + isSort: false, + }, + { + id: 'tempat_praktek', + align: 'left', + label: 'Tempat Praktek', + isSort: false, + }, + ]; + + const orders = { + order: order, + setOrder: setOrder, + orderBy: orderBy, + setOrderBy: setOrderBy, + }; + const createSortHandler = (property: string) => (event: React.MouseEvent) => { + handleRequestSort(event, property); + }; + const handleRequestSort = async (event: React.MouseEvent, property: string) => { + const isAsc = orders?.orderBy === property && orders?.order === 'asc'; + + orders?.setOrder(isAsc ? 'desc' : 'asc'); + orders?.setOrderBy(property); + }; + // Called on every row to map the data to the columns + function createData(data: Claims): Claims { + return { + ...data, + }; + } + + { + /* ------------------ TABLE ROW ------------------ */ + } + function Row(props: { row: ReturnType, isSelected: boolean, onSelect: (id: string) => void }) { + const { row, isSelected, onSelect } = props; + // Memperbaiki destrukturisasi props + + const handleRowCheckboxChange = () => { + onSelect(row.id); // Panggil fungsi onSelect dari komponen induk dengan id baris saat checkbox di baris diklik + }; + + const [open, setOpen] = React.useState(false); + + const test = 1000; + + return ( + + *': { borderBottom: 'unset' } }}> + {/* + setOpen(!open)}> + {open ? : } + + */} + {/* + + */} + {row?.nama_dokter} + {row?.lulusan} + {row?.str} + {row?.sip} + {row?.tempat_praktek} + {/* {row?.dCreateOn ? fDateTime(row?.dCreateOn) : ''} */} + {/* {row?.dCreateOn ? fDateTime(row?.dCreateOn) : ''} */} + {/* {row?.code} */} + {/* {row.code} */} + {/* {row?.provider} */} + + {/* COLLAPSIBLE ROW */} + + + + {/* + + Description : {row.description} + + */} + + + + + ); + } + { + /* ------------------ END TABLE ROW ------------------ */ + } + + + + function TableContent() { + return ( + + {/* ------------------ TABLE HEADER ------------------ */} + + + {selectedRows.length > 0 ? ( + <> + + + + {selectedRows.length > 0 ? selectedRows.length : '0'}  Selected + + + + + + {/* + + */} + + + + + + ) : ( + <> + {/* + + */} + {headCells && + headCells.map((headCell, index) => ( + + {headCell.isSort ? ( + + {headCell.label} + {orders?.orderBy === headCell.id ? ( + + {orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'} + + ) : null} + + ) : ( + headCell.label + )} + + ))} + + + )} + + + + + {/* ------------------ END TABLE HEADER ------------------ */} + + {/* ------------------ TABLE ROW ------------------ */} + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length === 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} + {/* ------------------ END TABLE ROW ------------------ */} +
+ ); + } + + return ( + + + +
+ + + {!currentImportFileName && ( + <> + + { + if (event.key === 'Enter') { + handleSearchSubmit(event); + } + }} + InputProps={{ + startAdornment: ( + + + + ), + placeholder: 'Search Name', + }} + /> + + + + { + + // loadDataTableData(); + setStartDate(value); + }} + renderInput={(params) => } + /> + + + + { + setEndDate(value); + }} + renderInput={(params) => ( + + )} + /> + + + {/* + { + providers ? ( + option.name || ''} + value={providers.find((item) => item.id === dataProvider) || null} + onChange={(event, value) => { + if (value) { + setDataProvider(value.id); + } else { + setDataProvider(null); + } + }} + renderInput={(params) => ( + + )} + /> + ):( + <> + Loading... + + ) + } + */} + + {/* + } + sx={{ p: 1.8 }} + loading={isLoadingImport} + onClick={handleClick} + > + + Import + + + + + Import + + { + handleGetTemplate(); + }} + > + Download Template + + + */} + + } + sx={{ p: 1.8 }} + onClick={handleExportReport} + loading={isLoading} + > + + Export to Excel + + + + + + )} + {currentImportFileName && ( + + + + + + + + } + sx={{ p: 1.8 }} + onClick={handleUpload} + loading={importLoading} + > + Upload + + + + )} + {importResult && ( + + + Last Import Result :{' '} + + {importResult.data.total_success_row ?? 0} + {' '} + Row Processed,{' '} + + {importResult.data.total_failed_row} + {' '} + Failed, + {/* {importResult.data.failed_rows.map((row, index) => ( + [Code={row.code ? row.code : 'Required'}] + ))} */} +  Report: +  Download Data Result Import + + + )} + +
+
+
+ + } + /> + + + + + Confirmation + + + + + + + + + + Are you sure to Download this files selected ? + {approve == "decline" ? ( + + + + ): ''} + + + + + + + +
+ ); + } diff --git a/frontend/dashboard/src/pages/Report/Prescription/Index.tsx b/frontend/dashboard/src/pages/Report/Prescription/Index.tsx index ff5c923e..f6ee8766 100755 --- a/frontend/dashboard/src/pages/Report/Prescription/Index.tsx +++ b/frontend/dashboard/src/pages/Report/Prescription/Index.tsx @@ -1,37 +1,35 @@ import { Card, Grid, Container } from '@mui/material'; import { useParams } from 'react-router-dom'; -import HeaderBreadcrumbs from '@/components/HeaderBreadcrumbs'; -import Page from '@/components/Page'; -import useSettings from '@/hooks/useSettings'; -import List from '../Prescription/List'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import Page from '../../../components/Page'; +import useSettings from '../../../hooks/useSettings'; +import List from './List'; -export default function Prescription(){ - const { themeStretch } = useSettings(); +export default function LinksehatPayments() { + const { themeStretch } = useSettings(); - const { id } = useParams(); + const { id } = useParams(); - const pageTitle = 'Prescription'; + const pageTitle = 'Resep Online'; + return ( + + + - return( - - - - - - - - - ); -} \ No newline at end of file + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/Prescription/List.tsx b/frontend/dashboard/src/pages/Report/Prescription/List.tsx index 7ca00adb..7ea1d96e 100755 --- a/frontend/dashboard/src/pages/Report/Prescription/List.tsx +++ b/frontend/dashboard/src/pages/Report/Prescription/List.tsx @@ -26,6 +26,8 @@ import { Autocomplete, InputAdornment, IconButton, + InputLabel, + Menu, } from '@mui/material'; import { @@ -39,6 +41,7 @@ import { import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; import useSettings from '../../../hooks/useSettings'; // components +import AutocompleteHealthcare from '@/components/autocomplete/AutocompleteHealthcare'; import axios from '../../../utils/axios'; import { LaravelPaginatedData } from '../../../@types/paginated-data'; import { Icd } from '../../../@types/diagnosis'; @@ -49,9 +52,8 @@ import { Props } from '../../../components/editor/index'; import { red } from '@mui/material/colors'; import { margin, padding } from '@mui/system'; import { enqueueSnackbar } from 'notistack'; +import { fNumber } from '@/utils/formatNumber'; import { Controller } from 'react-hook-form'; -import { User } from '../../../Models/User'; - import SvgIconStyle from '../../../components/SvgIconStyle'; import { GridSearchIcon } from '@mui/x-data-grid'; @@ -59,370 +61,520 @@ import { Search } from '@mui/icons-material'; import { Icon } from '@iconify/react'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import { MenuItem } from '@mui/material'; +import { fDateOnly, fDateTime } from '@/utils/formatTime'; +import AutocompleteLinksehatHealthcare from '@/components/autocomplete/AutocompleteLinksehatHealthcare'; +import { LoadingButton } from '@mui/lab'; +import UploadIcon from '@mui/icons-material/Upload'; -export default function List(){ +// ---------------------------------------------------------------------- + +export default function List() { + // Generate the every row of the table const navigate = useNavigate(); const { organization_id } = useParams(); const [searchParams, setSearchParams] = useSearchParams(); + const [organizationOptions, setOrganizationOptions] = useState([]); + const [searchParamsPaymentStatus, setSearchParamsPaymentStatus] = useSearchParams(); const [searchParamsOrganizations, setSearchParamsOrganizations] = useSearchParams(); const [searchParamsSpecialities, setSearchParamsSpecialities] = useSearchParams(); const [searchParamsFilter, setSearchParamsFilter] = useSearchParams(); + useEffect(() => { + // axios.get(`/search-organizations`).then((response) => { + // setOrganizationOptions(response.data); + // }); + }, []); + function Filter(props: any) { - // SEARCH - const searchInput = useRef(null); - const [searchText, setSearchText] = useState(''); - - //handle search - const handleSearchChange = (event: any) => { - const newSearchText = event.target.value ?? ''; - setSearchText(newSearchText); - }; - - const handleSearchSubmit = (event: any) => { - event.preventDefault(); - - props.onSearch(searchText); - }; + // SEARCH + const searchInput = useRef(null); + const [searchText, setSearchText] = useState(''); + const [importLoading, setImportLoading] = useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); - useEffect(() => { - // Trigger First Search - setSearchText(searchParams.get('search') ?? ''); - }, []); - - const item = [ - { - id: '', - value: '', - name: 'Semua', - }, - ]; + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; - return ( -
- - - { - if (event.key === 'Enter') { - handleSearchSubmit(event); + /* ------------------------------ handle params ----------------------------- */ + const [appliedParams, setAppliedParams] = useState({}); + const params = { + searchParams: searchParams, + setSearchParams: setSearchParams, + appliedParams: appliedParams, + setAppliedParams: setAppliedParams, + }; + + const handleGetData = (type :string) => { + const parameters = + Object.keys(appliedParams).length !== 0 + ? appliedParams + : Object.fromEntries([...searchParams.entries()]); + setImportLoading(true); + axios.get('/linksehat/prescription/generate-excel', { + params: { ...parameters }, + }).then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + setImportLoading(false); + }); + // axios.get(`report/logs/export`) + // .then((response) => { + // const link = document.createElement('a'); + // link.href = response.data.data.file_url; + // link.setAttribute('download', response.data.data.file_name); + // document.body.appendChild(link); + // link.click(); + // handleClose(); + // }) + } + + //handle search + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + + props.onSearch(searchText); + }; + + useEffect(() => { + // Trigger First Search + setSearchText(searchParams.get('search') ?? ''); + }, []); + + + + return ( + + + + { + if (event.key === 'Enter') { + // handleSearchSubmit(event); + + const filter = Object.fromEntries([ + ...searchParams.entries(), + ['search', searchText], + ]); + setSearchParams(filter); + loadDataTableData(filter); + } + }} + label="Search" + value={searchText} + InputProps={{ + // startAdornment: ( + // + // + // + // ), + placeholder: 'Nama Pasien', + }} + /> + + + + { + try { + if (value && !!Date.parse(value)) { + const date = value ? fDateOnly(value) : ''; + var entries = [...searchParams.entries(), ['prescription_start', date ?? '']]; + if (!searchParams.get('prescription_end')) { + entries = [...entries, ['prescription_end', date ?? '']]; } - }} - value={searchText} - InputProps={{ - startAdornment: ( - - - - ), - placeholder: 'Search', - }} + const filter = Object.fromEntries(entries); + + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => } + /> + + + + + + { + try { + if (value && !!Date.parse(value)) { + const date = fDateOnly(value); + var entries = [...searchParams.entries(), ['prescription_end', date ?? '']]; + if (!searchParams.get('prescription_start')) { + entries = [...entries, ['prescription_start', date ?? '']]; + } + const filter = Object.fromEntries(entries); + + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => ( + - - - - ); -} -function FilterForm(props: any) { - return( + )} + /> + + + + + } + sx={{ p: 1.8 }} + onClick={handleClick} + loading={importLoading} + > + Export + + + {handleGetData('')}}>Download Excel + + +
+ + ); + } + + function FilterForm(props: any) { + // IMPORT + return ( - - + container + spacing={2} + sx={{ p: 2, justifyContent: 'space-between', alignItems: 'center' }} + > + + + - - ); -} + ); + } -function createData(doctor: Practitioner): Practitioner { - return { - ...doctor, - /* user: doctor.user ? new User(doctor.user) : null; */ - }; -} + //TODO Create PaymentType + function createData(payments: any): any { + return { + ...payments, + }; + } -function Row(props: { row: ReturnType }) { - const { row } = props; - const [open, setOpen] = React.useState(false); - const [openDialog, setOpenDialog] = React.useState(false); + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(true); + const [openDialog, setOpenDialog] = React.useState(false); + const handleDelete = (model: any) => { + axios + .delete(`/doctors/${row.id}`) + .then((res) => { + setDataTableData({ + ...dataTableData, + data: dataTableData.data.filter((model) => model.id != row.id), + }); + enqueueSnackbar('Data berhasil dihapus', { variant: 'success' }); + }) + .catch((error) => { + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; + return ( + + + + setOpen(!open)}> + {open ? : } + + + {row.prescription_code ?? '-'} + {row.date_consultation ? fDateTime(row.date_consultation) : '-'} + {row.patient_name ?? '-'} + {row.doctor_name ?? '-'} + {/* + + + + + + */} + - return ( - - - - setOpen(!open)}> - {open ? : } - - + {/* COLLAPSIBLE ROW */} + + + + + + + + + Jenis Obat (Drugs) + + + Jumlah Obat (QTY) + + + Cara Minum Obat + - {row.user ? row.user.sFirstName : '-'} - {row.nIDDokter ? row.nIDDokter : '-'} - {row.nRating ? row.nRating : '-'} - {row.sNotes ? row.sNotes : '-'} - {row.dCreateOn ? row.dCreateOn : '-'} + {row.items?.map((item) => ( + + + {item.sItemName} + + + {item.nQty} + + + {item.sSigna} + + + ))} - {/* - - - - - - */} - - {/* COLLAPSIBLE ROW */} - - -{/* - - - - - - Metode Pembayaran - - - : {row.payment_method ? row.payment_method : '-'} - - - - Jenis Benefit - - - : - - - - Durasi - - - : {row.duration ? row.duration : '-'} - - - */} - - + + +
+ + + {/* END COLLAPSIBLE ROW */} - {/* END COLLAPSIBLE ROW */} - { - setOpenDialog(false); - }} - aria-labelledby="alert-dialog-title" - aria-describedby="alert-dialog-description" - > - - - - Apakah anda yakin ingin menghapus - - - {row.name}? - - - - - {/* */} - - - + { + setOpenDialog(false); + }} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + + + + Apakah anda yakin ingin menghapus + + + {row.name}? + + + + + + + + + ); + } + + const headStyle = { + fontWeight: 'bold', + }; + // Dummy Default Data + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const [dataTableLastRequest, setDataTableLastRequest] = useState(0); + const [dataTableResponseState, setDataTableResponseState] = useState('idle'); + const [dataTableData, setDataTableData] = useState({ + current_page: 1, + data: [], + path: '', + first_page_url: '', + last_page: 1, + last_page_url: '', + next_page_url: '', + prev_page_url: '', + per_page: 10, + from: 0, + to: 0, + total: 0, + }); + const [dataTablePage, setDataTablePage] = useState(5); + + const loadDataTableData = async (appliedFilter: any | null = null) => { + setDataTableLoading(true); + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + const response = await axios.get('/linksehat/prescription', { + params: filter, + }); + setDataTableLoading(false); + setDataTableData(response.data.data); + }; + + // const applyFilter = async (searchFilter: string) => { + // await loadDataTableData({ search: searchFilter }); + // setSearchParams({ search: searchFilter }); + // }; + + const applyItems = async ( + searchFilter: string, + searchFilterOrganization: string, + searchFilterPaymentStatus: string, + searchFilterAppointmentStart: string, + searchFilterAppointmentEnd: string + ) => { + await loadDataTableData({ + search: searchFilter, + organization_id: searchFilterOrganization, + payment_status: searchFilterPaymentStatus, + prescription_start: searchFilterAppointmentStart, + prescription_end: searchFilterAppointmentEnd, + }); + setSearchParamsFilter({ + search: searchFilter, + organization_id: searchFilterOrganization, + payment_status: searchFilterPaymentStatus, + prescription_start: searchFilterAppointmentStart, + prescription_end: searchFilterAppointmentEnd, + }); + }; + + const handlePageChange = (event: ChangeEvent, value: number) => { + const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); + loadDataTableData(filter); + setSearchParams(filter); + }; + + useEffect(() => { + loadDataTableData(); + }, []); + + return ( + + {/* */} + + + + + {/* The Main Table */} + + + + + + + + Prescription Code + + + Date + + + Patient + + + Doctor + + {/* + Aksi + */} + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} +
+
+ + +
+
); } - -const headStyle = { - fontWeight: 'bold', -}; -// Dummy Default Data -const [dataTableIsLoading, setDataTableLoading] = useState(true); -const [dataTableLastRequest, setDataTableLastRequest] = useState(0); -const [dataTableResponseState, setDataTableResponseState] = useState('idle'); -const [dataTableData, setDataTableData] = useState({ - current_page: 1, - data: [], - path: '', - first_page_url: '', - last_page: 1, - last_page_url: '', - next_page_url: '', - prev_page_url: '', - per_page: 10, - from: 0, - to: 0, - total: 0, -}); -const [dataTablePage, setDataTablePage] = useState(5); - -const loadDataTableData = async (appliedFilter: any | null = null) => { - setDataTableLoading(true); - const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); - const response = await axios.get('/doctorrating ', { - params: filter, - }); - setDataTableLoading(false); - setDataTableData(response.data); -}; - -// const applyFilter = async (searchFilter: string) => { -// await loadDataTableData({ search: searchFilter }); -// setSearchParams({ search: searchFilter }); -// }; - -const applyItems = async ( - searchFilter: string, - searchFilterOrganization: string, - searchFilterSpecialities: string -) => { - await loadDataTableData({ - search: searchFilter, - organization_id: searchFilterOrganization, - speciality_id: searchFilterSpecialities, - }); - setSearchParamsFilter({ - search: searchFilter, - organization_id: searchFilterOrganization, - speciality_id: searchFilterSpecialities, - }); -}; - -const handlePageChange = (event: ChangeEvent, value: number) => { - const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); - loadDataTableData(filter); - setSearchParams(filter); -}; - -useEffect(() => { - loadDataTableData(); -}, []); - -return ( - - {/* */} - - - - - {/* The Main Table */} - - - - - {/* */} - - - Nama User - - - ID Dokter - - - Rating - - - Notes - - - Created On - - - -{/* - - - Tanggal Booking - - - Tanggal Appointment - - - Faskes - - - Nama Dokter - - - Spesialisasi - - - Pasien - - - Dokter - - */} - - {dataTableIsLoading ? ( - - - - Loading - - - - ) : (dataTableData.data && dataTableData.data.length === 0) ? ( - - - - - No Data - - - - ) : ( - - {dataTableData && dataTableData.map((row) => ( - - ))} - - )} -
-
- - -
-
-); -} \ No newline at end of file diff --git a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Index.tsx b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Index.tsx new file mode 100755 index 00000000..3c1ff53c --- /dev/null +++ b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/Index.tsx @@ -0,0 +1,35 @@ +import { Card, Grid, Container } from '@mui/material'; +import { useParams } from 'react-router-dom'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import Page from '../../../components/Page'; +import useSettings from '../../../hooks/useSettings'; +import List from './List'; + +export default function LinksehatPayments() { + const { themeStretch } = useSettings(); + + const { id } = useParams(); + + const pageTitle = 'Riwayat Medis Peserta'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/List.tsx b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/List.tsx new file mode 100755 index 00000000..8a412049 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/RiwayatMedisPeserta/List.tsx @@ -0,0 +1,550 @@ +import { + Box, + Button, + Card, + Collapse, + Paper, + Select, + SelectChangeEvent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Typography, + Stack, + ButtonGroup, + Grid, + Chip, + Dialog, + DialogContent, + DialogContentText, + DialogActions, + FormControl, + Autocomplete, + InputAdornment, + IconButton, + InputLabel, + Menu, +} from '@mui/material'; + +import { + Link, + NavLink as RouterLink, + useSearchParams, + useNavigate, + useParams, +} from 'react-router-dom'; +// hooks +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +// components +import AutocompleteHealthcare from '@/components/autocomplete/AutocompleteHealthcare'; +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import { Icd } from '../../../@types/diagnosis'; +import BasePagination from '../../../components/BasePagination'; +import { Practitioner } from '../../../@types/doctor'; +import CreateIcon from '@mui/icons-material/Create'; +import { Props } from '../../../components/editor/index'; +import { red } from '@mui/material/colors'; +import { margin, padding } from '@mui/system'; +import { enqueueSnackbar } from 'notistack'; +import { fNumber } from '@/utils/formatNumber'; +import { Controller } from 'react-hook-form'; + +import SvgIconStyle from '../../../components/SvgIconStyle'; +import { GridSearchIcon } from '@mui/x-data-grid'; +import { Search } from '@mui/icons-material'; +import { Icon } from '@iconify/react'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import { MenuItem } from '@mui/material'; +import { fDateOnly, fDateTime } from '@/utils/formatTime'; +import AutocompleteLinksehatHealthcare from '@/components/autocomplete/AutocompleteLinksehatHealthcare'; +import { LoadingButton } from '@mui/lab'; +import UploadIcon from '@mui/icons-material/Upload'; + +// ---------------------------------------------------------------------- + +export default function List() { + // Generate the every row of the table + + const navigate = useNavigate(); + const { organization_id } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const [organizationOptions, setOrganizationOptions] = useState([]); + const [searchParamsPaymentStatus, setSearchParamsPaymentStatus] = useSearchParams(); + const [searchParamsOrganizations, setSearchParamsOrganizations] = useSearchParams(); + const [searchParamsSpecialities, setSearchParamsSpecialities] = useSearchParams(); + const [searchParamsFilter, setSearchParamsFilter] = useSearchParams(); + + useEffect(() => { + // axios.get(`/search-organizations`).then((response) => { + // setOrganizationOptions(response.data); + // }); + }, []); + + function Filter(props: any) { + // SEARCH + const searchInput = useRef(null); + const [searchText, setSearchText] = useState(''); + const [importLoading, setImportLoading] = useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + /* ------------------------------ handle params ----------------------------- */ + const [appliedParams, setAppliedParams] = useState({}); + const params = { + searchParams: searchParams, + setSearchParams: setSearchParams, + appliedParams: appliedParams, + setAppliedParams: setAppliedParams, + }; + + const handleGetData = (type :string) => { + const parameters = + Object.keys(appliedParams).length !== 0 + ? appliedParams + : Object.fromEntries([...searchParams.entries()]); + setImportLoading(true); + axios.get('/linksehat/phr/generate-excel', { + params: { ...parameters }, + }).then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + setImportLoading(false); + }); + // axios.get(`report/logs/export`) + // .then((response) => { + // const link = document.createElement('a'); + // link.href = response.data.data.file_url; + // link.setAttribute('download', response.data.data.file_name); + // document.body.appendChild(link); + // link.click(); + // handleClose(); + // }) + } + + //handle search + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + + props.onSearch(searchText); + }; + + useEffect(() => { + // Trigger First Search + setSearchText(searchParams.get('search') ?? ''); + }, []); + + + + return ( +
+ + + { + if (event.key === 'Enter') { + // handleSearchSubmit(event); + + const filter = Object.fromEntries([ + ...searchParams.entries(), + ['search', searchText], + ]); + setSearchParams(filter); + loadDataTableData(filter); + } + }} + label="Search" + value={searchText} + InputProps={{ + // startAdornment: ( + // + // + // + // ), + placeholder: 'Nama Pasien', + }} + /> + + + + { + try { + if (value && !!Date.parse(value)) { + const date = value ? fDateOnly(value) : ''; + var entries = [...searchParams.entries(), ['livechat_start', date ?? '']]; + if (!searchParams.get('livechat_end')) { + entries = [...entries, ['livechat_end', 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(), ['livechat_end', date ?? '']]; + if (!searchParams.get('livechat_start')) { + entries = [...entries, ['livechat_start', date ?? '']]; + } + const filter = Object.fromEntries(entries); + + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => ( + + )} + /> + + + + + } + sx={{ p: 1.8 }} + onClick={handleClick} + loading={importLoading} + > + Export + + + {handleGetData('')}}>Download Excel + + + +
+ ); + } + + function FilterForm(props: any) { + // IMPORT + return ( + + + + + + ); + } + + //TODO Create PaymentType + function createData(payments: any): any { + return { + ...payments, + }; + } + + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + const [openDialog, setOpenDialog] = React.useState(false); + + const handleDelete = (model: any) => { + axios + .delete(`/doctors/${row.id}`) + .then((res) => { + setDataTableData({ + ...dataTableData, + data: dataTableData.data.filter((model) => model.id != row.id), + }); + enqueueSnackbar('Data berhasil dihapus', { variant: 'success' }); + }) + .catch((error) => { + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; + + return ( + + + {row.healthcare ?? '-'} + {row.patient_name ?? '-'} + {row.doctor_name ?? '-'} + {row.specialis ?? '-' } + {row.date_consultation ? fDateTime(row.date_consultation) : '-'} + {row.subject ?? '-'} + {row.object ?? '-'} + {row.assessment ?? '-'} + {row.plan ?? '-'} + {/* + + + + + + */} + + + { + setOpenDialog(false); + }} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + + + + Apakah anda yakin ingin menghapus + + + {row.name}? + + + + + + + + + ); + } + + const headStyle = { + fontWeight: 'bold', + }; + // Dummy Default Data + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const [dataTableLastRequest, setDataTableLastRequest] = useState(0); + const [dataTableResponseState, setDataTableResponseState] = useState('idle'); + const [dataTableData, setDataTableData] = useState({ + current_page: 1, + data: [], + path: '', + first_page_url: '', + last_page: 1, + last_page_url: '', + next_page_url: '', + prev_page_url: '', + per_page: 10, + from: 0, + to: 0, + total: 0, + }); + const [dataTablePage, setDataTablePage] = useState(5); + + const loadDataTableData = async (appliedFilter: any | null = null) => { + setDataTableLoading(true); + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + const response = await axios.get('/linksehat/phr', { + params: filter, + }); + setDataTableLoading(false); + setDataTableData(response.data.data); + }; + + // const applyFilter = async (searchFilter: string) => { + // await loadDataTableData({ search: searchFilter }); + // setSearchParams({ search: searchFilter }); + // }; + + const applyItems = async ( + searchFilter: string, + searchFilterOrganization: string, + searchFilterPaymentStatus: string, + searchFilterAppointmentStart: string, + searchFilterAppointmentEnd: string + ) => { + await loadDataTableData({ + search: searchFilter, + organization_id: searchFilterOrganization, + payment_status: searchFilterPaymentStatus, + livechat_start: searchFilterAppointmentStart, + livechat_end: searchFilterAppointmentEnd, + }); + setSearchParamsFilter({ + search: searchFilter, + organization_id: searchFilterOrganization, + payment_status: searchFilterPaymentStatus, + livechat_start: searchFilterAppointmentStart, + livechat_end: searchFilterAppointmentEnd, + }); + }; + + const handlePageChange = (event: ChangeEvent, value: number) => { + const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); + loadDataTableData(filter); + setSearchParams(filter); + }; + + useEffect(() => { + loadDataTableData(); + }, []); + + return ( + + {/* */} + + + + + {/* The Main Table */} + + + + + + Healthcare + + + Patient + + + Doctor + + + Speciality + + + Date + + + Subjective + + + Objective + + + Assessment + + + Plan + + + + {/* + Aksi + */} + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} +
+
+ + +
+
+ ); +} diff --git a/frontend/dashboard/src/pages/Report/Rujukan/Index.tsx b/frontend/dashboard/src/pages/Report/Rujukan/Index.tsx new file mode 100644 index 00000000..bc840fe3 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/Rujukan/Index.tsx @@ -0,0 +1,35 @@ +import { Card, Grid, Container } from '@mui/material'; +import { useParams } from 'react-router-dom'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import Page from '../../../components/Page'; +import useSettings from '../../../hooks/useSettings'; +import List from './List'; + +export default function LinksehatPayments() { + const { themeStretch } = useSettings(); + + const { id } = useParams(); + + const pageTitle = 'Rujukan'; + return ( + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Report/Rujukan/List.tsx b/frontend/dashboard/src/pages/Report/Rujukan/List.tsx new file mode 100644 index 00000000..8a412049 --- /dev/null +++ b/frontend/dashboard/src/pages/Report/Rujukan/List.tsx @@ -0,0 +1,550 @@ +import { + Box, + Button, + Card, + Collapse, + Paper, + Select, + SelectChangeEvent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Typography, + Stack, + ButtonGroup, + Grid, + Chip, + Dialog, + DialogContent, + DialogContentText, + DialogActions, + FormControl, + Autocomplete, + InputAdornment, + IconButton, + InputLabel, + Menu, +} from '@mui/material'; + +import { + Link, + NavLink as RouterLink, + useSearchParams, + useNavigate, + useParams, +} from 'react-router-dom'; +// hooks +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +// components +import AutocompleteHealthcare from '@/components/autocomplete/AutocompleteHealthcare'; +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import { Icd } from '../../../@types/diagnosis'; +import BasePagination from '../../../components/BasePagination'; +import { Practitioner } from '../../../@types/doctor'; +import CreateIcon from '@mui/icons-material/Create'; +import { Props } from '../../../components/editor/index'; +import { red } from '@mui/material/colors'; +import { margin, padding } from '@mui/system'; +import { enqueueSnackbar } from 'notistack'; +import { fNumber } from '@/utils/formatNumber'; +import { Controller } from 'react-hook-form'; + +import SvgIconStyle from '../../../components/SvgIconStyle'; +import { GridSearchIcon } from '@mui/x-data-grid'; +import { Search } from '@mui/icons-material'; +import { Icon } from '@iconify/react'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import { MenuItem } from '@mui/material'; +import { fDateOnly, fDateTime } from '@/utils/formatTime'; +import AutocompleteLinksehatHealthcare from '@/components/autocomplete/AutocompleteLinksehatHealthcare'; +import { LoadingButton } from '@mui/lab'; +import UploadIcon from '@mui/icons-material/Upload'; + +// ---------------------------------------------------------------------- + +export default function List() { + // Generate the every row of the table + + const navigate = useNavigate(); + const { organization_id } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const [organizationOptions, setOrganizationOptions] = useState([]); + const [searchParamsPaymentStatus, setSearchParamsPaymentStatus] = useSearchParams(); + const [searchParamsOrganizations, setSearchParamsOrganizations] = useSearchParams(); + const [searchParamsSpecialities, setSearchParamsSpecialities] = useSearchParams(); + const [searchParamsFilter, setSearchParamsFilter] = useSearchParams(); + + useEffect(() => { + // axios.get(`/search-organizations`).then((response) => { + // setOrganizationOptions(response.data); + // }); + }, []); + + function Filter(props: any) { + // SEARCH + const searchInput = useRef(null); + const [searchText, setSearchText] = useState(''); + const [importLoading, setImportLoading] = useState(false); + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + /* ------------------------------ handle params ----------------------------- */ + const [appliedParams, setAppliedParams] = useState({}); + const params = { + searchParams: searchParams, + setSearchParams: setSearchParams, + appliedParams: appliedParams, + setAppliedParams: setAppliedParams, + }; + + const handleGetData = (type :string) => { + const parameters = + Object.keys(appliedParams).length !== 0 + ? appliedParams + : Object.fromEntries([...searchParams.entries()]); + setImportLoading(true); + axios.get('/linksehat/phr/generate-excel', { + params: { ...parameters }, + }).then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + setImportLoading(false); + }); + // axios.get(`report/logs/export`) + // .then((response) => { + // const link = document.createElement('a'); + // link.href = response.data.data.file_url; + // link.setAttribute('download', response.data.data.file_name); + // document.body.appendChild(link); + // link.click(); + // handleClose(); + // }) + } + + //handle search + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? ''; + setSearchText(newSearchText); + }; + + const handleSearchSubmit = (event: any) => { + event.preventDefault(); + + props.onSearch(searchText); + }; + + useEffect(() => { + // Trigger First Search + setSearchText(searchParams.get('search') ?? ''); + }, []); + + + + return ( +
+ + + { + if (event.key === 'Enter') { + // handleSearchSubmit(event); + + const filter = Object.fromEntries([ + ...searchParams.entries(), + ['search', searchText], + ]); + setSearchParams(filter); + loadDataTableData(filter); + } + }} + label="Search" + value={searchText} + InputProps={{ + // startAdornment: ( + // + // + // + // ), + placeholder: 'Nama Pasien', + }} + /> + + + + { + try { + if (value && !!Date.parse(value)) { + const date = value ? fDateOnly(value) : ''; + var entries = [...searchParams.entries(), ['livechat_start', date ?? '']]; + if (!searchParams.get('livechat_end')) { + entries = [...entries, ['livechat_end', 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(), ['livechat_end', date ?? '']]; + if (!searchParams.get('livechat_start')) { + entries = [...entries, ['livechat_start', date ?? '']]; + } + const filter = Object.fromEntries(entries); + + setSearchParams(filter); + loadDataTableData(filter); + } + } catch (e) {} + }} + renderInput={(params) => ( + + )} + /> + + + + + } + sx={{ p: 1.8 }} + onClick={handleClick} + loading={importLoading} + > + Export + + + {handleGetData('')}}>Download Excel + + + +
+ ); + } + + function FilterForm(props: any) { + // IMPORT + return ( + + + + + + ); + } + + //TODO Create PaymentType + function createData(payments: any): any { + return { + ...payments, + }; + } + + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + const [openDialog, setOpenDialog] = React.useState(false); + + const handleDelete = (model: any) => { + axios + .delete(`/doctors/${row.id}`) + .then((res) => { + setDataTableData({ + ...dataTableData, + data: dataTableData.data.filter((model) => model.id != row.id), + }); + enqueueSnackbar('Data berhasil dihapus', { variant: 'success' }); + }) + .catch((error) => { + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; + + return ( + + + {row.healthcare ?? '-'} + {row.patient_name ?? '-'} + {row.doctor_name ?? '-'} + {row.specialis ?? '-' } + {row.date_consultation ? fDateTime(row.date_consultation) : '-'} + {row.subject ?? '-'} + {row.object ?? '-'} + {row.assessment ?? '-'} + {row.plan ?? '-'} + {/* + + + + + + */} + + + { + setOpenDialog(false); + }} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + + + + Apakah anda yakin ingin menghapus + + + {row.name}? + + + + + + + + + ); + } + + const headStyle = { + fontWeight: 'bold', + }; + // Dummy Default Data + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const [dataTableLastRequest, setDataTableLastRequest] = useState(0); + const [dataTableResponseState, setDataTableResponseState] = useState('idle'); + const [dataTableData, setDataTableData] = useState({ + current_page: 1, + data: [], + path: '', + first_page_url: '', + last_page: 1, + last_page_url: '', + next_page_url: '', + prev_page_url: '', + per_page: 10, + from: 0, + to: 0, + total: 0, + }); + const [dataTablePage, setDataTablePage] = useState(5); + + const loadDataTableData = async (appliedFilter: any | null = null) => { + setDataTableLoading(true); + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + const response = await axios.get('/linksehat/phr', { + params: filter, + }); + setDataTableLoading(false); + setDataTableData(response.data.data); + }; + + // const applyFilter = async (searchFilter: string) => { + // await loadDataTableData({ search: searchFilter }); + // setSearchParams({ search: searchFilter }); + // }; + + const applyItems = async ( + searchFilter: string, + searchFilterOrganization: string, + searchFilterPaymentStatus: string, + searchFilterAppointmentStart: string, + searchFilterAppointmentEnd: string + ) => { + await loadDataTableData({ + search: searchFilter, + organization_id: searchFilterOrganization, + payment_status: searchFilterPaymentStatus, + livechat_start: searchFilterAppointmentStart, + livechat_end: searchFilterAppointmentEnd, + }); + setSearchParamsFilter({ + search: searchFilter, + organization_id: searchFilterOrganization, + payment_status: searchFilterPaymentStatus, + livechat_start: searchFilterAppointmentStart, + livechat_end: searchFilterAppointmentEnd, + }); + }; + + const handlePageChange = (event: ChangeEvent, value: number) => { + const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); + loadDataTableData(filter); + setSearchParams(filter); + }; + + useEffect(() => { + loadDataTableData(); + }, []); + + return ( + + {/* */} + + + + + {/* The Main Table */} + + + + + + Healthcare + + + Patient + + + Doctor + + + Speciality + + + Date + + + Subjective + + + Objective + + + Assessment + + + Plan + + + + {/* + Aksi + */} + + + {dataTableIsLoading ? ( + + + + Loading + + + + ) : dataTableData.data.length == 0 ? ( + + + + No Data + + + + ) : ( + + {dataTableData.data.map((row) => ( + + ))} + + )} +
+
+ + +
+
+ ); +} diff --git a/frontend/dashboard/src/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index f3681f67..2fa32d41 100755 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -12,8 +12,8 @@ import VerifyCode from '../pages/auth/VerifyCode'; import { AuthProvider } from '../contexts/LaravelAuthContext'; import AuthGuard from '../guards/AuthGuard'; import { Link, useParams, useSearchParams } from 'react-router-dom'; -import Prescription from '@/pages/Report/Prescription/Index'; -import DoctorRating from '@/pages/Report/DoctorRating/Index'; +import DoctorRating from '@/pages/Report/DoctorRating_v2/Index'; +import KatalogDokter from '@/pages/Report/KatalogDokter/Index'; // ---------------------------------------------------------------------- @@ -443,14 +443,30 @@ export default function Router() { element: , }, { - path: 'report/doctorrating', + path: 'report/doctor-rating', element: , }, + { + path: 'report/katalog-dokter', + element: , + }, { path: 'report/linksehat-payments', element: , }, + { + path: 'report/phr', + element: , + }, + { + path: 'report/prescription', + element: , + }, + { + path: 'report/rujukan', + element: , + }, { path: 'claims', element: , @@ -716,6 +732,10 @@ const EPrescriptionShow = Loadable(lazy(() => import('../pages/EPrescription/Liv const LinksehatPayment = Loadable(lazy(() => import('../pages/Report/LinksehatPayments/Index'))); +const RiwayatMedisPeserta = Loadable(lazy(() => import('../pages/Report/RiwayatMedisPeserta/Index'))); +const RujukanPasien = Loadable(lazy(() => import('../pages/Report/Rujukan/Index'))); +const Prescription = Loadable(lazy(() => import('../pages/Report/Prescription/Index'))); + const MasterDrug = Loadable(lazy(() => import('../pages/Master/Drug/Index'))); const MasterFormularium = Loadable(lazy(() => import('../pages/Master/Formularium/Index'))); diff --git a/lang/en/auth.php b/lang/en/auth.php old mode 100644 new mode 100755 diff --git a/lang/en/enrollment.php b/lang/en/enrollment.php old mode 100644 new mode 100755 diff --git a/lang/en/pagination.php b/lang/en/pagination.php old mode 100644 new mode 100755 diff --git a/lang/en/passwords.php b/lang/en/passwords.php old mode 100644 new mode 100755 diff --git a/lang/en/plan.php b/lang/en/plan.php old mode 100644 new mode 100755 diff --git a/lang/en/validation.php b/lang/en/validation.php old mode 100644 new mode 100755