39 Commits

Author SHA1 Message Date
R
5ee9293daf [Hotfix] Create Claim
(cherry picked from commit d0ee09a6e4)
2023-03-01 14:26:13 +07:00
R
89cd2a9d37 [WIP] A 2023-03-01 12:07:03 +07:00
R
ce0fde18dc Ingest Providers 2023-03-01 12:04:25 +07:00
R
acf9fa348e Change Benefit Display 2023-02-24 11:59:22 +07:00
R
74dd65efde Fix Wrong Loading 2023-02-24 10:56:05 +07:00
R
edc5ba9822 Change 999999999 to As Charge 2023-02-24 10:52:01 +07:00
R
8902718523 Add Dummy Notification Data 2023-02-24 10:45:03 +07:00
R
1c4f03ea83 Separate Invoice Upload 2023-02-24 09:47:35 +07:00
R
627904abba Update Show Claim Request 2023-02-24 09:40:37 +07:00
R
bd3f53b596 Fix Upload Files 2023-02-23 16:18:44 +07:00
R
69919878fa Fix Claim Request Null 2023-02-23 10:13:16 +07:00
R
f3bdf12bc4 Merge branch 'staging' of itcorp.primaya.id:rajif/aso into staging 2023-02-23 09:42:05 +07:00
R
eb1211cde7 Add Plan Limit & Usage 2023-02-23 09:41:58 +07:00
Linksehat Staging Server
b8ed27f2ff [Server Build] Rebuild Dashboard Staging 2023-02-22 08:51:59 +00:00
R
74cfcfa16b Ignore Build Folder 2023-02-22 13:36:15 +07:00
R
9f95e89a9a Merge branch 'feature/aso-digital-card' into staging 2023-02-22 13:21:50 +07:00
R
e51068b0a6 Fix Generate LOG 2023-02-22 12:30:02 +07:00
R
5cd23ff343 [Build] Dashboard Staging 2023-02-21 17:07:28 +07:00
R
615330bb46 Merge branch 'feature/generate-log' into staging 2023-02-21 17:02:53 +07:00
R
bcf6662db6 Update Generate Log 2023-02-21 17:01:57 +07:00
R
be43f8a4a4 Merge branch 'feature/aso-digital-card' into staging 2023-02-20 15:03:14 +07:00
R
dba421ad0b Update Digital Card Get Member Detail 2023-02-20 15:03:05 +07:00
R
c3a78f8a40 [Build] Dashboard Staging 2023-02-17 14:39:12 +07:00
R
6c6a7c3919 Merge branch 'feature/aso-digital-card' into staging 2023-02-17 14:31:02 +07:00
R
5c71b556a0 Update Linking With ASO 2023-02-17 14:29:50 +07:00
R
63c53d18d1 [Build] Staging Dashboard & Hospital Portal 2023-02-15 12:54:43 +07:00
R
912edcdae7 [Build] Dashboard Production 2023-02-15 10:47:39 +07:00
R
5a7695b404 Add Appointment Type 2023-02-15 10:46:37 +07:00
R
5f05f191c6 [Build] Dashboard Prod 2023-02-15 10:14:20 +07:00
R
46af57b17c Show additional patient information in Dashboard Appointment 2023-02-15 10:13:07 +07:00
R
016bd3f605 [Hotfix] No PaymentDetail 2023-02-15 10:02:49 +07:00
R
093f8160d2 [Build] Additional Information 2023-02-15 10:00:53 +07:00
R
d8f493103c Add Additional Information 2023-02-15 09:57:00 +07:00
R
7f77deb09e Order Appointment & Livechat by newset 2023-02-15 09:10:22 +07:00
R
d38bc8dbfc [Build] Dashboard 2023-02-15 09:07:33 +07:00
R
b225084991 Update OLDLMS Appointment & Livechat Status 2023-02-15 09:03:13 +07:00
R
d8a98f4648 Merge remote-tracking branch 'origin/feature/appointment' 2023-02-15 08:08:51 +07:00
pajri
f309f4039c list dan show appointment 2023-02-10 16:55:24 +07:00
pajri
387658a992 CRUD Doctor Hospital 2023-02-09 13:15:45 +07:00
271 changed files with 9980 additions and 2384 deletions

3
.gitignore vendored
View File

@@ -12,3 +12,6 @@ npm-debug.log
yarn-error.log
/.idea
/.vscode
/public/dashboard
/public/dashboard-staging

View File

@@ -2,14 +2,19 @@
namespace Modules\HospitalPortal\Http\Controllers\Api;
use App\Events\ClaimRequested;
use App\Helpers\Helper;
use App\Models\ClaimRequest;
use App\Models\File;
use App\Models\Member;
use App\Services\ClaimRequestService;
use App\Services\ClaimService;
use Exception;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\HospitalPortal\Transformers\ClaimRequestResource;
use Modules\HospitalPortal\Transformers\ClaimRequestShowResource;
use PDF;
class ClaimRequestController extends Controller
@@ -33,6 +38,7 @@ class ClaimRequestController extends Controller
$q->where('status', $status);
})
->with(['member'])
->orderBy('created_at', 'DESC')
->paginate();
return Helper::responseJson($claimRequests);
@@ -55,23 +61,58 @@ class ClaimRequestController extends Controller
public function store(Request $request)
{
$request->validate([
// 'submission_date' => 'required',
'member_id' => 'required',
// 'files' => ''
]);
$newClaimRequest = ClaimRequest::create([
'member_id' => $request->member_id,
'submission_date' => now(),
'status' => 'requested'
$member = Member::find($request->member_id);
$newClaimRequest = ClaimRequestService::storeClaimRequest(member: $member);
ClaimRequested::dispatch($newClaimRequest);
// Log History
$newClaimRequest->histories()->create([
'title' => 'New Claim Requested',
'description' => "Claim Requested for Member : {$member->member_id} - ({$member->full_name})",
'type' => 'info',
'system_origin' => 'hospital-portal'
]);
if ($request->hasFile('result_files')) {
foreach ($request->result_files as $file) {
$pathFile = File::storeFile('claim', $newClaimRequest->id, $file);
$pathFile = File::storeFile('claim-result', $newClaimRequest->id, $file);
$newClaimRequest->files()->updateOrCreate([
'type' => 'result',
'name' => File::getFileName('claim', $newClaimRequest->id, $file),
'type' => 'claim-result',
'name' => File::getFileName('claim-result', $newClaimRequest->id, $file),
'original_name' => $file->getClientOriginalName(),
'extension' => $file->getClientOriginalExtension(),
'path' => $pathFile,
'created_by' => auth()->user()->id,
'updated_by' => auth()->user()->id,
]);
}
}
if ($request->hasFile('diagnosa_files')) {
foreach ($request->diagnosa_files as $file) {
$pathFile = File::storeFile('claim-diagnosis', $newClaimRequest->id, $file);
$newClaimRequest->files()->updateOrCreate([
'type' => 'claim-diagnosis',
'name' => File::getFileName('claim-diagnosis', $newClaimRequest->id, $file),
'original_name' => $file->getClientOriginalName(),
'extension' => $file->getClientOriginalExtension(),
'path' => $pathFile,
'created_by' => auth()->user()->id,
'updated_by' => auth()->user()->id,
]);
}
}
if ($request->hasFile('result_files')) {
foreach ($request->result_files as $file) {
$pathFile = File::storeFile('claim-kondisi', $newClaimRequest->id, $file);
$newClaimRequest->files()->updateOrCreate([
'type' => 'claim-kondisi',
'name' => File::getFileName('claim-kondisi', $newClaimRequest->id, $file),
'original_name' => $file->getClientOriginalName(),
'extension' => $file->getClientOriginalExtension(),
'path' => $pathFile,
@@ -80,7 +121,6 @@ class ClaimRequestController extends Controller
]);
}
}
return ($request->result_files[0]->getClientOriginalName());
return Helper::responseJson(data: $request->toArray(), message: 'Claim Request berhasil ajukan!');
}
@@ -92,7 +132,14 @@ class ClaimRequestController extends Controller
*/
public function show($id)
{
return view('hospitalportal::show');
$claimRequest = ClaimRequest::findOrFail($id);
$claimRequest->load([
'histories' => function ($history) {
$history->latest();
}
]);
return Helper::responseJson(data: ClaimRequestShowResource::make($claimRequest));
}
/**

View File

@@ -39,5 +39,6 @@ Route::prefix('hospitalportal')->group(function () {
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

@@ -0,0 +1,31 @@
<?php
namespace Modules\HospitalPortal\Transformers;
use Illuminate\Http\Resources\Json\JsonResource;
class ClaimRequestResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
$data = parent::toArray($request);
$historiesGroupByDate = $this->histories->mapToGroups(function($history) {
return [$history->created_at->format('Y-m-d') => $history];
});
$data['histories_by_date'] = [];
foreach ($historiesGroupByDate as $date => $histories) {
$data['histories_by_date'][] = [
'date' => $date,
'histories' => $histories
];
}
return $data; //parent::toArray($request);
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Modules\HospitalPortal\Transformers;
use Illuminate\Http\Resources\Json\JsonResource;
class ClaimRequestShowResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
$data = parent::toArray($request);
$historiesGroupByDate = $this->histories->mapToGroups(function($history) {
return [$history->created_at->format('Y-m-d') => $history];
});
$data['histories_by_date'] = [];
foreach ($historiesGroupByDate as $date => $histories) {
$data['histories_by_date'][] = [
'date' => $date,
'histories' => $histories
];
}
return $data;
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace Modules\Internal\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Models\OLDLMS\Appointment;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\Internal\Transformers\AppointmentResource;
class AppointmentController extends Controller
{
/**
* Display a listing of the resource.
* @return Renderable
*/
public function index()
{
$appointments = Appointment::query()
->with('doctor.user', 'doctor.speciality', 'appointmentDetail', 'healthCare', 'user', 'user.detail')
->latest()
->paginate(15);
return response()->json(Helper::paginateResources(AppointmentResource::collection($appointments)));
}
/**
* 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)
{
//
}
/**
* Show the specified resource.
* @param int $id
* @return Renderable
*/
public function show($id)
{
$appointments = Appointment::query()
->with('doctor.user', 'doctor.speciality', 'appointmentDetail', 'healthCare')
->where('nID', $id)
->first();
return response()->json(new AppointmentResource($appointments));
}
/**
* Show the form for editing the specified resource.
* @param int $id
* @return Renderable
*/
public function edit($id)
{
return view('internal::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)
{
//
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Modules\Internal\Http\Controllers\Api;
use App\Models\City;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class CityController extends Controller
{
/**
* Display a listing of the resource.
* @return Renderable
*/
public function index(Request $request)
{
$city = City::where('province_id', $request->province_id)->orderBy('name', 'asc')->get();
if (!$city) {
return response(['message' => 'Tidak ada data'], 404);
} else {
return response(['message' => 'Data ditemukan', 'data' => $city]);
}
}
/**
* 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)
{
//
}
/**
* 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($id)
{
return view('internal::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)
{
//
}
}

View File

@@ -11,6 +11,7 @@ use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Knp\Snappy\Pdf;
use Modules\Internal\Transformers\ClaimRequestResource;
use Modules\Internal\Transformers\ClaimRequestShowResource;
class ClaimRequestController extends Controller
{
@@ -67,7 +68,15 @@ class ClaimRequestController extends Controller
*/
public function show($id)
{
return view('internal::show');
$claimRequest = ClaimRequest::findOrFail($id);
$claimRequest->load([
'histories' => function ($history) {
$history->latest();
},
'files'
]);
return Helper::responseJson(data: ClaimRequestShowResource::make($claimRequest));
}
/**
@@ -104,9 +113,20 @@ class ClaimRequestController extends Controller
public function approve($id)
{
$claimRequest = ClaimRequest::findOrFail($id);
$member = $claimRequest->member;
$claimRequest->status = 'approved';
$claimRequest->save();
// Store Generated Documents
$logContent = view('pdf.guaranted_leter', compact('member', 'claimRequest'));
$claimRequest->generatedDocuments()->create([
'type' => 'guarantee_letter',
'title' => 'Guarantee Letter for '. $member->full_name,
'document_type' => 'type',
'html_content' => $logContent,
'system_origin' => 'primecenter'
]);
return $claimRequest;
}

View File

@@ -10,6 +10,7 @@ use App\Models\Member;
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
use Box\Spout\Common\Entity\Row;
use Carbon\Carbon;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
@@ -42,10 +43,10 @@ class CorporateMemberController extends Controller
'claims' => function ($claim) {
// return $claim->whereBetween('requested_at', [now()->startOfYear(), now()->endOfYear()]);
// return $claim->used(now()->startOfYear(), now()->endOfYear());
}
},
'currentPlan',
'currentPlan.benefits'
])
->with('currentPlan')
// ->with
->paginate()
->appends($request->all());
@@ -231,15 +232,23 @@ class CorporateMemberController extends Controller
}
public function generateLog($member_id)
public function generateLog(Request $request, $member_id)
{
$member = Member::findOrFail($member_id)
->load(['currentPlan', 'currentPolicy', 'currentPlan.corporateBenefits', 'currentPlan.corporateBenefits.benefit']);
->load([
'currentPlan',
'currentPolicy',
'currentPlan.corporateBenefits' => function ($benefit) use ($request) {
return $benefit->when($request->benefit_ids, function ($q, $ids) {
return $q->whereIn('benefit_id', $ids);
});
},
'currentPlan.corporateBenefits.benefit']);
// dd($member->currentPlan->corporateBenefits->toArray());
$dateOfAdmission = $request->date_of_admission ? Carbon::parse($request->date_of_admission) : now();
// return view('pdf.guaranted_leter', compact('member'));
$pdf = PDF::loadView('pdf.guaranted_leter', compact('member'));
$pdf = PDF::loadView('pdf.guaranted_leter', compact(['member', 'dateOfAdmission']));
return $pdf->download('Guaranted Letter - '.$member->full_name.'.pdf');
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Modules\Internal\Http\Controllers\Api;
use App\Models\District;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class DistrictController extends Controller
{
/**
* Display a listing of the resource.
* @return Renderable
*/
public function index(Request $request)
{
$district = District::where('city_id', $request->city_id)->orderBy('name', 'asc')->get();
if (!$district) {
return response(['message' => 'Tidak ada data'], 404);
} else {
return response(['message' => 'Data ditemukan', 'data' => $district]);
}
}
/**
* 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)
{
//
}
/**
* 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($id)
{
return view('internal::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)
{
//
}
}

View File

@@ -3,6 +3,7 @@
namespace Modules\Internal\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Models\Person;
use App\Models\Practitioner;
use App\Models\PractitionerRole;
use Illuminate\Contracts\Support\Renderable;
@@ -18,12 +19,6 @@ class DoctorController extends Controller
*/
public function index(Request $request)
{
// $doctors = PractitionerRole::active()->with('practitioner.person', 'organization')
// ->when($request->search ?? null, function ($query, $search) {
// $query->whereHas('practitioner.person', function ($person) use ($search) {
// $person->where('name', 'LIKE', '%' . $search . '%');
// });
// })->paginate();
$doctors = Practitioner::with('person', 'practitionerRoles.organization', 'practitionerRoles.speciality')
->when($request->search ?? null, function ($query, $search) {
@@ -31,6 +26,9 @@ class DoctorController extends Controller
$person->where('name', 'LIKE', '%' . $search . '%');
});
})
->when($request->id ?? null, function ($query, $id) {
$query->where('id', $id);
})
->when($request->organization_id ?? null, function ($query, $organization_id) {
$query->whereHas('practitionerRoles', function ($practitionerRole) use ($organization_id) {
$practitionerRole->where('organization_id', $organization_id);
@@ -65,7 +63,58 @@ class DoctorController extends Controller
*/
public function store(Request $request)
{
//
$data_person = [
'name' => $request->name,
'gender' => $request->gender,
'address' => $request->address,
'phone' => $request->phone,
'email' => $request->email,
'birth_date' => date('Y-m-d', strtotime($request->birth_date)),
'birth_place' => $request->birth_place,
];
$person = Person::create($data_person);
$address = $person->addresses()->create([
'use' => 'both',
'type' => 'physical',
'text' => $request->address,
]);
$person->main_address_id = $address->id;
$person->save();
$practitioner = $person->practitioner()->create();
$practices = $request->practices;
if ($practices[0]['organization_id'] !== null) {
foreach ($practices as $key => $practice) {
if (isset($practice['specialities'])) {
//jika input spesialis
foreach ($practice['specialities'] as $key => $speciality) {
$speciality_id = $speciality['speciality_id'];
$organization_id = $practice['organization_id'];
$practitionerRole = $practitioner->practitionerRoles()->create([
'organization_id' => $organization_id,
'speciality_id' => $speciality_id,
]);
}
} else {
//jika tidak input spesialis
$speciality_id = null;
$organization_id = $practice['organization_id'];
$practitionerRole = $practitioner->practitionerRoles()->create([
'organization_id' => $organization_id,
'speciality_id' => $speciality_id,
]);
}
}
}
return response()->json([
'status' => 'success',
'message' => 'Data berhasil disimpan',
]);
}
/**
@@ -75,7 +124,8 @@ class DoctorController extends Controller
*/
public function show($id)
{
return view('internal::show');
$practitioner = Practitioner::with('person', 'practitionerRoles.organization', 'practitionerRoles.speciality')->find($id);
return response()->json(DoctorResource::make($practitioner));
}
/**
@@ -85,7 +135,8 @@ class DoctorController extends Controller
*/
public function edit($id)
{
return view('internal::edit');
$practitioner = Practitioner::with('person', 'practitionerRoles.organization', 'practitionerRoles.speciality')->find($id);
return response()->json(DoctorResource::make($practitioner));
}
/**
@@ -96,7 +147,81 @@ class DoctorController extends Controller
*/
public function update(Request $request, $id)
{
//
$practitioner = Practitioner::find($id);
$data_person = [
'name' => $request->name,
'gender' => $request->gender,
'address' => $request->address,
'phone' => $request->phone,
'email' => $request->email,
'birth_date' => date('Y-m-d', strtotime($request->birth_date)),
'birth_place' => $request->birth_place,
];
$person = $practitioner->person;
$person->update($data_person);
$address = $practitioner->person->addresses()->updateOrCreate([
'use' => 'both',
'type' => 'physical',
'text' => $request->address,
]);
$practitioner->person->main_address_id = $address->id;
$practitioner->person->save();
$practices = $request->practices;
$practitionerRole = $practitioner->practitionerRoles()->get() ?? null;
foreach ($practices as $practice) {
$organization_id = $practice['organization_id'];
foreach ($practice['specialities'] as $speciality) {
$speciality_id = $speciality['speciality_id'];
$cek = $practitionerRole->where('organization_id', $organization_id)
->where('speciality_id', $speciality_id)->first() ?? null;
if (!$cek || $practitionerRole->isEmpty()) {
// Create new practitioner role if not found
$practitioner->practitionerRoles()->create([
'organization_id' => $organization_id,
'speciality_id' => $speciality_id,
]);
}
}
}
if ($practitionerRole) {
// Remove practitioner roles that are no longer exists
$currentRoleIds = $practitionerRole->pluck('id')->toArray();
$newRoleIds = [];
foreach ($practices as $practice) {
$organization_id = $practice['organization_id'];
foreach ($practice['specialities'] as $speciality) {
$speciality_id = $speciality['speciality_id'];
$newPractitionerRole = $practitionerRole->where('organization_id', $organization_id)
->where('speciality_id', $speciality_id)
->first();
if ($newPractitionerRole) {
$newRoleIds[] = $newPractitionerRole->id;
}
}
}
$deletedRoleIds = array_diff($currentRoleIds, $newRoleIds);
if (count($deletedRoleIds) > 0) {
// Delete practitioner roles that are no longer exists
$data = $practitionerRole->whereIn('id', $deletedRoleIds);
$data->each(function ($item) {
$item->delete();
});
}
}
return response()->json([
'status' => 'success',
'message' => 'Data berhasil disimpan',
]);
}
/**
@@ -106,6 +231,14 @@ class DoctorController extends Controller
*/
public function destroy($id)
{
//
$practitioner = Practitioner::find($id);
$person = $practitioner->person->delete();
$address = $practitioner->person->addresses()->delete();
$practitionerRole = $practitioner->practitionerRoles()->delete();
$practitioner->delete();
return response()->json([
'status' => 'success',
'message' => 'Data berhasil dihapus',
]);
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Modules\Internal\Http\Controllers\Api;
use App\Helpers\Helper;
use App\Models\OLDLMS\Livechat;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\Internal\Transformers\LivechatResource;
class LivechatController extends Controller
{
/**
* Display a listing of the resource.
* @return Renderable
*/
public function index()
{
$livechat = Livechat::with('doctor.user', 'doctor.speciality', 'appointment.appointmentDetail', 'healthCare')
->where('nIDAppointment', '!=', null)->where('nIDAppointment', '!=', '')
->latest()
->paginate(15);
return response()->json(Helper::paginateResources(LivechatResource::collection($livechat)));
}
/**
* 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)
{
}
/**
* Show the specified resource.
* @param int $id
* @return Renderable
*/
public function show($id)
{
$livechat = Livechat::with('doctor.user', 'doctor.speciality', 'appointment.appointmentDetail', 'healthCare')
->where('nIDAppointment', '!=', null)->where('nIDAppointment', '!=', '')
->where('nID', $id)
->first();
return response()->json(new LivechatResource($livechat));
}
/**
* Show the form for editing the specified resource.
* @param int $id
* @return Renderable
*/
public function edit($id)
{
return view('internal::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)
{
//
}
}

View File

@@ -47,7 +47,44 @@ class OrganizationController extends Controller
*/
public function store(Request $request)
{
//
$organization = [
'code' => $request->code,
'name' => $request->name,
'type' => 'hospital',
'status' => $request->active == 1 ? 'active' : 'inactive',
'description' => $request->description,
];
$create_organization = Organization::create($organization);
if ($request->phone != null) {
$create_organization->metas()->create([
'system' => 'default',
'type' => 'phone',
'value' => $request->phone,
]);
}
$address = $create_organization->addresses()->create([
'use' => 'both',
'type' => 'physical',
'text' => $request->address,
'province_id' => $request->province_id,
'city_id' => $request->city_id,
'district_id' => $request->district_id,
'village_id' => $request->village_id,
'postal_code' => $request->postal_code,
'lat' => $request->lat,
'lng' => $request->lng,
]);
$create_organization->main_address_id = $address->id;
$create_organization->save();
return response()->json([
'message' => 'Data berhasil disimpan',
'data' => new OrganizationResource($create_organization)
]);
}
/**
@@ -57,7 +94,7 @@ class OrganizationController extends Controller
*/
public function show($id)
{
return view('internal::show');
return response()->json(OrganizationResource::make(Organization::find($id)));
}
/**
@@ -67,7 +104,7 @@ class OrganizationController extends Controller
*/
public function edit($id)
{
return view('internal::edit');
return response()->json(OrganizationResource::make(Organization::find($id)));
}
/**
@@ -78,7 +115,46 @@ class OrganizationController extends Controller
*/
public function update(Request $request, $id)
{
//
$update_organization = Organization::find($id);
$update_organization->update([
'code' => $request->code,
'name' => $request->name,
'type' => 'hospital',
'status' => $request->active == 1 ? 'active' : 'inactive',
'description' => $request->description,
]);
if ($request->phone != null) {
$update_organization->metas()->updateOrCreate([
'system' => 'default',
'type' => 'phone',
], [
'system' => 'default',
'type' => 'phone',
'value' => $request->phone,
]);
}
$update_organization->addresses()->updateOrCreate([
'id' => $update_organization->main_address_id
], [
'use' => 'both',
'type' => 'physical',
'text' => $request->address,
'province_id' => $request->province_id,
'city_id' => $request->city_id,
'district_id' => $request->district_id,
'village_id' => $request->village_id,
'postal_code' => $request->postal_code,
'lat' => $request->lat,
'lng' => $request->lng,
]);
return response()->json([
'message' => 'Data berhasil diubah',
'data' => new OrganizationResource($update_organization)
]);
}
/**
@@ -88,6 +164,12 @@ class OrganizationController extends Controller
*/
public function destroy($id)
{
//
$delete_organization = Organization::find($id);
$delete_organization->addresses()->delete();
$delete_organization->delete();
return response()->json([
'message' => 'Data berhasil dihapus',
'data' => new OrganizationResource($delete_organization)
]);
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Modules\Internal\Http\Controllers\Api;
use App\Models\Province;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class ProvinceController extends Controller
{
/**
* Display a listing of the resource.
* @return Renderable
*/
public function index(Request $request)
{
$province = Province::orderBy('name', 'ASC')->get();
if (empty($province)) {
return response(['message' => 'Tidak ada data'], 404);
} else {
return response(['message' => 'Data ditemukan', 'data' => $province]);
}
}
/**
* 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)
{
//
}
/**
* 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($id)
{
return view('internal::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)
{
//
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace Modules\Internal\Http\Controllers\Api;
use App\Models\Village;
use Illuminate\Contracts\Support\Renderable;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class VillageController extends Controller
{
/**
* Display a listing of the resource.
* @return Renderable
*/
public function index(Request $request)
{
$villages = Village::where('district_id', $request->district_id)->orderBy('name', 'asc')->get();
if (!$villages) {
return response(['message' => 'Tidak ada data'], 404);
} else {
return response(['message' => 'Data ditemukan', 'data' => $villages]);
}
}
/**
* 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)
{
//
}
/**
* 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($id)
{
return view('internal::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)
{
//
}
}

View File

@@ -3,7 +3,9 @@
use App\Http\Controllers\Api\MemberController as ApiMemberController;
use Modules\Internal\Http\Controllers\Api\AuthController;
use Illuminate\Http\Request;
use Modules\Internal\Http\Controllers\Api\AppointmentController;
use Modules\Internal\Http\Controllers\Api\BenefitController;
use Modules\Internal\Http\Controllers\Api\CityController;
use Modules\Internal\Http\Controllers\Api\ClaimController;
use Modules\Internal\Http\Controllers\Api\ClaimRequestController;
use Modules\Internal\Http\Controllers\Api\CorporateBenefitController;
@@ -14,14 +16,18 @@ use Modules\Internal\Http\Controllers\Api\CorporatePlanController;
use Modules\Internal\Http\Controllers\Api\CorporateServiceController;
use Modules\Internal\Http\Controllers\Api\DiagnosisController;
use Modules\Internal\Http\Controllers\Api\DiagnosisExclusionController;
use Modules\Internal\Http\Controllers\Api\DistrictController;
use Modules\Internal\Http\Controllers\Api\DivisionController;
use Modules\Internal\Http\Controllers\Api\DoctorController;
use Modules\Internal\Http\Controllers\Api\DrugController;
use Modules\Internal\Http\Controllers\Api\FormulariumController;
use Modules\Internal\Http\Controllers\Api\LivechatController;
use Modules\Internal\Http\Controllers\Api\MemberController;
use Modules\Internal\Http\Controllers\Api\OrganizationController;
use Modules\Internal\Http\Controllers\Api\PlanController;
use Modules\Internal\Http\Controllers\Api\ProvinceController;
use Modules\Internal\Http\Controllers\Api\SpecialityController;
use Modules\Internal\Http\Controllers\Api\VillageController;
/*
|--------------------------------------------------------------------------
@@ -120,16 +126,20 @@ Route::prefix('internal')->group(function () {
Route::get('search-organizations', [OrganizationController::class, 'searchOrganization']);
Route::get('search-specialities', [SpecialityController::class, 'searchSpeciality']);
Route::resource('organizations', OrganizationController::class);
Route::resource('appointments', AppointmentController::class);
Route::resource('live-chat', LivechatController::class);
Route::resource('doctors', DoctorController::class);
Route::get('generate-log/{member_id}', [CorporateMemberController::class, 'generateLog']);
Route::post('generate-log/{member_id}', [CorporateMemberController::class, 'generateLog']);
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::get('claim-requests/{id}', [ClaimRequestController::class, 'show'])->name('claim-requests.show');
});
// Route::resource('organizations', OrganizationController::class);
// Route::resource('doctors', DoctorController::class);
// Route::get('something', [DiagnosisExclusionController::class, 'index']);
Route::get('province', [ProvinceController::class, 'index']);
Route::get('city', [CityController::class, 'index']);
Route::get('district', [DistrictController::class, 'index']);
Route::get('village', [VillageController::class, 'index']);
});

View File

@@ -0,0 +1,53 @@
<?php
namespace Modules\Internal\Transformers;
use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;
class AppointmentResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
$appointment = [
'id' => $this->nID,
'patient_name' => $this->user ? $this->user->full_name : '',
'doctor_name' => $this->doctor ? $this->doctor->user?->full_name : '',
'speciality' => $this->doctor->speciality->sKeterangan,
'date_appointment' => Carbon::parse($this->appointmentDetail->dTanggalAppointment)->format('d-m-Y') . ' ' . $this->appointmentDetail->tTimeAppointment,
'date_created' => Carbon::parse($this->dCreateOn)->format('d-m-Y H:i:s') ?? null,
'appointment_media' => $this->sMedia,
'status' => $this->status_name,
'health_care' => $this->healthCare->sHealthCare ?? null,
'payment_method' => $this->payment_method ?? null,
'patient' => $this->user,
'booking_code' => $this->sBookingCode,
'his_detail' => [
'RegID' => $this->sRegID,
'Medrec' => $this->sNomorRekamMedis
],
'type' => $this->type
];
$payment_detail = null;
if ($this->appointmentDetail->sPaymentDetails != null) {
$payment_detail = [
'payment_type' => $this->appointmentDetail->sPaymentDetails['payment_type'] ?? '',
'transaction_time' => $this->appointmentDetail->sPaymentDetails['transaction_time'] ?? '',
'gross_amount' => $this->appointmentDetail->sPaymentDetails['gross_amount'] ?? '',
'currency' => $this->appointmentDetail->sPaymentDetails['currency'] ?? '',
'status_message' => $this->appointmentDetail->sPaymentDetails['status_message'] ?? '',
];
}
$appointment['payment_detail'] = $payment_detail;
return $appointment;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Modules\Internal\Transformers;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Str;
class ClaimRequestShowResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
$data = parent::toArray($request);
// Map Histories to Group by Dates
$historiesGroupByDate = $this->histories->mapToGroups(function($history) {
return [$history->created_at->format('Y-m-d') => $history];
});
$data['histories_by_date'] = [];
foreach ($historiesGroupByDate as $date => $histories) {
$data['histories_by_date'][] = [
'date' => $date,
'histories' => $histories
];
}
// Map Files by type
$filesGroupByType = $this->files->mapToGroups(function($file) {
return [Str::slug($file->type, '_') => $file];
});
$data['files_by_type'] = $filesGroupByType;
return $data;
}
}

View File

@@ -4,6 +4,8 @@ namespace Modules\Internal\Transformers;
use Illuminate\Http\Resources\Json\JsonResource;
use function PHPSTORM_META\map;
class DoctorResource extends JsonResource
{
/**
@@ -19,35 +21,51 @@ class DoctorResource extends JsonResource
// 'his_dokter_id' => $this->practitionerRoles->meta,
'name' => $this->person->name,
'person_id' => $this->person->id,
'phone' => $this->person->phone,
'email' => $this->person->email,
'gender' => $this->person->gender == "L" ? 'Laki-laki' : 'Perempuan',
'address' => $this->person->currentAddress->text,
'phone' => $this->person->phone ?? null,
'email' => $this->person->email ?? null,
'birth_date' => $this->person->birth_date ?? null,
'birth_place' => $this->person->birth_place ?? null,
'gender' => $this->person->gender == "L" || $this->person->gender == "male" ? 'male' : 'female',
'address' => $this->person->currentAddress->text ?? null,
'organizations' => $this->practitionerRoles->unique('organization_id')->map(function ($practitionerRole) {
return [
'organization_id' => $practitionerRole->organization->id,
'organization_name' => $practitionerRole->organization->name,
];
}),
})->values(),
"specialties" => $this->practitionerRoles->unique('speciality_id')->map(function ($practitionerRole) {
return [
'specialty_id' => $practitionerRole->speciality->id,
'specialty_name' => $practitionerRole->speciality->name,
];
}),
"departemen" => $this->practitionerRoles->map(function ($practitionerRole) {
return [
'departemen_id' => $practitionerRole->meta->DepartemenID,
];
}),
'education' => $this->meta->education,
'experience' => $this->meta->work_experience,
'award' => $this->meta->award,
'keilmuan' => $this->meta->Keilmuan,
'tipe_dokter' => $this->meta->tipeDokter,
// "departemen" => $this->practitionerRoles->map(function ($practitionerRole) {
// return [
// 'departemen_id' => $practitionerRole->meta->DepartemenID ?? null,
// ];
// }) ?? null,
'education' => $this->meta->education ?? null,
'experience' => $this->meta->work_experience ?? null,
'award' => $this->meta->award ?? null,
'keilmuan' => $this->meta->Keilmuan ?? null,
'tipe_dokter' => $this->meta->tipeDokter ?? null,
];
$grouped = $this->collection($this->practitionerRoles)->groupBy('organization_id');
$grouped->transform(function ($items, $key) {
return [
'organization_id' => $key,
'specialities' => $items->map(function ($item) {
return [
'speciality_id' => $item->speciality->id,
];
}),
];
});
$doctor['practices'] = $grouped->toArray();
return $doctor;
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace Modules\Internal\Transformers;
use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;
class LivechatResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request
* @return array
*/
public function toArray($request)
{
$livechat = [
'id' => $this->nID,
'doctor_name' => isset($this->doctor->user->sFirstName) ? $this->doctor->user->sFirstName . ' ' . $this->doctor->user->sLastName : null,
'speciality' => $this->doctor->speciality->sKeterangan ?? null,
'health_care' => $this->healthCare->sHealthCare ?? null,
'date_appointment' => Carbon::parse($this->appointment->appointmentDetail->dTanggalAppointment)->format('d-m-Y')
. ' ' . $this->appointment->appointmentDetail->tTimeAppointment ?? null,
'status_appointment' => $this->appointment->status_name ?? null,
'date_created' => Carbon::parse($this->appointment->dCreateOn)->format('d-m-Y H:i:s') ?? null,
'patient_media' => $this->sMedia ?? null,
'doctor_media' => $this->sMediaDokter ?? null,
'appointment_media' => $this->appointment->sMedia ?? null,
'status_chat' => $this->status_name ?? null,
'payment_method' => $this->appointment->payment_method ?? null,
];
$start_time = $this->dStartTime;
$end_time = $this->dEndTime;
$data_duration = 0 . ' jam ' . 0 . ' menit ' . 0 . ' detik';
if ($start_time != null && $end_time != null) {
$duration = Carbon::parse($start_time)->diffInMinutes(Carbon::parse($end_time));
$hours = floor($duration / 60);
$minutes = $duration % 60;
$seconds = ($duration - ($hours * 60) - $minutes) * 60;
$data_duration = $hours . ' jam ' . $minutes . ' menit ' . $seconds . ' detik';
}
$livechat['duration'] = $data_duration;
$payment_detail = null;
if ($this->appointment->appointmentDetail->sPaymentDetails != null) {
$payment_detail = [
'payment_type' => $this->appointment->appointmentDetail->sPaymentDetails['payment_type'],
'transaction_time' => $this->appointment->appointmentDetail->sPaymentDetails['transaction_time'],
'gross_amount' => $this->appointment->appointmentDetail->sPaymentDetails['gross_amount'],
'currency' => $this->appointment->appointmentDetail->sPaymentDetails['currency'],
'status_message' => $this->appointment->appointmentDetail->sPaymentDetails['status_message'],
];
}
$livechat['payment_detail'] = $payment_detail;
return $livechat;
}
}

View File

@@ -14,17 +14,24 @@ class OrganizationResource extends JsonResource
*/
public function toArray($request)
{
$organization = [
'id' => $this->id,
'name' => $this->name,
'type' => $this->type,
'code' => $this->code,
'description' => $this->description,
'kodeRs' => $this->meta->kodeRs ?? null,
'kodeRs' => $this->meta->KodeRS ?? null,
'phone' => $this->meta->phone ?? null,
'lat' => $this->currentAddress->lat ?? null,
'lng' => $this->currentAddress->lng ?? null,
'address' => $this->currentAddress ?? null,
'address' => $this->currentAddress->text ?? null,
'province_id' => $this->currentAddress->province_id ?? null,
'city_id' => $this->currentAddress->city_id ?? null,
'district_id' => $this->currentAddress->district_id ?? null,
'village_id' => $this->currentAddress->village_id ?? null,
'postal_code' => $this->currentAddress->postal_code ?? null,
'active' => $this->status == 'active' ? 1 : 0,
];
return $organization;

View File

@@ -2,6 +2,7 @@
namespace App\Events;
use App\Models\ClaimRequest;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
@@ -14,14 +15,16 @@ class ClaimRequested
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $claim_request;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
public function __construct(ClaimRequest $claimRequest)
{
//
$this->claim_request = $claimRequest;
}
/**

View File

@@ -4,7 +4,10 @@ namespace App\Http\Controllers\Api\OLDLMS;
use App\Helpers\Helper;
use App\Http\Controllers\Controller;
use App\Http\Resources\OLDLMS\MemberResource;
use App\Models\Corporate;
use App\Models\Member;
use App\Rules\NikRule;
use App\Services\ClaimService;
use Illuminate\Http\Request;
@@ -169,4 +172,92 @@ class MembershipController extends Controller
return Helper::responseJson(data: $limits);
}
public function linkingRules(Request $request)
{
$corporates = Corporate::query()
->when($request->search, function ($q, $search) {
$q->where('name', 'LIKE', '%'.$search.'%');
})
->get();
return Helper::responseJson(data: $corporates);
}
public function linkingValidate(Request $request)
{
$request->validate([
'corporate_id' => 'required'
]);
$corporate = Corporate::findOrFail($request->corporate_id);
// Make Validation from Linking Rules
$linkingRulesArr = $corporate->linking_rules->toArray();
$validationRules = [];
foreach ($linkingRulesArr as $field) {
$rules = ['required']; // Default is required if in the linking_rules
if ($field == 'email') {
$rules[] = 'email';
}
if ($field == 'nric') {
$rules[] = new NikRule;
}
$validationRules[$field] = $rules;
}
$request->validate($validationRules);
$member = Member::query()
->when(in_array('nric', $linkingRulesArr), function($q) use ($request) {
$q->where('nric', $request->nric);
})
->when(in_array('member_id', $linkingRulesArr), function($q) use ($request) {
$q->where('member_id', $request->member_id);
})
->when(in_array('name', $linkingRulesArr), function($q) use ($request) {
$q->where('name', $request->name);
})
->when(in_array('dob', $linkingRulesArr), function($q) use ($request) {
$q->where('birth_date', $request->dob);
})
->when(in_array('phone', $linkingRulesArr), function($q) use ($request) {
$q->whereHas('person', function ($person) use ($request) {
$person->where('phone', $request->phone);
});
})
->when(in_array('email', $linkingRulesArr), function($q) use ($request) {
$q->where('email', $request->email);
})
->when(in_array('nik', $linkingRulesArr), function($q) use ($request) {
$q->whereHas('employeds', function ($employed) use ($request) {
$employed->where('corporate_id', $request->corporate_id)
->where('nik', $request->nik);
});
})
->with([
'memberPlans' => function ($memberPlan) {
$memberPlan->latest();
},
])
->first();
if ($member) {
return Helper::responseJson(data: MemberResource::make($member), message: 'Data Member ditemukan!');
}
return Helper::responseJson(data: [], message: 'Member Tidak ditemukan', statusCode: 404, status: 'error');
}
public function show($member_id)
{
$member = Member::where('member_id', $member_id)->firstOrFail();
$member->load(['currentPlan', 'memberPlans']);
$member->totalUsage = ClaimService::getMemberTotalUsage($member);
return Helper::responseJson(data: MemberResource::make($member));
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace App\Http\Controllers;
use App;
use App\Models\GeneratedDocument;
use Illuminate\Http\Request;
use PDF;
use Response;
class GeneratedDocumentController extends Controller
{
// Display Content from generated_documents to used by pdf generator (wkhtmltopdf)
public function show($id)
{
$document = GeneratedDocument::findOrFail($id);
return $document->html_content.$document->html_content.$document->html_content.$document->html_content.$document->html_content;
}
public function header(Request $request)
{
return '<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<pre>'.json_encode($request->toArray()).'
<table>
<tbody>
<tr>
<td style="width: 200px; background-color: #00be67;"> asdkjasnd </td>
<td style="width: 500px; background-color: #0a94e3;" align="center"> asjdkadsn </td>
<td style="width: 200px; background-color: #7b3f25" align="right"> qkjwenkqwjenkqwjen </td>
</tr>
</tbody>
</table>
</body>
</html>';
}
public function footer()
{
return "<h2>Footer Fatherfucker</h2>";
}
public function pdf($id)
{
// return 'fuck';
// $document = GeneratedDocument::findOrFail($id);
// $pdf = PDF::loadFile('http://localhost:8000/');
// $pdf = PDF::loadFile('http://aso-linksehat.local/');
// return $pdf->inline();
// dd(route('generated-document.show', $id));
// $pdf = PDF::loadFile(route('generated-document.show', $id));
// return $pdf->inline();
// $snappy = App::make('snappy.pdf');
// $html = '<h1>Bill</h1><p>You owe me money, dude.</p>';
// // $snappy->generateFromHtml($html, '/tmp/bill-123.pdf');
// // $snappy->generate('http://www.github.com', '/tmp/github.pdf');
// //Or output:
// return new Response(
// $snappy->getOutputFromHtml($html),
// 200,
// array(
// 'Content-Type' => 'application/pdf',
// 'Content-Disposition' => 'attachment; filename="file.pdf"'
// )
// );
$pdf = PDF::loadFile(route('generated-document.show', $id));
// $pdf->loadFile(route('generated-document.show', $id));
// $pdf->loadFile(route('generated-document.show', $id));
// $pdf->loadFile(route('generated-document.show', $id));
// $pdf->loadFile(route('generated-document.show', $id));
// $pdf->setPaper('a4')->setOrientation('landscape')->setOption('margin-bottom', 0);
// $pdf->setOption('header-html', route('pdf.header'));
// $pdf->setOption('footer-html', route('pdf.header'));
// $pdf->setOption('footer-center', 'asdasdasd');
// $pdf->setOption('footer-html', route('pdf.footer'));
// $pdf->loadHtml('asdasdasd');
return $pdf->inline();
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Http\Resources\OLDLMS;
use App\Services\ClaimService;
use Illuminate\Http\Resources\Json\JsonResource;
class MemberResource extends JsonResource
@@ -14,6 +15,32 @@ class MemberResource extends JsonResource
*/
public function toArray($request)
{
return parent::toArray($request);
// $data = parent::toArray($request);
$currentMemberPlan = $this->memberPlans?->first();
$data = [
'member_id' => $this->member_id,
'birth_date' => $this->birth_date,
'email' => $this->email,
'phone' => $this->person->phone ?? null,
'full_name' => $this->full_name,
'nric' => $this->nric,
'plan' => $currentMemberPlan ? [
'code' => $currentMemberPlan->plan->code ?? null,
'start' => $currentMemberPlan->start,
'end' => $currentMemberPlan->end,
'limit' => $this->currentPlan->limit_rules
] : null,
'policy_code' => $this->currentPolicy?->code ?? null,
'corporate' => [
'code' => $this->currentPolicy?->corporate->code ?? null,
'name' => $this->currentPolicy?->corporate->name,
'welcome_message' => $this->currentPolicy?->corporate?->welcome_message,
'help_text' => $this->currentPolicy?->corporate?->help_text,
'avatar_url' => $this->currentpolicy?->corporate?->avatar_url
],
'limit_usage' => $this->totalUsage ?? null
];
return $data;
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Listeners;
use App\Events\ClaimRequested;
use App\Models\User;
use App\Notifications\ClaimRequestedNotification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class NotifyClaimRequested
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param \App\Events\ClaimRequested $event
* @return void
*/
public function handle(ClaimRequested $event)
{
// TODO List Of User that should be notified about Claim that Requested
$user = User::first();
$user->notify(new ClaimRequestedNotification());
}
}

View File

@@ -18,8 +18,11 @@ use Illuminate\Support\Str;
class Claim extends Model
{
use HasFactory, Blameable, SoftDeletes;
protected static $code_prefix = 'CLM';
protected $fillable = [
'claim_request_id',
'code',
'member_id',
'total_claim',
@@ -54,7 +57,8 @@ class Claim extends Model
static::creating(function ($model) {
try {
$model->code = (string) Str::orderedUuid(); // generate uuid
$model->uuid = (string) Str::orderedUuid(); // generate uuid
$model->code = self::getNextCode();
} catch (\Exception $e) {
abort(500, $e->getMessage());
}
@@ -78,9 +82,9 @@ class Claim extends Model
'status' => $model->status
]);
if ($model->status == 'requested') {
ClaimRequested::dispatch($model);
}
// if ($model->status == 'requested') {
// ClaimRequested::dispatch($model);
// }
if ($model->status == 'received') {
ClaimReceived::dispatch($model);
@@ -104,6 +108,20 @@ class Claim extends Model
}
});
}
public static function getNextCode()
{
$last_number = self::withTrashed()->max('code');
$next_number = empty($last_number) ? 1 : ((int) explode('-', $last_number)[1] + 1);
return self::makeCode($next_number);
}
public static function makeCode($next_number)
{
return (string) self::$code_prefix .'-'. str_pad($next_number, 5, 0, STR_PAD_LEFT);
}
public function files()
{

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Models;
use App\Traits\Blameable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ClaimHistory extends Model
{
use HasFactory, Blameable;
public $fillable = [
'claim_id',
'title',
'description',
'type',
'parent_id',
'data',
'system_origin'
];
public static $types = [
'info',
'document-request',
'document-submit'
];
public function parent()
{
return $this->belongsTo(ClaimHistory::class, 'parent_id');
}
public function childs()
{
return $this->hasMany(Claimhistory::class, 'parent_id');
}
public function claim()
{
return $this->belongsTo(Claim::class, 'claim_id');
}
public function historiable()
{
return $this->morphTo();
}
}

View File

@@ -2,10 +2,13 @@
namespace App\Models;
use App\Events\ClaimReceived;
use App\Events\ClaimRequested;
use App\Traits\Blameable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Str;
class ClaimRequest extends Model
{
@@ -13,17 +16,91 @@ class ClaimRequest extends Model
protected static $code_prefix = 'CRQ';
public $fillable = [
'submission_date',
'member_id',
'status',
'claim_id'
];
protected $hidden = [
// 'created_at',
'updated_at',
'deleted_at',
'created_by',
'updated_by',
'deleted_by',
];
public static $status = [
'draft' => 'Draft',
'requested' => 'Requested',
'received' => 'Received',
'approved' => 'Approved',
'postpone' => 'Postpone',
'paid' => 'Paid',
'declined' => 'Declined'
];
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
try {
$model->uuid = (string) Str::orderedUuid(); // generate uuid
$model->code = self::getNextCode();
} catch (\Exception $e) {
abort(500, $e->getMessage());
}
});
static::created(function ($model) {
// try {
// if (!empty($model->status) && $model->status == 'requested') {
// $model->histories()->create([
// 'title' => 'New Claim Requested',
// 'description' => "Claim Requested for Member : {$model->member->member_id} - ({$model->member->full_name})",
// 'type' => 'info'
// ]);
// }
// } catch (\Exception $e) {
// abort(500, $e->getMessage());
// }
});
static::updated(function ($model) {
if ($model->hasChanges(['status'])) {
// if ($model->status == 'requested') {
// $model->histories()->create([
// 'title' => 'New Claim Requested',
// 'description' => "Claim Requested for Member : {$model->member->member_id} - ({$model->member->full_name})",
// 'type' => 'info'
// ]);
// }
// if ($model->status == 'received') {
// ClaimReceived::dispatch($model);
// }
// if ($model->status == 'approved') {
// ClaimApproved::dispatch($model);
// }
// if ($model->status == 'postpone') {
// ClaimPostpone::dispatch($model);
// }
// if ($model->status == 'paid') {
// ClaimPaid::dispatch($model);
// }
// if ($model->status == 'declined') {
// ClaimDeclined::dispatch($model);
// }
}
});
}
public static function getNextCode()
@@ -39,17 +116,26 @@ class ClaimRequest extends Model
return (string) self::$code_prefix .'-'. str_pad($next_number, 5, 0, STR_PAD_LEFT);
}
public $fillable = [
'submission_date',
'member_id',
'status'
];
public function claims()
{
return $this->hasMany(Claim::class, 'claim_request_id');
}
public function files()
{
return $this->morphMany(File::class, 'fileable');
}
public function generatedDocuments()
{
return $this->morphMany(GeneratedDocument::class, 'generated_documentable');
}
public function histories()
{
return $this->morphMany(ClaimHistory::class, 'historiable');
}
public function member()
{
return $this->belongsTo(Member::class, 'member_id', 'id');

View File

@@ -3,6 +3,7 @@
namespace App\Models;
use App\Traits\Blameable;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
@@ -23,7 +24,7 @@ class Corporate extends Model
];
protected $casts = [
'linking_rules' => 'array',
'linking_rules' => AsArrayObject::class,
];
protected $appends = [

View File

@@ -38,7 +38,9 @@ class File extends Model
'import-temp' => 'import-temp/',
'avatar' => 'user-avatar/',
'dataDiri' => 'data-diri/',
'claim' => 'claim/'
'claim-result' => 'claim/',
'claim-diagnosis' => 'claim/',
'claim-kondisi' => 'claim/',
];
public function fileable()

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use PDF;
class GeneratedDocument extends Model
{
use HasFactory;
public $fillable = [
'type',
'title',
'document_type',
'html_content',
'system_origin',
'parent_id'
];
public function parent()
{
return $this->belongsTo(GeneratedDocument::class, 'parent_id');
}
public function childs()
{
return $this->hasMany(GeneratedDocument::class, 'parent_id');
}
public function generated_documentable()
{
return $this->morphTo();
}
public function makePdf()
{
return PDF::loadFile(route('generated-document.show', $this->id));
}
}

View File

@@ -141,7 +141,8 @@ class Member extends Model
public function currentPlan()
{
return $this->hasOneThrough(Plan::class, MemberPlan::class, 'member_id', 'id', 'id', 'plan_id')->latest();
return $this->hasOneThrough(Plan::class, MemberPlan::class, 'member_id', 'id', 'id', 'plan_id')
->latest(); // TODO Fix This
}
public function policies()

View File

@@ -31,4 +31,17 @@ class MemberPlan extends Model
{
return $this->belongsTo(CorporatePlan::class, 'plan_id', 'code');
}
public function plan()
{
return $this->belongsTo(Plan::class, 'plan_id');
}
public function scopeActive($q)
{
return $q
->where('start', '<', now())
->where('end', '>', now())
->latest();
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Models\OLDLMS;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
@@ -14,12 +15,99 @@ class Appointment extends Model
const UPDATED_AT = 'dUpdateOn';
const DELETED_AT = 'dDeleteOn';
public $sStatusNames = [
0 => 'Menunggu Pembayaran',
1 => 'Pembayaran Terkonfirmasi', // Pembayaran Diterima
2 => 'Ditolak',
3 => 'Dibatalkan', // Canceled
4 => 'Expired',
];
public $sPaymentMethodName = [
1 => 'Pribadi',
2 => 'On-Site Payment',
3 => 'OVO',
4 => 'Asuransi',
5 => 'Voucher',
];
public $nIDJenisBookingNames = [
1 => 'Rawat Jalan',
2 => 'Telekonsultasi',
3 => 'Chat Sekarang'
];
protected $connection = 'oldlms';
protected $table = 'tx_appointment';
public function detail()
protected $primaryKey = 'nID';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'nID',
'nIDDokter',
'nIDUser',
'sStatus',
'dCreateOn',
'dUpdateOn',
'dDeleteOn',
];
protected $appends = [
'status_name',
'payment_method',
'type'
];
protected function statusName(): Attribute
{
return $this->hasOne(AppointmentDetail::class, '');
return Attribute::make(
get: function ($value) {
return $this->sStatusNames[$this->sStatus] ?? '-';
},
);
}
protected function paymentMethod(): Attribute
{
return Attribute::make(
get: function ($value) {
return $this->sPaymentMethodName[$this->sPaymentMethod] ?? '-';
},
);
}
protected function type(): Attribute
{
return Attribute::make(
get: function($value) {
return $this->nIDJenisBookingNames[$this->nIDJenisBooking] ?? '-';
}
);
}
public function appointmentDetail()
{
return $this->hasOne(AppointmentDetail::class, 'nIDAppointment', 'nID');
}
public function doctor()
{
return $this->belongsTo(Dokter::class, 'nIDDokter', 'nID');
}
public function user()
{
return $this->belongsTo(User::class, 'nIDUser', 'nID');
}
public function healthCare()
{
return $this->belongsTo(Healthcare::class, 'nIDHealthCare', 'nID');
}
}

View File

@@ -8,4 +8,19 @@ use Illuminate\Database\Eloquent\Model;
class AppointmentDetail extends Model
{
use HasFactory;
const CREATED_AT = 'dCreateOn';
const UPDATED_AT = 'dUpdateOn';
const DELETED_AT = 'dDeleteOn';
protected $connection = 'oldlms';
protected $table = 'tx_appointment_detail';
protected $casts = [
'sPaymentDetails' => 'array',
];
public function appointment()
{
return $this->belongsTo(Appointment::class, 'nIDAppointment', 'nID');
}
}

View File

@@ -24,4 +24,14 @@ class Dokter extends Model
{
return $this->hasMany(JadwalDokter::class, 'nIDDokter', 'nID');
}
public function user()
{
return $this->belongsTo(User::class, 'nIDUser', 'nID');
}
public function speciality()
{
return $this->belongsTo(Speciality::class, 'nIDSpesialis', 'nID');
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Models\OLDLMS;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Kota extends Model
{
use HasFactory, SoftDeletes;
const CREATED_AT = 'dCreateOn';
const UPDATED_AT = 'dUpdateOn';
const DELETED_AT = 'dDeleteOn';
protected $connection = 'oldlms';
protected $table = 'tm_kota';
protected $primaryKey = 'nID';
public function provinsi()
{
return $this->belongsTo(Provinsi::class, 'nIDProvinsi', 'nID');
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Models\OLDLMS;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
class Livechat extends Model
{
use HasFactory;
public $sStatusNames = [
0 => 'Menunggu Konfirmasi',
1 => 'Diterima',
2 => 'Ditolak',
3 => 'Selesai',
4 => 'Expired',
];
const CREATED_AT = 'dCreateOn';
const UPDATED_AT = 'dUpdateOn';
const DELETED_AT = 'dDeleteOn';
protected $connection = 'oldlms';
protected $table = 'tx_livechat';
protected $appends = [
'status_name',
];
protected function statusName(): Attribute
{
return Attribute::make(
get: function ($value) {
return $this->sStatusNames[$this->sStatus] ?? '-';
},
);
}
public function user()
{
return $this->belongsTo(User::class, 'nIDUser', 'nID');
}
public function doctor()
{
return $this->belongsTo(Dokter::class, 'nIDDokter', 'nID');
}
public function appointment()
{
return $this->belongsTo(Appointment::class, 'nIDAppointment', 'nID');
}
public function healthCare()
{
return $this->belongsTo(Healthcare::class, 'nIDHealthCare', 'nID');
}
}

View File

@@ -0,0 +1,22 @@
<?php
namespace App\Models\OLDLMS;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Provinsi extends Model
{
use HasFactory, SoftDeletes;
const CREATED_AT = 'dCreateOn';
const UPDATED_AT = 'dUpdateOn';
const DELETED_AT = 'dDeleteOn';
protected $connection = 'oldlms';
protected $table = 'tm_provinsi';
protected $primaryKey = 'nID';
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Models\OLDLMS;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Speciality extends Model
{
use HasFactory;
const CREATED_AT = 'dCreateOn';
const UPDATED_AT = 'dUpdateOn';
const DELETED_AT = 'dDeleteOn';
protected $connection = 'oldlms';
protected $table = 'tm_spesialis';
protected $primaryKey = 'nID';
public function dokter()
{
return $this->hasMany(Dokter::class, 'nIDSpesialis', 'nID');
}
}

View File

@@ -2,10 +2,46 @@
namespace App\Models\OLDLMS;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class User extends Model
{
use HasFactory;
use HasFactory, SoftDeletes;
const CREATED_AT = 'dCreateOn';
const UPDATED_AT = 'dUpdateOn';
const DELETED_AT = 'dDeleteOn';
protected $connection = 'oldlms';
protected $table = 'tm_users';
protected $appends = [
'full_name',
];
protected function fullName(): Attribute
{
return Attribute::make(
get: function ($value) {
$names = [];
if (!empty($this->sFirstName)) {
array_push($names, $this->sFirstName);
}
if (!empty($this->sLastName)) {
array_push($names, $this->sLastName);
}
return implode(' ', $names);
}
);
}
public function detail()
{
return $this->hasOne(UserDetail::class, 'nIDUser', 'nID');
}
}

View File

@@ -8,4 +8,13 @@ use Illuminate\Database\Eloquent\Model;
class UserDetail extends Model
{
use HasFactory;
const CREATED_AT = 'dCreateOn';
const UPDATED_AT = 'dUpdateOn';
const DELETED_AT = 'dDeleteOn';
protected $connection = 'oldlms';
protected $table = 'tm_users_detail';
}

View File

@@ -114,6 +114,11 @@ class Person extends Model
return $this->hasOne(User::class, 'person_id');
}
public function practitioner()
{
return $this->hasOne(Practitioner::class, 'person_id');
}
public function appointmentParticipantables()
{
return $this->morphMany(AppointmentParticipant::class, 'participantable');

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class ClaimRequestedNotification extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['database'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', url('/'))
->line('Thank you for using our application!');
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
'asdasd' => 'asdasdsd'
];
}
}

View File

@@ -2,7 +2,9 @@
namespace App\Providers;
use App\Rules\NikRule;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;
use Str;

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Providers;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class ClaimRequested
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@@ -4,6 +4,7 @@ namespace App\Providers;
use App\Events\ClaimApproved;
use App\Listeners\LogClaimJournal;
use App\Listeners\NotifyClaimRequested;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
@@ -21,9 +22,14 @@ class EventServiceProvider extends ServiceProvider
SendEmailVerificationNotification::class,
],
ClaimRequested::class => [
NotifyClaimRequested::class,
],
ClaimApproved::class => [
LogClaimJournal::class,
]
],
];
/**
@@ -43,6 +49,6 @@ class EventServiceProvider extends ServiceProvider
*/
public function shouldDiscoverEvents()
{
return false;
return true;
}
}

66
app/Rules/NikRule.php Normal file
View File

@@ -0,0 +1,66 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class NikRule implements Rule
{
/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
// The NIK is a 16-digit number
if (!preg_match('/^[0-9]{16}$/', $value)) {
return false;
}
// // The first 6 digits represent the person's birth date in the format of YYMMDD
// $year = substr($value, 6, 2);
// $month = substr($value, 8, 2);
// $day = substr($value, 10, 2);
// // dd($year, $month, $day);
// // dd(checkdate($month, $day, "19{$year}"));
// if (!checkdate($month, $day, "19{$year}")) {
// return false;
// }
// // The next 2 digits represent the place of birth (province/city code)
// $provinceCode = substr($value, 6, 2);
// // The next 2 digits represent the person's gender (odd for male, even for female)
// $genderCode = substr($value, 14, 1);
// // The last 4 digits represent the sequence number of the person's birth in that day
// $sequenceNumber = substr($value, 12, 4);
return true;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return ':attribute bukan valid NIK Indonesia.';
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Services;
use App\Events\ClaimApproved;
use App\Events\ClaimRequested;
use App\Models\Claim;
use App\Models\ClaimRequest;
use App\Models\Icd;
use App\Models\Member;
use Carbon\Carbon;
use DB;
use Str;
class ClaimRequestService{
public static function storeClaimRequest($member, $submissionDate = null, $status = 'requested')
{
try {
DB::beginTransaction();
$claimRequestData = [
'member_id' => $member->id,
'submission_date' => $submissionDate ?? now(),
'status' => $status
];
$claimRequest = ClaimRequest::create($claimRequestData);
DB::commit();
return $claimRequest;
} catch (\Exception $error) {
DB::rollBack();
throw new \Exception($error);
}
}
}

View File

@@ -119,8 +119,6 @@ class ClaimService{
->active()
->first();
// dd($benefit->toArray());
// dd(compact(['plan', 'policy', 'corporate', 'benefit']));
$limits = [
'total_limit' => $corporateBenefit ? $corporateBenefit->limit_amount : 0,
'frequency_limit_name' => $corporateBenefit ? $corporateBenefit->max_frequency_period_name : null,
@@ -163,22 +161,21 @@ class ClaimService{
return $limits;
}
public static function storeClaim($member, $diagnosis, $totalClaim, $benefit, $status)
public static function storeClaim($member, $diagnosis = null, $totalClaim = null, $benefit = null, $status = 'requested', $claimRequest = null)
{
try {
DB::beginTransaction();
$claimData = [
'member_id' => $member->id,
'claim_request_id' => $claimRequest->id ?? null,
'diagnosis_id' => $diagnosis->id ?? null,
'total_claim' => $totalClaim,
'total_claim' => $totalClaim ?? null,
'currency' => 'IDR',
'plan_id' => $member->currentPlan->id,
'benefit_id' => $benefit->id,
'plan_id' => $member->currentPlan->id ?? null,
'benefit_id' => $benefit->id ?? null,
'status' => $status
];
// $claimData[$status.'_at'] = now();
// $claimData[$status.'_by'] = auth()->user()->id ?? null;
$claim = Claim::create($claimData);

View File

@@ -15,7 +15,9 @@ return new class extends Migration
{
Schema::create('claims', function (Blueprint $table) {
$table->id();
$table->uuid('uuid');
$table->string('code')->index();
$table->foreignId('claim_request_id')->nullable()->index();
$table->foreignId('member_id')->index();
// $table->foreignId('diagnosis_id')->index()->nullable();
$table->string('currency');

View File

@@ -15,6 +15,7 @@ return new class extends Migration
{
Schema::create('claim_requests', function (Blueprint $table) {
$table->id();
$table->uuid('uuid');
$table->string('code')->index();
$table->dateTime('submission_date')->nullable();
$table->foreignId('member_id');

View File

@@ -0,0 +1,43 @@
<?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('claim_histories', function (Blueprint $table) {
$table->id();
$table->morphs('historiable');
$table->string('title')->nullable();
$table->string('description')->nullable();
$table->string('type');
$table->foreignId('parent_id')->nullable();
$table->text('data')->nullable();
$table->string('system_origin')->nullable();
$table->timestamps();
$table->softDeletes();
$table->unsignedBigInteger('created_by')->nullable()->index();
$table->unsignedBigInteger('updated_by')->nullable()->index();
$table->unsignedBigInteger('deleted_by')->nullable()->index();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('claim_histories');
}
};

View File

@@ -0,0 +1,44 @@
<?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('generated_documents', function (Blueprint $table) {
$table->id();
$table->morphs('generated_documentable', 'generated_document_index');
$table->string('type');
$table->string('title');
$table->string('document_type')->nullable();
$table->string('system_origin');
$table->text('html_content')->nullable();
$table->text('data')->nullable();
$table->foreignId('parent_id')->nullable();
$table->timestamps();
$table->softDeletes();
$table->unsignedBigInteger('created_by')->nullable()->index();
$table->unsignedBigInteger('updated_by')->nullable()->index();
$table->unsignedBigInteger('deleted_by')->nullable()->index();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('generated_documents');
}
};

View File

@@ -0,0 +1,35 @@
<?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('notifications', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('type');
$table->morphs('notifiable');
$table->text('data');
$table->timestamp('read_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('notifications');
}
};

View File

@@ -0,0 +1,85 @@
<?php
namespace Database\Seeders;
use App\Models\OLDLMS\Healthcare;
use App\Models\OLDLMS\Kota;
use Box\Spout\Reader\Common\Creator\ReaderEntityFactory;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use Str;
class IngestProviderSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$file_path = resource_path('files/providers.csv');
$reader = ReaderEntityFactory::createReaderFromFile($file_path);
$reader->open($file_path);
$chunks = [];
$time = now();
foreach ($reader->getSheetIterator() as $sheet) {
foreach ($sheet->getRowIterator() as $index => $row) {
if ($index != 1) {
$row_data = [
'timezone' => 'Asia/Jakarta',
'nIDType' => 2,
'nIDCountry' => 1,
'sMoto' => 'Memberikan Kesehatan Pelayanan Terbaik',
'sStatus' => 1,
'sCreateBy' => 9999999,
];
$row_data['nIDHealthCareCategory'] = 1; // RS
foreach ($row->getCells() as $cell_index => $cell) {
if ($cell_index == 2) {
$namaKota = $cell->getValue();
$kota = Kota::where('sKota', 'LIKE', '%'.$namaKota.'%')->first();
if ($kota) {
$row_data['nIDKota'] = $kota->nID;
$row_data['nIDProvinsi'] = $kota->nIDProvinsi;
}
}
else if ($cell_index == 3) {
$row_data['sHealthCare'] = $cell->getValue();
$row_data['sSlug'] = Str::slug($row_data['sHealthCare']);
}
else if ($cell_index == 4) {
$row_data['sAlamat'] = $cell->getValue();
}
else if ($cell_index == 5) {
$row_data['sTelp'] = $cell->getValue();
}
}
// $chunks[] = $row_data;
try {
// Transaction
Healthcare::create($row_data);
}
catch(\Exception $e) {
dd($row_data);
}
}
if ($chunks && count($chunks) == 100) {
Healthcare::insert($chunks);
$chunks = [];
}
}
}
if ($chunks && count($chunks) > 0) {
Healthcare::insert($chunks);
$chunks = [];
}
}
}

View File

@@ -16,10 +16,7 @@ import { UserCurrentCorporateContext } from '../../contexts/UserCurrentCorporate
// ----------------------------------------------------------------------
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: '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: '11:00 WIB' },
{ info: 'Mohon lengkapi dokumen Alison Born', date: 'Selasa, 13 Februari 23', time: '09:43 WIB' },
];
// ----------------------------------------------------------------------

View File

@@ -24,10 +24,7 @@ const ItemStyle = styled(Card)(({ theme }) => ({
}));
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: '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: '11:00 WIB' },
{ info: 'Mohon lengkapi dokumen Alison Born', date: 'Selasa, 13 Februari 23', time: '09:43 WIB' },
];
// ----------------------------------------------------------------------

View File

@@ -0,0 +1,7 @@
GENERATE_SOURCEMAP=false
PORT=8083
REACT_APP_HOST_API_URL="http://localhost:8000"
VITE_API_URL="http://localhost:8000/api/internal"

View File

@@ -0,0 +1,296 @@
// @mui
import {
Button,
Box,
Stepper,
Step,
StepLabel,
Card,
Typography,
Divider,
Stack,
CircularProgress,
} from '@mui/material';
import { Add } from '@mui/icons-material';
// components
import MuiDialog from '@/components/MuiDialog';
// theme
import palette from '@/theme/palette';
// React
import { ReactElement, useEffect, useState } from 'react';
import { fDate } from '@/utils/formatTime';
import { addMinutes, format } from 'date-fns';
import { LoadingButton } from '@mui/lab';
import { enqueueSnackbar } from 'notistack';
import Iconify from '../Iconify';
type DataContent = {
claim: object;
isLoading: boolean;
handleDownloadLog: void;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContent[];
};
const steps = ['Review', 'Approval', 'Disbursement'];
const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
const claim = data.claim ?? null;
// ---------------------------------------------
// Step
const [currentStep, setCurrentStep] = useState(0);
useEffect(
function () {
if (claim?.status == 'requested') {
setCurrentStep(0);
}
if (claim?.status == 'approved') {
setCurrentStep(1);
}
if (claim?.status == 'closed') {
setCurrentStep(2);
}
},
[data]
);
// ----------------------------------------------------
// Date Stamp
let currentDate = null;
// ----------------------------------------------------
// Download LOG
const [loadingDownloadLog, setLoadingDownloadLog] = useState(false);
const handleDownloadLog = async (claimRequest) => {
setLoadingDownloadLog(true);
await data.handleDownloadLog(claimRequest).then(() => {
setLoadingDownloadLog(false);
});
};
// ----------------------------------------------------
// Handle Upload Invoice
const handleUploadInvoice = () => {
enqueueSnackbar('Something went wrong, please contact Link Medis Sehat', { variant: 'error' });
};
const getContent = () => (
<>
{data.isLoading && (
<Stack alignItems="center" justifyContent="space-between" sx={{ p: 4 }}>
<CircularProgress />
</Stack>
)}
{!data.isLoading && (
<>
<Stack
alignItems="center"
justifyContent="space-between"
direction="row"
sx={{ marginTop: 1 }}
>
<Typography variant="subtitle1" sx={{ height: 'max-content' }}>
Claim Request
</Typography>
<Stack>
<Typography variant="caption">Submission date</Typography>
{/* {JSON.stringify(data)} */}
<Typography variant="caption">
{claim.created_at && fDate(claim.created_at)}
</Typography>
</Stack>
</Stack>
<Box sx={{ width: '100%', marginTop: 2 }}>
<Stepper alternativeLabel activeStep={currentStep ?? 0}>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
</Box>
{/* { claim.status == 'approved' && (
<Stack sx={{ marginTop: 4}}>
<LoadingButton loading={false}
variant="contained"
startIcon={<Add />}
fullWidth
// sx={{ typography: 'subtitle2', borderColor: '#F5F5F5' }}
onClick={() => {handleUploadInvoice()}}
>
Upload Invoice
</LoadingButton>
</Stack>
)} */}
{claim.histories_by_date &&
claim.histories_by_date.map((historiesByDate) => (
<Stack key={historiesByDate.date}>
<Stack marginTop={2}>
<Typography variant="subtitle1" paddingY={2}>
{fDate(historiesByDate.date)}
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
{historiesByDate.histories &&
historiesByDate.histories.map((history) => (
<Stack key={history.id}>
{/* ---------------------------------TYPE INFO------------------------------------ */}
<Card sx={{ paddingY: 2, paddingX: 3 }}>
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Typography variant="body1">
{fDate(history.created_at, 'HH:mm')} WIB
</Typography>
<Typography
sx={{
backgroundColor: palette.light.warning.lighter,
color: palette.light.warning.dark,
borderColor: palette.light.warning.dark,
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Request
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
{history.title}
</Typography>
<Typography
variant="caption"
color="#757575"
sx={{ marginTop: 2, marginBottom: 1 }}
>
{history.description}
</Typography>
</Stack>
</Card>
</Stack>
))}
</Stack>
</Stack>
</Stack>
))}
<Stack direction="row" spacing={2} sx={{ marginTop: 2 }}>
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
{/* ---------------------------------TYPE INFO------------------------------------ */}
<Card sx={{ paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill"></Iconify> Dokumen Kelengkapan
</Typography>
{/* <Typography
sx={{
backgroundColor: palette.light.warning.lighter,
color: palette.light.warning.dark,
borderColor: palette.light.warning.dark,
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Dokumen
</Typography> */}
</Stack>
<Divider sx={{ marginY: 2 }} />
<Typography fontWeight="600">Kondisi</Typography>
<Stack>
<Stack
// divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{claim.files_by_type?.claim_kondisi &&
claim.files_by_type.claim_kondisi.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<a href={file.url} target="_blank" style={{ textDecoration: 'none' }}>
<Typography sx={{ color: 'text.secondary' }} variant="subtitle2">
- {file.name}
</Typography>
</a>
</Stack>
))}
</Stack>
<Typography fontWeight="600">Diagnosa</Typography>
<Stack
// divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{claim.files_by_type?.claim_diagnosis &&
claim.files_by_type.claim_diagnosis.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<a href={file.url} target="_blank" style={{ textDecoration: 'none' }}>
<Typography sx={{ color: 'text.secondary' }} variant="subtitle2">
- {file.name}
</Typography>
</a>
</Stack>
))}
</Stack>
<Typography fontWeight="600">Hasil</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{claim.files_by_type?.result &&
claim.files_by_type.result.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<a href={file.url} target="_blank" style={{ textDecoration: 'none' }}>
<Typography sx={{ color: 'text.secondary' }} variant="subtitle2">
- {file.name}
</Typography>
</a>
</Stack>
))}
</Stack>
</Stack>
</Card>
</Stack>
</Stack>
</>
)}
</>
);
return (
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
/>
);
};
export default DialogDetailClaim;

View File

@@ -76,6 +76,13 @@ const navConfig = [
title: 'CUSTOMER SERVICES',
children: [{ title: 'Request', path: '/cs-request' }],
},
{
title: 'REPORT',
children: [
{ title: 'Appointment', path: '/report/appointments' },
{ title: 'Live Chat', path: '/report/live-chat' },
],
},
{
title: 'USER MANAGEMENT',
path: '/users',

View File

@@ -36,12 +36,12 @@ import { LoadingButton } from '@mui/lab';
import { enqueueSnackbar } from 'notistack';
import { Divider } from '@mui/material';
import Iconify from '@/components/Iconify';
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
// import LoadingButton from '@/theme/overrides/LoadingButton';
export default function List() {
const [searchParams, setSearchParams] = useSearchParams();
const [importResult, setImportResult] = useState(null);
const navigate = useNavigate();
function SearchInput(props: any) {
// SEARCH
@@ -184,6 +184,16 @@ export default function List() {
<TableCell align="left">{row.service_type}</TableCell>
<TableCell align="right"><Chip label={row.status}/></TableCell>
<TableCell align="right">{ row.status == 'requested' && (<LoadingButton loading={loadingApprove} variant="outlined" onClick={() => {handleApprove(row)}}>Approve</LoadingButton> )}</TableCell>
<TableCell>
<IconButton
onClick={() => {
handleShowClaim(row);
}}
>
<Iconify icon="eva:eye-fill" />
</IconButton>
</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
@@ -278,6 +288,33 @@ export default function List() {
);
}
// ---------------------------------------------------------
// Dialog Detail Claim Request
const [openDialogDetailClaim, setOpenDialogDetailClaim] = useState(false);
const [loadingClaimDetail, setLoadingClaimDetail] = useState(true);
const [currentClaim, setCurrentClaim] = useState(null);
function handleShowClaim(claimRequest) {
setLoadingClaimDetail(true);
setOpenDialogDetailClaim(true);
axios.get(`/claim-requests/${claimRequest.id}`)
.then(({data}) => {
setCurrentClaim(data.data);
setLoadingClaimDetail(false);
})
.catch((err) => {
enqueueSnackbar(err.message, {variant: 'error'})
})
}
function handleDownloadLog() {
}
return (
<Card>
<ImportForm />
@@ -289,6 +326,14 @@ export default function List() {
handlePageChange={handlePageChange}
TableContent={<TableContent />}
/>
<DialogDetailClaim
openDialog={openDialogDetailClaim}
setOpenDialog={setOpenDialogDetailClaim}
title={{ name: 'Claim Request Detail' }}
data={{ claim: currentClaim, isLoading: loadingClaimDetail, handleDownloadLog }}
></DialogDetailClaim>
</Card>
);
}

View File

@@ -100,7 +100,7 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
policy_stop_service_net: currentCorporate?.current_policy?.minimal_stop_service_net || 0,
policy_start: currentCorporate?.current_policy?.start || '',
policy_end: currentCorporate?.current_policy?.end || '',
linking_rules: currentCorporate?.linking_rules || ['nrik', 'nik', 'member_id'],
linking_rules: currentCorporate?.linking_rules || ['nric', 'nik', 'member_id'],
type: currentCorporate?.type || 'corporate',
logo: currentCorporate?.logo || '',
}),
@@ -174,6 +174,8 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
formData.append('policy_end', data.policy_end);
formData.append('linking_rules', data.linking_rules);
console.log('MOTHERFUCKER', data.linking_rules)
if (!isEdit) {
const response = await axios.post('/corporates', formData);
} else {
@@ -269,7 +271,7 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
const linking_rules_checkbox_name = 'linking_rules';
const linking_tools = [
{
value: 'nrik',
value: 'nric',
label: 'No. KTP',
},
{
@@ -425,6 +427,7 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
Linking Rules
</Typography>
<Stack>
{JSON.stringify(getValues('linking_rules'))}
<RHFCustomMultiCheckbox name="linking_rules" options={linking_tools} />
</Stack>
</Stack>

View File

@@ -46,6 +46,7 @@ import { Member } from '../../../@types/member';
import BasePagination from '../../../components/BasePagination';
import { enqueueSnackbar } from 'notistack';
import { LoadingButton } from '@mui/lab';
import DialogLog from './sections/DialogLog';
export default function CorporatePlanList() {
const { themeStretch } = useSettings();
@@ -198,19 +199,17 @@ export default function CorporatePlanList() {
enqueueSnackbar('No File Selected', { variant: 'warning' });
}
};
const handleGetTemplate = (type :string) => {
axios.get('corporates/import-document-example/' + type)
.then((response) => {
const link = document.createElement('a');
link.href = response.data.data.file_url;
link.setAttribute('download', response.data.data.file_name);
document.body.appendChild(link);
link.click();
handleClose();
})
}
const handleGetTemplate = (type: string) => {
axios.get('corporates/import-document-example/' + type).then((response) => {
const link = document.createElement('a');
link.href = response.data.data.file_url;
link.setAttribute('download', response.data.data.file_name);
document.body.appendChild(link);
link.click();
handleClose();
});
};
return (
<div>
@@ -248,7 +247,13 @@ export default function CorporatePlanList() {
}}
>
<MenuItem onClick={handleImportButton}>Import</MenuItem>
<MenuItem onClick={() => {handleGetTemplate('member')}}>Download Template</MenuItem>
<MenuItem
onClick={() => {
handleGetTemplate('member');
}}
>
Download Template
</MenuItem>
</Menu>
</Stack>
)}
@@ -329,6 +334,15 @@ export default function CorporatePlanList() {
const { row } = props;
const [open, setOpen] = React.useState(false);
const [loadingLog, setLoadingLog] = React.useState(false);
const [dialogLogOpen, setDialogLogOpen] = React.useState(false);
// useEffect(function () {
// if (row.full_name == 'Pajri') {
// setDialogLogOpen(true);
// console.log('fuck');
// }
// }, []);
const handleActivate = (model: any, status: string) => {
axios
.put(`/members/${row.id}/activation`, {
@@ -355,33 +369,7 @@ export default function CorporatePlanList() {
);
});
};
const handleDownloadLog = (row: ReturnType<typeof createData>) => {
setLoadingLog(true);
axios.get(`generate-log/${row.id}`, {
responseType: 'blob'
})
.then((response) => {
window.open(URL.createObjectURL(response.data));
// const content = response.headers['content-type'];
// download(response.data, file.file_name, content);
// const link = document.createElement('a');
// console.log(response.data);
// link.href = response.data.data.file_url;
// link.setAttribute('download', response.data.data.file_name);
// document.body.appendChild(link);
// link.click();
setLoadingLog(false);
})
// .then((blobFile) => {
// new File([blobFile], 'asdads.pdf', { type: blobFile.type })
// setLoadingLog(false);
// })
.catch((response) => {
enqueueSnackbar(response.message, {variant: 'error'})
setLoadingLog(false);
})
}
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
@@ -546,31 +534,29 @@ export default function CorporatePlanList() {
</Grid>
<Grid>
<LoadingButton
id="upload-button"
variant="outlined"
startIcon={<InsertDriveFileIcon />}
// sx={{ p: 1.8 }}
onClick={() => {handleDownloadLog(row)}}
loading={loadingLog}
>
Download LOG
</LoadingButton>
<LoadingButton
id="upload-button"
variant="outlined"
startIcon={<InsertDriveFileIcon />}
// sx={{ p: 1.8 }}
// onClick={() => {handleDownloadLog(row)}}
onClick={() => {
setDialogLogOpen(true);
}}
loading={loadingLog}
>
Download LOG
</LoadingButton>
</Grid>
{/* <Typography sx={{ fontWeight: '600', mb: 1, mt: 2 }}>Sub Corporate</Typography>
<Grid container>
<Grid item xs={12}>
<Grid container>
<Grid item xs={6}>
Sub Corporates (asdasdasdasd)
</Grid>
<Grid item xs={6}>
: qweqweqweqwe
</Grid>
</Grid>
</Grid>
</Grid> */}
<DialogLog
title={{
name: `Generate LOG - ${row.full_name}`,
}}
openDialog={dialogLogOpen}
setOpenDialog={setDialogLogOpen}
data={{ member: row }}
></DialogLog>
</Box>
</Collapse>
</TableCell>

View File

@@ -0,0 +1,203 @@
// react
import { ReactElement, useEffect, useState } from 'react';
// mui
import {
Card,
Checkbox,
Divider,
Grid,
Input,
Link,
Stack,
Table,
TableCell,
TableContainer,
TableRow,
Typography,
} from '@mui/material';
import { styled } from '@mui/material/styles';
// Component
import MuiDialog from '@/components/MuiDialog';
import { Box } from '@mui/material';
import { TextField } from '@mui/material';
import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { fPostFormat } from '@/utils/formatTime';
import { LoadingButton } from '@mui/lab';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
import axios from '@/utils/axios';
import { enqueueSnackbar } from 'notistack';
type DataContent = {
info: string;
date: string;
time: string;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContent[];
};
const ItemNotificationStyle = styled(Card)(({ theme }) => ({
boxShadow: 'none',
padding: theme.spacing(1),
borderRadius: 0.5,
color: 'black',
}));
const DialogLog = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
const [openDialogClaim, setOpenDialogClaim] = useState(false);
const [dialogTitleClaim, setDialogTitleClaim] = useState('');
const [dateOfAdmission, setDateOfAdmission] = useState(new Date());
const [checkedBenefitIds, setCheckedBenefitIds] = useState([]);
const [benefitIds, setBenefitIds] = useState([]);
const [loadingLog, setLoadingLog] = useState(false);
useEffect(() => {
setBenefitIds(data.member.current_plan?.benefits.map((benefit) => benefit.id))
setCheckedBenefitIds(benefitIds)
console.log('Check All', benefitIds, 'X', data.member.current_plan?.benefits.map((benefit) => benefit.id))
}, [])
const clickHandler = () => {
setDialogTitleClaim('Claim Details');
setOpenDialogClaim(true);
};
const handleCheckAll = (event) => {
if (event.target.checked) {
setCheckedBenefitIds(benefitIds)
} else {
setCheckedBenefitIds([])
}
}
const handleCheckChange = (event, benefit) => {
if ( event.target.checked ) {
setCheckedBenefitIds([...checkedBenefitIds, benefit.id])
} else {
// setCheckedBenefitIds([])
setCheckedBenefitIds(checkedBenefitIds.filter((benefitId) => benefitId !== benefit.id))
}
}
const handleDownloadLog = (row) => {
setLoadingLog(true);
axios
.post(`generate-log/${row.id}`, {
date_of_admission : dateOfAdmission,
benefit_ids : checkedBenefitIds
}, {
responseType: 'blob',
})
.then((response) => {
window.open(URL.createObjectURL(response.data));
setLoadingLog(false);
setOpenDialog(false);
})
.catch((response) => {
enqueueSnackbar(response.message, { variant: 'error' });
setLoadingLog(false);
});
}
const getContent = () => (
<Stack sx={{ marginTop: 2 }}>
<ItemNotificationStyle>
<Stack>
<Grid container spacing={2}>
<Grid item xs={12}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DesktopDatePicker
inputFormat="dd/MM/Y"
value={dateOfAdmission}
onChange={(value) => {
setDateOfAdmission(new Date(fPostFormat(value)));
// console.log('value')
}}
renderInput={(params) => (
<TextField
{...params}
fullWidth
label="Date of Admission"
placeholder="dd/mm/yyyy"
/>
)}
/>
</LocalizationProvider>
</Grid>
<Grid item xs={12} sx={{marginTop: 2}}>
<Stack direction="row" alignItems="center" justifyContent={'space-between'}>
<Typography variant="body1" fontWeight={800}>List Of Benefit</Typography>
<Stack direction="row" alignItems="center">
<Typography>All</Typography>
<Checkbox onChange={handleCheckAll} checked={benefitIds.length == checkedBenefitIds.length}/>
</Stack>
</Stack>
</Grid>
<Grid item xs={12}>
<Stack divider={<Divider flexItem />}>
{ data.member.current_plan?.benefits && (
data.member.current_plan?.benefits.map((benefit, index) => (
<Stack direction="row" alignItems="center" key={index}>
<Box sx={{ width: '100%' }}>
<Typography>{benefit.code} {benefit.description ? ` - ${benefit.description} ` : ''}</Typography>
</Box>
<Checkbox checked={checkedBenefitIds.includes(benefit.id)} onClick={(event) => {handleCheckChange(event, benefit)} } />
</Stack>
))
)}
</Stack>
{/* <TableContainer>
<Table>
<TableRow>
<TableCell>
ASD
</TableCell>
<TableCell>
ASD
</TableCell>
</TableRow>
</Table>
</TableContainer> */}
</Grid>
<Grid item xs={12}>
<LoadingButton
id="upload-button"
variant="outlined"
fullWidth
startIcon={<InsertDriveFileIcon />}
onClick={() => {handleDownloadLog(data.member)}}
loading={loadingLog}
>
Download LOG
</LoadingButton>
</Grid>
</Grid>
</Stack>
</ItemNotificationStyle>
</Stack>
);
return (
<>
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
/>
</>
);
};
export default DialogLog;

View File

@@ -9,7 +9,6 @@ import Form from './Form';
import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs';
import axios from '../../../utils/axios';
import { Practitioner } from '../../../@types/doctor';
import ButtonBack from '../../../components/ButtonBack';
export default function Create() {
const { themeStretch } = useSettings();
@@ -31,7 +30,6 @@ export default function Create() {
<Page title="Membership: Create a new Dokter">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center">
<ButtonBack />
<HeaderBreadcrumbs
heading={!isEdit ? 'Manage a new Dokter' : 'Manage Dokter'}
links={[
@@ -54,40 +52,3 @@ export default function Create() {
</Page>
);
}
// const pageTitle = 'Create Data Dokter';
// return (
// <Page title={pageTitle}>
// <Container maxWidth={themeStretch ? false : 'xl'}>
// <HeaderBreadcrumbs
// heading={pageTitle}
// links={[
// {
// name: 'Master',
// href: '/master',
// },
// {
// name: 'Dokter',
// href: '/master/organizations/',
// },
// {
// name: 'Create',
// href: '/master/organizations/create/',
// },
// ]}
// />
// <Grid container spacing={2}>
// <Grid item xs={12}>
// <Card sx={{ p: 2 }}>
// <Form
// isSubmitting={isSubmitting}
// isEdit={isEdit}
// currentOrganizations={currentOrganizations}
// />
// </Card>
// </Grid>
// </Grid>
// </Container>
// </Page>
// );
// }

View File

@@ -8,7 +8,7 @@ import Select, { SelectChangeEvent } from '@mui/material/Select';
import * as React from 'react';
// form
import { useForm } from 'react-hook-form';
import { useForm, Controller } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
// @mui
import { styled } from '@mui/material/styles';
@@ -25,6 +25,7 @@ import {
Typography,
TextField,
Chip,
Autocomplete,
} from '@mui/material';
import CancelIcon from '@mui/icons-material/Cancel';
@@ -41,17 +42,20 @@ import {
RHFMultiCheckbox,
RHFCheckbox,
RHFCustomMultiCheckbox,
RHFSelect,
} from '../../../components/hook-form';
import axios from '../../../utils/axios';
import { fCurrency } from '../../../utils/formatNumber';
import { Practitioner } from '../../../@types/doctor';
import AddIcon from '@mui/icons-material/Add';
import { Label, Rowing } from '@mui/icons-material';
import { email } from '../../../_mock/email';
const LabelStyle = styled(Typography)(({ theme }) => ({
...theme.typography.subtitle2,
color: theme.palette.text.secondary,
marginBottom: theme.spacing(1),
...theme.typography.h6,
marginBottom: theme.spacing(2),
marginTop: theme.spacing(2),
}));
const HeaderStyle = styled('header')(({ theme }) => ({
@@ -66,7 +70,6 @@ const Title = styled(Typography)(({ theme }) => ({
boxShadow: 'none',
// paddingBottom: theme.spacing(3),
fontWeight: 700,
color: '#005B7F',
}));
interface FormValuesProps extends Partial<Practitioner> {
@@ -106,6 +109,8 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props)
() => ({
id: currentPractitioner?.id,
name: currentPractitioner?.name || '',
email: currentPractitioner?.email || '',
phone: currentPractitioner?.phone || '',
address: currentPractitioner?.address || '',
birth_date: currentPractitioner?.birth_date || '',
gender: currentPractitioner?.gender || '',
@@ -121,6 +126,8 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props)
[currentPractitioner]
);
console.log('currentPractitioner', currentPractitioner);
console.log('defaultValues', defaultValues);
function StatusLabel({ value }: { value: boolean }) {
@@ -168,91 +175,391 @@ export default function PractitionerForm({ isEdit, currentPractitioner }: Props)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEdit, currentPractitioner]);
const handleActivate = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue('active', event.target.checked);
const onSubmit = async (data: FormValuesProps) => {
try {
const formData = new FormData();
formData.append('name', data.name);
formData.append('gender', data.gender);
formData.append('address', data.address);
formData.append('birth_place', data.birth_place);
formData.append('birth_date', data.birth_date);
formData.append('email', data.email);
formData.append('phone', data.phone);
// formData.append('active', data.active ? '1' : '0');
forms.forEach((form, index) => {
formData.append(`practices[${index}][organization_id]`, form.organizationId);
form.specialities.forEach((speciality, i) => {
formData.append(`practices[${index}][specialities][${i}][speciality_id]`, speciality);
});
});
console.log('event.target.checked', event.target.checked);
if (!isEdit) {
console.log('formData', formData);
const response = await axios.post('/doctors', formData);
} else {
formData.append('_method', 'PUT');
const response = await axios.post('/doctors/' + currentPractitioner?.id ?? '', formData);
}
reset();
enqueueSnackbar(!isEdit ? 'Doctors Created Successfully!' : 'Doctors Udpated Successfully!', {
variant: 'success',
});
navigate('/master/doctors');
} catch (error: any) {
if (error && error.response.status === 422) {
console.log('error', error.response.data.errors);
for (const [key, value] of Object.entries(error.response.data.errors)) {
setError(key, { message: value[0] });
enqueueSnackbar(value[0] ?? 'Failed Processing Request', { variant: 'error' });
}
} else {
enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' });
}
}
const formData = new FormData();
formData.append('active', event.target.checked ? '1' : '0');
formData.append('_method', 'PUT');
axios.post('/doctors/' + currentPractitioner?.id ?? '', formData);
const ascent = document?.querySelector('ascent');
if (ascent != null) {
ascent.innerHTML = '';
}
};
enqueueSnackbar('active Updated Successfully!', { variant: 'success' });
const [organizations, setOrganizations] = useState<any>([]);
const [specialities, setSpecialities] = useState<any>([]);
useEffect(() => {
axios.get(`/search-organizations`).then((response) => {
setOrganizations(
response.data.map((item: any) => ({ ...item, name: item.name, value: item.id }))
);
});
axios.get(`/search-specialities`).then((response) => {
setSpecialities(
response.data.map((item: any) => ({ ...item, name: item.name, value: item.id }))
);
});
}, []);
// const specialities = [
// { name: 'Dentistry', id: 1 },
// { name: 'Dermatology', id: 2 },
// { name: 'General Medicine', id: 3 },
// { name: 'Pediatrics', id: 4 },
// { name: 'Surgery', id: 5 },
// ];
const practices = currentPractitioner?.practices || [];
// const practices = [
// {
// organization_id: 187,
// specialities: [
// {
// speciality_id: 7,
// },
// {
// speciality_id: 6,
// },
// ],
// },
// {
// organization_id: 181,
// specialities: [
// {
// speciality_id: 2,
// },
// ],
// },
// ];
const [forms, setForms] = useState<any>([]);
useEffect(() => {
if (practices.length > 0) {
const newForms = practices.map((practice: any) => {
return {
organizationId: practice.organization_id,
specialities: practice.specialities.map((s) => s.speciality_id),
};
});
setForms(newForms);
} else {
setForms([
{
organizationId: '',
specialities: [],
},
]);
}
}, [practices && practices.length]);
// }, []);
console.log('forms', forms);
const findValueOrganization = (organizationId) => {
if (organizationId === '' || organizationId === null) {
return { name: '', value: '' };
} else {
const organization = organizations.find((o) => o.id === organizationId);
return { name: organization?.name, value: organizationId };
}
};
// console.log('findValueOrganization', findValueOrganization(187));
// const findValueSpeciality = (specialityIds: number[]) => {
// if (specialityIds.length === 0) {
// return [];
// } else {
// const data = specialities.filter((s) => specialityIds.includes(s.id));
// return data.map((d) => ({ name: d.name, value: d.id }));
// }
// };
const findValueSpeciality = (values: any) => {
return specialities.filter((s) => values.includes(s.value));
};
// const [forms, setForms] = useState([
// {
// organizationId: '',
// specialities: [],
// },
// ]);
const addForm = () => {
setForms([
...forms,
{
organizationId: '',
specialities: [],
},
]);
};
console.log('forms', forms);
const gender = [
{
value: 'male',
label: 'Laki-Laki',
},
{
value: 'female',
label: 'Perempuan',
},
];
console.log('forms', forms);
// const handleSpecialitiesChange = (index: number, value: any) => {
// const newForms = [...forms];
// newForms[index].specialities = value.map((v: any) => ({ speciality_id: v.id }));
// setForms(newForms);
// };
// const handleSpecialitiesChange = (index: number, value: any) => {
// const updatedForms = [...forms];
// updatedForms[index].specialities = value.map((v: any) => v.speciality_id);
// setForms(updatedForms);
// };
const handleOrganizationIdChange = (index, value) => {
const updatedForms = [...forms];
updatedForms[index].organizationId = value.id;
setForms(updatedForms);
};
const handleSpecialitiesChange = (index: number, value: any) => {
setForms((forms) => {
forms[index].specialities = value.map((v: any) => v.value);
return [...forms];
});
};
// const availableOrganizations = organizations.filter(
// (org) =>
// !forms.some((f) => f.organization && f.organization.id === org.id) ||
// forms.findIndex((f) => f.organization && f.organization.id === org.id) === editIndex
// );
const availableOrganizations =
practices.length > 0
? organizations.filter(
(org) => !practices.some((practice) => practice.organization_id === org.id)
)
: organizations.filter((org) => !forms.some((f) => f.organizationId === org.id));
// const availableOrganizations = organizations.filter(
// (org) => !practices.some((p) => p.organization_id === org.id)
// );
const handleDeleteForm = (index) => {
const updatedForms = [...forms];
updatedForms.splice(index, 1);
setForms(updatedForms);
};
return (
<FormProvider methods={methods}>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={3}>
<Box sx={{ width: '100%' }}>
{/* <Stack spacing={3}> */}
<Card sx={{ p: 5 }}>
<HeaderStyle>
{/* <HeaderStyle>
<Grid item xs={6} md={6}>
<Title>Data Dokter</Title>
</Grid>
<Grid item xs={6} md={6}>
{/* <Typography>Status Rumah Sakit</Typography> */}
<RHFSwitch name="active" label="" onClick={handleActivate} />
<RHFSwitch name="active" label="" />
<StatusLabel value={values.active} />
</Grid>
</HeaderStyle>
</HeaderStyle> */}
<Title variant="h5">Informasi Umum</Title>
<Avatar
alt="Remy Sharp"
src={currentPractitioner?.avatar_url}
sx={{ width: 120, height: 120, marginBottom: 2 }}
/>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={7}>
<Span style={{ fontWeight: 'bold' }}>Nama Dokter</Span>
<Text>{currentPractitioner?.name ? currentPractitioner?.name : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>No Telp</Span>
<Text>{currentPractitioner?.phone ? currentPractitioner?.phone : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Tempat Lahir</Span>
<Text>
{currentPractitioner?.birth_place ? currentPractitioner?.birth_place : '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Alamat</Span>
<Text>{currentPractitioner?.address ? currentPractitioner?.address : '-'}</Text>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }} sx={{ mt: 2 }}>
<Grid item xs={12}>
<LabelStyle>Nama Dokter</LabelStyle>
<RHFTextField name="name" placeholder="Tuliskan Nama Dokter" />
</Grid>
<Grid item xs={5} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Span style={{ fontWeight: 'bold' }}>Jenis Kelamin</Span>
<Text>{currentPractitioner?.gender ? currentPractitioner?.gender : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Email</Span>
<Text>{currentPractitioner?.email ? currentPractitioner?.email : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Tanggal Lahir</Span>
<Text>
{currentPractitioner?.birth_date ? currentPractitioner?.birth_date : '-'}
</Text>
<Grid item xs={6}>
<LabelStyle>Jenis Kelamin</LabelStyle>
<RHFSelect name="gender" label="Pilih Jenis Kelamin">
<option value="" />
{gender.map((option, index) => (
<option key={index} value={option.value}>
{option.label}
</option>
))}
</RHFSelect>
</Grid>
<Grid item xs={6}>
<LabelStyle>Alamat</LabelStyle>
<RHFTextField name="address" placeholder="Tuliskan Alamat" />
</Grid>
<Grid item xs={6}>
<LabelStyle>Tempat Lahir</LabelStyle>
<RHFTextField name="birth_place" placeholder="Tuliskan Tempat Lahir" />
</Grid>
<Grid item xs={6}>
<LabelStyle>Tanggal Lahir</LabelStyle>
<RHFDatepicker name="birth_date" placeholder="Silahkan Pilih Tanggal Lahir" />
</Grid>
<Grid item xs={6}>
<LabelStyle>Email</LabelStyle>
<RHFTextField name="email" placeholder="Tuliskan Email" type="email" />
</Grid>
<Grid item xs={6}>
<LabelStyle>No. Telp</LabelStyle>
<RHFTextField name="phone" placeholder="Tuliskan Nomor Telepon" />
</Grid>
</Grid>
</Card>
<Card sx={{ p: 5, marginTop: 2 }}>
<Title variant="h5">Tempat Praktik</Title>
{currentPractitioner?.organizations?.map((item, index) => (
<Box key={index} sx={{ mt: 3 }}>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={7}>
<Text>{item.name}</Text>
<Stack spacing={3} direction="row" justifyContent="space-between">
<Title variant="h5">Tempat Praktik</Title>
<Button
variant="contained"
color="primary"
size="small"
sx={{ boxShadow: 'none' }}
onClick={addForm}
startIcon={<AddIcon />}
>
Tambah Tempat Praktik
</Button>
</Stack>
{forms.map((form, index) => (
<div key={index}>
<Box sx={{ mt: 3 }}>
<Stack spacing={3} direction="row" justifyContent="space-between">
<LabelStyle></LabelStyle>
{index !== 0 && (
<Button
sx={{ color: 'red', m: 1 }}
aria-label="close"
onClick={() => handleDeleteForm(index)}
>
Delete
</Button>
// <Button onClick={() => handleDeleteForm(index)}>Delete</Button>
)}
</Stack>
{/* <h1>{form.organizationId}</h1> */}
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={6}>
<Autocomplete
options={availableOrganizations}
value={findValueOrganization(form.organizationId) ?? ''}
getOptionLabel={(option) => option.name}
isOptionEqualToValue={(option, value) => option.value === value.value}
onChange={(event, value) => handleOrganizationIdChange(index, value)}
renderInput={(params) => (
<TextField {...params} label="Rumah Sakit" variant="outlined" />
)}
/>
{/* <Autocomplete
options={organizations}
value={findValueOrganization(form.organizationId)}
getOptionLabel={(option) =>
option.name ?? findValueOrganization(form.organizationId).name ?? ''
}
onChange={(event, value) => handleOrganizationIdChange(index, value)}
renderInput={(params) => (
<TextField {...params} label="Rumah Sakit" variant="outlined" />
)}
/> */}
</Grid>
<Grid item xs={6}>
{form.specialities && (
// <Autocomplete
// multiple
// // options={specialities}
// options={specialities}
// value={findValueSpeciality(form.specialities) ?? ''}
// getOptionLabel={(option) => option.name}
// isOptionEqualToValue={(option, value) => option.value === value.value}
// onChange={(event, value) => handleSpecialitiesChange(index, value)}
// renderInput={(params) => (
// <TextField {...params} label="Spesialis" variant="outlined" />
// )}
// />
<Autocomplete
multiple
options={specialities}
value={findValueSpeciality(form.specialities)}
getOptionLabel={(option) => option.name}
onChange={(event, value) => handleSpecialitiesChange(index, value)}
renderInput={(params) => (
<TextField {...params} label="Spesialis" variant="outlined" />
)}
/>
)}
</Grid>
</Grid>
</Grid>
</Box>
))}
</Card>
<Card sx={{ p: 5, marginTop: 2 }}>
<Title variant="h5">Spesialisasi</Title>
{currentPractitioner?.specialities?.map((item, index) => (
<Box key={index} sx={{ mt: 3 }}>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={7}>
<Text>{item.name}</Text>
</Grid>
</Grid>
</Box>
</Box>
</div>
))}
</Card>
<Box sx={{ width: '100%', mt: 5 }}>
<Stack
alignItems="center"
justifyContent="end"
direction={{ xs: 'column', md: 'row' }}
sx={{ width: 1, textAlign: { xs: 'center', md: 'left' } }}
>
<Grid item xs={12} md={4}>
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)' }}
type="submit"
variant="contained"
size="large"
// fullWidth={true}
loading={isSubmitting}
>
{!isEdit ? 'Simpan' : 'Simpan Perubahan'}
</LoadingButton>
</Grid>
</Stack>
</Box>
</Box>
</Stack>
</FormProvider>

View File

@@ -57,6 +57,7 @@ import { Search } from '@mui/icons-material';
import { Icon } from '@iconify/react';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add';
// ----------------------------------------------------------------------
@@ -205,9 +206,16 @@ export default function List() {
spacing={2}
sx={{ p: 2, justifyContent: 'space-between', alignItems: 'center' }}
>
<Grid item xs={12} md={12} lg={12}>
<Grid item xs={12} md={10} lg={10}>
<Filter onSearch={applyItems} />
</Grid>
<Grid item xs={12} md={2} lg={2} sx={{ textAlign: 'right' }}>
<Link to="/master/doctors/create" style={{ textDecoration: 'none' }}>
<Button variant="outlined" startIcon={<AddIcon />} sx={{ p: 1.8 }}>
Create
</Button>
</Link>
</Grid>
</Grid>
);
}
@@ -297,15 +305,22 @@ export default function List() {
<CheckStatus row={row} />
</TableCell> */}
{/* <TableCell align="center">
<TableCell align="center">
<ButtonGroup variant="text" aria-label="text button group">
<Link to={'/master/doctors/' + row.id}>
<Link to={'/master/doctors/' + row.id + '/edit'}>
<Button>
<Icon icon="ph:eye-bold" style={{ width: '24px', height: '24px' }} />
<Icon icon="ph:pencil-simple-fill" style={{ width: '24px', height: '24px' }} />
</Button>
</Link>
<Button
onClick={() => {
setOpenDialog(true);
}}
>
<Icon icon="eva:trash-2-outline" style={{ width: '24px', height: '24px' }} />
</Button>
</ButtonGroup>
</TableCell> */}
</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
@@ -336,7 +351,12 @@ export default function List() {
Jenis Kelamin
</Grid>
<Grid item xs={6}>
: {row.gender ? row.gender : '-'}
:{' '}
{row.gender == 'male'
? 'Laki-Laki'
: row.gender == 'female'
? 'Perempuan'
: '-'}
</Grid>
</Grid>
</Grid>

View File

@@ -154,7 +154,7 @@ export default function FormulariumForm({ isEdit, currentFormularium }: Props) {
const linking_rules_checkbox_name = "linking_rules"
const linking_tools = [
{
"value" : "nrik",
"value" : "nric",
"label" : "No. KTP"
},
{

View File

@@ -9,7 +9,6 @@ import Form from './Form';
import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs';
import axios from '../../../utils/axios';
import { Organizations } from '../../../@types/organization';
import ButtonBack from '../../../components/ButtonBack';
export default function Create() {
const { themeStretch } = useSettings();
@@ -31,25 +30,20 @@ export default function Create() {
<Page title="Membership: Create a new Rumah Sakit">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center">
<ButtonBack />
<HeaderBreadcrumbs
heading={!isEdit ? 'Create a new Rumah Sakit' : 'Edit Rumah Sakit'}
links={[
{ name: 'Master', href: '/master' },
{
name: 'Organizations',
href: '/master/organizations',
href: '/master/hospitals',
},
{ name: !isEdit ? 'Create' : currentOrganizations?.name ?? '' },
]}
/>
</Stack>
<Form
// isSubmitting={isSubmitting}
isEdit={isEdit}
currentOrganizations={currentOrganizations}
/>
<Form isEdit={isEdit} currentOrganizations={currentOrganizations} />
</Container>
</Page>
);

View File

@@ -65,7 +65,6 @@ const LabelStyle = styled(Typography)(({ theme }) => ({
}));
const HeaderStyle = styled('header')(({ theme }) => ({
padding: theme.spacing(5),
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
@@ -76,7 +75,6 @@ const Title = styled(Typography)(({ theme }) => ({
boxShadow: 'none',
// paddingBottom: theme.spacing(3),
fontWeight: 700,
color: '#005B7F',
}));
// const [timezone, setTimezone] = React.useState('');
@@ -110,10 +108,6 @@ export default function OrganizationsForm({ isEdit, currentOrganizations }: Prop
const NewCorporateSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
code: Yup.string().required('Corporate Code is required'),
active: Yup.boolean().required('Corporate Status is required'),
lat: Yup.string().required('Latitude is required'),
lng: Yup.string().required('Longitude is required'),
timezone: Yup.string().required('Timezone is required'),
// file: Yup.boolean().required('Corporate Status is required'),
});
@@ -133,12 +127,6 @@ export default function OrganizationsForm({ isEdit, currentOrganizations }: Prop
village_id: currentOrganizations?.village_id || '',
postal_code: currentOrganizations?.postal_code || '',
description: currentOrganizations?.description || '',
technology: currentOrganizations?.technology || '',
support_services: currentOrganizations?.support_services || '',
merchant_code: currentOrganizations?.merchant_code || '',
merchant_key: currentOrganizations?.merchant_key || '',
image_url: currentOrganizations?.image_url || '',
region_groups: currentOrganizations?.region_groups || '',
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentOrganizations]
@@ -192,25 +180,15 @@ export default function OrganizationsForm({ isEdit, currentOrganizations }: Prop
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEdit, currentOrganizations]);
const currentImage = currentOrganizations?.image_url;
console.log('currentImage', currentImage);
console.log('current_image', currentImage);
const [file, setFile] = useState(null);
console.log('file', file);
const onSubmit = async (data: FormValuesProps) => {
try {
const formData = new FormData();
console.log('data', data);
formData.append('name', data.name);
formData.append('code', data.code);
formData.append('phone', data.phone);
formData.append('lat', data.lat);
formData.append('lng', data.lng);
formData.append('address', data.address);
formData.append('timezone', data.timezone);
formData.append('active', data.active ? '1' : '0');
if (data.province_id === currentOrganizations?.province_id) {
formData.append('province_id', data.province_id);
@@ -235,21 +213,12 @@ export default function OrganizationsForm({ isEdit, currentOrganizations }: Prop
} else {
formData.append('village_id', data.village_id?.value ?? '');
}
if (data.region_groups === currentOrganizations?.region_groups) {
formData.append('region_groups', data.region_groups);
} else {
formData.append('region_groups', data.region_groups?.value ?? '');
}
formData.append('postal_code', data.postal_code);
formData.append('description', data.description);
formData.append('technology', data.technology);
formData.append('support_services', data.support_services);
formData.append('merchant_code', data.merchant_code);
formData.append('merchant_key', data.merchant_key);
formData.append('image', file);
if (!isEdit) {
console.log('formData', formData);
const response = await axios.post('/organizations', formData);
} else {
formData.append('_method', 'PUT');
@@ -263,9 +232,10 @@ export default function OrganizationsForm({ isEdit, currentOrganizations }: Prop
!isEdit ? 'Organizations Created Successfully!' : 'Organizations Udpated Successfully!',
{ variant: 'success' }
);
navigate('/master/organizations');
navigate('/master/hospitals');
} catch (error: any) {
if (error && error.response.status === 422) {
console.log('error', error.response.data.errors);
for (const [key, value] of Object.entries(error.response.data.errors)) {
setError(key, { message: value[0] });
enqueueSnackbar(value[0] ?? 'Failed Processing Request', { variant: 'error' });
@@ -281,34 +251,10 @@ export default function OrganizationsForm({ isEdit, currentOrganizations }: Prop
}
};
const [valueTab, setValueTab] = React.useState('1');
const handleChangeTab = (event: React.SyntheticEvent, newValueTab: string) => {
setValueTab(newValueTab);
};
const handleDrop = useCallback(
(acceptedFiles) => {
setValue(
'logo',
acceptedFiles.map((file: Blob | MediaSource) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
)
);
},
[setValue]
);
const handleRemove = (file: File | string) => {
setValue('logo', null);
};
const [province, setProvince] = useState<any>([]);
const [city, setCity] = useState<any>([]);
const [district, setDistrict] = useState<any>([]);
// const [village, setVillage] = useState<any>([]);
const [village, setVillage] = useState<any>([]);
useEffect(() => {
axios.get('/province').then((res) => {
@@ -335,17 +281,15 @@ export default function OrganizationsForm({ isEdit, currentOrganizations }: Prop
}
};
// if (values.province_id) {
// if (values.city_id) {
// loadDistrict();
// } else {
// loadCity();
// }
// } else {
// axios.get('/province').then((res) => {
// setProvince(res.data.data.map((item: any) => ({ value: item.id, label: item.name })));
// });
// }
const loadVillage = async () => {
if (values.district_id == currentOrganizations?.district_id) {
const res = await axios.get('/village?district_id=' + values.district_id);
setVillage(res.data.data.map((item: any) => ({ value: item.id, label: item.name })));
} else {
const res = await axios.get('/village?district_id=' + values.district_id?.value);
setVillage(res.data.data.map((item: any) => ({ value: item.id, label: item.name })));
}
};
if (values.province_id) {
loadCity();
@@ -354,12 +298,11 @@ export default function OrganizationsForm({ isEdit, currentOrganizations }: Prop
if (values.city_id) {
loadDistrict();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [values.province_id, values.city_id, values.district_id]);
console.log('province', values.province_id);
console.log('city', values.city_id);
console.log('district', values.district_id);
if (values.district_id) {
loadVillage();
}
}, [values.province_id, values.city_id, values.district_id, values.village_id]);
const findValueProvince = province.find(
(item: any) => item.value === currentOrganizations?.province_id
@@ -368,53 +311,8 @@ export default function OrganizationsForm({ isEdit, currentOrganizations }: Prop
const findValueDistrict = district.find(
(item: any) => item.value === currentOrganizations?.district_id
);
console.log('findValueProvince', findValueProvince);
console.log('findValueCity', findValueCity);
console.log('findValueDistrict', findValueDistrict);
const timezone = [
{
value: 'WIB',
label: 'WIB',
},
{
value: 'WITA',
label: 'WITA',
},
{
value: 'WIT',
label: 'WIT',
},
];
const region_groups = [
{
value: 'Jabodetabek',
label: 'Jabodetabek',
},
{
value: 'Jawa',
label: 'Jawa',
},
{
value: 'Kalimantan',
label: 'Kalimantan',
},
{
value: 'Papua',
label: 'Papua',
},
{
value: 'Sulawesi',
label: 'Sulawesi',
},
{
value: 'Sumatera',
label: 'Sumatera',
},
];
const findVaalueGroupWilayah = region_groups.find(
(item: any) => item.value === currentOrganizations?.region_groups
const findValueVillage = village.find(
(item: any) => item.value === currentOrganizations?.village_id
);
return (
@@ -431,326 +329,147 @@ export default function OrganizationsForm({ isEdit, currentOrganizations }: Prop
<StatusLabel value={values.active} />
</Grid>
</HeaderStyle>
<Box sx={{ width: '100%', typography: 'body1' }}>
<TabContext value={valueTab}>
<Box
sx={{
borderBottom: 1,
borderColor: 'divider',
backgroundColor: '#F4F6F8',
pl: 5,
pr: 5,
}}
>
<TabList onChange={handleChangeTab} aria-label="lab API tabs example">
<Tab label="Rumah Sakit" value="1" sx={{ pr: 5, pl: 5 }} />
<Tab label="Informasi" value="2" sx={{ pr: 5, pl: 5 }} />
<Tab label="Duitku Setting" value="3" sx={{ pr: 5, pl: 5 }} />
</TabList>
</Box>
<TabPanel value="1">
<Box sx={{ width: '100%', p: 5 }}>
<Grid container rowSpacing={4} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={12}>
<LabelStyle>Nama Rumah Sakit</LabelStyle>
<RHFTextField name="name" placeholder="Tuliskan Nama Rumah Sakit" />
</Grid>
<Grid item xs={12} md={12}>
<LabelStyle>Pilih Foto Rumah Sakit</LabelStyle>
<Box sx={{ width: '100%' }}>
<MyDropzone setFile={setFile} currentImage={currentImage} />
</Box>
</Grid>
<Grid item xs={12}>
<LabelStyle>Nomor IGD</LabelStyle>
<RHFTextField name="phone" placeholder="Tuliskan No IGD" />
</Grid>
<Grid item xs={12}>
<LabelStyle>Code Rumah Sakit</LabelStyle>
<RHFTextField name="code" placeholder="Tuliskan Code Rumah Sakit" />
</Grid>
<Grid item xs={12}>
<LabelStyle>Group Wilayah</LabelStyle>
<Controller
name="region_groups"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="combo-box-demo"
options={region_groups}
getOptionLabel={(option) =>
option.label ?? findVaalueGroupWilayah?.label ?? ''
}
value={value}
onChange={(event: any, newValue: any) => {
console.log('newValue', newValue);
setValue('region_groups', newValue?.value);
onChange(newValue);
}}
renderInput={(params) => (
<TextField
{...params}
label="Group Wilayah"
variant="outlined"
fullWidth
/>
)}
/>
)}
/>
</Grid>
<Grid item xs={12}>
<LabelStyle>Alamat</LabelStyle>
<RHFTextField name="address" placeholder="Tuliskan Alamat" />
</Grid>
<Grid item xs={12} md={6}>
<LabelStyle>Provinsi</LabelStyle>
{/*
<Controller
name="province_id"
control={control}
render={({ field: { onChange, value } }) => (
<Select
className="input-container"
size="medium"
disabled={!province?.length}
value={value}
onChange={(e: any) => {
onChange(e);
}}
fullWidth
MenuProps={{
PaperProps: {
sx: {
maxHeight: 224,
width: 250,
p: 1,
},
},
}}
>
{province?.map((item: any) => (
<MenuItem key={item.value} value={item.value}>
{item.label}
</MenuItem>
))}
</Select>
)}
/> */}
<Box sx={{ width: '100%', typography: 'body1', mt: 2 }}>
<Grid container rowSpacing={4} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={12}>
<LabelStyle>Nama Rumah Sakit</LabelStyle>
<RHFTextField name="name" placeholder="Tuliskan Nama Rumah Sakit" />
</Grid>
<Controller
name="province_id"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="combo-box-demo"
options={province}
getOptionLabel={(option) =>
option.label ?? findValueProvince?.label ?? ''
}
value={value}
onChange={(event: any, newValue: any) => {
console.log('newValue', newValue);
setValue('province_id', newValue?.value);
onChange(newValue);
}}
renderInput={(params) => (
<TextField
{...params}
label="Provinsi"
variant="outlined"
fullWidth
/>
)}
/>
)}
/>
</Grid>
<Grid item xs={12} md={6}>
<LabelStyle>Kabupaten / Kota</LabelStyle>
{/* <Controller
name="city_id"
control={control}
render={({ field: { onChange, value } }) => (
<Select
className="input-container"
size="medium"
disabled={!city?.length}
value={value}
onChange={(e: any) => {
onChange(e);
}}
fullWidth
MenuProps={{
PaperProps: {
sx: {
maxHeight: 224,
width: 250,
p: 1,
},
},
}}
>
{city?.map((item: any) => (
<MenuItem key={item.value} value={item.value}>
{item.label}
</MenuItem>
))}
</Select>
)}
/> */}
<Controller
name="city_id"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="combo-box-demo"
options={city}
getOptionLabel={(option) => option.label ?? findValueCity?.label ?? ''}
value={value}
onChange={(event: any, newValue: any) => {
console.log('newValue', newValue);
setValue('city_id', newValue?.value);
onChange(newValue);
}}
renderInput={(params) => (
<TextField
{...params}
label="Kabupaten / Kota"
variant="outlined"
fullWidth
/>
)}
/>
)}
/>
</Grid>
<Grid item xs={12} md={6}>
<LabelStyle>Code Rumah Sakit</LabelStyle>
<RHFTextField name="code" placeholder="Tuliskan Code Rumah Sakit" />
</Grid>
<Grid item xs={12} md={6}>
<LabelStyle>Nomor IGD</LabelStyle>
<RHFTextField name="phone" placeholder="Tuliskan No IGD" />
</Grid>
<Grid item xs={12}>
<LabelStyle>Alamat</LabelStyle>
<RHFTextField name="address" placeholder="Tuliskan Alamat" />
</Grid>
<Grid item xs={12} md={6}>
<LabelStyle>Provinsi</LabelStyle>
<Grid item xs={12} md={6}>
<LabelStyle>Kecamatan</LabelStyle>
<Controller
name="province_id"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="combo-box-demo"
options={province}
getOptionLabel={(option) => option.label ?? findValueProvince?.label ?? ''}
value={value}
onChange={(event: any, newValue: any) => {
console.log('newValue', newValue);
setValue('province_id', newValue?.value);
onChange(newValue);
}}
renderInput={(params) => (
<TextField {...params} label="Provinsi" variant="outlined" fullWidth />
)}
/>
)}
/>
</Grid>
<Grid item xs={12} md={6}>
<LabelStyle>Kabupaten / Kota</LabelStyle>
{/* <Controller
name="district_id"
control={control}
render={({ field: { onChange, value } }) => (
<Select
className="input-container"
size="medium"
disabled={!district?.length}
value={value}
onChange={(e: any) => {
onChange(e);
}}
fullWidth
MenuProps={{
PaperProps: {
sx: {
maxHeight: 224,
width: 250,
p: 1,
},
},
}}
>
{district?.map((item: any) => (
<MenuItem key={item.value} value={item.value}>
{item.label}
</MenuItem>
))}
</Select>
)}
/> */}
<Controller
name="city_id"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="combo-box-demo"
options={city}
getOptionLabel={(option) => option.label ?? findValueCity?.label ?? ''}
value={value}
onChange={(event: any, newValue: any) => {
console.log('newValue', newValue);
setValue('city_id', newValue?.value);
onChange(newValue);
}}
renderInput={(params) => (
<TextField
{...params}
label="Kabupaten / Kota"
variant="outlined"
fullWidth
/>
)}
/>
)}
/>
</Grid>
<Controller
name="district_id"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="combo-box-demo"
options={district}
getOptionLabel={(option) =>
option.label ?? findValueDistrict?.label ?? ''
}
value={value}
onChange={(event: any, newValue: any) => {
console.log('newValue', newValue);
setValue('district_id', newValue?.value);
onChange(newValue);
}}
renderInput={(params) => (
<TextField
{...params}
label="Kecamatan"
variant="outlined"
fullWidth
/>
)}
/>
)}
/>
</Grid>
<Grid item xs={12} md={6}>
<LabelStyle>Kode Pos</LabelStyle>
<RHFTextField name="postal_code" placeholder="Tuliskan Kode Pos" />
</Grid>
<Grid item xs={12} md={4}>
<LabelStyle>Latitude</LabelStyle>
<RHFTextField name="lat" placeholder="Tuliskan Lattitude" />
</Grid>
<Grid item xs={12} md={4}>
<LabelStyle>Longitude</LabelStyle>
<RHFTextField name="lng" placeholder="Tuliskan Longitude" />
</Grid>
<Grid item xs={12} md={4}>
<LabelStyle>Timezone</LabelStyle>
{/* <RHFTextField name="timezone" /> */}
<RHFSelect name="timezone" label="Pilih Timezone">
<option value="" />
{timezone.map((option, index) => (
<option key={index} value={option.value}>
{option.label}
</option>
))}
</RHFSelect>
</Grid>
</Grid>
</Box>
</TabPanel>
<TabPanel value="2">
<Box sx={{ width: '100%', p: 5 }}>
<Grid container rowSpacing={4} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={12}>
<LabelStyle>Deskripsi</LabelStyle>
<RHFEditor name="description" placeholder="Tuliskan Deskripsi" />
</Grid>
<Grid item xs={12}>
<LabelStyle>Teknologi</LabelStyle>
<RHFEditor name="technology" placeholder="Tuliskan Teknologi" />
</Grid>
<Grid item xs={12}>
<LabelStyle>Layanan Penunjang</LabelStyle>
<RHFEditor name="support_services" placeholder="Tuliskan Layanan Penunjang" />
</Grid>
</Grid>
</Box>
</TabPanel>
<TabPanel value="3">
<Box sx={{ width: '100%', p: 5 }}>
<Grid container rowSpacing={4} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={12}>
<LabelStyle>Merchant Code</LabelStyle>
<RHFTextField name="merchant_code" placeholder="Tuliskan Merchant Code" />
</Grid>
<Grid item xs={12}>
<LabelStyle>Merchant Key</LabelStyle>
<RHFTextField name="merchant_key" placeholder="Tuliskan Merchant Key" />
</Grid>
</Grid>
</Box>
</TabPanel>
</TabContext>
<Grid item xs={12} md={6}>
<LabelStyle>Kecamatan</LabelStyle>
<Controller
name="district_id"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="combo-box-demo"
options={district}
getOptionLabel={(option) => option.label ?? findValueDistrict?.label ?? ''}
value={value}
onChange={(event: any, newValue: any) => {
console.log('newValue', newValue);
setValue('district_id', newValue?.value);
onChange(newValue);
}}
renderInput={(params) => (
<TextField {...params} label="Kecamatan" variant="outlined" fullWidth />
)}
/>
)}
/>
</Grid>
<Grid item xs={12} md={6}>
<LabelStyle>Desa</LabelStyle>
<Controller
name="village_id"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="combo-box-demo"
options={village}
getOptionLabel={(option) => option.label ?? findValueVillage?.label ?? ''}
value={value}
onChange={(event: any, newValue: any) => {
console.log('newValue', newValue);
setValue('village_id', newValue?.value);
onChange(newValue);
}}
renderInput={(params) => (
<TextField {...params} label="Desa" variant="outlined" fullWidth />
)}
/>
)}
/>
</Grid>
<Grid item xs={12} md={4}>
<LabelStyle>Kode Pos</LabelStyle>
<RHFTextField name="postal_code" placeholder="Tuliskan Kode Pos" />
</Grid>
<Grid item xs={12} md={4}>
<LabelStyle>Latitude</LabelStyle>
<RHFTextField name="lat" placeholder="Tuliskan Lattitude" />
</Grid>
<Grid item xs={12} md={4}>
<LabelStyle>Longitude</LabelStyle>
<RHFTextField name="lng" placeholder="Tuliskan Longitude" />
</Grid>
<Grid item xs={12}>
<LabelStyle>Deskripsi</LabelStyle>
<RHFEditor name="description" placeholder="Tuliskan Deskripsi" />
</Grid>
</Grid>
</Box>
<Box sx={{ width: '100%', p: 5 }}>
<Box sx={{ width: '100%', mt: 5 }}>
<Stack
alignItems="center"
justifyContent="end"

View File

@@ -23,7 +23,7 @@ export default function Organizations() {
},
{
name: 'Rumah Sakit',
href: '/master/organizations',
href: '/master/hospitals',
},
]}
/>

View File

@@ -147,9 +147,11 @@ export default function List() {
>
<SearchInput onSearch={applyFilter} />
{/* <Link to="/master/organizations/create/" style={{ textDecoration: 'none' }}>
<ButtonCreate />
</Link> */}
<Link to="/master/hospitals/create" style={{ textDecoration: 'none' }}>
<Button variant="outlined" startIcon={<AddIcon />} sx={{ p: 1.8 }}>
Create
</Button>
</Link>
</Stack>
);
}
@@ -259,26 +261,26 @@ export default function List() {
</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">{row.phone}</TableCell>
<TableCell align="left">{row.address?.text}</TableCell>
<TableCell align="left">{row.address}</TableCell>
{/* <TableCell align="left">
<Stack direction="row">
<ButtonGroup variant="text" aria-label="text button group">
<Link to={'/master/organizations/' + row.id + '/edit'}>
<Button>
<Icon icon="ph:pencil-simple-fill" style={{ width: '24px', height: '24px' }} />
</Button>
</Link>
<Button
onClick={() => {
setOpenDialog(true);
}}
>
<Icon icon="eva:trash-2-outline" style={{ width: '24px', height: '24px' }} />
<TableCell align="right">
{/* <Stack direction="row"> */}
<ButtonGroup variant="text" aria-label="text button group">
<Link to={'/master/hospitals/' + row.id + '/edit'}>
<Button>
<Icon icon="ph:pencil-simple-fill" style={{ width: '24px', height: '24px' }} />
</Button>
</ButtonGroup>
</Stack>
</TableCell> */}
</Link>
<Button
onClick={() => {
setOpenDialog(true);
}}
>
<Icon icon="eva:trash-2-outline" style={{ width: '24px', height: '24px' }} />
</Button>
</ButtonGroup>
{/* </Stack> */}
</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}

View File

@@ -0,0 +1,93 @@
import { useEffect, useState } from 'react';
import { paramCase } from 'change-case';
import { useParams, useLocation } from 'react-router-dom';
// @mui
import { Container, Stack } from '@mui/material';
import useSettings from '../../../hooks/useSettings';
import Page from '../../../components/Page';
import Form from './Form';
import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs';
import axios from '../../../utils/axios';
import { Practitioner } from '../../../@types/doctor';
import ButtonBack from '../../../components/ButtonBack';
export default function Create() {
const { themeStretch } = useSettings();
const { id } = useParams();
const isEdit = id ? true : false;
const [currentPractitioner, setCurrentPractitioner] = useState<Practitioner>();
useEffect(() => {
if (isEdit) {
axios.get('/doctors/' + id).then((res) => {
setCurrentPractitioner(res.data);
});
}
}, [id]);
return (
<Page title="Membership: Create a new Dokter">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center">
{/* <ButtonBack /> */}
<HeaderBreadcrumbs
heading={!isEdit ? 'Manage a new Dokter' : 'Manage Dokter'}
links={[
{ name: 'Master', href: '/master' },
{
name: 'Doctors',
href: '/master/doctors',
},
{ name: !isEdit ? 'Create' : currentPractitioner?.name ?? '' },
]}
/>
</Stack>
<Form
// isSubmitting={isSubmitting}
isEdit={isEdit}
currentPractitioner={currentPractitioner}
/>
</Container>
</Page>
);
}
// const pageTitle = 'Create Data Dokter';
// return (
// <Page title={pageTitle}>
// <Container maxWidth={themeStretch ? false : 'xl'}>
// <HeaderBreadcrumbs
// heading={pageTitle}
// links={[
// {
// name: 'Master',
// href: '/master',
// },
// {
// name: 'Dokter',
// href: '/master/organizations/',
// },
// {
// name: 'Create',
// href: '/master/organizations/create/',
// },
// ]}
// />
// <Grid container spacing={2}>
// <Grid item xs={12}>
// <Card sx={{ p: 2 }}>
// <Form
// isSubmitting={isSubmitting}
// isEdit={isEdit}
// currentOrganizations={currentOrganizations}
// />
// </Card>
// </Grid>
// </Grid>
// </Container>
// </Page>
// );
// }

View File

@@ -0,0 +1,260 @@
import * as Yup from 'yup';
import { useSnackbar } from 'notistack';
import { useNavigate } from 'react-router-dom';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import MenuItem from '@mui/material/MenuItem';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import * as React from 'react';
// form
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
// @mui
import { styled } from '@mui/material/styles';
import { LoadingButton } from '@mui/lab';
import {
Box,
Avatar,
Button,
ButtonGroup,
Card,
FormHelperText,
Grid,
Stack,
Typography,
TextField,
Chip,
} from '@mui/material';
import CancelIcon from '@mui/icons-material/Cancel';
// components
import {
FormProvider,
RHFTextField,
RHFRadioGroup,
RHFUploadAvatar,
RHFSwitch,
RHFEditor,
RHFDatepicker,
RHFMultiCheckbox,
RHFCheckbox,
RHFCustomMultiCheckbox,
} from '../../../components/hook-form';
import axios from '../../../utils/axios';
import { fCurrency } from '../../../utils/formatNumber';
import { Practitioner } from '../../../@types/doctor';
import { Label, Rowing } from '@mui/icons-material';
const LabelStyle = styled(Typography)(({ theme }) => ({
...theme.typography.subtitle2,
color: theme.palette.text.secondary,
marginBottom: theme.spacing(1),
}));
const HeaderStyle = styled('header')(({ theme }) => ({
paddingBottom: theme.spacing(5),
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}));
const Title = styled(Typography)(({ theme }) => ({
...theme.typography.h4,
boxShadow: 'none',
// paddingBottom: theme.spacing(3),
fontWeight: 700,
color: '#005B7F',
}));
interface FormValuesProps extends Partial<Practitioner> {
taxes: boolean;
inStock: boolean;
}
type Props = {
isEdit: boolean;
currentPractitioner?: Practitioner;
};
const Span = styled(Typography)(({ theme }) => ({
boxShadow: 'none',
paddingBottom: theme.spacing(1),
}));
const Text = styled(Typography)(({ theme }) => ({
boxShadow: 'none',
paddingBottom: theme.spacing(3),
}));
export default function PractitionerForm({ isEdit, currentPractitioner }: Props) {
const navigate = useNavigate();
const [practitioner_group, setPractitionerGroups] = useState([]);
// const [ errors, setErrors ] = useState<{ [key: string]: string }>({});
const { enqueueSnackbar } = useSnackbar();
const NewCorporateSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
// file: Yup.boolean().required('Corporate Status is required'),
});
const defaultValues = useMemo(
() => ({
id: currentPractitioner?.id,
name: currentPractitioner?.name || '',
address: currentPractitioner?.address || '',
birth_date: currentPractitioner?.birth_date || '',
gender: currentPractitioner?.gender || '',
description: currentPractitioner?.description || '',
birth_place: currentPractitioner?.birth_place || '',
active: currentPractitioner?.active === 1 ? true : false,
avatar_url: currentPractitioner?.avatar_url || '',
doctor_id: currentPractitioner?.doctor_id || '',
organizations: currentPractitioner?.organizations || [],
specialities: currentPractitioner?.specialities || [],
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentPractitioner]
);
console.log('defaultValues', defaultValues);
function StatusLabel({ value }: { value: boolean }) {
return (
<Chip
label={value ? 'Aktif' : 'Tidak Aktif'}
size="medium"
sx={{
backgroundColor: value ? 'rgba(84, 214, 44, 0.16)' : 'rgba(255, 72, 66, 0.16)',
color: value ? '#229A16' : '#B72136',
padding: '1 8 1 8 px',
borderRadius: '4px',
fontSize: '12px',
fontWeight: 'bold',
}}
/>
);
}
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema),
defaultValues,
});
const {
reset,
watch,
control,
setValue,
getValues,
setError,
handleSubmit,
formState: { isSubmitting },
} = methods;
const values = watch();
useEffect(() => {
if (isEdit && currentPractitioner) {
reset(defaultValues);
}
if (!isEdit) {
reset(defaultValues);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEdit, currentPractitioner]);
const handleActivate = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue('active', event.target.checked);
console.log('event.target.checked', event.target.checked);
const formData = new FormData();
formData.append('active', event.target.checked ? '1' : '0');
formData.append('_method', 'PUT');
axios.post('/doctors/' + currentPractitioner?.id ?? '', formData);
enqueueSnackbar('active Updated Successfully!', { variant: 'success' });
};
return (
<FormProvider methods={methods}>
<Stack spacing={3}>
<Box sx={{ width: '100%' }}>
{/* <Stack spacing={3}> */}
<Card sx={{ p: 5 }}>
<HeaderStyle>
<Grid item xs={6} md={6}>
<Title>Data Dokter</Title>
</Grid>
<Grid item xs={6} md={6}>
{/* <Typography>Status Rumah Sakit</Typography> */}
<RHFSwitch name="active" label="" onClick={handleActivate} />
<StatusLabel value={values.active} />
</Grid>
</HeaderStyle>
<Title variant="h5">Informasi Umum</Title>
<Avatar
alt="Remy Sharp"
src={currentPractitioner?.avatar_url}
sx={{ width: 120, height: 120, marginBottom: 2 }}
/>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={7}>
<Span style={{ fontWeight: 'bold' }}>Nama Dokter</Span>
<Text>{currentPractitioner?.name ? currentPractitioner?.name : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>No Telp</Span>
<Text>{currentPractitioner?.phone ? currentPractitioner?.phone : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Tempat Lahir</Span>
<Text>
{currentPractitioner?.birth_place ? currentPractitioner?.birth_place : '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Alamat</Span>
<Text>{currentPractitioner?.address ? currentPractitioner?.address : '-'}</Text>
</Grid>
<Grid item xs={5} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Span style={{ fontWeight: 'bold' }}>Jenis Kelamin</Span>
<Text>{currentPractitioner?.gender ? currentPractitioner?.gender : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Email</Span>
<Text>{currentPractitioner?.email ? currentPractitioner?.email : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Tanggal Lahir</Span>
<Text>
{currentPractitioner?.birth_date ? currentPractitioner?.birth_date : '-'}
</Text>
</Grid>
</Grid>
</Card>
<Card sx={{ p: 5, marginTop: 2 }}>
<Title variant="h5">Tempat Praktik</Title>
{currentPractitioner?.organizations?.map((item, index) => (
<Box key={index} sx={{ mt: 3 }}>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={7}>
<Text>{item.name}</Text>
</Grid>
</Grid>
</Box>
))}
</Card>
<Card sx={{ p: 5, marginTop: 2 }}>
<Title variant="h5">Spesialisasi</Title>
{currentPractitioner?.specialities?.map((item, index) => (
<Box key={index} sx={{ mt: 3 }}>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={7}>
<Text>{item.name}</Text>
</Grid>
</Grid>
</Box>
))}
</Card>
</Box>
</Stack>
</FormProvider>
);
}

View File

@@ -0,0 +1,35 @@
import { Card, Grid, Container } from '@mui/material';
import { useParams } from 'react-router-dom';
import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs';
import Page from '../../../components/Page';
import useSettings from '../../../hooks/useSettings';
import List from './List';
export default function Doctors() {
const { themeStretch } = useSettings();
const { id } = useParams();
const pageTitle = 'Appointments';
return (
<Page title={pageTitle}>
<Container maxWidth={themeStretch ? false : 'xl'}>
<HeaderBreadcrumbs
heading={pageTitle}
links={[
{
name: 'Report',
href: '/report',
},
{
name: 'Appointments',
href: '/report/appointments',
},
]}
/>
<List />
</Container>
</Page>
);
}

View File

@@ -0,0 +1,416 @@
import {
Box,
Button,
Card,
Collapse,
Paper,
Select,
SelectChangeEvent,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Typography,
Stack,
ButtonGroup,
Grid,
Chip,
Dialog,
DialogContent,
DialogContentText,
DialogActions,
FormControl,
Autocomplete,
InputAdornment,
IconButton,
} from '@mui/material';
import {
Link,
NavLink as RouterLink,
useSearchParams,
useNavigate,
useParams,
} from 'react-router-dom';
// hooks
import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react';
import useSettings from '../../../hooks/useSettings';
// components
import axios from '../../../utils/axios';
import { LaravelPaginatedData } from '../../../@types/paginated-data';
import { Icd } from '../../../@types/diagnosis';
import BasePagination from '../../../components/BasePagination';
import { Practitioner } from '../../../@types/doctor';
import CreateIcon from '@mui/icons-material/Create';
import { Props } from '../../../components/editor/index';
import { red } from '@mui/material/colors';
import { margin, padding } from '@mui/system';
import { enqueueSnackbar } from 'notistack';
import { Controller } from 'react-hook-form';
import SvgIconStyle from '../../../components/SvgIconStyle';
import { GridSearchIcon } from '@mui/x-data-grid';
import { Search } from '@mui/icons-material';
import { Icon } from '@iconify/react';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
// ----------------------------------------------------------------------
export default function List() {
// Generate the every row of the table
const navigate = useNavigate();
const { organization_id } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const [searchParamsOrganizations, setSearchParamsOrganizations] = useSearchParams();
const [searchParamsSpecialities, setSearchParamsSpecialities] = useSearchParams();
const [searchParamsFilter, setSearchParamsFilter] = useSearchParams();
function Filter(props: any) {
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
const [searchText, setSearchText] = useState('');
//handle search
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? '';
setSearchText(newSearchText);
};
const handleSearchSubmit = (event: any) => {
event.preventDefault();
props.onSearch(searchText);
};
useEffect(() => {
// Trigger First Search
setSearchText(searchParams.get('search') ?? '');
}, []);
const item = [
{
id: '',
value: '',
name: 'Semua',
},
];
return (
<form style={{ width: '100%' }}>
<Grid container spacing={2} sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
<Grid item xs={12} sm={12} md={12} lg={12}>
<TextField
id="search-input"
ref={searchInput}
variant="outlined"
fullWidth
onChange={handleSearchChange}
onKeyDown={(event) => {
if (event.key === 'Enter') {
handleSearchSubmit(event);
}
}}
value={searchText}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Search />
</InputAdornment>
),
placeholder: 'Search',
}}
/>
</Grid>
</Grid>
</form>
);
}
function FilterForm(props: any) {
// IMPORT
return (
<Grid
container
spacing={2}
sx={{ p: 2, justifyContent: 'space-between', alignItems: 'center' }}
>
<Grid item xs={12} md={12} lg={12}>
<Filter onSearch={applyItems} />
</Grid>
</Grid>
);
}
function createData(doctor: Practitioner): Practitioner {
return {
...doctor,
};
}
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const [openDialog, setOpenDialog] = React.useState(false);
const handleDelete = (model: any) => {
axios
.delete(`/doctors/${row.id}`)
.then((res) => {
setDataTableData({
...dataTableData,
data: dataTableData.data.filter((model) => model.id != row.id),
});
enqueueSnackbar('Data berhasil dihapus', { variant: 'success' });
})
.catch((error) => {
enqueueSnackbar(
error.response.data.message ?? error.message ?? 'Failed Processing Request',
{ variant: 'error' }
);
});
};
return (
<React.Fragment>
<TableRow>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell>
<TableCell align="left">{row.date_created ? row.date_created : '-'}</TableCell>
<TableCell align="left">{row.date_appointment ? row.date_appointment : '-'}</TableCell>
<TableCell align="left">{row.booking_code ?? '-'}</TableCell>
<TableCell align="left">{row.patient_name ? row.patient_name : '-'}</TableCell>
<TableCell align="left">{row.health_care ? row.health_care : '-'}</TableCell>
<TableCell align="left">{row.doctor_name ? row.doctor_name : '-'}</TableCell>
<TableCell align="left">{row.type ? row.type : '-'}</TableCell>
<TableCell align="left">{row.status ? row.status : '-'}</TableCell>
<TableCell align="center">
<ButtonGroup variant="text" aria-label="text button group">
<Link to={'/report/appointments/' + row.id + '/show'}>
<Button>
<Icon icon="ph:eye-bold" style={{ width: '24px', height: '24px' }} />
</Button>
</Link>
</ButtonGroup>
</TableCell>
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Stack>
<Grid container>
<Grid item xs={2}>Spesialisasi</Grid><Grid item xs="10">: {row.speciality}</Grid>
<Grid item xs={2}>Via</Grid><Grid item xs="10">: {row.appointment_media}</Grid>
<Grid item xs={2}>Metode Pembayaran</Grid><Grid item xs="10">: {row.payment_method}</Grid>
<Grid item xs={2}>HIS RegID</Grid><Grid item xs="10">: {row.his_detail?.sRegID}</Grid>
<Grid item xs={2}>HIS Medrec</Grid><Grid item xs="10">: {row.his_detail?.Medrec}</Grid>
<Grid item xs={2}>No HP</Grid><Grid item xs="10">: {row.patient?.sPhone ?? ''}</Grid>
<Grid item xs={2}>E-mail</Grid><Grid item xs="10">: {row.patient?.sEmail ?? ''}</Grid>
<Grid item xs={2}>Alamat</Grid><Grid item xs="10">: {row.patient?.detail?.sAlamat ?? ''}</Grid>
<Grid item xs={2}>KTP</Grid><Grid item xs="10">: {row.patient?.detail?.sKTP ?? ''}</Grid>
</Grid>
</Stack>
</Collapse>
</TableCell>
</TableRow>
<Dialog
open={openDialog}
onClose={() => {
setOpenDialog(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent sx={{ p: 5 }}>
<Icon
icon="eva:trash-2-outline"
style={{
width: '100px',
height: '100px',
color: '#FF0000',
margin: 'auto',
display: 'block',
marginBottom: '20px',
alignContent: 'center',
}}
/>
<DialogContentText sx={{ fontWeight: 'bold', pb: 1 }} id="alert-dialog-title">
Apakah anda yakin ingin menghapus
</DialogContentText>
<Typography sx={{ fontWeight: 'bold' }} id="alert-dialog-title">
{row.name}?
</Typography>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setOpenDialog(false);
}}
color="primary"
>
Batal
</Button>
<Button
onClick={() => {
handleDelete(row.id);
}}
color="primary"
autoFocus
>
Hapus
</Button>
</DialogActions>
</Dialog>
</React.Fragment>
);
}
const headStyle = {
fontWeight: 'bold',
};
// Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = useState(true);
const [dataTableLastRequest, setDataTableLastRequest] = useState(0);
const [dataTableResponseState, setDataTableResponseState] = useState('idle');
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>({
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('/appointments', {
params: filter,
});
setDataTableLoading(false);
setDataTableData(response.data);
};
// const applyFilter = async (searchFilter: string) => {
// await loadDataTableData({ search: searchFilter });
// setSearchParams({ search: searchFilter });
// };
const applyItems = async (
searchFilter: string,
searchFilterOrganization: string,
searchFilterSpecialities: string
) => {
await loadDataTableData({
search: searchFilter,
organization_id: searchFilterOrganization,
speciality_id: searchFilterSpecialities,
});
setSearchParamsFilter({
search: searchFilter,
organization_id: searchFilterOrganization,
speciality_id: searchFilterSpecialities,
});
};
const handlePageChange = (event: ChangeEvent, value: number) => {
const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]);
loadDataTableData(filter);
setSearchParams(filter);
};
useEffect(() => {
loadDataTableData();
}, []);
return (
<Stack>
{/* <Ambulace /> */}
<Card sx={{ marginTop: '30px' }}>
<FilterForm sx={{ marginTop: '100px' }} />
{/* The Main Table */}
<TableContainer component={Paper}>
<Table>
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left" />
<TableCell style={headStyle} align="left">
Tanggal Pemesanan
</TableCell>
<TableCell style={headStyle} align="left">
Tanggal Appointment
</TableCell>
<TableCell style={headStyle} align="left">
Kode Booking
</TableCell>
<TableCell style={headStyle} align="left">
Pasien
</TableCell>
<TableCell style={headStyle} align="left">
Faskes
</TableCell>
<TableCell style={headStyle} align="left">
Dokter
</TableCell>
<TableCell style={headStyle} align="left">
Jenis
</TableCell>
<TableCell style={headStyle} align="left">
Status Appointment
</TableCell>
<TableCell style={headStyle} align="left" />
{/* <TableCell style={headStyle} align="center">
Aksi
</TableCell> */}
</TableRow>
</TableBody>
{dataTableIsLoading ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
Loading
</TableCell>
</TableRow>
</TableBody>
) : dataTableData.data.length == 0 ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
No Data
</TableCell>
</TableRow>
</TableBody>
) : (
<TableBody>
{dataTableData.data.map((row) => (
<Row key={row.id} row={row} />
))}
</TableBody>
)}
</Table>
</TableContainer>
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange} />
</Card>
</Stack>
);
}

View File

@@ -0,0 +1,53 @@
import { useEffect, useState } from 'react';
import { paramCase } from 'change-case';
import { useParams, useLocation } from 'react-router-dom';
// @mui
import { Container, Stack } from '@mui/material';
import useSettings from '../../../hooks/useSettings';
import Page from '../../../components/Page';
import View from './View';
import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs';
import axios from '../../../utils/axios';
import { Appointment } from '../../../@types/doctor';
export default function Create() {
const { themeStretch } = useSettings();
const { id } = useParams();
const isEdit = id ? true : false;
const [currentAppointment, setCurrentAppointment] = useState<Appointment>();
useEffect(() => {
if (isEdit) {
axios.get('/appointments/' + id).then((res) => {
setCurrentAppointment(res.data);
});
}
}, [id]);
return (
<Page title="Appointment">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center">
<HeaderBreadcrumbs
heading={!isEdit ? 'Appointment' : 'Appointment'}
links={[
{ name: 'Report', href: '/report' },
{
name: 'Appointments',
href: '/report/appointments',
},
]}
/>
</Stack>
<View
// isSubmitting={isSubmitting}
isEdit={isEdit}
currentAppointment={currentAppointment}
/>
</Container>
</Page>
);
}

View File

@@ -0,0 +1,275 @@
import * as Yup from 'yup';
import { useSnackbar } from 'notistack';
import { useNavigate } from 'react-router-dom';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import MenuItem from '@mui/material/MenuItem';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import * as React from 'react';
// form
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
// @mui
import { styled } from '@mui/material/styles';
import { LoadingButton } from '@mui/lab';
import {
Box,
Avatar,
Button,
ButtonGroup,
Card,
FormHelperText,
Grid,
Stack,
Typography,
TextField,
Chip,
Badge,
Divider,
} from '@mui/material';
import CancelIcon from '@mui/icons-material/Cancel';
// components
import {
FormProvider,
RHFTextField,
RHFRadioGroup,
RHFUploadAvatar,
RHFSwitch,
RHFEditor,
RHFDatepicker,
RHFMultiCheckbox,
RHFCheckbox,
RHFCustomMultiCheckbox,
} from '../../../components/hook-form';
import axios from '../../../utils/axios';
import { fCurrency } from '../../../utils/formatNumber';
import { Appointment } from '../../../@types/doctor';
import { Label, Rowing, Spa } from '@mui/icons-material';
import { border } from '@mui/system';
const LabelStyle = styled(Typography)(({ theme }) => ({
...theme.typography.subtitle2,
color: theme.palette.text.secondary,
marginBottom: theme.spacing(1),
}));
const HeaderStyle = styled('header')(({ theme }) => ({
paddingBottom: theme.spacing(5),
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}));
const Title = styled(Typography)(({ theme }) => ({
...theme.typography.h4,
boxShadow: 'none',
// paddingBottom: theme.spacing(3),
fontWeight: 700,
color: '#005B7F',
}));
interface FormValuesProps extends Partial<Appointment> {
taxes: boolean;
inStock: boolean;
}
type Props = {
isEdit: boolean;
currentAppointment?: Appointment;
};
const Span = styled(Typography)(({ theme }) => ({
boxShadow: 'none',
paddingBottom: theme.spacing(1),
}));
const Text = styled(Typography)(({ theme }) => ({
boxShadow: 'none',
paddingBottom: theme.spacing(3),
}));
export default function AppointmentForm({ isEdit, currentAppointment }: Props) {
const navigate = useNavigate();
// const [ errors, setErrors ] = useState<{ [key: string]: string }>({});
const { enqueueSnackbar } = useSnackbar();
const NewCorporateSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
// file: Yup.boolean().required('Corporate Status is required'),
});
const defaultValues = useMemo(
() => ({
id: currentAppointment?.id,
name: currentAppointment?.name || '',
address: currentAppointment?.address || '',
birth_date: currentAppointment?.birth_date || '',
gender: currentAppointment?.gender || '',
description: currentAppointment?.description || '',
birth_place: currentAppointment?.birth_place || '',
active: currentAppointment?.active === 1 ? true : false,
avatar_url: currentAppointment?.avatar_url || '',
doctor_id: currentAppointment?.doctor_id || '',
organizations: currentAppointment?.organizations || [],
specialities: currentAppointment?.specialities || [],
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentAppointment]
);
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema),
defaultValues,
});
const {
reset,
watch,
control,
setValue,
getValues,
setError,
handleSubmit,
formState: { isSubmitting },
} = methods;
const values = watch();
useEffect(() => {
if (isEdit && currentAppointment) {
reset(defaultValues);
}
if (!isEdit) {
reset(defaultValues);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEdit, currentAppointment]);
return (
<FormProvider methods={methods}>
<Stack spacing={3}>
<Box sx={{ width: '100%' }}>
{/* <Stack spacing={3}> */}
<Card sx={{ p: 5 }}>
<HeaderStyle>
<Grid item xs={6} md={6}>
<Stack
direction="row"
divider={<Divider orientation="vertical" flexItem />}
spacing={2}
>
<Title>Data Appointment</Title>
<Chip label={currentAppointment?.status} variant="outlined" />
</Stack>
</Grid>
</HeaderStyle>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={12}>
<Stack direction="row" spacing={2}>
<Grid item xs={6}>
<Stack direction="row" spacing={2}>
<Span style={{ fontWeight: 'bold' }}>Tanggal Booking :</Span>
<Text>
{currentAppointment?.date_created ? currentAppointment?.date_created : '-'}
</Text>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack direction="row" spacing={2}>
<Span style={{ fontWeight: 'bold' }}>Tanggal Appointment :</Span>
<Text>
{currentAppointment?.date_appointment
? currentAppointment?.date_appointment
: '-'}
</Text>
</Stack>
</Grid>
</Stack>
</Grid>
<Grid item xs={6}>
<Span style={{ fontWeight: 'bold' }}>Nama Dokter</Span>
<Text>
{currentAppointment?.doctor_name ? currentAppointment?.doctor_name : '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Faskes</Span>
<Text>
{currentAppointment?.health_care ? currentAppointment?.health_care : '-'}
</Text>
</Grid>
<Grid item xs={6} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Span style={{ fontWeight: 'bold' }}>Spesialis</Span>
<Text>{currentAppointment?.speciality ? currentAppointment?.speciality : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Appointment Via Web/App</Span>
<Text>
{currentAppointment?.appointment_media
? currentAppointment?.appointment_media
: '-'}
</Text>
</Grid>
</Grid>
</Card>
<Card sx={{ mt: 5, p: 5 }}>
<HeaderStyle>
<Grid item xs={6} md={6}>
<Title>Data Pembayaran</Title>
</Grid>
</HeaderStyle>
{currentAppointment?.payment_detail !== null ? (
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={6}>
<Span style={{ fontWeight: 'bold' }}>Metode Pembayaran</Span>
<Text>
{currentAppointment?.payment_method ? currentAppointment?.payment_method : '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Harga</Span>
<Text>
{currentAppointment?.payment_detail?.gross_amount
? currentAppointment?.payment_detail?.gross_amount
: '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Mata Uang</Span>
<Text>
{currentAppointment?.payment_detail?.currency
? currentAppointment?.payment_detail?.currency
: '-'}
</Text>
</Grid>
<Grid item xs={6} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Span style={{ fontWeight: 'bold' }}>Tipe Pembayaran</Span>
<Text>
{currentAppointment?.payment_detail?.payment_type
? currentAppointment?.payment_detail?.payment_type
: '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Waktu Transaksi</Span>
<Text>
{currentAppointment?.payment_detail?.transaction_time
? currentAppointment?.payment_detail?.transaction_time
: '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Status</Span>
<Text>
{currentAppointment?.payment_detail?.status_message
? currentAppointment?.payment_detail?.status_message
: '-'}
</Text>
</Grid>
</Grid>
) : (
<Span>Belum ada pembayaran</Span>
)}
</Card>
</Box>
</Stack>
</FormProvider>
);
}

View File

@@ -0,0 +1,93 @@
import { useEffect, useState } from 'react';
import { paramCase } from 'change-case';
import { useParams, useLocation } from 'react-router-dom';
// @mui
import { Container, Stack } from '@mui/material';
import useSettings from '../../../hooks/useSettings';
import Page from '../../../components/Page';
import Form from './Form';
import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs';
import axios from '../../../utils/axios';
import { Practitioner } from '../../../@types/doctor';
import ButtonBack from '../../../components/ButtonBack';
export default function Create() {
const { themeStretch } = useSettings();
const { id } = useParams();
const isEdit = id ? true : false;
const [currentPractitioner, setCurrentPractitioner] = useState<Practitioner>();
useEffect(() => {
if (isEdit) {
axios.get('/doctors/' + id).then((res) => {
setCurrentPractitioner(res.data);
});
}
}, [id]);
return (
<Page title="Membership: Create a new Dokter">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center">
{/* <ButtonBack /> */}
<HeaderBreadcrumbs
heading={!isEdit ? 'Manage a new Dokter' : 'Manage Dokter'}
links={[
{ name: 'Master', href: '/master' },
{
name: 'Doctors',
href: '/master/doctors',
},
{ name: !isEdit ? 'Create' : currentPractitioner?.name ?? '' },
]}
/>
</Stack>
<Form
// isSubmitting={isSubmitting}
isEdit={isEdit}
currentPractitioner={currentPractitioner}
/>
</Container>
</Page>
);
}
// const pageTitle = 'Create Data Dokter';
// return (
// <Page title={pageTitle}>
// <Container maxWidth={themeStretch ? false : 'xl'}>
// <HeaderBreadcrumbs
// heading={pageTitle}
// links={[
// {
// name: 'Master',
// href: '/master',
// },
// {
// name: 'Dokter',
// href: '/master/organizations/',
// },
// {
// name: 'Create',
// href: '/master/organizations/create/',
// },
// ]}
// />
// <Grid container spacing={2}>
// <Grid item xs={12}>
// <Card sx={{ p: 2 }}>
// <Form
// isSubmitting={isSubmitting}
// isEdit={isEdit}
// currentOrganizations={currentOrganizations}
// />
// </Card>
// </Grid>
// </Grid>
// </Container>
// </Page>
// );
// }

View File

@@ -0,0 +1,260 @@
import * as Yup from 'yup';
import { useSnackbar } from 'notistack';
import { useNavigate } from 'react-router-dom';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import MenuItem from '@mui/material/MenuItem';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import * as React from 'react';
// form
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
// @mui
import { styled } from '@mui/material/styles';
import { LoadingButton } from '@mui/lab';
import {
Box,
Avatar,
Button,
ButtonGroup,
Card,
FormHelperText,
Grid,
Stack,
Typography,
TextField,
Chip,
} from '@mui/material';
import CancelIcon from '@mui/icons-material/Cancel';
// components
import {
FormProvider,
RHFTextField,
RHFRadioGroup,
RHFUploadAvatar,
RHFSwitch,
RHFEditor,
RHFDatepicker,
RHFMultiCheckbox,
RHFCheckbox,
RHFCustomMultiCheckbox,
} from '../../../components/hook-form';
import axios from '../../../utils/axios';
import { fCurrency } from '../../../utils/formatNumber';
import { Practitioner } from '../../../@types/doctor';
import { Label, Rowing } from '@mui/icons-material';
const LabelStyle = styled(Typography)(({ theme }) => ({
...theme.typography.subtitle2,
color: theme.palette.text.secondary,
marginBottom: theme.spacing(1),
}));
const HeaderStyle = styled('header')(({ theme }) => ({
paddingBottom: theme.spacing(5),
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}));
const Title = styled(Typography)(({ theme }) => ({
...theme.typography.h4,
boxShadow: 'none',
// paddingBottom: theme.spacing(3),
fontWeight: 700,
color: '#005B7F',
}));
interface FormValuesProps extends Partial<Practitioner> {
taxes: boolean;
inStock: boolean;
}
type Props = {
isEdit: boolean;
currentPractitioner?: Practitioner;
};
const Span = styled(Typography)(({ theme }) => ({
boxShadow: 'none',
paddingBottom: theme.spacing(1),
}));
const Text = styled(Typography)(({ theme }) => ({
boxShadow: 'none',
paddingBottom: theme.spacing(3),
}));
export default function PractitionerForm({ isEdit, currentPractitioner }: Props) {
const navigate = useNavigate();
const [practitioner_group, setPractitionerGroups] = useState([]);
// const [ errors, setErrors ] = useState<{ [key: string]: string }>({});
const { enqueueSnackbar } = useSnackbar();
const NewCorporateSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
// file: Yup.boolean().required('Corporate Status is required'),
});
const defaultValues = useMemo(
() => ({
id: currentPractitioner?.id,
name: currentPractitioner?.name || '',
address: currentPractitioner?.address || '',
birth_date: currentPractitioner?.birth_date || '',
gender: currentPractitioner?.gender || '',
description: currentPractitioner?.description || '',
birth_place: currentPractitioner?.birth_place || '',
active: currentPractitioner?.active === 1 ? true : false,
avatar_url: currentPractitioner?.avatar_url || '',
doctor_id: currentPractitioner?.doctor_id || '',
organizations: currentPractitioner?.organizations || [],
specialities: currentPractitioner?.specialities || [],
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentPractitioner]
);
console.log('defaultValues', defaultValues);
function StatusLabel({ value }: { value: boolean }) {
return (
<Chip
label={value ? 'Aktif' : 'Tidak Aktif'}
size="medium"
sx={{
backgroundColor: value ? 'rgba(84, 214, 44, 0.16)' : 'rgba(255, 72, 66, 0.16)',
color: value ? '#229A16' : '#B72136',
padding: '1 8 1 8 px',
borderRadius: '4px',
fontSize: '12px',
fontWeight: 'bold',
}}
/>
);
}
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema),
defaultValues,
});
const {
reset,
watch,
control,
setValue,
getValues,
setError,
handleSubmit,
formState: { isSubmitting },
} = methods;
const values = watch();
useEffect(() => {
if (isEdit && currentPractitioner) {
reset(defaultValues);
}
if (!isEdit) {
reset(defaultValues);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEdit, currentPractitioner]);
const handleActivate = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue('active', event.target.checked);
console.log('event.target.checked', event.target.checked);
const formData = new FormData();
formData.append('active', event.target.checked ? '1' : '0');
formData.append('_method', 'PUT');
axios.post('/doctors/' + currentPractitioner?.id ?? '', formData);
enqueueSnackbar('active Updated Successfully!', { variant: 'success' });
};
return (
<FormProvider methods={methods}>
<Stack spacing={3}>
<Box sx={{ width: '100%' }}>
{/* <Stack spacing={3}> */}
<Card sx={{ p: 5 }}>
<HeaderStyle>
<Grid item xs={6} md={6}>
<Title>Data Dokter</Title>
</Grid>
<Grid item xs={6} md={6}>
{/* <Typography>Status Rumah Sakit</Typography> */}
<RHFSwitch name="active" label="" onClick={handleActivate} />
<StatusLabel value={values.active} />
</Grid>
</HeaderStyle>
<Title variant="h5">Informasi Umum</Title>
<Avatar
alt="Remy Sharp"
src={currentPractitioner?.avatar_url}
sx={{ width: 120, height: 120, marginBottom: 2 }}
/>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={7}>
<Span style={{ fontWeight: 'bold' }}>Nama Dokter</Span>
<Text>{currentPractitioner?.name ? currentPractitioner?.name : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>No Telp</Span>
<Text>{currentPractitioner?.phone ? currentPractitioner?.phone : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Tempat Lahir</Span>
<Text>
{currentPractitioner?.birth_place ? currentPractitioner?.birth_place : '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Alamat</Span>
<Text>{currentPractitioner?.address ? currentPractitioner?.address : '-'}</Text>
</Grid>
<Grid item xs={5} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Span style={{ fontWeight: 'bold' }}>Jenis Kelamin</Span>
<Text>{currentPractitioner?.gender ? currentPractitioner?.gender : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Email</Span>
<Text>{currentPractitioner?.email ? currentPractitioner?.email : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Tanggal Lahir</Span>
<Text>
{currentPractitioner?.birth_date ? currentPractitioner?.birth_date : '-'}
</Text>
</Grid>
</Grid>
</Card>
<Card sx={{ p: 5, marginTop: 2 }}>
<Title variant="h5">Tempat Praktik</Title>
{currentPractitioner?.organizations?.map((item, index) => (
<Box key={index} sx={{ mt: 3 }}>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={7}>
<Text>{item.name}</Text>
</Grid>
</Grid>
</Box>
))}
</Card>
<Card sx={{ p: 5, marginTop: 2 }}>
<Title variant="h5">Spesialisasi</Title>
{currentPractitioner?.specialities?.map((item, index) => (
<Box key={index} sx={{ mt: 3 }}>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={7}>
<Text>{item.name}</Text>
</Grid>
</Grid>
</Box>
))}
</Card>
</Box>
</Stack>
</FormProvider>
);
}

View File

@@ -0,0 +1,35 @@
import { Card, Grid, Container } from '@mui/material';
import { useParams } from 'react-router-dom';
import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs';
import Page from '../../../components/Page';
import useSettings from '../../../hooks/useSettings';
import List from './List';
export default function Doctors() {
const { themeStretch } = useSettings();
const { id } = useParams();
const pageTitle = 'Live Chat';
return (
<Page title={pageTitle}>
<Container maxWidth={themeStretch ? false : 'xl'}>
<HeaderBreadcrumbs
heading={pageTitle}
links={[
{
name: 'Report',
href: '/report',
},
{
name: 'Live Chat',
href: '/report/live-chat',
},
]}
/>
<List />
</Container>
</Page>
);
}

View File

@@ -0,0 +1,466 @@
import {
Box,
Button,
Card,
Collapse,
Paper,
Select,
SelectChangeEvent,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Typography,
Stack,
ButtonGroup,
Grid,
Chip,
Dialog,
DialogContent,
DialogContentText,
DialogActions,
FormControl,
Autocomplete,
InputAdornment,
IconButton,
} from '@mui/material';
import {
Link,
NavLink as RouterLink,
useSearchParams,
useNavigate,
useParams,
} from 'react-router-dom';
// hooks
import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react';
import useSettings from '../../../hooks/useSettings';
// components
import axios from '../../../utils/axios';
import { LaravelPaginatedData } from '../../../@types/paginated-data';
import { Icd } from '../../../@types/diagnosis';
import BasePagination from '../../../components/BasePagination';
import { Practitioner } from '../../../@types/doctor';
import CreateIcon from '@mui/icons-material/Create';
import { Props } from '../../../components/editor/index';
import { red } from '@mui/material/colors';
import { margin, padding } from '@mui/system';
import { enqueueSnackbar } from 'notistack';
import { Controller } from 'react-hook-form';
import SvgIconStyle from '../../../components/SvgIconStyle';
import { GridSearchIcon } from '@mui/x-data-grid';
import { Search } from '@mui/icons-material';
import { Icon } from '@iconify/react';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
// ----------------------------------------------------------------------
export default function List() {
// Generate the every row of the table
const navigate = useNavigate();
const { organization_id } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const [searchParamsOrganizations, setSearchParamsOrganizations] = useSearchParams();
const [searchParamsSpecialities, setSearchParamsSpecialities] = useSearchParams();
const [searchParamsFilter, setSearchParamsFilter] = useSearchParams();
function Filter(props: any) {
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
const [searchText, setSearchText] = useState('');
//handle search
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? '';
setSearchText(newSearchText);
};
const handleSearchSubmit = (event: any) => {
event.preventDefault();
props.onSearch(searchText);
};
useEffect(() => {
// Trigger First Search
setSearchText(searchParams.get('search') ?? '');
}, []);
const item = [
{
id: '',
value: '',
name: 'Semua',
},
];
return (
<form style={{ width: '100%' }}>
<Grid container spacing={2} sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
<Grid item xs={12} sm={12} md={12} lg={12}>
<TextField
id="search-input"
ref={searchInput}
variant="outlined"
fullWidth
onChange={handleSearchChange}
onKeyDown={(event) => {
if (event.key === 'Enter') {
handleSearchSubmit(event);
}
}}
value={searchText}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Search />
</InputAdornment>
),
placeholder: 'Search',
}}
/>
</Grid>
</Grid>
</form>
);
}
function FilterForm(props: any) {
// IMPORT
return (
<Grid
container
spacing={2}
sx={{ p: 2, justifyContent: 'space-between', alignItems: 'center' }}
>
<Grid item xs={12} md={12} lg={12}>
<Filter onSearch={applyItems} />
</Grid>
</Grid>
);
}
function createData(doctor: Practitioner): Practitioner {
return {
...doctor,
};
}
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const [openDialog, setOpenDialog] = React.useState(false);
const handleDelete = (model: any) => {
axios
.delete(`/doctors/${row.id}`)
.then((res) => {
setDataTableData({
...dataTableData,
data: dataTableData.data.filter((model) => model.id != row.id),
});
enqueueSnackbar('Data berhasil dihapus', { variant: 'success' });
})
.catch((error) => {
enqueueSnackbar(
error.response.data.message ?? error.message ?? 'Failed Processing Request',
{ variant: 'error' }
);
});
};
return (
<React.Fragment>
<TableRow>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell>
<TableCell align="left">{row.date_created ? row.date_created : '-'}</TableCell>
<TableCell align="left">{row.date_appointment ? row.date_appointment : '-'}</TableCell>
<TableCell align="left">{row.health_care ? row.health_care : '-'}</TableCell>
<TableCell align="left">{row.doctor_name ? row.doctor_name : '-'}</TableCell>
<TableCell align="left">{row.speciality ? row.speciality : '-'}</TableCell>
<TableCell align="left">{row.appointment_media ? row.appointment_media : '-'}</TableCell>
<TableCell align="left">{row.patient_media ? row.patient_media : '-'}</TableCell>
<TableCell align="left">{row.doctor_media ? row.doctor_media : '-'}</TableCell>
<TableCell align="left">
{row.status_appointment ? row.status_appointment : '-'}
</TableCell>
<TableCell align="left">{row.status_chat ? row.status_chat : '-'}</TableCell>
<TableCell align="center">
<ButtonGroup variant="text" aria-label="text button group">
<Link to={'/report/live-chat/' + row.id + '/show'}>
<Button>
<Icon icon="ph:eye-bold" style={{ width: '24px', height: '24px' }} />
</Button>
</Link>
</ButtonGroup>
</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell
style={{ paddingBottom: 0, paddingTop: 0, backgroundColor: 'rgba(244, 246, 248, 0.5)' }}
colSpan={6}
>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ margin: 1, pb: 2, pl: 4 }}>
<Grid container>
<Grid item xs={12} sx={{ padding: 2 }}>
<Grid container>
<Grid item xs={6}>
Metode Pembayaran
</Grid>
<Grid item xs={6}>
: {row.payment_method ? row.payment_method : '-'}
</Grid>
<Grid item xs={6}>
Jenis Benefit
</Grid>
<Grid item xs={6}>
: -
</Grid>
<Grid item xs={6}>
Durasi
</Grid>
<Grid item xs={6}>
: {row.duration ? row.duration : '-'}
</Grid>
</Grid>
</Grid>
</Grid>
</Box>
</Collapse>
</TableCell>
</TableRow>
{/* END COLLAPSIBLE ROW */}
<Dialog
open={openDialog}
onClose={() => {
setOpenDialog(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogContent sx={{ p: 5 }}>
<Icon
icon="eva:trash-2-outline"
style={{
width: '100px',
height: '100px',
color: '#FF0000',
margin: 'auto',
display: 'block',
marginBottom: '20px',
alignContent: 'center',
}}
/>
<DialogContentText sx={{ fontWeight: 'bold', pb: 1 }} id="alert-dialog-title">
Apakah anda yakin ingin menghapus
</DialogContentText>
<Typography sx={{ fontWeight: 'bold' }} id="alert-dialog-title">
{row.name}?
</Typography>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setOpenDialog(false);
}}
color="primary"
>
Batal
</Button>
<Button
onClick={() => {
handleDelete(row.id);
}}
color="primary"
autoFocus
>
Hapus
</Button>
</DialogActions>
</Dialog>
</React.Fragment>
);
}
const headStyle = {
fontWeight: 'bold',
};
// Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = useState(true);
const [dataTableLastRequest, setDataTableLastRequest] = useState(0);
const [dataTableResponseState, setDataTableResponseState] = useState('idle');
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>({
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('/live-chat', {
params: filter,
});
setDataTableLoading(false);
setDataTableData(response.data);
};
// const applyFilter = async (searchFilter: string) => {
// await loadDataTableData({ search: searchFilter });
// setSearchParams({ search: searchFilter });
// };
const applyItems = async (
searchFilter: string,
searchFilterOrganization: string,
searchFilterSpecialities: string
) => {
await loadDataTableData({
search: searchFilter,
organization_id: searchFilterOrganization,
speciality_id: searchFilterSpecialities,
});
setSearchParamsFilter({
search: searchFilter,
organization_id: searchFilterOrganization,
speciality_id: searchFilterSpecialities,
});
};
const handlePageChange = (event: ChangeEvent, value: number) => {
const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]);
loadDataTableData(filter);
setSearchParams(filter);
};
useEffect(() => {
loadDataTableData();
}, []);
return (
<Stack>
{/* <Ambulace /> */}
<Card sx={{ marginTop: '30px' }}>
<FilterForm sx={{ marginTop: '100px' }} />
{/* The Main Table */}
<TableContainer component={Paper}>
<Table>
<TableBody>
<TableRow>
{/* <TableCell colSpan={8} rowSpan={1} align="center" /> */}
<TableCell style={headStyle} align="left" />
<TableCell style={headStyle} rowSpan={2} align="left">
Tanggal Booking
</TableCell>
<TableCell style={headStyle} rowSpan={2} align="left">
Tanggal Appointment
</TableCell>
<TableCell style={headStyle} rowSpan={2} align="left">
Faskes
</TableCell>
<TableCell style={headStyle} rowSpan={2} align="left">
Nama Dokter
</TableCell>
<TableCell style={headStyle} rowSpan={2} align="left">
Spesialisasi
</TableCell>
<TableCell style={headStyle} rowSpan={2} align="left">
Appointment Via App/Website
</TableCell>
<TableCell
colSpan={2}
style={headStyle}
align="center"
sx={{ borderBottom: '3px solid #d7d7d7' }}
>
Chat Via App/Website
</TableCell>
<TableCell style={headStyle} rowSpan={2} align="left">
Status Appointment
</TableCell>
<TableCell style={headStyle} rowSpan={2} align="left">
Status Chat
</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left" />
{/* <TableCell style={headStyle} align="left">
Tanggal Booking
</TableCell>
<TableCell style={headStyle} align="left">
Tanggal Appointment
</TableCell>
<TableCell style={headStyle} align="left">
Faskes
</TableCell>
<TableCell style={headStyle} align="left">
Nama Dokter
</TableCell>
<TableCell style={headStyle} align="left">
Spesialisasi
</TableCell> */}
<TableCell style={headStyle} align="left">
Pasien
</TableCell>
<TableCell style={headStyle} align="left">
Dokter
</TableCell>
</TableRow>
</TableBody>
{dataTableIsLoading ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
Loading
</TableCell>
</TableRow>
</TableBody>
) : dataTableData.data.length == 0 ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
No Data
</TableCell>
</TableRow>
</TableBody>
) : (
<TableBody>
{dataTableData.data.map((row) => (
<Row key={row.id} row={row} />
))}
</TableBody>
)}
</Table>
</TableContainer>
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange} />
</Card>
</Stack>
);
}

View File

@@ -0,0 +1,53 @@
import { useEffect, useState } from 'react';
import { paramCase } from 'change-case';
import { useParams, useLocation } from 'react-router-dom';
// @mui
import { Container, Stack } from '@mui/material';
import useSettings from '../../../hooks/useSettings';
import Page from '../../../components/Page';
import View from './View';
import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs';
import axios from '../../../utils/axios';
import { Appointment } from '../../../@types/doctor';
export default function Create() {
const { themeStretch } = useSettings();
const { id } = useParams();
const isEdit = id ? true : false;
const [currentAppointment, setCurrentAppointment] = useState<Appointment>();
useEffect(() => {
if (isEdit) {
axios.get('/live-chat/' + id).then((res) => {
setCurrentAppointment(res.data);
});
}
}, [id]);
return (
<Page title="Live Chat">
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center">
<HeaderBreadcrumbs
heading={!isEdit ? 'Live Chat' : 'Live Chat'}
links={[
{ name: 'Report', href: '/report' },
{
name: 'Live Chat',
href: '/report/live-chat',
},
]}
/>
</Stack>
<View
// isSubmitting={isSubmitting}
isEdit={isEdit}
currentAppointment={currentAppointment}
/>
</Container>
</Page>
);
}

View File

@@ -0,0 +1,309 @@
import * as Yup from 'yup';
import { useSnackbar } from 'notistack';
import { useNavigate } from 'react-router-dom';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import MenuItem from '@mui/material/MenuItem';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import * as React from 'react';
// form
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
// @mui
import { styled } from '@mui/material/styles';
import { LoadingButton } from '@mui/lab';
import {
Box,
Avatar,
Button,
ButtonGroup,
Card,
FormHelperText,
Grid,
Stack,
Typography,
TextField,
Chip,
Badge,
Divider,
} from '@mui/material';
import CancelIcon from '@mui/icons-material/Cancel';
// components
import {
FormProvider,
RHFTextField,
RHFRadioGroup,
RHFUploadAvatar,
RHFSwitch,
RHFEditor,
RHFDatepicker,
RHFMultiCheckbox,
RHFCheckbox,
RHFCustomMultiCheckbox,
} from '../../../components/hook-form';
import axios from '../../../utils/axios';
import { fCurrency } from '../../../utils/formatNumber';
import { Appointment } from '../../../@types/doctor';
import { Label, Rowing, Spa } from '@mui/icons-material';
import { border, padding } from '@mui/system';
const LabelStyle = styled(Typography)(({ theme }) => ({
...theme.typography.subtitle2,
color: theme.palette.text.secondary,
marginBottom: theme.spacing(1),
}));
const HeaderStyle = styled('header')(({ theme }) => ({
paddingBottom: theme.spacing(5),
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}));
const Title = styled(Typography)(({ theme }) => ({
...theme.typography.h4,
boxShadow: 'none',
// paddingBottom: theme.spacing(3),
fontWeight: 700,
color: '#005B7F',
}));
interface FormValuesProps extends Partial<Appointment> {
taxes: boolean;
inStock: boolean;
}
type Props = {
isEdit: boolean;
currentAppointment?: Appointment;
};
const Span = styled(Typography)(({ theme }) => ({
boxShadow: 'none',
paddingBottom: theme.spacing(1),
}));
const Text = styled(Typography)(({ theme }) => ({
boxShadow: 'none',
paddingBottom: theme.spacing(3),
}));
export default function AppointmentForm({ isEdit, currentAppointment }: Props) {
const navigate = useNavigate();
// const [ errors, setErrors ] = useState<{ [key: string]: string }>({});
const { enqueueSnackbar } = useSnackbar();
const NewCorporateSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
// file: Yup.boolean().required('Corporate Status is required'),
});
const defaultValues = useMemo(
() => ({
id: currentAppointment?.id,
name: currentAppointment?.name || '',
address: currentAppointment?.address || '',
birth_date: currentAppointment?.birth_date || '',
gender: currentAppointment?.gender || '',
description: currentAppointment?.description || '',
birth_place: currentAppointment?.birth_place || '',
active: currentAppointment?.active === 1 ? true : false,
avatar_url: currentAppointment?.avatar_url || '',
doctor_id: currentAppointment?.doctor_id || '',
organizations: currentAppointment?.organizations || [],
specialities: currentAppointment?.specialities || [],
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentAppointment]
);
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema),
defaultValues,
});
const {
reset,
watch,
control,
setValue,
getValues,
setError,
handleSubmit,
formState: { isSubmitting },
} = methods;
const values = watch();
useEffect(() => {
if (isEdit && currentAppointment) {
reset(defaultValues);
}
if (!isEdit) {
reset(defaultValues);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEdit, currentAppointment]);
return (
<FormProvider methods={methods}>
<Stack spacing={3}>
<Box sx={{ width: '100%' }}>
{/* <Stack spacing={3}> */}
<Card sx={{ p: 5 }}>
<HeaderStyle>
<Grid item xs={6} md={6}>
<Stack
direction="row"
divider={<Divider orientation="vertical" flexItem />}
spacing={2}
>
<Title>Data Live Chat</Title>
</Stack>
</Grid>
</HeaderStyle>
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={12}>
<Stack direction="row" spacing={2}>
<Grid item xs={6}>
<Stack direction="row" spacing={2} alignItems="center">
<Span style={{ fontWeight: 'bold', paddingBottom: '0px' }}>
Status Appointment :
</Span>
<Chip
label={
currentAppointment?.status_appointment
? currentAppointment?.status_appointment
: '-'
}
variant="outlined"
/>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack direction="row" spacing={2} alignItems="center">
<Span style={{ fontWeight: 'bold', paddingBottom: '0px' }}>
Status Chat :
</Span>
<Chip
label={
currentAppointment?.status_chat ? currentAppointment?.status_chat : '-'
}
variant="outlined"
/>
</Stack>
</Grid>
</Stack>
</Grid>
<Grid item xs={12} sx={{ marginTop: '20px' }}>
<Stack direction="row" spacing={2}>
<Grid item xs={6}>
<Stack direction="row" spacing={2}>
<Span style={{ fontWeight: 'bold' }}>Tanggal Booking :</Span>
<Text>
{currentAppointment?.date_created ? currentAppointment?.date_created : '-'}
</Text>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack direction="row" spacing={2}>
<Span style={{ fontWeight: 'bold' }}>Tanggal Appointment :</Span>
<Text>
{currentAppointment?.date_appointment
? currentAppointment?.date_appointment
: '-'}
</Text>
</Stack>
</Grid>
</Stack>
</Grid>
<Grid item xs={6}>
<Span style={{ fontWeight: 'bold' }}>Nama Dokter</Span>
<Text>
{currentAppointment?.doctor_name ? currentAppointment?.doctor_name : '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Faskes</Span>
<Text>
{currentAppointment?.health_care ? currentAppointment?.health_care : '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Durasi</Span>
<Text>{currentAppointment?.duration ? currentAppointment?.duration : '-'}</Text>
</Grid>
<Grid item xs={6} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Span style={{ fontWeight: 'bold' }}>Spesialis</Span>
<Text>{currentAppointment?.speciality ? currentAppointment?.speciality : '-'}</Text>
<Span style={{ fontWeight: 'bold' }}>Appointment Via Web/App</Span>
<Text>
{currentAppointment?.appointment_media
? currentAppointment?.appointment_media
: '-'}
</Text>
</Grid>
</Grid>
</Card>
<Card sx={{ mt: 5, p: 5 }}>
<HeaderStyle>
<Grid item xs={6} md={6}>
<Title>Data Pembayaran</Title>
</Grid>
</HeaderStyle>
{currentAppointment?.payment_detail !== null ? (
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Grid item xs={6}>
<Span style={{ fontWeight: 'bold' }}>Metode Pembayaran</Span>
<Text>
{currentAppointment?.payment_method ? currentAppointment?.payment_method : '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Harga</Span>
<Text>
{currentAppointment?.payment_detail?.gross_amount
? currentAppointment?.payment_detail?.gross_amount
: '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Mata Uang</Span>
<Text>
{currentAppointment?.payment_detail?.currency
? currentAppointment?.payment_detail?.currency
: '-'}
</Text>
</Grid>
<Grid item xs={6} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
<Span style={{ fontWeight: 'bold' }}>Tipe Pembayaran</Span>
<Text>
{currentAppointment?.payment_detail?.payment_type
? currentAppointment?.payment_detail?.payment_type
: '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Waktu Transaksi</Span>
<Text>
{currentAppointment?.payment_detail?.transaction_time
? currentAppointment?.payment_detail?.transaction_time
: '-'}
</Text>
<Span style={{ fontWeight: 'bold' }}>Status</Span>
<Text>
{currentAppointment?.payment_detail?.status_message
? currentAppointment?.payment_detail?.status_message
: '-'}
</Text>
</Grid>
</Grid>
) : (
<Span>Belum ada pembayaran</Span>
)}
</Card>
</Box>
</Stack>
</FormProvider>
);
}

View File

@@ -194,10 +194,26 @@ export default function Router() {
path: 'master/doctors',
element: <MasterDoctors />,
},
{
path: 'master/doctors/create',
element: <MasterDoctorsCreate />,
},
{
path: 'master/doctors/:id/edit',
element: <MasterDoctorsCreate />,
},
{
path: 'master/hospitals',
element: <MasterHospitals />,
},
{
path: 'master/hospitals/create',
element: <MasterHospitalsCreate />,
},
{
path: 'master/hospitals/:id/edit',
element: <MasterHospitalsCreate />,
},
{
path: 'master/diagnosis',
element: <MasterDiagnosis />,
@@ -217,6 +233,39 @@ export default function Router() {
element: <MasterFormulariumCreate />,
},
{
path: 'report/appointments',
element: <Appointment />,
},
{
path: 'report/appointments/:id',
element: <AppointmentCreate />,
},
{
path: 'report/appointments/:id/show',
element: <AppointmentShow />,
},
{
path: 'report/appointments/:id/edit',
element: <AppointmentCreate />,
},
{
path: 'report/live-chat',
element: <Livechat />,
},
{
path: 'report/live-chat/:id',
element: <LivechatCreate />,
},
{
path: 'report/live-chat/:id/show',
element: <LivechatShow />,
},
{
path: 'report/live-chat/:id/edit',
element: <LivechatCreate />,
},
{
path: 'claims',
element: <Claims />,
@@ -324,7 +373,17 @@ const CorporateFormularium = Loadable(lazy(() => import('../pages/Corporates/For
const MasterDiagnosis = Loadable(lazy(() => import('../pages/Master/Diagnosis/Index')));
const MasterDoctors = Loadable(lazy(() => import('../pages/Master/Doctors/Index')));
const MasterDoctorsCreate = Loadable(lazy(() => import('../pages/Master/Doctors/Create')));
const MasterHospitals = Loadable(lazy(() => import('../pages/Master/Hospitals/Index')));
const MasterHospitalsCreate = Loadable(lazy(() => import('../pages/Master/Hospitals/Create')));
const Appointment = Loadable(lazy(() => import('../pages/Report/Appointments/Index')));
const AppointmentCreate = Loadable(lazy(() => import('../pages/Report/Appointments/Create')));
const AppointmentShow = Loadable(lazy(() => import('../pages/Report/Appointments/Show')));
const Livechat = Loadable(lazy(() => import('../pages/Report/Livechat/Index')));
const LivechatCreate = Loadable(lazy(() => import('../pages/Report/Livechat/Create')));
const LivechatShow = Loadable(lazy(() => import('../pages/Report/Livechat/Show')));
const MasterDrug = Loadable(lazy(() => import('../pages/Master/Drug/Index')));

View File

@@ -36,8 +36,8 @@ export const colorPresets = [
name: 'blue',
lighter: '#D1E9FC',
light: '#76B0F1',
main: '#2065D1',
dark: '#103996',
main: '#006a35',
dark: '#004422',
darker: '#061B64',
contrastText: '#fff',
},
@@ -63,7 +63,7 @@ export const colorPresets = [
},
];
export const defaultPreset = colorPresets[0];
export const defaultPreset = colorPresets[3];
export const purplePreset = colorPresets[1];
export const cyanPreset = colorPresets[2];
export const bluePreset = colorPresets[3];

View File

@@ -0,0 +1,325 @@
// @mui
import {
Button,
Box,
Stepper,
Step,
StepLabel,
Card,
Typography,
Divider,
Stack,
CircularProgress,
} from '@mui/material';
import { Add } from '@mui/icons-material';
// components
import MuiDialog from '../../components/MuiDialog';
// theme
import palette from '../../theme/palette';
// React
import { ReactElement, useEffect, useState } from 'react';
import { fDate } from '@/utils/formatTime';
import { addMinutes, format } from 'date-fns';
import { LoadingButton } from '@mui/lab';
import { enqueueSnackbar } from 'notistack';
import Iconify from '../Iconify';
type DataContent = {
claim: object;
isLoading: boolean;
handleDownloadLog: void;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContent[];
};
const steps = ['Review', 'Approval', 'Disbursement'];
const DialogDetailClaim = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => {
const claim = data.claim ?? null;
// ---------------------------------------------
// Step
const [currentStep, setCurrentStep] = useState(0);
useEffect(
function () {
if (claim?.status == 'requested') {
setCurrentStep(0);
}
if (claim?.status == 'approved') {
setCurrentStep(1);
}
if (claim?.status == 'closed') {
setCurrentStep(2);
}
},
[data]
);
// ----------------------------------------------------
// Date Stamp
let currentDate = null;
// ----------------------------------------------------
// Download LOG
const [loadingDownloadLog, setLoadingDownloadLog] = useState(false);
const handleDownloadLog = async (claimRequest) => {
setLoadingDownloadLog(true);
await data.handleDownloadLog(claimRequest).then(() => {
setLoadingDownloadLog(false);
});
};
// ----------------------------------------------------
// Handle Upload Invoice
const handleUploadInvoice = () => {
enqueueSnackbar('Something went wrong, please contact Link Medis Sehat', { variant: 'error' });
};
const getContent = () => (
<>
{data.isLoading && (
<Stack alignItems="center" justifyContent="space-between" sx={{ p: 4 }}>
<CircularProgress />
</Stack>
)}
{!data.isLoading && (
<>
<Stack
alignItems="center"
justifyContent="space-between"
direction="row"
sx={{ marginTop: 1 }}
>
<Typography variant="subtitle1" sx={{ height: 'max-content' }}>
Claim Request
</Typography>
<Stack>
<Typography variant="caption">Submission date</Typography>
{/* {JSON.stringify(data)} */}
<Typography variant="caption">
{claim.created_at && fDate(claim.created_at)}
</Typography>
</Stack>
</Stack>
{/* ----------------STEP---------------- */}
<Box sx={{ width: '100%', marginTop: 2 }}>
<Stepper alternativeLabel activeStep={currentStep ?? 0}>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
</Box>
{/* { claim.status == 'approved' && (
<Stack sx={{ marginTop: 4}}>
<LoadingButton loading={false}
variant="contained"
startIcon={<Add />}
fullWidth
// sx={{ typography: 'subtitle2', borderColor: '#F5F5F5' }}
onClick={() => {handleUploadInvoice()}}
>
Upload Invoice
</LoadingButton>
</Stack>
)} */}
<Stack direction="row" spacing={2} sx={{ marginTop: 2 }}>
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
{/* ---------------------------------TYPE INFO------------------------------------ */}
<Card sx={{ paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill"></Iconify> Pengajuan Penjaminan
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<LoadingButton loading={false}
variant="contained"
startIcon={<Add />}
fullWidth
// sx={{ typography: 'subtitle2', borderColor: '#F5F5F5' }}
onClick={() => {}}
>
Upload Invoice
</LoadingButton>
</Stack>
</Card>
</Stack>
</Stack>
{claim.histories_by_date &&
claim.histories_by_date.map((historiesByDate) => (
<Stack key={historiesByDate.date}>
<Stack marginTop={2}>
<Typography variant="subtitle1" paddingY={2}>
{fDate(historiesByDate.date)}
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
{historiesByDate.histories &&
historiesByDate.histories.map((history) => (
<Stack key={history.id}>
{/* ---------------------------------TYPE INFO------------------------------------ */}
<Card sx={{ paddingY: 2, paddingX: 3 }}>
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Typography variant="body1">
{fDate(history.created_at, 'HH:mm')} WIB
</Typography>
<Typography
sx={{
backgroundColor: palette.light.warning.lighter,
color: palette.light.warning.dark,
borderColor: palette.light.warning.dark,
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Request
</Typography>
</Stack>
<Divider sx={{ marginY: 2 }} />
<Stack>
<Typography variant="subtitle2" color="#404040">
{history.title}
</Typography>
<Typography
variant="caption"
color="#757575"
sx={{ marginTop: 2, marginBottom: 1 }}
>
{history.description}
</Typography>
</Stack>
</Card>
</Stack>
))}
</Stack>
</Stack>
</Stack>
))}
<Stack direction="row" spacing={2} sx={{ marginTop: 2 }}>
<Divider orientation="vertical" flexItem sx={{ borderStyle: 'dashed' }} />
<Stack spacing={2} sx={{ flex: 1, maxWidth: '100%' }}>
{/* ---------------------------------TYPE INFO------------------------------------ */}
<Card sx={{ paddingY: 2, paddingX: 3 }}>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill"></Iconify> Dokumen Kelengkapan
</Typography>
{/* <Typography
sx={{
backgroundColor: palette.light.warning.lighter,
color: palette.light.warning.dark,
borderColor: palette.light.warning.dark,
border: '1px solid',
borderRadius: '6px',
padding: 1,
}}
variant="caption"
>
Dokumen
</Typography> */}
</Stack>
<Divider sx={{ marginY: 2 }} />
<Typography fontWeight="600">Kondisi</Typography>
<Stack>
<Stack
// divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{claim.files_by_type?.claim_kondisi &&
claim.files_by_type.claim_kondisi.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<a href={file.url} target="_blank" style={{ textDecoration: 'none' }}>
<Typography sx={{ color: 'text.secondary' }} variant="subtitle2">
- {file.name}
</Typography>
</a>
</Stack>
))}
</Stack>
<Typography fontWeight="600">Diagnosa</Typography>
<Stack
// divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{claim.files_by_type?.claim_diagnosis &&
claim.files_by_type.claim_diagnosis.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<a href={file.url} target="_blank" style={{ textDecoration: 'none' }}>
<Typography sx={{ color: 'text.secondary' }} variant="subtitle2">
- {file.name}
</Typography>
</a>
</Stack>
))}
</Stack>
<Typography fontWeight="600">Hasil</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{claim.files_by_type?.result &&
claim.files_by_type.result.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<a href={file.url} target="_blank" style={{ textDecoration: 'none' }}>
<Typography sx={{ color: 'text.secondary' }} variant="subtitle2">
- {file.name}
</Typography>
</a>
</Stack>
))}
</Stack>
</Stack>
</Card>
</Stack>
</Stack>
</>
)}
</>
);
return (
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
/>
);
};
export default DialogDetailClaim;

View File

@@ -13,17 +13,19 @@ import { Stack } from '@mui/system';
import { Input } from '@mui/material';
//sections
import TableList from '@/sections/dashboard/TableList';
import { fDate } from '@/utils/formatTime';
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
// ----------------------------------------------------------------------
// const [notifications, setNotifications] = useState([])
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: '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: '11:00 WIB' },
];
// 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: '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: '11:00 WIB' },
// ];
// ----------------------------------------------------------------------
@@ -63,6 +65,23 @@ export default function Dashboard() {
// const [tableData, setTableData] = useState([]);
const [policyData, setPolicyData] = useState<PolicyProps>(defaultPolicyData);
// TODO Remove This
const [itemList, setItemList] = useState([]);
function handleDataLoaded(dataTable) {
let dummyData = [];
dataTable.map(function(data) {
if (data.status == 'approved') {
dummyData.push({
info: `LOG Approved for member ${data.member.full_name}`,
date: fDate(data.created_at, "dd MMMM"),
time: fDate(data.created_at, "HH:mm")
})
}
})
setItemList(dummyData);
}
return (
<Page title="Dashboard">
<Container maxWidth={themeStretch ? false : 'xl'}>
@@ -74,10 +93,11 @@ export default function Dashboard() {
<CardNotification data={itemList} />
</Grid>
<Grid item xs={12} lg={12} md={12}>
<TableList />
<TableList dataLoaded={handleDataLoaded}/>
</Grid>
</Grid>
</Container>
</Page>
);
}

View File

@@ -7,7 +7,7 @@ import Iconify from '@/components/Iconify';
// React
import { useState } from 'react';
import DialogNotification from './DialogNotification';
import DialogDetailClaim from './DialogDetailClaim';
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
// ----------------------------------------------------------------------
@@ -28,7 +28,7 @@ const RootNotificationStyle = styled(Card)(({ theme }) => ({
padding: '1rem 0.5rem',
color: 'black',
backgroundColor: theme.palette.grey[200],
maxHeight: '240px',
// maxHeight: '240px',
}));
const ItemNotificationStyle = styled(Card)(({ theme }) => ({
@@ -94,7 +94,7 @@ export default function CardNotification({ data }: NotificationProps) {
</Button>
</Stack>
<ItemNotificationStyle sx={{ marginTop: 2, overflowY: 'auto', maxHeight: '154px' }}>
<ItemNotificationStyle sx={{ marginTop: 2, overflowY: 'auto', maxHeight: '180px' }}>
{(data && data.length)
? data.map(({ info, date, time }, key) => (
<div key={key}>

View File

@@ -79,11 +79,12 @@ export default function DialogMember(member, handleSubmitSuccess) {
{ member && member?.current_plan?.corporate_benefits?.map((corporateBenefit, index) => {return (
<Grid item sm={6} key={index}>
<Card sx={{p: 2}}>
<Typography variant="body1" sx={{fontWeight: 500}}>{corporateBenefit.benefit.code}</Typography>
<Typography variant="body2">{corporateBenefit.benefit.description}</Typography>
<Typography variant="body1" sx={{fontWeight: 500}}>{corporateBenefit.benefit.description}</Typography>
<Typography variant="body2">{corporateBenefit.benefit.code}</Typography>
<Typography variant="body2" sx={{ marginTop: 2 }}>Yearly Limits</Typography>
<BorderLinearProgress variant="determinate" value={100 - (corporateBenefit.limit_amount ? ((corporateBenefit.usage ?? 0) / corporateBenefit.limit_amount) : 0)} sx={{ mb: 1 }} />
<Typography sx={{ textAlign: 'right'}}>{corporateBenefit.usage ?? 0} / {fCurrency(corporateBenefit.limit_amount ?? 0)}</Typography>
<Typography sx={{ textAlign: 'right'}}>{corporateBenefit.usage ?? 0} /
{corporateBenefit.limit_ammount < 9999999999 ? fCurrency(corporateBenefit.limit_amount ?? 0) : 'As Charge' }</Typography>
</Card>
</Grid>
)})}

View File

@@ -5,8 +5,8 @@ import { Card, Divider, Link, Stack, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';
// Component
import MuiDialog from '../../components/MuiDialog';
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
// Sections
import DialogDetailClaim from './DialogDetailClaim';
type DataContent = {
info: string;

View File

@@ -25,6 +25,48 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
},
}));
// ----------------------------------------------------------------------
// Files Diagnosa
const fileDiagnosaInput = useRef<HTMLInputElement>(null);
const [fileDiagnosas, setFileDiagnosas] = useState([]);
const handleDiagnosaInputChange = (event) => {
if (event.target.files[0]) {
setFileDiagnosas([...fileDiagnosas, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeDiagnosaFiles = (filesState, index) => {
setFileDiagnosas(
filesState.filter((file, fileIndex) => {
return fileIndex != index;
})
);
};
// ----------------------------------------------------------------------
// Files Result Kondisi
const fileKondisiInput = useRef<HTMLInputElement>(null);
const [fileKondisis, setFileKondisis] = useState([]);
const handleKondisiInputChange = (event) => {
if (event.target.files[0]) {
setFileKondisis([...fileKondisis, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeKondisiFiles = (filesState, index) => {
setFileKondisis(
filesState.filter((file, fileIndex) => {
return fileIndex != index;
})
);
};
// ----------------------------------------------------------------------
// Files Result Hasil Penunjang
@@ -53,9 +95,11 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
function submitRequest() {
setSubmitLoading(true);
const formData = makeFormData({
'member_id' : member.id,
'result_files' : fileHasilPenunjangs
})
member_id: member.id,
result_files: fileHasilPenunjangs,
diagnosa_files: fileDiagnosas,
kondisi_files: fileKondisis
});
axios
.post('/claim-requests', formData)
.then((response) => {
@@ -106,57 +150,164 @@ export default function FormRequestClaim({ member, handleSubmitSuccess }) {
/>
<Typography sx={{ textAlign: 'right', marginTop: 1 }}>
{fCurrency(member?.current_plan?.usage ?? 0)} /{' '}
{fCurrency(member?.current_plan?.limit_rules ?? 0)}
{member?.current_plan?.limit_rules < 9999999999 ? fCurrency(member?.current_plan?.limit_rules ?? 0) : 'As Charge' }
</Typography>
</Card>
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
Hasil Penunjang
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileHasilPenunjangs &&
fileHasilPenunjangs.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeFiles(fileHasilPenunjangs, index);
}}
></Iconify>
</Stack>
))}
{/* <Stack direction="row" justifyContent={'space-between'}>
<Typography>Nama File .pdf</Typography>
<Iconify icon="eva:trash-2-outline" color={'darkred'}></Iconify>
</Stack> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={4}
sx={{ marginY: 2 }}
>
{/* -------------------------------Upload Dokumen Kondisi------------------------------- */}
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill"></Iconify> Dokumen Kondisi
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileKondisis &&
fileKondisis.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: "text.secondary" }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeKondisiFiles(fileKondisis, index);
}}
></Iconify>
</Stack>
))}
{/* <Stack direction="row" justifyContent={'space-between'}>
<Typography>Nama File .pdf</Typography>
<Iconify icon="eva:trash-2-outline" color={'darkred'}></Iconify>
</Stack> */}
</Stack>
{/* { JSON.stringify(filesResult) } */}
<input
type="file"
id="file"
ref={fileKondisiInput}
style={{ display: 'none' }}
multiple
onChange={handleKondisiInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileKondisiInput.current.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
{/* -------------------------------Upload Dokumen Diagnosa------------------------------- */}
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill"></Iconify> Dokumen Diagnosa
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileDiagnosas &&
fileDiagnosas.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: "text.secondary" }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeDiagnosaFiles(fileDiagnosas, index);
}}
></Iconify>
</Stack>
))}
{/* <Stack direction="row" justifyContent={'space-between'}>
<Typography>Nama File .pdf</Typography>
<Iconify icon="eva:trash-2-outline" color={'darkred'}></Iconify>
</Stack> */}
</Stack>
{/* { JSON.stringify(filesResult) } */}
<input
type="file"
id="file"
ref={fileDiagnosaInput}
style={{ display: 'none' }}
multiple
onChange={handleDiagnosaInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileDiagnosaInput.current.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
{/* -------------------------------Upload Dokumen Hasil Penunjang------------------------------- */}
<Stack sx={{ marginTop: 2 }}>
<Typography variant="body1" fontWeight={600}>
<Iconify icon="eva:file-text-fill"></Iconify> Dokumen Hasil Penunjang
</Typography>
{/* <Typography variant="body2">Hasil Lab, </Typography> */}
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileHasilPenunjangs &&
fileHasilPenunjangs.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: "text.secondary" }}>{file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeFiles(fileHasilPenunjangs, index);
}}
></Iconify>
</Stack>
))}
{/* <Stack direction="row" justifyContent={'space-between'}>
<Typography>Nama File .pdf</Typography>
<Iconify icon="eva:trash-2-outline" color={'darkred'}></Iconify>
</Stack> */}
</Stack>
{/* { JSON.stringify(filesResult) } */}
<input
type="file"
id="file"
ref={fileHasilPenunjangInput}
style={{ display: 'none' }}
multiple
onChange={handleResultInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileHasilPenunjangInput.current.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add File
</LoadingButton>
</Stack>
{/* { JSON.stringify(filesResult) } */}
<input
type="file"
id="file"
ref={fileHasilPenunjangInput}
style={{ display: 'none' }}
multiple
onChange={handleResultInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileHasilPenunjangInput.current.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
</Stack>
<LoadingButton

View File

@@ -40,6 +40,7 @@ import { useSearchParams } from 'react-router-dom';
import { fSplit } from '@/utils/formatNumber';
import { Chip } from '@mui/material';
import { enqueueSnackbar } from 'notistack';
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
/* ---------------------------------- types --------------------------------- */
type PaginationTableProps = {
@@ -175,6 +176,7 @@ function EnhancedTableHead({ order, orderBy, onRequestSort }: EnhancedTableProps
}
/* -------------------------------------------------------------------------- */
// TODO Don't use emit
export default function TableList(props: any) {
const [dataTable, setDataTable] = useState([]);
const [paginationTable, setPaginationTable] = useState<PaginationTableProps>({
@@ -305,15 +307,40 @@ export default function TableList(props: any) {
setPaginationTable(response.data.data);
setRowsPerPage(response.data.data.per_page);
setIsLoading(false);
// TODO Remove This
props.dataLoaded(response.data.data.data);
})();
}, [appliedParams, searchParams, order, orderBy, setSearchParams]);
// ---------------------------------------------------------
// Dialog Detail Claim
const [openDialogDetailClaim, setOpenDialogDetailClaim] = useState(false);
const [loadingClaimDetail, setLoadingClaimDetail] = useState(true);
const [currentClaim, setCurrentClaim] = useState(null);
function handleShowClaim(claimRequest) {
setLoadingClaimDetail(true);
setOpenDialogDetailClaim(true);
axios.get(`/claim-requests/${claimRequest.id}`)
.then(({data}) => {
console.log(data.data);
setCurrentClaim(data.data);
setLoadingClaimDetail(false);
})
.catch((err) => {
enqueueSnackbar(err.message, {variant: 'error'})
})
}
// -----------------------------------------------------------------
// Download LOG
function handleDownloadLog(claimRequest) {
axios.get(`claim-requests/${claimRequest.id}/log`, {
responseType: 'blob'
})
async function handleDownloadLog(claimRequest) {
return axios
.get(`claim-requests/${claimRequest.id}/log`, {
responseType: 'blob',
})
.then((response) => {
window.open(URL.createObjectURL(response.data));
// setLoadingLog(false);
@@ -323,9 +350,9 @@ export default function TableList(props: any) {
// setLoadingLog(false);
// })
.catch((response) => {
enqueueSnackbar(response.message, {variant: 'error'})
enqueueSnackbar(response.message, { variant: 'error' });
// setLoadingLog(false);
})
});
}
return (
@@ -379,7 +406,11 @@ export default function TableList(props: any) {
>
{statusOptions &&
statusOptions.map((option, index) => (
<MenuItem value={option} sx={{ textTransform: 'capitalize' }} key={index}>
<MenuItem
value={option}
sx={{ textTransform: 'capitalize' }}
key={index}
>
{option}
</MenuItem>
))}
@@ -427,7 +458,9 @@ export default function TableList(props: any) {
color: palette.dark.success.darker,
},
}}
onClick={() => {handleDownloadLog(row)}}
onClick={() => {
handleDownloadLog(row);
}}
>
Download LOG
</Button>
@@ -451,6 +484,16 @@ export default function TableList(props: any) {
sx={{ textTransform: 'capitalize' }}
/>
</TableCell>
<TableCell align="right">
<IconButton
onClick={() => {
handleShowClaim(row);
}}
>
<Iconify icon="eva:eye-fill" />
</IconButton>
</TableCell>
</TableRow>
))
) : (
@@ -475,6 +518,16 @@ export default function TableList(props: any) {
</Grid>
{/* End Field 2 */}
</Grid>
<DialogDetailClaim
openDialog={openDialogDetailClaim}
setOpenDialog={setOpenDialogDetailClaim}
title={{ name: 'Claim Request Detail' }}
data={{ claim: currentClaim, isLoading: loadingClaimDetail, handleDownloadLog }}
></DialogDetailClaim>
</Card>
);
}

View File

@@ -2,8 +2,8 @@ import { format, getTime, formatDistanceToNow } from 'date-fns';
// ----------------------------------------------------------------------
export function fDate(date: Date | string | number) {
return format(new Date(date), 'dd MMMM yyyy');
export function fDate(date: Date | string | number, dateFormat = 'dd MMMM yyyy' ) {
return format(new Date(date), dateFormat);
}
export function fDateTime(date: Date | string | number) {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More