diff --git a/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php b/Modules/HospitalPortal/Http/Controllers/Api/AuthController.php index bf221968..701b0f78 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" ]); } @@ -92,52 +94,202 @@ class AuthController extends Controller public function verifyEmail(Request $request) { - $request->validate([ + $data = [ + 'email' => $request->email, + ]; + + $validator = Validator::make($request->all(), [ 'email' => 'required|email', + ], [ + 'email.required' => trans('Validation.required',['attribute' => 'Email']), + 'email.email' => trans('Validation.email'), ]); - $user = User::query() - ->where('email', $request->email) + 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); + } + + //send email + // Insert data notifications + $emailTo = $request->email; + $dataNotif = [ + 'user_id' => $user->id, + 'email' => $emailTo, + 'title' => 'Forgot Password', + 'description' => 'Request forgot password from Hospital Portal', + '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 Hospital Portal 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) + ->first(); + + return ApiResponse::apiResponse("Success", $res, trans('Message.success'), 200); + } + else + { + return ApiResponse::apiResponse("Internal Server Error", $data, trans('Message.server_error'), 500); + } + } + } + + 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 (!$user) { - return response(['message' => 'User Tidak Ditemukan'], 404); + 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); + } } - - Event(new ForgetPassword($user)); - - // Mail::to($user->email)->send(new SendVerifyEmail($user)); - - return response()->json($user); } public function forgetPassword(Request $request) { - $request->validate([ - 'new_password' => 'required', - 'confirm_new_password' => 'required' + $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']), ]); - $token = Crypt::decryptString($request->token); - $email = explode('|', $token)[0]; - - $user = User::query() - ->where('email', $email) + 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 (!$user) { - return response(['message' => 'User Tidak Ditemukan'], 404); - } + if($check) + { + $created_at = strtotime($check->created_at); // Konversi string waktu ke UNIX timestamp + $now = time(); // Waktu sekarang dalam UNIX timestamp - if ($request["new_password"] != $request["confirm_new_password"]) { - return response([ - 'message' => "Password Tidak Sama" - ], 404); - } + // Hitung selisih waktu dalam menit + $diffInMinutes = ($now - $created_at) / 60; - $user->update([ - 'password' => Hash::make($request->confirm_new_password), - ]); - return response()->json($user); + 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 3b73f731..a0591a36 100644 --- a/Modules/HospitalPortal/Routes/api.php +++ b/Modules/HospitalPortal/Routes/api.php @@ -28,9 +28,10 @@ Route::prefix('v1')->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('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/Modules/Internal/Http/Controllers/Api/CorporateController.php b/Modules/Internal/Http/Controllers/Api/CorporateController.php index bbb4c9d2..ab6bdea7 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 35fc734e..41364961 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; @@ -672,6 +674,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') diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index e7b29f1e..6c7ce2d2 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']); diff --git a/Modules/Linksehat/Http/Controllers/Api/AuthController.php b/Modules/Linksehat/Http/Controllers/Api/AuthController.php index 8d5a0d30..543897f8 100644 --- a/Modules/Linksehat/Http/Controllers/Api/AuthController.php +++ b/Modules/Linksehat/Http/Controllers/Api/AuthController.php @@ -19,33 +19,47 @@ 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', // 'phone' => '', // 'otp' => 'required_with:phone', 'password' => 'required', + 'fcm_token' => 'required' // 'otp' => 'required' ]); // 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(); + + $user->notificationTokens()->updateOrCreate([ + 'device_id' => $request->device_id, + 'token' => $request->fcm_token, + ], [ + 'origin' => $request->origin, + 'device_id' => $request->device_id, + 'type' => $request->type, + 'token' => $request->fcm_token, + 'status' => $request->status, + ]); + return Helper::responseJson( data: [ 'token' => $user->createToken('app')->plainTextToken, @@ -58,18 +72,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 +94,8 @@ class AuthController extends Controller }; } - public function resetPassword(Request $request){ + public function resetPassword(Request $request) + { $request->validate([ 'email' => 'required|email', 'code' => 'required', @@ -87,14 +103,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 +121,7 @@ class AuthController extends Controller } public function loginPhone(Request $request) - { + { $request->validate([ 'phone_or_email' => 'required', 'otp' => 'required', @@ -116,10 +132,9 @@ class AuthController extends Controller 'sPhone' => $request->phone_or_email, 'sVerificationCode' => $request->otp ]) - ->first(); - - if ($user){ + ->first(); + if ($user) { $updateVericationCode = User::with('detail') ->where([ 'sPhone' => $request->phone_or_email, @@ -127,6 +142,9 @@ class AuthController extends Controller ])->update([ 'sVerificationCode' => null, ]); + $user->fcm_token = $request->fcm_token; + $user->save(); + return Helper::responseJson( data: [ 'token' => $user->createToken('app')->plainTextToken, @@ -145,15 +163,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 +186,6 @@ class AuthController extends Controller message: 'Nomor tidak ditemukan' ); } - - - } public function register(Request $request) @@ -209,7 +224,7 @@ class AuthController extends Controller return Helper::responseJson(message: 'Behasil Logout.'); } - + public function mockOtp(Request $request) { $request->validate([ @@ -256,4 +271,32 @@ 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->fcm_token = $request->token; + $user->save(); + // $userUpdate = User::where + + $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/Http/Controllers/Api/ChatController.php b/Modules/Linksehat/Http/Controllers/Api/ChatController.php index a6a3307b..b1659599 100644 --- a/Modules/Linksehat/Http/Controllers/Api/ChatController.php +++ b/Modules/Linksehat/Http/Controllers/Api/ChatController.php @@ -74,7 +74,7 @@ class ChatController extends Controller public function listChannel(Request $request){ // Validasi request jika diperlukan $channel = Channel::where('member_id',$request->user_id)->get()->toArray(); - + if (!$channel) { $dataChannel = Channel::where('doctor_id',$request->user_id)->get()->toArray(); $data = []; @@ -86,11 +86,11 @@ class ChatController extends Controller ->first(); $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; $avatarMember = $user->detail->sImage ?? $urlAvatarDefault; - - $arr['id'] = $d['id']; - $arr['avatar'] = $avatarMember; - $arr['name'] = $user->sFirstName .' '.$user->sLastName; - $arr['last_message'] = $lastMessage; + + $arr['id'] = $d['id']; + $arr['avatar'] = $avatarMember; + $arr['name'] = $user->sFirstName .' '.$user->sLastName; + $arr['last_message'] = $lastMessage; array_push($data, $arr); } @@ -101,14 +101,14 @@ class ChatController extends Controller return response()->json(['message' => 'Get List Channel successfully', 'channel' => $channel]); } - + public function sendMessage(Request $request) { // Validasi request jika diperlukan $validatedData = $request->validate([ 'user_id' => 'required' ]); - + // Ambil data dari request $message = Message::create([ 'content' => $request->message, @@ -142,7 +142,7 @@ class ChatController extends Controller ]); } // Berikan respons yang sesuai ke klien - + $channel = Channel::where('id',$request->channel_id)->first(); if($channel->member_id == $request->user_id){ // Get nama dokter @@ -151,13 +151,13 @@ class ChatController extends Controller } else { // Get nama pasien $person = User::where('nID', $channel->member_id)->first(); - $name = $person->sFirstName . ' ' . $person->sLastName; + $name = $person->sFirstName . ' ' . $person->sLastName; } ChatMessageSent::dispatch($message); - + return response()->json([ - 'message' => 'Message sent successfully', + 'message' => 'Message sent successfully', 'data' => [ 'header' => $name, ] @@ -169,8 +169,8 @@ class ChatController extends Controller // Buat instance Pusher dengan konfigurasi yang sesuai $channel = Channel::where('id',$request->channel_id)->first(); $livechat = Livechat::where([ - 'doctor_id' => $channel->doctor_id, - 'patient_id' => $channel->member_id, + 'doctor_id' => $channel->doctor_id, + 'patient_id' => $channel->member_id, ])->latest('created_at')->first(); if($channel->member_id == $request->user_id){ @@ -181,37 +181,61 @@ class ChatController extends Controller $age = ''; $gender = ''; $question = ''; - + $consultationStart = $livechat->start_date; $consultationEnd = $livechat->end_date; $work = ''; + $weight = '-'; + $height = '-'; $address = ''; + $maritalStatus = ''; } else { // Get nama pasien $user = User::where('nID', $channel->member_id)->with('detail')->first(); - $name = $user->sFirstName . ' ' . $user->sLastName; - $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; - $avatar = $user->detail->sImage ?? $urlAvatarDefault; - $gender = DB::connection('oldlms')->table('tm_jenis_kelamin')->where('nID', $user->detail->nIDJenisKelamin)->first('sJenisKelamin'); - if ($gender){ - $gender = $gender->sJenisKelamin; + $name = $user->sFirstName . ' ' . $user->sLastName; + $gender = null; + if ($user->detail) { + $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatar = $user->detail->sImage ?? $urlAvatarDefault; + $gender = DB::connection('oldlms')->table('tm_jenis_kelamin')->where('nID', $user->detail->nIDJenisKelamin)->first('sJenisKelamin'); + $age = Helper::calculateAge($user->detail->dTanggalLahir); + if ($gender){ + $gender = $gender->sJenisKelamin; + } + $work = DB::connection('oldlms')->table('tm_pekerjaan')->where('nID', $user->detail->nIDPekerjaan)->first('sPekerjaan'); + + if($work){ + $work = $work->sPekerjaan; + } + $maritalStatus = DB::connection('oldlms')->table('tm_status_pernikahan')->where('nID', $user->detail->sMartialStatus)->first('sStatusPernikahan'); + if($maritalStatus){ + $maritalStatus = $maritalStatus->sStatusPernikahan; + } + + $weight = $user->detail->sWeight; + $height = $user->detail->sWeight; + } else { + $avatar = 'https://linksehat.dev/assets/img/users/male-avatar.png'; + $age = '-'; + $work = '-'; + $maritalStatus = '-'; + $weight = '-'; + $height = '-'; + } - $age = Helper::calculateAge($user->detail->dTanggalLahir); + $question = $livechat->descriptions; $consultationStart = $livechat->start_date; $consultationEnd = $livechat->end_date; - $work = DB::connection('oldlms')->table('tm_pekerjaan')->where('nID', $user->detail->nIDPekerjaan)->first('sPekerjaan'); - - if($work){ - $work = $work->sPekerjaan; - } + $address = DB::connection('oldlms')->table('tm_users_address')->where('nIDUser', $user->nID)->first('sAlamat'); if($address){ $address = $address->sAlamat; } + } - + // Ini Untul Chat $perPage = $request->input('per_page', 10); // Default 10 pesan per halaman $page = $request->input('page', 1); // Default halaman 1 @@ -220,7 +244,7 @@ class ChatController extends Controller ->where('type', '!=', 'trigger') ->orderBy('created_at', 'desc') // Urutkan berdasarkan created_at secara descending ->paginate($perPage, ['*'], 'page', $page); - + // Data Consultation Summary $consultationSummary = [ 'subject' => $livechat->subject, @@ -240,12 +264,22 @@ class ChatController extends Controller 'header' => $name, 'avatar' => $avatar, 'gender' => $gender, + 'marital_status' => $maritalStatus, 'age' => $age, + 'weight' => $weight, + 'height' => $height, 'question' => $question, + 'diseases' => [], + 'medications' => [], + 'allergy' => [], + 'family_history' => [], 'start' => $consultationStart, 'end' => $consultationEnd, 'work' => $work, 'address' => $address, + 'consultation_id' => $livechat->id, + 'consultation_status' => Helper::getStatusLivechat($livechat->status), + 'health_sertificate' => $healthSertificate, 'chat' => $data->items(), 'pagination' => [ 'total' => $data->total(), @@ -256,8 +290,7 @@ class ChatController extends Controller 'to' => $data->lastItem(), ], 'summary' => $consultationSummary, - 'livechat_id' => $livechat->id, - 'health_sertificate' => $healthSertificate, + ] ]); } @@ -278,7 +311,7 @@ class ChatController extends Controller ])->latest('created_at')->first(); $user = User::where('nID', $livechat->patient_id)->with('detail')->first(); - $name = $user->sFirstName . ' ' . $user->sLastName; + $name = $user->sFirstName . ' ' . $user->sLastName; $age = Helper::calculateAge($user->detail->dTanggalLahir); $person = Person::where('id', $livechat->doctor_id)->first(); @@ -294,7 +327,7 @@ class ChatController extends Controller } // Memuat view pdf_view.php ke dalam variabel $calculateDate = Helper::calculateDateDifference($livechat->health_certificate_start, $livechat->health_certificate_end); - + $data = [ 'name' => $name, 'age' => $age, diff --git a/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php b/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php index 4bcc60f1..14c36b91 100644 --- a/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php +++ b/Modules/Linksehat/Http/Controllers/Api/Doctor/AuthDoctorController.php @@ -24,15 +24,18 @@ class AuthDoctorController extends Controller { $data = [ 'email' => $request->email, - 'password' => $request->password + 'password' => $request->password, + 'fcm_token' => $request->fcm_token, ]; $validator = Validator::make($request->all(), [ 'email' => 'required|email', - 'password' => 'required' + 'password' => 'required', + 'fcm_token' => 'required' ], [ 'email.required' => trans('Validation.required',['attribute' => 'Email']), 'email.email' => trans('Validation.email'), 'password.required' => trans('Validation.required',['attribute' => 'Password']), + 'fcm_token.required' => trans('Validation.required',['attribute' => 'FCM Token']), ]); if ($validator->fails()) @@ -50,6 +53,18 @@ class AuthDoctorController extends Controller return ApiResponse::apiResponse('Bad Request', $data, trans('Message.password'), 400); } + $user->notificationTokens()->updateOrCreate([ + 'device_id' => $request->device_id, + 'token' => $request->fcm_token, + ], [ + 'origin' => $request->origin, + 'device_id' => $request->device_id, + 'type' => $request->type, + 'token' => $request->fcm_token, + 'status' => $request->status, + ]); + + $res_data = [ // 'user' => $user, 'token' => $user->createToken('app')->plainTextToken diff --git a/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php b/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php index 1809f0ac..7b4848db 100644 --- a/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php +++ b/Modules/Linksehat/Http/Controllers/Api/Doctor/ChatDoctorController.php @@ -3,6 +3,7 @@ namespace Modules\Linksehat\Http\Controllers\Api\Doctor; use App\Http\Controllers\Controller; +use App\Notifications\SendNotification; use App\Models\User; use App\Models\OLDLMS\User as UserLMS; use App\Models\Livechat; @@ -23,6 +24,8 @@ use Modules\HospitalPortal\Helpers\ApiResponse; use App\Helpers\Helper; use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\DB; +use Kreait\Firebase\Messaging\CloudMessage; +use Kreait\Laravel\Firebase\Facades\Firebase; class ChatDoctorController extends Controller { @@ -44,8 +47,12 @@ class ChatDoctorController extends Controller if($chat) { foreach($chat as $c){ $patient = UserLMS::where('nID',$c->patient_id)->with('detail')->first(); - $urlAvatarDefault = $patient->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; - $avatarMember = $patient->detail->sImage ?? $urlAvatarDefault; + if ( $patient->detail) { + $urlAvatarDefault = $patient->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; + $avatarMember = $patient->detail->sImage ?? $urlAvatarDefault; + } else { + $avatarMember = 'https://linksehat.dev/assets/img/users/male-avatar.png'; + } $arr['id'] = $c->id; $arr['patient_id'] = $patient->nID; $arr['avatar'] = $avatarMember; @@ -68,11 +75,11 @@ class ChatDoctorController extends Controller } else { $avatarMember = 'https://linksehat.dev/assets/img/users/male-avatar.png'; } - - $arr['id'] = $d['id']; - $arr['avatar'] = $avatarMember; - $arr['name'] = $user->sFirstName .' '.$user->sLastName; - $arr['last_message'] = $lastMessage; + + $arr['id'] = $d['id']; + $arr['avatar'] = $avatarMember; + $arr['name'] = $user->sFirstName .' '.$user->sLastName; + $arr['last_message'] = $lastMessage; array_push($dataOnGoing, $arr); } @@ -119,7 +126,7 @@ class ChatDoctorController extends Controller } return ApiResponse::apiResponse("Success", $data, trans('Message.success'), 200); } - + public function declineChat(Request $request) { $livechat = Livechat::find($request->id); @@ -128,6 +135,34 @@ class ChatDoctorController extends Controller $livechat->status = 3; // Decline // Menyimpan perubahan ke database $livechat->save(); + + // Send Notification + $doctorId = $livechat->doctor_id; + $title = 'Decline Livechat'; + $body = 'Decline Livechat'; + $channel = Channel::where([ + 'member_id' => $livechat->patient_id, + 'doctor_id' => $livechat->doctor_id + ])->first(); + $dataNotif = [ + 'channel_id' => $channel->id, + 'livechat_id' => $livechat->id, + 'type' => 'decline-chat' + ]; + + $user = UserLMS::where('nID',$livechat->patient_id)->first(); + if ($user) { + $user->notify(new SendNotification($title, $body, $dataNotif)); + return ApiResponse::apiResponse("Success",['message' => 'Livechat updated successfully'], trans('Message.success'), 200); + } else { + return Helper::responseJson( + status: 'Not Found', + statusCode: 404, + message: 'Doctor not found.' + ); + } + + return ApiResponse::apiResponse("Success",['message' => 'Livechat updated successfully'], trans('Message.success'), 200); } else { return response()->json(['message' => 'Livechat not found'], 404); @@ -143,7 +178,33 @@ class ChatDoctorController extends Controller $livechat->accept_date = date('Y-m-d H:i:s'); // Accept // Menyimpan perubahan ke database $livechat->save(); - return ApiResponse::apiResponse("Success",['message' => 'Livechat updated successfully'], trans('Message.success'), 200); + + // Send Notification + $doctorId = $livechat->doctor_id; + $title = 'Approve Request Livechat'; + $body = 'Approve Livechat'; + $channel = Channel::where([ + 'member_id' => $livechat->patient_id, + 'doctor_id' => $livechat->doctor_id + ])->first(); + $dataNotif = [ + 'channel_id' => $channel->id, + 'livechat_id' => $livechat->id, + 'type' => 'approve-chat' + ]; + + $user = UserLMS::where('nID',$livechat->patient_id)->first(); + if ($user) { + $user->notify(new SendNotification($title, $body, $dataNotif)); + return ApiResponse::apiResponse("Success",['message' => 'Livechat updated successfully'], trans('Message.success'), 200); + } else { + return Helper::responseJson( + status: 'Not Found', + statusCode: 404, + message: 'Doctor not found.' + ); + } + } else { return response()->json(['message' => 'Livechat not found'], 404); } @@ -155,10 +216,36 @@ class ChatDoctorController extends Controller if ($livechat) { // Memperbarui atribut model $livechat->status = 6; // End Chat - $livechat->end_date = date('Y-m-d H:i:s'); // Accept + $livechat->end_date = date('Y-m-d H:i:s'); // Endchat // Menyimpan perubahan ke database $livechat->save(); - return ApiResponse::apiResponse("Success",['message' => 'Livechat updated successfully'], trans('Message.success'), 200); + + // Send Notification + $doctorId = $livechat->doctor_id; + $title = 'End Livechat'; + $body = 'End Livechat'; + $channel = Channel::where([ + 'member_id' => $livechat->patient_id, + 'doctor_id' => $livechat->doctor_id + ])->first(); + $dataNotif = [ + 'channel_id' => $channel->id, + 'livechat_id' => $livechat->id, + 'type' => 'end-chat' + ]; + + $user = UserLMS::where('nID',$livechat->patient_id)->first(); + + if ($user) { + $user->notify(new SendNotification($title, $body, $dataNotif)); + return ApiResponse::apiResponse("Success",['message' => 'Livechat updated successfully'], trans('Message.success'), 200); + } else { + return Helper::responseJson( + status: 'Not Found', + statusCode: 404, + message: 'Doctor not found.' + ); + } } else { return response()->json(['message' => 'Livechat not found'], 404); } @@ -166,7 +253,6 @@ class ChatDoctorController extends Controller public function summaryChat(Request $request) { - $livechat = Livechat::find($request->id); if ($livechat) { // Memperbarui atribut model diff --git a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php index 4f5159b5..964a45ff 100644 --- a/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php +++ b/Modules/Linksehat/Http/Controllers/Api/DuitkuController.php @@ -3,11 +3,14 @@ namespace Modules\Linksehat\Http\Controllers\Api; use App\Helpers\Helper; +use App\Notifications\SendNotification; use App\Models\Organization; use App\Models\Speciality; use App\Models\Livechat; use App\Models\Channel; use App\Models\UserChannel; +use App\Models\User as UserAso; +use App\Models\OLDLMS\User; use Illuminate\Contracts\Support\Renderable; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; @@ -285,16 +288,15 @@ class DuitkuController extends Controller 'created_at' => date('Y-m-d H:i:s') ]); - if ($notif->resultCode == "00") { + if ($notif->resultCode == "00") { // berhasil melakukan pembayaran // Action Success $livechat = Livechat::where('uuid', $notif->merchantOrderId)->first(); // Update status pembayaran $livechat->payment_method = $notif->paymentCode; $livechat->status = 5; // success payment - $livechat->save(); - // Update start chat $livechat->start_date = date('Y-m-d H:i:s'); + $livechat->save(); // Buat dan simpan data channel ke dalam tabel $channel = Channel::updateOrCreate([ 'name' => $livechat->patient_id .'_' . $request->doctor_id, @@ -330,9 +332,26 @@ class DuitkuController extends Controller ] ); + // Send Notification + $doctorId = $livechat->doctor_id; + $userDokter = UserAso::find($doctorId); + $title = 'Payment Succes Livechat'; + $patient = User::where('nID', $livechat->patient_id)->first(); + $body = 'Payment Succes Livechat from ' . $patient->sFirstName . ' ' . $patient->sLastName; + $channel = Channel::where([ + 'member_id' => $livechat->patient_id, + 'doctor_id' => $livechat->doctor_id + ])->first(); + $dataNotif = [ + 'channel_id' => $channel->id, + 'livechat_id' => $livechat->id, + 'type' => 'success-payment' + ]; + $userDokter->notify(new SendNotification($title, $body, $dataNotif)); + // Berikan respons yang sesuai ke klien return response()->json(['message' => 'Channel created successfully', 'channel' => $channel]); - + } else if ($notif->resultCode == "01") { // Action Failed $livechat = Livechat::where('uuid', $notif->merchantOrderId)->first(); @@ -341,6 +360,22 @@ class DuitkuController extends Controller $livechat->status = 7; // failed payment $livechat->save(); + // Send Notification + $doctorId = $livechat->doctor_id; + $userDokter = UserAso::find($doctorId); + $title = 'Payment Failed Livechat'; + $patient = User::where('nID', $livechat->patient_id)->first(); + $body = 'Payment Failed Livechat from ' . $patient->sFirstName . ' ' . $patient->sLastName; + $channel = Channel::where([ + 'member_id' => $livechat->patient_id, + 'doctor_id' => $livechat->doctor_id + ])->first(); + $dataNotif = [ + 'channel_id' => $channel->id, + 'livechat_id' => $livechat->id, + 'type' => 'failed-payment' + ]; + $userDokter->notify(new SendNotification($title, $body, $dataNotif)); return response()->json(['message' => 'User Gagal melakukan pembayaran']); } diff --git a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php index b1e2d7e0..8a240e38 100644 --- a/Modules/Linksehat/Http/Controllers/Api/LivechatController.php +++ b/Modules/Linksehat/Http/Controllers/Api/LivechatController.php @@ -2,6 +2,7 @@ namespace Modules\Linksehat\Http\Controllers\Api; +use App\Notifications\SendNotification; use App\Helpers\Helper; use App\Helpers\DuitkuHelper; use App\Services\Duitku; @@ -9,8 +10,10 @@ use App\Models\Organization; use App\Models\PractitionerRole; use App\Models\Invoice; use App\Models\PaymentsMethods; +use App\Models\Channel; use App\Models\Livechat; use App\Models\OLDLMS\User; +use App\Models\User as UserAso; use Illuminate\Routing\Controller; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; @@ -20,6 +23,9 @@ use Illuminate\Support\Facades\Validator; use App\Http\Controllers\DuitkuController; use DB; +use Illuminate\Contracts\Filesystem\Cloud; +use Kreait\Firebase\Messaging\CloudMessage; +use Kreait\Laravel\Firebase\Facades\Firebase; use Str; class LivechatController extends Controller @@ -45,7 +51,7 @@ class LivechatController extends Controller $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; $avatarMember = $user->detail->sImage ?? $urlAvatarDefault; $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $user->nIDHubunganKeluarga)->first('sHubunganKeluarga'); - + $dataUser = [ 'id' => $user->nID, 'name' => $user->sFirstName . ' ' . $user->sLastName, @@ -59,7 +65,7 @@ class LivechatController extends Controller $urlAvatarDefault = $m['detail']['nIDJenisKelamin'] == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; $avatarMember = $m['detail']['sImage'] ?? $urlAvatarDefault; $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $m['nIDHubunganKeluarga'])->first('sHubunganKeluarga'); - + $data = [ 'id' => $m['nID'], 'name' => $m['full_name'], @@ -75,17 +81,17 @@ class LivechatController extends Controller $memberProfile = User::with('detail')->where('nIDUser', $nID)->get()->toArray(); $dataMember = User::with('detail')->where('nID', $nID)->get()->first(); - + if ($user->detail){ $urlAvatarDefault = $user->detail->nIDJenisKelamin == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; } else { $urlAvatarDefault = 'https://linksehat.dev/assets/img/users/male-avatar.png'; } $avatar = $user->detail->sImage ?? $urlAvatarDefault; - + $avatarMember = $dataMember->detail->sImage ?? $urlAvatarDefault; $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $user->detail->nIDHubunganKeluarga)->first('sHubunganKeluarga'); - + $dataUser = [ 'id' => $dataMember->nID, 'name' => $dataMember->sFirstName . ' ' . $dataMember->sLastName, @@ -99,20 +105,20 @@ class LivechatController extends Controller $urlAvatarDefault = $m['detail']['nIDJenisKelamin'] == 1 ? 'https://linksehat.dev/assets/img/users/male-avatar.png' : 'https://linksehat.dev/assets/img/users/female-avatar.png'; $avatarMember = $m['detail']['sImage'] ?? $urlAvatarDefault; $relationship = DB::connection('oldlms')->table('tm_hubungan_keluarga')->where('nID', $m['nIDHubunganKeluarga'])->first('sHubunganKeluarga'); - + $data = [ 'id' => $m['nID'], 'name' => $m['full_name'], 'relationship' => $relationship->sHubunganKeluarga, 'avatar' => $avatarMember, ]; - + array_push( $dataMemberProfile, $data); } } - } + } } - + return Helper::responseJson([ 'member' => $dataMemberProfile ]); @@ -133,11 +139,11 @@ class LivechatController extends Controller 'descriptions' => 'required', ], [ 'doctor_id.required' => 'ID Dokter harus diisi', - 'patient_id.required' => 'ID Dokter harus diisi', - 'descriptions.required' => 'Description harus diisi', + 'patient_id.required' => 'ID Pasien harus diisi', + 'descriptions.required' => 'Deskripsi harus diisi', ]); - if ($validator->fails()){ + if ($validator->fails()) { return Helper::responseJson( status: 'Bad Request', statusCode: 400, @@ -148,27 +154,52 @@ class LivechatController extends Controller /** * Status Livechat - * 1=Request, 2=Accept, 3=Decline, 4=Waiting Payment, 5=Success Payment, 6 = End Chat + * 1=Request, 2=Accept, 3=Decline, 4=Waiting Payment, 5=Success Payment, 6 = End Chat, 7=Payment Failed */ $timezone = date_default_timezone_get(); $data['request_date'] = date('Y-m-d H:i:s'); $data['timezone'] = $timezone; $data['uuid'] = (string) Str::orderedUuid(); - $data['status'] = 1; + $data['status'] = 1; // Request + $livechat = Livechat::create($data); $doctor = $livechat->doctor; - $data = [ + $responseData = [ 'id' => $livechat->id, 'request_date' => $livechat->request_date, - 'image_path' =>'https' + 'image_path' => 'https' // Ganti dengan path yang benar jika ada ]; - - return Helper::responseJson(data: $data); + // Send Notification + $doctorId = $livechat->doctor_id; + $user = UserAso::find($doctorId); + $title = 'New Request Livechat'; + $patient = User::where('nID', $livechat->patient_id)->first(); + $body = 'Request Livechat from ' . $patient->sFirstName . ' ' . $patient->sLastName; + $channel = Channel::where([ + 'member_id' => $livechat->patient_id, + 'doctor_id' => $livechat->doctor_id + ])->first(); + $dataNotif = [ + 'channel_id' => $channel->id, + 'livechat_id' => $livechat->id, + 'type' => 'request-chat' + ]; + + if ($user) { + $user->notify(new SendNotification($title, $body, $dataNotif)); + return Helper::responseJson(data: $responseData); + } else { + return Helper::responseJson( + status: 'Not Found', + statusCode: 404, + message: 'Doctor not found.' + ); + } } } - + public function consultation_request_show($id){ $livechat = Livechat::where('id', $id)->with(['doctor', 'practitioner'])->first(); $practitionerRole = PractitionerRole::where('id',$livechat->practitioner->id)->first(); @@ -198,7 +229,7 @@ class LivechatController extends Controller ], ], 'total' => $totalPay - + ]; return Helper::responseJson(data: $data); } @@ -216,9 +247,9 @@ class LivechatController extends Controller 'active' => 1, 'config_pmc_id' => 2, ])->get()->toArray(); - + $payment = DuitkuHelper::paymentMethod(); - + $price = $practitionerRole->price ? $practitionerRole->price : 30000; $discount = 0; $adminFee = 5000; @@ -234,7 +265,7 @@ class LivechatController extends Controller 'va' => $va ] // 'payment_method' => json_decode($payment) - + ]; return Helper::responseJson(data: $data); } @@ -290,7 +321,6 @@ class LivechatController extends Controller // Membuat invoice menggunakan DuitkuHelper $duitku = DuitkuHelper::createInvoice($data); - return response()->json(['success' => true, 'data' => $duitku], 200); } catch (Exception $e) { // Menangkap error dan mengembalikan respon error diff --git a/Modules/Linksehat/Routes/api.php b/Modules/Linksehat/Routes/api.php index 9344de6f..8ff01397 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 () { @@ -72,13 +72,14 @@ Route::prefix('linksehat')->group(function () { Route::get('doctors/{id}', 'show')->name('doctors.show'); }); - Route::middleware('auth:sanctum')->group(function () { + Route::middleware(['auth:sanctum', 'linksehat.old.auth'])->group(function () { + Route::post('notification-token', [AuthController::class,'notificationToken']); Route::get('profile/{id}', [ProfileController::class, 'index'])->name('profile'); Route::get('change-profile/{id}', [ProfileController::class, 'changeProfile'])->name('change-profile'); Route::post('profile', [ProfileController::class, 'update'])->name('profile.update'); Route::post('profile-add', [ProfileController::class, 'store'])->name('profile.store'); - Route::post('notification-tokens/delete/{id}', [NotificationTokenController::class, 'destroy'])->name('profile.delete.token'); - Route::post('notification-tokens', [NotificationTokenController::class, 'store'])->name('profile.store.token'); + // Route::post('notification-tokens/delete/{id}', [NotificationTokenController::class, 'destroy'])->name('profile.delete.token'); + // Route::post('notification-tokens', [NotificationTokenController::class, 'store'])->name('profile.store.token'); Route::apiResource('appointment', AppointmentController::class); Route::apiResource('families', PersonController::class)->except(['destroy']); @@ -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/app/Helpers/Helper.php b/app/Helpers/Helper.php index dae72916..fe4db039 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -477,7 +477,7 @@ class Helper public static function calculateAge($date_brith_day){ // Konversi tanggal lahir ke dalam format UNIX timestamp $dob = strtotime($date_brith_day); - + // Hitung umur berdasarkan tanggal lahir $umur = date('Y') - date('Y', $dob); @@ -498,5 +498,34 @@ class Helper return $start->diffInDays($end) + 1; } + public static function getStatusLivechat($status) { + switch ($status) { + case 1: + return 'Requested'; // Clearer status name + break; + case 2: + return 'Accepted'; + break; + case 3: + return 'Declined'; + break; + case 4: + return 'Waiting Payment'; + break; + case 5: + return 'Payment Successful'; + break; + case 6: + return 'Chat Ended'; + break; + case 7: + return 'Payment Failed'; + break; + default: + return 'Unknown Status'; // Handle unknown statuses + break; + } + } + } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 18e416ba..2885a5dd 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -16,6 +16,7 @@ class Kernel extends HttpKernel protected $middleware = [ // \App\Http\Middleware\TrustHosts::class, \App\Http\Middleware\TrustProxies::class, + \App\Http\Middleware\LinksehatOldAuthMiddleware::class, \Illuminate\Http\Middleware\HandleCors::class, \App\Http\Middleware\PreventRequestsDuringMaintenance::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, diff --git a/app/Http/Middleware/LinksehatOldAuthMiddleware.php b/app/Http/Middleware/LinksehatOldAuthMiddleware.php index 53073ec2..d519d545 100644 --- a/app/Http/Middleware/LinksehatOldAuthMiddleware.php +++ b/app/Http/Middleware/LinksehatOldAuthMiddleware.php @@ -5,6 +5,8 @@ namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use App\Models\OLDLMS\PersonalAccessToken; +use Laravel\Sanctum\Sanctum; class LinksehatOldAuthMiddleware { @@ -17,12 +19,12 @@ class LinksehatOldAuthMiddleware */ public function handle(Request $request, Closure $next) { - if ($request->header('authorization') == 'Bearer LpMbGm0NQvFC3lUBiy1Ch3NzS0CIPSmanR12FcdP') { - Auth::loginUsingId(1); - - return $next($request); - } + // if ($request->header('authorization') == 'Bearer LpMbGm0NQvFC3lUBiy1Ch3NzS0CIPSmanR12FcdP') { + // Auth::loginUsingId(1); - return abort(401, "Unauthenticated"); + // return $next($request); + // } + Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); + return $next($request); } } diff --git a/app/Models/OLDLMS/NotificationToken.php b/app/Models/OLDLMS/NotificationToken.php new file mode 100644 index 00000000..e740c8c2 --- /dev/null +++ b/app/Models/OLDLMS/NotificationToken.php @@ -0,0 +1,33 @@ +morphTo(); + } +} diff --git a/app/Models/OLDLMS/User.php b/app/Models/OLDLMS/User.php index c67e03ee..b940f65f 100644 --- a/app/Models/OLDLMS/User.php +++ b/app/Models/OLDLMS/User.php @@ -1,7 +1,6 @@ hasOne(UserDetail::class, 'nIDUser', 'nID'); } - + public function insurances() { return $this->hasMany(UserInsurance::class, 'nIDUser', 'nID'); @@ -72,4 +72,9 @@ class User extends Authenticatable { return $this->morphMany(NotificationToken::class, 'notifiabletoken'); } + + public function routeNotificationForFcm() + { + return $this->notificationTokens()->pluck('token')->toArray(); + } } diff --git a/app/Models/User.php b/app/Models/User.php index 64263dd5..f7dde5f6 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -86,7 +86,7 @@ class User extends Authenticatable { return $this->belongsToMany(Corporate::class, 'corporate_manager', 'user_id', 'corporate_id'); } - + public function metas() { return $this->morphMany(Meta::class, 'metaable'); @@ -102,13 +102,18 @@ class User extends Authenticatable return $this->hasMany(Person::class, 'owner_user_id'); } + public function getOrganization() + { + return $this->hasOne(OrganizationUser::class, 'user_id'); + } + public function notificationTokens() { return $this->morphMany(NotificationToken::class, 'notifiabletoken'); } - public function getOrganization() + public function routeNotificationForFcm() { - return $this->hasOne(OrganizationUser::class, 'user_id'); + return $this->notificationTokens()->pluck('token')->toArray(); } } diff --git a/app/Notifications/SendNotification.php b/app/Notifications/SendNotification.php new file mode 100644 index 00000000..b2ebdbab --- /dev/null +++ b/app/Notifications/SendNotification.php @@ -0,0 +1,90 @@ +title = $title; + $this->body = $body; + $this->data = is_array($data) ? $data : (array) $data; // Pastikan data adalah array + } + + public function via($notifiable) + { + return [FcmChannel::class]; + } + + // public function toFcm($notifiable) + // { + // return FcmMessage::create() + // ->setData($this->data) + // ->setNotification([ + // 'title' => $this->title, + // 'body' => $this->body, + // ]); + // } + + public function toFcm($notifiable) + { + $deviceTokens = $notifiable->routeNotificationFor('fcm'); + $notification = [ + 'title' => $this->title, + 'body' => $this->body, + ]; + + if (count($deviceTokens)){ + foreach($deviceTokens as $token) { + $message = CloudMessage::withTarget('token', $token) + ->withNotification($notification) // optional + ->withData($this->data); + Firebase::messaging()->send($message); + } + } + + $dataFcm = FcmMessage::create() + ->setToken($deviceTokens[0]) + ->setData([]) + ->setNotification( + FcmNotification::create() + ->setTitle('ini title') + ->setBody('ini body') + ) + ->setAndroid( + AndroidConfig::create() + ->setFcmOptions(AndroidFcmOptions::create()->setAnalyticsLabel('analytics')) + ->setNotification(AndroidNotification::create()->setColor('#0A0A0A')) + + )->setApns( + ApnsConfig::create() + ->setFcmOptions(ApnsFcmOptions::create()->setAnalyticsLabel('analytics_ios')) + ); + return $dataFcm; + } + + public function fcmProject($notifiable, $message){ + return 'app'; + } + +} 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/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", diff --git a/config/app.php b/config/app.php index 906288da..a7e64d95 100644 --- a/config/app.php +++ b/config/app.php @@ -202,6 +202,11 @@ return [ App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, + /** + * Package Firebase Cloud Message + */ + NotificationChannels\Fcm\FcmServiceProvider::class, + ], /* diff --git a/config/fcm.php b/config/fcm.php new file mode 100644 index 00000000..79133a66 --- /dev/null +++ b/config/fcm.php @@ -0,0 +1,5 @@ + env('FCM_SERVER_KEY'), +]; \ No newline at end of file diff --git a/config/firebase.php b/config/firebase.php new file mode 100644 index 00000000..02b495ac --- /dev/null +++ b/config/firebase.php @@ -0,0 +1,192 @@ + env('FIREBASE_PROJECT', 'app'), + + /* + * ------------------------------------------------------------------------ + * Firebase project configurations + * ------------------------------------------------------------------------ + */ + 'projects' => [ + 'app' => [ + /* + * ------------------------------------------------------------------------ + * Credentials / Service Account + * ------------------------------------------------------------------------ + * + * In order to access a Firebase project and its related services using a + * server SDK, requests must be authenticated. For server-to-server + * communication this is done with a Service Account. + * + * If you don't already have generated a Service Account, you can do so by + * following the instructions from the official documentation pages at + * + * https://firebase.google.com/docs/admin/setup#initialize_the_sdk + * + * Once you have downloaded the Service Account JSON file, you can use it + * to configure the package. + * + * If you don't provide credentials, the Firebase Admin SDK will try to + * auto-discover them + * + * - by checking the environment variable FIREBASE_CREDENTIALS + * - by checking the environment variable GOOGLE_APPLICATION_CREDENTIALS + * - by trying to find Google's well known file + * - by checking if the application is running on GCE/GCP + * + * If no credentials file can be found, an exception will be thrown the + * first time you try to access a component of the Firebase Admin SDK. + * + */ + 'credentials' => [ + 'file' => env('FIREBASE_CREDENTIALS', env('GOOGLE_APPLICATION_CREDENTIALS')), + + /* + * If you want to prevent the auto discovery of credentials, set the + * following parameter to false. If you disable it, you must + * provide a credentials file. + */ + 'auto_discovery' => true, + ], + + /* + * ------------------------------------------------------------------------ + * Firebase Auth Component + * ------------------------------------------------------------------------ + */ + + 'auth' => [ + 'tenant_id' => env('FIREBASE_AUTH_TENANT_ID'), + ], + + /* + * ------------------------------------------------------------------------ + * Firebase Realtime Database + * ------------------------------------------------------------------------ + */ + + 'database' => [ + /* + * In most of the cases the project ID defined in the credentials file + * determines the URL of your project's Realtime Database. If the + * connection to the Realtime Database fails, you can override + * its URL with the value you see at + * + * https://console.firebase.google.com/u/1/project/_/database + * + * Please make sure that you use a full URL like, for example, + * https://my-project-id.firebaseio.com + */ + 'url' => env('FIREBASE_DATABASE_URL'), + + /* + * As a best practice, a service should have access to only the resources it needs. + * To get more fine-grained control over the resources a Firebase app instance can access, + * use a unique identifier in your Security Rules to represent your service. + * + * https://firebase.google.com/docs/database/admin/start#authenticate-with-limited-privileges + */ + // 'auth_variable_override' => [ + // 'uid' => 'my-service-worker' + // ], + ], + + 'dynamic_links' => [ + /* + * Dynamic links can be built with any URL prefix registered on + * + * https://console.firebase.google.com/u/1/project/_/durablelinks/links/ + * + * You can define one of those domains as the default for new Dynamic + * Links created within your project. + * + * The value must be a valid domain, for example, + * https://example.page.link + */ + 'default_domain' => env('FIREBASE_DYNAMIC_LINKS_DEFAULT_DOMAIN'), + ], + + /* + * ------------------------------------------------------------------------ + * Firebase Cloud Storage + * ------------------------------------------------------------------------ + */ + + 'storage' => [ + /* + * Your project's default storage bucket usually uses the project ID + * as its name. If you have multiple storage buckets and want to + * use another one as the default for your application, you can + * override it here. + */ + + 'default_bucket' => env('FIREBASE_STORAGE_DEFAULT_BUCKET'), + ], + + /* + * ------------------------------------------------------------------------ + * Caching + * ------------------------------------------------------------------------ + * + * The Firebase Admin SDK can cache some data returned from the Firebase + * API, for example Google's public keys used to verify ID tokens. + * + */ + + 'cache_store' => env('FIREBASE_CACHE_STORE', 'file'), + + /* + * ------------------------------------------------------------------------ + * Logging + * ------------------------------------------------------------------------ + * + * Enable logging of HTTP interaction for insights and/or debugging. + * + * Log channels are defined in config/logging.php + * + * Successful HTTP messages are logged with the log level 'info'. + * Failed HTTP messages are logged with the the log level 'notice'. + * + * Note: Using the same channel for simple and debug logs will result in + * two entries per request and response. + */ + + 'logging' => [ + 'http_log_channel' => env('FIREBASE_HTTP_LOG_CHANNEL'), + 'http_debug_log_channel' => env('FIREBASE_HTTP_DEBUG_LOG_CHANNEL'), + ], + + /* + * ------------------------------------------------------------------------ + * HTTP Client Options + * ------------------------------------------------------------------------ + * + * Behavior of the HTTP Client performing the API requests + */ + 'http_client_options' => [ + /* + * Use a proxy that all API requests should be passed through. + * (default: none) + */ + 'proxy' => env('FIREBASE_HTTP_CLIENT_PROXY'), + + /* + * Set the maximum amount of seconds (float) that can pass before + * a request is considered timed out + * + * The default time out can be reviewed at + * https://github.com/kreait/firebase-php/blob/6.x/src/Firebase/Http/HttpClientOptions.php + */ + 'timeout' => env('FIREBASE_HTTP_CLIENT_TIMEOUT'), + ], + ], + ], +]; diff --git a/config/services.php b/config/services.php index e1904897..bcf1a378 100644 --- a/config/services.php +++ b/config/services.php @@ -37,4 +37,9 @@ return [ 'redirect' => env('GOOGLE_REDIRECT_URI'), ], + 'fcm' => [ + 'key' => env('FCM_SERVER_KEY'), + 'sender_id' => env('FCM_SENDER_ID'), + ], + ]; diff --git a/database/migrations/2022_11_22_083926_create_notification_tokens_table.php b/database/migrations/2022_11_22_083926_create_notification_tokens_table.php index 6b29d375..d9a30238 100644 --- a/database/migrations/2022_11_22_083926_create_notification_tokens_table.php +++ b/database/migrations/2022_11_22_083926_create_notification_tokens_table.php @@ -17,6 +17,7 @@ return new class extends Migration $table->id(); $table->morphs('notifiabletoken', 'notifiabletoken'); $table->string('origin'); + $table->string('device_id')->nullable(); $table->string('type'); $table->string('token'); $table->string('status'); @@ -26,7 +27,7 @@ return new class extends Migration $table->unsignedBigInteger('created_by')->nullable()->index(); $table->unsignedBigInteger('updated_by')->nullable()->index(); $table->unsignedBigInteger('deleted_by')->nullable()->index(); - + $table->index('token'); }); } diff --git a/database/migrations/2024_01_03_143801_add_column_to_notifications.php b/database/migrations/2024_01_03_143801_add_column_to_notifications.php new file mode 100644 index 00000000..c98d6a3d --- /dev/null +++ b/database/migrations/2024_01_03_143801_add_column_to_notifications.php @@ -0,0 +1,32 @@ +text('data')->after('notifiable_id')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('notifications', function (Blueprint $table) { + $table->dropColumn('data'); + }); + } +}; diff --git a/database/migrations/2024_05_22_150529_add_column_to_users_table.php b/database/migrations/2024_05_22_150529_add_column_to_users_table.php new file mode 100644 index 00000000..0afa02ae --- /dev/null +++ b/database/migrations/2024_05_22_150529_add_column_to_users_table.php @@ -0,0 +1,32 @@ +string('fcm_token')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('fcm_token'); + }); + } +}; 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 {/* - ) : 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/LanguageContext.tsx b/frontend/hospital-portal/src/contexts/LanguageContext.tsx index b8cce590..ca08a616 100644 --- a/frontend/hospital-portal/src/contexts/LanguageContext.tsx +++ b/frontend/hospital-portal/src/contexts/LanguageContext.tsx @@ -5,7 +5,7 @@ export const LanguageContext = createContext(); export const LanguageProvider = ({ children }) => { const [currentLocale, setCurrentLocale] = useState(localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID'); const [localeData, setLocaleData] = useState('id'); - const cancelToken = useRef(null); + const cancelToken = useRef(null); useEffect(() => { const fetchData = async () => { 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/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/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/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 a0165145..2bcb583b 100644 --- a/frontend/hospital-portal/src/routes/index.tsx +++ b/frontend/hospital-portal/src/routes/index.tsx @@ -54,7 +54,7 @@ export default function Router() { // { path: 'register-unprotected', element: }, { path: 'reset-password', element: }, { path: 'forget-password', element: }, - // { path: 'verify', element: }, + { path: 'verify', element: }, ], }, // { 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} { 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? - */} + {localeData.txtLupaSandi} + - 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 expiredCookie = 1/24/60; 1 menit +let expiredCookie = 12/24; //12 jam + +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 }; diff --git a/public/files/Template Import Invoice.xlsx b/public/files/Template Import Invoice.xlsx new file mode 100644 index 00000000..2851251e Binary files /dev/null and b/public/files/Template Import Invoice.xlsx differ