Merge branch 'staging' of itcorp.primaya.id:rajif/aso into staging

This commit is contained in:
2023-07-26 17:30:45 +07:00
56 changed files with 3620 additions and 2878 deletions

View File

@@ -37,7 +37,10 @@ class CorporateManageController extends Controller
*/ */
public function show($corporate_id) public function show($corporate_id)
{ {
// $userLogin = Auth::user();
$corporate = $userLogin->managedCorporates()->where('corporates.id', $corporate_id)->first();
return response()->json($corporate);
} }
/** /**

View File

@@ -3,6 +3,7 @@
namespace Modules\Client\Http\Controllers\Api; namespace Modules\Client\Http\Controllers\Api;
use App\Helpers\Helper; use App\Helpers\Helper;
use App\Models\Member;
use App\Services\CorporateMemberService; use App\Services\CorporateMemberService;
use Illuminate\Contracts\Support\Renderable; use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -10,6 +11,7 @@ use Illuminate\Routing\Controller;
use Modules\Client\Transformers\ClaimReport\MemberResources as ClaimReportMemberResources; use Modules\Client\Transformers\ClaimReport\MemberResources as ClaimReportMemberResources;
use Modules\Client\Transformers\Dashboard\MemberResources as DashboardMemberResources; use Modules\Client\Transformers\Dashboard\MemberResources as DashboardMemberResources;
use Modules\Client\Transformers\Dashboard\MemberAlarmCenterResources as DashboardMemberAlarmResources; use Modules\Client\Transformers\Dashboard\MemberAlarmCenterResources as DashboardMemberAlarmResources;
use Modules\Client\Transformers\DataMemberResource;
class CorporateMemberController extends Controller class CorporateMemberController extends Controller
{ {
@@ -41,4 +43,19 @@ class CorporateMemberController extends Controller
return response()->json(Helper::paginateResources(DashboardMemberResources::collection($members))); return response()->json(Helper::paginateResources(DashboardMemberResources::collection($members)));
} }
} }
public function show($corporate_id, $person_id)
{
$data = Member::with(['claims', 'person', 'employeds', 'currentPlan.benefits'])
->where('person_id', $person_id)
->whereHas('employeds', function ($query) use ($corporate_id) {
$query->where('corporate_id', $corporate_id);
})
->first();
$totalClaims = $data->claims->sum('total_claim');
$data->total_claims = $totalClaims;
return response()->json(DataMemberResource::make($data));
}
} }

View File

@@ -10,6 +10,7 @@ use Modules\Client\Http\Controllers\Api\ClaimController;
use Modules\Client\Http\Controllers\Api\TopUpController; use Modules\Client\Http\Controllers\Api\TopUpController;
use Modules\Internal\Http\Controllers\ClaimEncounterController; use Modules\Internal\Http\Controllers\ClaimEncounterController;
use App\Models\Encounter; use App\Models\Encounter;
use Modules\Client\Http\Controllers\Api\DataController;
use Modules\Internal\Transformers\EncounterResource; use Modules\Internal\Transformers\EncounterResource;
/* /*
@@ -33,12 +34,16 @@ Route::prefix('client')->group(function () {
Route::middleware('auth:sanctum')->group(function () { Route::middleware('auth:sanctum')->group(function () {
Route::post('logout', [AuthController::class, 'logout'])->name('logout'); Route::post('logout', [AuthController::class, 'logout'])->name('logout');
Route::get('user', [UserController::class, 'index']); Route::get('user', [UserController::class, 'index']);
Route::get('data/{id}', [DataController::class, 'show']);
Route::put('data/{id}', [DataController::class, 'update']);
Route::get('corporate-manage', [CorporateManageController::class, 'index']); Route::get('corporate-manage', [CorporateManageController::class, 'index']);
Route::get('corporate-manage/{corporate_id}', [CorporateManageController::class, 'show']);
Route::prefix('{corporate_id}')->group(function () { Route::prefix('{corporate_id}')->group(function () {
Route::get('policy', [CorporatePolicyController::class, 'index']); Route::get('policy', [CorporatePolicyController::class, 'index']);
Route::get('division', [CorporateDivisionController::class, 'index']); Route::get('division', [CorporateDivisionController::class, 'index']);
Route::get('members', [CorporateMemberController::class, 'index']); Route::get('members', [CorporateMemberController::class, 'index']);
Route::get('members/{id}', [CorporateMemberController::class, 'show']);
Route::get('claims/status', [ClaimController::class, 'status']); Route::get('claims/status', [ClaimController::class, 'status']);
Route::get('claims', [ClaimController::class, 'index']); Route::get('claims', [ClaimController::class, 'index']);
Route::get('claims/{claim_id}/encounters', [ClaimEncounterController::class, 'getEncounterData']); Route::get('claims/{claim_id}/encounters', [ClaimEncounterController::class, 'getEncounterData']);
@@ -47,10 +52,5 @@ Route::prefix('client')->group(function () {
Route::post('topup', [TopUpController::class, 'store']); Route::post('topup', [TopUpController::class, 'store']);
}); });
Route::get('claims/{id}', [ClaimController::class, 'show']); Route::get('claims/{id}', [ClaimController::class, 'show']);
}); });
}); });

View File

@@ -1,4 +1,4 @@
c<?php <?php
namespace Modules\Client\Transformers; namespace Modules\Client\Transformers;
@@ -25,7 +25,7 @@ class ClaimShowResource extends JsonResource
$itemData['nominal_dicover'] = $item['nominal_dicover'] ?? 0; $itemData['nominal_dicover'] = $item['nominal_dicover'] ?? 0;
$itemData['nominal_ditagihkan'] = $item['nominal_ditagihkan'] ?? 0; $itemData['nominal_ditagihkan'] = $item['nominal_ditagihkan'] ?? 0;
$itemData['nominal_total'] = $item['nominal_total'] ?? 0; $itemData['nominal_total'] = $item['nominal_total'] ?? 0;
// For React Frotnend // For React Frotnend
$itemData['biaya_diajukan'] = $itemData['nominal_ditagihkan']; $itemData['biaya_diajukan'] = $itemData['nominal_ditagihkan'];
$itemData['biaya_disetujui'] = $itemData['nominal_dicover']; $itemData['biaya_disetujui'] = $itemData['nominal_dicover'];
@@ -33,8 +33,12 @@ class ClaimShowResource extends JsonResource
return $itemData; return $itemData;
}); });
$data['primary_diagnosis'] = $this->diagnoses->filter(function($diagnosis){ return $diagnosis->type == 'primary';})->values(); $data['primary_diagnosis'] = $this->diagnoses->filter(function ($diagnosis) {
$data['secondary_diagnosis'] = $this->diagnoses->filter(function($diagnosis){ return $diagnosis->type == 'secondary';})->values(); return $diagnosis->type == 'primary';
})->values();
$data['secondary_diagnosis'] = $this->diagnoses->filter(function ($diagnosis) {
return $diagnosis->type == 'secondary';
})->values();
return $data; return $data;
} }

View File

@@ -0,0 +1,81 @@
<?php
namespace Modules\Client\Transformers;
use Illuminate\Http\Resources\Json\JsonResource;
class DataMemberResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
// return parent::toArray($request);
$data = [
'id' => $this->id,
'person_id' => $this->person_id,
'user_id' => $this->user_id,
'member_id' => $this->member_id,
'payor_id' => $this->payor_id,
'name_prefix' => $this->name_prefix,
'name' => $this->name,
'name_suffix' => $this->name_suffix,
'birth_date' => $this->birth_date,
'gender' => $this->gender,
'language' => $this->language,
'race' => $this->race,
'marital_status' => $this->marital_status,
'record_type' => $this->record_type,
'principal_id' => $this->principal_id,
'relation_with_principal' => $this->relation_with_principal,
'bpjs_class' => $this->bpjs_class,
'nric' => $this->nric,
'email' => $this->email,
'bank_info' => $this->bank_info,
'agent_code' => $this->agent_code,
'address1' => $this->address1,
'address2' => $this->address2,
'address3' => $this->address3,
'address4' => $this->address4,
'city' => $this->city,
'state' => $this->state,
'postal_code' => $this->postal_code,
'record_mode' => $this->record_mode,
'telephone_mobile' => $this->telephone_mobile,
'telephone_res' => $this->telephone_res,
'telephone_office' => $this->telephone_office,
'passport_no' => $this->passport_no,
'passport_country' => $this->passport_country,
'identification_code' => $this->identification_code,
'pre_existing' => $this->pre_existing,
'bpjs_id' => $this->bpjs_id,
'endorsement_date' => $this->endorsement_date,
'members_effective_date' => $this->members_effective_date,
'members_expire_date' => $this->members_expire_date,
'activation_date' => $this->activation_date,
'terminated_date' => $this->terminated_date,
'remarks' => $this->remarks,
'policy_in_force' => $this->policy_in_force,
'start_no_claim' => $this->start_no_claim,
'end_no_claim' => $this->end_no_claim,
'active' => $this->active,
'reason' => $this->reason,
'total_claims' => $this->total_claims,
'full_name' => $this->full_name,
'age' => $this->age,
'gender_code' => $this->gender_code,
'limit' => [
'current' => $this->total_claims ?? 0,
'total' => (int)$this->currentPlan->limit_rules ?? 0,
'percentage' => (!empty($this->currentPlan->limit_rules ?? 0)) ? (($this->total_claims / $this->currentPlan->limit_rules) * 100) : 0
],
'benefits' => $this->currentPlan->benefits,
];
return $data;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Modules\HospitalPortal\Helpers;
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

@@ -12,33 +12,48 @@ use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Modules\Internal\Emails\SendVerifyEmail; use Modules\Internal\Emails\SendVerifyEmail;
use Modules\Internal\Events\ForgetPassword; use Modules\Internal\Events\ForgetPassword;
use Illuminate\Support\Facades\Validator;
use Modules\HospitalPortal\Helpers\ApiResponse;
class AuthController extends Controller class AuthController extends Controller
{ {
public function login(Request $request) public function login(Request $request)
{ {
$request->validate([ $data = [
'email' => $request->email,
'password' => $request->password
];
$validator = Validator::make($request->all(), [
'email' => 'required|email', 'email' => 'required|email',
'password' => 'required' 'password' => 'required'
], [
'email.required' => trans('validation.required',['attribute' => 'Email']),
'email.email' => trans('validation.email'),
'password.required' => trans('validation.required',['attribute' => 'Password']),
]); ]);
$user = User::query() if ($validator->fails())
->where('email', $request->email) {
->first(); return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400);
if (!$user) {
return response(['message' => 'User Tidak Ditemukan'], 404);
} }
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)) { if (!Hash::check($request->password, $user->password)) {
return response(['message' => 'Password Salah'], 403); return ApiResponse::apiResponse('Bad Request', $data, trans('message.password'), 400);
}
$res_data = [
'user' => $user,
'token' => $user->createToken('app')->plainTextToken
];
return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200);
} }
return response([
'message' => 'Selamat Datang',
'user' => $user,
'token' => $user->createToken('app')->plainTextToken
]);
} }
public function logout(Request $request) public function logout(Request $request)

View File

@@ -108,7 +108,7 @@ class ClaimRequestController extends Controller
} }
} }
if ($request->hasFile('result_files')) { if ($request->hasFile('kondisi_files')) {
foreach ($request->result_files as $file) { foreach ($request->result_files as $file) {
$pathFile = File::storeFile('claim-kondisi', $newClaimRequest->id, $file); $pathFile = File::storeFile('claim-kondisi', $newClaimRequest->id, $file);
$newClaimRequest->files()->updateOrCreate([ $newClaimRequest->files()->updateOrCreate([

View File

@@ -7,6 +7,8 @@ use App\Models\Member;
use Illuminate\Contracts\Support\Renderable; use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Routing\Controller; use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Validator;
use Modules\HospitalPortal\Helpers\ApiResponse;
class MemberController extends Controller class MemberController extends Controller
{ {
@@ -16,26 +18,37 @@ class MemberController extends Controller
*/ */
public function search(Request $request) public function search(Request $request)
{ {
$request->validate([ $data = [
'no_polis' => $request->no_polis,
'birth_date' => $request->birth_date
];
$validator = Validator::make($request->all(), [
'no_polis' => 'required', 'no_polis' => 'required',
'birth_date' => 'required' 'birth_date' => 'required'
], [
'no_polis.required' => trans('validation.required',['attribute' => 'Member ID']),
'birth_date.required' => trans('validation.required',['attribute' => 'Birth Date']),
]); ]);
if ($validator->fails())
$member = Member::query() {
->where('member_id', $request->no_polis) return ApiResponse::apiResponse('Bad Request', $data, $validator->errors(), 400);
->where('birth_date', $request->birth_date) }
->with(['person', 'currentCorporate', else
// 'currentCorporate.corporateServices' => function ($corporateService) { {
// $corporateService->where('status', 'active'); $res_data = Member::query()
// }, ->where('member_id', $request->no_polis)
// 'currentCorporate.corporateServices.service' ->where('birth_date', $request->birth_date)
// 'currentPlan.benefits', ->with(['person', 'currentCorporate',
// 'currentPlan.corporateBenefit.plan', // 'currentCorporate.corporateServices' => function ($corporateService) {
'currentPlan.corporateBenefits.benefit' // $corporateService->where('status', 'active');
]) // },
->firstOrFail(); // 'currentCorporate.corporateServices.service'
// 'currentPlan.benefits',
// 'currentPlan.corporateBenefit.plan',
return Helper::responseJson($member); 'currentPlan.corporateBenefits.benefit'
])
->firstOrFail();
return ApiResponse::apiResponse("Success", $res_data, trans('message.success'), 200);
}
} }
} }

View File

@@ -0,0 +1,65 @@
<?php
namespace Modules\HospitalPortal\Http\Middleware;
use Modules\HospitalPortal\Helpers\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\HospitalPortal\Http\Middleware;
use Modules\HospitalPortal\Helpers\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)
{
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

@@ -5,6 +5,8 @@ use Modules\HospitalPortal\Http\Controllers\Api\AuthController;
use Modules\HospitalPortal\Http\Controllers\Api\ClaimRequestController; use Modules\HospitalPortal\Http\Controllers\Api\ClaimRequestController;
use Modules\HospitalPortal\Http\Controllers\Api\MemberController; use Modules\HospitalPortal\Http\Controllers\Api\MemberController;
use Modules\HospitalPortal\Http\Controllers\ClaimController; use Modules\HospitalPortal\Http\Controllers\ClaimController;
use Modules\HospitalPortal\Http\Middleware\Authentication;
use Modules\HospitalPortal\Http\Middleware\Authorization;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@@ -16,29 +18,39 @@ use Modules\HospitalPortal\Http\Controllers\ClaimController;
| is assigned the "api" middleware group. Enjoy building your API! | is assigned the "api" middleware group. Enjoy building your API!
| |
*/ */
Route::prefix('v1')->group(function() {
Route::prefix('hospitalportal')->group(function () {
Route::prefix('hospitalportal')->group(function () { Route::middleware(Authentication::class)->group(function () {
Route::controller(AuthController::class)->group(function () {
Route::post('login', [AuthController::class, 'login'])->name('login'); 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::middleware('auth:sanctum')->group(function () {
Route::post('logout', [AuthController::class, 'logout'])->name('logout');
Route::get('/user', function (Request $request) {
return $request->user();
}); });
Route::put('reset-password', [AuthController::class, 'resetPassword'])->name('resetPassword');
//Route::post('forget-password', [AuthController::class, 'forgetPassword'])->name('forget-password');
//Route::post('verify-email', [AuthController::class, 'verifyEmail'])->name('verify-email');
Route::get('claims', [ClaimController::class, 'index']);
Route::post('search-member', [MemberController::class, 'search']); Route::middleware('auth:sanctum')->group(function () {
Route::get('claim-requests', [ClaimRequestController::class, 'index'])->name('claim-requests.index'); Route::post('logout', [AuthController::class, 'logout'])->name('logout');
Route::post('claim-requests', [ClaimRequestController::class, 'store'])->name('claim-requests.store'); Route::get('/user', function (Request $request) {
Route::get('claim-requests/{claim_request_id}/log', [ClaimRequestController::class, 'generateLog'])->name('claim-requests.generate-log'); return $request->user();
Route::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show'); });
Route::put('reset-password', [AuthController::class, 'resetPassword'])->name('resetPassword');
Route::get('claims', [ClaimController::class, 'index']);
Route::middleware(Authorization::class)->group(function () {
Route::controller(MemberController::class)->group(function () {
Route::post('search-member', 'search');
});
});
Route::get('claim-requests', [ClaimRequestController::class, 'index'])->name('claim-requests.index');
Route::post('claim-requests', [ClaimRequestController::class, 'store'])->name('claim-requests.store');
Route::get('claim-requests/{claim_request_id}/log', [ClaimRequestController::class, 'generateLog'])->name('claim-requests.generate-log');
Route::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show');
});
}); });
}); });

View File

@@ -10,6 +10,8 @@ use Illuminate\Http\Request;
use Illuminate\Routing\Controller; use Illuminate\Routing\Controller;
use Modules\Internal\Transformers\ClaimRequestResource; use Modules\Internal\Transformers\ClaimRequestResource;
use Modules\Internal\Transformers\ClaimRequestShowResource; use Modules\Internal\Transformers\ClaimRequestShowResource;
use App\Models\File;
use App\Models\FilesMcu;
class ClaimRequestController extends Controller class ClaimRequestController extends Controller
{ {
@@ -140,4 +142,29 @@ class ClaimRequestController extends Controller
return $claimRequest; return $claimRequest;
} }
public function filesMcu(Request $request)
{
$request->validate([
'id' => 'required',
'memberid' => 'required'
]);
if ($request->hasFile('result_files')) {
$pathFile = File::storeFile('claim-result', $request->id, $request->result_files);
$data = [
'memberid' => $request->id,
'original_name' => $request->result_files->getClientOriginalName(),
'path' => $pathFile,
'created_by' => auth()->user()->id,
'updated_by' => auth()->user()->id
];
FilesMcu::create($data);
return Helper::responseJson(data: $request->toArray(), message: 'Berhasil tambah file MemberID '.$request->memberid.', silahkan lihat dilaporan');
}
else
{
return Helper::responseJson(data: $request->toArray(), message: 'Tidak ada file member yang ditambahkan');
}
}
} }

View File

@@ -18,6 +18,7 @@ use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Modules\Internal\Services\MemberEnrollmentService; use Modules\Internal\Services\MemberEnrollmentService;
use PDF; use PDF;
use Illuminate\Support\Facades\DB;
class CorporateMemberController extends Controller class CorporateMemberController extends Controller
{ {
@@ -34,9 +35,6 @@ class CorporateMemberController extends Controller
$members = Member::query() $members = Member::query()
->filter($request->all()) ->filter($request->all())
// ->where('corporate_id', $corporate_id) // ->where('corporate_id', $corporate_id)
->whereHas('employeds', function ($employeds) use ($corporate_id) {
$employeds->where('corporate_id', $corporate_id);
})
->with([ ->with([
'employeds', 'employeds',
'currentPolicy', 'currentPolicy',

View File

@@ -263,7 +263,7 @@ class ClaimEncounterController extends Controller
} }
} }
public function x`counters($claim_id) public function xcounters($claim_id)
{ {
$claim = Claim::findOrFail($claim_id); $claim = Claim::findOrFail($claim_id);
$encounters = $claim->encounters()->get(); $encounters = $claim->encounters()->get();

View File

@@ -158,7 +158,9 @@ Route::prefix('internal')->group(function () {
Route::resource('doctors', DoctorController::class); Route::resource('doctors', DoctorController::class);
Route::post('generate-log/{member_id}', [CorporateMemberController::class, 'generateLog']); Route::post('generate-log/{member_id}', [CorporateMemberController::class, 'generateLog']);
Route::controller(ClaimRequestController::class)->group(function(){
Route::post('files-mcu','filesMcu');
});
Route::get('claim-requests', [ClaimRequestController::class, 'index'])->name('claim-requests.index'); Route::get('claim-requests', [ClaimRequestController::class, 'index'])->name('claim-requests.index');
Route::post('claim-requests/{id}/approve', [ClaimRequestController::class, 'approve'])->name('claim-requests.approve'); Route::post('claim-requests/{id}/approve', [ClaimRequestController::class, 'approve'])->name('claim-requests.approve');
Route::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show'); Route::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show');

View File

@@ -2,7 +2,9 @@
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware; use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Facades\Auth;
class Authenticate extends Middleware class Authenticate extends Middleware
{ {
@@ -18,4 +20,13 @@ class Authenticate extends Middleware
return route('login'); return route('login');
} }
} }
public function handle($request, Closure $next, ...$guards)
{
if (Auth::guard('sanctum')->guest()) {
return response()->json(['error' => 'Bearer Authorization is required'], 401);
}
return parent::handle($request, $next, ...$guards);
}
} }

14
app/Models/FilesMcu.php Normal file
View File

@@ -0,0 +1,14 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class FilesMcu extends Model
{
use HasFactory;
protected $table = 'files_mcu';
protected $primaryKey = 'id';
protected $fillable = ['memberid', 'original_name', 'path', 'created_by','updated_by', 'created_at', 'updated_at'];
}

View File

@@ -0,0 +1,36 @@
<?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('files_mcu', function (Blueprint $table) {
$table->increments('id');
$table->bigInteger('memberid');
$table->string('original_name', 255);
$table->string('path', 255);
$table->bigInteger('created_by');
$table->bigInteger('updated_by');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('files_mcu');
}
};

View File

@@ -21,8 +21,7 @@ import { useState, SyntheticEvent, useContext, useEffect } from 'react';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import axios from '../../utils/axios'; import axios from '../../utils/axios';
import { fDate } from '../../utils/formatTime';
// sections // sections
// import ListTable from '../../sections/claimreports/ListTable'; // import ListTable from '../../sections/claimreports/ListTable';
@@ -107,69 +106,70 @@ const StyledTab = styled((props: StyledTabProps) => <Tab disableRipple {...props
}) })
); );
export default function ServiceMonitoring() { export default function ServiceMonitoring() {
const { themeStretch } = useSettings(); const { themeStretch } = useSettings();
const navigate = useNavigate(); const navigate = useNavigate();
const [value, setValue] = useState(0); const [value, setValue] = useState(0);
const handleChange = (event: SyntheticEvent, newValue: number) => { const handleChange = (event: SyntheticEvent, newValue: number) => {
setValue(newValue); setValue(newValue);
}; };
const [data, setData] = useState({}); const [data, setData] = useState({});
const [data1, setData1] = useState(); const [corporate, setCorporate] = useState();
const {corporateValue} = useContext(UserCurrentCorporateContext); const { corporateValue } = useContext(UserCurrentCorporateContext);
const {id} = useParams(); const { id } = useParams();
const claimId = '2' const claimId = '2';
console.log('id',id); console.log('id', id);
useEffect (() => { useEffect(() => {
console.log('fetching data...');
console.log('fetching data...') axios
axios.get('/data/'+id ) .get('/data/' + id)
.then(response => { .then((response) => {
console.log('data fetched...', response.data) console.log('data fetched...', response.data);
setData(response.data); setData(response.data);
}) })
.catch(error => { .catch((error) => {
console.error('error fetching data...', error); console.error('error fetching data...', error);
}); });
axios
.get('/corporate-manage/' + corporateValue)
.then((response) => {
console.log('corporate fetched...', response.data);
setCorporate(response.data);
})
.catch((error) => {
console.error('error fetching corporate...', error);
});
}, []); }, []);
console.log('Data:' , data); console.log('Data:', data);
const [encounterData, setEncounterData] = useState({}); const [encounterData, setEncounterData] = useState({});
useEffect(() => { useEffect(() => {
console.log('fetching encounter data...'); console.log('fetching encounter data...');
axios.get('/claims/${claim_id}/encounters') axios
.then(response => { .get('/claims/${claim_id}/encounters')
.then((response) => {
console.log('encounter data fetched...', response.data); console.log('encounter data fetched...', response.data);
setEncounterData(response.data); setEncounterData(response.data);
}) })
.catch(error => { .catch((error) => {
console.error('error fetching encounter data...', error); console.error('error fetching encounter data...', error);
}); });
}, []); }, []);
return ( return (
<Page title="Service Monitoring 123456"> <Page title="Service Monitoring 123456">
<Container maxWidth={themeStretch ? false : 'xl'}> <Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center" sx={{ marginBottom: 2 }}> <Stack direction="row" alignItems="center" sx={{ marginBottom: 2 }}>
<IconButton onClick={() => <IconButton
navigate('/alarm-center') onClick={() => navigate('/alarm-center')}
} sx={{ marginRight: '10px', color: '#424242' }}
sx={{ marginRight: '10px', color: '#424242' }}> >
<Iconify icon="heroicons-outline:arrow-narrow-left" /> <Iconify icon="heroicons-outline:arrow-narrow-left" />
</IconButton> </IconButton>
<Typography variant="h5">Service Monitoring</Typography> <Typography variant="h5">Service Monitoring</Typography>
@@ -206,23 +206,25 @@ export default function ServiceMonitoring() {
<Stack spacing={2}> <Stack spacing={2}>
<Stack> <Stack>
<Typography variant="caption">Nama perusahaan</Typography> <Typography variant="caption">Nama perusahaan</Typography>
<Typography variant="body2">{corporateValue}</Typography> <Typography variant="body2">{corporate?.name}</Typography>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="caption">Nama Lengkap</Typography> <Typography variant="caption">Nama Lengkap</Typography>
<Typography variant="body2">{data.name || 'Loading...'}</Typography> <Typography variant="body2">{data?.name || 'Loading...'}</Typography>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="caption">Tanggal lahir</Typography> <Typography variant="caption">Tanggal lahir</Typography>
<Typography variant="body2">{data.birth_date}</Typography> <Typography variant="body2">
{data?.birth_date ? fDate(data?.birth_date) : ''}
</Typography>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="caption">Email</Typography> <Typography variant="caption">Email</Typography>
<Typography variant="body2">{data.email}</Typography> <Typography variant="body2">{data?.email}</Typography>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="caption">No telepon</Typography> <Typography variant="caption">No telepon</Typography>
<Typography variant="body2">{data.phone}</Typography> <Typography variant="body2">{data?.phone}</Typography>
</Stack> </Stack>
</Stack> </Stack>
<Stack> <Stack>

View File

@@ -18,7 +18,6 @@ import { useEffect, useState, useContext } from 'react';
import axios from '../../utils/axios'; import axios from '../../utils/axios';
import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export default function UserProfile() { export default function UserProfile() {
@@ -27,23 +26,21 @@ export default function UserProfile() {
const [data, setData] = useState(); const [data, setData] = useState();
const [data1, setData1] = useState(); const [data1, setData1] = useState();
const { corporateValue } = useContext(UserCurrentCorporateContext);
const { id } = useParams();
const {corporateValue} = useContext(UserCurrentCorporateContext); useEffect(() => {
const {id} = useParams(); axios
.get(corporateValue + '/members/' + id)
useEffect (() => { .then((response) => {
axios.get('/data/'+id )
.then(response => {
setData(response.data); setData(response.data);
}) })
.catch(error => { .catch((error) => {
console.error(error); console.error(error);
}); });
}, []); }, []);
console.log('data', data) console.log('data', data);
return ( return (
<Page title="Profile Peserta Jessica Lie"> <Page title="Profile Peserta Jessica Lie">
@@ -52,7 +49,7 @@ export default function UserProfile() {
{/* <IconButton sx={{ marginRight: '10px', color: '#424242' }} onClick={() => navigate()}> {/* <IconButton sx={{ marginRight: '10px', color: '#424242' }} onClick={() => navigate()}>
<Iconify icon="heroicons-outline:arrow-narrow-left" /> <Iconify icon="heroicons-outline:arrow-narrow-left" />
</IconButton> */} </IconButton> */}
<ButtonBack/> <ButtonBack />
<Typography variant="h5">Profil Peserta</Typography> <Typography variant="h5">Profil Peserta</Typography>
</Stack> </Stack>
<Grid container spacing={2}> <Grid container spacing={2}>
@@ -74,7 +71,7 @@ export default function UserProfile() {
<Grid container spacing={2}> <Grid container spacing={2}>
{/* Item 1 */} {/* Item 1 */}
<Grid item xs={12}> <Grid item xs={12}>
<CardPolicyNumber /> <CardPolicyNumber data={data} />
</Grid> </Grid>
{/* Item 2 */} {/* Item 2 */}
<Grid item xs={12}> <Grid item xs={12}>

View File

@@ -278,7 +278,7 @@ export default function Index() {
params: { ...parameters }, params: { ...parameters },
}); });
console.log('member',corporateMembers ); console.log('member', corporateMembers);
const corporateTopUpLimit = await axios.get(`${corporateValue}/topup`); const corporateTopUpLimit = await axios.get(`${corporateValue}/topup`);
setSearchParams(parameters); setSearchParams(parameters);

View File

@@ -8,6 +8,8 @@ import {
linearProgressClasses, linearProgressClasses,
Grid, Grid,
} from '@mui/material'; } from '@mui/material';
import { useEffect, useState } from 'react';
import { description } from '../../../../../dashboard/src/_mock/text';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -25,203 +27,49 @@ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export default function CardBenefitSummary() { export default function CardBenefitSummary({ data }) {
const [benefits, setBenefits] = useState([]);
console.log('data', data);
useEffect(() => {
setBenefits(data);
}, [data]);
return ( return (
<div style={{ marginTop: '1rem' }}> <div style={{ marginTop: '1rem' }}>
<Typography padding={1} variant="subtitle2"> <Typography padding={1} variant="subtitle2">
Benefit Summary Benefit Summary
</Typography> </Typography>
<Card> <Card>
{/* {Object.entries(benefits?.data).map(([key, value]) => (
<div key={key}>
<span>Haloo</span>
</div>
))} */}
<Grid container spacing={1} marginTop={1} sx={{ backgroundColor: '#F4F6F8', padding: 1 }}> <Grid container spacing={1} marginTop={1} sx={{ backgroundColor: '#F4F6F8', padding: 1 }}>
{/* Card 1 */} {/* Card 1 */}
<Grid item xs={12} sm={6} md={6} lg={4}> {benefits?.map((item, key) => (
<Card sx={{ padding: 1 }}> <Grid item xs={12} sm={6} md={6} lg={4} key={key}>
<Stack spacing={1}> <Card sx={{ padding: 1 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}> <Stack spacing={1}>
Rawat Jalan <Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
</Typography> {item?.description}
<Typography variant="body2" color="#0A0A0A">
Yearly Limits
</Typography>
<BorderLinearProgress variant="determinate" value={100} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">10.000.000</Typography>
<Typography>/</Typography>
<Typography variant="body2" color="#757575">
10.000.000
</Typography> </Typography>
</Stack> <Typography variant="body2" color="#0A0A0A">
</Stack> Yearly Limits
</Card>
</Grid>
{/* Card 2 */}
<Grid item xs={12} sm={6} md={6} lg={4}>
<Card sx={{ padding: 1 }}>
<Stack spacing={1}>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
Rawat Inap
</Typography>
<Typography variant="body2" color="#0A0A0A">
Yearly Limits
</Typography>
<BorderLinearProgress variant="determinate" value={100} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">10.000.000</Typography>
<Typography>/</Typography>
<Typography variant="body2" color="#757575">
10.000.000
</Typography> </Typography>
<BorderLinearProgress variant="determinate" value={100} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">0</Typography>
<Typography>/</Typography>
<Typography variant="body2" color="#757575">
{item?.pivot['limit_amount']}
</Typography>
</Stack>
</Stack> </Stack>
</Stack> </Card>
</Card> </Grid>
</Grid> ))}
{/* Card 3 */}
<Grid item xs={12} sm={6} md={6} lg={4}>
<Card sx={{ padding: 1 }}>
<Stack spacing={1}>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
Manfaat Special
</Typography>
<Typography variant="body2" color="#0A0A0A">
Yearly Limits
</Typography>
<BorderLinearProgress variant="determinate" value={100} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">10.000.000</Typography>
<Typography>/</Typography>
<Typography variant="body2" color="#757575">
10.000.000
</Typography>
</Stack>
</Stack>
</Card>
</Grid>
{/* Card 4 */}
<Grid item xs={12} sm={6} md={6} lg={4}>
<Card sx={{ padding: 1 }}>
<Stack spacing={1}>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
Manfaat Special
</Typography>
<Typography variant="body2" color="#0A0A0A">
Yearly Limits
</Typography>
<BorderLinearProgress variant="determinate" value={100} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">10.000.000</Typography>
<Typography>/</Typography>
<Typography variant="body2" color="#757575">
10.000.000
</Typography>
</Stack>
</Stack>
</Card>
</Grid>
{/* Card 5 */}
<Grid item xs={12} sm={6} md={6} lg={4}>
<Card sx={{ padding: 1 }}>
<Stack spacing={1}>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
Perobatan Mata
</Typography>
<Typography variant="body2" color="#0A0A0A">
Yearly Limits
</Typography>
<BorderLinearProgress variant="determinate" value={100} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">10.000.000</Typography>
<Typography>/</Typography>
<Typography variant="body2" color="#757575">
10.000.000
</Typography>
</Stack>
</Stack>
</Card>
</Grid>
{/* Card 6 */}
<Grid item xs={12} sm={6} md={6} lg={4}>
<Card sx={{ padding: 1 }}>
<Stack spacing={1}>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
Perawatan Gigi
</Typography>
<Typography variant="body2" color="#0A0A0A">
Yearly Limits
</Typography>
<BorderLinearProgress variant="determinate" value={100} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">10.000.000</Typography>
<Typography>/</Typography>
<Typography variant="body2" color="#757575">
10.000.000
</Typography>
</Stack>
</Stack>
</Card>
</Grid>
{/* Card 7 */}
<Grid item xs={12} sm={6} md={6} lg={4}>
<Card sx={{ padding: 1 }}>
<Stack spacing={1}>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
Kehamilan
</Typography>
<Typography variant="body2" color="#0A0A0A">
Yearly Limits
</Typography>
<BorderLinearProgress variant="determinate" value={100} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">10.000.000</Typography>
<Typography>/</Typography>
<Typography variant="body2" color="#757575">
10.000.000
</Typography>
</Stack>
</Stack>
</Card>
</Grid>
{/* Card 8 */}
<Grid item xs={12} sm={6} md={6} lg={4}>
<Card sx={{ padding: 1 }}>
<Stack spacing={1}>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
Laboratorium
</Typography>
<Typography variant="body2" color="#0A0A0A">
Yearly Limits
</Typography>
<BorderLinearProgress variant="determinate" value={100} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">10.000.000</Typography>
<Typography>/</Typography>
<Typography variant="body2" color="#757575">
10.000.000
</Typography>
</Stack>
</Stack>
</Card>
</Grid>
{/* Card 9 */}
<Grid item xs={12} sm={6} md={6} lg={4}>
<Card sx={{ padding: 1 }}>
<Stack spacing={1}>
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
Manfaat Farmasi
</Typography>
<Typography variant="body2" color="#0A0A0A">
Yearly Limits
</Typography>
<BorderLinearProgress variant="determinate" value={100} />
<Stack direction="row" spacing={0.25}>
<Typography variant="body2">10.000.000</Typography>
<Typography>/</Typography>
<Typography variant="body2" color="#757575">
10.000.000
</Typography>
</Stack>
</Stack>
</Card>
</Grid>
</Grid> </Grid>
</Card> </Card>
</div> </div>

View File

@@ -33,10 +33,10 @@ const rows = [
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export default function CardClaimHistory() { export default function CardClaimHistory(benefitMember) {
const [page, setPage] = useState(0); const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5); const [rowsPerPage, setRowsPerPage] = useState(5);
console.log('benefitMember', benefitMember);
const handleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, newPage: number) => { const handleChangePage = (event: React.MouseEvent<HTMLButtonElement> | null, newPage: number) => {
setPage(newPage); setPage(newPage);
}; };

View File

@@ -9,21 +9,18 @@ import { UserCurrentCorporateContext } from '../../../contexts/UserCurrentCorpor
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material'; import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import { enqueueSnackbar } from 'notistack'; import { enqueueSnackbar } from 'notistack';
import { fDate } from '../../../utils/formatTime';
export default function CardPersonalInformation({ data }) {
export default function CardPersonalInformation({data}) {
/* const [data, setData] = useState(); */ /* const [data, setData] = useState(); */
const [openDialog, setOpenDialog] = useState(false); const [openDialog, setOpenDialog] = useState(false);
const [editedData, setEditedData] = useState(null); const [editedData, setEditedData] = useState(null);
const { id } = useParams(); const { id } = useParams();
const [weight, setWeight] = useState(data?.last_weight_kg || ''); const [weight, setWeight] = useState(data?.last_weight_kg || '');
const [height, setHeight] = useState(data?.last_height_cm || ''); const [height, setHeight] = useState(data?.last_height_cm || '');
const [email, setEmail] = useState(data?.email || '' ); const [email, setEmail] = useState(data?.email || '');
const [phone, setPhone] = useState(data?.phone || '' ); const [phone, setPhone] = useState(data?.phone || '');
const [address, setAddress] = useState(data?.main_address_id || '' ); const [address, setAddress] = useState(data?.main_address_id || '');
/* const [updatedData, setUpdatedData] = useState(data); */ /* const [updatedData, setUpdatedData] = useState(data); */
@@ -31,13 +28,11 @@ export default function CardPersonalInformation({data}) {
setWeight(data?.last_weight_kg || ''); setWeight(data?.last_weight_kg || '');
setHeight(data?.last_height_cm || ''); setHeight(data?.last_height_cm || '');
setEmail(data?.email || ''); setEmail(data?.email || '');
setPhone(data?.phone||''); setPhone(data?.phone || '');
setAddress(data?.main_address_id||''); setAddress(data?.main_address_id || '');
setEditedData(data); setEditedData(data);
setOpenDialog(true); setOpenDialog(true);
}; };
const handleCloseDialog = () => { const handleCloseDialog = () => {
// Close the dialog // Close the dialog
@@ -56,7 +51,7 @@ export default function CardPersonalInformation({data}) {
phone: phone, phone: phone,
main_address_id: address, main_address_id: address,
}; };
// Update the data in the database using the updatedData object // Update the data in the database using the updatedData object
axios axios
.put('/data/' + id, updatedData) .put('/data/' + id, updatedData)
@@ -70,17 +65,9 @@ export default function CardPersonalInformation({data}) {
enqueueSnackbar('Failed to update data', { variant: 'error' }); enqueueSnackbar('Failed to update data', { variant: 'error' });
}); });
}; };
return ( return (
<Card sx={{ borderRadius: '6px', paddingY: 2 }}> <Card sx={{ borderRadius: '6px', paddingY: 2 }}>
{/* Stack 1 */} {/* Stack 1 */}
<Stack <Stack
direction="row" direction="row"
@@ -89,7 +76,9 @@ export default function CardPersonalInformation({data}) {
sx={{ paddingY: 1, paddingX: 3 }} sx={{ paddingY: 1, paddingX: 3 }}
> >
<Typography variant="subtitle2">Informasi Pribadi</Typography> <Typography variant="subtitle2">Informasi Pribadi</Typography>
<Button startIcon={<Iconify icon="heroicons:pencil-solid" />} onClick={handleEditData}>Edit Data</Button> <Button startIcon={<Iconify icon="heroicons:pencil-solid" />} onClick={handleEditData}>
Edit Data
</Button>
</Stack> </Stack>
{/* Stack 2 */} {/* Stack 2 */}
<Stack direction="row" spacing={2} paddingX={2}> <Stack direction="row" spacing={2} paddingX={2}>
@@ -119,15 +108,15 @@ export default function CardPersonalInformation({data}) {
<Stack direction="row" paddingY={1} spacing={2} sx={{ flex: '100%' }}> <Stack direction="row" paddingY={1} spacing={2} sx={{ flex: '100%' }}>
<Stack sx={{ width: '60%' }}> <Stack sx={{ width: '60%' }}>
<Typography variant="caption">Nama Lengkap</Typography> <Typography variant="caption">Nama Lengkap</Typography>
<Typography variant="body2"> {data ?. name} </Typography> <Typography variant="body2"> {data?.name} </Typography>
</Stack> </Stack>
<Stack sx={{ width: '20%' }}> <Stack sx={{ width: '20%' }}>
<Typography variant="caption">Berat Badan </Typography> <Typography variant="caption">Berat Badan </Typography>
<Typography variant="body2">{data ?. last_weight_kg} kg</Typography> <Typography variant="body2">{data?.last_weight_kg} kg</Typography>
</Stack> </Stack>
<Stack sx={{ width: '20%' }}> <Stack sx={{ width: '20%' }}>
<Typography variant="caption">Tinggi Badan </Typography> <Typography variant="caption">Tinggi Badan </Typography>
<Typography variant="body2">{data ?. last_height_cm} cm</Typography> <Typography variant="body2">{data?.last_height_cm} cm</Typography>
</Stack> </Stack>
</Stack> </Stack>
</Stack> </Stack>
@@ -139,15 +128,18 @@ export default function CardPersonalInformation({data}) {
<Stack direction="row" spacing={2} sx={{ flex: '100%' }}> <Stack direction="row" spacing={2} sx={{ flex: '100%' }}>
<Stack sx={{ width: '100%' }}> <Stack sx={{ width: '100%' }}>
<Typography variant="caption">Tempat Lahir</Typography> <Typography variant="caption">Tempat Lahir</Typography>
<Typography variant="body2"> {data ?. birth_place} </Typography> <Typography variant="body2"> {data?.birth_place} </Typography>
</Stack> </Stack>
<Stack sx={{ width: '100%' }}> <Stack sx={{ width: '100%' }}>
<Typography variant="caption">Tanggal Lahir</Typography> <Typography variant="caption">Tanggal Lahir</Typography>
<Typography variant="body2">{data ?. birth_date}</Typography> <Typography variant="body2">
{' '}
{data?.birth_date ? fDate(data?.birth_date) : ''}
</Typography>
</Stack> </Stack>
<Stack sx={{ width: '100%' }}> <Stack sx={{ width: '100%' }}>
<Typography variant="caption">Jenis Kelamin</Typography> <Typography variant="caption">Jenis Kelamin</Typography>
<Typography variant="body2">{data ?. gender}</Typography> <Typography variant="body2">{data?.gender}</Typography>
</Stack> </Stack>
</Stack> </Stack>
</Stack> </Stack>
@@ -157,18 +149,16 @@ export default function CardPersonalInformation({data}) {
<Stack direction="row" spacing={2} sx={{ flex: '100%' }}> <Stack direction="row" spacing={2} sx={{ flex: '100%' }}>
<Stack sx={{ width: '100%' }}> <Stack sx={{ width: '100%' }}>
<Typography variant="caption">Nomor Telpon</Typography> <Typography variant="caption">Nomor Telpon</Typography>
<Typography variant="body2">{data ?. phone}</Typography> <Typography variant="body2">{data?.phone}</Typography>
</Stack> </Stack>
<Stack sx={{ width: '100%' }}> <Stack sx={{ width: '100%' }}>
<Typography variant="caption">Email</Typography> <Typography variant="caption">Email</Typography>
<Typography variant="body2">{data?. email}</Typography> <Typography variant="body2">{data?.email}</Typography>
</Stack> </Stack>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="caption">Alamat</Typography> <Typography variant="caption">Alamat</Typography>
<Typography variant="body2"> <Typography variant="body2">{data?.main_address_id}</Typography>
{data ?. main_address_id}
</Typography>
</Stack> </Stack>
</Stack> </Stack>
{/* Stack 3.3 */} {/* Stack 3.3 */}
@@ -183,7 +173,7 @@ export default function CardPersonalInformation({data}) {
> >
<Stack> <Stack>
<Typography variant="caption">Nomor NIK</Typography> <Typography variant="caption">Nomor NIK</Typography>
<Typography variant="body2">{data ?. nik}</Typography> <Typography variant="body2">{data?.nik}</Typography>
</Stack> </Stack>
<Stack> <Stack>
<Button variant="contained" startIcon={<VisibilityIcon />}> <Button variant="contained" startIcon={<VisibilityIcon />}>
@@ -198,65 +188,65 @@ export default function CardPersonalInformation({data}) {
<Stack direction="row" justifyContent="space-between" spacing={2} sx={{ flex: '100%' }}> <Stack direction="row" justifyContent="space-between" spacing={2} sx={{ flex: '100%' }}>
<Stack> <Stack>
<Typography variant="caption">Agama</Typography> <Typography variant="caption">Agama</Typography>
<Typography variant="body2">{data ?. religion}</Typography> <Typography variant="body2">{data?.religion}</Typography>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="caption">Status</Typography> <Typography variant="caption">Status</Typography>
<Typography variant="body2">{data ?. marital_status}</Typography> <Typography variant="body2">{data?.marital_status}</Typography>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="caption">Pendidikan</Typography> <Typography variant="caption">Pendidikan</Typography>
<Typography variant="body2">{data ?. last_education}</Typography> <Typography variant="body2">{data?.last_education}</Typography>
</Stack> </Stack>
<Stack> <Stack>
<Typography variant="caption">Pekerjaan</Typography> <Typography variant="caption">Pekerjaan</Typography>
<Typography variant="body2">{data ?. current_employment}</Typography> <Typography variant="body2">{data?.current_employment}</Typography>
</Stack> </Stack>
</Stack> </Stack>
</Stack> </Stack>
</Stack> </Stack>
{/* Dialog */} {/* Dialog */}
<Dialog open={openDialog} onClose={handleCloseDialog}> <Dialog open={openDialog} onClose={handleCloseDialog}>
<DialogTitle>Edit Data</DialogTitle> <DialogTitle>Edit Data</DialogTitle>
<DialogContent> <DialogContent>
<Stack spacing={2}> <Stack spacing={2}>
<TextField <TextField
label="Full Name" label="Full Name"
value={editedData ? editedData.name : ''} value={editedData ? editedData.name : ''}
onChange={(e) => setEditedData({ ...editedData, name: e.target.value })} onChange={(e) => setEditedData({ ...editedData, name: e.target.value })}
fullWidth fullWidth
sx={{ marginTop: '16px' }} sx={{ marginTop: '16px' }}
/> />
<TextField <TextField
label="Weight (kg)" label="Weight (kg)"
value={weight} value={weight}
onChange={(e) => setWeight(e.target.value)} onChange={(e) => setWeight(e.target.value)}
fullWidth fullWidth
sx={{ marginTop: '16px' }} sx={{ marginTop: '16px' }}
/> />
<TextField <TextField
label="Height (cm)" label="Height (cm)"
value={height} value={height}
onChange={(e) => setHeight(e.target.value)} onChange={(e) => setHeight(e.target.value)}
fullWidth fullWidth
sx={{ marginTop: '16px' }} sx={{ marginTop: '16px' }}
/> />
<TextField <TextField
label="Email Address" label="Email Address"
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
fullWidth fullWidth
sx={{ marginTop: '16px' }} sx={{ marginTop: '16px' }}
/> />
<TextField <TextField
label="Phone No." label="Phone No."
value={phone} value={phone}
onChange={(e) => setPhone(e.target.value)} onChange={(e) => setPhone(e.target.value)}
fullWidth fullWidth
sx={{ marginTop: '16px' }} sx={{ marginTop: '16px' }}
/> />
{/* <TextField {/* <TextField
label="Address" label="Address"
value={address} value={address}
onChange={(e) => setAddress(e.target.value)} onChange={(e) => setAddress(e.target.value)}
@@ -264,18 +254,17 @@ export default function CardPersonalInformation({data}) {
sx={{ marginTop: '16px' }} sx={{ marginTop: '16px' }}
/> */} /> */}
{/* Add more fields as needed */}
</Stack>
</DialogContent>
{/* Add more fields as needed */} <DialogActions>
</Stack> <Button onClick={handleCloseDialog}>Cancel</Button>
</DialogContent> <Button onClick={handleSaveData} variant="contained" color="primary">
Save
<DialogActions> </Button>
<Button onClick={handleCloseDialog}>Cancel</Button> </DialogActions>
<Button onClick={handleSaveData} variant="contained" color="primary"> </Dialog>
Save
</Button>
</DialogActions>
</Dialog>
</Card> </Card>
); );
} }

View File

@@ -20,7 +20,6 @@ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
}, },
})); }));
type DataMember = { type DataMember = {
id: number; id: number;
fullName: string; fullName: string;
@@ -65,18 +64,17 @@ type CardPolicyProps = {
members?: DataMember[]; members?: DataMember[];
}; };
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export default function CardPolicyNumber() {
export default function CardPolicyNumber({ data }) {
const { corporateValue } = useContext(UserCurrentCorporateContext);
const [policyNumber, setPolicyNumber] = useState('');
const [policyData, setPolicyData] = useState<CardPolicyProps>();
const { corporateValue } = useContext(UserCurrentCorporateContext); const [limitMember, setLimitMember] = useState();
const [policyNumber,setPolicyNumber] = useState(''); const [benefitMember, setBenefitMember] = useState();
const [policyData, setPolicyData] = useState<CardPolicyProps>();
/* axios.get(`${corporateValue}/topup`) /* axios.get(`${corporateValue}/topup`)
.then(response => { .then(response => {
console.log(response.data); console.log(response.data);
}) })
@@ -84,63 +82,68 @@ export default function CardPolicyNumber() {
console.error(error); console.error(error);
}); */ }); */
useEffect(() => { useEffect(() => {
axios axios
.get(`${corporateValue}/topup`) .get(`${corporateValue}/topup`)
.then(response => { .then((response) => {
const { data } = response.data; // Access the 'data' object from the response const { data } = response.data; // Access the 'data' object from the response
const { policyNumber } = data; // Access the 'policyNumber' field from the 'data' object const { policyNumber } = data; // Access the 'policyNumber' field from the 'data' object
setPolicyNumber(policyNumber); setPolicyNumber(policyNumber);
}) })
.catch(error => { .catch((error) => {
console.error(error); console.error(error);
}); });
// const corporatePolicyLimit = axios.get(`${corporateValue}/policy`); setLimitMember(data?.limit);
// const corporateTopUpLimit = axios.get(`${corporateValue}/topup`); setBenefitMember(data?.benefits);
// setPolicyData({
// limit: corporatePolicyLimit.data.data,
// topUpLimit: corporateTopUpLimit.data.data,
// });
}, [corporateValue]);
const calculateProgressValue = (current:number,total:number) => { // const corporatePolicyLimit = axios.get(`${corporateValue}/policy`);
return (current/total) * 100; // const corporateTopUpLimit = axios.get(`${corporateValue}/topup`);
// setPolicyData({
// limit: corporatePolicyLimit.data.data,
// topUpLimit: corporateTopUpLimit.data.data,
// });
}, [corporateValue, data]);
const calculateProgressValue = (current: number, total: number) => {
return (current / total) * 100;
}; };
const progressValue = calculateProgressValue(limitMember?.current, limitMember?.total);
const getMemberLimitUsage = (memberId: string) => { // const getMemberLimitUsage = (memberId: string) => {
if (policyData?.members) { // if (policyData?.members) {
const member = policyData.members.find(member => member.memberId === memberId); // const member = policyData.members.find((member) => member.memberId === memberId);
if (member) { // if (member) {
return member.limit; // return member.limit;
} // }
} // }
return null; // return null;
}; // };
const renderYearlyLimit = () => { // const renderYearlyLimit = () => {
if (policyData) { // if (policyData) {
const { myLimit } = policyData.limit; // const { myLimit } = policyData.limit;
const { balance, total, percentage } = myLimit; // console.log('myLimit', myLimit);
const progressValue = calculateProgressValue(balance, total); // const { balance, total, percentage } = myLimit;
// const progressValue = calculateProgressValue(balance, total);
return ( // return (
<Stack spacing={1} sx={{ width: '206.5px' }}> // <Stack spacing={1} sx={{ width: '206.5px' }}>
<Typography variant="subtitle2">Yearly Limit</Typography> // <Typography variant="subtitle2">Yearly Limit</Typography>
<BorderLinearProgress variant="determinate" value={progressValue} /> // <BorderLinearProgress variant="determinate" value={progressValue} />
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}> // <Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
{balance.toLocaleString()} /{' '} // {balance.toLocaleString()} /{' '}
<Typography variant="body2" color="#757575" component="span"> // <Typography variant="body2" color="#757575" component="span">
{total.toLocaleString()} // {total.toLocaleString()}
</Typography> // </Typography>
</Typography> // </Typography>
</Stack> // </Stack>
); // );
} // }
return null; // return null;
}; // };
return ( return (
<Card sx={{ padding: 2 }}> <Card sx={{ padding: 2 }}>
<Stack> <Stack>
<Stack direction="row" alignItems="center" spacing={1} justifyContent="space-between"> <Stack direction="row" alignItems="center" spacing={1} justifyContent="space-between">
@@ -153,17 +156,17 @@ export default function CardPolicyNumber() {
</Stack> </Stack>
<Stack spacing={1} sx={{ width: '206.5px' }}> <Stack spacing={1} sx={{ width: '206.5px' }}>
<Typography variant="subtitle2">Yearly Limit</Typography> <Typography variant="subtitle2">Yearly Limit</Typography>
<BorderLinearProgress variant="determinate" value={100} /> <BorderLinearProgress variant="determinate" value={progressValue} />
<Typography variant="subtitle2" sx={{ fontWeight: 500 }}> <Typography variant="subtitle2" sx={{ fontWeight: 500 }}>
10.000.000 /{' '} {limitMember?.current}/{' '}
<Typography variant="body2" color="#757575" component="span"> <Typography variant="body2" color="#757575" component="span">
10.000.000 {limitMember?.total}
</Typography> </Typography>
</Typography> </Typography>
</Stack> </Stack>
</Stack> </Stack>
{/* Benefit Summary */} {/* Benefit Summary */}
<CardBenefitSummary /> <CardBenefitSummary data={benefitMember} />
</Stack> </Stack>
</Card> </Card>
); );

View File

@@ -1,8 +1,9 @@
import { format, getTime, formatDistanceToNow } from 'date-fns'; import { format,parseISO, getTime, setHours, setMinutes , formatDistanceToNow } from 'date-fns';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export function fDate(date: Date | string | number) { export function fDate(date: Date | string | number) {
console.log(date);
return format(new Date(date), 'dd MMMM yyyy'); return format(new Date(date), 'dd MMMM yyyy');
} }
@@ -23,3 +24,16 @@ export function fToNow(date: Date | string | number) {
addSuffix: true addSuffix: true
}); });
} }
// export function fDateString(date) {
// const dateObj = parseISO(date);
// const formattedDate = format(dateObj, 'dd MMMM yyyy');
// return formattedDate;
// }
// export function fFormattedDateString(date : String) {
// console.log(date);
// const datePart = date.split(' ')[0]; // Memisahkan bagian tanggal
// const formattedDate = fDateString(datePart); // Menggunakan fungsi sebelumnya untuk memformat tanggal
// return formattedDate;
// }

View File

@@ -2,6 +2,6 @@ GENERATE_SOURCEMAP=false
PORT=8083 PORT=8083
REACT_APP_HOST_API_URL="http://lms.test" REACT_APP_HOST_API_URL="http://127.0.0.1:8000"
VITE_API_URL="http://lms.test/api/internal" VITE_API_URL="http://127.0.0.1:8000/api/internal"

View File

@@ -38,6 +38,7 @@
] ]
}, },
"dependencies": { "dependencies": {
"@ajoelp/json-to-formdata": "^1.5.0",
"@date-io/date-fns": "^2.16.0", "@date-io/date-fns": "^2.16.0",
"@emotion/cache": "^11.10.5", "@emotion/cache": "^11.10.5",
"@emotion/react": "^11.10.5", "@emotion/react": "^11.10.5",

View File

@@ -28,7 +28,9 @@ import {
ButtonGroup, ButtonGroup,
Grid, Grid,
Tooltip, Tooltip,
Divider,
} from '@mui/material'; } from '@mui/material';
import Iconify from '@/components/Iconify';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add'; import AddIcon from '@mui/icons-material/Add';
@@ -49,8 +51,34 @@ import { enqueueSnackbar } from 'notistack';
import { LoadingButton } from '@mui/lab'; import { LoadingButton } from '@mui/lab';
import DialogLog from './sections/DialogLog'; import DialogLog from './sections/DialogLog';
import HistoryIcon from '@mui/icons-material/History'; import HistoryIcon from '@mui/icons-material/History';
import { makeFormData } from '@/utils/jsonToFormData';
export default function CorporatePlanList() { export default function CorporatePlanList({handleSubmitSuccess}) {
// Files MCU
const fileMcuInput = useRef<HTMLInputElement>(null);
const [fileMcus, setFileMcus] = useState([]);
const handleMcuInputChange = (id, member_id) => (event) => {
if (event.target.files[0]) {
const updatedFiles = Array.from(event.target.files).map((file) => ({
file,
id
}));
setFileMcus((prevFileMcus) => {
const newFileMcus = [...prevFileMcus, ...updatedFiles];
submitRequest(id, newFileMcus, member_id);
return newFileMcus;
});
} else {
console.log('Tidak ada file');
}
};
const removeMcuFiles = (id, index) => {
setFileMcus((filesState) =>
filesState.filter((file, fileIndex) => fileIndex !== index || file.id !== id)
);
};
const [submitLoading, setSubmitLoading] = useState(false);
const { themeStretch } = useSettings(); const { themeStretch } = useSettings();
const { corporate_id } = useParams(); const { corporate_id } = useParams();
const [searchParams, setSearchParams] = useSearchParams(); const [searchParams, setSearchParams] = useSearchParams();
@@ -149,6 +177,47 @@ export default function CorporatePlanList() {
</form> </form>
); );
} }
function submitRequest(id, newFileMcus, member_id) {
setSubmitLoading(true);
if (newFileMcus && newFileMcus.length > 0) {
const fileWithId = newFileMcus.find((file) => file.id === id);
if (fileWithId) {
const formData = makeFormData({
id: id,
memberid: member_id,
result_files: fileWithId.file,
});
axios
.post('/files-mcu', formData)
.then((response) => {
const responseData = response?.data;
if(responseData)
{
setTimeout(() => {
loadDataTableData();
}, 2000);
enqueueSnackbar(responseData.message ?? 'Berhasil tambah file MemberID '+member_id+', silahkan lihat dilaporan', { variant: 'success' });
handleSubmitSuccess();
}
})
.catch(({ response }) => {
const responseData = response?.data;
if(responseData)
{
enqueueSnackbar(responseData.message ?? 'Something Went Wrong', { variant: 'error' });
}
})
.then(() => {
setSubmitLoading(false);
});
} else {
console.log(`File dengan ID ${id} tidak ditemukan`);
}
}
}
// End Files MCU
function ImportForm(props: any) { function ImportForm(props: any) {
// IMPORT // IMPORT
@@ -178,9 +247,7 @@ export default function CorporatePlanList() {
const handleMemberList = async (appliedFilter = null) => { const handleMemberList = async (appliedFilter = null) => {
axios.get('corporates/' + corporate_id + '/members/list').then((response) => { axios.get('corporates/' + corporate_id + '/members/list').then((response) => {
console.log(response);
const link = document.createElement('a'); const link = document.createElement('a');
console.log(response.data.data.file_name);
link.href = response.data.data.file_url; link.href = response.data.data.file_url;
link.setAttribute('download', response.data.data.file_name); link.setAttribute('download', response.data.data.file_name);
document.body.appendChild(link); document.body.appendChild(link);
@@ -577,21 +644,81 @@ export default function CorporatePlanList() {
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>
<Typography sx={{ fontWeight: '600', mb: 1 }}>File History</Typography>
<Grid container sx={{ pb: 2, mb: 2, borderBottom: 1 }}>
<Grid item xs={12}>
<Grid container>
<Grid item xs={12}>
{row.file_mcu_names
? row.file_mcu_names.split(',').map((fileName, index) => (
<div key={index}>{fileName}</div>
))
: '-'}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid> <Grid spacing={1}>
<LoadingButton <Stack sx={{ marginTop: 1}}>
id="upload-button" <LoadingButton
variant="outlined" id="upload-button"
startIcon={<InsertDriveFileIcon />} variant="outlined"
// sx={{ p: 1.8 }} startIcon={<InsertDriveFileIcon />}
// onClick={() => {handleDownloadLog(row)}} // sx={{ p: 1.8 }}
onClick={() => { // onClick={() => {handleDownloadLog(row)}}
setDialogLogOpen(true); onClick={() => {
}} setDialogLogOpen(true);
loading={loadingLog} }}
> loading={loadingLog}
Download LOG >
</LoadingButton> Download LOG
</LoadingButton>
</Stack>
{/* -------------------------------Upload Dokumen MCU------------------------------- */}
<Stack sx={{ marginTop: 1}}>
{/*<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2}}
>
{fileMcus &&
fileMcus
.filter((datas) => datas.id === row.id)
.map((datas, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: "text.secondary" }}>{datas.file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeMcuFiles(datas.id, index);
}}
sx={{ cursor: 'pointer' }}
></Iconify>
</Stack>
))}
</Stack>*/}
<input
type="file"
id={`file-${row.id}`}
ref={fileMcuInput}
style={{ display: 'none' }}
onChange={(event) => {
handleMcuInputChange(row.id, row.member_id)(event);
}}
accept="application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileMcuInput.current.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
</Grid> </Grid>
<DialogLog <DialogLog

View File

@@ -0,0 +1,6 @@
import jsonToFormData from '@ajoelp/json-to-formdata';
export function makeFormData(object: any) {
return jsonToFormData(object)
}

View File

@@ -1,4 +1,4 @@
REACT_APP_HOST_API_URL="http://localhost:8000" REACT_APP_HOST_API_URL="http://localhost:8000"
VITE_API_URL="http://localhost:8000/api/hospitalportal" VITE_API_URL="http://localhost:8000/api/v1/hospitalportal"

View File

@@ -1 +1 @@
VITE_API_URL="https://aso-api.linksehat.dev/api/hospitalportal" VITE_API_URL="https://aso-api.linksehat.dev/api/v1/hospitalportal"

View File

@@ -1703,6 +1703,7 @@ packages:
cpu: [arm] cpu: [arm]
os: [android] os: [android]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/@esbuild/linux-loong64/0.15.18: /@esbuild/linux-loong64/0.15.18:
@@ -1711,6 +1712,7 @@ packages:
cpu: [loong64] cpu: [loong64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/@eslint/eslintrc/1.4.1: /@eslint/eslintrc/1.4.1:
@@ -3412,6 +3414,7 @@ packages:
cpu: [x64] cpu: [x64]
os: [android] os: [android]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-android-arm64/0.15.18: /esbuild-android-arm64/0.15.18:
@@ -3420,6 +3423,7 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-darwin-64/0.15.18: /esbuild-darwin-64/0.15.18:
@@ -3428,6 +3432,7 @@ packages:
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-darwin-arm64/0.15.18: /esbuild-darwin-arm64/0.15.18:
@@ -3436,6 +3441,7 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-freebsd-64/0.15.18: /esbuild-freebsd-64/0.15.18:
@@ -3444,6 +3450,7 @@ packages:
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-freebsd-arm64/0.15.18: /esbuild-freebsd-arm64/0.15.18:
@@ -3452,6 +3459,7 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [freebsd] os: [freebsd]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-linux-32/0.15.18: /esbuild-linux-32/0.15.18:
@@ -3460,6 +3468,7 @@ packages:
cpu: [ia32] cpu: [ia32]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-linux-64/0.15.18: /esbuild-linux-64/0.15.18:
@@ -3468,6 +3477,7 @@ packages:
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-linux-arm/0.15.18: /esbuild-linux-arm/0.15.18:
@@ -3476,6 +3486,7 @@ packages:
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-linux-arm64/0.15.18: /esbuild-linux-arm64/0.15.18:
@@ -3484,6 +3495,7 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-linux-mips64le/0.15.18: /esbuild-linux-mips64le/0.15.18:
@@ -3492,6 +3504,7 @@ packages:
cpu: [mips64el] cpu: [mips64el]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-linux-ppc64le/0.15.18: /esbuild-linux-ppc64le/0.15.18:
@@ -3500,6 +3513,7 @@ packages:
cpu: [ppc64] cpu: [ppc64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-linux-riscv64/0.15.18: /esbuild-linux-riscv64/0.15.18:
@@ -3508,6 +3522,7 @@ packages:
cpu: [riscv64] cpu: [riscv64]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-linux-s390x/0.15.18: /esbuild-linux-s390x/0.15.18:
@@ -3516,6 +3531,7 @@ packages:
cpu: [s390x] cpu: [s390x]
os: [linux] os: [linux]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-netbsd-64/0.15.18: /esbuild-netbsd-64/0.15.18:
@@ -3524,6 +3540,7 @@ packages:
cpu: [x64] cpu: [x64]
os: [netbsd] os: [netbsd]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-openbsd-64/0.15.18: /esbuild-openbsd-64/0.15.18:
@@ -3532,6 +3549,7 @@ packages:
cpu: [x64] cpu: [x64]
os: [openbsd] os: [openbsd]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-sunos-64/0.15.18: /esbuild-sunos-64/0.15.18:
@@ -3540,6 +3558,7 @@ packages:
cpu: [x64] cpu: [x64]
os: [sunos] os: [sunos]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-windows-32/0.15.18: /esbuild-windows-32/0.15.18:
@@ -3548,6 +3567,7 @@ packages:
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-windows-64/0.15.18: /esbuild-windows-64/0.15.18:
@@ -3556,6 +3576,7 @@ packages:
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild-windows-arm64/0.15.18: /esbuild-windows-arm64/0.15.18:
@@ -3564,6 +3585,7 @@ packages:
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
requiresBuild: true requiresBuild: true
dev: false
optional: true optional: true
/esbuild/0.15.18: /esbuild/0.15.18:
@@ -3594,6 +3616,7 @@ packages:
esbuild-windows-32: 0.15.18 esbuild-windows-32: 0.15.18
esbuild-windows-64: 0.15.18 esbuild-windows-64: 0.15.18
esbuild-windows-arm64: 0.15.18 esbuild-windows-arm64: 0.15.18
dev: false
/escalade/3.1.1: /escalade/3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
@@ -4840,6 +4863,7 @@ packages:
resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==} resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true hasBin: true
dev: false
/natural-compare-lite/1.4.0: /natural-compare-lite/1.4.0:
resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
@@ -5068,6 +5092,7 @@ packages:
nanoid: 3.3.4 nanoid: 3.3.4
picocolors: 1.0.0 picocolors: 1.0.0
source-map-js: 1.0.2 source-map-js: 1.0.2
dev: false
/prelude-ls/1.2.1: /prelude-ls/1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
@@ -5538,6 +5563,7 @@ packages:
/source-map-js/1.0.2: /source-map-js/1.0.2:
resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: false
/source-map-support/0.5.21: /source-map-support/0.5.21:
resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
@@ -5974,6 +6000,7 @@ packages:
rollup: 2.79.1 rollup: 2.79.1
optionalDependencies: optionalDependencies:
fsevents: 2.3.2 fsevents: 2.3.2
dev: false
/webidl-conversions/4.0.2: /webidl-conversions/4.0.2:
resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}

View File

@@ -0,0 +1,18 @@
<svg height="20" viewBox="0 0 28 20" width="28" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs><rect id="a" height="20" rx="3" width="28"/>
<mask id="b" fill="#fff">
<use fill="#fff" fill-rule="evenodd" xlink:href="#a"/>
</mask>
</defs>
<g fill="none" fill-rule="evenodd">
<use fill="#0a17a7" xlink:href="#a"/>
<path d="m29.2824692-1.91644623 1.4911811 2.21076686-9.4483006 6.37223314 6.6746503.0001129v6.66666663l-6.6746503-.0007795 9.4483006 6.3731256-1.4911811 2.2107668-11.9501195-8.0608924.0009836 7.4777795h-6.6666666l-.000317-7.4777795-11.9488189 8.0608924-1.49118107-2.2107668 9.448-6.3731256-6.67434973.0007795v-6.66666663l6.67434973-.0001129-9.448-6.37223314 1.49118107-2.21076686 11.9488189 8.06.000317-7.4768871h6.6666666l-.0009836 7.4768871z" fill="#fff" mask="url(#b)"/>
<g stroke="#db1f35" stroke-linecap="round" stroke-width=".667">
<path d="m18.668 6.332 12.665-8.332" mask="url(#b)"/>
<path d="m20.013 21.35 11.354-7.652" mask="url(#b)" transform="matrix(1 0 0 -1 0 35.048)"/>
<path d="m8.006 6.31-11.843-7.981" mask="url(#b)"/>
<path d="m9.29 22.31-13.127-8.705" mask="url(#b)" transform="matrix(1 0 0 -1 0 35.915)"/>
</g>
<path d="m0 12h12v8h4v-8h12v-4h-12v-8h-4v8h-12z" fill="#e6273e" mask="url(#b)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,9 @@
<svg height="20" viewBox="0 0 28 20" width="28" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<rect id="a" height="10" rx="0" width="28"/>
</defs>
<g fill="none" fill-rule="evenodd">
<use fill="#D82028" xlink:href="#a"/>
<use fill="#FFF" xlink:href="#a" y="10"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 334 B

View File

@@ -0,0 +1,15 @@
{
"greeting": "Hello",
"buttonText": "Click Me",
"infoLogin": "Enter the registered account",
"txtLogin1" : "Sign in to Hospital Portal",
"txtLogin2" : "Enter your details below",
"txtCardSearchMember1" : "Guarantee Submission",
"txtCardSearchMember2" : "Find Member",
"txtCardSearchMember3" : "Date Birth",
"txtCardSearchMember4" : "Member ID",
"txtCardSearchMember5" : "Member",
"txtDialogMember1" : "Benefit Summary",
"txtDialogMember2" : "Request LOG",
"txtDialogMember3" : "Member Detail"
}

View File

@@ -0,0 +1,15 @@
{
"greeting": "Halo",
"buttonText": "Klik Saya",
"infoLogin": "Masukan akun yang telah terdaftar",
"txtLogin1" : "Masuk ke Hospital Portal",
"txtLogin2" : "Masukkan detail Anda di bawah ini",
"txtCardSearchMember1" : "Pengajuan Jaminan",
"txtCardSearchMember2" : "Cari Anggota",
"txtCardSearchMember3" : "Tanggal Lahir",
"txtCardSearchMember4" : "Member ID",
"txtCardSearchMember5" : "Member",
"txtDialogMember1" : "Ringkasan Manfaat",
"txtDialogMember2" : "Request LOG",
"txtDialogMember3" : "Detail Member"
}

View File

@@ -0,0 +1,7 @@
const getLocalizedData = async (locale) => {
const response = await fetch(`../public/lang/${locale}.json`); // Mengambil file lokal berdasarkan bahasa yang dipilih
const data = await response.json();
return data;
};
export default getLocalizedData;

View File

@@ -0,0 +1,38 @@
import React, { createContext, useState, useEffect, useRef } from 'react';
import getLocalizedData from '../LocalizationUtil';
export const LanguageContext = createContext();
export const LanguageProvider = ({ children }) => {
const [currentLocale, setCurrentLocale] = useState(localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID');
const [localeData, setLocaleData] = useState('id');
const cancelToken = useRef(null);
useEffect(() => {
const fetchData = async () => {
const token = { cancelled: false };
cancelToken.current = token;
try {
const data = await getLocalizedData(currentLocale);
if (!cancelToken.current.cancelled) {
setLocaleData(data);
}
} catch (error) {
console.error('Error fetching localized data:', error);
// Tangani kesalahan jika diperlukan
}
};
fetchData();
return () => {
cancelToken.current.cancelled = true;
};
}, [currentLocale]);
return (
<LanguageContext.Provider value={{ currentLocale, setCurrentLocale, localeData }}>
{children}
</LanguageContext.Provider>
);
};

View File

@@ -122,10 +122,18 @@ function AuthProvider({ children }: AuthProviderProps) {
initialize(); initialize();
}, []); }, []);
const headers = {
headers: {
'Accept': 'application/json',
'Content-Type' : 'application/json',
'Accept-Language': (localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID'),
},
};
const login = async (email: string, password: string) => axios const login = async (email: string, password: string) => axios
.post('/login', { email, password }) .post('/login', { email, password }, headers)
.then((response) => { .then((response) => {
const { user, token } = response.data; const { user, token } = response.data.data;
setSession(token); setSession(token);
dispatch({ dispatch({

View File

@@ -12,6 +12,7 @@ import { HelmetProvider } from 'react-helmet-async';
// contexts // contexts
import { SettingsProvider } from './contexts/SettingsContext'; import { SettingsProvider } from './contexts/SettingsContext';
import { CollapseDrawerProvider } from './contexts/CollapseDrawerContext'; import { CollapseDrawerProvider } from './contexts/CollapseDrawerContext';
import { LanguageProvider } from './contexts/LanguageContext';
// //
import App from './App'; import App from './App';
@@ -23,7 +24,9 @@ ReactDOM.render(
<SettingsProvider> <SettingsProvider>
<CollapseDrawerProvider> <CollapseDrawerProvider>
<BrowserRouter> <BrowserRouter>
<App /> <LanguageProvider>
<App />
</LanguageProvider>
</BrowserRouter> </BrowserRouter>
</CollapseDrawerProvider> </CollapseDrawerProvider>
</SettingsProvider> </SettingsProvider>

View File

@@ -1,28 +1,24 @@
import { useState } from 'react'; import React, { useState, useContext } from 'react';
// @mui // @mui
import { MenuItem, Stack } from '@mui/material'; import { ButtonBase, Box, Typography, MenuItem, Stack } from '@mui/material';
// components // components
import Image from '@/components/Image'; import Image from '@/components/Image';
import MenuPopover from '@/components/MenuPopover'; import MenuPopover from '@/components/MenuPopover';
import { IconButtonAnimate } from '@/components/animate'; import { IconButtonAnimate } from '@/components/animate';
import { LanguageContext } from '@/contexts/LanguageContext';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
const LANGS = [ const LANGS = [
{
label: 'Bahasa Indonesia',
value: 'id-ID',
icon: '/icons/ic_flag_id.svg',
},
{ {
label: 'English', label: 'English',
value: 'en', value: 'en-US',
icon: 'https://minimal-assets-api.vercel.app/assets/icons/ic_flag_en.svg', icon: '/icons/ic_flag_en.svg',
},
{
label: 'German',
value: 'de',
icon: 'https://minimal-assets-api.vercel.app/assets/icons/ic_flag_de.svg',
},
{
label: 'French',
value: 'fr',
icon: 'https://minimal-assets-api.vercel.app/assets/icons/ic_flag_fr.svg',
}, },
]; ];
@@ -39,18 +35,34 @@ export default function LanguagePopover() {
setOpen(null); setOpen(null);
}; };
const { setCurrentLocale } = useContext(LanguageContext);
const handleChangeLanguage = (language) => {
localStorage.setItem('currentLocale', language);
setCurrentLocale(language);
};
return ( return (
<> <>
<IconButtonAnimate <Box display="flex" alignItems="center" border={0} borderColor="grey.300" borderRadius={3} p={1}>
onClick={handleOpen}
sx={{ <IconButtonAnimate
width: 40, onClick={handleOpen}
height: 40, sx={{
...(open && { bgcolor: 'action.selected' }), width: 35,
}} height: 35,
> ...(open && { bgcolor: 'action.selected' }),
<Image disabledEffect src={LANGS[0].icon} alt={LANGS[0].label} /> }}
</IconButtonAnimate> >
<Image disabledEffect src={!localStorage.getItem('currentLocale') ? LANGS[0].icon : (localStorage.getItem('currentLocale') === 'id-ID' ? LANGS[0].icon : LANGS[1].icon)} alt={!localStorage.getItem('currentLocale') ? LANGS[0].label : (localStorage.getItem('currentLocale') === 'id-ID' ? LANGS[0].label : LANGS[1].label)} />
</IconButtonAnimate>
<ButtonBase onClick={handleOpen}>
<Typography variant="body2" component="span" marginRight={1} color="textPrimary">
Language
</Typography>
</ButtonBase>
</Box>
<MenuPopover <MenuPopover
open={Boolean(open)} open={Boolean(open)}
@@ -67,17 +79,22 @@ export default function LanguagePopover() {
{LANGS.map((option) => ( {LANGS.map((option) => (
<MenuItem <MenuItem
key={option.value} key={option.value}
selected={option.value === LANGS[0].value} selected={option.value === localStorage.getItem('currentLocale')}
onClick={handleClose} onClick = {() => {
handleClose();
handleChangeLanguage(option.value);
}}
> >
<Image <Image
disabledEffect disabledEffect
alt={option.label} alt={option.label}
src={option.icon} src={option.icon}
sx={{ width: 28, mr: 2 }} sx={{ width: 28 }}
/> />
<Typography variant="body2" component="span" marginLeft={1} color="textPrimary">
{option.label} {option.label}
</Typography>
</MenuItem> </MenuItem>
))} ))}
</Stack> </Stack>

View File

@@ -93,8 +93,8 @@ export default function DashboardHeader({
<Stack direction="row" alignItems="center" spacing={{ xs: 0.5, sm: 1.5 }}> <Stack direction="row" alignItems="center" spacing={{ xs: 0.5, sm: 1.5 }}>
<LanguagePopover /> <LanguagePopover />
<NotificationsPopover /> {/*<NotificationsPopover />
<ContactsPopover /> <ContactsPopover />*/}
<AccountPopover /> <AccountPopover />
</Stack> </Stack>
</Toolbar> </Toolbar>

View File

@@ -20,12 +20,12 @@ import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
// const [notifications, setNotifications] = useState([]) // const [notifications, setNotifications] = useState([])
// const itemList = [ const itemList = [
// // { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '08:00 WIB' }, { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '08:00 WIB' },
// // { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '09:00 WIB' }, { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '09:00 WIB' },
// // { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '10:00 WIB' }, { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '10:00 WIB' },
// // { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '11:00 WIB' }, { info: 'Mohon lengkapi dokumen Mahen sadarsa', date: 'Selasa, 20 April 22', time: '11:00 WIB' },
// ]; ];
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -66,7 +66,7 @@ export default function Dashboard() {
const [policyData, setPolicyData] = useState<PolicyProps>(defaultPolicyData); const [policyData, setPolicyData] = useState<PolicyProps>(defaultPolicyData);
// TODO Remove This // TODO Remove This
const [itemList, setItemList] = useState([]); //const [itemList, setItemList] = useState([]);
function handleDataLoaded(dataTable) { function handleDataLoaded(dataTable) {
let dummyData = []; let dummyData = [];
dataTable.map(function(data) { dataTable.map(function(data) {
@@ -79,7 +79,7 @@ export default function Dashboard() {
} }
}) })
setItemList(dummyData); //setItemList(dummyData);
} }
return ( return (

View File

@@ -1,3 +1,4 @@
import React, { useContext, useRef, useState, useEffect } from 'react';
import { capitalCase } from "change-case"; import { capitalCase } from "change-case";
import { Link as RouterLink } from "react-router-dom"; import { Link as RouterLink } from "react-router-dom";
// @mui // @mui
@@ -23,6 +24,7 @@ import Logo from "@/components/Logo";
import Image from "@/components/Image"; import Image from "@/components/Image";
// sections // sections
import { LoginForm } from "@/sections/auth/login"; import { LoginForm } from "@/sections/auth/login";
import { LanguageContext } from '@/contexts/LanguageContext';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -70,6 +72,7 @@ const ContentStyle = styled("div")(({ theme }) => ({
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export default function Login() { export default function Login() {
const { localeData } = useContext(LanguageContext);
const { method } = useAuth(); const { method } = useAuth();
const smUp = useResponsive("up", "sm"); const smUp = useResponsive("up", "sm");
@@ -80,7 +83,7 @@ export default function Login() {
<Page title="Login"> <Page title="Login">
<RootStyle> <RootStyle>
<HeaderStyle> <HeaderStyle>
<Logo sx={{ width: 150, height: 150 }} /> {/*<Logo sx={{ width: 150, height: 150 }} />
{smUp && ( {smUp && (
<Typography variant="body2" sx={{ mt: { md: -2 } }}> <Typography variant="body2" sx={{ mt: { md: -2 } }}>
Has problem with your account? {""} Has problem with your account? {""}
@@ -97,7 +100,7 @@ export default function Login() {
Contact Us Contact Us
</Link> </Link>
</Typography> </Typography>
)} )}*/}
</HeaderStyle> </HeaderStyle>
{/* {mdUp && ( {/* {mdUp && (
@@ -121,16 +124,17 @@ export default function Login() {
alignItems="center" alignItems="center"
sx={{ mb: 5 }} sx={{ mb: 5 }}
> >
<Logo sx={{ width: 90, height: 90 }} />
<Box sx={{ flexGrow: 1 }}> <Box sx={{ flexGrow: 1 }}>
<Typography variant="h4" gutterBottom> <Typography variant="h4" gutterBottom>
Sign in to LinkSehat {localeData.txtLogin1}
</Typography> </Typography>
<Typography sx={{ color: "text.secondary" }}> <Typography variant="body1" sx={{ color: 'text.secondary' }}>
Enter your details below. {localeData.txtLogin2}
</Typography> </Typography>
</Box> </Box>
<Tooltip {/*<Tooltip
title={capitalCase(method)} title={capitalCase(method)}
placement="right" placement="right"
> >
@@ -141,12 +145,12 @@ export default function Login() {
sx={{ width: 32, height: 32 }} sx={{ width: 32, height: 32 }}
/> />
</> </>
</Tooltip> </Tooltip>*/}
</Stack> </Stack>
<LoginForm /> <LoginForm />
{false && !smUp && ( {/*{false && !smUp && (
<Typography <Typography
variant="body2" variant="body2"
align="center" align="center"
@@ -161,7 +165,7 @@ export default function Login() {
Get started Get started
</Link> </Link>
</Typography> </Typography>
)} )}*/}
</ContentStyle> </ContentStyle>
</Container> </Container>
</RootStyle> </RootStyle>

View File

@@ -1,5 +1,5 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { useState } from 'react'; import React, { useContext, useRef, useState, useEffect } from 'react';
import { Link as RouterLink, useNavigate } from 'react-router-dom'; import { Link as RouterLink, useNavigate } from 'react-router-dom';
// form // form
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
@@ -15,6 +15,7 @@ import useIsMountedRef from '@/hooks/useIsMountedRef';
// components // components
import Iconify from '@/components/Iconify'; import Iconify from '@/components/Iconify';
import { FormProvider, RHFTextField, RHFCheckbox } from '@/components/hook-form'; import { FormProvider, RHFTextField, RHFCheckbox } from '@/components/hook-form';
import { LanguageContext } from '@/contexts/LanguageContext';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -26,6 +27,7 @@ type FormValuesProps = {
}; };
export default function LoginForm() { export default function LoginForm() {
const { localeData } = useContext(LanguageContext);
const { login } = useAuth(); const { login } = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -34,8 +36,8 @@ export default function LoginForm() {
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const LoginSchema = Yup.object().shape({ const LoginSchema = Yup.object().shape({
email: Yup.string().email('Email must be a valid email address').required('Email is required'), email: Yup.string().email('Format email tidak valid').required('Email harus diisi'),
password: Yup.string().required('Password is required'), password: Yup.string().required('Password harus diisi'),
}); });
const defaultValues = { const defaultValues = {
@@ -75,10 +77,10 @@ export default function LoginForm() {
return ( return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}> <FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={3}> <Stack spacing={3}>
<Alert severity="info">Email : admin@linksehat.dev & Password : password</Alert> <Alert severity="info">{localeData.infoLogin}</Alert>
{!!errors.afterSubmit && <Alert severity="error">{errors.afterSubmit.message}</Alert>} {!!errors.afterSubmit && <Alert severity="error">{errors.afterSubmit.data.meta.message}</Alert>}
<RHFTextField name="email" label="Email address" /> <RHFTextField name="email" label="Email" required/>
<RHFTextField <RHFTextField
name="password" name="password"
@@ -93,14 +95,15 @@ export default function LoginForm() {
</InputAdornment> </InputAdornment>
), ),
}} }}
required
/> />
</Stack> </Stack>
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ my: 2 }}> <Stack direction="row" alignItems="center" justifyContent="space-between" sx={{ my: 2 }}>
<RHFCheckbox name="remember" label="Remember me" /> {/*<RHFCheckbox name="remember" label="Remember me" />
<Link component={RouterLink} variant="subtitle2" to={PATH_AUTH.resetPassword}> <Link component={RouterLink} variant="subtitle2" to={PATH_AUTH.resetPassword}>
Forgot password? Forgot password?
</Link> </Link>*/}
</Stack> </Stack>
<LoadingButton <LoadingButton
@@ -109,6 +112,7 @@ export default function LoginForm() {
type="submit" type="submit"
variant="contained" variant="contained"
loading={isSubmitting} loading={isSubmitting}
sx={{ marginTop: 2 }}
> >
Login Login
</LoadingButton> </LoadingButton>

View File

@@ -13,7 +13,7 @@ import {
} from '@mui/material'; } from '@mui/material';
import { ChevronRight } from '@mui/icons-material'; import { ChevronRight } from '@mui/icons-material';
// React // React
import React, { useState } from 'react'; import React, { useContext, useState } from 'react';
import { LoadingButton } from '@mui/lab'; import { LoadingButton } from '@mui/lab';
import Iconify from '@/components/Iconify'; import Iconify from '@/components/Iconify';
import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers'; import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers';
@@ -25,6 +25,7 @@ import MuiDialog from '@/components/MuiDialog';
import axios from '@/utils/axios'; import axios from '@/utils/axios';
import { useSnackbar } from 'notistack'; import { useSnackbar } from 'notistack';
import DialogMember from './DialogMember'; import DialogMember from './DialogMember';
import { LanguageContext } from '@/contexts/LanguageContext';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@@ -46,29 +47,32 @@ const ItemNotificationStyle = styled(Card)(({ theme }) => ({
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export default function CardSearchMember(handleSubmitSuccess) { export default function CardSearchMember(handleSubmitSuccess) {
const { localeData } = useContext(LanguageContext);
const {enqueueSnackbar} = useSnackbar(); const {enqueueSnackbar} = useSnackbar();
const [noPolis, setNoPolis] = useState('AW001-01'); const [noPolis, setNoPolis] = useState('AW001-01');
const [tanggalLahir, setTanggalLahir] = useState('1991-01-10'); const [birthDate, setBirthDate] = useState('1991-01-10');
const [loadingBenefit, setLoadingBenefit] = useState(false); const [loadingBenefit, setLoadingBenefit] = useState(false);
const [loadingClaim, setLoadingClaim] = useState(false); const [loadingClaim, setLoadingClaim] = useState(false);
const [openDialogBenefit, setOpenDialogBenefit] = useState(false); const [openDialogBenefit, setOpenDialogBenefit] = useState(false);
const [openDialogClaim, setOpenDialogClaim] = useState(false); const [openDialogClaim, setOpenDialogClaim] = useState(false);
const [currentMember, setCurrentMember] = useState(null); const [currentMember, setCurrentMember] = useState(null);
const [nameMember, setNameMember] = useState('');
function handleSearchMember() { function handleSearchMember() {
setLoadingBenefit(true) setLoadingBenefit(true)
axios.post('/search-member', { axios.post('/search-member', {
no_polis: noPolis, no_polis: noPolis,
birth_date: tanggalLahir ? fPostFormat(tanggalLahir, 'yyyy-MM-dd') : null birth_date: birthDate ? fPostFormat(birthDate, 'yyyy-MM-dd') : null
}) })
.then((response) => { .then((response) => {
setOpenDialogBenefit(true) setOpenDialogBenefit(true)
setCurrentMember(response.data.data) setCurrentMember(response.data.data)
setNameMember(response.data.data.name);
}) })
.catch(({response}) => { .catch(({response}) => {
enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.message : 'Opps, Something went Wrong!'), {variant : "error"}) enqueueSnackbar(response.data.errors ? response.data.errors[0] : (response.data ? response.data.meta.message : 'Opps, Something went Wrong!'), {variant : "error"})
}) })
.then(() => { .then(() => {
setLoadingBenefit(false) setLoadingBenefit(false)
@@ -90,23 +94,23 @@ export default function CardSearchMember(handleSubmitSuccess) {
component="span" component="span"
sx={{ display: 'flex', alignItems: 'center' }} sx={{ display: 'flex', alignItems: 'center' }}
> >
Pengajuan Jaminan {localeData.txtCardSearchMember1}
</Typography> </Typography>
</Typography> </Typography>
</Stack> </Stack>
<Stack gap={2}> <Stack gap={2}>
<TextField variant="outlined" label="Member ID" value={noPolis} onChange={(event) => { <TextField variant="outlined" label={localeData.txtCardSearchMember4} value={noPolis} onChange={(event) => {
setNoPolis(event.target.value) setNoPolis(event.target.value)
}}></TextField> }} required></TextField>
<LocalizationProvider dateAdapter={AdapterDateFns}> <LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker <DatePicker
label="Tanggal Lahir" label={localeData.txtCardSearchMember3}
value={tanggalLahir} value={birthDate}
onChange={(newValue) => { onChange={(newValue) => {
setTanggalLahir( (newValue)); setTanggalLahir( (newValue));
}} }}
inputFormat="dd-MM-yyyy" inputFormat="dd-MM-yyyy"
renderInput={(params) => <TextField {...params} />} renderInput={(params) => <TextField {...params} required/>}
/> />
</LocalizationProvider> </LocalizationProvider>
@@ -124,7 +128,7 @@ export default function CardSearchMember(handleSubmitSuccess) {
}} }}
> >
<Iconify icon="eva:eye-fill" marginRight={0.75} /> <Iconify icon="eva:eye-fill" marginRight={0.75} />
Cari Member {localeData.txtCardSearchMember2}
</LoadingButton> </LoadingButton>
{/* <LoadingButton {/* <LoadingButton
variant="contained" variant="contained"
@@ -142,7 +146,7 @@ export default function CardSearchMember(handleSubmitSuccess) {
{/* {/*
<DialogBenefit open={openDialogBenefit} setOpen={setOpenDialogBenefit}></DialogBenefit> */} <DialogBenefit open={openDialogBenefit} setOpen={setOpenDialogBenefit}></DialogBenefit> */}
<MuiDialog <MuiDialog
title={{name: "Member"}} title={{name: nameMember}}
openDialog={openDialogBenefit} openDialog={openDialogBenefit}
setOpenDialog={setOpenDialogBenefit} setOpenDialog={setOpenDialogBenefit}
content={DialogMember(currentMember, () => {setOpenDialogBenefit(false); handleSubmitSuccess()})} content={DialogMember(currentMember, () => {setOpenDialogBenefit(false); handleSubmitSuccess()})}

View File

@@ -4,20 +4,22 @@ import { LoadingButton, TabPanel } from "@mui/lab";
import { Button, Card, Divider, Grid, LinearProgress, linearProgressClasses, Typography } from "@mui/material"; import { Button, Card, Divider, Grid, LinearProgress, linearProgressClasses, Typography } from "@mui/material";
import { Tab, Tabs } from "@mui/material"; import { Tab, Tabs } from "@mui/material";
import { Box, Stack } from "@mui/material"; import { Box, Stack } from "@mui/material";
import { useEffect, useState } from "react"; import React, { useEffect, useState, useContext } from "react";
import { fCurrency } from '@/utils/formatNumber'; import { fCurrency } from '@/utils/formatNumber';
import { fPostFormat } from '@/utils/formatTime'; import { fPostFormat } from '@/utils/formatTime';
import { Avatar } from '@mui/material'; import { Avatar } from '@mui/material';
import Iconify from '@/components/Iconify'; import Iconify from '@/components/Iconify';
import FormRequestClaim from './FormRequestClaim'; import FormRequestClaim from './FormRequestClaim';
import { LanguageContext } from '@/contexts/LanguageContext';
export default function DialogMember(member, handleSubmitSuccess) { export default function DialogMember(member, handleSubmitSuccess) {
const { localeData } = useContext(LanguageContext);
const [currentTab, setCurrentTab] = useState('request') const [currentTab, setCurrentTab] = useState('request')
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
useEffect(() => { useEffect(() => {
setCurrentTab('benefit') setCurrentTab('detail')
}, [member]) }, [member])
function handleChangeTab(event: React.SyntheticEvent, newValue: string) { function handleChangeTab(event: React.SyntheticEvent, newValue: string) {
@@ -67,13 +69,93 @@ export default function DialogMember(member, handleSubmitSuccess) {
onChange={handleChangeTab} onChange={handleChangeTab}
aria-label="wrapped label tabs example" aria-label="wrapped label tabs example"
> >
<Tab <Tab value="detail" label={localeData.txtDialogMember3} />
value="benefit" <Tab value="benefit" label={localeData.txtDialogMember1} />
label="Benefit Summary" <Tab value="request" label={localeData.txtDialogMember2} />
/>
<Tab value="request" label="Request Penjaminan" />
</Tabs> </Tabs>
<TabPanel value={currentTab} index={'detail'}>
<Stack spacing={2}>
<Grid container sx={{ pb: 2, mb: 2, borderBottom: 0 }}>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Mapping ID
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Policy Number
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
NRIC
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
NIK
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Email
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
</Grid>
</Grid>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Birth Date
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Gender
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Marital Status
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Language
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Race
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
<Grid item xs={6}>
Relationship
</Grid>
<Grid item xs={6}>
: {1 ?? '-'}
</Grid>
</Grid>
</Grid>
</Grid>
</Stack>
</TabPanel>
<TabPanel value={currentTab} index={'benefit'}> <TabPanel value={currentTab} index={'benefit'}>
<Grid container spacing={2}> <Grid container spacing={2}>
{ member && member?.current_plan?.corporate_benefits?.map((corporateBenefit, index) => {return ( { member && member?.current_plan?.corporate_benefits?.map((corporateBenefit, index) => {return (

View File

@@ -29,12 +29,18 @@ const setSession = (accessToken: string | null) => {
if (accessToken) { if (accessToken) {
localStorage.setItem('accessToken', accessToken); localStorage.setItem('accessToken', accessToken);
axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`; axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`;
axios.defaults.headers.common['Accept-Language'] = (localStorage.getItem('currentLocale') ? localStorage.getItem('currentLocale') : 'id-ID');
axios.defaults.headers.common['Accept'] = 'application/json';
axios.defaults.headers.common['Content-Type'] = 'application/json';
// This function below will handle when token is expired // This function below will handle when token is expired
// const { exp } = jwtDecode(accessToken); // const { exp } = jwtDecode(accessToken);
// handleTokenExpired(exp); // handleTokenExpired(exp);
} else { } else {
localStorage.removeItem('accessToken'); localStorage.removeItem('accessToken');
delete axios.defaults.headers.common.Authorization; 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'];
} }
}; };

4723
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
<?php
return [
'success' => 'Request has been successfully processed.',
'server_error' => 'Internal server error.',
'not_found' => 'Data not found',
'password' => 'Password wrong. Please try again.'
];

View File

@@ -0,0 +1,8 @@
<?php
return [
'required' => 'The :attribute field is required.',
'invalid' => 'The :attribute field is invalid.',
'max' => ':attribute cannot exceed :length characters.',
'email' => 'Invalid email format.'
];

View File

@@ -0,0 +1,8 @@
<?php
return [
'success' => 'Request berhasil dilakukan.',
'server_error' => 'Internal server error.',
'not_found' => 'Data tidak ditemukan.',
'password' => 'Password salah. Silakan coba lagi.'
];

View File

@@ -0,0 +1,8 @@
<?php
return [
'required' => 'Kolom :attribute harus diisi.',
'invalid' => 'Kolom :attribute tidak valid.',
'max' => ':attribute tidak boleh melebihi :length karakter.',
'email' => 'Format email salah.'
];

View File

@@ -31,8 +31,9 @@ Route::prefix('client')->group(function () {
Route::middleware('auth:sanctum')->group(function () { Route::middleware('auth:sanctum')->group(function () {
Route::post('logout', [AuthController::class, 'logout'])->name('logout'); Route::post('logout', [AuthController::class, 'logout'])->name('logout');
Route::get('user', [UserController::class, 'index']); Route::get('user', [UserController::class, 'index']);
Route::get('data/{id}', [DataController::class, 'show']); // Route::get('data/{id}', [DataController::class, 'show']);
Route::put('data/{id}', [DataController::class, 'update' ]); // Route::put('data/{id}', [DataController::class, 'update']);
Route::get('corporate-manage', [CorporateManageController::class, 'index']); Route::get('corporate-manage', [CorporateManageController::class, 'index']);
Route::prefix('{corporate_id}')->group(function () { Route::prefix('{corporate_id}')->group(function () {