From 7cd4f2c929551ed2928d5816400957646074b602 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Thu, 9 Nov 2023 11:36:10 +0700 Subject: [PATCH] Update Hospitals Corporate Bisa Import --- .../Http/Controllers/Api/DrugController.php | 2 +- .../Controllers/Api/HospitalController.php | 91 +++++++++ Modules/Internal/Routes/api.php | 2 + .../src/pages/Corporates/Hospital/List.tsx | 190 ++++++++++++++++-- .../dashboard/src/pages/Master/Drug/List.tsx | 20 +- public/files/Template - Hospitals.xlsx | Bin 0 -> 8376 bytes 6 files changed, 282 insertions(+), 23 deletions(-) create mode 100644 public/files/Template - Hospitals.xlsx diff --git a/Modules/Internal/Http/Controllers/Api/DrugController.php b/Modules/Internal/Http/Controllers/Api/DrugController.php index 2a25a575..97c7bd63 100644 --- a/Modules/Internal/Http/Controllers/Api/DrugController.php +++ b/Modules/Internal/Http/Controllers/Api/DrugController.php @@ -145,7 +145,7 @@ class DrugController extends Controller } $response = [ - 'message' => 'File uploaded and data saved to database!', + 'message' => 'File uploaded and data saved to database', 'data' => [ 'total_success_row' => $importedRows, 'total_failed_row' => count($failedRows), diff --git a/Modules/Internal/Http/Controllers/Api/HospitalController.php b/Modules/Internal/Http/Controllers/Api/HospitalController.php index 63a716f7..9d2be239 100644 --- a/Modules/Internal/Http/Controllers/Api/HospitalController.php +++ b/Modules/Internal/Http/Controllers/Api/HospitalController.php @@ -8,6 +8,8 @@ use Illuminate\Http\Request; use Illuminate\Routing\Controller; use Illuminate\Validation\Rule; use Illuminate\Support\Facades\DB; +use App\Helpers\Helper; +use Maatwebsite\Excel\Facades\Excel; class HospitalController extends Controller { @@ -150,4 +152,93 @@ class HospitalController extends Controller { // } + public function downloadTemplate() + { + return Helper::responseJson([ + 'file_name' => "Template - Hospitals.xlsx", + "file_url" => url('files/Template - Hospitals.xlsx') + ]); + } + + public function import(Request $request, $corporate_id) + { + if ($request->hasFile('file')) { + $file = $request->file('file'); + $data = Excel::toArray([], $file); + + $processedData = $this->processCategoryNames($data); + + $importedRows = 0; + $failedRows = []; + + foreach ($processedData as $row) { + $q = DB::table('organizations') + ->where('code', '=', $row['code']) + ->where('type', '=', 'hospital') + ->where('status', '=', 'active') + ->select('id', 'code', 'name') + ->limit(1) + ->first(); + try { + CorporateHospital::create( + [ + 'corporate_id' => $corporate_id, + 'code' => $q->code, + 'name' => $q->name, + 'organization_id' => $q->id, + 'description' => $request->description ? $request->description : null, + ] + ); + $importedRows++; + } catch (\Exception $e) { + $failedRows[] = $row; + } + } + + $response = [ + 'message' => 'File uploaded and data saved to database', + 'data' => [ + 'total_success_row' => $importedRows, + 'total_failed_row' => count($failedRows), + 'failed_rows' => $failedRows, + ], + ]; + + return response()->json($response); + } + + return response()->json(['error' => 'No file uploaded.']); + } + + private function processCategoryNames($data) + { + $header = []; + $row = []; + for ($i = 1; $i < count($data[0]); $i++) { + $row[] = $data[0][$i]; + $header[] = $data[0][0]; + } + + $filed = []; + foreach ($header[0] as $value) + { + $modelColumn = strtolower(preg_replace('/\s+/', '_', trim($value))); + $modelColumn = str_replace(['*', ' '], '', $modelColumn); + if($modelColumn) + { + $filed[] = $modelColumn; + } + } + + $result = []; + foreach ($row as $subarray) { + $trimmedSubarray = []; + for ($i = 0; $i < count($filed); $i++) { + $trimmedSubarray[$filed[$i]] = $subarray[$i] ? $subarray[$i] : null; + } + + $result[] = $trimmedSubarray; + } + return $result; + } } diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 00b9477a..dfc68def 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -109,6 +109,8 @@ Route::prefix('internal')->group(function () { Route::get('corporates/{corporate_id}/hospitals/data', [HospitalController::class, 'dataHospital']); Route::post('corporates/{corporate_id}/hospitals/save', [HospitalController::class, 'store']); Route::put('corporates/{corporate_id}/hospitals/{id}/edit', [HospitalController::class, 'update']); + Route::get('corporates/hospitals/download-template', [HospitalController::class, 'downloadTemplate']); + Route::post('corporates/{corporate_id}/hospitals/import', [HospitalController::class, 'import']); Route::get('corporates/{corporate_id}/members', [CorporateMemberController::class, 'index']); diff --git a/frontend/dashboard/src/pages/Corporates/Hospital/List.tsx b/frontend/dashboard/src/pages/Corporates/Hospital/List.tsx index 4f6b1ffb..081c2c4b 100644 --- a/frontend/dashboard/src/pages/Corporates/Hospital/List.tsx +++ b/frontend/dashboard/src/pages/Corporates/Hospital/List.tsx @@ -20,6 +20,8 @@ import { InputLabel, Select, FormHelperText, + Menu, + ButtonGroup } from '@mui/material'; import AddIcon from '@mui/icons-material/Add'; // hooks @@ -39,6 +41,10 @@ import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material import CloseIcon from '@mui/icons-material/Close'; import { enqueueSnackbar } from 'notistack'; import Label from '../../../components/Label'; +import DownloadIcon from '@mui/icons-material/Download'; +import { LoadingButton } from '@mui/lab'; +import CancelIcon from '@mui/icons-material/Cancel'; +import UploadIcon from '@mui/icons-material/Upload'; export default function HospitalList() { const { corporate_id } = useParams(); @@ -267,22 +273,182 @@ export default function HospitalList() { } // End dialog for update status devisions + const [anchorEl, setAnchorEl] = React.useState(null); + const createMenu = Boolean(anchorEl); + const importHospital = useRef(null); + const [currentImportFileName, setCurrentImportFileName] = useState(null); + const [importLoading, setImportLoading] = useState(false); + const [importResult, setImportResult] = useState(null); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const handleImportButton = () => { + if (importHospital?.current) { + handleClose(); + importHospital.current ? importHospital.current.click() : console.log('No File selected'); + } else { + alert('No file selected'); + } + }; + const handleCancelImportButton = () => { + if(importHospital.current) + { + importHospital.current.value = ''; + importHospital.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(importHospital.current && importHospital.current.files) + { + if (importHospital.current?.files.length) { + const formData = new FormData(); + formData.append('file', importHospital.current?.files[0]); + setImportLoading(true); + axios + .post('corporates/'+corporate_id+'/hospitals/import', formData) + .then((response) => { + handleCancelImportButton(); + loadDataTableData(); + setImportResult(response.data); + setImportLoading(false); + enqueueSnackbar('Success Import Hospitals', { variant: 'success' }); + }) + .catch((response) => { + enqueueSnackbar( + 'Looks like something went wrong. Please check your data and try again. ' + + response.message, + { variant: 'error' } + ); + setImportLoading(false); + }); + } else { + enqueueSnackbar('No File Selected', { variant: 'warning' }); + } + } +}; + const handleGetTemplate = () => { + axios.get('corporates/hospitals/download-template').then((response) => { + const link = document.createElement('a'); + link.href = response.data.data.file_url; + link.setAttribute('download', response.data.data.file_name); + document.body.appendChild(link); + link.click(); + handleClose(); + }); + + +}; + return ( - - - - + + {!currentImportFileName && ( + + + + + + Create + + + Import + + { + handleGetTemplate(); + }} + > + Download Template + + {/* + Download ICD + */} + + + )} + {currentImportFileName && ( + + + + + + + } + sx={{ p: 1.8 }} + onClick={handleUpload} + loading={importLoading} + > + Upload + + + )} + {importResult && ( + + + Last Import Result :{' '} + + {importResult.data.total_success_row ?? 0} + {' '} + Row Processed,{' '} + + {importResult.data.total_failed_row} + {' '} + Failed + {importResult.data.failed_rows.map((row, index) => ( + [Code={row.code ? row.code : 'Required'},Name={row.name ? row.name : 'Required'}] + ))} + + + )} {/* The Main Table */} diff --git a/frontend/dashboard/src/pages/Master/Drug/List.tsx b/frontend/dashboard/src/pages/Master/Drug/List.tsx index f3110b19..00afbecc 100644 --- a/frontend/dashboard/src/pages/Master/Drug/List.tsx +++ b/frontend/dashboard/src/pages/Master/Drug/List.tsx @@ -98,7 +98,7 @@ import { // Create Button Menu const [anchorEl, setAnchorEl] = React.useState(null); const createMenu = Boolean(anchorEl); - const importPlan = useRef(null); + const importDrug = useRef(null); const [currentImportFileName, setCurrentImportFileName] = useState(null); const [importLoading, setImportLoading] = useState(false); @@ -111,19 +111,19 @@ import { }; const handleImportButton = () => { - if (importPlan?.current) { + if (importDrug?.current) { handleClose(); - importPlan.current ? importPlan.current.click() : console.log('No File selected'); + importDrug.current ? importDrug.current.click() : console.log('No File selected'); } else { alert('No file selected'); } }; const handleCancelImportButton = () => { - if(importPlan.current) + if(importDrug.current) { - importPlan.current.value = ''; - importPlan.current.dispatchEvent(new Event('change', { bubbles: true })); + importDrug.current.value = ''; + importDrug.current.dispatchEvent(new Event('change', { bubbles: true })); } }; @@ -136,11 +136,11 @@ import { }; const handleUpload = () => { - if(importPlan.current && importPlan.current.files) + if(importDrug.current && importDrug.current.files) { - if (importPlan.current?.files.length) { + if (importDrug.current?.files.length) { const formData = new FormData(); - formData.append('file', importPlan.current?.files[0]); + formData.append('file', importDrug.current?.files[0]); setImportLoading(true); axios .post(`master/drugs/import`, formData) @@ -181,7 +181,7 @@ import { #TFW&pKz%^Vv^q6?r6NLckpW5C8yB19(KQcI^=WfN^91fB*nQ z)R%OycZJ%!8fkbrLR}2lJ?(6%a*+`kvjK?k=l>o5!!uBt*st`S6IbdSd@Z@gCil7O z5%9rYU=KdCs;DtY8B`K(H#0VREkbZk9V~}#&0mQ>^!XY0(ST)@)wWrbsHKfF*QYx) z;_5mD12n{x)yXG@v;pmm#bq5&YVLKJdHXy_t;Lq?oTp#QrE$hl3(f7kBVkSsT3{-3 z@V}Vk$Pa>`VIJ`_>@!>Yxq*^Ij*GDbQ;?XYO*|c6%YXP-rGDg}BQFgY+sTZ{-rJH)SBRJ(UBfvOw9 zCfRk}j$4&7BkF^9Jv9#21WAZdFUse8I=+~1mtmw+#lW&UMQIE?wsn9aR>Hw+(aE9t_COwNye|m_xj`9yl~WZFZJ09Z)pTRq0s$0kFwC@D%(}}ZJGICyw?kzFQB@M+n@_mcU3R+b_GMyjN zco~`5wtsz`)<*E%n@uUbSB-`j|AKo+riU*3)K9O5_ozR^tBW7)eM#?OUtk1f@xnhs zfIt#TtM^v$=3eaq>?UBXJHd~bIObopN4xYL37%6dl4X^9?z0w;D!jONiAv#I zOvMd_5al(`iI+r>*;Rm@*ioWJ2qrd9)^?It3pBolC%T{lf}>T<74RC%=}FdgD0>6Z zpDPrsOt-5IJTYK=p`M{??YVy^lemeDC%`%w?1L<+R1`V=n7u{B&?a;yG#oj4A$_B588dX=%j_-N`38(6Pi7PCh<()Njh=d z*%})#TPAz3buKb>?x9vOJJit=J6oQMVQ$l4QqT!AyK7(3$(C~90~$cJ(KHOF@`8m= z&NyNW_Z=m!pS0GM2SuzfY}BaR_1f3 zVA6TCH{>k&vOV;yfYZ44!RgTKo6da;e1+X7V`6mbgoD^Rz_s@0vcU{bgB3RtnPXJN zK#Nu?0-0kWe5;SyrP!A-qZU(e4url_?uLbH9t5tWYWjRP6p2+=HLZ+O#eRa5AP@eE zW%y_`r)KL)0h_wyW;u_F`Iyq^o$o>MoGiPZ-P`1L$Bp{InAtC7zRe!fU+>HZOko&` zN21@Sak8p%6Jc|WoK<<(GJMzY0p4PST#~)jJI@{E{=tJ#`ZH^kxk0y~O*ID=hvdQS zfN%|V)A3`GO>gDqe&}6q$lc{^I=ijEz`>8}qGRIwVO?D=;)HmV3;tCm&_o$|ciPI7#eqWusC`A>{NfTw`) zXaBcHsan5M7boVs`$ssc`@DM;xY>5+fw5=k{iwm?KE;YpJ{TIqDAdE*q*T~j9#@rp zitMSLZ~11}B9TudSzb*$fgbq23d^lL3kOG=Uk`6iWtIYI?Fu>HwT6|a=@ zjBtf;Y~}miX+%9}5zUj1SKyBGyIYmbp)M%F@5sX|jC*i@`Ewa|v4lchT{wRJaQ#@1 z(=~J*Ryc8ejjUjjYaa0evRn9gZ|qoVd`g<8s-5V&I-_;xmWQoj-=bYqXla8%0e18^ z(Ihdi#K;mc-Hl6!6~!&@B>+I>@*|}f$6tG#k3*0rD+|rk_*G)>>l{~HTwAqHnVZ@G zh^Zt^G1P?W2l9H4L4?&g3lc#N%yxt>7h|ROn;*1)#LhQM>^kCAQ{c_F=pA4qYkGqm zW-*|T615|X3EmiPEBi1RZTn(Tq9|piW)5x2I)adeIz-4lpH@ZKPnUnT;$_!699~ml zfqU1q7m6~k*nM;Ik@;u0KZf*@on~^3Wl2>(KOz`|-tdnMvvOAu+yE9|$H(aAdN7o) zWw3synmKvA(GNY=N<_IN#Ti1j*bei=x9}Bsb`$sE-A;mbb;{19>_~eFnr+Epdvpxd zX)D$A8_6ZbQkySGBM^!V$AWgEBPbfC2Fj(}*Qu_9O}=aVih7Hk_R0y5*mm-4SvkbT zMV2yt-&KrIh{Nl-B@z-32&ys_RGi{qq&G_mDVYI-LSR-XpA+J-owoMvhaK{dng)`SU+jy_QF`xM}K4eh=jH0n?IlA<2=-Iy23Ei?BKTP z>7m_W2EOM|lT>;qxfZ=4q1k(fBKY63C0E`Z#95JK3$GSO)048$6)Tx#oU zmv9)*!UCtu(6YY;tL{dKYCnmLwF0l|KJ^pPe5l3LLC`W_K0a|N26rSF?B^+QbRIpB z^IrFne%v`?D$mZN!q~xsxAx*m!*L-5CC$p}teMq=-41^DgcUYz=CG?sK*}feVI$35 zeZ%L4V`F{GGTfP;?Gs(R`m{>wNS|br%p# z;>D1;&KeL!B3y-f9QXxARLqPd&c|<^^uJ$F#ROJRK2>Cu8~Vhe)hnyWQmM&3%%iQw z_)%fVzf%4fqdm7!4qf(=j_$IN(@@$PB4>^F+@dr;HYv*padmTUW;6KwhxnY6-Q2a{N61#P5@KpZy#sZVU0I7_f=I4$@y4B_O`i zwV5Lo&?Ge!`Irq&3XQhO(9y?<*C~gJ|?u$=>UzsHTf~ z!py7bMJ%xMw*$$I2eMM03OapTAN}%2#Y8Tzw^CR&r7^ZUp0X#Kty{=x_K$$2jp>iG zOBusp@oI#RJ4(PZl~m_T^~QPck39L9!Gk_A35cKVdE{Q6D1+*gF}+>rYN;WRl8jMN zzE(6|i5Dl<1Hm3L(;q2UhbS}1VtT9Ya8C5t9cr&yKk|0DNEAbcU+!H<0>WvY=eC`n zI8xXbo-Ppe3ZMBHWhycx40q_?Fo+DMef@emeHufx>NopdxzOUPAAoTUF!5r-Vvq``1%lPErMcW3Mn8)d>lNX8*9@yXs!Uf`2t z2oeCx?D6_%^_aVgVB{7rdgo4F(yDy3X^K8-Ax=j!VnM$mgNHp87Q~F91`Vn(aJ+D? z=V*L-W!Y1+leDgOXy36Xl5{DMqVY^alSFuu&dKpLRYNyH!Bs+uZCV_9Jkb*N>r#WF z(GoedFoJ<%Jl7un!kfAa!|n|-)Bw?5`h)o5K~ZddAwoHlligxI2}HefMg*o8V97^s zeX!ANc#^x!{JT_5y#tY@&*)i+kXScnebnC+zOZBV*VDf%phPhuL8%mF`IMU`vb#x1 zS}bjQd*z)~!8u*R3^`}0psL3s?X8fFCVpY^469mLagAiBHGgvOL?}tP)qKWT;-&U` zk^0Gq@_cNOj29ccuDsD32dXw~G(xlO4P}Ip&(2x=_|TS{aO14k8K_bZbu@;B;)_~k zbkNVk!Duc^M!BpnW48l)Khcs&^%RT8u`mlK5wjSDExIGE7E|}K2_TDKRICwEu~0^l z5ze!5E8>Hgns<{>il`AEEk|i#-bC~pVNbI3>ocD7MI5xSxU=};TUH)24x>f7yfz+B zvF&cG2rk`e%jnSP-*1mnJ&@9Q8TT}I8e+f1n9D})%_F{=7=-+g$#HQ<-1Fr&!nf_$ z-%g4rM|5&$1{7p8PsYiJ#?DEPiAwCBKP-)OU5%1=*Q>Xz!3McG%DwYF(I0ObAhS+& zG77CVpVCU6B{c+=DttOyE6czOdgZyb7UtG$P=fl{&%J7xS0N3Q_eObx8nlpReo&7f(6dK&ec(4yet5!aE| zeUzuhg&TTw0s>#TAk0%J+H=T$Ed%carE3e`WJ|ClEs2yId0pKYJeg}T=J$xyMaF%j zbeK^)5g+@o%`|?}q6vb&kT?srHnFbDFUQQ2+T)WImsrfglWpuI?^N6HL!x=zxTtAZ z4wYhCbnMt`{m=>C=-(+D4YfyBu?u7xP$l@J@EpV=U{yAB4bCJr*cr`4U>||@xtyi% z2eJYOV!-~_5A&&Ld@pFvy5Hej$zeWQQW8z?F3_rzZd_nWTVld>W71h*`CiO@j!w9* zUEuVcTto$dh%$k9uy**)t3CP--Hm!N3oVT*`zX%$KI23%A$iDWy@e6H~~P-!Tq z`1cEUBXMa$vXW@W=6#%!`q2F#PLLf{*?ZmvOZ9dxiY||b9)-L_r8U`c%X32KHBEuA zs5&A!kthtMdOg);4Knu=Bp`!@#OH>*n=c@KV++IiM|BmfZcmo+jh~Th=JwA#{S;2-vC_?{;XV5XSVBx26d? zu$08RzQwArz!`mbSEtN3d%Bo4lkkJM;o6mSpjh()M>f_ee$GHNM*jo`3POsM3u|Q^ zgYHz%+=`HAfEWz`8;(@N^2r_B<*=Lep%J@dN)Op*mjRzUTy-%T zoD?W+m2WmIk4oAg35Z zuP+z0z^1wWxR|6QdHM!W4Tun=FHW0sLl5L;6puC{+WO}LM>sS&F9S|XE&L$-lNw7C zXQ-LFtFx88#ZP#6qr}lWe{?kiJ%H7Owr?=-WJjes2sRNUcXQOI-W$T3fOznF&CJ16#o58pg~Qar8Tx0Z{@*eUK6SzIs`BqS z2}2f9V3K{$(rQ8~L+X%TwroDC$h{y+XfB)0rw-paJ5Zb}b7#Crc{ccczcJaYKd3ZF zevJV$MJMG|a7Lsoj(yN?h_B>BZ@n6R{+gdRLWzp``wjcNwft{5`DH3 z!bmSf&(~uZTj7rOy9-WT(5(jjNIBuL2=}ipXyWMjuLr{C<{%1?t6QqKs=@JTy9@q7Yq@c{5cL?Y{KBXpfq!LTif2 z%L40&=~UUmrE6NnslI;cWje>*F}wCwGZ|C^W+V^G{8GQ3>qs_0PCRQ$sHx!8pp8dP za-M&L`m!-+z~Ks^(@rnajrAn`nAr1Vmz6k^l?+E<(;Ai&G9|D_@@t!D1I%%mgmR2; z;aJ9DD+7C?@BxW6_I1j>Q#OS_!!Bw<+GH^KxI^kt#BzC7A!=E2a>TKI-;_Wa)8&op zsS*4y_}y_^_qnpB;dj>H2L}9rHzn+#t|n$Ct|oLJmE{q|@A)5j%+->7q0K{`0fs8c zhoySJ20jhEB*bu&$Zratz$dlNuNRfg;(WtV8SVM(;)s*-t=Y7>VP_h>7P@9;(Mr~1 zlW7wQ=TQ3a+K-Bmt^@J;)FXOp-r|~38CRR#Nz7`U4K1~=XKOb|lKdxBnPV0VtSW(@7E1{|5$Ri-K!QZ*U)4qM<=`({Qn!V-8}q-UpWv^<#u8H%gPjd%K`o=x6AC?25x8azYG+?3w5}GUwQp) z>Fsppmvo-!59#gX=C*~uXAr;i06-}*0PtHraa;cP)&H+@4$xoZf5w8_>e~qZ(r=Re beEt6bsUnX8cZ(mKVgMT9xy}~(kE{O!hkixu literal 0 HcmV?d00001