From 4c8570b5426145658cf49679d43d24b8257b343c Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 20 Jul 2023 11:51:44 +0700 Subject: [PATCH] Update validation, dll Hospital-Portal --- .../HospitalPortal/Helpers/ApiResponse.php | 21 ++++ .../Http/Controllers/Api/AuthController.php | 45 ++++++--- .../Http/Controllers/Api/MemberController.php | 49 ++++++---- .../Http/Middleware/Authentication.php | 65 +++++++++++++ .../Http/Middleware/Authorization.php | 71 ++++++++++++++ Modules/HospitalPortal/Routes/api.php | 50 ++++++---- app/Http/Middleware/Authenticate.php | 11 +++ frontend/hospital-portal/.env.development | 2 +- .../public/icons/ic_flag_en.svg | 18 ++++ .../public/icons/ic_flag_id.svg | 9 ++ .../hospital-portal/public/lang/en-US.json | 15 +++ .../hospital-portal/public/lang/id-ID.json | 15 +++ .../hospital-portal/src/LocalizationUtil.ts | 7 ++ .../src/contexts/LanguageContext.tsx | 38 ++++++++ .../src/contexts/LaravelAuthContext.tsx | 12 ++- frontend/hospital-portal/src/index.tsx | 5 +- .../dashboard/header/LanguagePopover.tsx | 71 ++++++++------ .../src/layouts/dashboard/header/index.tsx | 4 +- .../hospital-portal/src/pages/Dashboard.tsx | 14 +-- .../hospital-portal/src/pages/auth/Login.tsx | 22 +++-- .../src/sections/auth/login/LoginForm.tsx | 20 ++-- .../sections/dashboard/CardSearchMember.tsx | 28 +++--- .../src/sections/dashboard/DialogMember.tsx | 96 +++++++++++++++++-- frontend/hospital-portal/src/utils/token.ts | 6 ++ resources/lang/en/Message.php | 8 ++ resources/lang/en/Validation.php | 8 ++ resources/lang/id/Message.php | 8 ++ resources/lang/id/Validation.php | 8 ++ 28 files changed, 598 insertions(+), 128 deletions(-) create mode 100644 Modules/HospitalPortal/Helpers/ApiResponse.php create mode 100644 Modules/HospitalPortal/Http/Middleware/Authentication.php create mode 100644 Modules/HospitalPortal/Http/Middleware/Authorization.php create mode 100644 frontend/hospital-portal/public/icons/ic_flag_en.svg create mode 100644 frontend/hospital-portal/public/icons/ic_flag_id.svg create mode 100644 frontend/hospital-portal/public/lang/en-US.json create mode 100644 frontend/hospital-portal/public/lang/id-ID.json create mode 100644 frontend/hospital-portal/src/LocalizationUtil.ts create mode 100644 frontend/hospital-portal/src/contexts/LanguageContext.tsx create mode 100644 resources/lang/en/Message.php create mode 100644 resources/lang/en/Validation.php create mode 100644 resources/lang/id/Message.php create mode 100644 resources/lang/id/Validation.php diff --git a/Modules/HospitalPortal/Helpers/ApiResponse.php b/Modules/HospitalPortal/Helpers/ApiResponse.php new file mode 100644 index 00000000..d24cbca7 --- /dev/null +++ b/Modules/HospitalPortal/Helpers/ApiResponse.php @@ -0,0 +1,21 @@ +first(); + } + return response()->json([ + 'meta' => [ + 'status' => $status, + 'code' => $statusCode, + 'message' => $message + ], + 'data' => $data, + ], $statusCode); + } +} \ No newline at end of file diff --git a/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php b/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php index a9a54aaf..bf221968 100644 --- a/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php +++ b/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php @@ -12,33 +12,48 @@ use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Mail; use Modules\Internal\Emails\SendVerifyEmail; use Modules\Internal\Events\ForgetPassword; +use Illuminate\Support\Facades\Validator; +use Modules\HospitalPortal\Helpers\ApiResponse; class AuthController extends Controller { public function login(Request $request) { - $request->validate([ + $data = [ + 'email' => $request->email, + 'password' => $request->password + ]; + $validator = Validator::make($request->all(), [ 'email' => 'required|email', 'password' => 'required' + ], [ + 'email.required' => trans('validation.required',['attribute' => 'Email']), + 'email.email' => trans('validation.email'), + 'password.required' => trans('validation.required',['attribute' => 'Password']), ]); - $user = User::query() - ->where('email', $request->email) - ->first(); - - if (!$user) { - return response(['message' => 'User Tidak Ditemukan'], 404); + if ($validator->fails()) + { + return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); } + else + { + $user = User::where('email', $request->email)->first(); + if (!$user) { + return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404); + } - if (!Hash::check($request->password, $user->password)) { - return response(['message' => 'Password Salah'], 403); + if (!Hash::check($request->password, $user->password)) { + return ApiResponse::apiResponse('Bad Request', $data, trans('message.password'), 400); + } + + $res_data = [ + 'user' => $user, + 'token' => $user->createToken('app')->plainTextToken + ]; + + return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200); } - - return response([ - 'message' => 'Selamat Datang', - 'user' => $user, - 'token' => $user->createToken('app')->plainTextToken - ]); } public function logout(Request $request) diff --git a/Modules/HospitalPortal/Http/Controllers/Api/MemberController.php b/Modules/HospitalPortal/Http/Controllers/Api/MemberController.php index 7cab7248..de33a43a 100644 --- a/Modules/HospitalPortal/Http/Controllers/Api/MemberController.php +++ b/Modules/HospitalPortal/Http/Controllers/Api/MemberController.php @@ -7,6 +7,8 @@ use App\Models\Member; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Validator; +use Modules\HospitalPortal\Helpers\ApiResponse; class MemberController extends Controller { @@ -16,26 +18,37 @@ class MemberController extends Controller */ public function search(Request $request) { - $request->validate([ + $data = [ + 'no_polis' => $request->no_polis, + 'birth_date' => $request->birth_date + ]; + $validator = Validator::make($request->all(), [ 'no_polis' => 'required', 'birth_date' => 'required' + ], [ + 'no_polis.required' => trans('validation.required',['attribute' => 'Member ID']), + 'birth_date.required' => trans('validation.required',['attribute' => 'Birth Date']), ]); - - $member = Member::query() - ->where('member_id', $request->no_polis) - ->where('birth_date', $request->birth_date) - ->with(['person', 'currentCorporate', - // 'currentCorporate.corporateServices' => function ($corporateService) { - // $corporateService->where('status', 'active'); - // }, - // 'currentCorporate.corporateServices.service' - // 'currentPlan.benefits', - // 'currentPlan.corporateBenefit.plan', - 'currentPlan.corporateBenefits.benefit' - ]) - ->firstOrFail(); - - - return Helper::responseJson($member); + if ($validator->fails()) + { + return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); + } + else + { + $res_data = Member::query() + ->where('member_id', $request->no_polis) + ->where('birth_date', $request->birth_date) + ->with(['person', 'currentCorporate', + // 'currentCorporate.corporateServices' => function ($corporateService) { + // $corporateService->where('status', 'active'); + // }, + // 'currentCorporate.corporateServices.service' + // 'currentPlan.benefits', + // 'currentPlan.corporateBenefit.plan', + 'currentPlan.corporateBenefits.benefit' + ]) + ->firstOrFail(); + return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200); + } } } diff --git a/Modules/HospitalPortal/Http/Middleware/Authentication.php b/Modules/HospitalPortal/Http/Middleware/Authentication.php new file mode 100644 index 00000000..a916db9e --- /dev/null +++ b/Modules/HospitalPortal/Http/Middleware/Authentication.php @@ -0,0 +1,65 @@ +header('Accept'); + $contentType = $request->header('Content-Type'); + $locale = $request->header('Accept-Language'); + + // Add language + if(!$locale) + { + 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); + } + if ($locale === 'en-US') + { + App::setLocale('en'); + } elseif ($locale === 'id-ID') + { + App::setLocale('id'); + } else + { + App::setLocale('en'); + } + + // Validate type accept & content type + if (!$acceptHeader) + { + return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Accept']), 401); + } + if (!$contentType) + { + 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); + } + if($contentType !== 'application/json') + { + return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Content-Type']), 400); + } + return $next($request); + } +} diff --git a/Modules/HospitalPortal/Http/Middleware/Authorization.php b/Modules/HospitalPortal/Http/Middleware/Authorization.php new file mode 100644 index 00000000..430258bb --- /dev/null +++ b/Modules/HospitalPortal/Http/Middleware/Authorization.php @@ -0,0 +1,71 @@ +header('Accept'); + $contentType = $request->header('Content-Type'); + $locale = $request->header('Accept-Language'); + $authorization = $request->header('Authorization'); + + // Add language + if(!$locale) + { + 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); + } + if ($locale === 'en-US') + { + App::setLocale('en'); + } elseif ($locale === 'id-ID') + { + App::setLocale('id'); + } else + { + App::setLocale('en'); + } + + // Validate authorization + if (empty($authorization) || strpos($authorization, 'Bearer ') !== 0) { + 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); + } + if (!$contentType) + { + 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); + } + if($contentType !== 'application/json') + { + 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 b3599848..3ff6dc32 100644 --- a/Modules/HospitalPortal/Routes/api.php +++ b/Modules/HospitalPortal/Routes/api.php @@ -5,6 +5,8 @@ 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\Middleware\Authentication; +use Modules\HospitalPortal\Http\Middleware\Authorization; /* |-------------------------------------------------------------------------- @@ -16,29 +18,39 @@ use Modules\HospitalPortal\Http\Controllers\ClaimController; | is assigned the "api" middleware group. Enjoy building your API! | */ +Route::prefix('v1')->group(function() { + Route::prefix('hospitalportal')->group(function () { -Route::prefix('hospitalportal')->group(function () { - - Route::post('login', [AuthController::class, 'login'])->name('login'); - Route::post('forget-password', [AuthController::class, 'forgetPassword'])->name('forget-password'); - Route::post('verify-email', [AuthController::class, 'verifyEmail'])->name('verify-email'); - - - Route::middleware('auth:sanctum')->group(function () { - - Route::post('logout', [AuthController::class, 'logout'])->name('logout'); - Route::get('/user', function (Request $request) { - return $request->user(); + Route::middleware(Authentication::class)->group(function () { + Route::controller(AuthController::class)->group(function () { + Route::post('login', 'login'); + }); }); - Route::put('reset-password', [AuthController::class, 'resetPassword'])->name('resetPassword'); + + //Route::post('forget-password', [AuthController::class, 'forgetPassword'])->name('forget-password'); + //Route::post('verify-email', [AuthController::class, 'verifyEmail'])->name('verify-email'); - Route::get('claims', [ClaimController::class, 'index']); - Route::post('search-member', [MemberController::class, 'search']); + Route::middleware('auth:sanctum')->group(function () { - 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'); - Route::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show'); + Route::post('logout', [AuthController::class, 'logout'])->name('logout'); + Route::get('/user', function (Request $request) { + return $request->user(); + }); + Route::put('reset-password', [AuthController::class, 'resetPassword'])->name('resetPassword'); + + Route::get('claims', [ClaimController::class, 'index']); + + Route::middleware(Authorization::class)->group(function () { + Route::controller(MemberController::class)->group(function () { + Route::post('search-member', 'search'); + }); + }); + + 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'); + Route::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show'); + }); }); }); diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 704089a7..46888585 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -2,7 +2,9 @@ namespace App\Http\Middleware; +use Closure; use Illuminate\Auth\Middleware\Authenticate as Middleware; +use Illuminate\Support\Facades\Auth; class Authenticate extends Middleware { @@ -18,4 +20,13 @@ class Authenticate extends Middleware return route('login'); } } + + public function handle($request, Closure $next, ...$guards) + { + if (Auth::guard('sanctum')->guest()) { + return response()->json(['error' => 'Bearer Authorization is required'], 401); + } + + return parent::handle($request, $next, ...$guards); + } } diff --git a/frontend/hospital-portal/.env.development b/frontend/hospital-portal/.env.development index 1256ddfc..5be659fc 100644 --- a/frontend/hospital-portal/.env.development +++ b/frontend/hospital-portal/.env.development @@ -1,4 +1,4 @@ REACT_APP_HOST_API_URL="http://localhost:8000" -VITE_API_URL="http://localhost:8000/api/hospitalportal" +VITE_API_URL="http://localhost:8000/api/v1/hospitalportal" diff --git a/frontend/hospital-portal/public/icons/ic_flag_en.svg b/frontend/hospital-portal/public/icons/ic_flag_en.svg new file mode 100644 index 00000000..55e243b2 --- /dev/null +++ b/frontend/hospital-portal/public/icons/ic_flag_en.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/hospital-portal/public/icons/ic_flag_id.svg b/frontend/hospital-portal/public/icons/ic_flag_id.svg new file mode 100644 index 00000000..1d4e5d5b --- /dev/null +++ b/frontend/hospital-portal/public/icons/ic_flag_id.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/frontend/hospital-portal/public/lang/en-US.json b/frontend/hospital-portal/public/lang/en-US.json new file mode 100644 index 00000000..baef7538 --- /dev/null +++ b/frontend/hospital-portal/public/lang/en-US.json @@ -0,0 +1,15 @@ +{ + "greeting": "Hello", + "buttonText": "Click Me", + "infoLogin": "Enter the registered account", + "txtLogin1" : "Sign in to Hospital Portal", + "txtLogin2" : "Enter your details below", + "txtCardSearchMember1" : "Guarantee Submission", + "txtCardSearchMember2" : "Find Member", + "txtCardSearchMember3" : "Date Birth", + "txtCardSearchMember4" : "Member ID", + "txtCardSearchMember5" : "Member", + "txtDialogMember1" : "Benefit Summary", + "txtDialogMember2" : "Request LOG", + "txtDialogMember3" : "Member Detail" +} diff --git a/frontend/hospital-portal/public/lang/id-ID.json b/frontend/hospital-portal/public/lang/id-ID.json new file mode 100644 index 00000000..41db3e54 --- /dev/null +++ b/frontend/hospital-portal/public/lang/id-ID.json @@ -0,0 +1,15 @@ +{ + "greeting": "Halo", + "buttonText": "Klik Saya", + "infoLogin": "Masukan akun yang telah terdaftar", + "txtLogin1" : "Masuk ke Hospital Portal", + "txtLogin2" : "Masukkan detail Anda di bawah ini", + "txtCardSearchMember1" : "Pengajuan Jaminan", + "txtCardSearchMember2" : "Cari Anggota", + "txtCardSearchMember3" : "Tanggal Lahir", + "txtCardSearchMember4" : "Member ID", + "txtCardSearchMember5" : "Member", + "txtDialogMember1" : "Ringkasan Manfaat", + "txtDialogMember2" : "Request LOG", + "txtDialogMember3" : "Detail Member" +} diff --git a/frontend/hospital-portal/src/LocalizationUtil.ts b/frontend/hospital-portal/src/LocalizationUtil.ts new file mode 100644 index 00000000..c7fa3520 --- /dev/null +++ b/frontend/hospital-portal/src/LocalizationUtil.ts @@ -0,0 +1,7 @@ +const getLocalizedData = async (locale) => { + const response = await fetch(`../public/lang/${locale}.json`); // Mengambil file lokal berdasarkan bahasa yang dipilih + const data = await response.json(); + return data; +}; + +export default getLocalizedData; \ No newline at end of file diff --git a/frontend/hospital-portal/src/contexts/LanguageContext.tsx b/frontend/hospital-portal/src/contexts/LanguageContext.tsx new file mode 100644 index 00000000..b8cce590 --- /dev/null +++ b/frontend/hospital-portal/src/contexts/LanguageContext.tsx @@ -0,0 +1,38 @@ +import React, { createContext, useState, useEffect, useRef } from 'react'; +import getLocalizedData from '../LocalizationUtil'; +export const LanguageContext = createContext(); + +export const LanguageProvider = ({ children }) => { + const [currentLocale, setCurrentLocale] = useState(localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID'); + const [localeData, setLocaleData] = useState('id'); + const cancelToken = useRef(null); + + useEffect(() => { + const fetchData = async () => { + const token = { cancelled: false }; + cancelToken.current = token; + + try { + const data = await getLocalizedData(currentLocale); + if (!cancelToken.current.cancelled) { + setLocaleData(data); + } + } catch (error) { + console.error('Error fetching localized data:', error); + // Tangani kesalahan jika diperlukan + } + }; + + fetchData(); + + return () => { + cancelToken.current.cancelled = true; + }; + }, [currentLocale]); + + return ( + + {children} + + ); +}; diff --git a/frontend/hospital-portal/src/contexts/LaravelAuthContext.tsx b/frontend/hospital-portal/src/contexts/LaravelAuthContext.tsx index 08c3eef8..c4ad4438 100644 --- a/frontend/hospital-portal/src/contexts/LaravelAuthContext.tsx +++ b/frontend/hospital-portal/src/contexts/LaravelAuthContext.tsx @@ -122,10 +122,18 @@ function AuthProvider({ children }: AuthProviderProps) { initialize(); }, []); + const headers = { + headers: { + 'Accept': 'application/json', + 'Content-Type' : 'application/json', + 'Accept-Language': (localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID'), + }, + }; + const login = async (email: string, password: string) => axios - .post('/login', { email, password }) + .post('/login', { email, password }, headers) .then((response) => { - const { user, token } = response.data; + const { user, token } = response.data.data; setSession(token); dispatch({ diff --git a/frontend/hospital-portal/src/index.tsx b/frontend/hospital-portal/src/index.tsx index ee6d44e7..52ea5b45 100644 --- a/frontend/hospital-portal/src/index.tsx +++ b/frontend/hospital-portal/src/index.tsx @@ -12,6 +12,7 @@ import { HelmetProvider } from 'react-helmet-async'; // contexts import { SettingsProvider } from './contexts/SettingsContext'; import { CollapseDrawerProvider } from './contexts/CollapseDrawerContext'; +import { LanguageProvider } from './contexts/LanguageContext'; // import App from './App'; @@ -23,7 +24,9 @@ ReactDOM.render( - + + + diff --git a/frontend/hospital-portal/src/layouts/dashboard/header/LanguagePopover.tsx b/frontend/hospital-portal/src/layouts/dashboard/header/LanguagePopover.tsx index 7b78ee18..a5e14ad6 100644 --- a/frontend/hospital-portal/src/layouts/dashboard/header/LanguagePopover.tsx +++ b/frontend/hospital-portal/src/layouts/dashboard/header/LanguagePopover.tsx @@ -1,28 +1,24 @@ -import { useState } from 'react'; +import React, { useState, useContext } from 'react'; // @mui -import { MenuItem, Stack } from '@mui/material'; +import { ButtonBase, Box, Typography, MenuItem, Stack } from '@mui/material'; // components import Image from '@/components/Image'; import MenuPopover from '@/components/MenuPopover'; import { IconButtonAnimate } from '@/components/animate'; +import { LanguageContext } from '@/contexts/LanguageContext'; // ---------------------------------------------------------------------- const LANGS = [ + { + label: 'Bahasa Indonesia', + value: 'id-ID', + icon: '/icons/ic_flag_id.svg', + }, { label: 'English', - value: 'en', - icon: 'https://minimal-assets-api.vercel.app/assets/icons/ic_flag_en.svg', - }, - { - label: 'German', - value: 'de', - icon: 'https://minimal-assets-api.vercel.app/assets/icons/ic_flag_de.svg', - }, - { - label: 'French', - value: 'fr', - icon: 'https://minimal-assets-api.vercel.app/assets/icons/ic_flag_fr.svg', + value: 'en-US', + icon: '/icons/ic_flag_en.svg', }, ]; @@ -39,18 +35,34 @@ export default function LanguagePopover() { setOpen(null); }; + const { setCurrentLocale } = useContext(LanguageContext); + const handleChangeLanguage = (language) => { + localStorage.setItem('currentLocale', language); + setCurrentLocale(language); + }; + return ( <> - - {LANGS[0].label} - + + + + {!localStorage.getItem('currentLocale') + + + + + + Language + + + ( { + handleClose(); + handleChangeLanguage(option.value); + }} > {option.label} + {option.label} + ))} diff --git a/frontend/hospital-portal/src/layouts/dashboard/header/index.tsx b/frontend/hospital-portal/src/layouts/dashboard/header/index.tsx index 156125b4..a79e2f3f 100644 --- a/frontend/hospital-portal/src/layouts/dashboard/header/index.tsx +++ b/frontend/hospital-portal/src/layouts/dashboard/header/index.tsx @@ -93,8 +93,8 @@ export default function DashboardHeader({ - - + {/* + */} diff --git a/frontend/hospital-portal/src/pages/Dashboard.tsx b/frontend/hospital-portal/src/pages/Dashboard.tsx index c394df27..ab771023 100644 --- a/frontend/hospital-portal/src/pages/Dashboard.tsx +++ b/frontend/hospital-portal/src/pages/Dashboard.tsx @@ -20,12 +20,12 @@ import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim'; // const [notifications, setNotifications] = useState([]) -// const itemList = [ -// // { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '08:00 WIB' }, -// // { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '09:00 WIB' }, -// // { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '10:00 WIB' }, -// // { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '11:00 WIB' }, -// ]; +const itemList = [ + { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '08:00 WIB' }, + { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '09:00 WIB' }, + { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '10:00 WIB' }, + { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '11:00 WIB' }, +]; // ---------------------------------------------------------------------- @@ -66,7 +66,7 @@ export default function Dashboard() { const [policyData, setPolicyData] = useState(defaultPolicyData); // TODO Remove This - const [itemList, setItemList] = useState([]); + //const [itemList, setItemList] = useState([]); function handleDataLoaded(dataTable) { let dummyData = []; dataTable.map(function(data) { diff --git a/frontend/hospital-portal/src/pages/auth/Login.tsx b/frontend/hospital-portal/src/pages/auth/Login.tsx index 4ab4f315..9f4369f9 100644 --- a/frontend/hospital-portal/src/pages/auth/Login.tsx +++ b/frontend/hospital-portal/src/pages/auth/Login.tsx @@ -1,3 +1,4 @@ +import React, { useContext, useRef, useState, useEffect } from 'react'; import { capitalCase } from "change-case"; import { Link as RouterLink } from "react-router-dom"; // @mui @@ -23,6 +24,7 @@ import Logo from "@/components/Logo"; import Image from "@/components/Image"; // sections import { LoginForm } from "@/sections/auth/login"; +import { LanguageContext } from '@/contexts/LanguageContext'; // ---------------------------------------------------------------------- @@ -70,6 +72,7 @@ const ContentStyle = styled("div")(({ theme }) => ({ // ---------------------------------------------------------------------- export default function Login() { + const { localeData } = useContext(LanguageContext); const { method } = useAuth(); const smUp = useResponsive("up", "sm"); @@ -80,7 +83,7 @@ export default function Login() { - + {/* {smUp && ( Has problem with your account? {""} @@ -97,7 +100,7 @@ export default function Login() { Contact Us - )} + )}*/} {/* {mdUp && ( @@ -121,16 +124,17 @@ export default function Login() { alignItems="center" sx={{ mb: 5 }} > + - Sign in to LinkSehat + {localeData.txtLogin1} - - Enter your details below. + + {localeData.txtLogin2} - @@ -141,12 +145,12 @@ export default function Login() { sx={{ width: 32, height: 32 }} /> - + */} - {false && !smUp && ( + {/*{false && !smUp && ( - )} + )}*/} diff --git a/frontend/hospital-portal/src/sections/auth/login/LoginForm.tsx b/frontend/hospital-portal/src/sections/auth/login/LoginForm.tsx index 42ddfac4..b19f8aa3 100644 --- a/frontend/hospital-portal/src/sections/auth/login/LoginForm.tsx +++ b/frontend/hospital-portal/src/sections/auth/login/LoginForm.tsx @@ -1,5 +1,5 @@ import * as Yup from 'yup'; -import { useState } from 'react'; +import React, { useContext, useRef, useState, useEffect } from 'react'; import { Link as RouterLink, useNavigate } from 'react-router-dom'; // form import { useForm } from 'react-hook-form'; @@ -15,6 +15,7 @@ import useIsMountedRef from '@/hooks/useIsMountedRef'; // components import Iconify from '@/components/Iconify'; import { FormProvider, RHFTextField, RHFCheckbox } from '@/components/hook-form'; +import { LanguageContext } from '@/contexts/LanguageContext'; // ---------------------------------------------------------------------- @@ -26,6 +27,7 @@ type FormValuesProps = { }; export default function LoginForm() { + const { localeData } = useContext(LanguageContext); const { login } = useAuth(); const navigate = useNavigate(); @@ -34,8 +36,8 @@ export default function LoginForm() { const [showPassword, setShowPassword] = useState(false); const LoginSchema = Yup.object().shape({ - email: Yup.string().email('Email must be a valid email address').required('Email is required'), - password: Yup.string().required('Password is required'), + email: Yup.string().email('Format email tidak valid').required('Email harus diisi'), + password: Yup.string().required('Password harus diisi'), }); const defaultValues = { @@ -75,10 +77,10 @@ export default function LoginForm() { return ( - Email : admin@linksehat.dev & Password : password - {!!errors.afterSubmit && {errors.afterSubmit.message}} + {localeData.infoLogin} + {!!errors.afterSubmit && {errors.afterSubmit.data.meta.message}} - + ), }} + required /> - + {/* Forgot password? - + */} Login diff --git a/frontend/hospital-portal/src/sections/dashboard/CardSearchMember.tsx b/frontend/hospital-portal/src/sections/dashboard/CardSearchMember.tsx index 7a7f7211..5d13683e 100644 --- a/frontend/hospital-portal/src/sections/dashboard/CardSearchMember.tsx +++ b/frontend/hospital-portal/src/sections/dashboard/CardSearchMember.tsx @@ -13,7 +13,7 @@ import { } from '@mui/material'; import { ChevronRight } from '@mui/icons-material'; // React -import React, { useState } from 'react'; +import React, { useContext, useState } from 'react'; import { LoadingButton } from '@mui/lab'; import Iconify from '@/components/Iconify'; import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers'; @@ -25,6 +25,7 @@ import MuiDialog from '@/components/MuiDialog'; import axios from '@/utils/axios'; import { useSnackbar } from 'notistack'; import DialogMember from './DialogMember'; +import { LanguageContext } from '@/contexts/LanguageContext'; // ---------------------------------------------------------------------- @@ -46,29 +47,32 @@ const ItemNotificationStyle = styled(Card)(({ theme }) => ({ // ---------------------------------------------------------------------- export default function CardSearchMember(handleSubmitSuccess) { + const { localeData } = useContext(LanguageContext); const {enqueueSnackbar} = useSnackbar(); const [noPolis, setNoPolis] = useState('AW001-01'); - const [tanggalLahir, setTanggalLahir] = useState('1991-01-10'); + const [birthDate, setBirthDate] = useState('1991-01-10'); const [loadingBenefit, setLoadingBenefit] = useState(false); const [loadingClaim, setLoadingClaim] = useState(false); const [openDialogBenefit, setOpenDialogBenefit] = useState(false); const [openDialogClaim, setOpenDialogClaim] = useState(false); const [currentMember, setCurrentMember] = useState(null); + const [nameMember, setNameMember] = useState(''); function handleSearchMember() { setLoadingBenefit(true) axios.post('/search-member', { no_polis: noPolis, - birth_date: tanggalLahir ? fPostFormat(tanggalLahir, 'yyyy-MM-dd') : null + birth_date: birthDate ? fPostFormat(birthDate, 'yyyy-MM-dd') : null }) .then((response) => { setOpenDialogBenefit(true) setCurrentMember(response.data.data) + setNameMember(response.data.data.name); }) .catch(({response}) => { - enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.message : 'Opps, Something went Wrong!'), {variant : "error"}) + enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.meta.message : 'Opps, Something went Wrong!'), {variant : "error"}) }) .then(() => { setLoadingBenefit(false) @@ -90,23 +94,23 @@ export default function CardSearchMember(handleSubmitSuccess) { component="span" sx={{ display: 'flex', alignItems: 'center' }} > - Pengajuan Jaminan + {localeData.txtCardSearchMember1} - { + { setNoPolis(event.target.value) - }}> + }} required> { setTanggalLahir( (newValue)); }} inputFormat="dd-MM-yyyy" - renderInput={(params) => } + renderInput={(params) => } /> @@ -124,7 +128,7 @@ export default function CardSearchMember(handleSubmitSuccess) { }} > - Cari Member + {localeData.txtCardSearchMember2} {/* */} {setOpenDialogBenefit(false); handleSubmitSuccess()})} diff --git a/frontend/hospital-portal/src/sections/dashboard/DialogMember.tsx b/frontend/hospital-portal/src/sections/dashboard/DialogMember.tsx index 12646730..b2192dcb 100644 --- a/frontend/hospital-portal/src/sections/dashboard/DialogMember.tsx +++ b/frontend/hospital-portal/src/sections/dashboard/DialogMember.tsx @@ -4,20 +4,22 @@ import { LoadingButton, TabPanel } from "@mui/lab"; import { Button, Card, Divider, Grid, LinearProgress, linearProgressClasses, Typography } from "@mui/material"; import { Tab, Tabs } from "@mui/material"; import { Box, Stack } from "@mui/material"; -import { useEffect, useState } from "react"; +import React, { useEffect, useState, useContext } from "react"; import { fCurrency } from '@/utils/formatNumber'; import { fPostFormat } from '@/utils/formatTime'; import { Avatar } from '@mui/material'; import Iconify from '@/components/Iconify'; import FormRequestClaim from './FormRequestClaim'; +import { LanguageContext } from '@/contexts/LanguageContext'; export default function DialogMember(member, handleSubmitSuccess) { + const { localeData } = useContext(LanguageContext); const [currentTab, setCurrentTab] = useState('request') // ---------------------------------------------------------------------- useEffect(() => { - setCurrentTab('benefit') + setCurrentTab('detail') }, [member]) function handleChangeTab(event: React.SyntheticEvent, newValue: string) { @@ -67,13 +69,93 @@ export default function DialogMember(member, handleSubmitSuccess) { onChange={handleChangeTab} aria-label="wrapped label tabs example" > - - + + + + + + + + + + Mapping ID + + + : {1 ?? '-'} + + + Policy Number + + + : {1 ?? '-'} + + + NRIC + + + : {1 ?? '-'} + + + + NIK + + + : {1 ?? '-'} + + + Email + + + : {1 ?? '-'} + + + + + + + Birth Date + + + : {1 ?? '-'} + + + Gender + + + : {1 ?? '-'} + + + Marital Status + + + : {1 ?? '-'} + + + Language + + + : {1 ?? '-'} + + + Race + + + : {1 ?? '-'} + + + Relationship + + + : {1 ?? '-'} + + + + + + + { member && member?.current_plan?.corporate_benefits?.map((corporateBenefit, index) => {return ( diff --git a/frontend/hospital-portal/src/utils/token.ts b/frontend/hospital-portal/src/utils/token.ts index db33ff36..ec74def0 100644 --- a/frontend/hospital-portal/src/utils/token.ts +++ b/frontend/hospital-portal/src/utils/token.ts @@ -29,12 +29,18 @@ const setSession = (accessToken: string | null) => { if (accessToken) { localStorage.setItem('accessToken', accessToken); axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`; + axios.defaults.headers.common['Accept-Language'] = (localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID'); + axios.defaults.headers.common['Accept'] = 'application/json'; + axios.defaults.headers.common['Content-Type'] = 'application/json'; // This function below will handle when token is expired // const { exp } = jwtDecode(accessToken); // handleTokenExpired(exp); } else { localStorage.removeItem('accessToken'); delete axios.defaults.headers.common.Authorization; + delete axios.defaults.headers.common['Accept-Language']; + delete axios.defaults.headers.common['Accept']; + delete axios.defaults.headers.common['Content-Type']; } }; diff --git a/resources/lang/en/Message.php b/resources/lang/en/Message.php new file mode 100644 index 00000000..b94896ea --- /dev/null +++ b/resources/lang/en/Message.php @@ -0,0 +1,8 @@ + 'Request has been successfully processed.', + 'server_error' => 'Internal server error.', + 'not_found' => 'Data not found', + 'password' => 'Password wrong. Please try again.' +]; diff --git a/resources/lang/en/Validation.php b/resources/lang/en/Validation.php new file mode 100644 index 00000000..c5c2b448 --- /dev/null +++ b/resources/lang/en/Validation.php @@ -0,0 +1,8 @@ + 'The :attribute field is required.', + 'invalid' => 'The :attribute field is invalid.', + 'max' => ':attribute cannot exceed :length characters.', + 'email' => 'Invalid email format.' +]; \ No newline at end of file diff --git a/resources/lang/id/Message.php b/resources/lang/id/Message.php new file mode 100644 index 00000000..18c0f99f --- /dev/null +++ b/resources/lang/id/Message.php @@ -0,0 +1,8 @@ + 'Request berhasil dilakukan.', + 'server_error' => 'Internal server error.', + 'not_found' => 'Data tidak ditemukan.', + 'password' => 'Password salah. Silakan coba lagi.' +]; diff --git a/resources/lang/id/Validation.php b/resources/lang/id/Validation.php new file mode 100644 index 00000000..2b2ffa07 --- /dev/null +++ b/resources/lang/id/Validation.php @@ -0,0 +1,8 @@ + 'Kolom :attribute harus diisi.', + 'invalid' => 'Kolom :attribute tidak valid.', + 'max' => ':attribute tidak boleh melebihi :length karakter.', + 'email' => 'Format email salah.' +]; \ No newline at end of file