Merge branch 'staging' of https://dev.sismedika.online/febio/aso into staging

This commit is contained in:
2024-04-24 16:47:43 +07:00
15 changed files with 811 additions and 10 deletions

View File

@@ -0,0 +1,21 @@
<?php
namespace Modules\Linksehat\Helpers\Doctor;
class ApiResponse
{
public static function apiResponse(string $status, array|object $data = null, string|array|object $message = null, int $statusCode)
{
if ($message instanceof \Illuminate\Support\MessageBag) {
$message = $message->first();
}
return response()->json([
'meta' => [
'status' => $status,
'code' => $statusCode,
'message' => $message
],
'data' => $data,
], $statusCode);
}
}

View File

@@ -0,0 +1,264 @@
<?php
namespace Modules\Linksehat\Http\Controllers\Api\Doctor;
use App\Http\Controllers\Controller;
use App\Models\User;
use Crypt;
use Error;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Modules\Internal\Emails\SendVerifyEmail;
use Modules\Internal\Events\ForgetPassword;
use Illuminate\Support\Facades\Validator;
use Modules\HospitalPortal\Helpers\ApiResponse;
use App\Helpers\Helper;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\DB;
class AuthDoctorController extends Controller
{
public function login(Request $request)
{
$data = [
'email' => $request->email,
'password' => $request->password
];
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'password' => 'required'
], [
'email.required' => trans('validation.required',['attribute' => 'Email']),
'email.email' => trans('validation.email'),
'password.required' => trans('validation.required',['attribute' => 'Password']),
]);
if ($validator->fails())
{
return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400);
}
else
{
$user = User::where('email', $request->email)->first();
if (!$user) {
return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404);
}
if (!Hash::check($request->password, $user->password)) {
return ApiResponse::apiResponse('Bad Request', $data, trans('message.password'), 400);
}
$res_data = [
'user' => $user,
'token' => $user->createToken('app')->plainTextToken
];
return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200);
}
}
public function logout(Request $request)
{
$request->user()->tokens()->delete();
return ApiResponse::apiResponse('Success', [], trans('message.logout'), 200);
}
public function forgotPassword(Request $request)
{
$data = [
'email' => $request->email,
];
$validator = Validator::make($request->all(), [
'email' => 'required|email',
], [
'email.required' => trans('validation.required',['attribute' => 'Email']),
'email.email' => trans('validation.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 App Doctor',
'type' => 1,
'isUnRead' => true,
'created_by' => auth()->check() ? auth()->user()->id : null,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
];
$sendNotif = Helper::insertNotification($dataNotif);
//Insert data password reset
$token = mt_rand(100000, 999999); // Menghasilkan angka acak antara 100000 dan 999999
$p_resets = DB::table('password_resets')
->insert([
'email' => $request->email,
'token' => $token,
'created_at' => date('Y-m-d H:i:s'),
]);
// Send Email after insert notifications
if($sendNotif && $p_resets)
{
//send to alarm
$nameTo = 'User';
$dataEmail = [
'email' => $emailTo,
'name' => $nameTo,
'subject' => 'Request Forgot Password from App Doctor Date '. date('Y-m-d H:i:s'),
'body' => View::make('email/forgot_password', ['token' => $token])->render(),
];
Helper::sendEmail($dataEmail);
$res = DB::table('password_resets')
->where('email', '=', $request->email)
->where('token', '=', $token)
->get();
return ApiResponse::apiResponse("Success", $res, trans('message.success'), 200);
}
else
{
return ApiResponse::apiResponse("Internal Server Error", $data, trans('message.server_error'), 500);
}
}
}
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']),
]);
if ($validator->fails())
{
return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400);
}
else
{
//Check Time
$check = DB::table('password_resets')
->where('email', '=', $request->email)
->where('token', '=', $request->token)
->select('created_at')
->first();
if($check)
{
$created_at = strtotime($check->created_at); // Konversi string waktu ke UNIX timestamp
$now = time(); // Waktu sekarang dalam UNIX timestamp
// Hitung selisih waktu dalam menit
$diffInMinutes = ($now - $created_at) / 60;
if ($diffInMinutes > 60) {
return ApiResponse::apiResponse('Not Found', $data, trans('message.token_expired'), 404);
} else {
// Lanjutkan dengan proses pemulihan kata sandi
return ApiResponse::apiResponse("Success", $data, trans('message.success'), 200);
}
}
else
{
return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404);
}
}
}
public function resetPassword(Request $request)
{
$data = [
'email' => $request->email,
'token' => $request->token,
'new_password' => $request->new_password
];
$validator = Validator::make($request->all(), [
'email' => 'required|email',
'token' => 'required|numeric',
'new_password' => [
'required',
'min:8',
'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/'
]
], [
'email.required' => trans('validation.required',['attribute' => 'Email']),
'email.email' => trans('validation.email'),
'token.required' => trans('validation.required',['attribute' => 'Token']),
'new_password.required' => trans('validation.required',['attribute' => 'New Password']),
'new_password.min' => trans('validation.min',['attribute' => 'New Password']),
'new_password.regex' => trans('validation.regex',['attribute' => 'New Password']),
]);
if ($validator->fails())
{
return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400);
}
else
{
//Check Time
$check = DB::table('password_resets')
->where('email', '=', $request->email)
->where('token', '=', $request->token)
->select('created_at')
->first();
if($check)
{
$created_at = strtotime($check->created_at); // Konversi string waktu ke UNIX timestamp
$now = time(); // Waktu sekarang dalam UNIX timestamp
// Hitung selisih waktu dalam menit
$diffInMinutes = ($now - $created_at) / 60;
if ($diffInMinutes > 60) {
return ApiResponse::apiResponse('Not Found', $data, trans('message.token_expired'), 404);
} else {
// Lanjutkan dengan proses pemulihan kata sandi
$user = User::where('email', $request->email)->first();
if ($user)
{
$newPassword = Hash::make($request->new_password);
$user->password = $newPassword;
$user->save();
return ApiResponse::apiResponse("Success", $data, trans('message.success'), 200);
}
else
{
return ApiResponse::apiResponse('Not Found', $data, trans('message.token_expired'), 404);
}
}
}
else
{
return ApiResponse::apiResponse('Not Found', $data, trans('message.not_found'), 404);
}
}
}
}

View File

@@ -0,0 +1,175 @@
<?php
namespace Modules\Linksehat\Http\Controllers\Api\Doctor;
use App\Http\Controllers\Controller;
use App\Models\User;
use Crypt;
use Error;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Modules\Internal\Emails\SendVerifyEmail;
use Modules\Internal\Events\ForgetPassword;
use Illuminate\Support\Facades\Validator;
use Modules\HospitalPortal\Helpers\ApiResponse;
use App\Helpers\Helper;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\DB;
class ProfileDoctorController extends Controller
{
public function getProfile()
{
$data = [
'user_id' => auth()->check() ? auth()->user()->id : null,
];
$user_id = auth()->check() ? auth()->user()->id : null;
//Get data Profile
$dataProfile = DB::table('users')
->join('persons','persons.id', '=', 'users.person_id')
->leftJoin('person_educations','person_educations.person_id', '=', 'persons.id')
->leftJoin('practitioners','practitioners.person_id', '=', 'persons.id')
->leftJoin('practitioner_roles','practitioner_roles.practitioner_id', '=', 'practitioners.id')
->where('users.id', '=', $user_id)
->select(
'persons.name',
DB::raw('
"Pediatrics" AS specialist
'),
DB::raw('
"4" AS rating
'),
'persons.name AS full_name',
'persons.birth_date as date_of_birth',
'persons.gender',
'persons.phone AS mobile_number',
'persons.email',
'practitioners.str_number',
'practitioners.exp_date_str',
'practitioner_roles.sip_number',
'practitioner_roles.exp_date_sip'
)
->first();
//Name
$dataName = [
'name' => $dataProfile->name,
'specialist' => $dataProfile->specialist,
'rating' => $dataProfile->rating
];
$res_data['dataName'] = $dataName;
// Basic
$dataProfileBasic = [
'full_name' => $dataProfile->full_name,
'date_of_birth' => $dataProfile->date_of_birth ? date('d M Y', strtotime($dataProfile->date_of_birth)) : '',
'gender' => $dataProfile->gender
];
$res_data['dataProfileBasic'] = $dataProfileBasic;
//Contact
$dataProfileContact = [
'mobile_number' => $dataProfile->mobile_number,
'email' => $dataProfile->email
];
$res_data['dataProfileContact'] = $dataProfileContact;
//Education
$dataEdu = DB::table('users')
->join('persons','persons.id', '=', 'users.person_id')
->leftJoin('person_educations','person_educations.person_id', '=', 'persons.id')
->where('users.id', '=', $user_id)
->select(
'person_educations.level_id',
'person_educations.name',
'person_educations.start_date',
'person_educations.end_date',
)
->get();
$dataEducations = [];
foreach($dataEdu as $val)
{
$dataEducations[] = [
'level_id' => $val->level_id,
'name' => $val->name,
'start_date' => date('d/m/Y', strtotime($val->start_date)),
'end_date' => date('d/m/Y', strtotime($val->end_date)),
];
}
$res_data['dataEducations'] = $dataEducations;
//Work Experience
$dataWork = DB::table('users')
->join('persons','persons.id', '=', 'users.person_id')
->leftJoin('practitioners','practitioners.person_id', '=', 'persons.id')
->leftJoin('practitioner_roles','practitioner_roles.practitioner_id', '=', 'practitioners.id')
->leftJoin('organizations','organizations.id', '=', 'practitioner_roles.organization_id')
->where('users.id', '=', $user_id)
->select(
'organizations.name',
'practitioner_roles.period_start',
'practitioner_roles.period_end',
)
->get();
$dataWorkExperience = [];
foreach ($dataWork as $val)
{
$dataWorkExperience[] = [
'name' => $val->name ? $val->name : '',
'period' => $this->fWorkExperience($val->period_start, $val->period_end)
];
}
$res_data['dataWorkExperience'] = $dataWorkExperience;
//STR
$dataStr = [
'str_number' => $dataProfile->str_number,
'exp_date_str' => $dataProfile->exp_date_str ? date('d M Y', strtotime($dataProfile->exp_date_str)) : ''
];
$res_data['dataStr'] = $dataStr;
//SIP
$dataSip = [
'sip_number' => $dataProfile->sip_number,
'exp_date_sip' => $dataProfile->exp_date_sip ? date('d M Y', strtotime($dataProfile->exp_date_sip)) : ''
];
$res_data['dataSip'] = $dataSip;
return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200);
}
public function fWorkExperience($start, $end)
{
$startDateString = $start; // Tanggal dan waktu awal
$endDateString = $end ; // Tanggal dan waktu akhir
// Mengubah string tanggal ke timestamp UNIX
$startTime = strtotime($startDateString);
$endTime = strtotime($endDateString);
// Menghitung selisih waktu dalam detik
$timeDifference = $endTime - $startTime;
// Menghitung jumlah tahun, bulan, dan hari dari selisih waktu
$years = floor($timeDifference / (365 * 24 * 60 * 60));
$months = floor(($timeDifference - ($years * 365 * 24 * 60 * 60)) / (30 * 24 * 60 * 60));
$days = floor(($timeDifference - ($years * 365 * 24 * 60 * 60) - ($months * 30 * 24 * 60 * 60)) / (24 * 60 * 60));
// Formatkan hasilnya
$experience = '';
if ($years > 0) {
$experience .= $years . ' years ';
}
if ($months > 0) {
$experience .= $months . ' months ';
}
if ($days > 0) {
$experience .= $days . ' days';
}
return $experience;
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace Modules\Linksehat\Http\Middleware\Doctor;
use Modules\Linksehat\Helpers\Doctor\ApiResponse;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\App;
class Authentication
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$acceptHeader = $request->header('Accept');
$contentType = $request->header('Content-Type');
$locale = $request->header('Accept-Language');
// Add language
if(!$locale)
{
return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Accept-Language']), 401);
}
if($locale !== 'en-US' && $locale !== 'id-ID')
{
return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Accept-Language']), 400);
}
if ($locale === 'en-US')
{
App::setLocale('en');
} elseif ($locale === 'id-ID')
{
App::setLocale('id');
} else
{
App::setLocale('en');
}
// Validate type accept & content type
if (!$acceptHeader)
{
return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Accept']), 401);
}
if (!$contentType)
{
return ApiResponse::apiResponse('Unauthorized', null, trans('validation.required', ['attribute' => 'Content-Type']), 401);
}
if ($acceptHeader !== 'application/json')
{
return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Accept']), 400);
}
if($contentType !== 'application/json')
{
return ApiResponse::apiResponse('Bad Request', null, trans('validation.invalid', ['attribute' => 'Content-Type']), 400);
}
return $next($request);
}
}

View File

@@ -0,0 +1,71 @@
<?php
namespace Modules\Linksehat\Http\Middleware\Doctor;
use Modules\Linksehat\Helpers\Doctor\ApiResponse;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\App;
class Authorization
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$acceptHeader = $request->header('Accept');
$contentType = $request->header('Content-Type');
$locale = $request->header('Accept-Language');
$authorization = $request->header('Authorization');
// Add language
if(!$locale)
{
return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Accept-Language']), 401);
}
if($locale !== 'en-US' && $locale !== 'id-ID')
{
return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Accept-Language']), 400);
}
if ($locale === 'en-US')
{
App::setLocale('en');
} elseif ($locale === 'id-ID')
{
App::setLocale('id');
} else
{
App::setLocale('en');
}
// Validate authorization
if (empty($authorization) || strpos($authorization, 'Bearer ') !== 0) {
return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Authorization']), 401);
}
// Validate type accept & content type
if (!$acceptHeader)
{
return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Accept']), 401);
}
if (!$contentType && $request->isMethod('post'))
{
return ApiResponse::apiResponse('Unauthorized', null, trans('Validation.required', ['attribute' => 'Content-Type']), 401);
}
if ($acceptHeader !== 'application/json')
{
return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Accept']), 400);
}
if($contentType !== 'application/json' && $request->isMethod('post'))
{
return ApiResponse::apiResponse('Bad Request', null, trans('Validation.invalid', ['attribute' => 'Content-Type']), 400);
}
return $next($request);
}
}

View File

@@ -15,6 +15,10 @@ use Modules\Linksehat\Http\Controllers\Api\SpecialityController;
use Modules\Linksehat\Http\Controllers\Api\LinkingController;
use Modules\Linksehat\Http\Controllers\Api\HomeController;
use Modules\Linksehat\Http\Controllers\Api\LivechatController;
use Modules\Linksehat\Http\Middleware\Doctor\Authentication;
use Modules\Linksehat\Http\Middleware\Doctor\Authorization;
use Modules\Linksehat\Http\Controllers\Api\Doctor\AuthDoctorController;
use Modules\Linksehat\Http\Controllers\Api\Doctor\ProfileDoctorController;
/*
|--------------------------------------------------------------------------
@@ -110,4 +114,33 @@ Route::prefix('linksehat')->group(function () {
Route::post('payment-method-duitku', [DuitkuController::class, 'paymentMethod']);
Route::post('callback-duitku', [DuitkuController::class, 'callback']);
Route::get('redirect-duitku', [DuitkuController::class, 'redirect']);
//DOCTOR API
Route::prefix('doctor')->group(function() {
//Version 1.0
Route::prefix('v1')->group(function() {
Route::middleware(Authentication::class)->group(function () {
Route::controller(AuthDoctorController::class)->group(function () {
Route::post('login', 'login');
});
});
Route::middleware('auth:sanctum')->group(function () {
Route::middleware(Authorization::class)->group(function () {
Route::controller(AuthDoctorController::class)->group(function () {
Route::post('logout', 'logout');
Route::post('forgot-password', 'forgotPassword');
});
Route::controller(ProfileDoctorController::class)->group(function () {
Route::get('get-profile', 'getProfile');
});
});
});
Route::controller(AuthDoctorController::class)->group(function () {
Route::post('forgot-password', 'forgotPassword');
Route::post('verif-code', 'verifCode');
Route::post('resend-code', 'forgotPassword');
Route::post('reset-password', 'resetPassword');
});
});
});
;});

View File

@@ -391,9 +391,7 @@ class Helper
$mail->send();
return true;
} catch (\Exception $e) {
dd($e);
return ($mail->ErrorInfo);
return false;
return $mail->ErrorInfo;
}
}
@@ -463,16 +461,16 @@ class Helper
$lonFrom = deg2rad($longitudeFrom);
$latTo = deg2rad($latitudeTo);
$lonTo = deg2rad($longitudeTo);
// Calculate the change in coordinates
$deltaLat = $latTo - $latFrom;
$deltaLon = $lonTo - $lonFrom;
// Apply Haversine formula
$a = sin($deltaLat / 2) * sin($deltaLat / 2) + cos($latFrom) * cos($latTo) * sin($deltaLon / 2) * sin($deltaLon / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
$distance = $earthRadius * $c;
return $distance;
}

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('person_educations', function (Blueprint $table) {
$table->id();
$table->bigInteger('person_id');
$table->integer('level_id')->comment('1=SD, 2= SMP, 3=SMA dst.');
$table->string('name', 255);
$table->dateTime('start_date');
$table->dateTime('end_date');
$table->bigInteger('created_by')->nullable();
$table->bigInteger('updated_by')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('person_educations');
}
};

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('practitioners', function (Blueprint $table) {
$table->string('str_number', 255)->after('person_id')->nullable();
$table->dateTime('exp_date_str')->after('str_number')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('practitioners', function (Blueprint $table) {
//
});
}
};

View File

@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('practitioner_roles', function (Blueprint $table) {
$table->string('sip_number', 255)->after('period_end')->nullable();
$table->dateTime('exp_date_sip')->after('sip_number')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('practitioner_roles', function (Blueprint $table) {
//
});
}
};

View File

@@ -7,4 +7,6 @@ return [
'password' => 'Password wrong. Please try again.',
'read_notification' => 'Notification has been read.',
'already_exists' => 'Data already exists.',
'logout' => 'User logged out successfully.',
'token_expired' => 'Token has expired. Please re-request the token.'
];

View File

@@ -6,5 +6,7 @@ return [
'max' => [
'file' => ':attribute max size is :max.',
],
'email' => 'Invalid email format.'
];
'email' => 'Invalid email format.',
'regex' => 'The :attribute must contain at least one uppercase letter, one lowercase letter, and one numeric digit.',
'min' => 'The :attribute must be at least 8 characters.'
];

View File

@@ -7,4 +7,6 @@ return [
'password' => 'Password salah. Silakan coba lagi.',
'read_notification' => 'Notifikasi telah dibaca.',
'already_exists' => 'Data sudah ada.',
'logout' => 'User berhasil logout.',
'token_expired' => 'Token telah kedaluwarsa. Silakan minta ulang token.'
];

View File

@@ -6,5 +6,7 @@ return [
'max' => [
'file' => ':attribute tidak boleh melebihi :max.',
],
'email' => 'Format email salah.'
];
'email' => 'Format email salah.',
'regex' => ':attribute harus berisi setidaknya satu huruf besar, satu huruf kecil, dan satu digit angka.',
'min' => ':attribute setidaknya harus terdiri dari 8 karakter.'
];

View File

@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Verification Code</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: ##5eebd3;
margin: 0;
padding: 0;
}
.container {
max-width: 600px;
margin: 50px auto;
background-color: #fff;
padding: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h2 {
text-align: center;
margin-bottom: 20px;
}
.verification-code {
font-size: 24px;
font-weight: bold;
text-align: center;
padding: 20px;
background-color: #3fccb4;
color: #fff;
border-radius: 5px;
}
.info-text {
text-align: center;
margin-top: 20px;
}
.footer-text {
text-align: center;
margin-top: 30px;
color: #666;
}
</style>
</head>
<body>
<div class="container">
<h2>Verification Code</h2>
<div class="verification-code">{{$token}}</div>
<p class="info-text">This code is valid for 60 minutes.</p>
<p class="footer-text">If you did not request this code, please ignore this message.</p>
</div>
</body>
</html>