Notification Hospital Portal
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -244,4 +244,14 @@ class Helper
|
|||||||
return $convertedDate;
|
return $convertedDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function sendNotification()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function sendEmail()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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.',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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.',
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user