diff --git a/Modules/Internal/Http/Controllers/Api/CorporateController.php b/Modules/Internal/Http/Controllers/Api/CorporateController.php index 0a69b08c..013047ee 100644 --- a/Modules/Internal/Http/Controllers/Api/CorporateController.php +++ b/Modules/Internal/Http/Controllers/Api/CorporateController.php @@ -532,6 +532,18 @@ class CorporateController extends Controller "file_url" => url('files/Template - ICD.xlsx') ]); break; + case 'master-formularium': + return Helper::responseJson([ + 'file_name' => "Template - Formularium.xlsx", + "file_url" => url('files/Template - Formularium.xlsx') + ]); + break; + case 'master-formularium-corporate': + return Helper::responseJson([ + 'file_name' => "Template - Formularium.xlsx", + "file_url" => url('files/Template - Formularium - Corporate.xlsx') + ]); + break; default: return Helper::responseJson([], 'error', 404); break; diff --git a/Modules/Internal/Http/Controllers/Api/CorporateFormulariumController.php b/Modules/Internal/Http/Controllers/Api/CorporateFormulariumController.php index 4f2c87af..51914ad3 100644 --- a/Modules/Internal/Http/Controllers/Api/CorporateFormulariumController.php +++ b/Modules/Internal/Http/Controllers/Api/CorporateFormulariumController.php @@ -5,10 +5,20 @@ namespace Modules\Internal\Http\Controllers\Api; use App\Helpers\Helper; use App\Models\CorporateFormularium; use App\Models\Formularium; +use App\Services\ImportService; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Storage; use Modules\Internal\Transformers\CorporateFormulariumResource; +use Modules\Internal\Services\FormulariumService; + +use Box\Spout\Reader\Common\Creator\ReaderEntityFactory; +use Box\Spout\Writer\Common\Creator\Style\StyleBuilder; +use Box\Spout\Common\Entity\Style\CellAlignment; +use Box\Spout\Common\Entity\Style\Color; +use Box\Spout\Writer\Common\Creator\WriterEntityFactory; +use Box\Spout\Common\Entity\Row; class CorporateFormulariumController extends Controller { @@ -16,25 +26,24 @@ class CorporateFormulariumController extends Controller * Display a listing of the resource. * @return Renderable */ + public function __construct(FormulariumService $formulariumService) + { + $this->formulariumService = $formulariumService; + } public function index(Request $request, $corporate_id) { - $formulariums = Formularium::query() - ->filter($request->all()); - if (!empty($request->status) && $request->status == 'inactive') { - $formulariums = $formulariums->whereDoesntHave('corporateFormulariums'); - } else if (!empty($request->status) && $request->status == 'all') { - - } else { // Active or Default - $formulariums->whereHas('corporateFormulariums', function ($corporateFormularium) use ($corporate_id){ - $corporateFormularium->where('corporate_id', $corporate_id); - }); - } - $formulariums = $formulariums->with(['corporateFormulariums' => function ($query) use ($corporate_id) { - $query->where('corporate_id', $corporate_id); - }]) - ->withCount('items') - ->paginate(); + $formulariums = CorporateFormularium::query() + ->where('corporate_id', $corporate_id) + ->paginate(15); + // if (!empty($request->status) && $request->status == 'inactive') { + // $formulariums = $formulariums->whereDoesntHave('corporateFormulariums'); + // } else if (!empty($request->status) && $request->status == 'all') { + // } else { // Active or Default + // $formulariums->whereHas('corporateFormulariums', function ($corporateFormularium) use ($corporate_id){ + // $corporateFormularium->where('corporate_id', $corporate_id); + // }); + // } return Helper::paginateResources(CorporateFormulariumResource::collection($formulariums)); } @@ -101,7 +110,7 @@ class CorporateFormulariumController extends Controller public function updateStatus($corporate_id, $formularium_id , $status) { if ($status == 'activate') { - $corporateFormularium = CorporateFormularium::firstOrCreate([ + $corporateFormularium = CorporateFormularium::updateOrCreate([ 'corporate_id' => $corporate_id, 'formularium_id' => $formularium_id ], [ @@ -128,4 +137,194 @@ class CorporateFormulariumController extends Controller ]); } } + + public function import(Request $request, $id) + { + $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); + + // $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 => 'active', + ]; + + 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'])){ + throw new ImportRowException(__('Internal Code is REQUIRED'), 0, null, $row); + } + if ( + // empty($row_data['code']) && + // empty($row_data['description']) && + empty($row_data['code']) && + empty($row_data['active']) + ) { + continue; + } + + // Save the Row + $formulariums = Formularium::where('code', $row_data['code'])->first(); + if ($formulariums){ + $row_data['formularium_id'] = $formulariums->id; + } else { + throw new ImportRowException(__('Internal Code is Not Found'), 0, null, $row); + } + $formulariumService = new FormulariumService(); + $formulariumService->handleFormuariumCorporateRow($row_data, $id); + + // 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, + ] + ]; + } + + public function generateFormulariumList(Request $request, $id){ + // Mendapatkan data yang akan diekspor (misalnya, dari database) + // $data = Formularium::get()->toArray(); + $formulariums = Formularium::query() + ->filter($request->all()); + // if (!empty($request->status) && $request->status == 'inactive') { + // $formulariums = $formulariums->whereDoesntHave('corporateFormulariums'); + // } else if (!empty($request->status) && $request->status == 'all') { + + // } else { // Active or Default + // $formulariums->whereHas('corporateFormulariums', function ($corporateFormularium) use ($corporate_id){ + // $corporateFormularium->where('corporate_id', $corporate_id); + // }); + // } + $data = $formulariums->with(['corporateFormulariums' => function ($query) use ($id) { + $query->where('corporate_id', $id); + }])->get()->toArray(); + // Membuat penulis entitas Spout + $writer = WriterEntityFactory::createXLSXWriter(); + + // Membuka penulis untuk menulis ke file + $writer->openToFile(public_path('files/TemplateFormulariumList.xlsx')); + /** Create a style with the StyleBuilder */ + $style = (new StyleBuilder()) + ->setFontBold() + ->build(); + + // Menulis header kolom + $headers_map_to_table_fields = $this->formulariumService->listing_doc_headers; + $headerRow = WriterEntityFactory::createRowFromArray($headers_map_to_table_fields, $style); + + $writer->addRow($headerRow); + + // Menulis data + if (!empty($data)) { + foreach ($data as $item) { + $rowData = [ + $item['code'], // code + $item['name'], // name + $item['description'], // Description + $item['manufacturer'], // manufacturer + $item['category_name'], // category_name + $item['kategori_obat'], // kategori_obat + $item['uom'], // uom + $item['general_indication'], // Description + $item['composition'], // composition + $item['atc_code'], // atc_code + $item['class'], // class + $item['bpom_registration'], // bpom_registration + $item['classifications'], // classifications + $item['cat_for'], // cat_for + + ]; + + $row = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($row); + } + } + + // Menutup penulis + $writer->close(); + + // Mengembalikan response untuk mengunduh file + $filePath = public_path('files/TemplateFormulariumList.xlsx'); + + return Helper::responseJson([ + 'file_name' => "Formularium List " . date('Y-m-d h:i:s'), + "file_url" => url('files/TemplateFormulariumList.xlsx') + ]); + + } } diff --git a/Modules/Internal/Http/Controllers/Api/FormulariumController.php b/Modules/Internal/Http/Controllers/Api/FormulariumController.php index a49e9963..ce2d86bf 100644 --- a/Modules/Internal/Http/Controllers/Api/FormulariumController.php +++ b/Modules/Internal/Http/Controllers/Api/FormulariumController.php @@ -3,9 +3,21 @@ namespace Modules\Internal\Http\Controllers\Api; use App\Models\Formularium; +use App\Services\ImportService; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use App\Helpers\Helper; +use Illuminate\Support\Facades\Storage; +use Modules\Internal\Services\FormulariumService; + +use Box\Spout\Reader\Common\Creator\ReaderEntityFactory; +use Box\Spout\Writer\Common\Creator\Style\StyleBuilder; +use Box\Spout\Common\Entity\Style\CellAlignment; +use Box\Spout\Common\Entity\Style\Color; +use Box\Spout\Writer\Common\Creator\WriterEntityFactory; +use Box\Spout\Common\Entity\Row; + class FormulariumController extends Controller { @@ -13,10 +25,27 @@ class FormulariumController extends Controller * Display a listing of the resource. * @return Renderable */ - public function index(Request $request) + public function __construct(FormulariumService $formulariumService) { - $formulariums = Formularium::withTrashed()->withCount('items')->filter($request->toArray())->paginate(); - + $this->formulariumService = $formulariumService; + } + public function index(Request $request, $id) + { + if ($request->search) { + return Formularium::when($request->search ?? null, function ($formularium) use ($request, $id) { + $formularium->where('formularium_template_id', $id) + ->where('name', 'LIKE', '%' . $request->search . '%') + ->orWhere('code', 'LIKE', '%' . $request->search . '%'); + })->paginate(15); + } + else { + $formulariums = Formularium::query() + // ->filter($request->toArray()) + ->where('formularium_template_id', $id) + ->orderBy('name', 'ASC') + ->paginate(15); + return $formulariums; + } return $formulariums; } @@ -34,7 +63,7 @@ class FormulariumController extends Controller * @param Request $request * @return Renderable */ - public function store(Request $request) + public function store(Request $request, $id) { $request->validate([ 'name' => 'required|string|max:255', @@ -87,7 +116,7 @@ class FormulariumController extends Controller // } - public function import(Request $request) + public function import(Request $request, $id) { $request->validate([ 'file' => 'required|file|mimes:xls,xlsx,csv,txt', @@ -95,7 +124,6 @@ class FormulariumController extends Controller // 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', @@ -107,7 +135,6 @@ class FormulariumController extends Controller $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) { @@ -130,16 +157,19 @@ class FormulariumController extends Controller $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' + 1 => 'name', + 2 => 'description', + 3 => 'manufacturer', + 4 => 'category_name', + 5 => 'kategori_obat', + 6 => 'uom', + 7 => 'general_indication', + 8 => 'composition', + 9 => 'atc_code', + 10 => 'class', + 11 => 'bpom_registration', + 12 => 'classifications', + 13 => 'cat_for', ]; foreach ($row->getCells() as $header_index => $cell) { @@ -157,20 +187,27 @@ class FormulariumController extends Controller 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'])) { + empty($row_data['code']) && + empty($row_data['name']) && + empty($row_data['description']) && + empty($row_data['manufacturer']) && + empty($row_data['category_name']) && + empty($row_data['kategori_obat']) && + empty($row_data['uom']) && + empty($row_data['general_indication']) && + empty($row_data['composition']) && + empty($row_data['atc_code']) && + empty($row_data['class']) && + empty($row_data['bpom_registration']) && + empty($row_data['classifications']) && + empty($row_data['cat_for']) + ) { continue; } // Save the Row - $exclusionService = new ExclusionService(); - $exclusionService->handleDiagnosisExclusionRow($corporate, $row_data); + $formulariumService = new FormulariumService(); + $formulariumService->handleFormuariumTemplateRow($row_data, $id); // Write Success Result to File $import->addArrayToRow(array_merge($row_data, [ @@ -211,4 +248,64 @@ class FormulariumController extends Controller ] ]; } + + public function generateFormulariumList(Request $request) + { + // Mendapatkan data yang akan diekspor (misalnya, dari database) + $data = Formularium::get()->toArray(); + + // Membuat penulis entitas Spout + $writer = WriterEntityFactory::createXLSXWriter(); + + // Membuka penulis untuk menulis ke file + $writer->openToFile(public_path('files/TemplateFormulariumList.xlsx')); + /** Create a style with the StyleBuilder */ + $style = (new StyleBuilder()) + ->setFontBold() + ->build(); + + // Menulis header kolom + $headers_map_to_table_fields = $this->formulariumService->listing_doc_headers; + $headerRow = WriterEntityFactory::createRowFromArray($headers_map_to_table_fields, $style); + + $writer->addRow($headerRow); + + // Menulis data + if (!empty($data)) { + foreach ($data as $item) { + $rowData = [ + $item['code'], // code + $item['name'], // name + $item['description'], // Description + $item['manufacturer'], // manufacturer + $item['category_name'], // category_name + $item['kategori_obat'], // kategori_obat + $item['uom'], // uom + $item['general_indication'], // Description + $item['composition'], // composition + $item['atc_code'], // atc_code + $item['class'], // class + $item['bpom_registration'], // bpom_registration + $item['classifications'], // classifications + $item['cat_for'], // cat_for + + ]; + + $row = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($row); + } + } + + // Menutup penulis + $writer->close(); + + // Mengembalikan response untuk mengunduh file + $filePath = public_path('files/TemplateFormulariumList.xlsx'); + + return Helper::responseJson([ + 'file_name' => "Formularium List " . date('Y-m-d h:i:s'), + "file_url" => url('files/TemplateFormulariumList.xlsx') + ]); + + } } diff --git a/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php b/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php new file mode 100644 index 00000000..1276bb7c --- /dev/null +++ b/Modules/Internal/Http/Controllers/Api/FormulariumTemplateController.php @@ -0,0 +1,314 @@ +search){ + return FormulariumTemplate::when($request->search ?? null, function($icd, $search) { + $icd->where('name', 'LIKE', '%'.$search.'%') + ->orWhere('description', 'LIKE', '%'.$search.'%'); + })->paginate(15); + } else { + $diagnosisTemplate = FormulariumTemplate::query() + // ->filter($request->toArray()) + ->orderBy('name', 'ASC') + ->paginate(15); + return $diagnosisTemplate; + } + + } + + /** + * 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' + ]); + + $newDiagnosisTemplate = FormulariumTemplate::create($request->all()); + + return $newDiagnosisTemplate; + } + + /** + * 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) + { + $FormulariumTemplate = FormulariumTemplate::findOrFail($id); + + return $FormulariumTemplate; + } + + /** + * Update the specified resource in storage. + * @param Request $request + * @param int $id + * @return Renderable + */ + public function update(Request $request, $id) + { + $FormulariumTemplate = FormulariumTemplate::findOrFail($id); + $request->validate([ + 'name' => 'required' + ]); + $FormulariumTemplate->fill([ + // 'code' => $request->code, + 'name' => $request->name, + 'description' => $request->description, + 'active' => $request->active, + ])->save(); + + return $FormulariumTemplate; + } + + /** + * Remove the specified resource from storage. + * @param int $id + * @return Renderable + */ + public function destroy($id) + { + // + } + + public function search(Request $request) + { + return FormulariumTemplate::when($request->search ?? null, function($icd, $search) { + $icd->where('name', 'LIKE', '%'.$search.'%') + ->orWhere('code', 'LIKE', '%'.$search.'%'); + })->limit(10)->get(); + } + + public function import(Request $request) + { + $request->validate([ + 'file' => 'required|file|mimes:xls,xlsx,csv,txt', + ]); + $file_name = now()->getPreciseTimestamp(3).'-'.$request->file('file')->getClientOriginalName(); + $file = $request->file('file')->storeAs('temp', $file_name); + + $import = new ImportService(); + $import->read(Storage::path('temp/'.$file_name)); + $import->write(Storage::disk('public')->path('temp/result-'.$file_name), 'xsls'); + + $imported_icd_data = 0; + $failed_icd_data = []; + 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 => 'parent_code', + 2 => 'reff_exc', + 3 => 'description_en', + 4 => 'description_id', + 5 => 'keywords', + 6 => 'version', + 7 => 'active', + ]; + + 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['parent_code']) && + empty($row_data['reff_exc']) && + empty($row_data['description_en']) && + empty($row_data['description_id']) && + empty($row_data['keywords']) && + empty($row_data['version']) && + empty($row_data['active']) + ) { + continue; + } + + // Save the Row + $icdService = new IcdService(); + $icdService->handleIcdRow($row_data); + + // Write Success Result to File + $import->addArrayToRow(array_merge($row_data, [ + 'Ingest Code' => 200, + 'Ingest Note' => 'Success', + ]), $sheet->getName()); + $imported_icd_data++; + + } 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()); + $failed_icd_data[] = ['row_number' => $index, 'error' => $e->getMessage(), 'data' => $row_data]; + } 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()); + $failed_icd_data[] = ['row_number' => $index, 'error' => $e->getMessage(), 'data' => $row_data]; + } + } + } + + break; // Only Read First Row + } + $import->reader->close(); + Storage::delete('temp/'.$file_name); + $import->writer->close(); + + return [ + 'total_successed_row' => $imported_icd_data, + 'total_failed_row' => count($failed_icd_data), + 'failed_row' => $failed_icd_data, + 'result_file' => [ + 'url' => Storage::disk('public')->url('temp/result-'.$file_name), + 'name' => 'result-'.$file_name, + ] + ]; + } + + public function activation(Request $request, $id) + { + $request->validate([ + 'active' => 'required' + ]); + $Icd = FormulariumTemplate::findOrFail($id); + $Icd->active = $request->active == '1'; + + if ($Icd->save()) { + return response()->json([ + 'icd' => $Icd, + 'message' => 'Status Updated Successfully' + ]); + } + } + + public function generateIcdList(Request $request){ + // Mendapatkan data yang akan diekspor (misalnya, dari database) + $data = Icd::get()->toArray(); + + // Membuat penulis entitas Spout + $writer = WriterEntityFactory::createXLSXWriter(); + + // Membuka penulis untuk menulis ke file + $writer->openToFile(public_path('files/CorporateMembershipList.xlsx')); + /** Create a style with the StyleBuilder */ + $style = (new StyleBuilder()) + ->setFontBold() + ->build(); + + // Menulis header kolom + $headers_map_to_table_fields = $this->icdService->listing_doc_headers; + $headerRow = WriterEntityFactory::createRowFromArray($headers_map_to_table_fields, $style); + + $writer->addRow($headerRow); + + // Menulis data + if (!empty($data)) { + foreach ($data as $item) { + $rowData = [ + $item['rev'], // Rev + $item['version'], // Version + $item['code'], // Code + $item['parent_code'], // Parent Code + $item['name'], // Name + $item['description'], // Description + $item['active'] == 1 ? 'Active' : 'Inactive', // Status + $item['type'], // Type + ]; + + $row = WriterEntityFactory::createRowFromArray($rowData); + $writer->addRow($row); + } + } + + // Menutup penulis + $writer->close(); + + // Mengembalikan response untuk mengunduh file + $filePath = public_path('files/CorporateMembershipList.xlsx'); + + return Helper::responseJson([ + 'file_name' => "Diagnosis ICD List " . date('Y-m-d h:i:s'), + "file_url" => url('files/CorporateMembershipList.xlsx') + ]); + + } +} diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 9315ddbc..fc2603d3 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -23,6 +23,7 @@ use Modules\Internal\Http\Controllers\Api\DoctorController; use Modules\Internal\Http\Controllers\Api\DoctorRatingController; 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\LivechatController; use Modules\Internal\Http\Controllers\Api\MemberController; @@ -116,6 +117,8 @@ Route::prefix('internal')->group(function () { Route::post('corporates/{corporate_id}/services/{service_code}/specialities/exclusion', [CorporateServiceController::class, 'storeExclusion']); Route::get('corporates/{corporate_id}/formulariums', [CorporateFormulariumController::class, 'index']); + Route::get('corporates/{corporate_id}/formulariums/list', [CorporateFormulariumController::class, 'generateFormulariumList']); + Route::post('corporates/{corporate_id}/formulariums/import', [CorporateFormulariumController::class, 'import']); Route::put('corporates/{corporate_id}/formulariums/{formularium_id}/{action}', [CorporateFormulariumController::class, 'updateStatus']); Route::controller(CorporateController::class)->group(function () { Route::post('add-files-doc', 'addFilesDoc'); @@ -136,19 +139,27 @@ Route::prefix('internal')->group(function () { Route::get('master/diagnosis-template/{id}/edit', [DiagnosisTemplateController::class, 'edit']); Route::put('master/diagnosis-template/{id}/update', [DiagnosisTemplateController::class, 'update']); + Route::get('master/formulariums/{formulariums_template_id}', [FormulariumController::class, 'index']); + Route::post('master/formulariums/{formulariums_template_id}', [FormulariumController::class, 'store']); + Route::post('master/formulariums/{formulariums_template_id}/import', [FormulariumController::class, 'import']); + Route::get('master/formulariums/{formulariums_template_id}/list', [FormulariumController::class, 'generateFormulariumList']); + + Route::get('master/formularium-template', [FormulariumTemplateController::class, 'index']); + Route::get('master/formularium-template/search', [FormulariumTemplateController::class, 'search']); + Route::post('master/formularium-template/store', [FormulariumTemplateController::class, 'store']); + Route::put('master/formularium-template/{id}/activation', [FormulariumTemplateController::class, 'activation']); + Route::get('master/formularium-template/{id}/edit', [FormulariumTemplateController::class, 'edit']); + Route::put('master/formularium-template/{id}/update', [FormulariumTemplateController::class, 'update']); + Route::get('master/diagnosis/{diagnosis_template_id}', [DiagnosisController::class, 'index']); Route::get('master/diagnosis/{diagnosis_template_id}/search', [DiagnosisController::class, 'search']); Route::post('master/diagnosis/{diagnosis_template_id}/import', [DiagnosisController::class, 'import']); Route::get('master/diagnosis/{diagnosis_template_id}/list', [DiagnosisController::class, 'generateIcdList']); Route::put('master/diagnosis/{diagnosis_template_id}/activation', [DiagnosisController::class, 'activation']); - - 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']); + Route::get('members', [MemberController::class, 'index']); Route::get('members/{member_id}/benefits', [MemberController::class, 'benefits']); diff --git a/Modules/Internal/Services/FormulariumService.php b/Modules/Internal/Services/FormulariumService.php index 7eb67acf..1d7e9a58 100644 --- a/Modules/Internal/Services/FormulariumService.php +++ b/Modules/Internal/Services/FormulariumService.php @@ -7,10 +7,11 @@ use App\Models\Benefit; use App\Models\Corporate; use App\Models\Drug; use App\Models\Formularium; +use App\Models\CorporateFormularium; use App\Models\Plan; use Box\Spout\Writer\Common\Creator\WriterEntityFactory; -class CorporateService +class FormulariumService { protected function validateFormulariumRow($row) { @@ -45,4 +46,75 @@ class CorporateService throw $e; } } + + public function handleFormuariumTemplateRow($row, $id) + { + try { + $formularium = Formularium::updateOrCreate( + [ + 'code' => $row['code'], + 'formularium_template_id' => $id + ], + [ + 'code' => $row['code'], + 'name' => $row['name'], + 'description' => $row['description'], + 'manufacturer' => $row['manufacturer'], + 'category_name' => $row['category_name'], + 'kategori_obat' => $row['kategori_obat'], + 'uom' => $row['uom'], + 'composition' => $row['composition'], + 'general_indication' => $row['general_indication'], + 'atc_code' => $row['atc_code'], + 'class' => $row['class'], + 'bpom_registration' => $row['bpom_registration'], + 'classifications' => $row['classifications'], + 'cat_for' => $row['cat_for'], + 'formularium_template_id' => $id, + ]); + + return $formularium; + } catch (\Exception $e) { + throw $e; + } + } + + public function handleFormuariumCorporateRow($row, $id) + { + try { + $formularium = CorporateFormularium::updateOrCreate( + [ + 'formularium_id' => $row['formularium_id'], + 'corporate_id' => $id + ], + [ + 'formularium_id' => $row['formularium_id'], + 'corporate_id' => $id, + 'active' => $row['active'] + ]); + + return $formularium; + } catch (\Exception $e) { + throw $e; + } + } + + public $listing_doc_headers = [ + "Internal Code", + "Name", + "Description", + "Manufacturer", + "Category Name", + "Kategori Obat", + "UOM", + "Composition", + "General Indication", + "ATC Code", + "Class", + "BPOM Registration", + "Classifications", + "Cat For (O = obat, VS = Vitamin Suplemen, H=herbal, M=makanan, etc) ", + ]; + + } diff --git a/Modules/Internal/Transformers/CorporateFormulariumResource.php b/Modules/Internal/Transformers/CorporateFormulariumResource.php index ecaf2fac..bb639c34 100644 --- a/Modules/Internal/Transformers/CorporateFormulariumResource.php +++ b/Modules/Internal/Transformers/CorporateFormulariumResource.php @@ -14,13 +14,28 @@ class CorporateFormulariumResource extends JsonResource */ public function toArray($request) { + + return [ - 'id' => $this->id, - 'code' => $this->code, - 'name' => $this->name, - 'items_count' => $this->items_count, - 'status' => $this->corporateFormulariums->count() ? 'active' : 'inactive', - 'corporate_formulariums' => $this->coporateFormulariums, + 'id' => $this->formularium->id, + 'code' => $this->formularium->code, + 'name' => $this->formularium->name, + 'description' => $this->formularium->description, + 'manufacturer' => $this->formularium->manufacturer, + 'category_name' => $this->formularium->category_name, + 'kategori_obat' => $this->formularium->kategori_obat, + 'uom' => $this->formularium->uom, + 'general_indication' => $this->formularium->general_indication, + 'composition' => $this->formularium->composition, + 'atc_code' => $this->formularium->atc_code, + 'class' => $this->formularium->class, + 'bpom_registration' => $this->formularium->bpom_registration, + 'classifications' => $this->formularium->classifications, + 'cat_for' => $this->formularium->cat_for, + 'items_count' => $this->formularium->items_count, + 'status' => $this->active ? 'active' : 'inactive', + // 'corporate_formulariums' => $this->formua, + 'active' => $this->active == 1 ? 'Active' : 'Inactive', ]; } } diff --git a/app/Models/CorporateFormularium.php b/app/Models/CorporateFormularium.php index 18cf8abb..24e62b0f 100644 --- a/app/Models/CorporateFormularium.php +++ b/app/Models/CorporateFormularium.php @@ -15,7 +15,8 @@ class CorporateFormularium extends Model protected $fillable = [ 'corporate_id', - 'formularium_id' + 'formularium_id', + 'active' ]; public function corporate() diff --git a/app/Models/Formularium.php b/app/Models/Formularium.php index b8063432..83adb6bc 100644 --- a/app/Models/Formularium.php +++ b/app/Models/Formularium.php @@ -17,6 +17,19 @@ class Formularium extends Model protected $fillable = [ 'code', 'name', + 'description', + 'manufacturer', + 'category_name', + 'kategori_obat', + 'uom', + 'general_indication', + 'composition', + 'atc_code', + 'class', + 'bpom_registration', + 'classifications', + 'cat_for', + 'formularium_template_id' ]; public function setCodeAttribute($value) @@ -26,7 +39,7 @@ class Formularium extends Model public function corporateFormulariums() { - return $this->hasMany(CorporateFormularium::class); + return $this->hasMany(CorporateFormularium::class, 'formularium_id', 'id'); } public function items() diff --git a/app/Models/FormulariumTemplate.php b/app/Models/FormulariumTemplate.php new file mode 100644 index 00000000..d15017a8 --- /dev/null +++ b/app/Models/FormulariumTemplate.php @@ -0,0 +1,26 @@ +logAuditTrail($model, 'updated'); }); - IcdTemplate::deleted(function ($model) { $this->logAuditTrail($model, 'deleted'); }); + // Formualrium Template + FormulariumTemplate::updated(function ($model) { + $this->logAuditTrail($model, 'updated'); + }); + FormulariumTemplate::deleted(function ($model) { + $this->logAuditTrail($model, 'deleted'); + }); + } diff --git a/database/migrations/2023_09_22_111147_create_formularium_templates2_table.php b/database/migrations/2023_09_22_111147_create_formularium_templates2_table.php new file mode 100644 index 00000000..96cec199 --- /dev/null +++ b/database/migrations/2023_09_22_111147_create_formularium_templates2_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('name'); + $table->string('description'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('formularium_templates'); + } +}; diff --git a/database/migrations/2023_09_22_143511_add_formularium_template_id_to_formularium.php b/database/migrations/2023_09_22_143511_add_formularium_template_id_to_formularium.php new file mode 100644 index 00000000..f06d93aa --- /dev/null +++ b/database/migrations/2023_09_22_143511_add_formularium_template_id_to_formularium.php @@ -0,0 +1,32 @@ +integer('formularium_template_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('formulariums', function (Blueprint $table) { + $table->dropColumn('formularium_template_id'); + }); + } +}; diff --git a/database/migrations/2023_09_22_170828_add_column_to_formularium.php b/database/migrations/2023_09_22_170828_add_column_to_formularium.php new file mode 100644 index 00000000..59ed19b4 --- /dev/null +++ b/database/migrations/2023_09_22_170828_add_column_to_formularium.php @@ -0,0 +1,52 @@ +text('description')->after('name');; + $table->string('manufacturer')->after('description'); + $table->string('category_name')->after('manufacturer'); + $table->string('kategori_obat')->after('category_name'); + $table->string('uom')->after('kategori_obat'); + $table->text('general_indication')->after('uom'); + $table->string('atc_code')->after('general_indication'); + $table->string('class')->after('atc_code'); + $table->string('bpom_registration')->after('class'); + $table->string('classifications')->after('bpom_registration'); + $table->string('cat_for')->after('classifications'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('formulariums', function (Blueprint $table) { + $table->dropColumn('description'); + $table->dropColumn('manufacturer'); + $table->dropColumn('category_name'); + $table->dropColumn('kategori_obat'); + $table->dropColumn('uom'); + $table->dropColumn('general_indication'); + $table->dropColumn('atc_code'); + $table->dropColumn('class'); + $table->dropColumn('bpom_registration'); + $table->dropColumn('classifications'); + $table->dropColumn('cat_for'); + }); + } +}; diff --git a/database/migrations/2023_09_25_091157_add_column_to_formularium.php b/database/migrations/2023_09_25_091157_add_column_to_formularium.php new file mode 100644 index 00000000..9fa87d61 --- /dev/null +++ b/database/migrations/2023_09_25_091157_add_column_to_formularium.php @@ -0,0 +1,32 @@ +text('composition')->after('general_indication'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('formulariums', function (Blueprint $table) { + $table->dropColumn('composition'); + }); + } +}; diff --git a/database/migrations/2023_09_25_133336_add_column_to_corporate_formulariums.php b/database/migrations/2023_09_25_133336_add_column_to_corporate_formulariums.php new file mode 100644 index 00000000..d1fe8ef3 --- /dev/null +++ b/database/migrations/2023_09_25_133336_add_column_to_corporate_formulariums.php @@ -0,0 +1,32 @@ +integer('active'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('corporate_formulariums', function (Blueprint $table) { + $table->dropColumn('active'); + }); + } +}; diff --git a/frontend/dashboard/src/components/history/History.tsx b/frontend/dashboard/src/components/history/History.tsx new file mode 100644 index 00000000..f657b5ab --- /dev/null +++ b/frontend/dashboard/src/components/history/History.tsx @@ -0,0 +1,209 @@ +// @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 = 'Audittrail Corporate'; + + const { themeStretch } = useSettings(); + + const { corporate_id } = useParams(); + + const [corporate, setCorporate] = useState(); + const [ currentCorporate, setCurrentCorporate ] = useState(); + + const configuredCorporateContext = useContext(ConfiguredCorporateContext); + + useEffect(() => { + setCorporate(configuredCorporateContext.currentCorporate); + const model = 'App\\Models\\Corporate'; + const url = `/audittrail/${corporate_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 === 'created_by' || key === 'updated_by') { + 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); + + return ( + + {`${field}`} + {`${value}`} + {renderedValue} + + ); + })} + + + + + ))} +
+ ); +} diff --git a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx index 2629d771..90ac94b9 100644 --- a/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx +++ b/frontend/dashboard/src/layouts/dashboard/navbar/NavConfig.tsx @@ -52,7 +52,7 @@ const navConfig = [ children: [ { title: 'Corporate', path: '/corporates' }, // { title: 'Corporate Create', path: '/corporates/create' }, - { title: 'Formularium', path: '/master/formularium' }, + { title: 'Formularium', path: '/master/formularium-template' }, { title: 'Obat', path: '/master/drugs' }, { title: 'Master ICD-10 Diagnosis', path: '/master/diagnosis-template' }, { title: 'Hospitals', path: '/hospitals' }, diff --git a/frontend/dashboard/src/pages/Corporates/Formularium/List-old.tsx b/frontend/dashboard/src/pages/Corporates/Formularium/List-old.tsx new file mode 100644 index 00000000..d50912fc --- /dev/null +++ b/frontend/dashboard/src/pages/Corporates/Formularium/List-old.tsx @@ -0,0 +1,355 @@ +// @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, Input, Grid } 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 filterForm = useRef(); + const [searchText, setSearchText] = useState(""); + const [searchStatus, setSearchStatus] = useState("active"); + + const handleSearchChange = (event: any) => { + const newSearchText = event.target.value ?? '' + setSearchText(newSearchText); + } + + const handleStatusChange = (event: any) => { + const newSearchStatus = event.target.value ?? '' + console.log('changing to', newSearchStatus) + setSearchStatus(newSearchStatus) + // console.log(searchStatus); + + const searchFilter = { + "search" : searchText, + "status" : newSearchStatus + }; + + props.onSearch(searchFilter); + } + + const handleSubmit = (event?: any) => { + event?.preventDefault(); + const searchFilter = { + "search" : searchText, + "status" : searchStatus + }; + + props.onSearch(searchFilter); // Trigger to Parent + } + + useEffect(() => { + setSearchText(searchParams.get('search') ?? ''); + setSearchStatus(searchParams.get('status') ?? searchStatus ?? 'active'); + }, [searchParams]) + + return ( +
+ + + + + + + + + {/* ITS FUCKING MAGIC, SUBMIT BY ENTER WORKING IF THIS BUTTON IS AVAILABLE */} + + +
+ ); + } + + // Called on every row to map the data to the columns + function createData( plan: CorporatePlan ): CorporatePlan { + return { + ...plan, + } + } + + const handleInactiveAction = (formularium : any) => { + enqueueSnackbar('Fuck yuo'), { variant: 'error' }; + 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) => { + console.log(error) + enqueueSnackbar(error.data?.message ?? (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' } }}> + + setOpen(!open)} + > + {open ? : } + + + {row.code} + {row.atc_code} + {row.name} + {row.category_name} + {row.uom} + {/* {row.items_count} */} + {( row.status == 'active' ? + : + + )} + + {/* COLLAPSIBLE ROW */} + + + + + + Detail + + + + + Description + + + : {row.description ?? '-'} + + + General Indication + + + : {row.general_indication ?? '-'} + + + Composition + + + : {row.composition ?? '-'} + + + + + + + Kategori Obat + + + : {row.kategori_obat ?? '-'} + + + BPOM Registration + + + : {row.bpom_registration ?? '-'} + + + Classifications + + + : {row.classifications ?? '-'} + + + Cat For + + + : {row.cat_for ?? '-'} + + + Class + + + : {row.class ?? '-'} + + + Manufacturer + + + : {row.manufacturer ?? '-'} + + + + + + + + + + ); + } + + 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(searchFilter); + setSearchParams(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 + ATC Code + Name + Category Name + UOM + Status + + + {dataTableIsLoading ? + ( + + + Loading + + + ) : ( + dataTableData.data.length == 0 ? + ( + + + No Data + + + ) : ( + + {dataTableData.data.map(row => ( + + ))} + + ) + )} +
+
+ +
+
+ ); +} diff --git a/frontend/dashboard/src/pages/Corporates/Formularium/List.tsx b/frontend/dashboard/src/pages/Corporates/Formularium/List.tsx index 64bc80c2..9326d0bb 100644 --- a/frontend/dashboard/src/pages/Corporates/Formularium/List.tsx +++ b/frontend/dashboard/src/pages/Corporates/Formularium/List.tsx @@ -1,5 +1,5 @@ // @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, Input, Grid } from '@mui/material'; +import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Grid, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Pagination } from '@mui/material'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; import AddIcon from '@mui/icons-material/Add'; @@ -8,23 +8,307 @@ 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'; +import { 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 { Icd } from '../../../@types/diagnosis'; import BasePagination from '../../../components/BasePagination'; -import { enqueueSnackbar } from 'notistack'; - -export default function PlanList() { +export default function List() { + const navigate = useNavigate(); const { themeStretch } = useSettings(); const { corporate_id } = useParams(); const [searchParams, setSearchParams] = useSearchParams(); - const navigate = useNavigate(); + 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 handleGetTemplate = (type :string) => { + axios.get('corporates/import-document-example/' + type) + .then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + }) + } + + const handleImportChange = (event: any) => { + if (event.target.files[0]) { + setCurrentImportFileName(event.target.files[0].name) + } else { + setCurrentImportFileName(null); + } + } + + const handleFormulariumList = async (appliedFilter = null) => { + axios.get(`corporates/${corporate_id}/formulariums/list`).then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + enqueueSnackbar('Download Success', { variant: 'succes' }) + }) + .catch(response => { + enqueueSnackbar('Looks like something went wrong. Please check your data and try again. ' + response.message, { variant: 'error' }) + }) + ; + } + + const handleUpload = () => { + if (importForm.current?.files.length) { + const formData = new FormData(); + formData.append("file", importForm.current?.files[0]) + axios.post(`corporates/${corporate_id}/formulariums/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' }) + } + } + + return ( +
+ + {( !currentImportFileName && + + {/*

kjasndkjandskjasndkjansdkjansd

*/} + + + {/* {navigate(`/master/formularium/create/${formularium_template_id}`)} }>Create */} + Import + {handleGetTemplate('master-formularium-corporate')}}>Download Template + Download Formularium + +
+ )} + + {( 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.atc_code} + {row.name} + {row.category_name} + {row.uom} + {row.active} + + {/* + */} + + {/* COLLAPSIBLE ROW */} + + + + + + Detail + + + + + Description + + + : {row.description ?? '-'} + + + General Indication + + + : {row.general_indication ?? '-'} + + + Composition + + + : {row.composition ?? '-'} + + + + + + + Kategori Obat + + + : {row.kategori_obat ?? '-'} + + + BPOM Registration + + + : {row.bpom_registration ?? '-'} + + + Classifications + + + : {row.classifications ?? '-'} + + + Cat For + + + : {row.cat_for ?? '-'} + + + Class + + + : {row.class ?? '-'} + + + Manufacturer + + + : {row.manufacturer ?? '-'} + + + + + + + + + + ); + } // 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: [], @@ -39,169 +323,12 @@ export default function PlanList() { to: 0, total: 0 }); - - - function SearchInput(props: any) { - // SEARCH - const searchInput = useRef(null); - // const filterForm = useRef(); - const [searchText, setSearchText] = useState(""); - const [searchStatus, setSearchStatus] = useState("active"); - - const handleSearchChange = (event: any) => { - const newSearchText = event.target.value ?? '' - setSearchText(newSearchText); - } - - const handleStatusChange = (event: any) => { - const newSearchStatus = event.target.value ?? '' - console.log('changing to', newSearchStatus) - setSearchStatus(newSearchStatus) - // console.log(searchStatus); - - const searchFilter = { - "search" : searchText, - "status" : newSearchStatus - }; - - props.onSearch(searchFilter); - } - - const handleSubmit = (event?: any) => { - event?.preventDefault(); - const searchFilter = { - "search" : searchText, - "status" : searchStatus - }; - - props.onSearch(searchFilter); // Trigger to Parent - } - - useEffect(() => { - setSearchText(searchParams.get('search') ?? ''); - setSearchStatus(searchParams.get('status') ?? searchStatus ?? 'active'); - }, [searchParams]) - - return ( -
- - - - - - - - - {/* ITS FUCKING MAGIC, SUBMIT BY ENTER WORKING IF THIS BUTTON IS AVAILABLE */} - - -
- ); - } - - // Called on every row to map the data to the columns - function createData( plan: CorporatePlan ): CorporatePlan { - return { - ...plan, - } - } - - const handleInactiveAction = (formularium : any) => { - enqueueSnackbar('Fuck yuo'), { variant: 'error' }; - 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) => { - console.log(error) - enqueueSnackbar(error.data?.message ?? (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 [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('/corporates/'+corporate_id+'/formulariums', { params: filter }); + const response = await axios.get(`corporates/${corporate_id}/formulariums`, { params: filter }); // console.log(response.data); setDataTableLoading(false); @@ -212,9 +339,9 @@ export default function PlanList() { fontWeight: 'bold', }; - const applyFilter = async (searchFilter: any) => { - await loadDataTableData(searchFilter); - setSearchParams(searchFilter); + const applyFilter = async (searchFilter: string) => { + await loadDataTableData({ "search" : searchFilter }); + setSearchParams({ "search" : searchFilter }); } const handlePageChange = (event : ChangeEvent, value: number) => { @@ -223,28 +350,26 @@ export default function PlanList() { setSearchParams(filter); } - useEffect(() => { loadDataTableData(); }, []) return ( - - - - + {/* The Main Table */} - # + Code + ATC Code Name - Total Item - Status + Category Name + UOM + Status {dataTableIsLoading ? @@ -265,13 +390,14 @@ export default function PlanList() { ) : ( {dataTableData.data.map(row => ( - + ))} ) )}
+
diff --git a/frontend/dashboard/src/pages/Master/Diagnosis/List.tsx b/frontend/dashboard/src/pages/Master/Diagnosis/List.tsx index ee729f7f..15a62cf7 100644 --- a/frontend/dashboard/src/pages/Master/Diagnosis/List.tsx +++ b/frontend/dashboard/src/pages/Master/Diagnosis/List.tsx @@ -89,7 +89,7 @@ export default function List() { enqueueSnackbar('Looks like something went wrong. Please check your data and try again. ' + response.message, { variant: 'error' }) }) ; - } + } const handleCancelImportButton = () => { importForm.current.value = ""; diff --git a/frontend/dashboard/src/pages/Master/Formularium/Index.tsx b/frontend/dashboard/src/pages/Master/Formularium/Index.tsx index 22b005cb..ff6c2ba3 100644 --- a/frontend/dashboard/src/pages/Master/Formularium/Index.tsx +++ b/frontend/dashboard/src/pages/Master/Formularium/Index.tsx @@ -21,11 +21,15 @@ export default function Drugs() { links={[ { name: 'Master', - href: '/master', + href: '/master/formularium-template', + }, + { + name: 'Formularium Template', + href: '/master/formularium-template', }, { name: 'Formularium', - href: '/master/formulariums', + href: '/master/formularium-template', }, ]} /> diff --git a/frontend/dashboard/src/pages/Master/Formularium/List.tsx b/frontend/dashboard/src/pages/Master/Formularium/List.tsx index fc93a3e0..1c1fb879 100644 --- a/frontend/dashboard/src/pages/Master/Formularium/List.tsx +++ b/frontend/dashboard/src/pages/Master/Formularium/List.tsx @@ -1,5 +1,5 @@ // @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 { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Grid, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Pagination } from '@mui/material'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; import AddIcon from '@mui/icons-material/Add'; @@ -18,7 +18,7 @@ import BasePagination from '../../../components/BasePagination'; export default function List() { const navigate = useNavigate(); const { themeStretch } = useSettings(); - const { corporate_id } = useParams(); + const { formularium_template_id } = useParams(); const [searchParams, setSearchParams] = useSearchParams(); const [importResult, setImportResult] = useState(null); @@ -78,6 +78,18 @@ export default function List() { importForm.current.dispatchEvent(new Event("change", { bubbles: true })); } + const handleGetTemplate = (type :string) => { + axios.get('corporates/import-document-example/' + type) + .then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + }) + } + const handleImportChange = (event: any) => { if (event.target.files[0]) { setCurrentImportFileName(event.target.files[0].name) @@ -86,11 +98,27 @@ export default function List() { } } + const handleFormulariumList = async (appliedFilter = null) => { + axios.get(`master/formulariums/${formularium_template_id}/list`).then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + enqueueSnackbar('Download Success', { variant: 'succes' }) + }) + .catch(response => { + enqueueSnackbar('Looks like something went wrong. Please check your data and try again. ' + response.message, { variant: 'error' }) + }) + ; + } + const handleUpload = () => { if (importForm.current?.files.length) { const formData = new FormData(); formData.append("file", importForm.current?.files[0]) - axios.post(`master/formularium/import`, formData ) + axios.post(`master/formulariums/${formularium_template_id}/import`, formData ) .then(response => { handleCancelImportButton(); loadDataTableData(); @@ -131,9 +159,10 @@ export default function List() { 'aria-labelledby': 'basic-button', }} > - {navigate('/master/formularium/create')} }>Create + {/* {navigate(`/master/formularium/create/${formularium_template_id}`)} }>Create */} Import - Download Template + {handleGetTemplate('master-formularium')}}>Download Template + Download Formularium )} @@ -188,20 +217,85 @@ export default function List() { {row.code} + {row.atc_code} {row.name} - {row.items_count} + {row.category_name} + {row.uom} - - + {/* + */} {/* COLLAPSIBLE ROW */} - + + - - - Description : {row.description} - + + Detail + + + + + Description + + + : {row.description ?? '-'} + + + General Indication + + + : {row.general_indication ?? '-'} + + + Composition + + + : {row.composition ?? '-'} + + + + + + + Kategori Obat + + + : {row.kategori_obat ?? '-'} + + + BPOM Registration + + + : {row.bpom_registration ?? '-'} + + + Classifications + + + : {row.classifications ?? '-'} + + + Cat For + + + : {row.cat_for ?? '-'} + + + Class + + + : {row.class ?? '-'} + + + Manufacturer + + + : {row.manufacturer ?? '-'} + + + + @@ -233,7 +327,7 @@ export default function List() { 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 }); + const response = await axios.get('/master/formulariums/'+formularium_template_id, { params: filter }); // console.log(response.data); setDataTableLoading(false); @@ -262,7 +356,6 @@ export default function List() { return ( - {/* The Main Table */} @@ -271,10 +364,10 @@ export default function List() { Code + ATC Code Name - Total Item - Status - Action + Category Name + UOM {dataTableIsLoading ? diff --git a/frontend/dashboard/src/pages/Master/Formularium/Master/CreateUpdate.tsx b/frontend/dashboard/src/pages/Master/Formularium/Master/CreateUpdate.tsx new file mode 100644 index 00000000..5aaaf2eb --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Formularium/Master/CreateUpdate.tsx @@ -0,0 +1,71 @@ + +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 CorporatePlanForm from './Form'; +import { CorporatePlan } from '../../../../@types/corporates'; +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 [ currentCorporatePlan, setCurrentCorporatePlan ] = useState(); + + + const navigate = useNavigate(); + + const isEdit = !!id; + + useEffect(() => { + if (isEdit) { + axios.get('/master/formularium-template/'+id+'/edit') + .then((res) => { + setCurrentCorporatePlan(res.data); + }) + .catch((err) => { + if (err.response.status === 404) { + navigate('/404'); + } + }) + } + }, [corporate_id, id]); + + + return ( + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Formularium/Master/Form.tsx b/frontend/dashboard/src/pages/Master/Formularium/Master/Form.tsx new file mode 100644 index 00000000..d98ea1db --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Formularium/Master/Form.tsx @@ -0,0 +1,128 @@ +import * as Yup from 'yup'; +import { LoadingButton } from "@mui/lab"; +import { Card, Grid, Stack, Typography } from "@mui/material"; +import { CorporatePlan } from "../../../../@types/corporates"; +import { FormProvider, RHFSwitch, RHFTextField } from "../../../../components/hook-form"; +import { useEffect, useMemo } from 'react'; +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import { useSnackbar } from 'notistack'; +import { useNavigate, useParams } from 'react-router-dom'; +import axios from '../../../../utils/axios'; + +type Props = { + isEdit: boolean; + currentCorporatePlan?: CorporatePlan; +}; + +export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Props) { + + const { enqueueSnackbar } = useSnackbar(); + const navigate = useNavigate(); + const { corporate_id } = useParams(); + + const NewCorporatePlanSchema = Yup.object().shape({ + name: Yup.string().required('Name is required'), + }); + + const defaultValues = useMemo( + () => ({ + name: currentCorporatePlan?.name || '', + description: currentCorporatePlan?.description || '', + active: currentCorporatePlan?.active === 1 ? true : false, + }), + [currentCorporatePlan] + ); + + useEffect(() => { + if (isEdit && currentCorporatePlan) { + reset(defaultValues); + } + if (!isEdit) { + reset(defaultValues); + } + }, [isEdit, currentCorporatePlan]); + + const methods = useForm({ + resolver: yupResolver(NewCorporatePlanSchema), + defaultValues, + }); + + const { + reset, + watch, + control, + setValue, + getValues, + setError, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + + const onSubmit = async (data: any) => { + if (!isEdit) { + await axios + .post('/master/formularium-template/store', data) + .then((res) => { + enqueueSnackbar('Formularium created successfully', { variant: 'success' }); + }) + .then((res) => { + navigate('/master/formularium-template', { 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('/master/formularium-template/' + currentCorporatePlan?.id + '/update', data) + .then((res) => { + enqueueSnackbar('Formularium updated successfully', { variant: 'success' }); + }) + .then((res) => { + navigate('/master/formularium-template' , { replace: true }); + }) + .catch(({ response }) => { + enqueueSnackbar('Update Failed : '+ response.data.message, { variant: 'error' }); + }); + } + }; + + return ( + + + + + + + Detail + + + + + + + { isEdit? 'Update' : 'Create' } + + + + + + {/* + + + + + */} + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Formularium/Master/History.tsx b/frontend/dashboard/src/pages/Master/Formularium/Master/History.tsx new file mode 100644 index 00000000..143c68de --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Formularium/Master/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 = 'Formularium 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\\FormulariumTemplate'; + const url = `/audittrail/${id}?model=${model}`; + axios.get(url) + .then((res) => { + setCurrentCorporate(res.data); + }) + .catch((error) => { + console.error('Terjadi kesalahan:', error); + }); + + }, [configuredCorporateContext]); + + return ( +
+ + {currentCorporate?.data.map((item, index) => ( + + + {`Data has ${item.action} by ${item.user_id} on ${fDateTime(item.updated_at)}`} + + + + + Field + Old Value + New Values + + + + {Object.entries(item.old_values).map(([key, value]) => { + let renderedValue; + if (key === 'deleted_by' || + key === 'deleted_at' || + key === 'created_by' || + key === 'created_at' || + key === 'updated_by' || + key === 'description' + ) { + return null; // Melewati iterasi saat key adalah 'deleted_by' + } + switch (key) { + case 'welcome_message': + renderedValue = item.new_values[key].replace(/<[^>]*>/g, ''); + value = value.replace(/<[^>]*>/g, ''); + break; + case 'help_text': + renderedValue = item.new_values[key].replace(/<[^>]*>/g, ''); + value = value.replace(/<[^>]*>/g, ''); + break; + case 'active': + renderedValue = item.new_values[key] == 1 ? 'Active' : 'Inactive'; + value = value == 1 ? 'Active' : 'Inactive'; + break; + case 'created_at': + renderedValue = fDateTime(item.new_values[key]); + value = fDateTime(value); + break; + case 'updated_at': + renderedValue = fDateTime(item.new_values[key]); + value = fDateTime(value); + break; + case '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/dashboard/src/pages/Master/Formularium/Master/Index.tsx b/frontend/dashboard/src/pages/Master/Formularium/Master/Index.tsx new file mode 100644 index 00000000..e31faf0d --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Formularium/Master/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 Divisions() { + const { themeStretch } = useSettings(); + + const { corporate_id } = useParams(); + + const pageTitle = 'Formularium Template'; + return ( + + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Formularium/Master/List.tsx b/frontend/dashboard/src/pages/Master/Formularium/Master/List.tsx new file mode 100644 index 00000000..971d0d8a --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Formularium/Master/List.tsx @@ -0,0 +1,334 @@ +// @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'; +import HistoryIcon from '@mui/icons-material/History'; +// hooks +import { Link, NavLink as RouterLink } 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 { Icd } from '../../../../@types/diagnosis'; +import BasePagination from '../../../../components/BasePagination'; +import { enqueueSnackbar } from 'notistack'; + +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 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( 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); + + const handleActivate = (model: any, status: string) => { + axios + .put(`/master/formularium-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: 'unset' } }}> + {row.name} + {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/formularium-template', { 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 */} + + + + + Name + Description + 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 510193e7..46914438 100644 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -272,14 +272,31 @@ export default function Router() { }, { - path: 'master/formularium', + path: 'master/formularium/:formularium_template_id', element: , }, { - path: 'master/formularium/create', + path: 'master/formularium/create/:formularium_template_id', element: , }, + { + path: 'master/formularium-template', + element: , + }, + { + path: 'master/formularium-template/create', + element: , + }, + { + path: 'master/formularium-template/:id/formularium-template-history', + element: , + }, + { + path: 'master/formularium-template/:id/edit', + element: , + }, + { path: 'report/appointments', element: , @@ -472,6 +489,10 @@ 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 MasterFormulariumTemplate = Loadable(lazy(() => import('../pages/Master/Formularium/Master/Index'))); +const MasterFormulariumTemplateCreate = Loadable(lazy(() => import('../pages/Master/Formularium/Master/CreateUpdate'))); +const MasterFormulariumTemplateHistories = Loadable(lazy(() => import('../pages/Master/Formularium/Master/History'))); + const CorporateServices = Loadable(lazy(() => import('../pages/Corporates/Services/Index'))); const CorporateServicesCreate = Loadable(lazy(() => import('../pages/Corporates/Services/Create'))); const CorporateServicesHistory = Loadable(lazy(() => import('../pages/Corporates/Services/sections/History'))); diff --git a/public/files/Template - Formularium - Corporate.xlsx b/public/files/Template - Formularium - Corporate.xlsx new file mode 100644 index 00000000..892bb0a1 Binary files /dev/null and b/public/files/Template - Formularium - Corporate.xlsx differ diff --git a/public/files/Template - Formularium.xlsx b/public/files/Template - Formularium.xlsx new file mode 100644 index 00000000..e672809e Binary files /dev/null and b/public/files/Template - Formularium.xlsx differ diff --git a/public/files/TemplateFormulariumList.xlsx b/public/files/TemplateFormulariumList.xlsx new file mode 100644 index 00000000..348dafe3 Binary files /dev/null and b/public/files/TemplateFormulariumList.xlsx differ diff --git a/public/files/TemplateICDList.xlsx b/public/files/TemplateICDList.xlsx index fe796c35..c94ee48a 100644 Binary files a/public/files/TemplateICDList.xlsx and b/public/files/TemplateICDList.xlsx differ