From 7ee4a3ef380356d9976666249d1910a84496fd6f Mon Sep 17 00:00:00 2001 From: Tb Fajri Date: Tue, 19 Sep 2023 14:54:19 +0700 Subject: [PATCH] Update fitur impor dan history exlusion --- .../Controllers/Api/AuditTrailController.php | 2 +- .../Controllers/Api/CorporateController.php | 8 +- .../Controllers/Api/DiagnosisController.php | 209 ++++++++- Modules/Internal/Routes/api.php | 3 + Modules/Internal/Services/IcdService.php | 55 +++ app/Models/Icd.php | 12 +- app/Providers/AppServiceProvider.php | 10 + .../2023_09_18_154540_add_active_to_icd.php | 32 ++ .../client-portal/src/@types/diagnosis.ts | 1 + .../Corporates/Division/CreateUpdate.tsx | 21 +- .../src/pages/Corporates/Division/Index.tsx | 2 +- .../dashboard/src/pages/Corporates/Index.tsx | 4 +- .../src/pages/Master/Diagnosis/History.tsx | 215 ++++++++++ .../src/pages/Master/Diagnosis/Index.tsx | 2 +- .../pages/Master/Diagnosis/List-master.tsx | 397 ++++++++++++++++++ .../src/pages/Master/Diagnosis/List.tsx | 106 ++++- .../Master/Diagnosis/Master/CreateUpdate.tsx | 75 ++++ .../pages/Master/Diagnosis/Master/Form.tsx | 129 ++++++ .../pages/Master/Diagnosis/Master/Index.tsx | 38 ++ .../pages/Master/Diagnosis/Master/List.tsx | 397 ++++++++++++++++++ frontend/dashboard/src/routes/index.tsx | 13 + public/files/CorporateMembershipList.xlsx | Bin 6011 -> 7092 bytes public/files/Template - ICD.xlsx | Bin 0 -> 9981 bytes 23 files changed, 1708 insertions(+), 23 deletions(-) create mode 100644 Modules/Internal/Services/IcdService.php create mode 100644 database/migrations/2023_09_18_154540_add_active_to_icd.php create mode 100644 frontend/dashboard/src/pages/Master/Diagnosis/History.tsx create mode 100644 frontend/dashboard/src/pages/Master/Diagnosis/List-master.tsx create mode 100644 frontend/dashboard/src/pages/Master/Diagnosis/Master/CreateUpdate.tsx create mode 100644 frontend/dashboard/src/pages/Master/Diagnosis/Master/Form.tsx create mode 100644 frontend/dashboard/src/pages/Master/Diagnosis/Master/Index.tsx create mode 100644 frontend/dashboard/src/pages/Master/Diagnosis/Master/List.tsx create mode 100644 public/files/Template - ICD.xlsx diff --git a/Modules/Internal/Http/Controllers/Api/AuditTrailController.php b/Modules/Internal/Http/Controllers/Api/AuditTrailController.php index dd5ca4e4..dd73e6b1 100644 --- a/Modules/Internal/Http/Controllers/Api/AuditTrailController.php +++ b/Modules/Internal/Http/Controllers/Api/AuditTrailController.php @@ -19,7 +19,7 @@ class AuditTrailController extends Controller { $audittrails = AuditTrail::query() ->where('model', '=', $request->model) ->where('model_id', '=', $id) - // ->latest() + ->latest() ->paginate(1000); return response()->json(Helper::paginateResources(AuditTrailResource::collection($audittrails))); } diff --git a/Modules/Internal/Http/Controllers/Api/CorporateController.php b/Modules/Internal/Http/Controllers/Api/CorporateController.php index 9b8bf00d..0ea5cb51 100644 --- a/Modules/Internal/Http/Controllers/Api/CorporateController.php +++ b/Modules/Internal/Http/Controllers/Api/CorporateController.php @@ -524,7 +524,13 @@ class CorporateController extends Controller 'file_name' => "Corporate Exclusion Import.xlsx", "file_url" => url('files/Corporate Exclusion Import.xlsx') ]); - break; + break; + case 'master-icd': + return Helper::responseJson([ + 'file_name' => "Template - ICD.xlsx", + "file_url" => url('files/Template - ICD.xlsx') + ]); + break; default: return Helper::responseJson([], 'error', 404); break; diff --git a/Modules/Internal/Http/Controllers/Api/DiagnosisController.php b/Modules/Internal/Http/Controllers/Api/DiagnosisController.php index f42a6b49..8ad0cec7 100644 --- a/Modules/Internal/Http/Controllers/Api/DiagnosisController.php +++ b/Modules/Internal/Http/Controllers/Api/DiagnosisController.php @@ -3,20 +3,40 @@ namespace Modules\Internal\Http\Controllers\Api; use App\Models\Icd; +use App\Services\ImportService; +use App\Helpers\Helper; use Illuminate\Contracts\Support\Renderable; + +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; + use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Storage; +use Modules\Internal\Services\IcdService; class DiagnosisController extends Controller { + public function __construct(IcdService $icdService) + { + $this->icdService = $icdService; + } /** * Display a listing of the resource. * @return Renderable */ public function index(Request $request) { - $diagnosis = Icd::withTrashed()->filter($request->toArray())->paginate(); - + + $diagnosis = Icd::query() + ->filter($request->toArray()) + // ->where('deleted_at', '=', NULL) + ->orderBy('code', 'ASC') + ->paginate(15); return $diagnosis; } @@ -87,4 +107,189 @@ class DiagnosisController extends Controller ->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', + ]; + + 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, $diagnosis_id) + { + $request->validate([ + 'active' => 'required' + ]); + + $Icd = Icd::findOrFail($diagnosis_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 d55c5f42..84325536 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -125,6 +125,9 @@ Route::prefix('internal')->group(function () { Route::get('master/diagnosis', [DiagnosisController::class, 'index']); Route::get('master/diagnosis/search', [DiagnosisController::class, 'search']); + Route::post('master/diagnosis/import', [DiagnosisController::class, 'import']); + Route::get('master/diagnosis/list', [DiagnosisController::class, 'generateIcdList']); + Route::put('master/diagnosis/{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']); diff --git a/Modules/Internal/Services/IcdService.php b/Modules/Internal/Services/IcdService.php new file mode 100644 index 00000000..c3a22c20 --- /dev/null +++ b/Modules/Internal/Services/IcdService.php @@ -0,0 +1,55 @@ +validateDiagnosisExclusionRow($row); + $icd = Icd::updateOrCreate([ + 'code' => $row['code'] + ], [ + "code" => $row['code'], + "parent_code" => $row['parent_code'] ?? null, + "rev" => $row['reff_exc'], + "name" => $row['description_en'], + "version" => $row["version"] ?? null, + "active" => $row["active"] ?? 0 + ]); + + return true; + } catch (\Exception $e) { + throw $e; + } + } + + public $listing_doc_headers = [ + "Rev", + "Version", + "Code", + "Parent Code", + "Name", + "Description", + "Status", + "Type" + ]; +} diff --git a/app/Models/Icd.php b/app/Models/Icd.php index 9dfa57ff..69497c4a 100644 --- a/app/Models/Icd.php +++ b/app/Models/Icd.php @@ -6,6 +6,7 @@ use App\Traits\Blameable; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Altek\Accountant\Contracts\Recordable; class Icd extends Model { @@ -20,11 +21,12 @@ class Icd extends Model 'name', 'description', 'parent_code', + 'active' ]; public $appends = [ 'type', - 'active' + // 'active' ]; protected $hidden = [ @@ -41,10 +43,10 @@ class Icd extends Model return 'ICD-'.$this->rev; } - public function getActiveAttribute() - { - return empty($this->deleted_at); - } + // public function getActiveAttribute() + // { + // return empty($this->deleted_at); + // } public function subCategories() { diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index d56e97c2..c0da828f 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -11,6 +11,7 @@ use App\Models\CorporateService; use App\Models\CorporatePlan; use App\Models\CorporateBenefit; use App\Models\Member; +use App\Models\Icd; use App\Models\AuditTrail; use Illuminate\Support\Facades\Auth; use Str; @@ -94,6 +95,15 @@ class AppServiceProvider extends ServiceProvider $this->logAuditTrail($model, 'deleted'); }); + // ICD or exlusion + Icd::updated(function ($model) { + $this->logAuditTrail($model, 'updated'); + }); + + Icd::deleted(function ($model) { + $this->logAuditTrail($model, 'deleted'); + }); + } diff --git a/database/migrations/2023_09_18_154540_add_active_to_icd.php b/database/migrations/2023_09_18_154540_add_active_to_icd.php new file mode 100644 index 00000000..22257349 --- /dev/null +++ b/database/migrations/2023_09_18_154540_add_active_to_icd.php @@ -0,0 +1,32 @@ +integer('active'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('icd', function (Blueprint $table) { + $table->dropColumn('active'); + }); + } +}; diff --git a/frontend/client-portal/src/@types/diagnosis.ts b/frontend/client-portal/src/@types/diagnosis.ts index a33734b5..f6d674ce 100644 --- a/frontend/client-portal/src/@types/diagnosis.ts +++ b/frontend/client-portal/src/@types/diagnosis.ts @@ -2,6 +2,7 @@ export type Icd = { id: number; type: string; rev: string; + active: number; version?: string; code: string; name: string; diff --git a/frontend/dashboard/src/pages/Corporates/Division/CreateUpdate.tsx b/frontend/dashboard/src/pages/Corporates/Division/CreateUpdate.tsx index d4ad3ea9..31ed9c23 100644 --- a/frontend/dashboard/src/pages/Corporates/Division/CreateUpdate.tsx +++ b/frontend/dashboard/src/pages/Corporates/Division/CreateUpdate.tsx @@ -3,18 +3,29 @@ import { useNavigate, useParams } from "react-router-dom"; import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs"; import Page from "../../../components/Page"; import useSettings from "../../../hooks/useSettings"; -import { useEffect, useMemo, useState } from 'react'; +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; @@ -44,16 +55,16 @@ export default function PlanCreate() { href: '/corporates', }, { - name: 'Corporate Name', - href: '/corporates/'+corporate_id, + name: corporate?.name ?? '-', + href: '/corporate/'+corporate_id, }, { name: 'Division', - href: '/corporates/'+corporate_id+'/divisions', + href: '/corporate/'+corporate_id+'/divisions', }, { name: !isEdit ? 'Create' : 'Edit', - href: '/corporates/'+corporate_id+'/divisions/'+id, + href: '/corporate/'+corporate_id+'/divisions/'+id, }, ]} /> diff --git a/frontend/dashboard/src/pages/Corporates/Division/Index.tsx b/frontend/dashboard/src/pages/Corporates/Division/Index.tsx index 84aa6e6e..43361b05 100644 --- a/frontend/dashboard/src/pages/Corporates/Division/Index.tsx +++ b/frontend/dashboard/src/pages/Corporates/Division/Index.tsx @@ -32,7 +32,7 @@ export default function Divisions() { links={[ { name: 'Corporates', - href: '/corporates', + href: '/corporate', }, { name: corporate?.name ?? '-', diff --git a/frontend/dashboard/src/pages/Corporates/Index.tsx b/frontend/dashboard/src/pages/Corporates/Index.tsx index 580600c5..5253961b 100644 --- a/frontend/dashboard/src/pages/Corporates/Index.tsx +++ b/frontend/dashboard/src/pages/Corporates/Index.tsx @@ -386,8 +386,8 @@ export default function Corporates() { - : {fDate(row.current_policy?.start)}- - {fDate(row.current_policy?.end)} + {/* : {fDate(row.current_policy?.start)}- */} + {/* {fDate(row.current_policy?.end)} */} diff --git a/frontend/dashboard/src/pages/Master/Diagnosis/History.tsx b/frontend/dashboard/src/pages/Master/Diagnosis/History.tsx new file mode 100644 index 00000000..1e6b7361 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Diagnosis/History.tsx @@ -0,0 +1,215 @@ +// @mui +import { + Box, + Button, + Card, + Collapse, + Container, + FormControl, + Grid, + IconButton, + InputLabel, + MenuItem, + OutlinedInput, + Paper, + Select, + SelectChangeEvent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Typography, + Badge, + Stack, +} from '@mui/material'; +import * as React from 'react'; +import { useParams } from 'react-router-dom'; +import { styled } from '@mui/material/styles'; +import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp'; +import MuiAccordion, { AccordionProps } from '@mui/material/Accordion'; +import { useContext, useEffect, useState } from 'react'; +import MuiAccordionSummary, { + AccordionSummaryProps, +} from '@mui/material/AccordionSummary'; +import useSettings from '../../../hooks/useSettings'; +import axios from '../../../utils/axios'; +import { ConfiguredCorporateContext } from '@/contexts/ConfiguredCorporateContext'; +import MuiAccordionDetails from '@mui/material/AccordionDetails'; +import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs'; +import { Corporate } from '@/@types/corporates'; +import { fDate, fDateTime } from '@/utils/formatTime'; + +const Accordion = styled((props: AccordionProps) => ( + +))(({ theme }) => ({ + border: `1px solid ${theme.palette.divider}`, + '&:not(:last-child)': { + borderBottom: 0, + }, + '&:before': { + display: 'none', + }, +})); + +const AccordionSummary = styled((props: AccordionSummaryProps) => ( + } + {...props} + /> +))(({ theme }) => ({ + backgroundColor: + theme.palette.mode === 'dark' + ? 'rgba(255, 255, 255, .05)' + : 'rgba(0, 0, 0, .03)', + flexDirection: 'row-reverse', + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(90deg)', + }, + '& .MuiAccordionSummary-content': { + marginLeft: theme.spacing(1), + }, +})); + +const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({ + padding: theme.spacing(2), + borderTop: '1px solid rgba(0, 0, 0, .125)', +})); + +export default function CustomizedAccordions() { + const [expanded, setExpanded] = React.useState('panel1'); + + const handleChange = + (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => { + setExpanded(newExpanded ? panel : false); + }; + const pageTitle = 'Diagnosis Audittrail'; + + 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\\Icd'; + 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); + + return ( + + {`${field}`} + {`${value}`} + {renderedValue} + + ); + })} + + + + + ))} +
+ ); +} diff --git a/frontend/dashboard/src/pages/Master/Diagnosis/Index.tsx b/frontend/dashboard/src/pages/Master/Diagnosis/Index.tsx index 4da33ed2..7f4f7381 100644 --- a/frontend/dashboard/src/pages/Master/Diagnosis/Index.tsx +++ b/frontend/dashboard/src/pages/Master/Diagnosis/Index.tsx @@ -21,7 +21,7 @@ export default function Divisions() { links={[ { name: 'Master', - href: '/master', + href: '/master/diagnosis', }, { name: 'Diagnosis', diff --git a/frontend/dashboard/src/pages/Master/Diagnosis/List-master.tsx b/frontend/dashboard/src/pages/Master/Diagnosis/List-master.tsx new file mode 100644 index 00000000..70127b94 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Diagnosis/List-master.tsx @@ -0,0 +1,397 @@ +// @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

*/} + + + Create Template + {handleGetTemplate('master-icd')}}>Download Template + Download ICD + +
+ )} + + {( 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); + + const handleActivate = (model: any, status: string) => { + axios + .put(`/master/diagnosis/${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' } }}> + + setOpen(!open)} + > + {open ? : } + + + {row.type} + {row.code} + {row.name} + {row.version} + + + {/* */} + + {row.active == 1 && ( + + )} + {row.active != 1 && ( + + )} + + + {/* */} + + + + + + {/* 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/diagnosis', { 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/Diagnosis/List.tsx b/frontend/dashboard/src/pages/Master/Diagnosis/List.tsx index ef1aad24..6719e31f 100644 --- a/frontend/dashboard/src/pages/Master/Diagnosis/List.tsx +++ b/frontend/dashboard/src/pages/Master/Diagnosis/List.tsx @@ -5,7 +5,9 @@ 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'; @@ -73,6 +75,22 @@ export default function List() { } } + 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(); + 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 handleCancelImportButton = () => { importForm.current.value = ""; importForm.current.dispatchEvent(new Event("change", { bubbles: true })); @@ -90,12 +108,13 @@ export default function List() { 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 ) + 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'); + enqueueSnackbar('Succesfully import data '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows', { variant: 'success' }) }) .catch(response => { enqueueSnackbar('Looks like something went wrong. Please check your data and try again. ' + response.message, { variant: 'error' }) @@ -104,6 +123,19 @@ export default function List() { 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 (
@@ -132,7 +164,8 @@ export default function List() { }} > Import - Download Template + {handleGetTemplate('master-icd')}}>Download Template + Download ICD )} @@ -174,6 +207,37 @@ export default function List() { const { row } = props; const [open, setOpen] = React.useState(false); + const handleActivate = (model: any, status: string) => { + axios + .put(`/master/diagnosis/${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; + }), + }); + enqueueSnackbar( + 'Updated Successfully!', + { variant: 'success' } + ); + }) + .catch((error) => { + // console.log('asdasd', error.response.data.message) + enqueueSnackbar( + error.response.data.message ?? error.message ?? 'Failed Processing Request', + { variant: 'error' } + ); + }); + }; + return ( *': { borderBottom: 'unset' } }}> @@ -190,9 +254,41 @@ export default function List() { {row.code} {row.name} {row.version} + - - + {/* */} + + {row.active == 1 && ( + + )} + {row.active != 1 && ( + + )} + + + {/* */} + + + + {/* COLLAPSIBLE ROW */} @@ -234,7 +330,7 @@ export default function List() { setDataTableLoading(true); const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); const response = await axios.get('/master/diagnosis', { params: filter }); - // console.log(response.data); + console.log(response.data); setDataTableLoading(false); setDataTableData(response.data); diff --git a/frontend/dashboard/src/pages/Master/Diagnosis/Master/CreateUpdate.tsx b/frontend/dashboard/src/pages/Master/Diagnosis/Master/CreateUpdate.tsx new file mode 100644 index 00000000..7e5d53b2 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Diagnosis/Master/CreateUpdate.tsx @@ -0,0 +1,75 @@ + +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('/corporates/'+corporate_id+'/divisions/'+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/Diagnosis/Master/Form.tsx b/frontend/dashboard/src/pages/Master/Diagnosis/Master/Form.tsx new file mode 100644 index 00000000..99663c77 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Diagnosis/Master/Form.tsx @@ -0,0 +1,129 @@ +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'), + code: Yup.string().required('Corporate Code is required'), + }); + + const defaultValues = useMemo( + () => ({ + name: currentCorporatePlan?.name || '', + code: currentCorporatePlan?.code || '', + 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('/corporates/' + corporate_id + '/divisions', data) + .then((res) => { + enqueueSnackbar('Division created successfully', { variant: 'success' }); + }) + .then((res) => { + navigate('/corporate/' + corporate_id + '/divisions', { 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('/corporates/' + corporate_id + '/divisions/' + currentCorporatePlan?.id , data) + .then((res) => { + enqueueSnackbar('Division updated successfully', { variant: 'success' }); + }) + .then((res) => { + navigate('/corporate/' + corporate_id + '/divisions/' , { replace: true }); + }) + .catch(({ response }) => { + enqueueSnackbar('Update Failed : '+ response.data.message, { variant: 'error' }); + }); + } + }; + + return ( + + + + + + + Division Detail + + + + + + + { isEdit? 'Update' : 'Create' } + + + + + + + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Diagnosis/Master/Index.tsx b/frontend/dashboard/src/pages/Master/Diagnosis/Master/Index.tsx new file mode 100644 index 00000000..ab1cecb2 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Diagnosis/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 = 'Diagnosis'; + return ( + + + + + + + + + ); +} diff --git a/frontend/dashboard/src/pages/Master/Diagnosis/Master/List.tsx b/frontend/dashboard/src/pages/Master/Diagnosis/Master/List.tsx new file mode 100644 index 00000000..b40379e1 --- /dev/null +++ b/frontend/dashboard/src/pages/Master/Diagnosis/Master/List.tsx @@ -0,0 +1,397 @@ +// @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

*/} + + + Create Template + {handleGetTemplate('master-icd')}}>Download Template + Download ICD + +
+ )} + + {( 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); + + const handleActivate = (model: any, status: string) => { + axios + .put(`/master/diagnosis/${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' } }}> + + setOpen(!open)} + > + {open ? : } + + + {row.type} + {row.code} + {row.name} + {row.version} + + + {/* */} + + {row.active == 1 && ( + + )} + {row.active != 1 && ( + + )} + + + {/* */} + + + + + + {/* 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/diagnosis', { 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/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index 455291c5..29f81fe6 100644 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -236,10 +236,18 @@ export default function Router() { path: 'master/hospitals/:id/edit', element: , }, + { + path: 'master/diagnosis-template', + element: , + }, { path: 'master/diagnosis', element: , }, + { + path: 'master/diagnosis/:id/diagnosis-history', + element: , + }, { path: 'master/drugs', @@ -412,9 +420,14 @@ 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 MasterDiagnosisTemplate = Loadable(lazy(() => import('../pages/Master/Diagnosis/Master/Index'))); +const MasterDiagnosisHistories = Loadable( + lazy(() => import('../pages/Master/Diagnosis/History')) +); const MasterDoctors = Loadable(lazy(() => import('../pages/Master/Doctors/Index'))); const MasterDoctorsCreate = Loadable(lazy(() => import('../pages/Master/Doctors/Create'))); const MasterHospitals = Loadable(lazy(() => import('../pages/Master/Hospitals/Index'))); diff --git a/public/files/CorporateMembershipList.xlsx b/public/files/CorporateMembershipList.xlsx index 4094bd780ab06b2014052a8f09513bf400d01222..062ac1cc41b2cfe73c201ce529d3e38ba4e266b5 100644 GIT binary patch delta 4867 zcmZWtcU)7;whj;}5|J7bkP02_RrldXqpxGl+nQ6qVj2 z(h~^nP()CQ5RkP zc>$*D=>sQAGb&!B#08{nh%4yHcnjLzBl@hAyINhuO4V^`Hmo2WH)#*8954y=iN`tmXCg zs}gO$Ov542djf~;L}qAjQRBhF$E%^$3U7BdyZXbeYYzgNO2$G)e$_Uk#;3;&n}ZL( zKy&%3ACr2%UiCgytKBlNPxWkl{k(aqudn1=!%+>XLM8im4f|W zzfQoN2Xb#;`;b2RIi#|WqC3WugO{4~viab*NR^-av9$UlWYeA-JC$Fve`)nZGcj!B zk;e1&MbC2t^)z^wqihcPF_%MNlR&{&@~KDYprbpFPJeL-3P6f*Q6}2>oM7QE`#$d* ziLL8pcT?;_*45PsK`W(RzA+HY|LN%)?wn{M`>$fnpTQA zy`?D67Ze|y8=ulHHAKA@J-Twp+mBjr=q#GJY5DhoK4c?&t{6S`l~X%M^h1sA*Tw#h z@X5gzd7B^y(951CteX0j`3av;Fq+bT2bs+ko9Xu{vX|=HL#cY*fgD!Yac;adw**(o zmC7R$4)n9JN6VNC8inG6Elt%9qr!osF3-0dugiqRy_)zc8Nr_`zck@pS)90%QTWBp z_KnHVlVUm3q0=W9pB7g&NTl~a5w>|-JC$rqt$Fr7yxb5`akh^fz5MF3$W|}D{LHxD zmZVf~dN5Jpmo%DLnEN_jyPY;tWO_cXYVeUOmSB8o`BB}$JKu>HI%?{f_TBFsFuPvs zR%dFrsi(@Oa81h2Cg-rPd%-J3HQvf^N2-K!*m|n!9YyfS!j3B6j7u&Ko1bV%`9SO| zk$&>VNGOkB{9|& z$j=l!8vXL=wqxLCU{t!{C+4q(dj|)-dxXe4>l@`kd^y_#ZTU-geV=CTPo)N3ThFf% zGHvpx-l1urD32TS#rbF)tP`DQZdzWOZq*g+5OiAemyeY~PZ2qy#p-W6$@MA}T(iAw z^}^nEusWEFQf4A`;jWdZ`L7Zsnz?<@gLmiLZ*b*4n1EvyR)bz0#kGjAx~re_JZL$mYG2#a4)%@0DF`6Ses(u1>2qdvUSM_NQ9rzTIZbM@^`Qfs;y? zo1Og+CJuSj^RD8Wh%U{K5CKgeizlK_yU=4M4n?wFYYJ&se4p-qMg=!64NR>D2uxN+ zqc|Hpa!~X}k1Uk(t)rP9 zAW}0Y8HcByvG6l;1n35jcQ+POHU%yJC50ak1D5VHZ~|M#3!rPA;IY?0BR`oZ$c287iFu#%V5;-}6tLCzz6Y|clF~YEj$JPLSxG4!H>p;C zwAtz_pcuKqlQYeajGOEJUbV5P-ae@uG*xLRK9WzMT*;%b51Qk?6sg~~ft(lX zd87H03ddjc?3yxoLyFaGr6i<%<8!8PIT-+_$9n3$W(IP)uym z`nmMc@?0Fd^f&)&w`AYM=cTP+tQdH`3vApx5ky<=XEIU3M`1?{xq0pr-KB+%`^eAP zVfp-mfcyYL5hW1NzB*aZZ>V$O1UJt;B8WeYO|KmW@Sp7|U*j_kkt*%f$`IJILO7^Id8uH&UG0``=l5;5(usz(M#Jo4 z1fJcxsOb$6^nu*J4N3U!)+wM?#jP48rV5l(y-4^}L+#g}7+Rg1vO%yLkihzJqWYYw zF(6OS%ni_?Nyv9VT&cuyi$(ICam7xVIO4RP_1#2ucO!6Hv{gs62A}|~)$XXA5h6?a zl5V+{TOc6!utr3WDuReRg;7CsqtS97U-CTvKoqY$7c^!vNz>q(<|TqCqElp-$j-Eco!a0`QAM;jRL+kX4L9 zy@EkK;*g&8#orq#9SOjl6NMQ>gV~e7D?Rq+A8BquN`AKl01wIFk27_E2S)&6QIPgz z$YD8Ept@?|MO7w-#Ui3#Afitsfc33il2|l6hox`O`15FT*`zSo0e+)VkUDkfIR9w} z)hI7j%s2OLX*$0z-AX4J)|w2nd+?{I%Mig8$o(IzqI$53`Y?~X01sljlav-h`oMWN zUYF$nhm5seqYv_GzS2|mBl1+H_rg7sw?|LbsI&o3@Jn77nLd&@iVaP0HKBTKtYZ#6Y zvBR`oH_XXmu{Em?h2e-h%z{2qT&EcW-yU^GneUqm>!ee!cuUU9n#U_Wej=cfy)61s zw_XJb0>_FgDA{U6p8mU-Gf`dDW~}2XE?^5}uO)=7P;U$`LhWgV9>j7BPHJ#8oO&Pl z0{p#PNv;uG5a;UbU&kj}+-X>gCZt`BPkc%{l!WhYmdM3b+ zRx+KS{@7;hSvoELaR%yw6V95MAVd>cDIBR@c8cP$vLyEnRWuBU;p$ z#kJ!Uq6?xHq-Q#l*9d8<*W|5n#kZM&1s0cM2l*<-Crg|nZBQeGJge))Z+T29w;>{! z30O1ejgmHVhmnzF;%%lNRlCpO{pDjPbJcY;tPwMOsvnW~825*Frs=8i8 zR>;Q<5n)Wg&hL;pbUnGnOg=%&N#SzYTNxSP*dA6MmNjkXOBrOZ+sJInLmdNQVdR;6 zUPP;7O8NdedTu&;LkKqhigrL2qt}TVo>5hDQqQix_nWK~(LX zisqOJy;gD$M9>PjW%$%P{@)QYZ~z2lcg+r8{~IhvEw zct$hiD~sPFHctRj6TmEcqzW`!1>(X$IWbT=2Kpc!J2#Ao!9XQv2v4!zsaQxl7DB|5 z&5W;SrzI&WdXN-5;uJehIK~{Q1I^ZfxV)j9-cY(X^g%6lt_Kn04V9ct$9h*{A+=aY z1vVIj&RE;tv(2bvWVv-_xsg;mI6Y!;9x)tr5UYF8L0w{M0d7!zItU)@26aq=8YM$H zlc0)1N(uyVs1$4z z4&@Ak$_g#VTLs`W1-z@UkV@=0rnjOk!)-ESCPJ};)w2{kD(jM*?CC_s4jbz8q@jmk zBXcOH84IU$%I1D57VBMwfmC9~F^mdATbkQs+8UcFog*WuO93i%lhJL5m8`<-^Ov7o z*XP$s5kp9-V8~_-rDi-v_UR!c*sOp)odSaTsbDmGySF1?vuyfwkC>#CGnlyo#8D_B z_gewNQZAxcVwMeM!~i1mE-LwQESlq!jF%yPRUXu~2Wt%yvmE#&u4PCH;hn11+XtyP zdo(az!Legx`FAfpWm8_MO8R?HBX9Ol_q5Jnr!n1f*LSwQyq+;lrnaT5HAu~J+)d)z zhotPVz$vLq{Tg#!!StnmvNt^Hf5YA$rN|F`xufP6fYD%f2X}~yg+#(;ar$&F5cDVn zA*qH5dk#B3RzL{pCay`A8`@OmyPx=1T(VgO*qlKdEeIh=%nG3Bp$%ya@i&l^GF7cS zdazFNT9(+X$0(#yeBq7$jU=fVAG3>R-_UQ*Sf8SE*e9he6yq+XRi3e!#?a&zS~qV+ z&0e!|*Lc|(>=2(FVhAD*=ramss`6lIyST?VsIzxt`Og4Sw$(+yMEdO2bKDSK1SPu|s=JiT( zyRfG}Eh_sl8{fu}_zj|X3$@l@GfM(6cyt2Ti8*h1t#k!{0{a5 zZ3uUF_sR~~{~rIiK zaC5Vqp;P&O5|vi$4Za^->?d9+HZfM_4`wsBf`!W>Z_H|5hNGNi9tsLaf}IruZ3?#f z&xhz#t~-rUS5A|^aPz$=E#{AjA80RI0bN#Zqv;k(2o)Kiz8>iqkC%{J9oG56?~5vH zIQrD5Vurv>L`JCTtyZ%CeRK7k7LSmc_)zh4+Vj&N>Lh@*t%LX3S0sw$X@uu#zq~$v zN|x7nqIr4Vw_P7W;(}i*{km84Hk}$BWHuH`fO#hqI^>PCg}@E5=lQ^! z6I0}{xfDyvnNmdBJdeYP+l~3>o{&>%5)LYQhr?t~HC`&KD1EVC9$J<3y~2q%G-OxE zE^naMOHLW>`V;VvDw14;dwBm##Hkhs0@;qRv1@buTh}3-5z+di>)=Js|Cy^r)TRF^ z>i_@{z<*ldJw71G0DR()vkzFD-A$O30G9iM`6{xvn80$aXAR;1KmdjJ{6$E^U>U;y zeMo6G0KkYH01)_xlL8n3!1#Gs1^D^jQugo*z$yj%cw3qNW54L@7PfrPSg=p5!BU<6 z-3sr8ivAbe&ENn3quJC*t0LmER;+wn1F+t=l$Cy4E7L=qziD__mo{rtuF&64{{whp BGEtcV2&01?$X93 z`)?&>kDzTQliyno{7mxOJ#~2aeWj-JcL6Tv_lJvtV?TGd4@GF#IY#~XT%TKK#<0=( zVKAB;{>5l_Vdu{UTzAPyRf0~`+V$!g@78)Wy|pE$p>DWp&%Nh)PPJ3vUzP3O<_)i| ze+#q(^>;5`OkG#(5-Tz^ad-<3xoCAW*592nbo0rnzU3HDMcV7AEY_H=!X@ww`R8;-HebWkBfM`YeAUJM+$kp_Bf!#jh`LE=!VA`4Zm#WdQNfY;j?IQR>jk}+lZ{@{7{*Z}Nkyip(STu{ z+@8m?RZFWAK@ZFNK76`4h6dBpD`^iMY2LVP)4ayS@9OzZQi&_&5tlY?SIUec&`(=& zQ^e)gpLU!@tp$RJt_KNVBZ3@y*}&i_AG#oO+xt?Y9VPT6=FwV)ea|fM#_DASdT?$} zUhaz`vO-q4Oon%c&MfVbmhb8X%`Lveala%*veuHhY<_L-wDbOQ)-USv(GFZGfAl#; zY2;}Z?@8K*{}sax$+?SQaaHxLkJo4BNBt*s-x8-BM|GE~%7<|4jqN3FxdFQh zaQ_I^$2905tiG$Xq|$t+JoCHz^nSJb?#?W$B#N=_gg5jPh`7^)|}L8CMhQz*r1lw5kZ zCPHw!CSc4%b%BCftg&KzHD zqI9!a*iFGaOvOgub`g2_smUZdNRz(SrxyK(k?`5p#wD=XFN{XE`HHs7TADDgQ|KNh z)Z}0zPncve-Z|&~kjfQ@$kk+oOc1YrS*fJM+!{ktbZ|7vA6&AFl2+ z2=1*>$*WQe6X0O0v1tAGTIZS$0>cfu$k~i~aKX8*u>G%Ru1z699!G}GlIEkrls+Ph zT1lmIrC`+RHe9o9BDYW8Kkz7pp3v`OtX+30d+Lz>E6F@s&8` z3$81%$~j&hC6BOBT4sL9Qa52irP(FuR$2CoO4t4=ag}|$Qlqm@S>EK@I>BGRhIj6i z>@p_PSzB)fV@lVSl*78pQ|sRnlV4{z6{eoO`W39&48}gUxQ_DV!;p{T>%*rk#Pzrv=yNpni9*oc!SBkUNY%;2#!qEZ4Q@6@jBh!^v z+SdB(M_xi%q#*_N}oa4DXOb1l_Kwj7?+s8Isuy*$?#oQpz9*iCm+)rPT=BKj+Qx5*C{@k znqwn1<0FT3!-A-;#1O(3UEE|guHXyn@%1Eysq7@fxIbRzfeAXVxt9#b zPpxGhp|4R&x;ok;wA&+q;3Fx-d$Kd{+P4gX_c3&wfF2nq3o_I9hb2F(H+iU zcVj_NH`w(2Imd58^98R@L|5KA(>b`MJ-7v6jZ$(`y=Gbl4;f(JVdhx`^vQ7r?#%Q( zSHKe?;=u%PaAMs-<8*gjY|OIrC(mxN$7T21k6%J z=>BA<4mXE10@IF{4$c<{MyChGOLH(Zx$emxTT|Wna4uwfkd)lfgx7Au0~jO>9nXqr zZu%5|EMQ~XgdS(xhHjecFM#yoOX&tI&(mYGExeG!V2dQJ-;A@8&h>~;Is++AbMY}g za{KzAAWWu-gSZ$fA6LD}<*W>vUW7`o16IIoITn#h;DO$4lqW<^$V3B@=8u9`Mgxbr zr6B;ftCEf(Fc(75z6wY?L##M&dqbq10W00xhHnHQjRFbH0xlf_ylN@&(JLm!!Dbdz z0dV-5ZCYp?RttFuDDIJ%*`=M2kEZetJoY{c2zUbb@hoO=82|WT{&go4wreJGktTkU zmM9Z88OD}TzC6F^a}&P;^;0VRwEj3WU>rSd;Px!q#y)z*BNKJIx08xA>p?<|?8Hg+R3bC+{57CoyH%Bkcrw?DUoTl6#V#!YJ-4AvaJ;-Ml!E8L z)fGeTUS#kfw|Sg5HNBC&`~ki8sivm|fzu8okH7Rtki$I5&O8p#yGbaKSD&(^`Pdh( zn=+pw&tG)BvMMx}p5xjFn4YM`^HcCj^YF@E7tRfrVoMCN34?S9@_3;Kk{~J2iWF%6 z1E{F83Ue(9*;(L5k72%1<|{o`_A+mTsUM=U*R{Pl42cfUKQf62 zrtoGnc;)GMb}QBQ)~a!rRlN%vUM7E_7x0i4p^z0qnw||}H|$+`+*l=4%>LR`Bfres zH2j_^d>cBx4_)U}1KC8>KX8q-_e5;;}Q39ICN+g z&yC!CdVqM^!avcHhUY&Gx8NDK=2_QOXR}h5yR3ddFd-l>5|CdANLlp*0{>I~8ZXXX zuR_9b-4R~!L*$pvG^E*mB-HY*lALI>vS`0o2dL2Wz=3qM1s+n!?xZpQ)w@UddWV9n z4O0)VbijpdFESSIj^+Z6K0;4A6&x~r-K@73Sj=3W5jvF=Pk2~Z<^BA=Q=4{0NTs3p z$gDY}k8t=Y;+2}LmPw>T=^Dv?)?DjSI2n)2i2OT6fF2#hch)*VyZtsd+o^foZXC<^gC+S56< zmI{%B&-THg`}$B*@>KhnKD0AHO8lCvxP#ZvD4oiRp$_(1rwggAB!FO^0B1tV#M~Ll+8HeI*Oj+z4lr}rP*JxE36ea4#TJeXsqCCK?BR{0<1$fC zdON)Y`V56qg1D7YaQqFW#IH`ztizx5te=L=5m?Gntayh2i;5KxN@L(PhFS0WYh6s+ zi)Cmz2)h4jOQ=_usOawIAr%g4G$Kekz8NRqqJ{zlzq@V4oCf38dKC8tR0>xW_xAFK z-@O5Uq)|OlG%3nXY5oXIQqcQH?>+f~PiI0OFKSgBJ@i*8`SJ3iCp*w&$zAO?nLI36>F5V*Q@bzCX8H9k~VRwk9dZz8Eqw{z*; z5C?_sE$}KrfFy>fXFx0S?~X^l&kxdgtr4Wme36a0&m>k+yL`ga1?bx|B9e%4lHp z`-8t`P6yxIeVFR*wm!1S&M5_noQ!&g8MVM0es<-w4MR3vh}Y2a{!2{wr7fQE*)?86 zm}HA@3PnsXug`_WDyIiVTEV~pxnm4h{SIVhDtAyk#BF8_+=tD+OEu1nTnqf$JskRN zAF2X`c>d!bqJ=4(O#g5xmpa@Yip&)Ud{g*sQSwdj3_VPnpPNI20kR(2`eOUai980hL;@gjjBn$s8F5qJi8`0 zmL<1|bXJs(KL1TLTq~D-l5Mb;@PALyh-*v4XCf|*936i1TvIKsSLH$)QM?05AXveU zv2HhfIOfzX(Q-$vk05ZiGh5Yq&!6UCAHt(-Wlx=uvs;!rf!Fta9pMG*xyE5Z%URA+ zqJK)n&+Qm3R^4CSJGQwxwdF6Nb@+O?BCMFl6kO>zM}bT~fi>Xwu{y`3h9=L~nEP1w zDapK0aJ|HpKb!qvEycjzG#GdE1mzE#w7_U8sc6=HD09O_OV0y8Cw!#<~ z8vwWk`}l>ZsvMl{ZT|lK2e(*2C+`3mm8U_{%01p5H001(8$P3>gYbXE!77hTw1OTD6#B8h` zLDr6XDz3I52OVaxl_lwCSZLY|05s(J|BnA*4-~2NShusHH>+(6iMPB7`4m)803>Q4 z(xTi%_yCSkr>t4Wr@i&0d8?>e4s4@Rv>ei+KH6=ybCcH8#lq&-d<=_B*tQtY4ZpM(=ocw{D3Wuxe3CBj;vD}tuVX%-GRWtpM z0_dynI|4oJ8qm^oPI!jgU_T`$0;vVHrY1#pjyII0A03EQnd2_P>9EQb>Z`6vB)5A< zu`+bZP4RifNQBLbpz^HPNsu`L5z))pu#QPh-odj_%s2Gbu|MgU0ek50yvA!tT`j3` z(!1sJ9f=B4&5)m{aH%(6i$pp}ayZ%L?9MI@Vd~Wc?aEys-h=T#9%nX;StqzYLz|>& zY*9z9&Hg}#Kyq{ z>A=GL+xb6w{tt8VFP~l%BP-v*iX3<#aU0ZqF)<&7E-LLJBGE{u;`L5)4y`&olag@0 zm4*mig}@KywRf}EZSU+nfB1Gc+4%}*QRq`FK8hNb;=sgfJ0}EsYWsv&c16pb7|s)C z6X!{+(KH_bgJp8aom%M;~HVBS6H=CJMw6Nb=F_lGRu+yexs55LMnQ z4lJvEk+B^+^wE1NA!iFkD40iPcQgsR+riLey2PW~lI-k?Kvmh4$E?gS&7POsRnOS+ z%Zd2MR?IsOCb^VuC34QEm+V8YzfonJd23d29`&WVcD_LDDC|D$@eL)(TYyxof0E=> z^vyFf7y!TkQrgfUo&j63xY*cR7~0ra{8q<`RCR1Jxv@O;%pPD?QhgE8g|jn-g|+gD zRjqP7^Hq5C-`Bp=FiKXLxV>n8PG&_@sF+iUQGSFFb8y!u=QEU!Vhwb?n! zHTt6{W6vndTMHCeYSH|~%Ew|dM#7t{`F?$ONJ#EQm{2jk`zRn|kD~eMXC&FmA%@YV zdw-{_fcTgrcqF{z*Odp{Z7=mO$dhNPnEE&fWwboSfmdvJMT};035+BJqwg{NV+oKl z7S*bDXsW1%;7i(W%{IeQxQK;|%w)fYLQhmvqYaU0bsaUpPiw82YoR70cpHlt$=mp5 z`>AnpZWNhhb3>UrPoB-N_ zuGHTaoG$KgF=u(VTb0FCPh4EMTypcC5I&=n3#crW@(Ejvj9{Ukh9BmZo=jb4w<{z|Q9HRJ%MxJjnu8JZK{w7Ef7X`l~%* z>U}`m5%KsU@0B_#H@q6k{E}Ad7e$aS+EbQAR2ucAHziz1D-fg;!F|tt>73JWl+`Mapj1e};6%_Cxl0DDznVcR@Per?S5%*yFlDng9FW!ZI zo+A38r~Gs0yYI-U#gnfJG zR8|#grmdZYG$Jq?xjnm^58dd7JTFd%o-e$+PA017X%w(GIzF>^a$Q>pbv(08%r`$w zHP|tG?3GLkkyIZ++mow7m~^Qs@C3J`z!Aj`&QM~k|xu=U4N(-n>~k5 z_jM&1Il)Ji^(-M*D|9kgi6yhdIpDwKX!z$M@j=tvsW{hGv^ zF2-T~b5f(~!Lp<1O)p*R>@j2C>A~Bzo^KP2kdgjp$dG=F5N3xY@)mdi01xuSABo(- z6a;c~VEK7r`z@oV#Lil;lA;G5sLi+t&wayNU?06tRo#J)16Rka_jN z0vk`2QkJz!&$)ib!yN}}+5)kopYtd$Xa;(Wwo9U>J+43p_i`h0T|X@Pq>0JZNK<3U zWb1H#_at{~;d^*t_9+lp%wW6cnTWX}&cx*sy=9EV5nrC{4z9LX;bm~e7jGbwBr*|q zm{_48tF2{~QKci>cx72_d52*L>JV5vFrTRBkPX$cO`aX7Ap~af2^Xnk-a5Q9%zAlW zWgWg=2)`{j9EapsW=myTSt)zzwVoAU)|J%zTA2DZHL`3-Jl_P# z5(E8}x|Ac>fU1=BTtwa3=WG^qQcRC@n^+O_vZ*fDk4kn_Bzds1jXQJLH1;Y5sq`Jhy>7DhVKj)LT7hS!JO;`8Hgv+$VG8I@M?D||Azj6y#uq_ua zn3-`iL>pWki&kOwq{&}uK79#FjFl8OsCKn-mYwrdEmTV5$ZEAU=`K@cXU5NpAX~w7 zkU6dbb+A3jM67i^r==ByKD5gxF7g zi5pjt)8z-RAQdn=QdA67n72=9h0XmKpyW_~xwtwZEx&3EzdJ6{Le~a7GnnW-+IaS5 zPCY)I0q&HkSJa)AxUwW_Vq$bQqvdmm;Ac+GOc1V&nuE`{%b=izRN^C zBIT6<0ttK{-!IZiU3f?3TF{sicW2Lw&h!W}M$(a*-~DT9Zo@@ykwX9gx=4SU{Qu;c zqbbM=#Paj}lVZCX1EGX3uv&4ig^(RwZn;(?sTP)oEMw;>jZ@=@s%v%?-*B+UG!bG# zQ}LWB(Pm{QirNUojS9n{G@nKyQdVt|jL-~xrzl>h{Aekq>5LNk`o1|g_ae>9?ZokD zwE2VE>kmlnF$sDt3daH|Vc$PcjwN_oaMN;n$V@lo*fZx8u zT!~wLN+6NsnWe}13Ji>{`|_6CXOTMWn?yTBI?gvDV!wqCI1JfJ#ds7km%6@&3htil zWE8t7@lxLI`Azc6Y|)}9VYG{C1?5zq%2zuoM1uo1mG+6rW{wTg*!Z5UxQ|}YK7HzC zdPQ}&fm_RhGxCO7d>vjx%sS(Ny7K0zlc)XdZ0An1wPrLI z%9PVLKBwk8?@qjRlmUvu^~9$}wFA{YoP}XMoyvORg2;fW3>ELPKoaDJrI$#hB6Nb< z9YLvYb4_-pGO+ZtI;N!ebQNk(EfmBh?dt-W1FFqk>SwTx+czUH7m)o#nq%mO6r)fI zca@Z7)X870YliK!3j*FjD^W_W^n68@9|l&`h0^jYb2|wQ*8ot22#D$Gw!Ru23>?<;5A*cK3rGCLdiBq&gU`G_M4;ip?Dn80?yyV75E)yWMaBI zy{J*iUPin4N#c4$PVX?!3Oc9{8*ub()Q_l<>&FxCN)<7552~J}=tF^G z3M?k+>=S_upo^n8t1=TCJ6|vkZ3xC<))j&Hyp#_2dWwo$Y6Pn65*PG3$y(?b-4xs= zYfNLS z_Pw@vYN4j(e%#0JAD^SaV7+?Nj2XKY{&gs%g67tx_n8Zn13X-$*3nHI??+nDhPY29 z;spt#g_~*G+!x!JB8N#=RR`UP%ZHnSw`013rJzBJq!Z1qv-2|%^Q0yZpI+*NY#ku}9Y z^IuZl%bclzIjA#?vVxo#u+xW&Z{$wEk>O)6Ke7M}88Nf*}hBX{4j_bc3C%xszEz^UEwI ziZI8@aMn>)U)*IzzJY`rjjo2IpmBEjdc%4`dyegJGz^QEq{4d1bp0zAn`a}?O3^9c(&*d?s1(=dQ9H& zD4ZiipRE|w-k}R>t|kjJkbJa5cjW5^v$s*01;>(+O9prsZ+ZP?$DWru2SxS&b&Sj`x+7inCQ5j6h`h@#mLTPOrEH zj!I~Rx>Nf#orD>8q*5=hd*_-a-Xgg8@VnW1bLT2;Du~)7QqW$H5_ZQu*AJbG)d&YJ zHu2CfJ$TVsBFsyDXNn^*Xhm1?Q~HEaQyAck_onZzOncjj9&JfKT2UKyZ08zE+hF*m zvsvl4Ddzpvb*6y;jL=XSy?)&4o>be{Y5~ouKwg;|c?j`Re0jBmcNur23`Y9v zbe_g7fD7}+numYwR|JVzeZ+^zTx>)D0Oj}c>)_~W33B*N+jhRuvY2JX@*>$40ya0c zbPmSoH!{ki1Xv@_wCQ4N$%#9$RgCfw-W>29Q`wm{W-}SFlsh_}rpj-O@kn_r03-JE zOOj#`&xiQK^7>WV)~MU`Y&c8;6|I69fod_-V0jCVv=(pNskcL00|`og0MSXr1p;Lz z?b^Xwm8feORh4$B{3=rT9JkfU_?)b~J?ggkFDE%Aq%te024u&|!muMQjlDhbdBFx2 zT}jim>Ubd@cGb~Rv0MlR;UqCcT=yync_U&`lh?En`t@(B=?Cza8VVRSmDxchI>&dZJN6c6?t%nOH;{)+gHa@w?Uy-)2Yu%p6R@GM#S209<96g z*+)bOjhyEk#8xA~k3wai(xk+wI;kT~DdwuQrosq2;B(MNDV}Lg*N<|yaAb;%kR>QB zo)U_j2rMF_UiFCc8I0<(RYjg)_fs8@w7I}0Qf)8Zr|lTkYkrZudq+xOWjp)MRw^4Q z?=#s2ri6oul#!e?49~D-Wwl-={iP0r(Na#fU0e4n$#$!mK;qR@1y2Q2#OGSHaUwL- zNHfFaZDVEYhGM4}a>q9HRVs4!2)!_?vA2yKcNdk!qqlEIo7rt!?R&4nRoAp!(Pt-B zl*#KclUUUhjux%ckZ*Oob^R;_f=NG z<-&86S=5H@9vC9c?3hd;Z#xbSzDupA__(bjri`trj%V=x9`WBkv;Vw(wl`} zlD9&h`LIJkkJDnBtKsXd%PiEdmZ-7R-?ZM3NW*FWW5j-4eEF+=b?*<|C7oM>W&;~= z+YAkENdgJ8IqE^mKH6+-E6>bR{$$6~S!bB4Z*v=M<2QI$X|b~Wi-@_#Kl*cGyL-rm z)MY!?L2)lB_D2}>c!$pJ_6MrgG6M{J?Wqxp;RCwfpd;pZbNlnrEr4t+ij z5FUka#}slG>kkf@8rXx3l^yNPtWADW$s?*w3YHDh8TCC-uy^5eLyxX;k2-@AA5xm> zm$OTD{@CAebN)sp3WNWCQNBCz!yetxc5t{d8Pe28C@dvfwvmPQzLV7nVp<&ek)eR$ zc6|Jr{o{7l(Cx}j9O`JdQm!Dx$_?3kiDrE1K3z9U_krq9k^E#*X=ekhbKAR^K2Jbo z<->`Lai~awx5g&iJQO8`86zuU_x*aih~E_V`n8fgx0aQTAzP)x(VF47$q;g zy0Dn6yAGt(c_>Xqi-~AWae9cmfqdaVr^|d+?9y$JJE9ODQ2g$L-+C*5OqYLew)~nj z|7^ARN4C9hXT^GX0dp?~c8{G5Ko(PeL0+f20b_VQ13#M>b%Ax~UP%P{vc7IRwEq0# z4Szo2ThwUA@doICH6vWpvP1V7rdE-1?(I1&L?-?ygE8ioWEeR)sgQ3`>exu-0g9A& zcpYAy)8!F}`B8#DrX@pMS$nQR35Sac%&m^~5UN<@st*{~Jk|Vo1(Y=IcOyJW_^mTr z2PKY&{3Us!^aVdl@N~c2JAP*=?-_Z2juI^AZ=fo z-#uUp8Gc8Qy)wwr@wYf&dGw!++0e{-H?dp_Mg%?ZVU|zQK2YB*X|StZ*j$l<))#0P ztwe@Ho`>gtgaXT#&Ja%e9e53>u^&utB->e7fDGLH_Sh{9;`qnl%#5n{jmw>q=HxR2?K#VwGoX0Er_EIa{Jd|yCr<}mS|P*p&jCE3rOFbA44?s|Nr(Yy z5*phWDcIZCIEAk~3l3(0i$4_+1J6BItxyfv3xF@I_Mel8Gn% zy`qShB#DN~0k84wU~9i9pou$*vh$Lj<>vDNGV#pONmIW{#x13nz)l zX(h3~Y|-qch7C{I$JIQSBv;+sMKK>ZUD}~KoSu>1QL1}5G98G*U&Stwf!&h zAR+h1krE?fGs}t`Z~${nf;>;iGp~T`Q)DQic3Nft6;op(n`88;f-<-$dUqWn=n~!gX#XkD+DWrB~Tfd`js1tr~s3ip)&r_>@|6 zi|`;WgqTR}lenVzF zpIjDnJ#o^?%74Fl+HrR5rs&0drDMWDwRm4~kMw8ego0**h@F4m!t>{F{dxVD4Lu6d ze+T&c=7K*Bf4k;FIPsUQ29FINFS!0P?T2*kA1%K=Hvap%&@WQ}!0^dW!FY>=20f3-q0Kh*= m{$umMC+feNLzDf*{ExJ)APonJK>z?5@}q`?aocao81R2{c~KJp literal 0 HcmV?d00001