From 676e5413859e9ad950a4e960766606500f093d61 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Mon, 20 Nov 2023 16:36:27 +0700 Subject: [PATCH] Notification Hospital Portal --- .../Http/Controllers/Api/MemberController.php | 6 +- .../Api/NotificationController.php | 86 +++++++++++++++++++ .../Http/Middleware/Authorization.php | 18 ++-- Modules/HospitalPortal/Routes/api.php | 10 ++- app/Helpers/Helper.php | 10 +++ ...1_20_103217_create_notifications_table.php | 38 ++++++++ ...104805_create_notification_types_table.php | 33 +++++++ .../dashboard/header/LanguagePopover.tsx | 1 + .../dashboard/header/NotificationsPopover.tsx | 54 +++++++++--- .../src/layouts/dashboard/header/index.tsx | 4 +- resources/lang/en/Message.php | 3 +- resources/lang/id/Message.php | 3 +- 12 files changed, 238 insertions(+), 28 deletions(-) create mode 100644 Modules/HospitalPortal/Http/Controllers/Api/NotificationController.php create mode 100644 database/migrations/2023_11_20_103217_create_notifications_table.php create mode 100644 database/migrations/2023_11_20_104805_create_notification_types_table.php diff --git a/Modules/HospitalPortal/Http/Controllers/Api/MemberController.php b/Modules/HospitalPortal/Http/Controllers/Api/MemberController.php index f2df36dc..b11abbb0 100644 --- a/Modules/HospitalPortal/Http/Controllers/Api/MemberController.php +++ b/Modules/HospitalPortal/Http/Controllers/Api/MemberController.php @@ -27,8 +27,8 @@ class MemberController extends Controller 'no_polis' => 'required', 'birth_date' => 'required' ], [ - 'no_polis.required' => trans('validation.required',['attribute' => 'Member ID']), - 'birth_date.required' => trans('validation.required',['attribute' => 'Birth Date']), + 'no_polis.required' => trans('Validation.required',['attribute' => 'Member ID']), + 'birth_date.required' => trans('Validation.required',['attribute' => 'Birth Date']), ]); if ($validator->fails()) { @@ -73,7 +73,7 @@ class MemberController extends Controller $res_data['services'] = $services; - return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200); + return ApiResponse::apiResponse("Success", $res_data, trans('Message.success'), 200); } } } diff --git a/Modules/HospitalPortal/Http/Controllers/Api/NotificationController.php b/Modules/HospitalPortal/Http/Controllers/Api/NotificationController.php new file mode 100644 index 00000000..54d82e5a --- /dev/null +++ b/Modules/HospitalPortal/Http/Controllers/Api/NotificationController.php @@ -0,0 +1,86 @@ + $hospital_id, + ]; + if (!$hospital_id) + { + return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); + } + else + { + try { + $notifications = DB::table('notifications') + ->join('notification_types', 'notification_types.id', '=', 'notifications.type') + ->select( + 'notifications.id', + 'notifications.title', + 'notifications.description', + 'notifications.avatar', + 'notification_types.type', + DB::raw('DATE_FORMAT(notifications.created_at, "%Y-%m-%dT%H:%i:%s.000Z") as createdAt'), + 'notifications.isUnRead', + ) + ->where('hospital_id', '=', $hospital_id) + ->get(); + $res_data['notifications'] = $notifications; + return ApiResponse::apiResponse("Success", $res_data, trans('Message.success'), 200); + } + catch (\Exception $e) { + return ApiResponse::apiResponse("Error", $data, $e->getMessage(), 500); + } + } + } + + public function setReadNotification(Request $request) + { + $data = [ + 'hospital_id' => $request->hospital_id, + 'id' => $request->id, + 'isUnRead'=> 0, + ]; + $validator = Validator::make($request->all(), [ + 'hospital_id' => 'required', + 'id' => 'required' + ], [ + 'hospital_id.required' => trans('Validation.required',['attribute' => 'Hospital ID']), + 'id.required' => trans('Validation.required',['attribute' => 'ID']), + ]); + if ($validator->fails()) + { + return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); + } + else + { + try { + DB::beginTransaction(); + DB::table('notifications') + ->where('notifications.id', '=', $request->id) + ->update($data); + DB::commit(); + return ApiResponse::apiResponse("Success", $data, trans('Message.read_notification'), 200); + + } + catch (\Exception $e) { + DB::rollback(); + return ApiResponse::apiResponse("Error", $data, $e->getMessage(), 500); + } + } + } + +} \ No newline at end of file diff --git a/Modules/HospitalPortal/Http/Middleware/Authorization.php b/Modules/HospitalPortal/Http/Middleware/Authorization.php index 430258bb..de3dfd1d 100644 --- a/Modules/HospitalPortal/Http/Middleware/Authorization.php +++ b/Modules/HospitalPortal/Http/Middleware/Authorization.php @@ -27,11 +27,11 @@ class Authorization // Add language if(!$locale) { - return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Accept-Language']), 401); + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Accept-Language']), 401); } if($locale !== 'en-US' && $locale !== 'id-ID') { - return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Accept-Language']), 400); + return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Accept-Language']), 400); } if ($locale === 'en-US') { @@ -46,25 +46,25 @@ class Authorization // Validate authorization if (empty($authorization) || strpos($authorization, 'Bearer ') !== 0) { - return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Authorization']), 401); + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Authorization']), 401); } // Validate type accept & content type if (!$acceptHeader) { - return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Accept']), 401); + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Accept']), 401); } - if (!$contentType) + if (!$contentType && $request->isMethod('post')) { - return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Content-Type']), 401); + return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Content-Type']), 401); } if ($acceptHeader !== 'application/json') { - return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Accept']), 400); + return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Accept']), 400); } - if($contentType !== 'application/json') + if($contentType !== 'application/json' && $request->isMethod('post')) { - return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Content-Type']), 400); + return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Content-Type']), 400); } return $next($request); } diff --git a/Modules/HospitalPortal/Routes/api.php b/Modules/HospitalPortal/Routes/api.php index 6cd299cd..2aff378c 100644 --- a/Modules/HospitalPortal/Routes/api.php +++ b/Modules/HospitalPortal/Routes/api.php @@ -5,6 +5,7 @@ use Modules\HospitalPortal\Http\Controllers\Api\AuthController; use Modules\HospitalPortal\Http\Controllers\Api\ClaimRequestController; use Modules\HospitalPortal\Http\Controllers\Api\MemberController; use Modules\HospitalPortal\Http\Controllers\ClaimController; +use Modules\HospitalPortal\Http\Controllers\Api\NotificationController; use Modules\HospitalPortal\Http\Middleware\Authentication; use Modules\HospitalPortal\Http\Middleware\Authorization; @@ -42,11 +43,18 @@ Route::prefix('v1')->group(function() { Route::get('claims', [ClaimController::class, 'index']); Route::middleware(Authorization::class)->group(function () { + //Search Member Route::controller(MemberController::class)->group(function () { Route::post('search-member', 'search'); }); + //Notification + Route::controller(NotificationController::class)->group(function() { + //get notifications + Route::get('notifications/{hospital_id}', 'getNotifications'); + //Set read notification + Route::post('set-read-notification', 'setReadNotification'); + }); }); - Route::get('claim-requests', [ClaimRequestController::class, 'index'])->name('claim-requests.index'); Route::post('claim-requests', [ClaimRequestController::class, 'store'])->name('claim-requests.store'); Route::get('claim-requests/{claim_request_id}/log', [ClaimRequestController::class, 'generateLog'])->name('claim-requests.generate-log'); diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 6121dd3a..3118be39 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -244,4 +244,14 @@ class Helper return $convertedDate; } + public static function sendNotification() + { + + } + + public static function sendEmail() + { + + } + } diff --git a/database/migrations/2023_11_20_103217_create_notifications_table.php b/database/migrations/2023_11_20_103217_create_notifications_table.php new file mode 100644 index 00000000..aeca488a --- /dev/null +++ b/database/migrations/2023_11_20_103217_create_notifications_table.php @@ -0,0 +1,38 @@ +id(); + $table->bigInteger('hospital_id'); + $table->string('title', 255); + $table->string('description', 255); + $table->string('avatar', 255)->nullable(); + $table->integer('type'); + $table->boolean('isUnRead')->default(false); + $table->bigInteger('created_by')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('notifications'); + } +}; diff --git a/database/migrations/2023_11_20_104805_create_notification_types_table.php b/database/migrations/2023_11_20_104805_create_notification_types_table.php new file mode 100644 index 00000000..b1271fa8 --- /dev/null +++ b/database/migrations/2023_11_20_104805_create_notification_types_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('type', 255); + $table->string('description', 255); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('notification_types'); + } +}; diff --git a/frontend/hospital-portal/src/layouts/dashboard/header/LanguagePopover.tsx b/frontend/hospital-portal/src/layouts/dashboard/header/LanguagePopover.tsx index f5bc67e3..e1f8ccc0 100644 --- a/frontend/hospital-portal/src/layouts/dashboard/header/LanguagePopover.tsx +++ b/frontend/hospital-portal/src/layouts/dashboard/header/LanguagePopover.tsx @@ -39,6 +39,7 @@ export default function LanguagePopover() { const handleChangeLanguage = (language) => { localStorage.setItem('currentLocale', language); setCurrentLocale(language); + window.location.reload(); }; return ( diff --git a/frontend/hospital-portal/src/layouts/dashboard/header/NotificationsPopover.tsx b/frontend/hospital-portal/src/layouts/dashboard/header/NotificationsPopover.tsx index b6f713ff..e0d2ad19 100644 --- a/frontend/hospital-portal/src/layouts/dashboard/header/NotificationsPopover.tsx +++ b/frontend/hospital-portal/src/layouts/dashboard/header/NotificationsPopover.tsx @@ -1,5 +1,5 @@ import { noCase } from 'change-case'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; // @mui import { Box, @@ -18,19 +18,32 @@ import { // utils import { fToNow } from '@/utils/formatTime'; // _mock_ -import { _notifications } from '@/_mock'; +//import { _notifications } from '@/_mock'; // components import Iconify from '@/components/Iconify'; import Scrollbar from '@/components/Scrollbar'; import MenuPopover from '@/components/MenuPopover'; import { IconButtonAnimate } from '@/components/animate'; +import axios from '@/utils/axios'; +import { useSnackbar } from 'notistack'; // ---------------------------------------------------------------------- export default function NotificationsPopover() { - const [notifications, setNotifications] = useState(_notifications); + const [notifications, setNotifications] = useState([]); + const {enqueueSnackbar} = useSnackbar(); + useEffect(() => { + axios + .get('notifications/1') + .then((response) => { + setNotifications(response.data.data.notifications); + }) + .catch((error) => { + enqueueSnackbar(error.response.data.meta.message, {variant : "error"}); + }); + }, []); - const totalUnRead = notifications.filter((item) => item.isUnRead === true).length; + const totalUnRead = notifications.filter((item) => item.isUnRead === 1).length; const [open, setOpen] = useState(null); @@ -46,7 +59,7 @@ export default function NotificationsPopover() { setNotifications( notifications.map((notification) => ({ ...notification, - isUnRead: false, + isUnRead: 0, })) ); }; @@ -98,7 +111,7 @@ export default function NotificationsPopover() { } > {notifications.slice(0, 2).map((notification) => ( - + ))} @@ -111,18 +124,18 @@ export default function NotificationsPopover() { } > {notifications.slice(2, 5).map((notification) => ( - + ))} - + {/* - + */} ); @@ -140,11 +153,30 @@ type NotificationItemProps = { isUnRead: boolean; }; -function NotificationItem({ notification }: { notification: NotificationItemProps }) { +function NotificationItem({ notification, onClick }: { notification: NotificationItemProps, onClick: (id: string) => void}) { const { avatar, title } = renderContent(notification); + const {enqueueSnackbar} = useSnackbar(); + const handleClick = () => { + const data = { + 'hospital_id' : 1, + 'id' : notification.id, + }; + if(notification.isUnRead) + { + axios + .post('set-read-notification', data) + .then((response) => { + enqueueSnackbar(response.data.meta.message, {variant : "success"}); + }) + .catch((error) => { + enqueueSnackbar(error.response.data.meta.message, {variant : "error"}); + }); + } + }; return ( - {/* - */} + + {/* */} diff --git a/resources/lang/en/Message.php b/resources/lang/en/Message.php index b94896ea..0cd2443f 100644 --- a/resources/lang/en/Message.php +++ b/resources/lang/en/Message.php @@ -4,5 +4,6 @@ return [ 'success' => 'Request has been successfully processed.', 'server_error' => 'Internal server error.', 'not_found' => 'Data not found', - 'password' => 'Password wrong. Please try again.' + 'password' => 'Password wrong. Please try again.', + 'read_notification' => 'Notification has been read.', ]; diff --git a/resources/lang/id/Message.php b/resources/lang/id/Message.php index 18c0f99f..61ed1c80 100644 --- a/resources/lang/id/Message.php +++ b/resources/lang/id/Message.php @@ -4,5 +4,6 @@ return [ 'success' => 'Request berhasil dilakukan.', 'server_error' => 'Internal server error.', 'not_found' => 'Data tidak ditemukan.', - 'password' => 'Password salah. Silakan coba lagi.' + 'password' => 'Password salah. Silakan coba lagi.', + 'read_notification' => 'Notifikasi telah dibaca.', ];