diff --git a/Modules/Client/Http/Controllers/Api/AuthController.php b/Modules/Client/Http/Controllers/Api/AuthController.php index 977302d6..ee7f3445 100755 --- a/Modules/Client/Http/Controllers/Api/AuthController.php +++ b/Modules/Client/Http/Controllers/Api/AuthController.php @@ -81,7 +81,7 @@ class AuthController extends Controller ); } - return Helper::responseJson(message: 'OTP yang anda masukan salah!'); + return Helper::responseJson(status: 'error', message: 'OTP yang anda masukan salah!'); } public function logout(Request $request) diff --git a/Modules/Client/Http/Controllers/Api/CorporateManageController.php b/Modules/Client/Http/Controllers/Api/CorporateManageController.php new file mode 100755 index 00000000..efd87ce6 --- /dev/null +++ b/Modules/Client/Http/Controllers/Api/CorporateManageController.php @@ -0,0 +1,73 @@ +managedCorporates()->select(['corporates.id', 'corporates.name'])->get(); + + return response()->json($corporate); + } + + /** + * Store a newly created resource in storage. + * @param Request $request + * @return Renderable + */ + public function store(Request $request) + { + } + + /** + * Show the specified resource. + * @param int $id + * @return Renderable + */ + public function show($corporate_id) + { + // + } + + /** + * Show the form for editing the specified resource. + * @param int $id + * @return Renderable + */ + public function edit($id) + { + return view('client::edit'); + } + + /** + * Update the specified resource in storage. + * @param Request $request + * @param int $id + * @return Renderable + */ + public function update(Request $request, $id) + { + // + } + + /** + * Remove the specified resource from storage. + * @param int $id + * @return Renderable + */ + public function destroy($id) + { + // + } +} diff --git a/Modules/Client/Http/Controllers/Api/DashboardController.php b/Modules/Client/Http/Controllers/Api/CorporatePolicyController.php similarity index 74% rename from Modules/Client/Http/Controllers/Api/DashboardController.php rename to Modules/Client/Http/Controllers/Api/CorporatePolicyController.php index 5067b1cd..45eba51d 100755 --- a/Modules/Client/Http/Controllers/Api/DashboardController.php +++ b/Modules/Client/Http/Controllers/Api/CorporatePolicyController.php @@ -5,25 +5,25 @@ namespace Modules\Client\Http\Controllers\Api; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Auth; +use Modules\Client\Transformers\DashboardResources; -class DashboardController extends Controller +class CorporatePolicyController extends Controller { /** * Display a listing of the resource. * @return Renderable */ - public function index() + public function index(Request $request, $corporate_id) { - $user = auth()->user(); + $user = Auth::user(); + $currentCorporate = $user->managedCorporates() + ->with(['currentPolicy', 'employees']) + ->find($corporate_id); - $corporate = $user->managedCorporates() - ->withCount('employees') - ->with(['policies' => function ($policy) { - $policy->limit(1)->latest(); - }]) - ->first(); + $data = DashboardResources::make($currentCorporate); - return response()->json(compact('corporate')); + return response()->json($data); } /** diff --git a/Modules/Client/Http/Controllers/Api/DivisionController.php b/Modules/Client/Http/Controllers/Api/DivisionController.php new file mode 100755 index 00000000..0ddfde4b --- /dev/null +++ b/Modules/Client/Http/Controllers/Api/DivisionController.php @@ -0,0 +1,116 @@ +where('corporate_id', $corporate_id) + ->get(['id', 'name']); + + return response()->json($division); + } + + /** + * Show the form for creating a new resource. + * @return Renderable + */ + public function create() + { + return view('internal::create'); + } + + /** + * Store a newly created resource in storage. + * @param Request $request + * @return Renderable + */ + public function store(Request $request, $corporate_id) + { + $request->validate([ + 'code' => [ + 'required', + ], + 'name' => 'required' + ]); + + $newCorporatePlan = CorporateDivision::create([ + 'corporate_id' => $corporate_id, + 'code' => $request->code, + 'name' => $request->name, + ]); + + return $newCorporatePlan; + } + + /** + * Show the specified resource. + * @param int $id + * @return Renderable + */ + public function show($id) + { + return view('internal::show'); + } + + /** + * Show the form for editing the specified resource. + * @param int $id + * @return Renderable + */ + public function edit($corporate_id, $id) + { + $corporatePlan = CorporateDivision::findOrFail($id); + + return $corporatePlan; + } + + /** + * Update the specified resource in storage. + * @param Request $request + * @param int $id + * @return Renderable + */ + public function update(Request $request, $corporate_id, $id) + { + $corporatePlan = CorporateDivision::findOrFail($id); + $request->validate([ + 'code' => [ + 'required', + Rule::unique('corporate_plans')->where('corporate_id', $corporate_id)->ignore($corporatePlan->id) + ], + 'name' => 'required' + ]); + + $corporatePlan->fill([ + 'code' => $request->code, + 'name' => $request->name, + 'active' => $request->active, + ])->save(); + + return $corporatePlan; + } + + /** + * Remove the specified resource from storage. + * @param int $id + * @return Renderable + */ + public function destroy($id) + { + // + } +} diff --git a/Modules/Client/Http/Controllers/Api/MemberController.php b/Modules/Client/Http/Controllers/Api/MemberController.php index 9897450d..2283fc3f 100755 --- a/Modules/Client/Http/Controllers/Api/MemberController.php +++ b/Modules/Client/Http/Controllers/Api/MemberController.php @@ -7,6 +7,7 @@ use App\Models\Member; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Modules\Client\Transformers\MemberResources; class MemberController extends Controller { @@ -14,29 +15,25 @@ class MemberController extends Controller * Display a listing of the resource. * @return Renderable */ - public function index(Request $request) + public function index(Request $request, $corporate_id) { - $user = auth()->user(); - - $corporate = $user->managedCorporates()->first(); - // $plans = + $limit = $request->has('per_page') ? $request->per_page : 10; $members = Member::query() - ->whereHas('employeds', function($corporateEmployee) use ($corporate) { - $corporateEmployee->where('corporate_id', $corporate->id); - }); - if ($request->has('search')) { - $members - ->where('member_id', 'like', "%" . $request->search . "%") - ->orWhere('payor_id', 'like', "%" . $request->search . "%") - ->orWhere('name', 'like', "%" . $request->search . "%"); - } + ->whereHas('employeds', function ($corporateEmployee) use ($corporate_id) { + $corporateEmployee->where('corporate_id', $corporate_id); + })->when($request->input('division'), function ($division, $division_id) { + $division->whereHas('division', function ($corporateEmployee) use ($division_id) { + $corporateEmployee->where('division_id', $division_id); + }); + })->when($request->input('search'), function ($query, $search) { + $query->where('member_id', 'like', "%" . $search . "%") + ->orWhere('name', 'like', "%" . $search . "%"); + })->when($request->has('orderBy'), function ($query) use ($request) { + $query->orderBy($request->orderBy, $request->order); + })->paginate($limit); - $members = $members->paginate(); - - return response()->json([ - 'members' => Helper::paginateResources($members) - ]); + return response()->json(Helper::paginateResources(MemberResources::collection($members))); } /** diff --git a/Modules/Client/Http/Controllers/Api/UserController.php b/Modules/Client/Http/Controllers/Api/UserController.php index b7cb8812..dce0cd0a 100755 --- a/Modules/Client/Http/Controllers/Api/UserController.php +++ b/Modules/Client/Http/Controllers/Api/UserController.php @@ -5,6 +5,7 @@ namespace Modules\Client\Http\Controllers\Api; use Illuminate\Contracts\Support\Renderable; use Illuminate\Http\Request; use Illuminate\Routing\Controller; +use Illuminate\Support\Facades\Auth; class UserController extends Controller { @@ -14,7 +15,10 @@ class UserController extends Controller */ public function index() { - return response()->json(auth()->user()); + $userLogin = Auth::user(); + $corporateSelected = $userLogin->managedCorporates()->select('corporates.id')->where('active', 1)->first(); + + return response()->json(['user' => $userLogin, 'corporate' => $corporateSelected]); } /** diff --git a/Modules/Client/Routes/api.php b/Modules/Client/Routes/api.php index 2ab4c9af..533a78d6 100755 --- a/Modules/Client/Routes/api.php +++ b/Modules/Client/Routes/api.php @@ -1,7 +1,9 @@ group(function () { Route::middleware('auth:sanctum')->group(function () { Route::post('logout', [AuthController::class, 'logout'])->name('logout'); - Route::get('/user', [UserController::class, 'index']); + Route::get('user', [UserController::class, 'index']); - Route::get('dashboard', [DashboardController::class, 'index']); - Route::get('members', [MemberController::class, 'index']); + Route::get('corporate-manage', [CorporateManageController::class, 'index']); + Route::prefix('{corporate_id}')->group(function () { + Route::get('policy', [CorporatePolicyController::class, 'index']); + Route::get('division', [DivisionController::class, 'index']); + Route::get('members', [MemberController::class, 'index']); + }); }); }); diff --git a/Modules/Client/Transformers/DashboardResources.php b/Modules/Client/Transformers/DashboardResources.php new file mode 100644 index 00000000..a09b6bda --- /dev/null +++ b/Modules/Client/Transformers/DashboardResources.php @@ -0,0 +1,38 @@ +currentPolicy->limit_balance; + $myLimitTotal = (int)$this->currentPolicy->total_premi; + + $lockBalance = (int)$this->currentPolicy->minimal_stop_service_net; + $lockPercentage = (int)$this->currentPolicy->minimal_stop_service_percentage; + + return [ + 'policy' => [ + 'myLimit' => [ + 'balance' => $myLimitBalance, + 'total' => $myLimitTotal, + 'percentage' => ($myLimitBalance / $myLimitTotal) * 100, + ], + 'lockLimit' => [ + 'balance' => $lockBalance, + 'percentage' => $lockPercentage + ] + ], + ]; + } +} diff --git a/Modules/Client/Transformers/MemberResources.php b/Modules/Client/Transformers/MemberResources.php new file mode 100644 index 00000000..6a25d1fd --- /dev/null +++ b/Modules/Client/Transformers/MemberResources.php @@ -0,0 +1,29 @@ + $this->member_id, + 'fullName' => $this->full_name, + 'division' => $this->division->name, + 'limit' => [ + 'current' => 2000000, + 'total' => 4000000, + 'percentage' => (2000000 / 4000000) * 100 + ], + 'status' => $this->active + ]; + } +} diff --git a/Modules/Internal/Http/Controllers/Api/CorporateController.php b/Modules/Internal/Http/Controllers/Api/CorporateController.php index d840195a..978bef4e 100755 --- a/Modules/Internal/Http/Controllers/Api/CorporateController.php +++ b/Modules/Internal/Http/Controllers/Api/CorporateController.php @@ -26,18 +26,17 @@ class CorporateController extends Controller public function index(Request $request) { $corporates = Corporate::query() - ->when($request->search, function ($query, $search) { - return $query->where('name', 'LIKE', '%'.$search.'%') - ->orWhere('code', 'LIKE', '%'.$search.'%'); - }) - ->with('currentPolicy', 'subCorporates') - ->withCount([ - 'employees', - 'corporatePlans', - 'corporateBenefits', - ]) - ->where('type', 'corporate') - ->paginate(10); + ->when($request->search, function ($query, $search) { + return $query->where('name', 'LIKE', '%' . $search . '%') + ->orWhere('code', 'LIKE', '%' . $search . '%'); + }) + ->with('currentPolicy', 'subCorporates') + ->withCount([ + 'employees', + 'corporateBenefits', + ]) + ->where('type', 'corporate') + ->paginate(10); return $corporates; } @@ -48,10 +47,10 @@ class CorporateController extends Controller */ public function create() { - $corporateGroups = Corporate::whereNull('parent_id')->get()->map(function($corporate) { + $corporateGroups = Corporate::whereNull('parent_id')->get()->map(function ($corporate) { return [ 'value' => $corporate->id, - 'label' => $corporate->name.' ('.$corporate->code.')', + 'label' => $corporate->name . ' (' . $corporate->code . ')', ]; }); @@ -80,7 +79,7 @@ class CorporateController extends Controller 'policy_stop_service_percentage' => 'required_with:policy_code', 'policy_stop_service_net' => 'required_with:policy_code', ]); - + try { DB::beginTransaction(); $corporate_data = $request->all(); @@ -162,7 +161,7 @@ class CorporateController extends Controller 'policy_stop_service_percentage' => 'required_with:policy_code', 'policy_stop_service_net' => 'required_with:policy_code', ]); - + try { DB::beginTransaction(); @@ -206,7 +205,8 @@ class CorporateController extends Controller // } - public function activation(Request $request, $corporate_id) { + public function activation(Request $request, $corporate_id) + { $request->validate([ 'active' => 'required' ]); @@ -230,16 +230,16 @@ class CorporateController extends Controller 'file' => 'required|file|mimes:xls,xlsx,csv,txt', ]); // dd($request->toArray()); - $file_name = now()->getPreciseTimestamp(3).'-'.$request->file('file')->getClientOriginalName(); + $file_name = now()->getPreciseTimestamp(3) . '-' . $request->file('file')->getClientOriginalName(); $file = $request->file('file')->storeAs('temp', $file_name); $corporate = Corporate::findOrFail($corporate_id); - + $import = new ImportService(); - $import->read(Storage::path('temp/'.$file_name)); - $import->write(Storage::disk('public')->path('temp/result-'.$file_name), 'xsls'); + $import->read(Storage::path('temp/' . $file_name)); + $import->write(Storage::disk('public')->path('temp/result-' . $file_name), 'xsls'); foreach ($import->sheetsIterator() as $sheetIndex => $sheet) { - if ( !in_array($sheet->getName(), ['Plan', 'Benefit']) ) { + if (!in_array($sheet->getName(), ['Plan', 'Benefit'])) { continue; } else { if ($sheetIndex == 1) { // Rename First Sheet to Writer @@ -250,9 +250,9 @@ class CorporateController extends Controller $nextWriterSheet->setName($sheet->getName()); } - if ( $sheet->getName() == 'Plan' ) { + if ($sheet->getName() == 'Plan') { $headers_map_to_table_fields = Plan::$doc_headers_to_field_map; - } else if ( $sheet->getName() == 'Benefit' ) { + } else if ($sheet->getName() == 'Benefit') { $headers_map_to_table_fields = Benefit::$doc_headers_to_field_map; } @@ -260,7 +260,6 @@ class CorporateController extends Controller $result_headers = array_keys($headers_map_to_table_fields); $result_headers = array_merge($result_headers, ['Ingest Code', 'Ingest Note']); $import->addArrayToRow($result_headers); - } $doc_headers_indexes = []; @@ -268,8 +267,8 @@ class CorporateController extends Controller if ($index == 1) { // First Row Must be Header foreach ($row->getCells() as $index => $cell) { $title = $cell->getValue(); - $title = preg_replace( "/\r|\n/", " ", $title ); - $title = preg_replace('/\xc2\xa0/', " ", $title ); + $title = preg_replace("/\r|\n/", " ", $title); + $title = preg_replace('/\xc2\xa0/', " ", $title); $title = rtrim($title); $title = ltrim($title); $doc_headers_indexes[$index] = $title; @@ -285,9 +284,9 @@ class CorporateController extends Controller try { // Process the Row Data $corporateService = new CorporateService(); - if ( $sheet->getName() == 'Plan' ) { + if ($sheet->getName() == 'Plan') { $corporateService->handlePlanRow($corporate, $row_data); - } else if ( $sheet->getName() == 'Benefit' ) { + } else if ($sheet->getName() == 'Benefit') { $corporateService->handleBenefitRow($corporate, $row_data); } @@ -296,7 +295,6 @@ class CorporateController extends Controller 'Ingest Code' => 200, 'Ingest Note' => 'Success', ]), $sheet->getName()); - } catch (ImportRowException $e) { // Write Data Validation Error to File $import->addArrayToRow(array_merge($row_data, [ @@ -315,7 +313,7 @@ class CorporateController extends Controller } } $import->reader->close(); - Storage::delete('temp/'.$file_name); + Storage::delete('temp/' . $file_name); $import->writer->close(); return [ @@ -323,8 +321,8 @@ class CorporateController extends Controller // 'total_failed_row' => count($failed_plan_data), // 'failed_row' => $failed_plan_data, 'result_file' => [ - 'url' => Storage::disk('public')->url('temp/result-'.$file_name), - 'name' => 'result-'.$file_name, + 'url' => Storage::disk('public')->url('temp/result-' . $file_name), + 'name' => 'result-' . $file_name, ] ]; } diff --git a/Modules/Internal/Http/Controllers/Api/CorporateMemberController.php b/Modules/Internal/Http/Controllers/Api/CorporateMemberController.php index c03e44b8..d52f029e 100755 --- a/Modules/Internal/Http/Controllers/Api/CorporateMemberController.php +++ b/Modules/Internal/Http/Controllers/Api/CorporateMemberController.php @@ -29,25 +29,25 @@ class CorporateMemberController extends Controller public function index(Request $request, $corporate_id) { $members = Member::query() - ->filter($request->all()) - // ->where('corporate_id', $corporate_id) - ->whereHas('employeds', function ($employeds) use ($corporate_id) { - $employeds->where('corporate_id', $corporate_id); - }) - ->with([ - 'employeds', - 'currentPolicy', - // 'claims', - 'claims' => function ($claim) { - return $claim->whereBetween('requested_at', [now()->startOfYear(), now()->endOfYear()]); - // return $claim->used(now()->startOfYear(), now()->endOfYear()); - } - ]) - ->with('currentPlan') - // ->with - ->paginate() - ->appends($request->all()); - + ->filter($request->all()) + // ->where('corporate_id', $corporate_id) + ->whereHas('employeds', function ($employeds) use ($corporate_id) { + $employeds->where('corporate_id', $corporate_id); + }) + ->with([ + 'employeds', + 'currentPolicy', + // 'claims', + 'claims' => function ($claim) { + return $claim->whereBetween('requested_at', [now()->startOfYear(), now()->endOfYear()]); + // return $claim->used(now()->startOfYear(), now()->endOfYear()); + } + ]) + ->with('currentPlan') + // ->with + ->paginate() + ->appends($request->all()); + return Helper::paginateResources(MemberDataTableResource::collection($members)); } @@ -116,17 +116,17 @@ class CorporateMemberController extends Controller { $request->validate([ 'file' => 'required|file|mimes:xls,xlsx,csv,txt', - ]); + ]); $corporate = Corporate::findOrFail($corporate_id)->load('currentPolicy'); - - $file_name = now()->getPreciseTimestamp(3).'-'.$request->file('file')->getClientOriginalName(); + + $file_name = now()->getPreciseTimestamp(3) . '-' . $request->file('file')->getClientOriginalName(); $file = $request->file('file')->storeAs('temp', $file_name); - $reader = ReaderEntityFactory::createReaderFromFile(Storage::path('temp/'.$file_name)); - $reader->open(Storage::path('temp/'.$file_name)); - + $reader = ReaderEntityFactory::createReaderFromFile(Storage::path('temp/' . $file_name)); + $reader->open(Storage::path('temp/' . $file_name)); + $writer = WriterEntityFactory::createXLSXWriter(); - $writer->openToFile(Storage::disk('public')->path('temp/result-'.$file_name)); + $writer->openToFile(Storage::disk('public')->path('temp/result-' . $file_name)); $headers_map_to_table_fields = $this->memberEnrollmentService->doc_headers_to_field_map; @@ -144,8 +144,8 @@ class CorporateMemberController extends Controller foreach ($row->getCells() as $index => $cell) { // Clear up the string and remove all spaces $title = $cell->getValue(); - $title = preg_replace( "/\r|\n/", " ", $title ); - $title = preg_replace('/\xc2\xa0/', " ", $title ); + $title = preg_replace("/\r|\n/", " ", $title); + $title = preg_replace('/\xc2\xa0/', " ", $title); $title = rtrim($title); $title = ltrim($title); $doc_headers_indexes[$index] = $title; @@ -162,7 +162,7 @@ class CorporateMemberController extends Controller try { // dd($new_member_data); $rowResponse = $this->memberEnrollmentService->handleImportRow($corporate, $new_member_data); - + // Write Success Result to File $singleRow = WriterEntityFactory::createRow($this->memberEnrollmentService->makeResultRowWithResultFormat($rowResponse)); $writer->addRow($singleRow); @@ -187,14 +187,13 @@ class CorporateMemberController extends Controller $failed_member_data[] = ['row_number' => $index, 'error' => $e->getMessage()]; } } - } - + break; //only read first sheet } $reader->close(); $writer->close(); - Storage::delete('temp/'.$file_name); + Storage::delete('temp/' . $file_name); // throw(404); return [ @@ -202,8 +201,8 @@ class CorporateMemberController extends Controller 'total_failed_row' => count($failed_member_data), 'failed_row' => $failed_member_data, 'result_file' => [ - 'url' => Storage::disk('public')->url('temp/result-'.$file_name), - 'name' => 'result-'.$file_name, + 'url' => Storage::disk('public')->url('temp/result-' . $file_name), + 'name' => 'result-' . $file_name, ] ]; } diff --git a/Modules/Internal/Http/Controllers/Api/DivisionController.php b/Modules/Internal/Http/Controllers/Api/DivisionController.php index fd8a690b..3584abff 100755 --- a/Modules/Internal/Http/Controllers/Api/DivisionController.php +++ b/Modules/Internal/Http/Controllers/Api/DivisionController.php @@ -17,11 +17,11 @@ class DivisionController extends Controller public function index(Request $request, $corporate_id) { $benefits = CorporateDivision::query() - ->filter($request->all()) - ->where('corporate_id', $corporate_id) - ->paginate(0) - ->appends($request->all()); - + ->filter($request->all()) + ->where('corporate_id', $corporate_id) + ->paginate(0) + ->appends($request->all()); + return $benefits; } @@ -44,7 +44,6 @@ class DivisionController extends Controller $request->validate([ 'code' => [ 'required', - Rule::unique('corporate_plans')->where('corporate_id', $corporate_id) ], 'name' => 'required' ]); diff --git a/Modules/Internal/Services/MemberEnrollmentService.php b/Modules/Internal/Services/MemberEnrollmentService.php index 50a7c283..b7b7e33b 100755 --- a/Modules/Internal/Services/MemberEnrollmentService.php +++ b/Modules/Internal/Services/MemberEnrollmentService.php @@ -27,7 +27,7 @@ class MemberEnrollmentService "Halodoc Member ID" => "halodoc_member_id", "Corporate ID" => "corporate_id", "NIK" => "nik", - "Division" => "division_code", + "Division" => "division_name", "Branch Code" => "branch_code", "Bank Info" => "banks_info", "Language" => "language", @@ -89,7 +89,7 @@ class MemberEnrollmentService "Internal Use" => "internal_use_6", "StartNoClaim" => "start_no_claim", "EndNoClaim" => "end_no_claim", - "Option Mode" => "option_mode", + "Option Mode" => "option_mode", "Policy Inforce" => "policy_in_force", "Renewal activation date" => "renewal_activation_date", "Renewal Activation Date" => "renewal_activation_date", @@ -106,7 +106,7 @@ class MemberEnrollmentService "halodoc_member_id" => "Halodoc Member ID", "corporate_id" => "Corporate ID", "nik" => "NIK", - "division_code" => "Division", + "division_name" => "Division", "branch_code" => "Branch Code", "banks_info" => "Bank Info", "language" => "Language", @@ -264,7 +264,7 @@ class MemberEnrollmentService if (!empty($row['principal_id'])) { throw new ImportRowException(__('enrollment.PRINCIPAL_ID_NOT_REQUIRED'), 0, null, $row); } - + if (empty($row['corporate_id'])) { throw new ImportRowException(__('enrollment.CORPORATE_ID_REQUIRED'), 0, null, $row); } @@ -306,34 +306,38 @@ class MemberEnrollmentService throw new ImportRowException(__('enrollment.INVALID_MARITAL_STATUS'), 0, null, $row); } - if (empty($row['member_effective_date']) ) { + if (empty($row['member_effective_date'])) { throw new ImportRowException(__('enrollment.MEMBER_EFFECTIVE_REQUIRED'), 0, null, $row); } // TODO EFFECTIVE DATE VALIDATION - if (empty($row['member_expiry_date']) ) { + if (empty($row['member_expiry_date'])) { throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_REQUIRED'), 0, null, $row); } // TODO EFFECTIVE DATE VALIDATION // TODO FKTP VALIDATION // TODO FKRTL VALIDATION - + if (!empty($row['marital_status']) && !in_array($row['marital_status'], ['S', 'M', 'D'])) { throw new ImportRowException(__('enrollment.INVALID_MARITAL_STATUS'), 0, null, $row); } - + if (empty($row['name'])) { throw new ImportRowException(__('enrollment.NAME_REQUIRED'), 0, null, $row); } - if (!empty($row['telephone_mobile']) - && !(substr($row['telephone_mobile'], 0, 4) == '+628' || substr($row['telephone_mobile'], 0, 3) == '628')) { + if ( + !empty($row['telephone_mobile']) + && !(substr($row['telephone_mobile'], 0, 4) == '+628' || substr($row['telephone_mobile'], 0, 3) == '628') + ) { throw new ImportRowException(__('enrollment.PHONE_INVALID'), 0, null, $row); } - - if (!empty($row['email']) - && !filter_var($row['email'], FILTER_VALIDATE_EMAIL)) { + + if ( + !empty($row['email']) + && !filter_var($row['email'], FILTER_VALIDATE_EMAIL) + ) { throw new ImportRowException(__('enrollment.EMAIL_INVALID'), 0, null, $row); } @@ -396,16 +400,16 @@ class MemberEnrollmentService switch ($row['record_mode']) { case "1": // New Member $member = Member::query() - ->where('member_id', $row['member_id']) - // ->whereHas('employeds', function ($query) use ($corporate) { - // $query->where('corporate_id', $corporate->id); - // }) - ->first(); - + ->where('member_id', $row['member_id']) + // ->whereHas('employeds', function ($query) use ($corporate) { + // $query->where('corporate_id', $corporate->id); + // }) + ->first(); + // Validate If Exist Member if ($member) { throw new ImportRowException(__('enrollment.MEMBER_UNIQUE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } else { @@ -418,7 +422,7 @@ class MemberEnrollmentService if ($memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -449,8 +453,28 @@ class MemberEnrollmentService ]); $memberPolicy->save(); - if (!empty($row['division'])) { - $division_id = CorporateDivision::where('code', $row['division_code'])->where('')->pluck('id'); + if (!empty($row['division_name'])) { + $division_id = CorporateDivision::query()->where('code', $row['division_name'])->pluck('id')->first(); + + if (empty($division_id)) { + $corporateCodeArray = explode(' ', $row['division_name']); + + if (!empty($corporateCodeArray[1])) { + $corporateCode = substr($corporateCodeArray[0], 0, 1) . substr($corporateCodeArray[1], 0, 1); + } elseif (!empty($corporateCodeArray[2])) { + $corporateCode = substr($corporateCodeArray[0], 0, 1) . substr($corporateCodeArray[1], 0, 1); + } else { + $corporateCode = substr($row['division_name'], 0, 1); + } + + $division = CorporateDivision::query()->create([ + 'corporate_id' => $corporate->id, + 'name' => $row['division_name'], + 'code' => $corporateCode, + ]); + + $division_id = $division->id; + } } $member->employeds()->create([ @@ -476,13 +500,13 @@ class MemberEnrollmentService break; case "2": // Member Information Update (Without Replacement Card) $member = Member::query() - ->where('member_id', $row['member_id']) - ->first(); - + ->where('member_id', $row['member_id']) + ->first(); + // Validate If Exist Member if (!$member) { throw new ImportRowException(__('enrollment.MEMBER_NOT_FOUND', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -496,23 +520,23 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + $memberPolicy->member->fill($member_data); if (!$memberPolicy->member->isDirty()) { throw new ImportRowException(__('enrollment.MEMBER_NO_CHANGE'), 0, null, $row); } - + $memberPolicy->member->save(); DB::commit(); } catch (\Exception $e) { @@ -523,13 +547,13 @@ class MemberEnrollmentService break; case "3": // Member Deletion $member = Member::query() - ->where('member_id', $row['member_id']) - ->first(); - + ->where('member_id', $row['member_id']) + ->first(); + // Validate If Exist Member if (!$member) { throw new ImportRowException(__('enrollment.MEMBER_NOT_FOUND', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -541,21 +565,21 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } $member = $memberPolicy->member; $member->active = false; - + $member->save(); break; case "5": // Member Renewal Policy (without card) @@ -566,14 +590,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -583,9 +607,10 @@ class MemberEnrollmentService throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_INVALID'), 0, null, $row); } - if (Carbon::parse($memberPolicy->end) > Carbon::parse(strtotime($row['member_expiry_date'])) - || $memberPolicy->end > Carbon::parse(strtotime($row['member_expiry_date'])) - ) { + if ( + Carbon::parse($memberPolicy->end) > Carbon::parse(strtotime($row['member_expiry_date'])) + || $memberPolicy->end > Carbon::parse(strtotime($row['member_expiry_date'])) + ) { throw new ImportRowException(__('enrollment.MEMBER_RENEWAL_STILL_ACTIVE'), 0, null, $row); } @@ -609,14 +634,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -625,9 +650,10 @@ class MemberEnrollmentService throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_INVALID'), 0, null, $row); } - if (Carbon::parse($memberPolicy->end) > Carbon::parse(strtotime($row['member_expiry_date'])) - || $memberPolicy->end > Carbon::parse(strtotime($row['member_expiry_date'])) - ) { + if ( + Carbon::parse($memberPolicy->end) > Carbon::parse(strtotime($row['member_expiry_date'])) + || $memberPolicy->end > Carbon::parse(strtotime($row['member_expiry_date'])) + ) { throw new ImportRowException(__('enrollment.MEMBER_RENEWAL_STILL_ACTIVE'), 0, null, $row); } @@ -651,7 +677,7 @@ class MemberEnrollmentService // 'policy_id' => $row['policy_number'] // ]), 0, null, $row); // } - + // // Read Option Mode // $option_mode = explode('!', $row['option_mode']); // $corp_code = $option_mode[1] ?? null; @@ -677,7 +703,7 @@ class MemberEnrollmentService $member_id_old = $member_id[0] ?? null; $member_id_new = $member_id[1] ?? null; - + $memberPolicy = MemberPolicy::query() ->where('policy_id', $policy_number_old) ->where('member_id', $member_id_old) @@ -686,14 +712,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $member_id_old, + 'member_id' => $member_id_old, 'policy_id' => $policy_number_old ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $member_id_old, + 'member_id' => $member_id_old, 'policy_id' => $policy_number_old ]), 0, null, $row); } @@ -702,12 +728,12 @@ class MemberEnrollmentService if (!empty($row['principal_id'])) { throw new ImportRowException(__('enrollment.PRINCIPAL_ID_NOT_REQUIRED'), 0, null, $row); } - + if (empty($row['corporate_id'])) { throw new ImportRowException(__('enrollment.CORPORATE_ID_REQUIRED'), 0, null, $row); } } - + if ($record_type_new == 'D') { if (empty($row['principal_id'])) { throw new ImportRowException(__('enrollment.PRINCIPAL_ID_REQUIRED'), 0, null, $row); @@ -739,19 +765,19 @@ class MemberEnrollmentService // 'policy_id' => $row['policy_number'] // ]), 0, null, $row); // } - + try { DB::beginTransaction(); - - if ( !empty($record_type_new) ) { + + if (!empty($record_type_new)) { $member = $memberPolicy->member; $member->record_type = $record_type_new; $member->principal_id = $row['principal_id']; $member->save(); } - - if ( !empty($corp_code_new) ) { + + if (!empty($corp_code_new)) { $oldCorporate = Corporate::where('code', $corp_code_old)->first(); $newCorporate = Corporate::where('code', $corp_code_new)->first(); @@ -759,24 +785,26 @@ class MemberEnrollmentService throw new ImportRowException(__('enrollment.CORPORATE_NOT_FOUND'), 0, null, $row); } $corporateEmployee = CorporateEmployee::where('corporate_id', $oldCorporate->id) - ->where('member_id', $memberPolicy->member->id) - ->first(); - $newCorporateEmployee = CorporateEmployee::updateOrCreate([ - 'corporate_id' => $oldCorporate->id, - 'member_id' => $memberPolicy->member->id - ], - [ - 'corporate_id' => $newCorporate->id, - 'member_id' => $memberPolicy->member->id - ]); + ->where('member_id', $memberPolicy->member->id) + ->first(); + $newCorporateEmployee = CorporateEmployee::updateOrCreate( + [ + 'corporate_id' => $oldCorporate->id, + 'member_id' => $memberPolicy->member->id + ], + [ + 'corporate_id' => $newCorporate->id, + 'member_id' => $memberPolicy->member->id + ] + ); } - if ( !empty($policy_number_new) ) { + if (!empty($policy_number_new)) { $memberPolicy->policy_id = $policy_number_new; $memberPolicy->save(); } - if ( !empty($member_id_new) ) { + if (!empty($member_id_new)) { $memberPolicy->member_id = $member_id_new; $memberPolicy->save(); @@ -800,7 +828,7 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -808,7 +836,7 @@ class MemberEnrollmentService if (Carbon::parse(strtotime($row['member_effective_date'])) < now() || Carbon::parse(strtotime($row['member_expiry_date'])) < now()) { throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_MUST_BE_AFTER_TODAY'), 0, null, $row); } - + if (Carbon::parse(strtotime($row['member_effective_date'])) > Carbon::parse(strtotime($row['member_expiry_date']))) { throw new ImportRowException(__('enrollment.MEMBER_EXPIRY_DATE_INVALID'), 0, null, $row); } @@ -828,14 +856,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -851,10 +879,10 @@ class MemberEnrollmentService $newMemberPolicy->save(); break; - - - // THESE MODES BELOW ARE DISABLED + + + // THESE MODES BELOW ARE DISABLED case "4": // Member Update Start and End Date throw new ImportRowException(__('MODE 4 NOT HANDLED PROPERLY, TRY TO USE MODE 2'), 0, null, $row); break; @@ -865,14 +893,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -896,9 +924,9 @@ class MemberEnrollmentService case "8": // Member Information Update (With Replacement Card) throw new ImportRowException(__('MODE 8 NOT HANDLED PROPERLY, TRY TO USE MODE 2'), 0, null, $row); break; - // case "10": // No Information Available + // case "10": // No Information Available - // break; + // break; case "11": // Advance Renewal with OLD Card No. (PRINT) throw new ImportRowException(__('MODE 11 NOT HANDLED PROPERLY, TRY TO USE MODE 13'), 0, null, $row); @@ -907,9 +935,9 @@ class MemberEnrollmentService throw new ImportRowException(__('MODE 12 NOT HANDLED PROPERLY, TRY TO USE MODE 13'), 0, null, $row); break; - // case "14": // No Information Available + // case "14": // No Information Available - // break; + // break; case "15": // Lost Card / Change Card with new card number (Print) (Rarely Used) throw new ImportRowException(__('MODE 15 NOT HANDLED PROPERLY, TRY TO USE MODE 2'), 0, null, $row); @@ -918,10 +946,10 @@ class MemberEnrollmentService throw new ImportRowException(__('MODE 16 NOT HANDLED PROPERLY, TRY TO USE MODE 2'), 0, null, $row); break; $plan = CorporatePlan::query() - ->where('corporate_id', $corporate->id) - ->where('code', $row['plan_id']) - ->where('active', true) - ->first(); + ->where('corporate_id', $corporate->id) + ->where('code', $row['plan_id']) + ->where('active', true) + ->first(); if (!$plan) { throw new ImportRowException(__('enrollment.PLAN_NOT_FOUND'), 0, null, $row); } @@ -933,14 +961,14 @@ class MemberEnrollmentService if (!$memberPolicy) { throw new ImportRowException(__('enrollment.MEMBER_NOT_EXISTS', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } - + if ($memberPolicy->status != 'active') { throw new ImportRowException(__('enrollment.MEMBER_INACTIVE', [ - 'member_id' => $row['member_id'], + 'member_id' => $row['member_id'], 'policy_id' => $row['policy_number'] ]), 0, null, $row); } @@ -978,7 +1006,7 @@ class MemberEnrollmentService } // This returning row with format or order as it is - public function makeResultRow($row_data) + public function makeResultRow($row_data) { $cells = []; foreach ($row_data as $cellValue) { diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 34de3771..78355560 100755 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -158,10 +158,10 @@ class Helper * @param string $message * @return JsonResponse */ - public static function responseJson(array|object $data = [], int $statusCode = Response::HTTP_OK, string $message = 'Data berhasil di ambil'): JsonResponse + public static function responseJson(array|object $data = [], string $status = 'success', int $statusCode = Response::HTTP_OK, string $message = 'Data berhasil di ambil'): JsonResponse { return response()->json([ - 'status' => in_array($statusCode, [200, 201, 204]) ? 'success' : 'error', + 'status' => $status, 'statusCode' => $statusCode, 'message' => $message, 'data' => $data, diff --git a/app/Models/Corporate.php b/app/Models/Corporate.php index 3cb36d89..9053a1dd 100755 --- a/app/Models/Corporate.php +++ b/app/Models/Corporate.php @@ -44,10 +44,10 @@ class Corporate extends Model public function currentPolicy() { return $this->hasOne(CorporatePolicy::class) - // ->where('start', '<=', now()) - // ->where('end', '>=', now()) - ->where('active', true) - ->latestOfMany(); + // ->where('start', '<=', now()) + // ->where('end', '>=', now()) + ->where('active', true) + ->latestOfMany(); } public function corporatePlans() diff --git a/app/Models/CorporateEmployee.php b/app/Models/CorporateEmployee.php index 6263d027..5c0ea63a 100755 --- a/app/Models/CorporateEmployee.php +++ b/app/Models/CorporateEmployee.php @@ -19,4 +19,9 @@ class CorporateEmployee extends Model 'nik', 'status' ]; + + public function division() + { + return $this->belongsTo(CorporateDivision::class, 'division_id'); + } } diff --git a/app/Models/Member.php b/app/Models/Member.php index 791541f0..d8496645 100755 --- a/app/Models/Member.php +++ b/app/Models/Member.php @@ -63,7 +63,7 @@ class Member extends Model 'gender_code', '' ]; - + protected $hidden = [ 'created_at', 'updated_at', @@ -86,15 +86,15 @@ class Member extends Model public function corporates() { return $this - ->belongsToMany(Corporate::class, 'corporate_employees', 'corporate_id', 'member_id') - ->withPivot([ - 'branch_code', - 'divison_id', - 'nik', - 'status', - 'start', - 'end' - ]); + ->belongsToMany(Corporate::class, 'corporate_employees', 'corporate_id', 'member_id') + ->withPivot([ + 'branch_code', + 'division_id', + 'nik', + 'status', + 'start', + 'end' + ]); } public function currentCorporate() @@ -133,7 +133,7 @@ class Member extends Model { return $this->hasOneThrough(Plan::class, MemberPlan::class, 'member_id', 'id', 'id', 'plan_id')->latest(); } - + public function policies() { return $this->hasMany(MemberPolicy::class, 'member_id', 'member_id'); @@ -142,10 +142,10 @@ class Member extends Model public function currentPolicy() { return $this->hasOneThrough(CorporatePolicy::class, MemberPolicy::class, 'member_id', 'code', 'member_id', 'policy_id') - ->where('corporate_policies.start', '<', now()) - ->where('corporate_policies.end', '>', now()) - ->where('member_policies.start', '<', now()) - ->where('member_policies.end', '>', now()); + ->where('corporate_policies.start', '<', now()) + ->where('corporate_policies.end', '>', now()) + ->where('member_policies.start', '<', now()) + ->where('member_policies.end', '>', now()); // return $this->hasOne(MemberPolicy::class, 'member_id', 'member_id')->where('status', 'active')->latestOfMany(); } @@ -181,13 +181,17 @@ class Member extends Model { $query->when($filters['search'] ?? false, function ($query, $search) { return $query - ->where('member_id', 'like', "%" . $search . "%") - ->orWhere('payor_id', 'like', "%" . $search . "%") - ->orWhere('name', 'like', "%" . $search . "%") - ; - // ->orWhereHas('corporatePlan', function ($query) use ($search) { - // $query->where('code', 'like', "%" . $search . "%"); - // }); + ->where('member_id', 'like', "%" . $search . "%") + ->orWhere('payor_id', 'like', "%" . $search . "%") + ->orWhere('name', 'like', "%" . $search . "%"); + // ->orWhereHas('corporatePlan', function ($query) use ($search) { + // $query->where('code', 'like', "%" . $search . "%"); + // }); }); } + + public function division() + { + return $this->hasOneThrough(CorporateDivision::class, CorporateEmployee::class, 'member_id', 'id', 'id', 'division_id'); + } } diff --git a/database/migrations/2022_11_22_135948_create_claims_table.php b/database/migrations/2022_11_22_135948_create_claims_table.php index 0e4550ec..786a4a12 100644 --- a/database/migrations/2022_11_22_135948_create_claims_table.php +++ b/database/migrations/2022_11_22_135948_create_claims_table.php @@ -22,16 +22,14 @@ return new class extends Migration $table->string('currency'); $table->foreignId('plan_id')->index(); $table->foreignId('benefit_id')->index(); + $table->string('status'); - + $table->dateTime('requested_at')->nullable(); $table->unsignedBigInteger('requested_by')->nullable()->index(); - + $table->dateTime('received_at')->nullable(); $table->unsignedBigInteger('received_by')->nullable()->index(); - - $table->dateTime('approved_at')->nullable(); - $table->unsignedBigInteger('approved_by')->nullable()->index(); $table->dateTime('declined')->nullable(); $table->unsignedBigInteger('declined_by')->nullable()->index(); diff --git a/frontend/client-portal/src/@types/auth.ts b/frontend/client-portal/src/@types/auth.ts index dbc15390..a5d2236c 100755 --- a/frontend/client-portal/src/@types/auth.ts +++ b/frontend/client-portal/src/@types/auth.ts @@ -28,7 +28,7 @@ export type JWTContextType = { method: 'jwt'; login: (phoneOrEmail: string) => Promise; validateOtp: (phoneOrEmail: string, otp: string) => Promise - logout: () => Promise; + logout: () => void; }; // export type FirebaseContextType = { diff --git a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx index 8c887190..896cc131 100755 --- a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx +++ b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx @@ -132,16 +132,9 @@ function AuthProvider({ children }: AuthProviderProps) { axios .post('/verify-code', { phoneOrEmail: phoneOrEmail, otp }) .then((response) => { - const { user, token } = response.data.data; + const { token } = response.data.data; setSession(token); - dispatch({ - type: Types.ValidateOtp, - payload: { - user, - }, - }); - return response.data; }) .catch((error) => { @@ -149,8 +142,7 @@ function AuthProvider({ children }: AuthProviderProps) { if (error.response.status !== 422) throw error.response; }); - const logout = async () => { - await axios.post('/logout'); + const logout = () => { setSession(null); dispatch({ type: Types.Logout }); }; diff --git a/frontend/client-portal/src/contexts/UserCurrentCorporate.tsx b/frontend/client-portal/src/contexts/UserCurrentCorporate.tsx new file mode 100755 index 00000000..5f1ffcdc --- /dev/null +++ b/frontend/client-portal/src/contexts/UserCurrentCorporate.tsx @@ -0,0 +1,8 @@ +import { createContext } from 'react'; + +const UserCurrentCorporateContext = createContext({ + corporateValue: '', + setCorporateValue: (value: string) => Promise, +}); + +export { UserCurrentCorporateContext }; diff --git a/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx b/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx index be9d5cbe..64bb2e33 100755 --- a/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx +++ b/frontend/client-portal/src/layouts/dashboard/header/AccountPopover.tsx @@ -8,6 +8,7 @@ import { IconButtonAnimate } from '../../../components/animate'; import { useNavigate } from 'react-router-dom'; import useAuth from '../../../hooks/useAuth'; import useLocalStorage from '../../../hooks/useLocalStorage'; +import { enqueueSnackbar } from 'notistack'; // ---------------------------------------------------------------------- @@ -33,10 +34,6 @@ export default function AccountPopover() { const navigate = useNavigate(); const { logout } = useAuth(); - const [emailOrPhone, setEmailOrPhone] = useLocalStorage('emailOrPhone', ''); - const [emailOrPhoneForm, setEmailOrPhoneForm] = useLocalStorage('emailOrPhoneForm', false); - const [loginOrVerifyCode, setLoginOrVerifyCode] = useLocalStorage('loginOrVerifyCode', false); - const handleOpen = (event: React.MouseEvent) => { setOpen(event.currentTarget); }; @@ -46,11 +43,9 @@ export default function AccountPopover() { }; const handleLogout = () => { - setEmailOrPhone(''); - setEmailOrPhoneForm(false); - setLoginOrVerifyCode(false); logout(); navigate('/auth/login'); + enqueueSnackbar('Logout Berhasil!', { variant: 'success' }); }; return ( diff --git a/frontend/client-portal/src/layouts/dashboard/header/CorporatePopover.tsx b/frontend/client-portal/src/layouts/dashboard/header/CorporatePopover.tsx new file mode 100755 index 00000000..331af30f --- /dev/null +++ b/frontend/client-portal/src/layouts/dashboard/header/CorporatePopover.tsx @@ -0,0 +1,48 @@ +import { FormControl, InputLabel, MenuItem, Select, SelectChangeEvent } from '@mui/material'; +import { useContext, useEffect, useState } from 'react'; +import { UserCurrentCorporateContext } from '../../../contexts/UserCurrentCorporate'; +import axios from '../../../utils/axios'; + +/* ---------------------------------- types --------------------------------- */ +type CorporateDataProps = { + id: number; + name: string; +}; +/* -------------------------------------------------------------------------- */ + +export default function CorporatePopover() { + const { corporateValue, setCorporateValue } = useContext(UserCurrentCorporateContext); + const [corporateData, setCorporateData] = useState([]); + + const handleCorporateChange = (event: SelectChangeEvent) => { + setCorporateValue(event.target.value as string); + }; + + useEffect(() => { + (async () => { + // @ts-ignore + const corporateManages = await axios.get(`/corporate-manage`); + setCorporateData(corporateManages.data); + })(); + }, []); + + return ( + + Corporate + + + ); +} diff --git a/frontend/client-portal/src/layouts/dashboard/header/index.tsx b/frontend/client-portal/src/layouts/dashboard/header/index.tsx index f39781e7..4b1a488f 100755 --- a/frontend/client-portal/src/layouts/dashboard/header/index.tsx +++ b/frontend/client-portal/src/layouts/dashboard/header/index.tsx @@ -18,6 +18,7 @@ import AccountPopover from './AccountPopover'; import LanguagePopover from './LanguagePopover'; import ContactsPopover from './ContactsPopover'; import NotificationsPopover from './NotificationsPopover'; +import CorporatePopover from './CorporatePopover'; // ---------------------------------------------------------------------- @@ -92,6 +93,7 @@ export default function DashboardHeader({ + diff --git a/frontend/client-portal/src/layouts/dashboard/index.tsx b/frontend/client-portal/src/layouts/dashboard/index.tsx index 569ddb4c..b0426c7a 100755 --- a/frontend/client-portal/src/layouts/dashboard/index.tsx +++ b/frontend/client-portal/src/layouts/dashboard/index.tsx @@ -13,6 +13,9 @@ import { HEADER, NAVBAR } from '../../config'; import DashboardHeader from './header'; import NavbarVertical from './navbar/NavbarVertical'; import NavbarHorizontal from './navbar/NavbarHorizontal'; +import useLocalStorage from '../../hooks/useLocalStorage'; +import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; +import useAuth from '../../hooks/useAuth'; // ---------------------------------------------------------------------- @@ -47,6 +50,7 @@ export default function DashboardLayout() { const { collapseClick, isCollapse } = useCollapseDrawer(); const { themeLayout } = useSettings(); + const { user } = useAuth(); const isDesktop = useResponsive('up', 'lg'); @@ -54,6 +58,12 @@ export default function DashboardLayout() { const verticalLayout = themeLayout === 'vertical'; + const [corporateValue, setCorporateValue] = useLocalStorage( + 'corporateValue', + `${user.corporate.id}` + ); + const value = { corporateValue, setCorporateValue }; + if (verticalLayout) { return ( <> @@ -86,19 +96,21 @@ export default function DashboardLayout() { } return ( - - setOpen(true)} /> + + + setOpen(true)} /> - setOpen(false)} /> + setOpen(false)} /> - - - - + + + + + ); } diff --git a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx index d9da4786..1cd1f3dc 100755 --- a/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx +++ b/frontend/client-portal/src/pages/Dashboard/Dashboard.tsx @@ -8,7 +8,10 @@ import Page from '../../components/Page'; import CardNotification from '../../sections/dashboard/CardNotification'; import CardBalance from '../../sections/dashboard/CardBalance'; import TableList from '../../sections/dashboard/TableList'; -import List from './List'; +import { useContext, useEffect, useState } from 'react'; +import axios from '../../utils/axios'; +import { Stack } from '@mui/system'; +import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; // ---------------------------------------------------------------------- @@ -21,43 +24,70 @@ const itemList = [ // ---------------------------------------------------------------------- +/* ---------------------------------- types --------------------------------- */ + +type PolicyProps = { + myLimit: { + balance: number; + total: number; + percentage: number; + }; + lockLimit: { + balance: number; + percentage: number; + }; +}; + +/* -------------------------------------------------------------------------- */ + +/* ------------------------------ default data ------------------------------ */ +const defaultPolicyData = { + myLimit: { + balance: 0, + total: 0, + percentage: 0, + }, + lockLimit: { + balance: 0, + percentage: 0, + }, +}; +/* -------------------------------------------------------------------------- */ + export default function Dashboard() { const { themeStretch } = useSettings(); + const { corporateValue } = useContext(UserCurrentCorporateContext); - // const [corporate, setCorporate] = useState({}); + // const [tableData, setTableData] = useState([]); + const [policyData, setPolicyData] = useState(defaultPolicyData); - // const loadSomething = () => { - // axios - // .get('dashboard') - // .then((res) => { - // setCorporate(res.data.corporate); - // }) - // .catch((err) => { - // alert('Opps, Something Went Wrong when collecting dashboard data'); - // }); - // }; - - // useEffect(() => { - // loadSomething(); - // }, []); + useEffect(() => { + (async () => { + setPolicyData(defaultPolicyData); + await new Promise((resolve) => setTimeout(resolve, 250)); + const dashboard = await axios.get(`${corporateValue}/policy`); + setPolicyData(dashboard.data.policy); + })(); + }, [corporateValue]); return ( - - Dashboard - + + + Dashboard + + - + - - + + - {/* */} diff --git a/frontend/client-portal/src/pages/Dashboard/List.tsx b/frontend/client-portal/src/pages/Dashboard/List.tsx deleted file mode 100644 index dabb387b..00000000 --- a/frontend/client-portal/src/pages/Dashboard/List.tsx +++ /dev/null @@ -1,373 +0,0 @@ -// @mui -import { - Box, - Button, - Card, - Collapse, - IconButton, - InputLabel, - MenuItem, - OutlinedInput, - Paper, - Select, - SelectChangeEvent, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - TextField, - Typography, - Badge, - Tab, - Tabs, - CardHeader, - Stack, - Menu, - ButtonGroup, - Pagination, - Grid, - Autocomplete, -} from '@mui/material'; -import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; -import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; -import AddIcon from '@mui/icons-material/Add'; -import UploadIcon from '@mui/icons-material/Upload'; -import CancelIcon from '@mui/icons-material/Cancel'; -// hooks -import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react'; -import useSettings from '../../hooks/useSettings'; -import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; -// components -import axios from '../../utils/axios'; -import { LaravelPaginatedData } from '../../@types/paginated-data'; -import { Icd } from '../../@types/diagnosis'; -import BasePagination from '../../components/BasePagination'; -import { Member } from '../../@types/member'; -import Iconify from '../../components/Iconify'; - -const options = [ - { label: 'The Shawshank Redemption', value: 1994 }, - { label: 'The Godfather', year: 1972 }, - { label: 'The Godfather: Part II', year: 1974 }, - { label: 'The Dark Knight', year: 2008 }, - { label: '12 Angry Men', year: 1957 }, - { label: "Schindler's List", year: 1993 }, - { label: 'Pulp Fiction', year: 1994 }, -]; - -export default function List() { - const navigate = useNavigate(); - const { themeStretch } = useSettings(); - const { corporate_id } = useParams(); - const [searchParams, setSearchParams] = useSearchParams(); - const [importResult, setImportResult] = useState(null); - - function SearchInput(props: any) { - // SEARCH - const searchInput = useRef(null); - const [searchText, setSearchText] = useState(''); - - const [value, setValue] = useState(options[0]); - const [inputValue, setInputValue] = useState(''); - - const handleSearchChange = (event: any) => { - const newSearchText = event.target.value ?? ''; - setSearchText(newSearchText); - }; - - const handleSearchSubmit = (event: any) => { - event.preventDefault(); - props.onSearch(searchText); // Trigger to Parent - }; - - useEffect(() => { - // Trigger First Search - setSearchText(searchParams.get('search') ?? ''); - console.log(value, inputValue); - }, [searchParams, value, inputValue]); - - return ( -
- - { - setValue(newValue); - }} - inputValue={inputValue} - onInputChange={(event, newInputValue) => { - setInputValue(newInputValue); - }} - id="controllable-states-demo" - options={options} - sx={{ width: 300 }} - renderInput={(params) => } - /> - - -
- ); - } - - function ImportForm(props: any) { - // IMPORT - // Create Button Menu - const [anchorEl, setAnchorEl] = React.useState(null); - const createMenu = Boolean(anchorEl); - const importForm = useRef(null); - const [currentImportFileName, setCurrentImportFileName] = useState(null); - - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - const handleImportButton = () => { - if (importForm?.current) { - handleClose(); - importForm.current ? importForm.current.click() : console.log('No File selected'); - } else { - alert('No file selected'); - } - }; - - const handleCancelImportButton = () => { - importForm.current.value = ''; - importForm.current.dispatchEvent(new Event('change', { bubbles: true })); - }; - - const handleImportChange = (event: any) => { - if (event.target.files[0]) { - setCurrentImportFileName(event.target.files[0].name); - } else { - setCurrentImportFileName(null); - } - }; - - const handleUpload = () => { - if (importForm.current?.files.length) { - const formData = new FormData(); - formData.append('file', importForm.current?.files[0]); - axios - .post(`master/formularium/import`, formData) - .then((response) => { - handleCancelImportButton(); - loadDataTableData(); - setImportResult(response.data); - // alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows'); - }) - .catch((response) => { - alert( - 'Looks like something went wrong. Please check your data and try again. ' + - response.message - ); - }); - } else { - alert('No File Selected'); - } - }; - - return ( - - - - - - ); - } - - // Called on every row to map the data to the columns - function createData(member: Member): Member { - return { - ...member, - }; - } - - // Generate the every row of the table - function Row(props: { row: ReturnType }) { - const { row } = props; - const [open, setOpen] = React.useState(true); - - return ( - - *': { borderBottom: 'unset' } }}> - - setOpen(!open)}> - {open ? : } - - - {row.member_id} - {row.payor_id} - {row.name} - {row.nik} - {row.nric} - - - - - {/* */} - - {/* COLLAPSIBLE ROW */} - - - - - - - - - - - - - ); - } - - // Dummy Default Data - const [dataTableIsLoading, setDataTableLoading] = useState(true); - const [dataTableLastRequest, setDataTableLastRequest] = useState(0); - const [dataTableResponseState, setDataTableResponseState] = useState('idle'); - const [dataTableData, setDataTableData] = useState({ - current_page: 1, - data: [], - path: '', - first_page_url: '', - last_page: 1, - last_page_url: '', - next_page_url: '', - prev_page_url: '', - per_page: 10, - from: 0, - to: 0, - total: 0, - }); - const [dataTablePage, setDataTablePage] = useState(5); - - const loadDataTableData = async (appliedFilter: any | null = null) => { - setDataTableLoading(true); - const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]); - const response = await axios.get('/members', { params: filter }); - - setDataTableData(response.data.members); - setDataTableLoading(false); - }; - - const headStyle = { - fontWeight: 'bold', - }; - - const applyFilter = async (searchFilter: string) => { - await loadDataTableData({ search: searchFilter }); - setSearchParams({ search: searchFilter }); - }; - - const handlePageChange = (event: ChangeEvent, value: number) => { - const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]); - loadDataTableData(filter); - setSearchParams(filter); - }; - - useEffect(() => { - loadDataTableData(); - }, []); - - return ( - - - - - {/* The Main Table */} - - - - - - Member ID - - - Name - - - Divisi - - - Limit - - - Status - - - {' '} - - - - {dataTableIsLoading ? ( - - - - Loading - - - - ) : dataTableData.data.length === 0 ? ( - - - - No Data - - - - ) : ( - - {dataTableData.data.map((row) => ( - - ))} - - )} -
-
- - -
-
- ); -} diff --git a/frontend/client-portal/src/pages/auth/Login.tsx b/frontend/client-portal/src/pages/auth/Login.tsx index b0e37c57..78694ecd 100755 --- a/frontend/client-portal/src/pages/auth/Login.tsx +++ b/frontend/client-portal/src/pages/auth/Login.tsx @@ -83,11 +83,7 @@ export default function Login() {
- + Tidak mendapatkan kode? diff --git a/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx b/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx index 1a3ba1b3..e393c4bd 100755 --- a/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx @@ -11,6 +11,7 @@ import useAuth from '../../../hooks/useAuth'; import useIsMountedRef from '../../../hooks/useIsMountedRef'; /* ------------------------------- components ------------------------------- */ import { FormProvider, RHFTextField } from '../../../components/hook-form'; +import { enqueueSnackbar } from 'notistack'; /* ---------------------------------- types --------------------------------- */ @@ -56,6 +57,10 @@ export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: Log setEmailOrPhone(data.email); setLoginOrVerifyCode(true); reset(); + enqueueSnackbar('Kode OTP telah dikirim, silahkan cek email yang login', { + variant: 'success', + autoHideDuration: 2000, + }); } catch (error: any) { reset(); diff --git a/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx b/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx index 2309a6cd..40f4f716 100755 --- a/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx @@ -11,6 +11,7 @@ import { FormProvider, RHFTextField } from '../../../components/hook-form'; /* ---------------------------------- hooks --------------------------------- */ import useAuth from '../../../hooks/useAuth'; import useIsMountedRef from '../../../hooks/useIsMountedRef'; +import { enqueueSnackbar } from 'notistack'; /* ---------------------------------- types --------------------------------- */ @@ -56,6 +57,10 @@ export default function LoginPhoneForm({ setEmailOrPhone, setLoginOrVerifyCode } setEmailOrPhone(0 + data.phone); setLoginOrVerifyCode(true); reset(); + enqueueSnackbar('Kode OTP telah dikirim, silahkan cek pada nomor yang telah login', { + variant: 'success', + autoHideDuration: 2000, + }); } catch (error: any) { reset(); diff --git a/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx b/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx index 47d99760..97e797b5 100755 --- a/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/VerifyCodeForm.tsx @@ -17,8 +17,6 @@ import useAuth from '../../../hooks/useAuth'; type VerifyCodeFormProps = { emailOrPhone: string; - setEmailOrPhoneForm: Function; - setLoginOrVerifyCode: Function; }; type FormValuesProps = { @@ -39,11 +37,7 @@ type responseProps = { /* -------------------------------------------------------------------------- */ -export default function VerifyCodeForm({ - emailOrPhone, - setEmailOrPhoneForm, - setLoginOrVerifyCode, -}: VerifyCodeFormProps) { +export default function VerifyCodeForm({ emailOrPhone }: VerifyCodeFormProps) { const navigate = useNavigate(); const { validateOtp } = useAuth(); const { enqueueSnackbar } = useSnackbar(); @@ -87,24 +81,25 @@ export default function VerifyCodeForm({ }, [setValue]); const onSubmit = async (data: FormValuesProps) => { - try { - await new Promise((resolve) => setTimeout(resolve, 1000)); - // @ts-ignore - const response: responseProps = await validateOtp(emailOrPhone, Object.values(data).join('')); + // @ts-ignore + const response: responseProps = await validateOtp(emailOrPhone, Object.values(data).join('')); - if (response.data.length === 0) { - return enqueueSnackbar(response.message, { - variant: 'error', - autoHideDuration: 4000, - preventDuplicate: true, - }); - } - - navigate('/dashboard'); - enqueueSnackbar('Verify success!', { variant: 'success' }); - } catch (error) { - console.error(error); + if (response.data.length === 0) { + return enqueueSnackbar(response.message, { + variant: 'error', + autoHideDuration: 4000, + preventDuplicate: true, + }); + } else { + enqueueSnackbar('Verify success!', { variant: 'success', autoHideDuration: 1000 }); + await new Promise((resolve) => setTimeout(resolve, 2000)); } + + navigate('/dashboard'); + enqueueSnackbar('Login Berhasil!', { variant: 'success' }); + localStorage.removeItem('loginOrVerifyCode'); + localStorage.removeItem('emailOrPhone'); + localStorage.removeItem('emailOrPhoneForm'); }; const handleChangeWithNextField = ( diff --git a/frontend/client-portal/src/sections/dashboard/CardBalance.tsx b/frontend/client-portal/src/sections/dashboard/CardBalance.tsx index dd64309c..e2ef813a 100644 --- a/frontend/client-portal/src/sections/dashboard/CardBalance.tsx +++ b/frontend/client-portal/src/sections/dashboard/CardBalance.tsx @@ -13,24 +13,30 @@ import Iconify from '../../components/Iconify'; // React import { useState } from 'react'; // utils -import { fCurrency } from '../../utils/formatNumber'; -// +import { fCurrency, fSplit } from '../../utils/formatNumber'; +/* -------------------------------- sections -------------------------------- */ import DialogTopUpLimit from './DialogTopUpLimit'; import DialogClaimSubmitMember from './DialogClaimSubmitMember'; -// ---------------------------------------------------------------------- +/* ---------------------------------- types --------------------------------- */ -type DataMembers = { - name: string; - memberId: string; - saldo: string; +type CardBalanceProps = { + data: { + myLimit: { + balance: number; + total: number; + percentage: number; + }; + lockLimit: { + balance: number; + percentage: number; + }; + }; }; -type NotificationProps = { - data?: { members: DataMembers[] }; -}; +/* -------------------------------------------------------------------------- */ -// ---------------------------------------------------------------------- +/* --------------------------------- styled --------------------------------- */ const RootBalanceStyle = styled(Card)(({ theme }) => ({ boxShadow: 'none', @@ -52,19 +58,15 @@ const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ }, })); -// ---------------------------------------------------------------------- +/* -------------------------------------------------------------------------- */ -const INITIAL = '500.000.000'; -const TOTAL = 375000000; -const PERCENT = 75; - -// ---------------------------------------------------------------------- - -export default function CardBalance({ data }: NotificationProps) { +export default function CardBalance(props: CardBalanceProps) { const [openDialog, setOpenDialog] = useState(false); const [dialogTitle, setDialogTitle] = useState(''); const [isDialog, setIsDialog] = useState(''); + const { myLimit, lockLimit } = props.data; + const clickHandler = (isDialog: string) => { switch (isDialog) { case 'submitClaim': @@ -91,18 +93,26 @@ export default function CardBalance({ data }: NotificationProps) { Total Limit - {fCurrency(TOTAL)} - / {INITIAL} + + {fCurrency(myLimit ? myLimit.balance : 0)} + + + / {fSplit(myLimit ? myLimit.total : 0)} + - {PERCENT}% + {myLimit ? myLimit.percentage : 0}% - + @@ -113,11 +123,11 @@ export default function CardBalance({ data }: NotificationProps) { sx={{ color: '#424242', marginRight: '6px' }} /> - Lock Fund ( 25% ) + Lock Fund ( {lockLimit ? lockLimit.percentage : 0}% ) - 125.000.000 / 125.000.000 + {fSplit(lockLimit ? lockLimit.balance : 0)} / {fSplit(myLimit ? myLimit.total : 0)} @@ -146,7 +156,7 @@ export default function CardBalance({ data }: NotificationProps) { openDialog={openDialog} setOpenDialog={setOpenDialog} title={{ name: dialogTitle }} - data={data?.members} + // data={data?.members} /> )} diff --git a/frontend/client-portal/src/sections/dashboard/TableList.tsx b/frontend/client-portal/src/sections/dashboard/TableList.tsx index c1100464..a263765f 100755 --- a/frontend/client-portal/src/sections/dashboard/TableList.tsx +++ b/frontend/client-portal/src/sections/dashboard/TableList.tsx @@ -1,4 +1,5 @@ /* ---------------------------------- @mui ---------------------------------- */ +import { styled } from '@mui/material/styles'; import { Paper, Table, @@ -14,21 +15,32 @@ import { IconButton, Card, Grid, + FormControl, + InputLabel, + Select, + MenuItem, + SelectChangeEvent, + Stack, + Typography, + LinearProgress, + linearProgressClasses, } from '@mui/material'; import { visuallyHidden } from '@mui/utils'; +import { Add as AddIcon } from '@mui/icons-material'; /* ---------------------------------- axios --------------------------------- */ -import axios from 'axios'; +import axios from '../../utils/axios'; /* ---------------------------------- react --------------------------------- */ -import { useEffect, useState } from 'react'; +import { useContext, useEffect, useRef, useState } from 'react'; /* -------------------------------- component ------------------------------- */ import Iconify from '../../components/Iconify'; import BaseTablePagination from '../../components/BaseTablePagination'; /* ---------------------------------- theme --------------------------------- */ import palette from '../../theme/palette'; import { useSearchParams } from 'react-router-dom'; +import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate'; +import { fSplit } from '../../utils/formatNumber'; /* ---------------------------------- types --------------------------------- */ - type PaginationTableProps = { current_page: number; from: number; @@ -41,11 +53,15 @@ type PaginationTableProps = { }; type DataTableProps = { - name: string; - member_id: string; + fullName: string; + memberId: string; division: string; - limit: string; - status: string; + limit: { + current: number; + total: number; + percentage: number; + }; + status: number; }; type Order = 'asc' | 'desc'; @@ -63,10 +79,27 @@ interface EnhancedTableProps { orderBy: string; } +type DivisionDataProps = { + id: number; + name: string; +}; +/* -------------------------------------------------------------------------- */ + +/* --------------------------------- styled --------------------------------- */ +const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({ + height: 10, + borderRadius: 6, + [`&.${linearProgressClasses.colorPrimary}`]: { + backgroundColor: '#D1F1F1', + }, + [`& .${linearProgressClasses.bar}`]: { + borderRadius: 6, + backgroundColor: theme.palette.primary.main, + }, +})); /* -------------------------------------------------------------------------- */ /* -------------------------- enchanced table head -------------------------- */ - const headCells: readonly HeadCell[] = [ { id: 'member_id', @@ -84,16 +117,16 @@ const headCells: readonly HeadCell[] = [ id: 'division', align: 'center', label: 'Divisi', - isSort: true, + isSort: false, }, { id: 'limit', align: 'center', label: 'Limit', - isSort: true, + isSort: false, }, { - id: 'status', + id: 'active', align: 'center', label: 'Status', isSort: true, @@ -139,19 +172,12 @@ function EnhancedTableHead({ order, orderBy, onRequestSort }: EnhancedTableProps ); } - /* -------------------------------------------------------------------------- */ -export default function TableList() { - const [order, setOrder] = useState('asc'); - const [orderBy, setOrderBy] = useState('name'); - const [customSearchParams, setCustomSearchParams] = useSearchParams(); - const [isLoading, setIsLoading] = useState(true); +export default function TableList(props: any) { + const { corporateValue } = useContext(UserCurrentCorporateContext); + const [dataTable, setDataTable] = useState([]); - const [dataDivision, setDataDivision] = useState([]); - const [page, setPage] = useState(0); - const [rowsPerPage, setRowsPerPage] = useState(10); - const [appliedParams, setAppliedParams] = useState({}); const [paginationTable, setPaginationTable] = useState({ current_page: 0, from: 0, @@ -163,13 +189,22 @@ export default function TableList() { total: 0, }); + const [isLoading, setIsLoading] = useState(true); + const [searchParams, setSearchParams] = useSearchParams(); + const [appliedParams, setAppliedParams] = useState({}); + + const [order, setOrder] = useState('asc'); + const [orderBy, setOrderBy] = useState('name'); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + /* ------------------------------- handle sort ------------------------------ */ const handleRequestSort = async (event: React.MouseEvent, property: string) => { const isAsc = orderBy === property && order === 'asc'; setOrder(isAsc ? 'desc' : 'asc'); setOrderBy(property); const params = Object.fromEntries([ - ...customSearchParams.entries(), + ...searchParams.entries(), ['order', isAsc ? 'desc' : 'asc'], ['orderBy', property], ]); @@ -177,37 +212,81 @@ export default function TableList() { }; /* -------------------------------------------------------------------------- */ + /* ----------------------------- Field Container ---------------------------- */ + /* ----------------------------- division field ----------------------------- */ + const [divisionValue, setDivisionValue] = useState('all'); + const [divisionData, setDivisionData] = useState([]); + + const handleDivisionChange = (event: SelectChangeEvent) => { + setDivisionValue(event.target.value as string); + + if (event.target.value === 'all') { + searchParams.delete('division'); + const params = Object.fromEntries([...searchParams.entries()]); + setAppliedParams(params); + } else { + const params = Object.fromEntries([ + ...searchParams.entries(), + ['division', event.target.value as string], + ]); + setAppliedParams(params); + } + }; + /* -------------------------------------------------------------------------- */ + /* ------------------------------ Search field ------------------------------ */ const [searchText, setSearchText] = useState(''); - const handleSearch = (event: React.ChangeEvent) => { - setSearchText(event.target.value); - }; - const handleSearchSubmit = async (event: React.FormEvent) => { event.preventDefault(); setIsLoading(true); - const params = Object.fromEntries([...customSearchParams.entries(), ['search', searchText]]); + if (searchText === '') { + searchParams.delete('search'); + const params = Object.fromEntries([...searchParams.entries()]); + setAppliedParams(params); + } else { + const params = Object.fromEntries([...searchParams.entries(), ['search', searchText]]); + setAppliedParams(params); + } await new Promise((resolve) => setTimeout(resolve, 500)); - setAppliedParams(params); setIsLoading(false); }; /* -------------------------------------------------------------------------- */ - /* ---------------------------- table pagination ---------------------------- */ + /* ------------------------------ import button ----------------------------- */ + const [currentImportFileName, setCurrentImportFileName] = useState(null); + const importForm = useRef(null); + const handleImportChange = (event: any) => { + if (event.target.files[0]) { + setCurrentImportFileName(event.target.files[0].name); + } else { + setCurrentImportFileName(null); + } + }; + /* -------------------------------------------------------------------------- */ + + /* ------------------------------ create button ----------------------------- */ + const [anchorEl, setAnchorEl] = useState(null); + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + /* -------------------------------------------------------------------------- */ + /* -------------------------------------------------------------------------- */ + + /* ---------------------------- table pagination ---------------------------- */ /* ------------------------ button change pagination ------------------------ */ const onPageChangeHandle = async ( event: React.MouseEvent | null, newPage: number ) => { setIsLoading(true); - const params = Object.fromEntries([...customSearchParams.entries(), ['page', newPage + 1]]); + const params = Object.fromEntries([...searchParams.entries(), ['page', newPage + 1]]); setPage(newPage); await new Promise((resolve) => setTimeout(resolve, 500)); setAppliedParams(params); setIsLoading(false); - // setCustomSearchParams.set('page', newPage + 1); + // setSearchParams.set('page', newPage + 1); }; /* -------------------------------------------------------------------------- */ @@ -216,7 +295,7 @@ export default function TableList() { setIsLoading(true); setPage(0); const params = Object.fromEntries([ - ...customSearchParams.entries(), + ...searchParams.entries(), ['per_page', parseInt(event.target.value, 10)], ]); setRowsPerPage(parseInt(event.target.value, 10)); @@ -225,52 +304,102 @@ export default function TableList() { setIsLoading(false); }; /* -------------------------------------------------------------------------- */ - /* -------------------------------------------------------------------------- */ useEffect(() => { (async () => { setIsLoading(true); + const division = await axios.get(`${corporateValue}/division`); + setDivisionData(division.data); + const params = Object.keys(appliedParams).length !== 0 ? appliedParams - : Object.fromEntries([ - ...customSearchParams.entries(), - ['order', order], - ['orderBy', orderBy], - ]); + : Object.fromEntries([...searchParams.entries(), ['order', order], ['orderBy', orderBy]]); - const response = await axios.get('http://localhost:8001/api/dashboard', { - params: params, + const corporateMembers = await axios.get(`${corporateValue}/members`, { + params, }); - const division = await axios.get('http://localhost:8001/api/division'); - setDataDivision(division.data); - - setCustomSearchParams(params); - setDataTable(response.data.data); - setPaginationTable(response.data.meta); - setRowsPerPage(response.data.meta.per_page); + setSearchParams(params); + setDataTable(corporateMembers.data.data); + // setPaginationTable(response.data.meta); + // setRowsPerPage(response.data.meta.per_page); setIsLoading(false); })(); - }, [appliedParams, customSearchParams, order, orderBy, setCustomSearchParams]); + }, [appliedParams, searchParams, order, orderBy, setSearchParams, corporateValue]); return ( {/* Field 1 */} -
- - + + + + Division + + + + +
+ setSearchText(event.target.value)} + value={searchText} + fullWidth + /> + +
+ + + + + + +
{/* End Field 1 */} {/* Field 2 */} @@ -292,12 +421,23 @@ export default function TableList() { ) : dataTable.length >= 1 ? ( dataTable.map((row: DataTableProps, index) => ( - {row.member_id} - {row.name} + {row.memberId} + {row.fullName} {row.division} - {row.limit} + + + + + {fSplit(row.limit.current)} / {fSplit(row.limit.total)} + + + - {row.status.toLowerCase() === 'active' ? ( + {row.status === 1 ? ( ) : ( )} diff --git a/frontend/client-portal/src/utils/formatNumber.ts b/frontend/client-portal/src/utils/formatNumber.ts index 353c0299..834dd5f4 100755 --- a/frontend/client-portal/src/utils/formatNumber.ts +++ b/frontend/client-portal/src/utils/formatNumber.ts @@ -28,6 +28,10 @@ export function fCurrency(number: string | number) { return numeral(number).format('$0,0'); } +export function fSplit(number: string | number) { + return numeral(number).format('0,0'); +} + export function fPercent(number: number) { return numeral(number / 100).format('0.0%'); }