diff --git a/Modules/Internal/Http/Controllers/Api/CorporateFormulariumController.php b/Modules/Internal/Http/Controllers/Api/CorporateFormulariumController.php new file mode 100644 index 00000000..84dd56b8 --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/CorporateFormulariumController.php @@ -0,0 +1,121 @@ +with(['corporateFormulariums' => function ($query) use ($corporate_id) { + $query->where('corporate_id', $corporate_id); + }]) + ->withCount('items') + ->paginate(); + + return Helper::paginateResources(CorporateFormulariumResource::collection($formulariums)); + } + + /** + * 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) + { + // + } + + public function updateStatus($corporate_id, $formularium_id , $status) + { + if ($status == 'activate') { + $corporateFormularium = CorporateFormularium::firstOrCreate([ + 'corporate_id' => $corporate_id, + 'formularium_id' => $formularium_id + ], [ + 'corporate_id' => $corporate_id, + 'formularium_id' => $formularium_id, + ]); + + return response()->json([ + 'status' => 'active', + 'message' => 'Formularium activated' + ]); + } else { + $deleteCorporateFormularium = CorporateFormularium::where([ + 'corporate_id' => $corporate_id, + 'formularium_id' => $formularium_id + ])->delete(); + + return ($deleteCorporateFormularium) ? response()->json([ + 'status' => 'inactive', + 'message' => 'Formularium deactivated' + ]) : response()->json([ + 'status' => 'error', + 'message' => 'Error deactivating formularium' + ]); + } + } +} diff --git a/Modules/Internal/Http/Controllers/Api/DrugController.php b/Modules/Internal/Http/Controllers/Api/DrugController.php new file mode 100644 index 00000000..1402699e --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/DrugController.php @@ -0,0 +1,82 @@ +filter($request->toArray())->paginate(); + + return $drugs; + } + + /** + * 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) + { + // + } +} diff --git a/Modules/Internal/Http/Controllers/Api/FormulariumController.php b/Modules/Internal/Http/Controllers/Api/FormulariumController.php new file mode 100644 index 00000000..a49e9963 --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/FormulariumController.php @@ -0,0 +1,214 @@ +withCount('items')->filter($request->toArray())->paginate(); + + return $formulariums; + } + + /** + * 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) + { + $request->validate([ + 'name' => 'required|string|max:255', + 'file' => 'optional|file|mimes:xlsx,xls', + ]); + + $formularium = Formularium::create($request->all()); + + return $formularium; + } + + /** + * 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) + { + // + } + + public function import(Request $request) + { + $request->validate([ + 'file' => 'required|file|mimes:xls,xlsx,csv,txt', + ]); + // dd($request->toArray()); + $file_name = now()->getPreciseTimestamp(3).'-'.$request->file('file')->getClientOriginalName(); + $file = $request->file('file')->storeAs('temp', $file_name); + $corporate = Corporate::findOrFail($corporate_id); + + // $importLog = $corporate->importLogs()->create([ + // 'type' => 'diagnosis-exclusions', + // 'file_path' => $file, + // 'status' => 'pending', + // 'progress' => 0, + // ]); + + $import = new ImportService(); + $import->read(Storage::path('temp/'.$file_name)); + $import->write(Storage::disk('public')->path('temp/result-'.$file_name), 'xsls'); + + foreach ($import->sheetsIterator() as $sheetIndex => $sheet) { + $doc_headers_indexes = []; + foreach ($sheet->getRowIterator() as $index => $row) { + if ($index == 1) { // First Row Must be Header + foreach ($row->getCells() as $index => $cell) { + $title = $cell->getValue(); + $title = preg_replace( "/\r|\n/", " ", $title ); + $title = preg_replace('/\xc2\xa0/', " ", $title ); + $title = rtrim($title); + $title = ltrim($title); + $doc_headers_indexes[$index] = $title; + } + + // Write Header to File + $result_headers = array_merge($doc_headers_indexes, ['Ingest Code', 'Ingest Note']); + $import->addArrayToRow($result_headers); + + // TODO Validate if First Row not Header + } else { // Next Row Should be Data + $row_data = []; + $row_map = [ + 0 => 'code', + 1 => 'description', + 2 => 'ip_exclusion', + 3 => 'op_exclusion', + 4 => 'de_exclusion', + 5 => 'ma_exclusion', + 6 => 'sp_exclusion', + 7 => 'pre_exist_exclusion', + 8 => 'op_de_exclusion', + 9 => 'keterangan', + 10 => 'maternity_waiting' + ]; + + foreach ($row->getCells() as $header_index => $cell) { + if (isset($row_map[$header_index])) { + $value = $cell->getValue(); + $value = preg_replace( "/\r|\n/", " ", $value ); + $value = preg_replace('/\xc2\xa0/', " ", $value ); + $value = rtrim($value); + $value = ltrim($value); + $row_data[$row_map[$header_index]] = $cell->getValue(); + } + } + + try { // Process the Row Data + if ( + // empty($row_data['code']) && + // empty($row_data['description']) && + empty($row_data['ip_exclusion']) && + empty($row_data['op_exclusion']) && + empty($row_data['de_exclusion']) && + empty($row_data['ma_exclusion']) && + empty($row_data['sp_exclusion']) && + empty($row_data['pre_exis_exclusion']) && + empty($row_data['op_de_exclusion']) && + empty($row_data['maternity_waiting'])) { + continue; + } + + // Save the Row + $exclusionService = new ExclusionService(); + $exclusionService->handleDiagnosisExclusionRow($corporate, $row_data); + + // Write Success Result to File + $import->addArrayToRow(array_merge($row_data, [ + 'Ingest Code' => 200, + 'Ingest Note' => 'Success', + ]), $sheet->getName()); + + } catch (ImportRowException $e) { + // Write Data Validation Error to File + $import->addArrayToRow(array_merge($row_data, [ + 'Ingest Code' => $e->getCode(), + 'Ingest Note' => $e->getMessage(), + ]), $sheet->getName()); + } catch (\Exception $e) { + throw new \Exception($e); + // Write Server Error to File + $import->addArrayToRow(array_merge($row_data, [ + 'Ingest Code' => 500, + 'Ingest Note' => env('APP_DEBUG') ? $e->getMessage() : 'Server Error', + ]), $sheet->getName()); + } + } + } + + break; // Only Read First Row + } + $import->reader->close(); + Storage::delete('temp/'.$file_name); + $import->writer->close(); + + return [ + // 'total_successed_row' => $imported_plan_data, + // 'total_failed_row' => count($failed_plan_data), + // 'failed_row' => $failed_plan_data, + 'result_file' => [ + 'url' => Storage::disk('public')->url('temp/result-'.$file_name), + 'name' => 'result-'.$file_name, + ] + ]; + } +} diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 3270159d..b99f863d 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -5,11 +5,14 @@ use Illuminate\Http\Request; use Modules\Internal\Http\Controllers\Api\BenefitController; use Modules\Internal\Http\Controllers\Api\CorporateBenefitController; use Modules\Internal\Http\Controllers\Api\CorporateController; +use Modules\Internal\Http\Controllers\Api\CorporateFormulariumController; use Modules\Internal\Http\Controllers\Api\CorporatePlanController; use Modules\Internal\Http\Controllers\Api\CorporateServiceController; use Modules\Internal\Http\Controllers\Api\DiagnosisController; use Modules\Internal\Http\Controllers\Api\DiagnosisExclusionController; use Modules\Internal\Http\Controllers\Api\DivisionController; +use Modules\Internal\Http\Controllers\Api\DrugController; +use Modules\Internal\Http\Controllers\Api\FormulariumController; use Modules\Internal\Http\Controllers\Api\MemberController; use Modules\Internal\Http\Controllers\Api\PlanController; @@ -72,10 +75,17 @@ Route::prefix('internal')->group(function () { Route::get('corporates/{corporate_id}/services', [CorporateServiceController::class, 'index']); Route::post('corporates/{corporate_id}/services', [CorporateServiceController::class, 'update']); + Route::get('corporates/{corporate_id}/formulariums', [CorporateFormulariumController::class, 'index']); + Route::put('corporates/{corporate_id}/formulariums/{formularium_id}/{action}', [CorporateFormulariumController::class, 'updateStatus']); + // Route::get('corporates/{corporate_id}/diagnosis-exclusions', [DiagnosisExclusionController::class, 'index']); // Route::get('corporates/{corporate_id}/diagnosis-exclusions/import', [DiagnosisExclusionController::class, 'import']); Route::get('master/diagnosis', [DiagnosisController::class, 'index']); + Route::get('master/drugs', [DrugController::class, 'index']); + Route::get('master/formulariums', [FormulariumController::class, 'index']); + Route::post('master/formulariums', [FormulariumController::class, 'store']); + Route::post('master/formulariums/import', [FormulariumController::class, 'import']); }); diff --git a/Modules/Internal/Services/FormulariumService.php b/Modules/Internal/Services/FormulariumService.php new file mode 100644 index 00000000..7eb67acf --- /dev/null +++ b/Modules/Internal/Services/FormulariumService.php @@ -0,0 +1,48 @@ +id; + + $this->validatePlanRow($formularium_item_data); + $drug = Drug::where('code', $formularium_item_data['item_code'])->first(); + + if (empty($drug)) { + throw new ImportRowException(__('formularium.DRUG_CODE_NOT_FOUND'), 0, null, $row); + } + + $formulariumItem = $formularium->items()->create([ + 'item_id' => $drug->id, + ]); + + return $formulariumItem; + } catch (\Exception $e) { + throw $e; + } + } +} diff --git a/Modules/Internal/Transformers/CorporateFormulariumResource.php b/Modules/Internal/Transformers/CorporateFormulariumResource.php new file mode 100644 index 00000000..ecaf2fac --- /dev/null +++ b/Modules/Internal/Transformers/CorporateFormulariumResource.php @@ -0,0 +1,26 @@ + $this->id, + 'code' => $this->code, + 'name' => $this->name, + 'items_count' => $this->items_count, + 'status' => $this->corporateFormulariums->count() ? 'active' : 'inactive', + 'corporate_formulariums' => $this->coporateFormulariums, + ]; + } +} diff --git a/app/Models/Brand.php b/app/Models/Brand.php new file mode 100644 index 00000000..c1405e8d --- /dev/null +++ b/app/Models/Brand.php @@ -0,0 +1,11 @@ +belongsTo(Corporate::class); + } + + public function formularium() + { + return $this->belongsTo(Formularium::class); + } +} diff --git a/app/Models/Drug.php b/app/Models/Drug.php new file mode 100644 index 00000000..ad74b58a --- /dev/null +++ b/app/Models/Drug.php @@ -0,0 +1,66 @@ +hasMany(DrugCategories::class, 'drug_id'); + } + + // public function externalIdentifiers() + // { + // return $this->hasMany(DrugExternalIdentifier::class, 'drug_id'); + // } + + public function brand() + { + return $this->belongsTo(Brand::class, 'brand_id'); + } + + public function identifiers() + { + return $this->morphMany(Identifier::class, 'identifiable'); + } + + public function manufacturers() + { + return $this->belongsToMany(Organization::class, 'drug_manufacturers', 'drug_id', 'organization_id'); + } + + public function scopeFilter($query, Array $filters) + { + $query->when($filters['search'] ?? false, function ($query, $search) { + return $query + ->where('code', 'like', "%" . $search . "%") + ->orWhere('name', 'like', "%" . $search . "%") + ->orWhere('type', 'like', "%" . $search . "%") + ; + }); + } +} diff --git a/app/Models/DrugAtc.php b/app/Models/DrugAtc.php new file mode 100644 index 00000000..80a4421d --- /dev/null +++ b/app/Models/DrugAtc.php @@ -0,0 +1,11 @@ +belongsTo(Drug::class, 'drug_id'); + } + + public function category() + { + return $this->belongsTo(Category::class, 'category_id'); + } +} diff --git a/app/Models/DrugComposition.php b/app/Models/DrugComposition.php new file mode 100644 index 00000000..6fc86366 --- /dev/null +++ b/app/Models/DrugComposition.php @@ -0,0 +1,11 @@ +attributes['code'] = !empty($value) ? $value : Str::upper(Str::random('6')); + } + + public function corporateFormulariums() + { + return $this->hasMany(CorporateFormularium::class); + } + + public function items() + { + return $this->belongsToMany(Drug::class, 'formularium_items', 'formularium_id', 'item_id'); + } + + public function scopeFilter($query, Array $filters) + { + $query->when($filters['search'] ?? false, function ($query, $search) { + return $query + ->where('code', 'like', "%" . $search . "%") + ->orWhere('name', 'like', "%" . $search . "%") + ; + }); + } +} diff --git a/app/Models/FormulariumItem.php b/app/Models/FormulariumItem.php new file mode 100644 index 00000000..405feee0 --- /dev/null +++ b/app/Models/FormulariumItem.php @@ -0,0 +1,11 @@ +morphTo(); + } +} diff --git a/app/Models/Ingredient.php b/app/Models/Ingredient.php new file mode 100644 index 00000000..b85da8b7 --- /dev/null +++ b/app/Models/Ingredient.php @@ -0,0 +1,11 @@ +timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); }); } diff --git a/database/migrations/2022_06_16_045414_create_corporates_table.php b/database/migrations/2022_06_16_045414_create_corporates_table.php index f7cd2c93..3ec14ab8 100644 --- a/database/migrations/2022_06_16_045414_create_corporates_table.php +++ b/database/migrations/2022_06_16_045414_create_corporates_table.php @@ -25,9 +25,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); }); } diff --git a/database/migrations/2022_06_16_045441_create_corporate_divisions_table.php b/database/migrations/2022_06_16_045441_create_corporate_divisions_table.php index 135657c4..3a55a06e 100644 --- a/database/migrations/2022_06_16_045441_create_corporate_divisions_table.php +++ b/database/migrations/2022_06_16_045441_create_corporate_divisions_table.php @@ -25,9 +25,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); }); } diff --git a/database/migrations/2022_06_17_024432_create_corporate_employees_table.php b/database/migrations/2022_06_17_024432_create_corporate_employees_table.php index 47672082..5c821f8d 100644 --- a/database/migrations/2022_06_17_024432_create_corporate_employees_table.php +++ b/database/migrations/2022_06_17_024432_create_corporate_employees_table.php @@ -28,9 +28,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); }); } diff --git a/database/migrations/2022_06_21_042321_create_corporate_policies_table.php b/database/migrations/2022_06_21_042321_create_corporate_policies_table.php index 2faf6161..140c21f9 100644 --- a/database/migrations/2022_06_21_042321_create_corporate_policies_table.php +++ b/database/migrations/2022_06_21_042321_create_corporate_policies_table.php @@ -32,9 +32,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); }); } diff --git a/database/migrations/2022_07_21_121346_create_member_policies_table.php b/database/migrations/2022_07_21_121346_create_member_policies_table.php index c2ef0b69..bea869a3 100644 --- a/database/migrations/2022_07_21_121346_create_member_policies_table.php +++ b/database/migrations/2022_07_21_121346_create_member_policies_table.php @@ -24,9 +24,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); $table->index(['policy_id', 'member_id']); }); diff --git a/database/migrations/2022_07_25_050001_create_member_plans_table.php b/database/migrations/2022_07_25_050001_create_member_plans_table.php index ac188df7..701b45bb 100644 --- a/database/migrations/2022_07_25_050001_create_member_plans_table.php +++ b/database/migrations/2022_07_25_050001_create_member_plans_table.php @@ -24,9 +24,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); $table->index(['member_id', 'plan_id']); }); diff --git a/database/migrations/2022_07_28_032235_create_icd_table.php b/database/migrations/2022_07_28_032235_create_icd_table.php index 2d3938cd..39d95fd5 100644 --- a/database/migrations/2022_07_28_032235_create_icd_table.php +++ b/database/migrations/2022_07_28_032235_create_icd_table.php @@ -25,9 +25,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); }); } diff --git a/database/migrations/2022_08_02_061122_create_exclusions_table.php b/database/migrations/2022_08_02_061122_create_exclusions_table.php index 1c2ac7ea..0a23ee9a 100644 --- a/database/migrations/2022_08_02_061122_create_exclusions_table.php +++ b/database/migrations/2022_08_02_061122_create_exclusions_table.php @@ -23,9 +23,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); }); } diff --git a/database/migrations/2022_08_02_061127_create_exclusion_rules_table.php b/database/migrations/2022_08_02_061127_create_exclusion_rules_table.php index 8ad76a60..167bf91e 100644 --- a/database/migrations/2022_08_02_061127_create_exclusion_rules_table.php +++ b/database/migrations/2022_08_02_061127_create_exclusion_rules_table.php @@ -22,9 +22,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); }); } diff --git a/database/migrations/2022_08_05_035511_create_corporate_services_table.php b/database/migrations/2022_08_05_035511_create_corporate_services_table.php index 1448545d..3eff32ff 100644 --- a/database/migrations/2022_08_05_035511_create_corporate_services_table.php +++ b/database/migrations/2022_08_05_035511_create_corporate_services_table.php @@ -22,9 +22,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); $table->index(['corporate_id', 'service_code']); }); diff --git a/database/migrations/2022_08_08_042246_create_corporate_service_configs_table.php b/database/migrations/2022_08_08_042246_create_corporate_service_configs_table.php index 1315a962..e74954a7 100644 --- a/database/migrations/2022_08_08_042246_create_corporate_service_configs_table.php +++ b/database/migrations/2022_08_08_042246_create_corporate_service_configs_table.php @@ -22,9 +22,9 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); - $table->unsignedBigInteger('created_by')->nullable(); - $table->unsignedBigInteger('updated_by')->nullable(); - $table->unsignedBigInteger('deleted_by')->nullable(); + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); }); } diff --git a/database/migrations/2022_08_09_043235_create_drugs_table.php b/database/migrations/2022_08_09_043235_create_drugs_table.php new file mode 100644 index 00000000..048b6966 --- /dev/null +++ b/database/migrations/2022_08_09_043235_create_drugs_table.php @@ -0,0 +1,52 @@ +id(); + $table->string('code')->index(); + $table->string('name')->index(); + $table->string('generic_name')->nullable()->index(); + $table->foreignId('unit_id')->nullable(); + $table->foreignId('selling_unit_id')->index()->nullable(); + $table->text('description')->nullable(); + $table->foreignId('brand_id')->nullable(); + $table->string('mims_class')->nullable(); + $table->text('indications')->nullable(); + $table->string('atc_code')->nullable()->index(); + $table->string('segmentation')->nullable(); + $table->string('type')->nullable(); + $table->string('dosage')->nullable(); + $table->text('remark')->nullable(); + $table->string('status')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('drugs'); + } +}; diff --git a/database/migrations/2022_08_09_043243_create_brands_table.php b/database/migrations/2022_08_09_043243_create_brands_table.php new file mode 100644 index 00000000..3c6a3e44 --- /dev/null +++ b/database/migrations/2022_08_09_043243_create_brands_table.php @@ -0,0 +1,37 @@ +id(); + $table->string('name'); + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('brands'); + } +}; diff --git a/database/migrations/2022_08_09_092811_create_categories_table.php b/database/migrations/2022_08_09_092811_create_categories_table.php new file mode 100644 index 00000000..ad0c29ce --- /dev/null +++ b/database/migrations/2022_08_09_092811_create_categories_table.php @@ -0,0 +1,41 @@ +id(); + $table->foreignId('parent_id')->index()->nullable(); + $table->string('code')->index()->nullable(); + $table->string('name'); + $table->string('type')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('categories'); + } +}; diff --git a/database/migrations/2022_08_09_092845_create_drug_categories_table.php b/database/migrations/2022_08_09_092845_create_drug_categories_table.php new file mode 100644 index 00000000..13f1a30d --- /dev/null +++ b/database/migrations/2022_08_09_092845_create_drug_categories_table.php @@ -0,0 +1,39 @@ +id(); + $table->foreignId('drug_id'); + $table->foreignId('category_id'); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('drug_categories'); + } +}; diff --git a/database/migrations/2022_08_09_095513_create_organizations_table.php b/database/migrations/2022_08_09_095513_create_organizations_table.php new file mode 100644 index 00000000..3c57d9be --- /dev/null +++ b/database/migrations/2022_08_09_095513_create_organizations_table.php @@ -0,0 +1,42 @@ +id(); + $table->string('name'); + $table->string('type')->nullable(); + $table->string('status')->nullable()->default('active'); + $table->text('description')->nullable(); + $table->foreignId('part_of')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('organizations'); + } +}; diff --git a/database/migrations/2022_08_11_024030_create_drug_compositions_table.php b/database/migrations/2022_08_11_024030_create_drug_compositions_table.php new file mode 100644 index 00000000..8241ecf6 --- /dev/null +++ b/database/migrations/2022_08_11_024030_create_drug_compositions_table.php @@ -0,0 +1,41 @@ +id(); + $table->foreignId('drug_id'); + $table->foreignId('ingredient_id'); + $table->boolean('active')->default(true); + $table->string('strength')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('drug_compositions'); + } +}; diff --git a/database/migrations/2022_08_11_025942_create_drug_atcs_table.php b/database/migrations/2022_08_11_025942_create_drug_atcs_table.php new file mode 100644 index 00000000..3ed5817f --- /dev/null +++ b/database/migrations/2022_08_11_025942_create_drug_atcs_table.php @@ -0,0 +1,39 @@ +id(); + $table->string('code')->index(); + $table->string('name')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('drug_atcs'); + } +}; diff --git a/database/migrations/2022_08_11_030815_create_identifiers_table.php b/database/migrations/2022_08_11_030815_create_identifiers_table.php new file mode 100644 index 00000000..381ae30c --- /dev/null +++ b/database/migrations/2022_08_11_030815_create_identifiers_table.php @@ -0,0 +1,45 @@ +id(); + $table->morphs('identifiable'); + $table->string('use')->nullable(); + $table->string('type')->nullable(); + $table->string('system')->nullable(); + $table->string('value')->nullable(); + $table->date('period_start')->nullable(); + $table->date('period_end')->nullable(); + $table->string('assigner')->nullable(); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('identifiers'); + } +}; diff --git a/database/migrations/2022_08_11_031728_create_ingredients_table.php b/database/migrations/2022_08_11_031728_create_ingredients_table.php new file mode 100644 index 00000000..267d8937 --- /dev/null +++ b/database/migrations/2022_08_11_031728_create_ingredients_table.php @@ -0,0 +1,40 @@ +id(); + // $table->foreignId('drug_id'); + $table->text('substance'); + $table->string('snomed_code')->index()->nullable(); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('ingredients'); + } +}; diff --git a/database/migrations/2022_08_12_020643_create_drug_manufacturers_table.php b/database/migrations/2022_08_12_020643_create_drug_manufacturers_table.php new file mode 100644 index 00000000..565d8c3d --- /dev/null +++ b/database/migrations/2022_08_12_020643_create_drug_manufacturers_table.php @@ -0,0 +1,39 @@ +id(); + $table->foreignId('drug_id'); + $table->foreignId('organization_id'); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('drug_manufacturers'); + } +}; diff --git a/database/migrations/2022_08_12_025718_create_units_table.php b/database/migrations/2022_08_12_025718_create_units_table.php new file mode 100644 index 00000000..271db8ec --- /dev/null +++ b/database/migrations/2022_08_12_025718_create_units_table.php @@ -0,0 +1,39 @@ +id(); + $table->string('name')->index(); + $table->string('metrics')->index()->nullable(); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('units'); + } +}; diff --git a/database/migrations/2022_08_12_041455_create_formulariums_table.php b/database/migrations/2022_08_12_041455_create_formulariums_table.php new file mode 100644 index 00000000..9fb1bb00 --- /dev/null +++ b/database/migrations/2022_08_12_041455_create_formulariums_table.php @@ -0,0 +1,38 @@ +id(); + $table->string('code')->index(); + $table->string('name')->index(); + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('formulariums'); + } +}; diff --git a/database/migrations/2022_08_12_042229_create_formularium_items_table.php b/database/migrations/2022_08_12_042229_create_formularium_items_table.php new file mode 100644 index 00000000..84494a96 --- /dev/null +++ b/database/migrations/2022_08_12_042229_create_formularium_items_table.php @@ -0,0 +1,38 @@ +id(); + $table->foreignId('formularium_id'); + $table->foreignId('item_id'); + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('formularium_items'); + } +}; diff --git a/database/migrations/2022_08_15_043309_create_corporate_formulariums_table.php b/database/migrations/2022_08_15_043309_create_corporate_formulariums_table.php new file mode 100644 index 00000000..ba0a383c --- /dev/null +++ b/database/migrations/2022_08_15_043309_create_corporate_formulariums_table.php @@ -0,0 +1,39 @@ +id(); + $table->foreignId('corporate_id'); + $table->foreignId('formularium_id'); + + $table->timestamps(); + $table->softDeletes(); + + $table->foreignId('created_by')->nullable(); + $table->foreignId('updated_by')->nullable(); + $table->foreignId('deleted_by')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('corporate_formulariums'); + } +}; diff --git a/database/seeders/DrugSeeder.php b/database/seeders/DrugSeeder.php new file mode 100644 index 00000000..9ff425f2 --- /dev/null +++ b/database/seeders/DrugSeeder.php @@ -0,0 +1,181 @@ +open($file_path); + + $chunks = []; + $time = now(); + $isData = false; + $cell_map = [ // Urutan kolom di excel + 'no', + 'code', + 'name', + 'group', + 'kategori', + 'satuan', + 'nama_cetak', + 'golongan', + 'sub_golongan', + 'komposisi', + 'sediaan', + 'brand', + 'strength', + 'satuan_strength', + 'pabrikan', + 'jenis', + 'otc', + 'resep', + 'obat_terlarang', + 'pemakaian', + 'essential', + 'live_saving' + ]; + foreach ($reader->getSheetIterator() as $sheet) { + foreach ($sheet->getRowIterator() as $index => $row) { + if ($index >= 6) { + $row_data = []; + foreach ($row->getCells() as $cell_index => $cell) { + $row_data[$cell_map[$cell_index]] = $cell->getValue(); + } + $brand = !empty($row_data['golongan']) ? Brand::firstOrCreate([ + 'name' => $row_data['brand'] + ], [ + 'name' => $row_data['brand'] + ]) : null; + + $manufacturer = Organization::firstOrCreate([ + 'name' => $row_data['pabrikan'] + ], [ + 'name' => $row_data['pabrikan'], + 'type' => 'manufacturer' + ]); + + if (in_array($row_data['golongan'], ['VITAMINS & MINERALS', 'NUTRITIONS'])) { + $type = 'vitamin'; + } else { + $type = 'drug'; + } + + if (!empty($row_data['komposisi'])) { + $ingredient = Ingredient::firstOrCreate([ + 'substance' => $row_data['komposisi'] + ], [ + 'substance' => $row_data['komposisi'] + ]); + } else { + $ingredient = null; + } + + if (!empty($row_data['sediaan'])) { + $sediaan = Unit::firstOrCreate([ + 'name' => $row_data['sediaan'] + ], [ + 'name' => $row_data['sediaan'] + ]); + } else { + $sediaan = null; + } + + $golongan = !empty($row_data['golongan']) ? Category::firstOrCreate([ + 'name' => $row_data['golongan'] + ], [ + 'name' => $row_data['golongan'] + ]) : null; + + $sub_golongan = !empty($row_data['sub_golongan']) ? Category::firstOrCreate([ + 'name' => $row_data['sub_golongan'] + ], [ + 'name' => $row_data['sub_golongan'] + ]) : null; + + $new_drug_data = [ + 'name' => $row_data['name'], + 'generic_name' => $row_data['nama_cetak'], + 'code' => $row_data['code'], + 'description' => null, + 'brand_id' => $brand->id ?? null, + // 'manufacturer_id' => $manufacturer->id ?? null, + 'mims_class' => null, + 'indications' => null, + 'atc_code' => null, + 'segmentation' => null, + 'type' => $type, + 'dosage' => '', + 'remark' => '', + 'selling_unit_id' => $sediaan->id ?? null, + 'status' => 'active', + 'unit_id' => $sediaan->id ?? null, + ]; + $drug = Drug::create($new_drug_data); + $drug->identifiers()->create([ + 'use' => 'usual', + 'type' => 'CCP', + 'value' => $row_data['code'], + ]); + + if ($ingredient) { + DrugComposition::create([ + 'drug_id' => $drug->id, + 'ingredient_id' => $ingredient->id, + ]); + } + + if ($golongan) { + DrugCategory::create([ + 'drug_id' => $drug->id, + 'category_id' => $golongan->id, + ]); + } + + if ($sub_golongan) { + DrugCategory::create([ + 'drug_id' => $drug->id, + 'category_id' => $sub_golongan->id, + ]); + } + + if ($manufacturer) { + $drug->manufacturers()->attach($manufacturer->id); + } + + // $chunks[] = $row_data; + } + + // if ($chunks && count($chunks) == 1000) { + // Icd::insert($chunks); + // $chunks = []; + // } + } + } + // if ($chunks && count($chunks) > 0) { + // Icd::insert($chunks); + // $chunks = []; + // } + } +} diff --git a/frontend/dashboard/package.json b/frontend/dashboard/package.json index 01b44edf..9e818e9f 100644 --- a/frontend/dashboard/package.json +++ b/frontend/dashboard/package.json @@ -7,7 +7,7 @@ "scripts": { "lint": "eslint --ext .ts,.tsx ./src", "lint:fix": "eslint --fix --ext .ts,.tsx ./src", - "start": "vite", + "start": "vite --port=3000", "build": "vite build --mode production && cp .htaccess build/.htaccess && rm -f -r ../../public/dashboard && cp -r build ../../public/dashboard", "serve": "vite preview", "clear-all": "rm -rf build node_modules", @@ -76,7 +76,7 @@ "simplebar-react": "^2.4.1", "stylis": "^4.1.1", "stylis-plugin-rtl": "^2.1.1", - "vite": "^2.9.14", + "vite": "^3.0.4", "vite-plugin-svgr": "^2.2.1", "yup": "^0.32.11" }, diff --git a/frontend/dashboard/pnpm-lock.yaml b/frontend/dashboard/pnpm-lock.yaml index 0233476b..2cbd89c1 100644 --- a/frontend/dashboard/pnpm-lock.yaml +++ b/frontend/dashboard/pnpm-lock.yaml @@ -66,7 +66,7 @@ specifiers: stylis: ^4.1.1 stylis-plugin-rtl: ^2.1.1 typescript: ^4.7.4 - vite: ^2.9.14 + vite: ^3.0.4 vite-plugin-pwa: ^0.12.3 vite-plugin-svgr: ^2.2.1 yup: ^0.32.11 @@ -111,8 +111,8 @@ dependencies: simplebar-react: 2.4.1_sfoxds7t5ydpegc3knd667wn6m stylis: 4.1.1 stylis-plugin-rtl: 2.1.1_stylis@4.1.1 - vite: 2.9.14 - vite-plugin-svgr: 2.2.1_vite@2.9.14 + vite: 3.0.4 + vite-plugin-svgr: 2.2.1_vite@3.0.4 yup: 0.32.11 devDependencies: @@ -142,7 +142,7 @@ devDependencies: eslint-plugin-react-hooks: 4.3.0_eslint@8.20.0 prettier: 2.7.1 typescript: 4.7.4 - vite-plugin-pwa: 0.12.3_vite@2.9.14 + vite-plugin-pwa: 0.12.3_vite@3.0.4 packages: @@ -5626,7 +5626,7 @@ packages: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} dev: true - /vite-plugin-pwa/0.12.3_vite@2.9.14: + /vite-plugin-pwa/0.12.3_vite@3.0.4: resolution: {integrity: sha512-gmYdIVXpmBuNjzbJFPZFzxWYrX4lHqwMAlOtjmXBbxApiHjx9QPXKQPJjSpeTeosLKvVbNcKSAAhfxMda0QVNQ==} peerDependencies: vite: ^2.0.0 || ^3.0.0-0 @@ -5635,7 +5635,7 @@ packages: fast-glob: 3.2.11 pretty-bytes: 6.0.0 rollup: 2.77.0 - vite: 2.9.14 + vite: 3.0.4 workbox-build: 6.5.3 workbox-window: 6.5.3 transitivePeerDependencies: @@ -5643,26 +5643,27 @@ packages: - supports-color dev: true - /vite-plugin-svgr/2.2.1_vite@2.9.14: + /vite-plugin-svgr/2.2.1_vite@3.0.4: resolution: {integrity: sha512-+EqwahbwjETJH/ssA/66dNYyKN1cO0AStq96MuXmq5maU7AePBMf2lDKfQna49tJZAjtRz+R899BWCsUUP45Fg==} peerDependencies: vite: ^2.6.0 || 3 dependencies: '@rollup/pluginutils': 4.2.1 '@svgr/core': 6.3.1 - vite: 2.9.14 + vite: 3.0.4 transitivePeerDependencies: - supports-color dev: false - /vite/2.9.14: - resolution: {integrity: sha512-P/UCjSpSMcE54r4mPak55hWAZPlyfS369svib/gpmz8/01L822lMPOJ/RYW6tLCe1RPvMvOsJ17erf55bKp4Hw==} - engines: {node: '>=12.2.0'} + /vite/3.0.4: + resolution: {integrity: sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==} + engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: less: '*' sass: '*' stylus: '*' + terser: ^5.4.0 peerDependenciesMeta: less: optional: true @@ -5670,6 +5671,8 @@ packages: optional: true stylus: optional: true + terser: + optional: true dependencies: esbuild: 0.14.49 postcss: 8.4.14 diff --git a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx index ba8539f4..bae56f71 100644 --- a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx +++ b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx @@ -54,8 +54,9 @@ const navConfig = [ children: [ { title: 'Corporate', path: '/corporates' }, { title: 'Corporate Create', path: '/corporates/create' }, - { title: 'Formularium', path: '/formularium' }, - { title: 'Diagnosis Library (ICD-X)', path: '/masterdiagnosis' }, + { title: 'Formularium', path: '/master/formularium' }, + { title: 'Obat', path: '/master/drugs' }, + { title: 'Diagnosis Library (ICD-X)', path: '/master/diagnosis' }, { title: 'Hospitals', path: '/hospitals' }, ], }, diff --git a/frontend/dashboard/src/pages/Corporates/Form.tsx b/frontend/dashboard/src/pages/Corporates/Form.tsx index 36f407b4..f6a02a4e 100644 --- a/frontend/dashboard/src/pages/Corporates/Form.tsx +++ b/frontend/dashboard/src/pages/Corporates/Form.tsx @@ -358,7 +358,7 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) { - {!isEdit ? 'Create Product' : 'Save Changes'} + {!isEdit ? 'Save New Corporate' : 'Save Corporate'} diff --git a/frontend/dashboard/src/pages/Corporates/Formularium/Index.tsx b/frontend/dashboard/src/pages/Corporates/Formularium/Index.tsx new file mode 100644 index 00000000..9cbe7785 --- /dev/null +++ b/frontend/dashboard/src/pages/Corporates/Formularium/Index.tsx @@ -0,0 +1,43 @@ +import { Card, Grid } from "@mui/material"; +import { useParams } from "react-router-dom"; +import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs"; +import Page from "../../../components/Page"; +import useSettings from "../../../hooks/useSettings"; +import CorporateTabNavigations from "../CorporateTabNavigations"; +import List from "./List"; + + + +export default function CorporateFormularium() { + const { themeStretch } = useSettings(); + + const { corporate_id } = useParams(); + + return ( + + + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Corporates/Formularium/List.tsx b/frontend/dashboard/src/pages/Corporates/Formularium/List.tsx new file mode 100644 index 00000000..f8ddee2b --- /dev/null +++ b/frontend/dashboard/src/pages/Corporates/Formularium/List.tsx @@ -0,0 +1,237 @@ +// @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 } from '@mui/material'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import AddIcon from '@mui/icons-material/Add'; +import UploadIcon from '@mui/icons-material/Upload'; +import CancelIcon from '@mui/icons-material/Cancel'; +// hooks +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom'; +// components +import axios from '../../../utils/axios'; +import { CorporatePlan } from '../../../@types/corporates'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import BasePagination from '../../../components/BasePagination'; +import { enqueueSnackbar } from 'notistack'; + + +export default function PlanList() { + const { themeStretch } = useSettings(); + const { corporate_id } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const navigate = useNavigate(); + + // Dummy Default Data + const [dataTableIsLoading, setDataTableLoading] = useState(true); + const [dataTableData, setDataTableData] = useState({ + current_page: 1, + data: [], + path: "", + first_page_url: "", + last_page: 1, + last_page_url: "", + next_page_url: "", + prev_page_url: "", + per_page: 10, + from: 0, + to: 0, + total: 0 + }); + + + 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 handleSubmit = (event: any) => { + event.preventDefault(); + props.onSearch(searchText); // Trigger to Parent + } + + useEffect(() => { + // console.log('Search Input: useEffect') + setSearchText(searchParams.get('search') ?? ''); + }, [searchParams]) + + return ( +
+ + + ); + } + + // Called on every row to map the data to the columns + function createData( plan: CorporatePlan ): CorporatePlan { + return { + ...plan, + } + } + + const handleInactiveAction = (formularium : any) => { + axios + .put('/corporates/'+corporate_id+'/formulariums/'+formularium.id+'/activate') + .then(() => { + setDataTableData( + { + ...dataTableData, + data: dataTableData.data.map((item: any) => { + if (item.id === formularium.id) { + return { + ...item, + status: 'active' + } + } + + return item; + }) + }) + }) + .catch((error) => { + enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' }); + }) + } + + const handleActiveAction = (formularium : any) => { + axios + .put('/corporates/'+corporate_id+'/formulariums/'+formularium.id+'/deactivate') + .then(() => { + setDataTableData( + { + ...dataTableData, + data: dataTableData.data.map((item: any) => { + if (item.id === formularium.id) { + return { + ...item, + status: 'inactive' + } + } + + return item; + }) + }) + }) + .catch((error) => { + enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' }); + }) + } + + // Generate the every row of the table + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + + return ( + + *': { borderBottom: 'unset' } }}> + {row.id} + {row.code} + {row.name} + {row.items_count} + {( row.status == 'active' ? + : + + )} + + {/* COLLAPSIBLE ROW */} + + + + + + No Extra Data + + + + + + + ); + } + + const loadDataTableData = async (appliedFilter : any | null = null) => { + setDataTableLoading(true); + const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); + const response = await axios.get('/corporates/'+corporate_id+'/formulariums', { params: filter }); + // console.log(response.data); + setDataTableLoading(false); + + setDataTableData(response.data); + } + + const headStyle = { + fontWeight: 'bold', + }; + + const applyFilter = async (searchFilter: any) => { + await loadDataTableData({ "search" : searchFilter }); + setSearchParams({ "search" : searchFilter }); + } + + const handlePageChange = (event : ChangeEvent, value: number) => { + const filter = Object.fromEntries([...searchParams.entries(), ["page", value]]); + loadDataTableData(filter); + setSearchParams(filter); + } + + + useEffect(() => { + loadDataTableData(); + }, []) + + return ( + + + + + + + {/* The Main Table */} + + + + + # + Code + Name + Total Item + Status + + + {dataTableIsLoading ? + ( + + + Loading + + + ) : ( + dataTableData.data.length == 0 ? + ( + + + No Data + + + ) : ( + + {dataTableData.data.map(row => ( + + ))} + + ) + )} +
+
+ +
+
+ ); +} diff --git a/frontend/dashboard/src/pages/Master/Drug/Create.tsx b/frontend/dashboard/src/pages/Master/Drug/Create.tsx new file mode 100644 index 00000000..5e9fccdf --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Drug/Create.tsx @@ -0,0 +1,241 @@ +import * as Yup from 'yup'; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Card, Collapse, Divider, Grid, Stack, Typography } from "@mui/material"; +import { useForm } from "react-hook-form"; +import { useParams } from "react-router-dom"; +import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs"; +import { FormProvider, RHFCheckbox, RHFSelect, RHFTextField } from "../../../components/hook-form"; +import Page from "../../../components/Page"; +import useSettings from "../../../hooks/useSettings"; +import CorporateTabNavigations from "../CorporateTabNavigations"; +import DivisionsList from "./List"; +import { useMemo, useState } from 'react'; + + + +export default function Divisions() { + const { themeStretch } = useSettings(); + + const { corporate_id } = useParams(); + + const NewDivisionSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + code: Yup.string().required('Corporate Code is required'), + active: Yup.boolean().required('Corporate Status is required'), + }); + + const defaultValues = useMemo( + () => ({ + code: '', + }), + [] + ); + + const methods = useForm({ + resolver: yupResolver(NewDivisionSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const onSubmit = async (data: any) => { + console.log(data); + }; + + const [open, setOpen] = useState(false); + + const benefits = [ + { + 'category' : 'General Practitioner', + 'childs' : [ + { + 'name' : 'External Doctor Online', + 'code' : 'gp-external-doctor-online' + }, + { + 'name' : 'External Doctor Offline', + 'code' : 'gp-external-doctor-offline' + }, + { + 'name' : 'Internal Doctor Online', + 'code' : 'gp-internal-doctor-online' + }, + { + 'name' : 'Internal Doctor Offline', + 'code' : 'gp-internal-doctor-offline' + }, + ] + }, + { + 'category' : 'Specialist', + 'childs' : [ + { + 'name' : 'External Doctor Online', + 'code' : 'sp-external-doctor-online' + }, + { + 'name' : 'External Doctor Offline', + 'code' : 'sp-external-doctor-offline' + }, + { + 'name' : 'Internal Doctor Online', + 'code' : 'sp-internal-doctor-online' + }, + { + 'name' : 'Internal Doctor Offline', + 'code' : 'sp-internal-doctor-offline' + }, + ] + }, + { + 'category' : 'Medicines', + 'childs' : [ + { + 'name' : 'Vitamins', + 'code' : 'medicines-vitamins' + }, + { + 'name' : 'Delivery Fee', + 'code' : 'medicines-delivery-fee' + }, + ] + }, + ]; + + const products = [ + { + 'name' : 'Inpatient', + 'code' : 'IP', + }, + { + 'name' : 'Outpatient', + 'code' : 'OP', + }, + { + 'name' : 'Dental', + 'code' : 'DT', + }, + { + 'name' : 'Dental', + 'code' : 'DTL', + }, + { + 'name' : 'Matternity', + 'code' : 'MT', + }, + { + 'name' : 'Special Benefit', + 'code' : 'SB', + }, + ]; + + return ( + + + + + + + + + + + + Benefit Detail + + + + + + Benefit Configuration + + + }> + + + {benefits.map(row => ( + + {row.category} + + {row.childs.map(benefit => ( + + + + ))} + + + ))} + Admin Fee + + {benefits.map(row => ( + + + + ))} + + + + + + + {benefits.map(row => ( + + {row.category} + + {row.childs.map(benefit => ( + + + + ))} + + + ))} + Admin Fee + + {benefits.map(row => ( + + + + ))} + + + + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Drug/Index.tsx b/frontend/dashboard/src/pages/Master/Drug/Index.tsx new file mode 100644 index 00000000..0bdff569 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Drug/Index.tsx @@ -0,0 +1,38 @@ +import { Card, Grid } from "@mui/material"; +import { useParams } from "react-router-dom"; +import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs"; +import Page from "../../../components/Page"; +import useSettings from "../../../hooks/useSettings"; +import List from "./List"; + + + +export default function Drugs() { + const { themeStretch } = useSettings(); + + const { corporate_id } = useParams(); + + const pageTitle = 'Drug'; + return ( + + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Drug/List.tsx b/frontend/dashboard/src/pages/Master/Drug/List.tsx new file mode 100644 index 00000000..195ad6a2 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Drug/List.tsx @@ -0,0 +1,310 @@ +// @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 } from '@mui/material'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import AddIcon from '@mui/icons-material/Add'; +import UploadIcon from '@mui/icons-material/Upload'; +import CancelIcon from '@mui/icons-material/Cancel'; +// hooks +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +import { useParams, useSearchParams } from 'react-router-dom'; +// components +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import { Icd } from '../../../@types/diagnosis'; +import BasePagination from '../../../components/BasePagination'; + +export default function List() { + 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 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(`corporates/${corporate_id}/import-plan-benefit`, 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 => { + alert('Looks like something went wrong. Please check your data and try again. ' + response.message) + }) + } else { + alert('No File Selected') + } + } + + return ( +
+ + {( !currentImportFileName && + + {/*

kjasndkjandskjasndkjansdkjansd

*/} + + + Import + Download Template + +
+ )} + + {( currentImportFileName && + + + + + + + + )} + {( importResult && + + Last Import Result Report : {importResult.result_file?.name ?? "-"} + + )} +
+ ); + } + + // Called on every row to map the data to the columns + function createData( icd: Icd ): Icd { + return { + ...icd, + } + } + + // Generate the every row of the table + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + + return ( + + *': { borderBottom: 'unset' } }}> + + setOpen(!open)} + > + {open ? : } + + + {row.type} + {row.code} + {row.name} + {row.version} + + + + + {/* COLLAPSIBLE ROW */} + + + + + + Description : {row.description} + + + + + + + ); + } + + // 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('/master/drugs', { 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); + } + + useEffect(() => { + loadDataTableData(); + }, []) + + return ( + + + + + {/* The Main Table */} + + + + + + Type + Code + Name + Version + Status + Action + + + {dataTableIsLoading ? + ( + + + Loading + + + ) : ( + dataTableData.data.length == 0 ? + ( + + + No Data + + + ) : ( + + {dataTableData.data.map(row => ( + + ))} + + ) + )} +
+
+ + +
+
+ ); +} diff --git a/frontend/dashboard/src/pages/Master/Formularium/Create.tsx b/frontend/dashboard/src/pages/Master/Formularium/Create.tsx new file mode 100644 index 00000000..e0527248 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Formularium/Create.tsx @@ -0,0 +1,86 @@ +import * as Yup from 'yup'; +import { yupResolver } from "@hookform/resolvers/yup"; +import { Card, Collapse, Divider, Grid, Stack, Typography } from "@mui/material"; +import { useForm } from "react-hook-form"; +import { useParams } from "react-router-dom"; +import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs"; +import { FormProvider, RHFCheckbox, RHFSelect, RHFTextField } from "../../../components/hook-form"; +import Page from "../../../components/Page"; +import useSettings from "../../../hooks/useSettings"; +import { useMemo, useState } from 'react'; +import Form from "./Form"; + + +export default function Divisions() { + const { themeStretch } = useSettings(); + + const [isEdit, setIsEdit] = useState(false); + + const [currentFormularium, setCurrentFormularium] = useState({}); + + const NewDivisionSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + code: Yup.string().required('Corporate Code is required'), + active: Yup.boolean().required('Corporate Status is required'), + }); + + const defaultValues = useMemo( + () => ({ + code: '', + }), + [] + ); + + const methods = useForm({ + resolver: yupResolver(NewDivisionSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const onSubmit = async (data: any) => { + console.log(data) + }; + + const pageTitle = 'Create Formularium'; + return ( + + + + + + + + +
+ + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Formularium/Form.tsx b/frontend/dashboard/src/pages/Master/Formularium/Form.tsx new file mode 100644 index 00000000..9ec18471 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Formularium/Form.tsx @@ -0,0 +1,239 @@ +import * as Yup from 'yup'; +import { useSnackbar } from 'notistack'; +import { useNavigate } from 'react-router-dom'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +// form +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +// @mui +import { styled } from '@mui/material/styles'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Button, + ButtonGroup, + Card, + FormHelperText, + Grid, + Stack, + Typography, +} from '@mui/material'; + +import CancelIcon from '@mui/icons-material/Cancel'; + +// components +import { + FormProvider, + RHFTextField, + RHFRadioGroup, + RHFUploadAvatar, + RHFSwitch, + RHFEditor, + RHFDatepicker, + RHFMultiCheckbox, + RHFCheckbox, + RHFCustomMultiCheckbox, +} from '../../../components/hook-form'; +import { Corporate } from '../../../@types/corporates'; +import axios from '../../../utils/axios'; +import { fCurrency } from '../../../utils/formatNumber'; + +const LabelStyle = styled(Typography)(({ theme }) => ({ + ...theme.typography.subtitle2, + color: theme.palette.text.secondary, + marginBottom: theme.spacing(1), +})); + +interface FormValuesProps extends Partial { + taxes: boolean; + inStock: boolean; +} + +type Props = { + isEdit: boolean; + currentFormularium?: Corporate; +}; + +export default function FormulariumForm({ isEdit, currentFormularium }: Props) { + const navigate = useNavigate(); + + // const [ errors, setErrors ] = useState<{ [key: string]: string }>({}); + + const { enqueueSnackbar } = useSnackbar(); + + const NewCorporateSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + // code: Yup.string().required('Corporate Code is required'), + // file: Yup.boolean().required('Corporate Status is required'), + }); + + const defaultValues = useMemo( + () => ({ + code: currentFormularium?.code || '', + name: currentFormularium?.name || '', + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [currentFormularium] + ); + + const methods = useForm({ + resolver: yupResolver(NewCorporateSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const values = watch(); + + useEffect(() => { + if (isEdit && currentFormularium) { + reset(defaultValues); + } + if (!isEdit) { + reset(defaultValues); + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isEdit, currentFormularium]); + + const onSubmit = async (data: FormValuesProps) => { + try { + if (!isEdit) { + const response = await axios.post('/master/formulariums', data); + } else { + const response = await axios.put('/master/formulariums/' + currentFormularium?.id ?? '', data); + } + reset(); + enqueueSnackbar(!isEdit ? 'Formularium Created Successfully!' : 'Formularium Udpated Successfully!', { variant: 'success' }); + navigate('/master/formularium'); + } catch (error: any) { + if (error && error.response.status === 422) { + for (const [key, value] of Object.entries(error.response.data.errors)) { + setError(key, { message: value[0] }); + enqueueSnackbar(value[0] ?? 'Failed Processing Request', { variant: 'error' }); + } + } + else { + enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' }); + } + } + + const ascent = document?.querySelector("ascent"); + if (ascent != null) { + ascent.innerHTML = ""; + } + }; + + const handleDrop = useCallback( + (acceptedFiles) => { + setValue( + 'logo', + acceptedFiles.map((file: Blob | MediaSource) => + Object.assign(file, { + preview: URL.createObjectURL(file), + }) + ) + ); + }, + [setValue] + ); + + const handleRemove = (file: File | string) => { + setValue('logo', null); + }; + + const linking_rules_checkbox_name = "linking_rules" + const linking_tools = [ + { + "value" : "nrik", + "label" : "No. KTP" + }, + { + "value" : "nik", + "label" : "Nomor Induk Karyawan (NIK)" + }, + { + "value" : "member_id", + "label" : "Member ID" + }, + { + "value" : "phone", + "label" : "Nomor Telepon" + }, + { + "value" : "email", + "label" : "E-Mail" + }, + ] + + + const importForm = useRef(null) + const [anchorEl, setAnchorEl] = useState(null); + const [currentImportFileName, setCurrentImportFileName] = useState(null); + + const handleClose = () => { + setAnchorEl(null); + }; + + const handleImportButton = () => { + if (importForm?.current) { + handleClose(); + importForm.current ? importForm.current.click() : console.log('No File selected'); + } else { + alert('No file selected') + } + } + + const handleCancelImportButton = () => { + importForm.current.value = ""; + importForm.current.dispatchEvent(new Event("change", { bubbles: true })); + } + + const handleImportChange = (event: any) => { + if (event.target.files[0]) { + setCurrentImportFileName(event.target.files[0].name) + } else { + setCurrentImportFileName(null); + } + } + + + return ( + + + + + Formularium Detail + +
+ + {(!(currentFormularium?.id) && Will be generated if empty)} +
+ + + + Formularium Drug List Import + + + + + {(currentImportFileName && )} + + + + {!isEdit ? 'Save New Corporate' : 'Save Update'} + + +
+
+ ); +}; diff --git a/frontend/dashboard/src/pages/Master/Formularium/Index.tsx b/frontend/dashboard/src/pages/Master/Formularium/Index.tsx new file mode 100644 index 00000000..22b005cb --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Formularium/Index.tsx @@ -0,0 +1,38 @@ +import { Card, Grid } from "@mui/material"; +import { useParams } from "react-router-dom"; +import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs"; +import Page from "../../../components/Page"; +import useSettings from "../../../hooks/useSettings"; +import List from "./List"; + + + +export default function Drugs() { + const { themeStretch } = useSettings(); + + const { corporate_id } = useParams(); + + const pageTitle = 'Formularium'; + return ( + + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Formularium/List.tsx b/frontend/dashboard/src/pages/Master/Formularium/List.tsx new file mode 100644 index 00000000..a8c235d9 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Formularium/List.tsx @@ -0,0 +1,310 @@ +// @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 } from '@mui/material'; +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import AddIcon from '@mui/icons-material/Add'; +import UploadIcon from '@mui/icons-material/Upload'; +import CancelIcon from '@mui/icons-material/Cancel'; +// hooks +import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; +import useSettings from '../../../hooks/useSettings'; +import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; +// components +import axios from '../../../utils/axios'; +import { LaravelPaginatedData } from '../../../@types/paginated-data'; +import { Icd } from '../../../@types/diagnosis'; +import BasePagination from '../../../components/BasePagination'; + +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 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/formularium/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 => { + alert('Looks like something went wrong. Please check your data and try again. ' + response.message) + }) + } else { + alert('No File Selected') + } + } + + return ( +
+ + {( !currentImportFileName && + + {/*

kjasndkjandskjasndkjansdkjansd

*/} + + + {navigate('/master/formularium/create')} }>Create + Import + Download Template + +
+ )} + + {( currentImportFileName && + + + + + + + + )} + {( importResult && + + Last Import Result Report : {importResult.result_file?.name ?? "-"} + + )} +
+ ); + } + + // Called on every row to map the data to the columns + function createData( icd: Icd ): Icd { + return { + ...icd, + } + } + + // Generate the every row of the table + function Row(props: { row: ReturnType }) { + const { row } = props; + const [open, setOpen] = React.useState(false); + + return ( + + *': { borderBottom: 'unset' } }}> + + setOpen(!open)} + > + {open ? : } + + + {row.code} + {row.name} + {row.items_count} + + + + + {/* COLLAPSIBLE ROW */} + + + + + + Description : {row.description} + + + + + + + ); + } + + // 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('/master/formulariums', { 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); + } + + useEffect(() => { + loadDataTableData(); + }, []) + + return ( + + + + + {/* The Main Table */} + + + + + + Code + Name + Total Item + Status + Action + + + {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 26a78ff7..0a47e0dd 100644 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -163,6 +163,11 @@ export default function Router() { element: , }, + { + path: 'corporates/:corporate_id/formularium', + element: , + }, + { path: 'corporates/:corporate_id/diagnosis-exclusions', element: , @@ -172,6 +177,20 @@ export default function Router() { path: 'master/diagnosis', element: , }, + + { + path: 'master/drugs', + element: , + }, + + { + path: 'master/formularium', + element: , + }, + { + path: 'master/formularium/create', + element: , + }, ] }, // { @@ -240,6 +259,13 @@ const Plans = Loadable(lazy(() => import('../pages/Corporates/Plan/Index'))); const DiagnosisExclusions = Loadable(lazy(() => import('../pages/Corporates/DiagnosisExclusion/Index'))); +const CorporateFormularium = Loadable(lazy(() => import('../pages/Corporates/Formularium/Index'))); + const MasterDiagnosis = Loadable(lazy(() => import('../pages/Master/Diagnosis/Index'))); +const MasterDrug = Loadable(lazy(() => import('../pages/Master/Drug/Index'))); + +const MasterFormularium = Loadable(lazy(() => import('../pages/Master/Formularium/Index'))); +const MasterFormulariumCreate = Loadable(lazy(() => import('../pages/Master/Formularium/Create'))); + const CorporateServices = Loadable(lazy(() => import('../pages/Corporates/Services/Index'))); diff --git a/resources/files/daftar_masteritem_ccp_14-06-2022.xlsx b/resources/files/daftar_masteritem_ccp_14-06-2022.xlsx new file mode 100644 index 00000000..002328eb Binary files /dev/null and b/resources/files/daftar_masteritem_ccp_14-06-2022.xlsx differ