Notification Hospital Portal

This commit is contained in:
ivan-sim
2023-11-20 16:36:27 +07:00
parent 13ef733010
commit 676e541385
12 changed files with 238 additions and 28 deletions

View File

@@ -27,8 +27,8 @@ class MemberController extends Controller
'no_polis' => 'required', 'no_polis' => 'required',
'birth_date' => 'required' 'birth_date' => 'required'
], [ ], [
'no_polis.required' => trans('validation.required',['attribute' => 'Member ID']), 'no_polis.required' => trans('Validation.required',['attribute' => 'Member ID']),
'birth_date.required' => trans('validation.required',['attribute' => 'Birth Date']), 'birth_date.required' => trans('Validation.required',['attribute' => 'Birth Date']),
]); ]);
if ($validator->fails()) if ($validator->fails())
{ {
@@ -73,7 +73,7 @@ class MemberController extends Controller
$res_data['services'] = $services; $res_data['services'] = $services;
return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200); return ApiResponse::apiResponse("Success", $res_data, trans('Message.success'), 200);
} }
} }
} }

View File

@@ -0,0 +1,86 @@
<?php
namespace Modules\HospitalPortal\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Models\Claim;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\HospitalPortal\Helpers\ApiResponse;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
class NotificationController extends Controller
{
public function getNotifications(Request $request, $hospital_id)
{
$data = [
'hospital_id' => $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);
}
}
}
}

View File

@@ -27,11 +27,11 @@ class Authorization
// Add language // Add language
if(!$locale) 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') 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') if ($locale === 'en-US')
{ {
@@ -46,25 +46,25 @@ class Authorization
// Validate authorization // Validate authorization
if (empty($authorization) || strpos($authorization, 'Bearer ') !== 0) { 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 // Validate type accept & content type
if (!$acceptHeader) 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') 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); return $next($request);
} }

View File

@@ -5,6 +5,7 @@ use Modules\HospitalPortal\Http\Controllers\Api\AuthController;
use Modules\HospitalPortal\Http\Controllers\Api\ClaimRequestController; use Modules\HospitalPortal\Http\Controllers\Api\ClaimRequestController;
use Modules\HospitalPortal\Http\Controllers\Api\MemberController; use Modules\HospitalPortal\Http\Controllers\Api\MemberController;
use Modules\HospitalPortal\Http\Controllers\ClaimController; use Modules\HospitalPortal\Http\Controllers\ClaimController;
use Modules\HospitalPortal\Http\Controllers\Api\NotificationController;
use Modules\HospitalPortal\Http\Middleware\Authentication; use Modules\HospitalPortal\Http\Middleware\Authentication;
use Modules\HospitalPortal\Http\Middleware\Authorization; use Modules\HospitalPortal\Http\Middleware\Authorization;
@@ -42,11 +43,18 @@ Route::prefix('v1')->group(function() {
Route::get('claims', [ClaimController::class, 'index']); Route::get('claims', [ClaimController::class, 'index']);
Route::middleware(Authorization::class)->group(function () { Route::middleware(Authorization::class)->group(function () {
//Search Member
Route::controller(MemberController::class)->group(function () { Route::controller(MemberController::class)->group(function () {
Route::post('search-member', 'search'); 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::get('claim-requests', [ClaimRequestController::class, 'index'])->name('claim-requests.index');
Route::post('claim-requests', [ClaimRequestController::class, 'store'])->name('claim-requests.store'); 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'); Route::get('claim-requests/{claim_request_id}/log', [ClaimRequestController::class, 'generateLog'])->name('claim-requests.generate-log');

View File

@@ -244,4 +244,14 @@ class Helper
return $convertedDate; return $convertedDate;
} }
public static function sendNotification()
{
}
public static function sendEmail()
{
}
} }

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('notifications', function (Blueprint $table) {
$table->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');
}
};

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('notification_types', function (Blueprint $table) {
$table->id();
$table->string('type', 255);
$table->string('description', 255);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('notification_types');
}
};

View File

@@ -39,6 +39,7 @@ export default function LanguagePopover() {
const handleChangeLanguage = (language) => { const handleChangeLanguage = (language) => {
localStorage.setItem('currentLocale', language); localStorage.setItem('currentLocale', language);
setCurrentLocale(language); setCurrentLocale(language);
window.location.reload();
}; };
return ( return (

View File

@@ -1,5 +1,5 @@
import { noCase } from 'change-case'; import { noCase } from 'change-case';
import { useState } from 'react'; import { useState, useEffect } from 'react';
// @mui // @mui
import { import {
Box, Box,
@@ -18,19 +18,32 @@ import {
// utils // utils
import { fToNow } from '@/utils/formatTime'; import { fToNow } from '@/utils/formatTime';
// _mock_ // _mock_
import { _notifications } from '@/_mock'; //import { _notifications } from '@/_mock';
// components // components
import Iconify from '@/components/Iconify'; import Iconify from '@/components/Iconify';
import Scrollbar from '@/components/Scrollbar'; import Scrollbar from '@/components/Scrollbar';
import MenuPopover from '@/components/MenuPopover'; import MenuPopover from '@/components/MenuPopover';
import { IconButtonAnimate } from '@/components/animate'; import { IconButtonAnimate } from '@/components/animate';
import axios from '@/utils/axios';
import { useSnackbar } from 'notistack';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export default function NotificationsPopover() { 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<HTMLElement | null>(null); const [open, setOpen] = useState<HTMLElement | null>(null);
@@ -46,7 +59,7 @@ export default function NotificationsPopover() {
setNotifications( setNotifications(
notifications.map((notification) => ({ notifications.map((notification) => ({
...notification, ...notification,
isUnRead: false, isUnRead: 0,
})) }))
); );
}; };
@@ -98,7 +111,7 @@ export default function NotificationsPopover() {
} }
> >
{notifications.slice(0, 2).map((notification) => ( {notifications.slice(0, 2).map((notification) => (
<NotificationItem key={notification.id} notification={notification} /> <NotificationItem key={notification.id} notification={notification} onClick={notification.id}/>
))} ))}
</List> </List>
@@ -111,18 +124,18 @@ export default function NotificationsPopover() {
} }
> >
{notifications.slice(2, 5).map((notification) => ( {notifications.slice(2, 5).map((notification) => (
<NotificationItem key={notification.id} notification={notification} /> <NotificationItem key={notification.id} notification={notification} onClick={notification.id}/>
))} ))}
</List> </List>
</Scrollbar> </Scrollbar>
<Divider sx={{ borderStyle: 'dashed' }} /> <Divider sx={{ borderStyle: 'dashed' }} />
<Box sx={{ p: 1 }}> {/* <Box sx={{ p: 1 }}>
<Button fullWidth disableRipple> <Button fullWidth disableRipple>
View All View All
</Button> </Button>
</Box> </Box> */}
</MenuPopover> </MenuPopover>
</> </>
); );
@@ -140,11 +153,30 @@ type NotificationItemProps = {
isUnRead: boolean; isUnRead: boolean;
}; };
function NotificationItem({ notification }: { notification: NotificationItemProps }) { function NotificationItem({ notification, onClick }: { notification: NotificationItemProps, onClick: (id: string) => void}) {
const { avatar, title } = renderContent(notification); 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 ( return (
<ListItemButton <ListItemButton
onClick={handleClick}
sx={{ sx={{
py: 1.5, py: 1.5,
px: 2.5, px: 2.5,
@@ -212,7 +244,7 @@ function renderContent(notification: NotificationItemProps) {
title, title,
}; };
} }
if (notification.type === 'mail') { if (notification.type === 'mail' || notification.type === 'request_document') {
return { return {
avatar: ( avatar: (
<img <img

View File

@@ -93,8 +93,8 @@ export default function DashboardHeader({
<Stack direction="row" alignItems="center" spacing={{ xs: 0.5, sm: 1.5 }}> <Stack direction="row" alignItems="center" spacing={{ xs: 0.5, sm: 1.5 }}>
<LanguagePopover /> <LanguagePopover />
{/*<NotificationsPopover /> <NotificationsPopover />
<ContactsPopover />*/} {/* <ContactsPopover /> */}
<AccountPopover /> <AccountPopover />
</Stack> </Stack>
</Toolbar> </Toolbar>

View File

@@ -4,5 +4,6 @@ return [
'success' => 'Request has been successfully processed.', 'success' => 'Request has been successfully processed.',
'server_error' => 'Internal server error.', 'server_error' => 'Internal server error.',
'not_found' => 'Data not found', 'not_found' => 'Data not found',
'password' => 'Password wrong. Please try again.' 'password' => 'Password wrong. Please try again.',
'read_notification' => 'Notification has been read.',
]; ];

View File

@@ -4,5 +4,6 @@ return [
'success' => 'Request berhasil dilakukan.', 'success' => 'Request berhasil dilakukan.',
'server_error' => 'Internal server error.', 'server_error' => 'Internal server error.',
'not_found' => 'Data tidak ditemukan.', 'not_found' => 'Data tidak ditemukan.',
'password' => 'Password salah. Silakan coba lagi.' 'password' => 'Password salah. Silakan coba lagi.',
'read_notification' => 'Notifikasi telah dibaca.',
]; ];