[WIP] Update Claim
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Modules\Internal\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\Benefit;
|
||||
use App\Models\Claim;
|
||||
use App\Models\Icd;
|
||||
@@ -10,6 +11,7 @@ use App\Services\ClaimService;
|
||||
use Illuminate\Contracts\Support\Renderable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Modules\Internal\Transformers\ClaimShowResource;
|
||||
|
||||
class ClaimController extends Controller
|
||||
{
|
||||
@@ -87,13 +89,21 @@ class ClaimController extends Controller
|
||||
->with([
|
||||
'member',
|
||||
'member.currentPlan',
|
||||
'member.currentPlan.benefits',
|
||||
'member.currentCorporate',
|
||||
'member.currentPolicy',
|
||||
'diagnosis',
|
||||
'diagnoses',
|
||||
'benefit',
|
||||
'files'
|
||||
'files',
|
||||
'claimRequest',
|
||||
'claimRequest.files',
|
||||
'items',
|
||||
'items.claim_itemable'
|
||||
])
|
||||
->findOrFail($id);
|
||||
|
||||
return response()->json($claim);
|
||||
return Helper::responseJson(ClaimShowResource::make($claim));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,4 +141,92 @@ class ClaimController extends Controller
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function updateItems(Request $request, $id)
|
||||
{
|
||||
$request->validate([]);
|
||||
$claim = Claim::findOrFail($id);
|
||||
|
||||
$order = 1;
|
||||
$data = [];
|
||||
$claim->items()->forceDelete();
|
||||
foreach ($request->benefit_items as $benefitItem) {
|
||||
$benefit = Benefit::find($benefitItem['id']);
|
||||
if ($benefit) {
|
||||
$benefit->claimItem()->create([
|
||||
'claim_id' => $claim->id,
|
||||
'order' => $order,
|
||||
'name' => $benefit->code,
|
||||
'currency' => 'IDR',
|
||||
'nominal_ditagihkan' => $benefitItem['biaya_diajukan'] ?? 0,
|
||||
'nominal_dicover' => $benefitItem['biaya_disetujui'] ?? 0,
|
||||
'nominal_total' => $benefitItem['biaya_disetujui'] ?? 0,
|
||||
]);
|
||||
}
|
||||
|
||||
$order++;
|
||||
}
|
||||
return Helper::responseJson([], message: "Item Claim berhasil di update");
|
||||
}
|
||||
|
||||
public function updateDiagnosis(Request $request, $id)
|
||||
{
|
||||
$request->validate([]);
|
||||
$claim = Claim::findOrFail($id);
|
||||
|
||||
$claim->diagnoses()->forceDelete();
|
||||
if ($request->primary) {
|
||||
foreach ($request->primary as $diagnosisId) {
|
||||
$claim->diagnoses()->create([
|
||||
'claim_id' => $claim->id,
|
||||
'type' => 'primary',
|
||||
'diagnosis_id' => $diagnosisId,
|
||||
'note' => '',
|
||||
'description' => '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->secondary) {
|
||||
foreach ($request->secondary as $diagnosisId) {
|
||||
$claim->diagnoses()->create([
|
||||
'claim_id' => $claim->id,
|
||||
'type' => 'secondary',
|
||||
'diagnosis_id' => $diagnosisId,
|
||||
'note' => '',
|
||||
'description' => '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return Helper::responseJson([], message: "Diagnosis berhasil di update");
|
||||
}
|
||||
|
||||
public function decline($id)
|
||||
{
|
||||
$claim = Claim::findOrFail($id);
|
||||
$claim->status = 'declined';
|
||||
$claim->save();
|
||||
|
||||
return Helper::responseJson($claim, message: "Claim berhasil di decline");
|
||||
}
|
||||
|
||||
public function approve($id)
|
||||
{
|
||||
$claim = Claim::findOrFail($id);
|
||||
$claim->status = 'approved';
|
||||
$claim->save();
|
||||
|
||||
return Helper::responseJson($claim, message: "Claim berhasil di approve");
|
||||
}
|
||||
|
||||
public function reOpen($id)
|
||||
{
|
||||
$claim = Claim::findOrFail($id);
|
||||
$claim->status = 'received';
|
||||
$claim->save();
|
||||
|
||||
return Helper::responseJson($claim, message: "Claim berhasil di approve");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,10 @@ namespace Modules\Internal\Http\Controllers\Api;
|
||||
|
||||
use App\Helpers\Helper;
|
||||
use App\Models\ClaimRequest;
|
||||
use App\Models\Member;
|
||||
use Exception;
|
||||
use App\Services\ClaimService;
|
||||
use Illuminate\Contracts\Support\Renderable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Knp\Snappy\Pdf;
|
||||
use Modules\Internal\Transformers\ClaimRequestResource;
|
||||
use Modules\Internal\Transformers\ClaimRequestShowResource;
|
||||
|
||||
@@ -115,19 +113,31 @@ class ClaimRequestController extends Controller
|
||||
$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'
|
||||
]);
|
||||
try {
|
||||
|
||||
// Create New Claim
|
||||
$newClaim = ClaimService::storeClaim(member: $member, status: 'received', claimRequest: $claimRequest);
|
||||
|
||||
// Update Claim Request Status & Link with Claim
|
||||
$claimRequest->status = 'approved';
|
||||
$claimRequest->claim_id = $newClaim->id;
|
||||
$claimRequest->save();
|
||||
|
||||
// Store Generated Documents LOG
|
||||
$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'
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
|
||||
}
|
||||
|
||||
return $claimRequest;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
101
Modules/Internal/Http/Controllers/Api/OptionController.php
Normal file
101
Modules/Internal/Http/Controllers/Api/OptionController.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Internal\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Icd;
|
||||
use Illuminate\Contracts\Support\Renderable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
|
||||
class OptionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
* @return Renderable
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'type' => 'required',
|
||||
'search' => 'required'
|
||||
]);
|
||||
|
||||
switch ($request->type) {
|
||||
case 'diagnosis':
|
||||
$icds = Icd::query()
|
||||
->where('code', 'LIKE', '%'.$request->search.'%')
|
||||
->orWhere('name', 'LIKE', '%'.$request->search.'%')
|
||||
->limit('10')
|
||||
->get();
|
||||
|
||||
return $icds;
|
||||
break;
|
||||
|
||||
default:
|
||||
# code...
|
||||
break;
|
||||
}
|
||||
|
||||
return view('internal::index');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ 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\OptionController;
|
||||
use Modules\Internal\Http\Controllers\Api\OrganizationController;
|
||||
use Modules\Internal\Http\Controllers\Api\PlanController;
|
||||
use Modules\Internal\Http\Controllers\Api\ProvinceController;
|
||||
@@ -119,6 +120,11 @@ Route::prefix('internal')->group(function () {
|
||||
Route::get('members/{member_id}/benefits', [MemberController::class, 'benefits']);
|
||||
|
||||
Route::get('claims', [ClaimController::class, 'index']);
|
||||
Route::post('claims/{id}/update-items', [ClaimController::class, 'updateItems'])->name('claim.update-items');
|
||||
Route::post('claims/{id}/update-diagnosis', [ClaimController::class, 'updateDiagnosis'])->name('claim.update-diagnosis');
|
||||
Route::post('claims/{id}/decline', [ClaimController::class, 'decline'])->name('claim.decline');
|
||||
Route::post('claims/{id}/approve', [ClaimController::class, 'approve'])->name('claim.approve');
|
||||
Route::post('claims/{id}/re-open', [ClaimController::class, 'reOpen'])->name('claim.re-open');
|
||||
Route::post('claims', [ClaimController::class, 'store']);
|
||||
Route::get('claims/{id}', [ClaimController::class, 'show']);
|
||||
Route::post('check-limit', [ClaimController::class, 'checkLimit']);
|
||||
@@ -142,4 +148,5 @@ Route::prefix('internal')->group(function () {
|
||||
Route::get('city', [CityController::class, 'index']);
|
||||
Route::get('district', [DistrictController::class, 'index']);
|
||||
Route::get('village', [VillageController::class, 'index']);
|
||||
Route::get('options', [OptionController::class, 'index']);
|
||||
});
|
||||
|
||||
42
Modules/Internal/Transformers/ClaimShowResource.php
Normal file
42
Modules/Internal/Transformers/ClaimShowResource.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Internal\Transformers;
|
||||
|
||||
use App\Models\Benefit;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ClaimShowResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
$data = parent::toArray($request);
|
||||
// $data['']
|
||||
$data['benefit_items'] = $this->items
|
||||
->filter(function ($item) {
|
||||
return $item->claim_itemable_type == Benefit::class;
|
||||
})
|
||||
->map(function ($item) {
|
||||
$itemData = $item->claim_itemable->toArray();
|
||||
$itemData['nominal_dicover'] = $item['nominal_dicover'] ?? 0;
|
||||
$itemData['nominal_ditagihkan'] = $item['nominal_ditagihkan'] ?? 0;
|
||||
$itemData['nominal_total'] = $item['nominal_total'] ?? 0;
|
||||
|
||||
// For React Frotnend
|
||||
$itemData['biaya_diajukan'] = $itemData['nominal_ditagihkan'];
|
||||
$itemData['biaya_disetujui'] = $itemData['nominal_dicover'];
|
||||
|
||||
return $itemData;
|
||||
});
|
||||
|
||||
$data['primary_diagnosis'] = $this->diagnoses->filter(function($diagnosis){ return $diagnosis->type == 'primary';})->values();
|
||||
$data['secondary_diagnosis'] = $this->diagnoses->filter(function($diagnosis){ return $diagnosis->type == 'secondary';})->values();
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,11 @@ class Benefit extends Model
|
||||
// return $this->belongsTo(Benefit::class, 'benefit_code', 'code');
|
||||
// }
|
||||
|
||||
public function claimItem()
|
||||
{
|
||||
return $this->morphMany(ClaimItem::class, 'claim_itemable');
|
||||
}
|
||||
|
||||
public function plans()
|
||||
{
|
||||
return $this->belongsToMany(Plan::class, 'corporate_benefits', 'benefit_id', 'id')
|
||||
|
||||
@@ -139,6 +139,11 @@ class Claim extends Model
|
||||
return $this->morphMany(File::class, 'fileable');
|
||||
}
|
||||
|
||||
public function items()
|
||||
{
|
||||
return $this->hasMany(ClaimItem::class, 'claim_id');
|
||||
}
|
||||
|
||||
public function member()
|
||||
{
|
||||
return $this->belongsTo(Member::class, 'member_id');
|
||||
|
||||
@@ -10,4 +10,12 @@ class ClaimDiagnosis extends Model
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'claim_diagnosis';
|
||||
|
||||
public $fillable = [
|
||||
'claim_id',
|
||||
'type',
|
||||
'diagnosis_id',
|
||||
'note',
|
||||
'description',
|
||||
];
|
||||
}
|
||||
|
||||
32
app/Models/ClaimItem.php
Normal file
32
app/Models/ClaimItem.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Blameable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class ClaimItem extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes, Blameable;
|
||||
|
||||
protected $fillable = [
|
||||
'claim_id',
|
||||
'order',
|
||||
'currency',
|
||||
'nominal_ditagihkan',
|
||||
'nominal_dicover',
|
||||
'nominal_total',
|
||||
];
|
||||
|
||||
public function claim()
|
||||
{
|
||||
return $this->belongsTo(Claim::class, 'claim_id');
|
||||
}
|
||||
|
||||
public function claim_itemable()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,8 @@ class File extends Model
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
'fileable_type',
|
||||
'fileable_id'
|
||||
];
|
||||
|
||||
public $appends = [
|
||||
|
||||
@@ -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_items', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('claim_id');
|
||||
$table->smallInteger('order')->default(0);
|
||||
$table->morphs('claim_itemable');
|
||||
$table->string('currency');
|
||||
$table->string('nominal_ditagihkan')->nullable();
|
||||
$table->string('nominal_dicover')->nullable();
|
||||
$table->string('nominal_total')->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_items');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
import axios from "@/utils/axios";
|
||||
import { Autocomplete, TextField, CircularProgress } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function AutocompleteDiagnosis({ onChange, textLabel, currentValue = null })
|
||||
{
|
||||
const [options, setOptions] = useState([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
return (<Autocomplete
|
||||
defaultValue={currentValue ?? null}
|
||||
onChange={(event, value) => {onChange(value)}}
|
||||
isOptionEqualToValue={(option, value) => option.title === value.title}
|
||||
getOptionLabel={(option) => option.title}
|
||||
options={options}
|
||||
loading={loading}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label={textLabel != null ? textLabel : "Diagnosa ICD-X"}
|
||||
onChange={(event) => {
|
||||
setLoading(true);
|
||||
axios.get('options?type=diagnosis&search='+event.target.value)
|
||||
.then((res) => {
|
||||
setOptions(res.data.map(function(icd) {
|
||||
return {
|
||||
title: icd.code + '-' + icd.name,
|
||||
value: icd.id
|
||||
}
|
||||
}))
|
||||
})
|
||||
.then(() => {
|
||||
setLoading(false);
|
||||
})
|
||||
}}
|
||||
InputProps={{
|
||||
...params.InputProps,
|
||||
endAdornment: (
|
||||
<>
|
||||
{loading ? <CircularProgress color="inherit" size={20} /> : null}
|
||||
{params.InputProps.endAdornment}
|
||||
</>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
395
frontend/dashboard/src/pages/Claims/Show.tsx
Normal file
395
frontend/dashboard/src/pages/Claims/Show.tsx
Normal file
@@ -0,0 +1,395 @@
|
||||
import * as Yup from 'yup';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import {
|
||||
Autocomplete,
|
||||
Box,
|
||||
Button,
|
||||
Card,
|
||||
Collapse,
|
||||
Container,
|
||||
Divider,
|
||||
Grid,
|
||||
InputAdornment,
|
||||
Paper,
|
||||
Stack,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableRow,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
|
||||
import { FormProvider, RHFCheckbox, RHFSelect, RHFTextField } from '../../components/hook-form';
|
||||
import Page from '../../components/Page';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import MemberSelectDialog from '../../components/dialogs/MemberSelectDialog';
|
||||
import { styled } from '@mui/system';
|
||||
import axios from '../../utils/axios';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
import { fCurrency } from '../../utils/formatNumber';
|
||||
import Iconify from '../../components/Iconify';
|
||||
import Form from './Form';
|
||||
import Documents from './components/Documents';
|
||||
import DiagnosisHistory from './components/DiagnosisHistory';
|
||||
import ClaimItems from './components/ClaimItems';
|
||||
import DialogMemberBenefit from './components/DialogMemberBenefit';
|
||||
import AutocompleteDiagnosis from '@/components/autocomplete/AutocompleteDiagnosis';
|
||||
|
||||
export default function ClaimsCreateUpdate() {
|
||||
const { themeStretch } = useSettings();
|
||||
const { id } = useParams();
|
||||
|
||||
const isEdit = id ? true : false;
|
||||
|
||||
const [currentClaim, setCurrentClaim] = useState();
|
||||
const [documents, setDocuments] = useState([]);
|
||||
|
||||
const Item = styled(Paper)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
|
||||
...theme.typography.body2,
|
||||
padding: theme.spacing(1),
|
||||
textAlign: 'center',
|
||||
color: theme.palette.text.secondary,
|
||||
}));
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Claim Item
|
||||
const [claimItems, setClaimItems] = useState([]);
|
||||
const [dialogAddClaimItemOpen, setDialogAddClaimItemOpen] = useState(false);
|
||||
const [loadingClaimItems, setLoadingClaimItems] = useState(false);
|
||||
|
||||
const handleAddClaimItems = (items) => {
|
||||
setClaimItems([...claimItems, ...items]);
|
||||
};
|
||||
|
||||
const handleSaveClaimItems = () => {
|
||||
console.log('Storing ', claimItems);
|
||||
setLoadingClaimItems(true);
|
||||
axios
|
||||
.post(`claims/${id}/update-items`, {
|
||||
benefit_items: claimItems.map((benefit) => {
|
||||
return {
|
||||
id: benefit.id,
|
||||
biaya_diajukan: benefit.biaya_diajukan,
|
||||
biaya_disetujui: benefit.biaya_disetujui,
|
||||
};
|
||||
}),
|
||||
})
|
||||
.then((res) => {
|
||||
enqueueSnackbar(res.data.message, { variant: 'success' });
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoadingClaimItems(false);
|
||||
enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' });
|
||||
})
|
||||
.then(() => {
|
||||
setLoadingClaimItems(false);
|
||||
});
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// Diagnosis
|
||||
const [primaryDiagnosis, setPrimaryDiagnosis] = useState(null);
|
||||
const [secondaryDiagnosis, setSecondaryDiagnosis] = useState(null);
|
||||
const [loadingDiagnosis, setLoadingDiagnosis] = useState(false);
|
||||
|
||||
const handlePrimaryDiagnosisChange = ({ title, value }) => {
|
||||
setPrimaryDiagnosis(value);
|
||||
};
|
||||
|
||||
const handleSecondaryDiagnosisChange = ({ title, value }) => {
|
||||
setSecondaryDiagnosis(value);
|
||||
};
|
||||
|
||||
const handleSaveDiagnosis = () => {
|
||||
setLoadingDiagnosis(true);
|
||||
|
||||
axios
|
||||
.post(`claims/${id}/update-diagnosis`, {
|
||||
primary: [primaryDiagnosis],
|
||||
secondary: [secondaryDiagnosis],
|
||||
})
|
||||
.then((res) => {
|
||||
enqueueSnackbar(res.data.message, { variant: 'success' });
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoadingDiagnosis(false);
|
||||
enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' });
|
||||
})
|
||||
.then(() => {
|
||||
setLoadingDiagnosis(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleDecline = () => {
|
||||
axios
|
||||
.post(`claims/${id}/decline`)
|
||||
.then((res) => {
|
||||
enqueueSnackbar(res.data.message, { variant: 'success' });
|
||||
setCurrentClaim({ ...currentClaim, status: 'declined' });
|
||||
})
|
||||
.catch((err) => {
|
||||
// setLoadingDiagnosis(false)
|
||||
enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' });
|
||||
})
|
||||
.then(() => {
|
||||
// setLoadingDiagnosis(false)
|
||||
});
|
||||
};
|
||||
|
||||
const handleApprove = () => {
|
||||
axios
|
||||
.post(`claims/${id}/approve`)
|
||||
.then((res) => {
|
||||
enqueueSnackbar(res.data.message, { variant: 'success' });
|
||||
setCurrentClaim({ ...currentClaim, status: 'approved' });
|
||||
})
|
||||
.catch((err) => {
|
||||
// setLoadingDiagnosis(false)
|
||||
enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' });
|
||||
})
|
||||
.then(() => {
|
||||
// setLoadingDiagnosis(false)
|
||||
});
|
||||
};
|
||||
|
||||
const handleReOpen = () => {
|
||||
axios
|
||||
.post(`claims/${id}/re-open`)
|
||||
.then((res) => {
|
||||
enqueueSnackbar(res.data.message, { variant: 'success' });
|
||||
setCurrentClaim({ ...currentClaim, status: 'received' });
|
||||
})
|
||||
.catch((err) => {
|
||||
// setLoadingDiagnosis(false)
|
||||
enqueueSnackbar(err.response?.data?.message ?? err?.message, { variant: 'error' });
|
||||
})
|
||||
.then(() => {
|
||||
// setLoadingDiagnosis(false)
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
axios.get('/claims/' + id).then(({ data }) => {
|
||||
const claim = data.data;
|
||||
const allFiles = [...claim.claim_request.files, ...claim.files];
|
||||
|
||||
setCurrentClaim(claim);
|
||||
setDocuments(allFiles);
|
||||
setClaimItems(claim.benefit_items);
|
||||
});
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<Page title={`Claim : ${currentClaim?.code}`}>
|
||||
<Container maxWidth={themeStretch ? false : 'xl'}>
|
||||
<Stack direction="row" alignItems="center" justifyContent="space-between">
|
||||
<HeaderBreadcrumbs
|
||||
heading={`Claim : ${currentClaim?.code}`}
|
||||
links={[
|
||||
{ name: 'Dashboard', href: '/dashboard' },
|
||||
{
|
||||
name: 'Claim',
|
||||
href: '/claims',
|
||||
},
|
||||
{ name: currentClaim?.code ?? '' },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* Action Button */}
|
||||
<Stack direction="row" spacing={2} sx={{ position: 'relative', bottom: '15px' }}>
|
||||
{(currentClaim?.status == 'requested' || currentClaim?.status == 'received') && (
|
||||
<>
|
||||
<LoadingButton
|
||||
loading={false}
|
||||
variant="outlined"
|
||||
color="error"
|
||||
onClick={() => {
|
||||
handleDecline();
|
||||
}}
|
||||
>
|
||||
Decline
|
||||
</LoadingButton>
|
||||
<LoadingButton
|
||||
loading={false}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
handleApprove();
|
||||
}}
|
||||
>
|
||||
Approve
|
||||
</LoadingButton>
|
||||
</>
|
||||
)}
|
||||
{(currentClaim?.status == 'declined' || currentClaim?.status == 'approved') && (
|
||||
<LoadingButton
|
||||
loading={false}
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
handleReOpen();
|
||||
}}
|
||||
>
|
||||
Re-Open
|
||||
</LoadingButton>
|
||||
)}
|
||||
|
||||
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Paper variant="outlined" sx={{ background: '#f4f6f8', p: 2, marginY: 2 }}>
|
||||
<Typography>Status : {currentClaim?.status}</Typography>
|
||||
</Paper>
|
||||
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={5}>
|
||||
{/* Dokumen Tambahan */}
|
||||
<Documents files={documents}></Documents>
|
||||
|
||||
{/* Riwayat Diagnosa */}
|
||||
<DiagnosisHistory diagnosis={[]}></DiagnosisHistory>
|
||||
|
||||
{/* Ringkasan Data Member */}
|
||||
<Paper variant="outlined" sx={{ background: '#f4f6f8', p: 2, marginTop: 2 }}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Stack direction="row" alignItems="center" spacing={1}>
|
||||
<Iconify icon="eva:bell-fill" />
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
Ringkasan Data Nasabah
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Iconify icon="eva:eye-fill" />
|
||||
</Stack>
|
||||
|
||||
<Paper sx={{ background: 'white', marginTop: 2, p: 2 }}>
|
||||
<Stack>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<Stack>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
Nama Lengkap
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
{currentClaim?.member?.full_name}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
Nomor Polis
|
||||
</Typography>
|
||||
<Typography variant="body2">{currentClaim?.member?.full_name}</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
Member ID
|
||||
</Typography>
|
||||
<Typography variant="body2">{currentClaim?.member?.member_id}</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
Tipe Claim
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
{currentClaim?.claim_request.payment_type_name}
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} md={6}>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
Tipe Nasabah
|
||||
</Typography>
|
||||
<Typography variant="body2">
|
||||
{currentClaim?.member?.current_corporate?.name}
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Paper>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={7}>
|
||||
{/* Diagnosis */}
|
||||
<Paper variant="outlined" sx={{ background: '#f4f6f8', p: 2 }}>
|
||||
<Paper variant="outlined" sx={{ background: 'white', p: 2 }}>
|
||||
<Stack spacing={2}>
|
||||
<AutocompleteDiagnosis
|
||||
onChange={handlePrimaryDiagnosisChange}
|
||||
textLabel="Diagnosa Utama (ICD-X)"
|
||||
currentValue={
|
||||
currentClaim?.primary_diagnosis ? currentClaim?.primary_diagnosis : null
|
||||
}
|
||||
></AutocompleteDiagnosis>
|
||||
<AutocompleteDiagnosis
|
||||
onChange={handleSecondaryDiagnosisChange}
|
||||
textLabel="Diagnosa Tambahan (ICD-X)"
|
||||
currentValue={
|
||||
currentClaim?.secondary_diagnosis ? currentClaim?.secondary_diagnosis : null
|
||||
}
|
||||
></AutocompleteDiagnosis>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
sx={{ marginTop: 2 }}
|
||||
loading={loadingDiagnosis}
|
||||
onClick={() => {
|
||||
handleSaveDiagnosis();
|
||||
}}
|
||||
>
|
||||
Simpan Claim Item
|
||||
</LoadingButton>
|
||||
</Paper>
|
||||
|
||||
<Paper variant="outlined" sx={{ background: '#f4f6f8', p: 2, marginTop: 2 }}>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Typography sx={{ marginBottom: 1 }}>Client Benefit Configuration</Typography>
|
||||
<Typography
|
||||
onClick={() => {
|
||||
setDialogAddClaimItemOpen(true);
|
||||
}}
|
||||
>
|
||||
+ Add Benefit
|
||||
</Typography>
|
||||
</Stack>
|
||||
<ClaimItems items={claimItems} setItems={setClaimItems}></ClaimItems>
|
||||
<Stack alignItems={'flex-end'}>
|
||||
<LoadingButton
|
||||
variant="contained"
|
||||
sx={{ marginTop: 2 }}
|
||||
loading={loadingClaimItems}
|
||||
onClick={() => {
|
||||
handleSaveClaimItems();
|
||||
}}
|
||||
>
|
||||
Simpan Claim Item
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
|
||||
<DialogMemberBenefit
|
||||
openDialog={dialogAddClaimItemOpen}
|
||||
setOpenDialog={setDialogAddClaimItemOpen}
|
||||
member={currentClaim?.member ?? null}
|
||||
onSubmit={handleAddClaimItems}
|
||||
/>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Container>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import Iconify from '@/components/Iconify';
|
||||
import { Divider, InputAdornment, Paper, Stack, TextField, Typography } from '@mui/material';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function ClaimItems({ items, setItems }) {
|
||||
|
||||
const handleChangeBiayaDiajukan = (event, itemIndex: Number) => {
|
||||
setItems(items.map((item, index) => {
|
||||
if (index == itemIndex) {
|
||||
return {...item, biaya_diajukan : event.target.value}
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
const handleChangeBiayaDisetujui = (event, itemIndex: Number) => {
|
||||
setItems(items.map((item, index) => {
|
||||
if (index == itemIndex) {
|
||||
return {...item, biaya_disetujui : event.target.value}
|
||||
} else {
|
||||
return item;
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
const calculateBiayaDitolak = (biayaDiajukan: Number | null, biayaDisetujui: Number | null) => {
|
||||
return (biayaDiajukan ? biayaDiajukan : 0) - (biayaDisetujui ? biayaDisetujui : 0)
|
||||
}
|
||||
|
||||
const handleDeleteItem = (itemIndex: Number) => {
|
||||
setItems(items.filter((item, index) => index != itemIndex))
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack spacing={2}>
|
||||
{items.length > 0 ? (
|
||||
items.map((item, index) => (
|
||||
<Paper variant="outlined" sx={{ background: 'white', p: 2 }} key={index}>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Typography>#{index+1} ({item.code}) {item.description}</Typography>
|
||||
<Iconify icon="eva:trash-fill" color="red" onClick={() => {handleDeleteItem(index)}}></Iconify>
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-evenly"
|
||||
divider={<Divider orientation="vertical" flexItem />}
|
||||
>
|
||||
<TextField label="Biaya Diajukan" variant="standard" fullWidth type="number" value={item.biaya_diajukan ?? 0} onChange={(event) => {handleChangeBiayaDiajukan(event, index)}}>
|
||||
<InputAdornment position="start">IDR</InputAdornment>
|
||||
{/* <InputMask mask="(0)999 999 99 99" maskChar=" " /> */}
|
||||
</TextField>
|
||||
<TextField label="Biaya Disetujui" variant="standard" fullWidth type="number" value={item.biaya_disetujui ?? 0} onChange={(event) => {handleChangeBiayaDisetujui(event, index)}}>
|
||||
<InputAdornment position="start">IDR</InputAdornment>
|
||||
{/* <InputMask mask="(0)999 999 99 99" maskChar=" " /> */}
|
||||
</TextField>
|
||||
<TextField label="Biaya Ditolak" variant="standard" fullWidth type="number" value={calculateBiayaDitolak(item.biaya_diajukan, item.biaya_disetujui)}>
|
||||
<InputAdornment position="start">IDR</InputAdornment>
|
||||
{/* <InputMask mask="(0)999 999 99 99" maskChar=" " /> */}
|
||||
</TextField>
|
||||
</Stack>
|
||||
</Paper>
|
||||
))
|
||||
) : (
|
||||
<Typography>No Benefit Item</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import Iconify from '@/components/Iconify';
|
||||
import { Paper, Stack, Typography } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function DiagnosisHistory({ diagnosis }) {
|
||||
function DiagnosaItem({ item }) {
|
||||
return (
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ p: 1 }}>
|
||||
<Stack>
|
||||
<Typography variant="body2" fontWeight="600">
|
||||
Nama Penyakit
|
||||
</Typography>
|
||||
<Typography variant="body2">Claim Terakhir : 23 Januari 2023 08:00</Typography>
|
||||
</Stack>
|
||||
<Iconify icon="eva:arrow-ios-forward-fill"></Iconify>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper variant="outlined" sx={{ background: '#f4f6f8', p: 2, marginTop: 2 }}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center">
|
||||
<Stack direction="row" alignItems="center" spacing={1}>
|
||||
<Iconify icon="eva:bell-fill" />
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
Riwayat Diagnosa
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Typography
|
||||
variant="body2"
|
||||
onClick={() => {
|
||||
setOpenDialogRequestDocument(true);
|
||||
}}
|
||||
>
|
||||
View All
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
<Paper sx={{ background: 'white', marginTop: 2 }}>
|
||||
{ diagnosis.length > 0 ? (
|
||||
<Stack sx={{ maxHeight: '250px', overflowY: 'scroll' }}>
|
||||
{ diagnosis.map((diagnosa, index) => (
|
||||
<DiagnosaItem item={diagnosa} key={index}></DiagnosaItem>
|
||||
)) }
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack sx={{ p: 1 }}>
|
||||
<Typography>Belum ada History Perawatan</Typography>
|
||||
</Stack>
|
||||
) }
|
||||
</Paper>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import MuiDialog from "@/components/MuiDialog";
|
||||
import { Button, Checkbox, Typography } from "@mui/material";
|
||||
import { Paper } from "@mui/material";
|
||||
import { Stack } from '@mui/material';
|
||||
import { useState } from "react";
|
||||
|
||||
|
||||
export default function DialogMemberBenefit({member, setOpenDialog, openDialog, onSubmit}) {
|
||||
|
||||
const benefits = member?.current_plan?.benefits ?? [];
|
||||
const [selectedBenefits, setSelectedBenefits] = useState([]);
|
||||
|
||||
const toggleBenefit = (benefit) => {
|
||||
if (selectedBenefits.includes(benefit)) {
|
||||
console.log('removing', benefit)
|
||||
setSelectedBenefits(selectedBenefits.filter((throughBenefit) => benefit.id != throughBenefit.id))
|
||||
} else {
|
||||
console.log('adding', benefit)
|
||||
setSelectedBenefits([...selectedBenefits, benefit])
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
onSubmit(selectedBenefits);
|
||||
console.log ('submitting')
|
||||
setOpenDialog(false);
|
||||
setSelectedBenefits([]);
|
||||
}
|
||||
|
||||
const getContent = () => (
|
||||
<Stack spacing={1} marginTop={2}>
|
||||
{ benefits.map((benefit, index) => (
|
||||
|
||||
<Paper sx={{ background: 'white', marginTop: 2, p: 2 }} key={index}>
|
||||
<Stack direction="row" justifyContent="space-between" alignItems='center'>
|
||||
<Stack>
|
||||
<Typography variant="body1" fontWeight={600}>{benefit.description}</Typography>
|
||||
<Typography variant="body2">{benefit.code}</Typography>
|
||||
</Stack>
|
||||
<Checkbox checked={selectedBenefits.includes(benefit)} onClick={() => { toggleBenefit(benefit) }}></Checkbox>
|
||||
</Stack>
|
||||
</Paper>
|
||||
))}
|
||||
|
||||
<Button variant="contained" onClick={() => {handleSubmit()}}>Tambah</Button>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<MuiDialog
|
||||
title={{name: "Add Member Benefit"}}
|
||||
openDialog={openDialog}
|
||||
setOpenDialog={setOpenDialog}
|
||||
content={getContent()}
|
||||
maxWidth="xl"
|
||||
/>
|
||||
);
|
||||
}
|
||||
68
frontend/dashboard/src/pages/Claims/components/Documents.tsx
Normal file
68
frontend/dashboard/src/pages/Claims/components/Documents.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import Iconify from '@/components/Iconify';
|
||||
import { Paper, Stack, Typography } from '@mui/material';
|
||||
import { useState } from 'react';
|
||||
|
||||
export default function Documents({ files }) {
|
||||
// --------------------------------------------------------------
|
||||
// Dialog Request Document
|
||||
const [openDialogRequestDocument, setOpenDialogRequestDocument] = useState(false);
|
||||
|
||||
function FileItem({item}) {
|
||||
function fileCategory(type: string) {
|
||||
switch(type) {
|
||||
case 'claim-result':
|
||||
return 'Claim Result';
|
||||
case 'claim-diagnosis':
|
||||
return 'Claim Diagnosis';
|
||||
case 'claim-condition':
|
||||
return 'Claim Condition';
|
||||
default:
|
||||
return 'Other File';
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack direction="row" justifyContent="space-between" alignItems="center" sx={{ p: 1 }}>
|
||||
<Stack>
|
||||
<Typography variant="body2" fontWeight="600">
|
||||
{ fileCategory(item.type) }
|
||||
</Typography>
|
||||
<Typography variant="body2"><a href={item.url} target="_blank">{ item.name }</a></Typography>
|
||||
</Stack>
|
||||
<Iconify icon="eva:arrow-ios-forward-fill"></Iconify>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper variant="outlined" sx={{ background: '#f4f6f8', p: 2 }}>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
Dokumen Tambahan
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
onClick={() => {
|
||||
setOpenDialogRequestDocument(true);
|
||||
}}
|
||||
>
|
||||
+ Request Document
|
||||
</Typography>
|
||||
</Stack>
|
||||
|
||||
<Paper sx={{ background: 'white', marginTop: 2 }}>
|
||||
{ files.length > 0 ? (
|
||||
<Stack sx={{ maxHeight: '250px', overflowY: 'scroll' }}>
|
||||
{ files.map((file, index) => (
|
||||
<FileItem item={file} key={index}></FileItem>
|
||||
)) }
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack sx={{ p: 1 }}>
|
||||
<Typography>Belum ada History Perawatan</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
</Paper>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
@@ -280,7 +280,7 @@ export default function Router() {
|
||||
},
|
||||
{
|
||||
path: 'claims/:id',
|
||||
element: <ClaimsCreate />,
|
||||
element: <ClaimShow />,
|
||||
},
|
||||
{
|
||||
path: 'profile',
|
||||
@@ -402,5 +402,6 @@ const Profile = Loadable(lazy(() => import('../pages/Profile/Index')));
|
||||
|
||||
const Claims = Loadable(lazy(() => import('../pages/Claims/Index')));
|
||||
const ClaimsCreate = Loadable(lazy(() => import('../pages/Claims/CreateUpdate')));
|
||||
const ClaimShow = Loadable(lazy(() => import('../pages/Claims/Show')));
|
||||
|
||||
const ClaimRequests = Loadable(lazy(() => import('../pages/ClaimRequests/Index')));
|
||||
Reference in New Issue
Block a user