From 6c2a4dae1ba59b9e19e3707edc17c2f4f36cfcca Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Mon, 20 May 2024 11:33:48 +0700 Subject: [PATCH 01/13] Update Add user hospital portal --- .../Http/Controllers/Api/AuthController.php | 114 ++++++++++++------ Modules/HospitalPortal/Routes/api.php | 12 +- app/Providers/AppServiceProvider.php | 2 +- .../hospital-portal/public/lang/en-US.json | 5 +- .../hospital-portal/public/lang/id-ID.json | 3 +- frontend/hospital-portal/src/@types/auth.ts | 2 +- .../hospital-portal/src/components/Table.tsx | 22 ++-- .../src/contexts/LaravelAuthContext.tsx | 33 ++--- .../dashboard/header/AccountPopover.tsx | 6 +- .../hospital-portal/src/pages/auth/Login.tsx | 21 ++-- frontend/hospital-portal/src/routes/index.tsx | 4 +- frontend/hospital-portal/src/routes/paths.ts | 2 +- .../src/sections/auth/login/LoginForm.tsx | 6 +- frontend/hospital-portal/src/utils/token.ts | 81 +++++++++++-- 14 files changed, 217 insertions(+), 96 deletions(-) diff --git a/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php b/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php index bf221968..44beb6b9 100644 --- a/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php +++ b/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php @@ -14,6 +14,9 @@ use Modules\Internal\Emails\SendVerifyEmail; use Modules\Internal\Events\ForgetPassword; use Illuminate\Support\Facades\Validator; use Modules\HospitalPortal\Helpers\ApiResponse; +use Illuminate\Support\Facades\DB; +use App\Helpers\Helper; +use Illuminate\Support\Facades\View; class AuthController extends Controller { @@ -27,9 +30,9 @@ class AuthController extends Controller 'email' => 'required|email', 'password' => 'required' ], [ - 'email.required' => trans('validation.required',['attribute' => 'Email']), - 'email.email' => trans('validation.email'), - 'password.required' => trans('validation.required',['attribute' => 'Password']), + 'email.required' => trans('Validation.required',['attribute' => 'Email']), + 'email.email' => trans('Validation.email'), + 'password.required' => trans('Validation.required',['attribute' => 'Password']), ]); if ($validator->fails()) @@ -40,11 +43,11 @@ class AuthController extends Controller { $user = User::where('email', $request->email)->first(); if (!$user) { - return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404); + return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); } if (!Hash::check($request->password, $user->password)) { - return ApiResponse::apiResponse('Bad Request', $data, trans('message.password'), 400); + return ApiResponse::apiResponse('Bad Request', $data, trans('Message.password'), 400); } $res_data = [ @@ -52,16 +55,15 @@ class AuthController extends Controller 'token' => $user->createToken('app')->plainTextToken ]; - return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200); + return ApiResponse::apiResponse("Success", $res_data, trans('Message.success'), 200); } } public function logout(Request $request) { - $token = $request->bearerToken(); - Auth::user()->tokens()->where('id', $token)->delete(); + $request->user()->tokens()->delete(); - return response(['message' => 'Berhasil Logout.']); + return ApiResponse::apiResponse('Success', [], trans('Message.logout'), 200); } public function resetPassword(Request $request) @@ -75,12 +77,12 @@ class AuthController extends Controller ]); if (!Hash::check($request['old_password'], $user->password)) { - return response(['message' => 'Password Salah'], 403); + return response(['Message' => 'Password Salah'], 403); } if ($request["new_password"] != $request["confirm_new_password"]) { return response([ - 'message' => "Password Tidak Sama" + 'Message' => "Password Tidak Sama" ]); } @@ -101,7 +103,7 @@ class AuthController extends Controller ->first(); if (!$user) { - return response(['message' => 'User Tidak Ditemukan'], 404); + return response(['Message' => 'User Tidak Ditemukan'], 404); } Event(new ForgetPassword($user)); @@ -111,33 +113,77 @@ class AuthController extends Controller return response()->json($user); } - public function forgetPassword(Request $request) + public function forgotPassword(Request $request) { - $request->validate([ - 'new_password' => 'required', - 'confirm_new_password' => 'required' + $data = [ + 'email' => $request->email, + ]; + + $validator = Validator::make($request->all(), [ + 'email' => 'required|email', + ], [ + 'email.required' => trans('Validation.required',['attribute' => 'Email']), + 'email.email' => trans('Validation.email'), ]); - $token = Crypt::decryptString($request->token); - $email = explode('|', $token)[0]; - - $user = User::query() - ->where('email', $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 ($request["new_password"] != $request["confirm_new_password"]) { - return response([ - 'message' => "Password Tidak Sama" - ], 404); + //send email + // Insert data notifications + $emailTo = $request->email; + $dataNotif = [ + 'user_id' => $user->id, + 'email' => $emailTo, + 'title' => 'Forgot Password', + 'description' => 'Request forgot password from App Doctor', + 'type' => 1, + 'isUnRead' => true, + 'created_by' => auth()->check() ? auth()->user()->id : null, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]; + $sendNotif = Helper::insertNotification($dataNotif); + //Insert data password reset + $token = mt_rand(100000, 999999); // Menghasilkan angka acak antara 100000 dan 999999 + $p_resets = DB::table('password_resets') + ->insert([ + 'email' => $request->email, + 'token' => $token, + 'created_at' => date('Y-m-d H:i:s'), + ]); + // Send Email after insert notifications + if($sendNotif && $p_resets) + { + //send to alarm + $nameTo = 'User'; + $dataEmail = [ + 'email' => $emailTo, + 'name' => $nameTo, + 'subject' => 'Request Forgot Password from App Doctor Date '. date('Y-m-d H:i:s'), + 'body' => View::make('email/forgot_password', ['token' => $token])->render(), + ]; + Helper::sendEmail($dataEmail); + + $res = DB::table('password_resets') + ->where('email', '=', $request->email) + ->where('token', '=', $token) + ->get(); + + return ApiResponse::apiResponse("Success", $res, trans('Message.success'), 200); + } + else + { + return ApiResponse::apiResponse("Internal Server Error", $data, trans('Message.server_error'), 500); + } } - - $user->update([ - 'password' => Hash::make($request->confirm_new_password), - ]); - return response()->json($user); } } diff --git a/Modules/HospitalPortal/Routes/api.php b/Modules/HospitalPortal/Routes/api.php index 3b73f731..b1deb0c8 100644 --- a/Modules/HospitalPortal/Routes/api.php +++ b/Modules/HospitalPortal/Routes/api.php @@ -24,13 +24,15 @@ Route::prefix('v1')->group(function() { Route::prefix('hospitalportal')->group(function () { Route::middleware(Authentication::class)->group(function () { - Route::controller(AuthController::class)->group(function () { - Route::post('login', 'login'); + Route::middleware('switch.db')->group(function () { + Route::controller(AuthController::class)->group(function () { + Route::post('login', 'login'); + }); }); }); - - //Route::post('forget-password', [AuthController::class, 'forgetPassword'])->name('forget-password'); - //Route::post('verify-email', [AuthController::class, 'verifyEmail'])->name('verify-email'); + + Route::post('forgot-password', [AuthController::class, 'forgotPassword']); + // Route::post('verify-email', [AuthController::class, 'verifyEmail'])->name('verify-email'); Route::middleware('auth:sanctum')->group(function () { diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 456eb067..267e7d8e 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -206,7 +206,7 @@ class AppServiceProvider extends ServiceProvider $this->logAuditTrail($model, 'deleted'); }); - Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); + // Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); } private function logAuditTrail($model, $action) diff --git a/frontend/hospital-portal/public/lang/en-US.json b/frontend/hospital-portal/public/lang/en-US.json index 6adf54c1..19a07abf 100644 --- a/frontend/hospital-portal/public/lang/en-US.json +++ b/frontend/hospital-portal/public/lang/en-US.json @@ -14,7 +14,7 @@ "txtDialogMember3" : "Detail", "txtDialogMember4" : "Please select services", "txtDialogMember5" : "Admission Date", - "txtDialogMember6" : "Please select admission date", + "txtDialogMember6" : "Please select admission date", "txtWarningDischargeDate" : "Please select discharge date", "txtCreateAt" : "Create at", "txtDateBirth" : "Date of Birth", @@ -59,5 +59,6 @@ "txtApprove": "Approve", "txtDialogConfirmation": "Are you sure you want to proceed with this action?", "txtStartDate": "Start Date", - "txtEndDate": "End Date" + "txtEndDate": "End Date", + "txtHelp1" : "Has problem with your account?" } diff --git a/frontend/hospital-portal/public/lang/id-ID.json b/frontend/hospital-portal/public/lang/id-ID.json index 5bb34e8e..b2eb32f6 100644 --- a/frontend/hospital-portal/public/lang/id-ID.json +++ b/frontend/hospital-portal/public/lang/id-ID.json @@ -59,5 +59,6 @@ "txtApprove": "Terima", "txtDialogConfirmation": "Apakah Anda yakin ingin melanjutkan tindakan ini?", "txtStartDate": "Tanggal Mulai", - "txtEndDate": "Tanggal Akhir" + "txtEndDate": "Tanggal Akhir", + "txtHelp1" : "Punya masalah dengan akun Anda?" } diff --git a/frontend/hospital-portal/src/@types/auth.ts b/frontend/hospital-portal/src/@types/auth.ts index 3df73c76..9ccd1060 100644 --- a/frontend/hospital-portal/src/@types/auth.ts +++ b/frontend/hospital-portal/src/@types/auth.ts @@ -26,7 +26,7 @@ export type JWTContextType = { isInitialized: boolean; user: AuthUser; method: 'jwt'; - login: (email: string, password: string) => Promise; + login: (email: string, password: string, rememberMe: boolean) => Promise; register: (email: string, password: string, firstName: string, lastName: string) => Promise; logout: () => Promise; }; diff --git a/frontend/hospital-portal/src/components/Table.tsx b/frontend/hospital-portal/src/components/Table.tsx index 7334e85b..d1df63f1 100644 --- a/frontend/hospital-portal/src/components/Table.tsx +++ b/frontend/hospital-portal/src/components/Table.tsx @@ -93,7 +93,7 @@ export default function Table({ ]); params.setAppliedParams(parameters); }; - + const { localeData }: any = useContext(LanguageContext); /* -------------------------------------------------------------------------- */ @@ -106,7 +106,7 @@ export default function Table({ return ( - {selected.useSelected && selected.selectedRows.length > 0 ? ( + {selected.useSelected && selected.selectedRows.length > 0 ? ( <> @@ -169,10 +169,10 @@ export default function Table({ ))} - + )} - - + + ); @@ -294,7 +294,7 @@ export default function Table({ } - + ) : null } @@ -380,7 +380,7 @@ export default function Table({ - ) : null } + ) : null } {/* Export Report */} @@ -389,11 +389,11 @@ export default function Table({ - ) : null } + ) : null } @@ -428,7 +428,7 @@ export default function Table({ ):( - + ))} {headCells && @@ -443,7 +443,7 @@ export default function Table({ )) ) : ( - + {localeData.txtDataNotFound} diff --git a/frontend/hospital-portal/src/contexts/LaravelAuthContext.tsx b/frontend/hospital-portal/src/contexts/LaravelAuthContext.tsx index e5719f70..3371bf04 100644 --- a/frontend/hospital-portal/src/contexts/LaravelAuthContext.tsx +++ b/frontend/hospital-portal/src/contexts/LaravelAuthContext.tsx @@ -2,7 +2,7 @@ import { createContext, ReactNode, useEffect, useReducer } from 'react'; // utils import axios from '@/utils/axios'; // import { isValidToken, setSession } from '@/utils/jwt'; -import { setSession, getSession, setUser, getUser } from '@/utils/token'; +import { setSession, getSession, setUser, getUser, getCookie } from '@/utils/token'; // @types import { ActionMap, AuthState, AuthUser, JWTContextType } from '@/@types/auth'; // ---------------------------------------------------------------------- @@ -86,12 +86,16 @@ function AuthProvider({ children }: AuthProviderProps) { const initialize = async () => { try { const accessToken = getSession(); - if (accessToken) { - setSession(accessToken); + const rememberMe = getCookie('rememberMe') == 'OK' ? false : true; + + if (accessToken) { + const userString = getUser(); + const storedUser = userString ? JSON.parse(userString) : null; + setUser(storedUser, rememberMe); + setSession(accessToken, rememberMe); + const response = await axios.get('/user'); + const user = response.data; - const response = await axios.get('/user'); - const user = response.data; - dispatch({ type: Types.Initial, payload: { @@ -126,16 +130,16 @@ function AuthProvider({ children }: AuthProviderProps) { headers: { 'Accept': 'application/json', 'Content-Type' : 'application/json', - 'Accept-Language': (localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID'), + 'Accept-Language': localStorage.getItem('currentLocale') ?? 'id-ID', }, }; - const login = async (email: string, password: string) => axios + const login = async (email: string, password: string, rememberMe: boolean) => axios .post('/login', { email, password }, headers) .then((response) => { const { user, token } = response.data.data; - setSession(token); - setUser(user); + setSession(token, rememberMe); + setUser(user, rememberMe); dispatch({ type: Types.Login, @@ -168,8 +172,9 @@ function AuthProvider({ children }: AuthProviderProps) { }; const logout = async () => { - setSession(null); - setUser(null); + await axios.post('logout'); + setSession(null, false); + setUser(null, false); dispatch({ type: Types.Logout }); }; @@ -187,9 +192,9 @@ function AuthProvider({ children }: AuthProviderProps) { ); // if (state.isInitialized) { - // return (!state.isAuthenticated && location.pathname !== '/auth/login') ? + // return (!state.isAuthenticated && location.pathname !== '/auth/login') ? // () - // : false && location.pathname == '/auth/login' ? + // : false && location.pathname == '/auth/login' ? // () // : ( // - hospitaladmin@gmail.com + {storedUser?.email} diff --git a/frontend/hospital-portal/src/pages/auth/Login.tsx b/frontend/hospital-portal/src/pages/auth/Login.tsx index debeae42..d2ada8bf 100644 --- a/frontend/hospital-portal/src/pages/auth/Login.tsx +++ b/frontend/hospital-portal/src/pages/auth/Login.tsx @@ -78,29 +78,28 @@ export default function Login() { const smUp = useResponsive("up", "sm"); const mdUp = useResponsive("up", "md"); + const handleClick = () => { + window.location.href = 'https://wa.me/6285890008500'; + }; return ( - {/* + {smUp && ( - Has problem with your account? {""} + {localeData.txtHelp1} {""} { - window.location.href = - "mailto:admin@linksehat.com"; - e.preventDefault(); - }} + onClick={handleClick} > - Contact Us + {localeData.txtContactUs} - )}*/} + )} {/* {mdUp && ( @@ -116,7 +115,7 @@ export default function Login() { /> )} */} - + @@ -125,7 +124,7 @@ export default function Login() { alignItems="center" sx={{ mb: 5 }} > - + diff --git a/frontend/hospital-portal/src/routes/index.tsx b/frontend/hospital-portal/src/routes/index.tsx index a0165145..132c7b1d 100644 --- a/frontend/hospital-portal/src/routes/index.tsx +++ b/frontend/hospital-portal/src/routes/index.tsx @@ -52,7 +52,7 @@ export default function Router() { }, // { path: 'login-unprotected', element: }, // { path: 'register-unprotected', element: }, - { path: 'reset-password', element: }, + { path: 'forgot-password', element: }, { path: 'forget-password', element: }, // { path: 'verify', element: }, ], @@ -117,7 +117,7 @@ export default function Router() { } const Login = Loadable(lazy(() => import('@/pages/auth/Login'))); -const ResetPassword = Loadable(lazy(() => import('@/pages/auth/ResetPassword'))); +const ResetPassword = Loadable(lazy(() => import('@/pages/auth/VerifyCode'))); const ForgetPassword = Loadable(lazy(() => import('@/pages/auth/ForgetPassword'))); // Dashboard diff --git a/frontend/hospital-portal/src/routes/paths.ts b/frontend/hospital-portal/src/routes/paths.ts index 07f3aa11..e3735b97 100644 --- a/frontend/hospital-portal/src/routes/paths.ts +++ b/frontend/hospital-portal/src/routes/paths.ts @@ -12,5 +12,5 @@ export const PATH_AUTH = { loginUnprotected: path(ROOTS_AUTH, '/login-unprotected'), registerUnprotected: path(ROOTS_AUTH, '/register-unprotected'), verify: path(ROOTS_AUTH, '/verify'), - resetPassword: path(ROOTS_AUTH, '/reset-password'), + resetPassword: path(ROOTS_AUTH, '/forgot-password'), }; diff --git a/frontend/hospital-portal/src/sections/auth/login/LoginForm.tsx b/frontend/hospital-portal/src/sections/auth/login/LoginForm.tsx index a2383267..c0a0bfc3 100644 --- a/frontend/hospital-portal/src/sections/auth/login/LoginForm.tsx +++ b/frontend/hospital-portal/src/sections/auth/login/LoginForm.tsx @@ -60,7 +60,7 @@ export default function LoginForm() { const onSubmit = async (data: FormValuesProps) => { try { - const loginResult = await login(data.email, data.password); + const loginResult = await login(data.email, data.password, data.remember); navigate('/dashboard'); } catch (error) { @@ -100,10 +100,10 @@ export default function LoginForm() { - {/* + Forgot password? - */} + { +let expiredCookie = '12 * 60'; + +const setCookie = (name:any, value:any, days:any) => { + let expires = ""; + if (days) { + const date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + expires = "; expires=" + date.toUTCString(); + } + document.cookie = name + "=" + decodeURIComponent(value || "") + expires + "; path=/; SameSite=Strict"; + }; + +const setSession = (accessToken: string | null, rememberMe: boolean) => { if (accessToken) { - localStorage.setItem('accessToken', accessToken); + const userString = getUser(); + const storedUser = userString ? JSON.parse(userString) : null; + if(rememberMe) + { + localStorage.setItem('accessToken', accessToken); + } + else + { + setCookie('accessToken', accessToken, expiredCookie); + setCookie('rememberMe', 'OK', expiredCookie); + } + 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-Language'] = localStorage.getItem('currentLocale') ?? 'id-ID'; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.common['Content-Type'] = 'application/json'; + axios.defaults.headers.common['Organization-id'] = storedUser?.organization_id; // This function below will handle when token is expired // const { exp } = jwtDecode(accessToken); // handleTokenExpired(exp); } else { localStorage.removeItem('accessToken'); + removeCookie('accessToken'); + removeCookie('rememberMe'); 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']; + delete axios.defaults.headers.common['Content-Type']; } }; -const setUser = (user: any) => { +const setUser = (user: any, rememberMe: boolean) => { if (user) { - localStorage.setItem('user', user); + if(rememberMe) + { + localStorage.setItem('user', JSON.stringify(user)); + } + else + { + setCookie('user', JSON.stringify(user), expiredCookie); + setCookie('rememberMe', 'OK', expiredCookie); + } + } else { localStorage.removeItem('user'); + removeCookie('user'); + removeCookie('rememberMe'); } }; -const getSession = () => window.localStorage.getItem('accessToken') -const getUser = () => window.localStorage.getItem('user') +const getCookie = (name:any) => { + const cookies = document.cookie.split('; '); + for (let i = 0; i < cookies.length; i++) { + const cookiePair = cookies[i].split('='); + if (cookiePair[0] === name) { + return decodeURIComponent(cookiePair[1]); + } + } + return null; + }; -export { setSession, getSession, setUser, getUser }; +const getSession = () => { + const localToken = window.localStorage.getItem('accessToken'); + const cookieToken = getCookie('accessToken'); + // Prioritaskan token dari localStorage + return localToken || cookieToken; +}; +// const getUser = () => window.localStorage.getItem('user') || window.sessionStorage.getItem('user') +const getUser = () => { + const localUser = window.localStorage.getItem('user'); + const cookieUser = getCookie('user'); + + // Prioritaskan token dari localStorage + return localUser || cookieUser; +}; +const removeCookie = (name:any) => { + document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; +}; + +export { setSession, getSession, setUser, getUser, getCookie }; From 0cd2cf71fc6cd4c68c7a015f501f543b6bd9ba99 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Mon, 20 May 2024 11:35:31 +0700 Subject: [PATCH 02/13] Update Add user hospital portal --- Modules/HospitalPortal/Routes/api.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Modules/HospitalPortal/Routes/api.php b/Modules/HospitalPortal/Routes/api.php index b1deb0c8..7b9cbeb3 100644 --- a/Modules/HospitalPortal/Routes/api.php +++ b/Modules/HospitalPortal/Routes/api.php @@ -24,10 +24,8 @@ Route::prefix('v1')->group(function() { Route::prefix('hospitalportal')->group(function () { Route::middleware(Authentication::class)->group(function () { - Route::middleware('switch.db')->group(function () { - Route::controller(AuthController::class)->group(function () { - Route::post('login', 'login'); - }); + Route::controller(AuthController::class)->group(function () { + Route::post('login', 'login'); }); }); From f50a4f6409381e7b17641a0cefc717b03bfdcaa2 Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Tue, 21 May 2024 10:36:39 +0700 Subject: [PATCH 03/13] Update Add user hospital portal --- .../Http/Controllers/Api/AuthController.php | 154 +++++++++++++++--- Modules/HospitalPortal/Routes/api.php | 5 +- .../hospital-portal/public/lang/en-US.json | 20 ++- .../hospital-portal/public/lang/id-ID.json | 19 ++- .../src/pages/auth/ForgetPassword.tsx | 8 +- .../src/pages/auth/ResetPassword.tsx | 31 ++-- .../src/pages/auth/VerifyCode.tsx | 63 ++++++- frontend/hospital-portal/src/routes/index.tsx | 6 +- frontend/hospital-portal/src/routes/paths.ts | 2 +- .../forget-password/ForgetPasswordForm.tsx | 33 ++-- .../src/sections/auth/login/LoginForm.tsx | 6 +- .../auth/reset-password/ResetPasswordForm.tsx | 16 +- .../auth/verify-code/VerifyCodeForm.tsx | 114 ++++++++----- frontend/hospital-portal/src/utils/token.ts | 3 +- 14 files changed, 366 insertions(+), 114 deletions(-) diff --git a/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php b/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php index 44beb6b9..701b0f78 100644 --- a/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php +++ b/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php @@ -93,27 +93,6 @@ class AuthController extends Controller } public function verifyEmail(Request $request) - { - $request->validate([ - 'email' => 'required|email', - ]); - - $user = User::query() - ->where('email', $request->email) - ->first(); - - if (!$user) { - return response(['Message' => 'User Tidak Ditemukan'], 404); - } - - Event(new ForgetPassword($user)); - - // Mail::to($user->email)->send(new SendVerifyEmail($user)); - - return response()->json($user); - } - - public function forgotPassword(Request $request) { $data = [ 'email' => $request->email, @@ -144,7 +123,7 @@ class AuthController extends Controller 'user_id' => $user->id, 'email' => $emailTo, 'title' => 'Forgot Password', - 'description' => 'Request forgot password from App Doctor', + 'description' => 'Request forgot password from Hospital Portal', 'type' => 1, 'isUnRead' => true, 'created_by' => auth()->check() ? auth()->user()->id : null, @@ -168,7 +147,7 @@ class AuthController extends Controller $dataEmail = [ 'email' => $emailTo, 'name' => $nameTo, - 'subject' => 'Request Forgot Password from App Doctor Date '. date('Y-m-d H:i:s'), + 'subject' => 'Request Forgot Password from Hospital Portal Date '. date('Y-m-d H:i:s'), 'body' => View::make('email/forgot_password', ['token' => $token])->render(), ]; Helper::sendEmail($dataEmail); @@ -176,7 +155,7 @@ class AuthController extends Controller $res = DB::table('password_resets') ->where('email', '=', $request->email) ->where('token', '=', $token) - ->get(); + ->first(); return ApiResponse::apiResponse("Success", $res, trans('Message.success'), 200); } @@ -186,4 +165,131 @@ class AuthController extends Controller } } } + + public function verifCode(Request $request) + { + $data = [ + 'email' => $request->email, + 'token' => $request->token, + ]; + + $validator = Validator::make($request->all(), [ + 'email' => 'required|email', + 'token' => 'required|numeric', + ], [ + 'email.required' => trans('Validation.required',['attribute' => 'Email']), + 'email.email' => trans('Validation.email'), + 'token.required' => trans('Validation.required',['attribute' => 'Token']), + 'token.numeric' => trans('Validation.required',['attribute' => 'Code Numeric']), + ]); + + if ($validator->fails()) + { + return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); + } + else + { + //Check Time + $check = DB::table('password_resets') + ->where('email', '=', $request->email) + ->where('token', '=', $request->token) + ->select('created_at') + ->first(); + + if($check) + { + $created_at = strtotime($check->created_at); // Konversi string waktu ke UNIX timestamp + $now = time(); // Waktu sekarang dalam UNIX timestamp + + // Hitung selisih waktu dalam menit + $diffInMinutes = ($now - $created_at) / 60; + + if ($diffInMinutes > 60) { + return ApiResponse::apiResponse('Not Found', $data, trans('Message.token_expired'), 404); + } else { + // Lanjutkan dengan proses pemulihan kata sandi + return ApiResponse::apiResponse("Success", $data, trans('Message.success'), 200); + } + } + else + { + return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); + } + } + } + + public function forgetPassword(Request $request) + { + $data = [ + 'email' => $request->email, + 'token' => $request->token, + 'new_password' => $request->new_password + ]; + + $validator = Validator::make($request->all(), [ + 'email' => 'required|email', + 'token' => 'required|numeric', + 'new_password' => [ + 'required', + 'min:8', + 'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/' + ] + ], [ + 'email.required' => trans('Validation.required',['attribute' => 'Email']), + 'email.email' => trans('Validation.email'), + 'token.required' => trans('Validation.required',['attribute' => 'Token']), + 'new_password.required' => trans('Validation.required',['attribute' => 'New Password']), + 'new_password.min' => trans('Validation.min',['attribute' => 'New Password']), + 'new_password.regex' => trans('Validation.regex',['attribute' => 'New Password']), + ]); + + if($request->new_password != $request->confirm_new_password) + { + return ApiResponse::apiResponse('Bad Request', $data, 'Confirm password is not the same', 400); + } + else if ($validator->fails()) + { + return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400); + } + else + { + //Check Time + $check = DB::table('password_resets') + ->where('email', '=', $request->email) + ->where('token', '=', $request->token) + ->select('created_at') + ->first(); + + if($check) + { + $created_at = strtotime($check->created_at); // Konversi string waktu ke UNIX timestamp + $now = time(); // Waktu sekarang dalam UNIX timestamp + + // Hitung selisih waktu dalam menit + $diffInMinutes = ($now - $created_at) / 60; + + if ($diffInMinutes > 60) { + return ApiResponse::apiResponse('Not Found', $data, trans('Message.token_expired'), 404); + } else { + // Lanjutkan dengan proses pemulihan kata sandi + $user = User::where('email', $request->email)->first(); + if ($user) + { + $newPassword = Hash::make($request->new_password); + $user->password = $newPassword; + $user->save(); + return ApiResponse::apiResponse("Success", $data, trans('Message.success'), 200); + } + else + { + return ApiResponse::apiResponse('Not Found', $data, trans('Message.token_expired'), 404); + } + } + } + else + { + return ApiResponse::apiResponse('Not Found', $data, trans('Message.not_found'), 404); + } + } + } } diff --git a/Modules/HospitalPortal/Routes/api.php b/Modules/HospitalPortal/Routes/api.php index 7b9cbeb3..a0591a36 100644 --- a/Modules/HospitalPortal/Routes/api.php +++ b/Modules/HospitalPortal/Routes/api.php @@ -29,8 +29,9 @@ Route::prefix('v1')->group(function() { }); }); - Route::post('forgot-password', [AuthController::class, 'forgotPassword']); - // Route::post('verify-email', [AuthController::class, 'verifyEmail'])->name('verify-email'); + Route::post('forget-password', [AuthController::class, 'forgetPassword']); + Route::post('verify-email', [AuthController::class, 'verifyEmail'])->name('verify-email'); + Route::post('verify-code', [AuthController::class, 'verifCode']); Route::middleware('auth:sanctum')->group(function () { diff --git a/frontend/hospital-portal/public/lang/en-US.json b/frontend/hospital-portal/public/lang/en-US.json index 19a07abf..2f260eca 100644 --- a/frontend/hospital-portal/public/lang/en-US.json +++ b/frontend/hospital-portal/public/lang/en-US.json @@ -60,5 +60,23 @@ "txtDialogConfirmation": "Are you sure you want to proceed with this action?", "txtStartDate": "Start Date", "txtEndDate": "End Date", - "txtHelp1" : "Has problem with your account?" + "txtHelp1" : "Has problem with your account?", + "txtLupaSandi": "Forgot password?", + "txtIngatkanSaya": "Remember me", + "txtLogin": "Login", + "txtForgotYourPassword": "Forgot your password?", + "txtPleaseEnterPassword": "Please enter the email address associated with your account and We will email you a code to reset your password.", + "txtBack": "Back", + "txtSuccessSend": "Request sent successfully", + "txtCodeConfirm": "We have sent a confirmation email to", + "txtPleasCheck": "Please check your email.", + "txtCheckEmail": "Please check your email!", + "txtEmail": "We have emailed a 6-digit confirmation code and check spam folder, please enter the code in below box to verify your email.", + "txtDont": "Don’t have a code?", + "txtResendCode": "Resend code", + "txtSecond": "Second", + "txtPleaseInput": "Please enter your new password.", + "txtNewPassword": "New Password", + "txtConfPassword": "Confirm Kata Sandi" + } diff --git a/frontend/hospital-portal/public/lang/id-ID.json b/frontend/hospital-portal/public/lang/id-ID.json index b2eb32f6..52ea72a6 100644 --- a/frontend/hospital-portal/public/lang/id-ID.json +++ b/frontend/hospital-portal/public/lang/id-ID.json @@ -60,5 +60,22 @@ "txtDialogConfirmation": "Apakah Anda yakin ingin melanjutkan tindakan ini?", "txtStartDate": "Tanggal Mulai", "txtEndDate": "Tanggal Akhir", - "txtHelp1" : "Punya masalah dengan akun Anda?" + "txtHelp1" : "Punya masalah dengan akun Anda?", + "txtLupaSandi": "Lupa sandi?", + "txtIngatkanSaya": "Ingatkan saya", + "txtLogin": "Masuk", + "txtForgotYourPassword": "Lupa password anda?", + "txtPleaseEnterPassword": "Silakan masukkan alamat email yang terkait dengan akun Anda dan Kami akan mengirimkan email berisi kode untuk mengatur ulang kata sandi Anda.", + "txtBack": "Kembali", + "txtSuccessSend": "Permintaan berhasil dikirim", + "txtCodeConfirm": "Kami telah mengirimkan email konfirmasi ke", + "txtPleasCheck": "Mohon cek email Anda.", + "txtCheckEmail": "Mohon periksa email Anda!", + "txtEmail": "Kami telah mengirimkan kode konfirmasi 6 digit melalui email cek juga difolder spam, silakan masukkan kode di kotak bawah ini untuk memverifikasi email Anda.", + "txtDont": "Tidak mendapatkan kode?", + "txtResendCode": "Kirim ulang kode", + "txtSecond": "Detik", + "txtPleaseInput": "Mohon masukan kata sandi baru Anda.", + "txtNewPassword": "Kata Sandi Baru", + "txtConfPassword": "Konfirmasi Kata Sandi" } diff --git a/frontend/hospital-portal/src/pages/auth/ForgetPassword.tsx b/frontend/hospital-portal/src/pages/auth/ForgetPassword.tsx index 5920fc62..26fa1d18 100644 --- a/frontend/hospital-portal/src/pages/auth/ForgetPassword.tsx +++ b/frontend/hospital-portal/src/pages/auth/ForgetPassword.tsx @@ -12,6 +12,8 @@ import Iconify from '@/components/Iconify'; // sections import { ForgetPasswordForm } from '@/sections/auth/forget-password'; import { useSearchParams } from 'react-router-dom'; +import { useState, useContext, useEffect } from 'react'; +import { LanguageContext } from '@/contexts/LanguageContext'; // ---------------------------------------------------------------------- @@ -25,6 +27,8 @@ const RootStyle = styled('div')(({ theme }) => ({ // ---------------------------------------------------------------------- export default function ForgetPassword() { + + const { localeData } = useContext(LanguageContext); const [searchParams, setSearchParams] = useSearchParams(); const token = searchParams.get('token'); @@ -42,12 +46,12 @@ export default function ForgetPassword() { startIcon={} sx={{ mb: 3 }} > - Back + {localeData.txtBack} - Please enter your new password. + {localeData.txtPleaseInput} diff --git a/frontend/hospital-portal/src/pages/auth/ResetPassword.tsx b/frontend/hospital-portal/src/pages/auth/ResetPassword.tsx index 52e2982f..931c363e 100644 --- a/frontend/hospital-portal/src/pages/auth/ResetPassword.tsx +++ b/frontend/hospital-portal/src/pages/auth/ResetPassword.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useContext } from 'react'; import { Link as RouterLink } from 'react-router-dom'; // @mui import { styled } from '@mui/material/styles'; @@ -13,6 +13,9 @@ import Page from '@/components/Page'; import { ResetPasswordForm } from '@/sections/auth/reset-password'; // assets import { SentIcon } from '@/assets'; +import { LanguageContext } from '@/contexts/LanguageContext'; + +import { useNavigate } from 'react-router-dom'; // Import useNavigate // ---------------------------------------------------------------------- @@ -27,10 +30,17 @@ const RootStyle = styled('div')(({ theme }) => ({ // ---------------------------------------------------------------------- export default function ResetPassword() { + const { localeData } = useContext(LanguageContext); const [email, setEmail] = useState(''); const [sent, setSent] = useState(false); + const navigate = useNavigate(); + + const handleSent = () => { + setSent(true); + }; + return ( @@ -41,16 +51,15 @@ export default function ResetPassword() { {!sent ? ( <> - Forgot your password? + {localeData.txtForgotYourPassword} - Please enter the email address associated with your account and We will email you - a link to reset your password. + {localeData.txtPleaseEnterPassword} setSent(true)} + onSent={handleSent} onGetEmail={(value) => setEmail(value)} /> @@ -61,7 +70,7 @@ export default function ResetPassword() { to={PATH_AUTH.login} sx={{ mt: 3 }} > - Back + {localeData.txtBack} ) : ( @@ -69,24 +78,24 @@ export default function ResetPassword() { - Request sent successfully + {localeData.txtSuccessSend} - We have sent a confirmation email to   + {localeData.txtCodeConfirm}   {email}
- Please check your email. + {localeData.txtPleasCheck}
)} diff --git a/frontend/hospital-portal/src/pages/auth/VerifyCode.tsx b/frontend/hospital-portal/src/pages/auth/VerifyCode.tsx index 903226aa..0aaff5e3 100644 --- a/frontend/hospital-portal/src/pages/auth/VerifyCode.tsx +++ b/frontend/hospital-portal/src/pages/auth/VerifyCode.tsx @@ -1,4 +1,4 @@ -import { Link as RouterLink } from 'react-router-dom'; +import { Link as RouterLink, useLocation } from 'react-router-dom'; // @mui import { styled } from '@mui/material/styles'; import { Box, Button, Link, Container, Typography } from '@mui/material'; @@ -11,6 +11,9 @@ import Page from '@/components/Page'; import Iconify from '@/components/Iconify'; // sections import { VerifyCodeForm } from '@/sections/auth/verify-code'; +import { useState, useContext, useEffect } from 'react'; +import { LanguageContext } from '@/contexts/LanguageContext'; +import axios from '@/utils/axios'; // ---------------------------------------------------------------------- @@ -21,9 +24,41 @@ const RootStyle = styled('div')(({ theme }) => ({ padding: theme.spacing(12, 0), })); +function useQuery() { + return new URLSearchParams(useLocation().search); + } + // ---------------------------------------------------------------------- export default function VerifyCode() { + const { localeData } = useContext(LanguageContext); + const query = useQuery(); + const email = query.get('email'); + const [timer, setTimer] = useState(60); // Initialize timer with 60 seconds + const [canResend, setCanResend] = useState(false); // State to control resend button visibility + useEffect(() => { + const interval = setInterval(() => { + setTimer((prev) => { + if (prev > 0) { + return prev - 1; + } else { + clearInterval(interval); + setCanResend(true); // Enable resend button when timer reaches 0 + return 0; + } + }); + }, 1000); + + return () => clearInterval(interval); // Cleanup interval on component unmount + }, []); + + const handleResend = () => { + setCanResend(false); + setTimer(60); // Reset timer to 60 seconds + // Add logic to resend the code here + axios.post('/verify-email', {email: email}); + }; + return ( @@ -34,30 +69,42 @@ export default function VerifyCode() { - Please check your email! + {localeData.txtCheckEmail} + {email} - We have emailed a 6-digit confirmation code to acb@domain, please enter the code in - below box to verify your email. + {localeData.txtEmail} - + - + {/* Don’t have a code?   {}}> Resend code + */} + + {localeData.txtDont}   + {canResend ? ( + + {localeData.txtResendCode} + + ) : ( + {`${localeData.txtResendCode} ${timer} ${localeData.txtSecond}`} + )}
diff --git a/frontend/hospital-portal/src/routes/index.tsx b/frontend/hospital-portal/src/routes/index.tsx index 132c7b1d..2bcb583b 100644 --- a/frontend/hospital-portal/src/routes/index.tsx +++ b/frontend/hospital-portal/src/routes/index.tsx @@ -52,9 +52,9 @@ export default function Router() { }, // { path: 'login-unprotected', element: }, // { path: 'register-unprotected', element: }, - { path: 'forgot-password', element: }, + { path: 'reset-password', element: }, { path: 'forget-password', element: }, - // { path: 'verify', element: }, + { path: 'verify', element: }, ], }, // { @@ -117,7 +117,7 @@ export default function Router() { } const Login = Loadable(lazy(() => import('@/pages/auth/Login'))); -const ResetPassword = Loadable(lazy(() => import('@/pages/auth/VerifyCode'))); +const ResetPassword = Loadable(lazy(() => import('@/pages/auth/ResetPassword'))); const ForgetPassword = Loadable(lazy(() => import('@/pages/auth/ForgetPassword'))); // Dashboard diff --git a/frontend/hospital-portal/src/routes/paths.ts b/frontend/hospital-portal/src/routes/paths.ts index e3735b97..07f3aa11 100644 --- a/frontend/hospital-portal/src/routes/paths.ts +++ b/frontend/hospital-portal/src/routes/paths.ts @@ -12,5 +12,5 @@ export const PATH_AUTH = { loginUnprotected: path(ROOTS_AUTH, '/login-unprotected'), registerUnprotected: path(ROOTS_AUTH, '/register-unprotected'), verify: path(ROOTS_AUTH, '/verify'), - resetPassword: path(ROOTS_AUTH, '/forgot-password'), + resetPassword: path(ROOTS_AUTH, '/reset-password'), }; diff --git a/frontend/hospital-portal/src/sections/auth/forget-password/ForgetPasswordForm.tsx b/frontend/hospital-portal/src/sections/auth/forget-password/ForgetPasswordForm.tsx index 4f6cc67c..2c1b4b2f 100644 --- a/frontend/hospital-portal/src/sections/auth/forget-password/ForgetPasswordForm.tsx +++ b/frontend/hospital-portal/src/sections/auth/forget-password/ForgetPasswordForm.tsx @@ -2,8 +2,8 @@ import * as Yup from 'yup'; // form import { yupResolver } from '@hookform/resolvers/yup'; import { useForm } from 'react-hook-form'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { Link as RouterLink, useNavigate } from 'react-router-dom'; +import { useCallback, useEffect, useContext, useMemo, useRef, useState } from 'react'; +import { Link as RouterLink, useNavigate, useLocation } from 'react-router-dom'; // @mui import { Alert, IconButton, InputAdornment, Stack, Typography } from '@mui/material'; @@ -14,6 +14,9 @@ import useIsMountedRef from '@/hooks/useIsMountedRef'; import { FormProvider, RHFTextField } from '@/components/hook-form'; import axios from '@/utils/axios'; import Iconify from '@/components/Iconify'; +import { LanguageContext } from '@/contexts/LanguageContext'; + +import { useSnackbar } from 'notistack'; // ---------------------------------------------------------------------- @@ -26,9 +29,19 @@ type Props = { token: string; }; +function useQuery() { + return new URLSearchParams(useLocation().search); + } + export default function ForgetPasswordForm({ token }: Props) { + const { localeData } = useContext(LanguageContext); const isMountedRef = useIsMountedRef(); const navigate = useNavigate(); + const query = useQuery(); + const email = query.get('email'); + + const token_ = query.get('token'); + const { enqueueSnackbar } = useSnackbar(); const [showPasswordNew, setShowPasswordNew] = useState(false); const [showPasswordConfirmNew, setShowPasswordConfirmNew] = useState(false); @@ -47,19 +60,19 @@ export default function ForgetPasswordForm({ token }: Props) { const onSubmit = async (data: FormValuesProps) => { try { - await axios.post('/forget-password', { ...data, token }); - console.log(data); + await axios.post('/forget-password', { ...data, email:email, token:token_ }); + // await new Promise((resolve) => setTimeout(resolve, 500)); await new Promise((resolve) => setTimeout(resolve, 500)); if (isMountedRef.current) { + enqueueSnackbar('Password reset was successful', { variant: 'success' }); navigate('/auth/login', { replace: true }); } } catch (error) { - console.log(error.response.data); if (isMountedRef.current) { - setError('afterSubmit', { ...error, message: error.response.data.message }); + setError('afterSubmit', { ...error, message: error.response.data.meta.message }); } } }; @@ -68,10 +81,10 @@ export default function ForgetPasswordForm({ token }: Props) { {!!errors.afterSubmit && {errors.afterSubmit.message}} - Kata Sandi Baru + {localeData.txtNewPassword} - Konfirmasi Kata Sandi + {localeData.txtConfPassword} - + - Forgot password? + {localeData.txtLupaSandi} @@ -114,7 +114,7 @@ export default function LoginForm() { loading={isSubmitting} sx={{ marginTop: 2 }} > - Login + {localeData.txtLogin} ); diff --git a/frontend/hospital-portal/src/sections/auth/reset-password/ResetPasswordForm.tsx b/frontend/hospital-portal/src/sections/auth/reset-password/ResetPasswordForm.tsx index 671f9f76..a30e30e2 100644 --- a/frontend/hospital-portal/src/sections/auth/reset-password/ResetPasswordForm.tsx +++ b/frontend/hospital-portal/src/sections/auth/reset-password/ResetPasswordForm.tsx @@ -10,6 +10,7 @@ import useIsMountedRef from '@/hooks/useIsMountedRef'; // components import { FormProvider, RHFTextField } from '@/components/hook-form'; import axios from '@/utils/axios'; +import { useNavigate } from 'react-router-dom'; // ---------------------------------------------------------------------- @@ -26,6 +27,8 @@ type Props = { export default function ResetPasswordForm({ onSent, onGetEmail }: Props) { const isMountedRef = useIsMountedRef(); + const navigate = useNavigate(); + const ResetPasswordSchema = Yup.object().shape({ email: Yup.string().email('Email must be a valid email address').required('Email is required'), }); @@ -43,19 +46,22 @@ export default function ResetPasswordForm({ onSent, onGetEmail }: Props) { const onSubmit = async (data: FormValuesProps) => { try { - await axios.post('/verify-email', data); - console.log(data); + const response = await axios.post('/verify-email', data); + if(response.data.data.email) + { + onGetEmail(response.data.data.email); + navigate(`/auth/verify?email=${response.data.data.email}`); + } // await new Promise((resolve) => setTimeout(resolve, 500)); await new Promise((resolve) => setTimeout(resolve, 500)); if (isMountedRef.current) { onSent(); - onGetEmail(data.email); + onGetEmail(response.data.data.email); } } catch (error) { - console.log(error.response.data); if (isMountedRef.current) { - setError('afterSubmit', { ...error, message: error.response.data.message }); + setError('afterSubmit', { ...error, message: error.response.data.meta.message }); } } }; diff --git a/frontend/hospital-portal/src/sections/auth/verify-code/VerifyCodeForm.tsx b/frontend/hospital-portal/src/sections/auth/verify-code/VerifyCodeForm.tsx index 7ebb07fc..a656270e 100644 --- a/frontend/hospital-portal/src/sections/auth/verify-code/VerifyCodeForm.tsx +++ b/frontend/hospital-portal/src/sections/auth/verify-code/VerifyCodeForm.tsx @@ -6,9 +6,12 @@ import { useEffect } from 'react'; import { useForm, Controller } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; // @mui -import { OutlinedInput, Stack } from '@mui/material'; +import { OutlinedInput, Alert, Stack } from '@mui/material'; +import useIsMountedRef from '@/hooks/useIsMountedRef'; import { LoadingButton } from '@mui/lab'; +import { FormProvider, RHFTextField } from '@/components/hook-form'; // routes +import axios from '@/utils/axios'; // import { PATH_DASHBOARD } from '@/routes/paths'; // ---------------------------------------------------------------------- @@ -23,8 +26,11 @@ type FormValuesProps = { }; type ValueNames = 'code1' | 'code2' | 'code3' | 'code4' | 'code5' | 'code6'; - -export default function VerifyCodeForm() { +type Props = { + onGetEmail: (value: string) => void; + }; +export default function VerifyCodeForm({ onGetEmail }: Props) { + const isMountedRef = useIsMountedRef(); const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); @@ -51,8 +57,9 @@ export default function VerifyCodeForm() { watch, control, setValue, + setError, handleSubmit, - formState: { isSubmitting, isValid }, + formState: { isSubmitting, isValid , errors}, } = useForm({ mode: 'onBlur', resolver: yupResolver(VerifyCodeSchema), @@ -67,16 +74,36 @@ export default function VerifyCodeForm() { }, []); const onSubmit = async (data: FormValuesProps) => { + // try { + // await new Promise((resolve) => setTimeout(resolve, 500)); + // console.log('code:', Object.values(data).join('')); + + // enqueueSnackbar('Verify success!', { variant: 'success' }); + + // navigate('/dashboard', { replace: true }); + // } catch (error) { + // console.error(error); + // } + try { - await new Promise((resolve) => setTimeout(resolve, 500)); - console.log('code:', Object.values(data).join('')); - - enqueueSnackbar('Verify success!', { variant: 'success' }); - - navigate('/dashboard', { replace: true }); - } catch (error) { - console.error(error); - } + const response = await axios.post('/verify-code', {email: onGetEmail, token: Object.values(data).join('')}); + // await new Promise((resolve) => setTimeout(resolve, 500)); + await new Promise((resolve) => setTimeout(resolve, 500)); + if(response.data.meta.code === 200) + { + navigate(`/auth/forget-password?email=${response.data.data.email}&token=${response.data.data.token}`); + } + if (isMountedRef.current) { + if(response.data.meta.code === 404) + { + setError('afterSubmit', { ...response, message: response.data.meta.message }); + } + } + } catch (error) { + if (isMountedRef.current) { + setError('afterSubmit', { ...error, message: error.response.data.meta.message }); + } + } }; const handlePasteClipboard = (event: ClipboardEvent) => { @@ -115,35 +142,38 @@ export default function VerifyCodeForm() { return (
- - {Object.keys(values).map((name, index) => ( - ( - ) => - handleChangeWithNextField(event, field.onChange) - } - inputProps={{ - maxLength: 1, - sx: { - p: 0, - textAlign: 'center', - width: { xs: 36, sm: 56 }, - height: { xs: 36, sm: 56 }, - }, - }} - /> - )} - /> - ))} - + + {!!errors.afterSubmit && {errors.afterSubmit.message}} + + {Object.keys(values).map((name, index) => ( + ( + ) => + handleChangeWithNextField(event, field.onChange) + } + inputProps={{ + maxLength: 1, + sx: { + p: 0, + textAlign: 'center', + width: { xs: 36, sm: 56 }, + height: { xs: 36, sm: 56 }, + }, + }} + /> + )} + /> + ))} + + { let expires = ""; From 81311928da89818e3514a1ddaf1716bd06cb10b9 Mon Sep 17 00:00:00 2001 From: pajri Date: Tue, 21 May 2024 14:49:24 +0700 Subject: [PATCH 04/13] api token firebase --- .../Http/Controllers/Api/AuthController.php | 72 +- Modules/Linksehat/Routes/api.php | 23 +- composer.json | 2 + composer.lock | 1902 ++++++++++++++++- 4 files changed, 1913 insertions(+), 86 deletions(-) diff --git a/Modules/Linksehat/Http/Controllers/Api/AuthController.php b/Modules/Linksehat/Http/Controllers/Api/AuthController.php index 8d5a0d30..b5a25a85 100644 --- a/Modules/Linksehat/Http/Controllers/Api/AuthController.php +++ b/Modules/Linksehat/Http/Controllers/Api/AuthController.php @@ -19,12 +19,13 @@ class AuthController extends Controller { private $url; - public function __construct() { + public function __construct() + { $this->url = $_ENV['LMS_APP_URL']; } public function login(Request $request) - { + { $request->validate([ // 'email' => 'email', 'phone_or_email' => 'required', @@ -35,17 +36,17 @@ class AuthController extends Controller ]); // Login hit ke API linksehat, karena encrypt nya pake CI - $response = Http::post($this->url.'login', [ + $response = Http::post($this->url . 'login', [ 'sEmail' => $request->phone_or_email, 'sPassword' => $request->password, 'sRemember' => $request->remember ]); $response = $response->json(); - if ($response['success']){ + if ($response['success']) { $user = User::with('detail') - ->where('sEmail', $request->phone_or_email) - ->first(); + ->where('sEmail', $request->phone_or_email) + ->first(); return Helper::responseJson( data: [ 'token' => $user->createToken('app')->plainTextToken, @@ -58,18 +59,19 @@ class AuthController extends Controller }; } - public function forgetPassword(Request $request){ + public function forgetPassword(Request $request) + { $request->validate([ 'email' => 'required|email', ]); // Login hit ke API linksehat, karena encrypt nya pake CI - $response = Http::post($this->url.'forgot_password', [ + $response = Http::post($this->url . 'forgot_password', [ 'sEmail' => $request->email, ]); $response = $response->json(); - if ($response['success']){ + if ($response['success']) { return Helper::responseJson( data: [], message: 'Message has been sent.' @@ -79,7 +81,8 @@ class AuthController extends Controller }; } - public function resetPassword(Request $request){ + public function resetPassword(Request $request) + { $request->validate([ 'email' => 'required|email', 'code' => 'required', @@ -87,14 +90,14 @@ class AuthController extends Controller ]); // Login hit ke API linksehat, karena encrypt nya pake CI - $response = Http::post($this->url.'reset_password', [ + $response = Http::post($this->url . 'reset_password', [ 'sCode' => $request->code, 'sEmail' => $request->email, 'sPassword' => $request->password, ]); $response = $response->json(); - if ($response['success']){ + if ($response['success']) { return Helper::responseJson( data: [], message: 'Password telah di reset' @@ -105,7 +108,7 @@ class AuthController extends Controller } public function loginPhone(Request $request) - { + { $request->validate([ 'phone_or_email' => 'required', 'otp' => 'required', @@ -116,9 +119,9 @@ class AuthController extends Controller 'sPhone' => $request->phone_or_email, 'sVerificationCode' => $request->otp ]) - ->first(); + ->first(); - if ($user){ + if ($user) { $updateVericationCode = User::with('detail') ->where([ @@ -145,15 +148,15 @@ class AuthController extends Controller $user = User::with('detail') ->where('sPhone', $request->phone_or_email) ->first(); - - if ($user){ + + if ($user) { // Request OTP ke API linksehat - $response = Http::post($this->url.'generate_code', [ + $response = Http::post($this->url . 'generate_code', [ 'sPhone' => $request->phone_or_email ]); - + $response = $response->json(); - + return Helper::responseJson( data: [ 'otp' => $response['message'], @@ -168,9 +171,6 @@ class AuthController extends Controller message: 'Nomor tidak ditemukan' ); } - - - } public function register(Request $request) @@ -209,7 +209,7 @@ class AuthController extends Controller return Helper::responseJson(message: 'Behasil Logout.'); } - + public function mockOtp(Request $request) { $request->validate([ @@ -256,4 +256,28 @@ class AuthController extends Controller 'token' => $user->createToken('app')->plainTextToken ], message: 'Selamat Datang'); } + + public function notificationToken(Request $request) + { + $user = Auth::user(); + $origin = $request->origin; + $device_id = $request->device_id; + $type = $request->type; + $token = $request->token; + $status = $request->status; + + $user->notificationTokens()->updateOrCreate([ + 'device_id' => $device_id, + ], [ + 'origin' => $origin, + 'device_id' => $device_id, + 'type' => $type, + 'token' => $token, + 'status' => $status, + ]); + + return response()->json([ + 'message' => 'Token berhasil disimpan', + ]); + } } diff --git a/Modules/Linksehat/Routes/api.php b/Modules/Linksehat/Routes/api.php index 9344de6f..9d50ff63 100644 --- a/Modules/Linksehat/Routes/api.php +++ b/Modules/Linksehat/Routes/api.php @@ -34,7 +34,7 @@ use Modules\Linksehat\Http\Controllers\Api\Doctor\ChatDoctorController; Broadcast::routes(['middleware' => ['auth:sanctum']]); Route::prefix('linksehat')->group(function () { - + Route::get('dashboard/{query}/{limit?}', [DashboardController::class, 'index']); Route::controller(SearchController::class)->group(function () { @@ -44,6 +44,7 @@ Route::prefix('linksehat')->group(function () { Route::controller(AuthController::class)->group(function () { Route::post('otp-request', 'otpRequest'); + Route::post('notification-token', 'notificationToken'); Route::post('mock-otp', 'mockOtp'); Route::post('login', 'login'); Route::post('logout', 'logout'); @@ -99,7 +100,7 @@ Route::prefix('linksehat')->group(function () { Route::get('home', 'index')->name('homes.index'); Route::get('home/hospital', 'listHospital')->name('homes.listHospital'); }); - + Route::controller(LivechatController::class)->group(function () { Route::get('livechat', 'index')->name('livechats.index'); Route::get('livechat/consultation', 'consultation')->name('livechats.consultation'); @@ -114,15 +115,14 @@ Route::prefix('linksehat')->group(function () { Route::controller(ChatController::class)->group(function () { Route::post('livechat/send-message', 'sendMessage'); Route::get('livechat/get-message', 'getMessage'); - Route::post('livechat/channel','createChannel'); - Route::get('livechat/channel','listChannel'); - Route::get('livechat/{id}/health-sertificate','downloadHealtcare'); + Route::post('livechat/channel', 'createChannel'); + Route::get('livechat/channel', 'listChannel'); + Route::get('livechat/{id}/health-sertificate', 'downloadHealtcare'); }); - + Route::post('create-invoice-duitku', [DuitkuController::class, 'createInvoice']); Route::post('check-status-duitku', [DuitkuController::class, 'checkStatus']); - }); Route::post('payment-method-duitku', [DuitkuController::class, 'paymentMethod']); @@ -130,9 +130,9 @@ Route::prefix('linksehat')->group(function () { Route::get('redirect-duitku', [DuitkuController::class, 'redirect']); //DOCTOR API - Route::prefix('doctor')->group(function() { + Route::prefix('doctor')->group(function () { //Version 1.0 - Route::prefix('v1')->group(function() { + Route::prefix('v1')->group(function () { Route::middleware(Authentication::class)->group(function () { Route::controller(AuthDoctorController::class)->group(function () { Route::post('login', 'login'); @@ -163,7 +163,6 @@ Route::prefix('linksehat')->group(function () { Route::post('resend-code', 'forgotPassword'); Route::post('reset-password', 'resetPassword'); }); - }); - }); -;}); + });; +}); diff --git a/composer.json b/composer.json index 59cebfc4..15a2ed2c 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,8 @@ "guzzlehttp/guzzle": "^7.2", "h4cc/wkhtmltoimage-amd64": "^0.12.4", "h4cc/wkhtmltopdf-amd64": "0.12.x", + "kreait/firebase-php": "^6.9", + "laravel-notification-channels/fcm": "^2.1", "laravel/framework": "^9.11", "laravel/sanctum": "^2.15", "laravel/socialite": "^5.5", diff --git a/composer.lock b/composer.lock index 7aaddc93..af543dec 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1575df47935c996ba01c7337aadaa305", + "content-hash": "376b19f2ad42a940a917ec375fdd6c9d", "packages": [ { "name": "barryvdh/laravel-dompdf", @@ -161,6 +161,133 @@ ], "time": "2023-04-07T10:38:54+00:00" }, + { + "name": "beste/clock", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/beste/clock.git", + "reference": "cf2cc16e6670a16fd2defc6d383313de829fa291" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beste/clock/zipball/cf2cc16e6670a16fd2defc6d383313de829fa291", + "reference": "cf2cc16e6670a16fd2defc6d383313de829fa291", + "shasum": "" + }, + "require": { + "php": "^8.0", + "stella-maris/clock": "^0.1.6" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.2", + "phpstan/phpstan": "^1.9.1", + "phpstan/phpstan-phpunit": "^1.2.2", + "phpstan/phpstan-strict-rules": "^1.4.4", + "phpunit/phpunit": "^9.5.26", + "psalm/plugin-phpunit": "^0.16.1", + "vimeo/psalm": "^4.29" + }, + "type": "library", + "autoload": { + "files": [ + "src/Clock.php" + ], + "psr-4": { + "Beste\\Clock\\": "src/Clock" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jérôme Gamez", + "email": "jerome@gamez.name" + } + ], + "description": "A collection of Clock implementations", + "keywords": [ + "clock", + "clock-interface", + "psr-20", + "psr20" + ], + "support": { + "issues": "https://github.com/beste/clock/issues", + "source": "https://github.com/beste/clock/tree/2.3.1" + }, + "funding": [ + { + "url": "https://github.com/jeromegamez", + "type": "github" + } + ], + "time": "2022-11-25T18:24:28+00:00" + }, + { + "name": "beste/json", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/beste/json.git", + "reference": "2d7aea5a7ceeb428350ba450e4a227ac581359b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/beste/json/zipball/2d7aea5a7ceeb428350ba450e4a227ac581359b4", + "reference": "2d7aea5a7ceeb428350ba450e4a227ac581359b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-phpunit": "^1.3", + "phpstan/phpstan-strict-rules": "^1.5", + "phpunit/phpunit": "^10.4.2", + "rector/rector": "^0.18.10" + }, + "type": "library", + "autoload": { + "files": [ + "src/Json.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jérôme Gamez", + "email": "jerome@gamez.name" + } + ], + "description": "A simple JSON helper to decode and encode JSON", + "keywords": [ + "helper", + "json" + ], + "support": { + "issues": "https://github.com/beste/json/issues", + "source": "https://github.com/beste/json/tree/1.4.0" + }, + "funding": [ + { + "url": "https://github.com/jeromegamez", + "type": "github" + } + ], + "time": "2023-11-30T22:41:32+00:00" + }, { "name": "box/spout", "version": "v3.3.0", @@ -976,6 +1103,125 @@ }, "time": "2023-11-17T15:01:25+00:00" }, + { + "name": "fig/http-message-util", + "version": "1.1.5", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message-util.git", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765", + "reference": "9d94dc0154230ac39e5bf89398b324a86f63f765", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "suggest": { + "psr/http-message": "The package containing the PSR-7 interfaces" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fig\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Utility classes and constants for use with PSR-7 (psr/http-message)", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "issues": "https://github.com/php-fig/http-message-util/issues", + "source": "https://github.com/php-fig/http-message-util/tree/1.1.5" + }, + "time": "2020-11-24T22:02:12+00:00" + }, + { + "name": "firebase/php-jwt", + "version": "v6.10.1", + "source": { + "type": "git", + "url": "https://github.com/firebase/php-jwt.git", + "reference": "500501c2ce893c824c801da135d02661199f60c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5", + "reference": "500501c2ce893c824c801da135d02661199f60c5", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "guzzlehttp/guzzle": "^7.4", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "psr/cache": "^2.0||^3.0", + "psr/http-client": "^1.0", + "psr/http-factory": "^1.0" + }, + "suggest": { + "ext-sodium": "Support EdDSA (Ed25519) signatures", + "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present" + }, + "type": "library", + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "jwt", + "php" + ], + "support": { + "issues": "https://github.com/firebase/php-jwt/issues", + "source": "https://github.com/firebase/php-jwt/tree/v6.10.1" + }, + "time": "2024-05-18T18:05:11+00:00" + }, { "name": "fruitcake/laravel-cors", "version": "v3.0.0", @@ -1127,6 +1373,431 @@ ], "time": "2023-10-12T05:21:21+00:00" }, + { + "name": "google/auth", + "version": "v1.39.0", + "source": { + "type": "git", + "url": "https://github.com/googleapis/google-auth-library-php.git", + "reference": "23e8e696d87f8d7dfefbd347ca1c99ce17ecb368" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/23e8e696d87f8d7dfefbd347ca1c99ce17ecb368", + "reference": "23e8e696d87f8d7dfefbd347ca1c99ce17ecb368", + "shasum": "" + }, + "require": { + "firebase/php-jwt": "^6.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.4.5", + "php": "^8.0", + "psr/cache": "^2.0||^3.0", + "psr/http-message": "^1.1||^2.0" + }, + "require-dev": { + "guzzlehttp/promises": "^2.0", + "kelvinmo/simplejwt": "0.7.1", + "phpseclib/phpseclib": "^3.0.35", + "phpspec/prophecy-phpunit": "^2.1", + "phpunit/phpunit": "^9.6", + "sebastian/comparator": ">=1.2.3", + "squizlabs/php_codesniffer": "^3.5", + "symfony/process": "^6.0||^7.0", + "webmozart/assert": "^1.11" + }, + "suggest": { + "phpseclib/phpseclib": "May be used in place of OpenSSL for signing strings or for token management. Please require version ^2." + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\Auth\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Google Auth Library for PHP", + "homepage": "http://github.com/google/google-auth-library-php", + "keywords": [ + "Authentication", + "google", + "oauth2" + ], + "support": { + "docs": "https://googleapis.github.io/google-auth-library-php/main/", + "issues": "https://github.com/googleapis/google-auth-library-php/issues", + "source": "https://github.com/googleapis/google-auth-library-php/tree/v1.39.0" + }, + "time": "2024-05-02T16:03:51+00:00" + }, + { + "name": "google/cloud-core", + "version": "v1.58.1", + "source": { + "type": "git", + "url": "https://github.com/googleapis/google-cloud-php-core.git", + "reference": "db3e0ab25103e0ca953f6e1e0ca5a39e363b8988" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/google-cloud-php-core/zipball/db3e0ab25103e0ca953f6e1e0ca5a39e363b8988", + "reference": "db3e0ab25103e0ca953f6e1e0ca5a39e363b8988", + "shasum": "" + }, + "require": { + "google/auth": "^1.34", + "google/gax": "^1.30", + "guzzlehttp/guzzle": "^6.5.8|^7.4.4", + "guzzlehttp/promises": "^1.4||^2.0", + "guzzlehttp/psr7": "^2.6", + "monolog/monolog": "^2.9|^3.0", + "php": "^8.0", + "psr/http-message": "^1.0|^2.0", + "rize/uri-template": "~0.3" + }, + "require-dev": { + "erusev/parsedown": "^1.6", + "google/cloud-common-protos": "~0.5", + "opis/closure": "^3", + "phpdocumentor/reflection": "^5.3.3", + "phpdocumentor/reflection-docblock": "^5.3", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.0", + "squizlabs/php_codesniffer": "2.*" + }, + "suggest": { + "opis/closure": "May be used to serialize closures to process jobs in the batch daemon. Please require version ^3.", + "symfony/lock": "Required for the Spanner cached based session pool. Please require the following commit: 3.3.x-dev#1ba6ac9" + }, + "bin": [ + "bin/google-cloud-batch" + ], + "type": "library", + "extra": { + "component": { + "id": "cloud-core", + "target": "googleapis/google-cloud-php-core.git", + "path": "Core", + "entry": "src/ServiceBuilder.php" + } + }, + "autoload": { + "psr-4": { + "Google\\Cloud\\Core\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Google Cloud PHP shared dependency, providing functionality useful to all components.", + "support": { + "source": "https://github.com/googleapis/google-cloud-php-core/tree/v1.58.1" + }, + "time": "2024-05-03T18:32:44+00:00" + }, + { + "name": "google/cloud-storage", + "version": "v1.42.0", + "source": { + "type": "git", + "url": "https://github.com/googleapis/google-cloud-php-storage.git", + "reference": "1c77f5882c30bec95ab2837b9534a946325d1c57" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/google-cloud-php-storage/zipball/1c77f5882c30bec95ab2837b9534a946325d1c57", + "reference": "1c77f5882c30bec95ab2837b9534a946325d1c57", + "shasum": "" + }, + "require": { + "google/cloud-core": "^1.55", + "php": "^8.0", + "ramsey/uuid": "^4.2.3" + }, + "require-dev": { + "erusev/parsedown": "^1.6", + "google/cloud-pubsub": "^2.0", + "phpdocumentor/reflection": "^5.3.3", + "phpdocumentor/reflection-docblock": "^5.3", + "phpseclib/phpseclib": "^2.0||^3.0", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.0", + "squizlabs/php_codesniffer": "2.*" + }, + "suggest": { + "google/cloud-pubsub": "May be used to register a topic to receive bucket notifications.", + "phpseclib/phpseclib": "May be used in place of OpenSSL for creating signed Cloud Storage URLs. Please require version ^2." + }, + "type": "library", + "extra": { + "component": { + "id": "cloud-storage", + "target": "googleapis/google-cloud-php-storage.git", + "path": "Storage", + "entry": "src/StorageClient.php" + } + }, + "autoload": { + "psr-4": { + "Google\\Cloud\\Storage\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Cloud Storage Client for PHP", + "support": { + "source": "https://github.com/googleapis/google-cloud-php-storage/tree/v1.42.0" + }, + "time": "2024-05-19T17:27:42+00:00" + }, + { + "name": "google/common-protos", + "version": "v4.6.0", + "source": { + "type": "git", + "url": "https://github.com/googleapis/common-protos-php.git", + "reference": "f8588298a0a204aef2db15ce501530e476ec883f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/common-protos-php/zipball/f8588298a0a204aef2db15ce501530e476ec883f", + "reference": "f8588298a0a204aef2db15ce501530e476ec883f", + "shasum": "" + }, + "require": { + "google/protobuf": "^v3.25.3||^4.26.1", + "php": "^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\Api\\": "src/Api", + "Google\\Iam\\": "src/Iam", + "Google\\Rpc\\": "src/Rpc", + "Google\\Type\\": "src/Type", + "Google\\Cloud\\": "src/Cloud", + "GPBMetadata\\Google\\Api\\": "metadata/Api", + "GPBMetadata\\Google\\Iam\\": "metadata/Iam", + "GPBMetadata\\Google\\Rpc\\": "metadata/Rpc", + "GPBMetadata\\Google\\Type\\": "metadata/Type", + "GPBMetadata\\Google\\Cloud\\": "metadata/Cloud", + "GPBMetadata\\Google\\Logging\\": "metadata/Logging" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Google API Common Protos for PHP", + "homepage": "https://github.com/googleapis/common-protos-php", + "keywords": [ + "google" + ], + "support": { + "issues": "https://github.com/googleapis/common-protos-php/issues", + "source": "https://github.com/googleapis/common-protos-php/tree/v4.6.0" + }, + "time": "2024-04-03T19:11:54+00:00" + }, + { + "name": "google/gax", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/googleapis/gax-php.git", + "reference": "12a158e9b503df0087ebf9e218e8d75dc815a521" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/gax-php/zipball/12a158e9b503df0087ebf9e218e8d75dc815a521", + "reference": "12a158e9b503df0087ebf9e218e8d75dc815a521", + "shasum": "" + }, + "require": { + "google/auth": "^1.34.0", + "google/common-protos": "^4.4", + "google/grpc-gcp": "^0.4", + "google/longrunning": "~0.4", + "google/protobuf": "^v3.25.3||^4.26.1", + "grpc/grpc": "^1.13", + "guzzlehttp/promises": "^2.0", + "guzzlehttp/psr7": "^2.0", + "php": "^8.0", + "ramsey/uuid": "^4.0" + }, + "conflict": { + "ext-protobuf": "<3.7.0" + }, + "require-dev": { + "phpspec/prophecy-phpunit": "^2.1", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.6", + "squizlabs/php_codesniffer": "3.*" + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\ApiCore\\": "src", + "GPBMetadata\\ApiCore\\": "metadata/ApiCore" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Google API Core for PHP", + "homepage": "https://github.com/googleapis/gax-php", + "keywords": [ + "google" + ], + "support": { + "issues": "https://github.com/googleapis/gax-php/issues", + "source": "https://github.com/googleapis/gax-php/tree/v1.33.0" + }, + "time": "2024-05-14T14:55:14+00:00" + }, + { + "name": "google/grpc-gcp", + "version": "v0.4.0", + "source": { + "type": "git", + "url": "https://github.com/GoogleCloudPlatform/grpc-gcp-php.git", + "reference": "2a80dbf690922aa52bb6bb79b9a32a9637a5c2d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GoogleCloudPlatform/grpc-gcp-php/zipball/2a80dbf690922aa52bb6bb79b9a32a9637a5c2d9", + "reference": "2a80dbf690922aa52bb6bb79b9a32a9637a5c2d9", + "shasum": "" + }, + "require": { + "google/auth": "^1.3", + "google/protobuf": "^v3.25.3||^4.26.1", + "grpc/grpc": "^v1.13.0", + "php": "^8.0", + "psr/cache": "^1.0.1||^2.0.0||^3.0.0" + }, + "require-dev": { + "google/cloud-spanner": "^1.7", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Grpc\\Gcp\\": "src/" + }, + "classmap": [ + "src/generated/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "gRPC GCP library for channel management", + "support": { + "issues": "https://github.com/GoogleCloudPlatform/grpc-gcp-php/issues", + "source": "https://github.com/GoogleCloudPlatform/grpc-gcp-php/tree/v0.4.0" + }, + "time": "2024-04-03T16:37:55+00:00" + }, + { + "name": "google/longrunning", + "version": "0.4.2", + "source": { + "type": "git", + "url": "https://github.com/googleapis/php-longrunning.git", + "reference": "dd38c97ee348ad73bfb581aa276a536161f4b181" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/googleapis/php-longrunning/zipball/dd38c97ee348ad73bfb581aa276a536161f4b181", + "reference": "dd38c97ee348ad73bfb581aa276a536161f4b181", + "shasum": "" + }, + "require-dev": { + "google/gax": "^1.30", + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "component": { + "id": "longrunning", + "path": "LongRunning", + "entry": null, + "target": "googleapis/php-longrunning" + } + }, + "autoload": { + "psr-4": { + "Google\\LongRunning\\": "src/LongRunning", + "Google\\ApiCore\\LongRunning\\": "src/ApiCore/LongRunning", + "GPBMetadata\\Google\\Longrunning\\": "metadata/Longrunning" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "Google LongRunning Client for PHP", + "support": { + "source": "https://github.com/googleapis/php-longrunning/tree/v0.4.2" + }, + "time": "2024-05-03T18:32:44+00:00" + }, + { + "name": "google/protobuf", + "version": "v4.26.1", + "source": { + "type": "git", + "url": "https://github.com/protocolbuffers/protobuf-php.git", + "reference": "5c46b0eb09e7ad3e6efef3c5a85e2a34108c52ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/5c46b0eb09e7ad3e6efef3c5a85e2a34108c52ae", + "reference": "5c46b0eb09e7ad3e6efef3c5a85e2a34108c52ae", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": ">=5.0.0" + }, + "suggest": { + "ext-bcmath": "Need to support JSON deserialization" + }, + "type": "library", + "autoload": { + "psr-4": { + "Google\\Protobuf\\": "src/Google/Protobuf", + "GPBMetadata\\Google\\Protobuf\\": "src/GPBMetadata/Google/Protobuf" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "proto library for PHP", + "homepage": "https://developers.google.com/protocol-buffers/", + "keywords": [ + "proto" + ], + "support": { + "source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.26.1" + }, + "time": "2024-03-27T19:56:50+00:00" + }, { "name": "graham-campbell/result-type", "version": "v1.1.2", @@ -1189,6 +1860,50 @@ ], "time": "2023-11-12T22:16:48+00:00" }, + { + "name": "grpc/grpc", + "version": "1.57.0", + "source": { + "type": "git", + "url": "https://github.com/grpc/grpc-php.git", + "reference": "b610c42022ed3a22f831439cb93802f2a4502fdf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/grpc/grpc-php/zipball/b610c42022ed3a22f831439cb93802f2a4502fdf", + "reference": "b610c42022ed3a22f831439cb93802f2a4502fdf", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "google/auth": "^v1.3.0" + }, + "suggest": { + "ext-protobuf": "For better performance, install the protobuf C extension.", + "google/protobuf": "To get started using grpc quickly, install the native protobuf library." + }, + "type": "library", + "autoload": { + "psr-4": { + "Grpc\\": "src/lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "gRPC library for PHP", + "homepage": "https://grpc.io", + "keywords": [ + "rpc" + ], + "support": { + "source": "https://github.com/grpc/grpc-php/tree/v1.57.0" + }, + "time": "2023-08-14T23:57:54+00:00" + }, { "name": "guzzlehttp/guzzle", "version": "7.8.1", @@ -1844,6 +2559,324 @@ }, "time": "2023-12-18T09:12:11+00:00" }, + { + "name": "kreait/firebase-php", + "version": "6.9.6", + "source": { + "type": "git", + "url": "https://github.com/kreait/firebase-php.git", + "reference": "d6592be9b27a7c0b13f484f5af494e278901e441" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kreait/firebase-php/zipball/d6592be9b27a7c0b13f484f5af494e278901e441", + "reference": "d6592be9b27a7c0b13f484f5af494e278901e441", + "shasum": "" + }, + "require": { + "beste/clock": "^2.1", + "beste/json": "^1.0", + "ext-ctype": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "fig/http-message-util": "^1.1", + "google/auth": "^1.21", + "google/cloud-core": "^1.44.2", + "google/cloud-storage": "^1.26.2", + "guzzlehttp/guzzle": "^7.4.5", + "kreait/firebase-tokens": "^3.0", + "lcobucci/jwt": "^4.1", + "mtdowling/jmespath.php": "^2.6.1", + "php": "^7.4|^8.0", + "psr/cache": "^1.0.1|^2.0|^3.0", + "psr/log": "^1.1|^2.0|^3.0", + "riverline/multipart-parser": "^2.0.8", + "symfony/polyfill-php80": "^1.23", + "symfony/polyfill-php81": "^1.23" + }, + "require-dev": { + "google/cloud-firestore": "^1.25.1", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8.2", + "phpstan/phpstan-phpunit": "^1.1.1", + "phpunit/phpunit": "^9.5.22", + "symfony/var-dumper": "^5.4|^6.1.3" + }, + "suggest": { + "google/cloud-firestore": "^1.0 to use the Firestore component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-7.x": "7.x-dev", + "dev-6.x": "6.x-dev", + "dev-5.x": "5.x-dev", + "dev-4.x": "4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Kreait\\Firebase\\": "src/Firebase" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jérôme Gamez", + "homepage": "https://github.com/jeromegamez" + } + ], + "description": "Firebase Admin SDK", + "homepage": "https://github.com/kreait/firebase-php", + "keywords": [ + "api", + "database", + "firebase", + "google", + "sdk" + ], + "support": { + "docs": "https://firebase-php.readthedocs.io", + "issues": "https://github.com/kreait/firebase-php/issues", + "source": "https://github.com/kreait/firebase-php" + }, + "funding": [ + { + "url": "https://github.com/sponsors/jeromegamez", + "type": "github" + } + ], + "time": "2023-06-10T06:44:56+00:00" + }, + { + "name": "kreait/firebase-tokens", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/kreait/firebase-tokens-php.git", + "reference": "3f732ae04f6548f5130709daf06fcc1ca0561002" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kreait/firebase-tokens-php/zipball/3f732ae04f6548f5130709daf06fcc1ca0561002", + "reference": "3f732ae04f6548f5130709daf06fcc1ca0561002", + "shasum": "" + }, + "require": { + "beste/clock": "^2.0", + "ext-json": "*", + "ext-openssl": "*", + "fig/http-message-util": "^1.1.5", + "guzzlehttp/guzzle": "^7.4.5", + "lcobucci/jwt": "^4.1.5", + "php": "^7.4|^8.0", + "psr/cache": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.4", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.2", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5.10", + "rector/rector": "^0.12.9", + "symfony/cache": "^5.4|^6.0", + "symfony/var-dumper": "^5.3|^6.0" + }, + "suggest": { + "psr/cache-implementation": "to cache fetched remote public keys" + }, + "type": "library", + "autoload": { + "psr-4": { + "Kreait\\Firebase\\JWT\\": "src/JWT" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jérôme Gamez", + "homepage": "https://github.com/jeromegamez" + } + ], + "description": "A library to work with Firebase tokens", + "homepage": "https://github.com/kreait/firebase-token-php", + "keywords": [ + "Authentication", + "auth", + "firebase", + "google", + "token" + ], + "support": { + "issues": "https://github.com/kreait/firebase-tokens-php/issues", + "source": "https://github.com/kreait/firebase-tokens-php/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sponsors/jeromegamez", + "type": "github" + } + ], + "time": "2022-08-22T21:40:00+00:00" + }, + { + "name": "kreait/laravel-firebase", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/kreait/laravel-firebase.git", + "reference": "35bdebd32cde14735e4edaa13d6684becf6607b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kreait/laravel-firebase/zipball/35bdebd32cde14735e4edaa13d6684becf6607b4", + "reference": "35bdebd32cde14735e4edaa13d6684becf6607b4", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^8.0 || ^9.0", + "illuminate/support": "^8.0 || ^9.0", + "kreait/firebase-php": "^6.7", + "php": "^7.4 || ^8.0", + "symfony/cache": "^5.4 || ^6.1.2" + }, + "require-dev": { + "orchestra/testbench": "^6.0 || 7.0", + "symplify/easy-coding-standard": "^10.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + }, + "laravel": { + "providers": [ + "Kreait\\Laravel\\Firebase\\ServiceProvider" + ], + "aliases": { + "Firebase": "Kreait\\Laravel\\Firebase\\Facades\\Firebase" + } + } + }, + "autoload": { + "psr-4": { + "Kreait\\Laravel\\Firebase\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jérôme Gamez", + "email": "jerome@gamez.name" + } + ], + "description": "A Laravel package for the Firebase PHP Admin SDK", + "keywords": [ + "FCM", + "api", + "database", + "firebase", + "gcm", + "laravel", + "sdk" + ], + "support": { + "issues": "https://github.com/kreait/laravel-firebase/issues", + "source": "https://github.com/kreait/laravel-firebase/tree/4.2.0" + }, + "funding": [ + { + "url": "https://www.buymeacoffee.com/jeromegamez", + "type": "custom" + }, + { + "url": "https://www.paypal.me/jeromegamez", + "type": "custom" + }, + { + "url": "https://github.com/jeromegamez", + "type": "github" + }, + { + "url": "https://ko-fi.com/jeromegamez", + "type": "ko_fi" + }, + { + "url": "https://www.patreon.com/jeromegamez", + "type": "patreon" + } + ], + "time": "2022-07-28T18:03:37+00:00" + }, + { + "name": "laravel-notification-channels/fcm", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/laravel-notification-channels/fcm.git", + "reference": "e77f18d314d2d466cee76efe2c7ec051f2345286" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel-notification-channels/fcm/zipball/e77f18d314d2d466cee76efe2c7ec051f2345286", + "reference": "e77f18d314d2d466cee76efe2c7ec051f2345286", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.2 || ^7.0", + "illuminate/notifications": "~5.6 || ~6.0 || ~7.0 || ~8.0 || ~9.0|^10.0", + "illuminate/support": "~5.6 || ~6.0 || ~7.0 || ~8.0 || ~9.0|^10.0", + "kreait/laravel-firebase": "^1.3 || ^2.1 || ^3.0 || ^4.0|^1.0", + "php": ">=7.1.3", + "spatie/enum": "^2.3 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.5.1", + "phpunit/phpunit": "^9.5.10" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NotificationChannels\\Fcm\\FcmServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "NotificationChannels\\Fcm\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Bautista", + "email": "chris.bautista@coreproc.ph", + "homepage": "https://coreproc.com", + "role": "Developer" + } + ], + "description": "FCM (Firebase Cloud Messaging) Notifications Driver for Laravel", + "homepage": "https://github.com/laravel-notification-channels/fcm", + "support": { + "issues": "https://github.com/laravel-notification-channels/fcm/issues", + "source": "https://github.com/laravel-notification-channels/fcm/tree/2.7.0" + }, + "time": "2023-01-31T23:52:17+00:00" + }, { "name": "laravel/framework", "version": "v9.52.16", @@ -2306,6 +3339,144 @@ }, "time": "2023-08-15T14:27:00+00:00" }, + { + "name": "lcobucci/clock", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/clock.git", + "reference": "6f28b826ea01306b07980cb8320ab30b966cd715" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/clock/zipball/6f28b826ea01306b07980cb8320ab30b966cd715", + "reference": "6f28b826ea01306b07980cb8320ab30b966cd715", + "shasum": "" + }, + "require": { + "php": "~8.2.0 || ~8.3.0", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "infection/infection": "^0.27", + "lcobucci/coding-standard": "^11.0.0", + "phpstan/extension-installer": "^1.3.1", + "phpstan/phpstan": "^1.10.25", + "phpstan/phpstan-deprecation-rules": "^1.1.3", + "phpstan/phpstan-phpunit": "^1.3.13", + "phpstan/phpstan-strict-rules": "^1.5.1", + "phpunit/phpunit": "^10.2.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\Clock\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com" + } + ], + "description": "Yet another clock abstraction", + "support": { + "issues": "https://github.com/lcobucci/clock/issues", + "source": "https://github.com/lcobucci/clock/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2023-11-17T17:00:27+00:00" + }, + { + "name": "lcobucci/jwt", + "version": "4.3.0", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "reference": "4d7de2fe0d51a96418c0d04004986e410e87f6b4", + "shasum": "" + }, + "require": { + "ext-hash": "*", + "ext-json": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-sodium": "*", + "lcobucci/clock": "^2.0 || ^3.0", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "infection/infection": "^0.21", + "lcobucci/coding-standard": "^6.0", + "mikey179/vfsstream": "^1.6.7", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/php-invoker": "^3.1", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Lcobucci\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Luís Cobucci", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "keywords": [ + "JWS", + "jwt" + ], + "support": { + "issues": "https://github.com/lcobucci/jwt/issues", + "source": "https://github.com/lcobucci/jwt/tree/4.3.0" + }, + "funding": [ + { + "url": "https://github.com/lcobucci", + "type": "github" + }, + { + "url": "https://www.patreon.com/lcobucci", + "type": "patreon" + } + ], + "time": "2023-01-02T13:28:00+00:00" + }, { "name": "league/commonmark", "version": "2.4.1", @@ -3419,6 +4590,72 @@ ], "time": "2023-10-27T15:25:26+00:00" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.7.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/bbb69a935c2cbb0c03d7f481a238027430f6440b", + "reference": "bbb69a935c2cbb0c03d7f481a238027430f6440b", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^3.0.3", + "phpunit/phpunit": "^8.5.33" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "files": [ + "src/JmesPath.php" + ], + "psr-4": { + "JmesPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.7.0" + }, + "time": "2023-08-25T10:54:48+00:00" + }, { "name": "nesbot/carbon", "version": "2.72.1", @@ -4384,6 +5621,55 @@ ], "time": "2023-11-12T21:59:55+00:00" }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, { "name": "psr/clock", "version": "1.0.0", @@ -5162,6 +6448,124 @@ ], "time": "2023-11-08T05:53:05+00:00" }, + { + "name": "riverline/multipart-parser", + "version": "2.1.2", + "source": { + "type": "git", + "url": "https://github.com/Riverline/multipart-parser.git", + "reference": "7a9f4646db5181516c61b8e0225a343189beedcd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Riverline/multipart-parser/zipball/7a9f4646db5181516c61b8e0225a343189beedcd", + "reference": "7a9f4646db5181516c61b8e0225a343189beedcd", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=5.6.0" + }, + "require-dev": { + "laminas/laminas-diactoros": "^1.8.7 || ^2.11.1", + "phpunit/phpunit": "^5.7 || ^9.0", + "psr/http-message": "^1.0", + "symfony/psr-http-message-bridge": "^1.1 || ^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Riverline\\MultiPartParser\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Romain Cambien", + "email": "romain@cambien.net" + }, + { + "name": "Riverline", + "homepage": "http://www.riverline.fr" + } + ], + "description": "One class library to parse multipart content with encoding and charset support.", + "keywords": [ + "http", + "multipart", + "parser" + ], + "support": { + "issues": "https://github.com/Riverline/multipart-parser/issues", + "source": "https://github.com/Riverline/multipart-parser/tree/2.1.2" + }, + "time": "2024-03-12T16:46:05+00:00" + }, + { + "name": "rize/uri-template", + "version": "0.3.6", + "source": { + "type": "git", + "url": "https://github.com/rize/UriTemplate.git", + "reference": "34efe65c79710eed0883884f2285ae6d4a0aad19" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rize/UriTemplate/zipball/34efe65c79710eed0883884f2285ae6d4a0aad19", + "reference": "34efe65c79710eed0883884f2285ae6d4a0aad19", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "Rize\\": "src/Rize" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marut K", + "homepage": "http://twitter.com/rezigned" + } + ], + "description": "PHP URI Template (RFC 6570) supports both expansion & extraction", + "keywords": [ + "RFC 6570", + "template", + "uri" + ], + "support": { + "issues": "https://github.com/rize/UriTemplate/issues", + "source": "https://github.com/rize/UriTemplate/tree/0.3.6" + }, + "funding": [ + { + "url": "https://www.paypal.me/rezigned", + "type": "custom" + }, + { + "url": "https://github.com/rezigned", + "type": "github" + }, + { + "url": "https://opencollective.com/rize-uri-template", + "type": "open_collective" + } + ], + "time": "2024-03-10T08:07:49+00:00" + }, { "name": "sabberworm/php-css-parser", "version": "8.4.0", @@ -5281,6 +6685,82 @@ ], "time": "2023-12-21T10:00:28+00:00" }, + { + "name": "spatie/enum", + "version": "3.13.0", + "source": { + "type": "git", + "url": "https://github.com/spatie/enum.git", + "reference": "f1a0f464ba909491a53e60a955ce84ad7cd93a2c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/spatie/enum/zipball/f1a0f464ba909491a53e60a955ce84ad7cd93a2c", + "reference": "f1a0f464ba909491a53e60a955ce84ad7cd93a2c", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": "^8.0" + }, + "require-dev": { + "fakerphp/faker": "^1.9.1", + "larapack/dd": "^1.1", + "phpunit/phpunit": "^9.0", + "vimeo/psalm": "^4.3" + }, + "suggest": { + "fakerphp/faker": "To use the enum faker provider", + "phpunit/phpunit": "To use the enum assertions" + }, + "type": "library", + "autoload": { + "psr-4": { + "Spatie\\Enum\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brent Roose", + "email": "brent@spatie.be", + "homepage": "https://spatie.be", + "role": "Developer" + }, + { + "name": "Tom Witkowski", + "email": "dev@gummibeer.de", + "homepage": "https://gummibeer.de", + "role": "Developer" + } + ], + "description": "PHP Enums", + "homepage": "https://github.com/spatie/enum", + "keywords": [ + "enum", + "enumerable", + "spatie" + ], + "support": { + "docs": "https://docs.spatie.be/enum", + "issues": "https://github.com/spatie/enum/issues", + "source": "https://github.com/spatie/enum" + }, + "funding": [ + { + "url": "https://spatie.be/open-source/support-us", + "type": "custom" + }, + { + "url": "https://github.com/spatie", + "type": "github" + } + ], + "time": "2022-04-22T08:51:55+00:00" + }, { "name": "spatie/image", "version": "2.2.7", @@ -5548,6 +7028,225 @@ ], "time": "2023-12-25T11:46:58+00:00" }, + { + "name": "stella-maris/clock", + "version": "0.1.7", + "source": { + "type": "git", + "url": "https://github.com/stella-maris-solutions/clock.git", + "reference": "fa23ce16019289a18bb3446fdecd45befcdd94f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stella-maris-solutions/clock/zipball/fa23ce16019289a18bb3446fdecd45befcdd94f8", + "reference": "fa23ce16019289a18bb3446fdecd45befcdd94f8", + "shasum": "" + }, + "require": { + "php": "^7.0|^8.0", + "psr/clock": "^1.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "StellaMaris\\Clock\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Heigl", + "role": "Maintainer" + } + ], + "description": "A pre-release of the proposed PSR-20 Clock-Interface", + "homepage": "https://gitlab.com/stella-maris/clock", + "keywords": [ + "clock", + "datetime", + "point in time", + "psr20" + ], + "support": { + "source": "https://github.com/stella-maris-solutions/clock/tree/0.1.7" + }, + "time": "2022-11-25T16:15:06+00:00" + }, + { + "name": "symfony/cache", + "version": "v6.4.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "b9e9b93c9817ec6c789c7943f5e54b57a041c16a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/b9e9b93c9817ec6c789c7943f5e54b57a041c16a", + "reference": "b9e9b93c9817ec6c789c7943f5e54b57a041c16a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.3.6|^7.0" + }, + "conflict": { + "doctrine/dbal": "<2.13.1", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/var-dumper": "<5.4" + }, + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/filesystem": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "classmap": [ + "Traits/ValueWrapper.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "support": { + "source": "https://github.com/symfony/cache/tree/v6.4.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:22:46+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v3.5.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/df6a1a44c890faded49a5fca33c2d5c5fd3c2197", + "reference": "df6a1a44c890faded49a5fca33c2d5c5fd3c2197", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.5.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:32:20+00:00" + }, { "name": "symfony/console", "version": "v6.4.2", @@ -6999,6 +8698,82 @@ ], "time": "2023-01-26T09:26:14+00:00" }, + { + "name": "symfony/polyfill-php81", + "version": "v1.29.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d", + "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-01-29T20:11:03+00:00" + }, { "name": "symfony/polyfill-php83", "version": "v1.28.0", @@ -7805,6 +9580,82 @@ ], "time": "2023-12-28T19:16:56+00:00" }, + { + "name": "symfony/var-exporter", + "version": "v7.0.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "cdecc0022e40e90340ba1a59a3d5ccf069777078" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/cdecc0022e40e90340ba1a59a3d5ccf069777078", + "reference": "cdecc0022e40e90340ba1a59a3d5ccf069777078", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "symfony/property-access": "^6.4|^7.0", + "symfony/serializer": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v7.0.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-04-18T09:29:19+00:00" + }, { "name": "tijsverkoyen/css-to-inline-styles", "version": "v2.2.7", @@ -10101,55 +11952,6 @@ ], "time": "2023-12-01T16:55:19+00:00" }, - { - "name": "psr/cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" - }, - "time": "2021-02-03T23:26:27+00:00" - }, { "name": "sebastian/cli-parser", "version": "1.0.1", From 6fef852e2cba44fa1b4a452dd34a867dc27135da Mon Sep 17 00:00:00 2001 From: ivan-sim Date: Tue, 21 May 2024 17:01:56 +0700 Subject: [PATCH 05/13] Update Import Billing & Invoice --- .../Controllers/Api/CorporateController.php | 6 + .../Controllers/Api/RequestLogController.php | 185 +++++++++++++++++- Modules/Internal/Routes/api.php | 6 +- .../pages/CustomerService/FinalLog/List.tsx | 72 +++++-- public/files/Template Import Invoice.xlsx | Bin 0 -> 10028 bytes 5 files changed, 251 insertions(+), 18 deletions(-) create mode 100644 public/files/Template Import Invoice.xlsx diff --git a/Modules/Internal/Http/Controllers/Api/CorporateController.php b/Modules/Internal/Http/Controllers/Api/CorporateController.php index 91870d34..8208ca90 100644 --- a/Modules/Internal/Http/Controllers/Api/CorporateController.php +++ b/Modules/Internal/Http/Controllers/Api/CorporateController.php @@ -575,6 +575,12 @@ class CorporateController extends Controller "file_url" => url('files/Template Import Request LOG.xlsx') ]); break; + case 'final-log-invoice': + return Helper::responseJson([ + 'file_name' => "Template Import Invoice.xlsx", + "file_url" => url('files/Template Import Invoice.xlsx') + ]); + break; default: return Helper::responseJson([], 'error', 404); break; diff --git a/Modules/Internal/Http/Controllers/Api/RequestLogController.php b/Modules/Internal/Http/Controllers/Api/RequestLogController.php index 6418e627..05af4eeb 100644 --- a/Modules/Internal/Http/Controllers/Api/RequestLogController.php +++ b/Modules/Internal/Http/Controllers/Api/RequestLogController.php @@ -22,6 +22,8 @@ use Carbon\Carbon; use Maatwebsite\Excel\Facades\Excel; use Box\Spout\Reader\Common\Creator\ReaderEntityFactory; use Box\Spout\Writer\Common\Creator\WriterEntityFactory; +use Box\Spout\Writer\Common\Creator\Style\StyleBuilder; +use Box\Spout\Common\Entity\Style\CellAlignment; use Exception; @@ -212,7 +214,7 @@ class RequestLogController extends Controller }); return Helper::responseJson(data: $manipulatedIcds); } - + public function hospitals(){ $organizations = Organization::query() ->where([ @@ -330,7 +332,7 @@ class RequestLogController extends Controller // Hapus semua manfaat log permintaan terkait RequestLogBenefit::where('request_log_id', $id)->delete(); - + return response()->json([ 'error' => false, 'message' => 'Delete Final LOG', @@ -461,7 +463,7 @@ class RequestLogController extends Controller // if($requestLog->service_code != 'IP'){ // $requestLog->discharge_date = Carbon::now(); // } - + $requestLog->save(); @@ -670,6 +672,181 @@ class RequestLogController extends Controller ]; } + public function importInvoice(Request $request) + { + if ($request->hasFile('file')) { + $file = $request->file('file'); + $data = Excel::toArray([], $file); + + $processedData = $this->processCategoryNames($data); + + $importedRows = 0; + $result_rows = []; + $failedRows = []; + + foreach ($processedData as $row) { + if($row['code']) + { + try { + $affectedRows = DB::table('request_logs') + ->where('code','=', $row['code']) + ->update([ + 'invoice_no' => $row['invoice_no'], + 'billing_no' => $row['billing_no'], + ]); + + if ($affectedRows === 0) { + $row['code_error'] = '500'; + $row['error'] = 'Gagal update karena data sudah ada '; + $result_rows[] = $row; + $failedRows[] = $row; + } else { + $importedRows += $affectedRows; + $row['code_error'] = '200'; + $row['error'] = 'Sukses'; + $result_rows[] = $row; + } + } catch (\Exception $e) { + $row['code_error'] = '500'; + $row['error'] = $e->getMessage(); + if(!$row['code']) + { + $row['error'] = 'Kolom Code wajib isi'; + } + if(!$row['invoice_no']) + { + $row['error'] = 'No Invoice wajib isi'; + } + if(!$row['billing_no']) + { + $row['error'] = 'No Billing wajib isi'; + } + $result_rows[] = $row; + $failedRows[] = $row; + } + } + } + + $response = [ + 'message' => 'File uploaded and data saved to database', + 'metaData' => 'invoice', + 'data' => [ + 'total_success_row' => $importedRows, + 'total_failed_row' => count($failedRows), + 'result_rows' => $result_rows, + ], + ]; + + 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; + } + + public function exportFiledInvoice(Request $request) + { + $writer = WriterEntityFactory::createXLSXWriter(); + $writer->openToFile(public_path('files/Report-Data-Result-Import.xlsx')); + $header = [ + 'Code*', + 'Inovice No*', + 'Billing NO*', + 'Ingest Code', + 'Ingest Note' + ]; + $style = (new StyleBuilder()) + ->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + + $headerRow = WriterEntityFactory::createRowFromArray($header, $style); + $writer->addRow($headerRow); + // ============================ + + foreach($request->params as $item) + { + + $rowData = [ + $item['code'], + $item['invoice_no'], + $item['billing_no'], + $item['code_error'], + $item['error'] + ]; + $style = (new StyleBuilder()) + //->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + $row = WriterEntityFactory::createRowFromArray($rowData, $style); + $writer->addRow($row); + } + $footer = [ + '', + '', + '', + '', + '' + ]; + $style = (new StyleBuilder()) + ->setFontBold() + // ->setFontSize(15) + // ->setFontColor(Color::BLUE) + // ->setShouldWrapText() + ->setCellAlignment(CellAlignment::LEFT) + // ->setBackgroundColor(Color::YELLOW) + ->build(); + + $footerRow = WriterEntityFactory::createRowFromArray($footer, $style); + $writer->addRow($footerRow); + + $writer->close(); + + return Helper::responseJson([ + 'file_name' => 'Report-Data-Result-Import', + "file_url" => url('files/Report-Data-Result-Import.xlsx') + ]); + } + public function claimRequestDetail($claimRequestId) { $status = DB::table('claim_requests') @@ -962,7 +1139,7 @@ class RequestLogController extends Controller // Menghapus file dari penyimpanan if (Storage::exists($path)) { Storage::delete($path); - + // Update entri file dari basis data File::where('path', $request->path)->update([ 'deleted_at' => Carbon::now(), // Gunakan Carbon untuk mendapatkan tanggal dan waktu saat ini diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 56fc9861..c0a19526 100644 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -278,6 +278,8 @@ Route::prefix('internal')->group(function () { Route::put('customer-service/request/final_log/{id}', [RequestLogController::class, 'deleteFinalLog']); Route::get('customer-service/request/{id}/download', [RequestLogController::class, 'generateRequestLog']); Route::post('customer-service/request/import', [RequestLogController::class, 'importRequestLog']); + Route::post('customer-service/request/import-invoice', [RequestLogController::class, 'importInvoice']); + Route::post('customer-service/request/exportFiledInvoice', [RequestLogController::class, 'exportFiledInvoice']); Route::get('customer-service/request/data', [RequestLogController::class, 'generateDataRequestLogExcel']); Route::post('customer-service/request/{id}/add_file', [RequestLogController::class, 'requestFiles']); Route::post('customer-service/request/{id}/delete_file', [RequestLogController::class, 'deleteFiles']); @@ -306,13 +308,13 @@ Route::prefix('internal')->group(function () { Route::resource('appointments', AppointmentController::class); Route::get('live-chat/export', [LivechatController::class, 'export']); Route::resource('live-chat', LivechatController::class); - + Route::get('prescription', [PrescriptionController::class, 'index']); Route::post('prescription', [PrescriptionController::class, 'store']); Route::get('prescription-download/{id}', [PrescriptionController::class, 'downloadPrescription']); - + Route::get('prescription/{id}', [PrescriptionController::class, 'index']); Route::get('doctorrating', [DoctorRatingController::class, 'index']); Route::get('doctorrating/{id}', [PrescriptionController::class, 'index']); diff --git a/frontend/dashboard/src/pages/CustomerService/FinalLog/List.tsx b/frontend/dashboard/src/pages/CustomerService/FinalLog/List.tsx index 40d31b34..cd580050 100644 --- a/frontend/dashboard/src/pages/CustomerService/FinalLog/List.tsx +++ b/frontend/dashboard/src/pages/CustomerService/FinalLog/List.tsx @@ -115,13 +115,12 @@ export default function List() { const handleClose = () => { setAnchorEl(null); }; - - const handleImportButton = () => { + const [paramImport, setParamImport] = useState(''); + const handleImportButton = (param:any) => { + setParamImport(param); if (importForm?.current) { handleClose(); importForm.current ? importForm.current.click() : console.log('No File selected'); - } else { - alert('No file selected'); } }; @@ -144,12 +143,18 @@ export default function List() { formData.append('file', importForm.current?.files[0]); setImportLoading(true); + let url = 'claim-requests/import'; + if(paramImport == 'invoice') + { + url = 'customer-service/request/import-invoice' + } axios - .post(`claim-requests/import`, formData) + .post(`${url}`, formData) .then((response) => { handleCancelImportButton(); - loadDataTableData(); + // loadDataTableData(); setImportResult(response.data); + setParamImport(response.data.metaData); // alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows'); setImportLoading(false); }) @@ -166,6 +171,28 @@ export default function List() { } }; + const handleExportReportFiled = async () => { + + await axios + .post('customer-service/request/exportFiledInvoice', { params: importResult?.data.result_rows }) + .then((res) => { + enqueueSnackbar('Data berhasil di Export', { + variant: 'success', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }); + setImportLoading(false); + + document.location.href = res.data.data.file_url; + }) + .catch((err) => + enqueueSnackbar('Data Gagal di Export', { + variant: 'error', + anchorOrigin: { horizontal: 'right', vertical: 'top' }, + }) + + ); + }; + const handleGetTemplate = (type :string) => { axios.get('corporates/import-document-example/' + type) .then((response) => { @@ -220,9 +247,11 @@ export default function List() { 'aria-labelledby': 'basic-button', }} > - Import + {handleImportButton('claim')}}>Import {handleGetTemplate('claim-request')}}>Download Template {handleGetData('data-plan-benefit')}}>Download Claim Request + {handleImportButton('invoice')}}>Import Invoice + {handleGetTemplate('final-log-invoice')}}>Download Template Invoice {/*