finishing - tuning Diagnosis Exclusion

This commit is contained in:
korospace
2023-10-20 15:40:30 +07:00
parent 8a3b0f3d11
commit a1362b5362
10 changed files with 403 additions and 258 deletions

View File

@@ -277,6 +277,7 @@ class DiagnosisExclusionController extends Controller
$gender = implode(",", $gender);
$gender = trim($gender, ",");
$exclusion->rules()->corporate_id = $corporate_id;
$exclusion->rules()->updateOrCreate([
'exclusion_id' => $exclusion->id,
@@ -302,7 +303,6 @@ class DiagnosisExclusionController extends Controller
'values' => $data['min_age'] ?? '',
]);
$exclusion->rules()->updateOrCreate([
'exclusion_id' => $exclusion->id,
'name' => 'max_age',
@@ -330,6 +330,59 @@ class DiagnosisExclusionController extends Controller
return Helper::paginateResources(DiagnosisExclusionResource::collection($exclusions));
}
/**
* Bagaskoro BSD 20-10-2023
*
* Fungsi untuk get detil exclusion
*/
public function detilExclusion(Request $request, $corporate_id, $id_exclusion)
{
$corporate = Corporate::query()
->with(['currentPolicy', 'plans'])
->withCount('corporatePlans')
->withCount('employees')
->findOrFail($corporate_id);
$plans = $corporate['plans']->map(function ($plan) {
return $plan['code'];
});
$exclusions = Exclusion::query()
->where('id', $id_exclusion)
->where('type', 'diagnosis')
->where('deleted_at', null)
->with(['rules'])
->get();
$exclusion = DiagnosisExclusionResource::collection($exclusions);
return response()->json([
'error' => false,
'messages' => "success",
'data' => [
'exclusion' => empty($exclusion) ? [] : $exclusion[0],
'plans' => $plans,
]
],200);
}
/**
* Bagaskoro BSD 19-10-2023
*
* Fungsi untuk update status active
*/
protected function messages()
{
return [
'required' => ':attribute harus diisi',
'integer' => ':attribute harus angka',
'unique' => ':attribute (:input) sudah ada',
'max' => ':attribute maximal :max karakter',
'exists' => ':attribute (:input) tidak ditemukan',
'digits_between'=> ':attribute maximal :max digit minimal :min digit'
];
}
public function updateActivation(Request $request)
{
// validation rule
@@ -340,8 +393,20 @@ class DiagnosisExclusionController extends Controller
// validation error
if ($validator->fails()) {
return response()->json($validator->getMessageBag(),400);
return response()->json([
'error' => true,
'messages' => $validator->getMessageBag()
],400);
}
Exclusion::where('id', $request->id)->update([
'active' => $request->active
]);
return response()->json([
'error' => false,
'messages' => "status berhasil diupdate",
'data' => []
],200);
}
}

View File

@@ -105,8 +105,9 @@ Route::prefix('internal')->group(function () {
Route::get('corporates/{corporate_id}/diagnosis-exclusions', [DiagnosisExclusionController::class, 'index']);
Route::get('corporates/{corporate_id}/diagnosis-exclusions/{id_exclusion}', [DiagnosisExclusionController::class, 'detilExclusion']); // By Bagaskoro, get detil exclusion
Route::post('corporates/{corporate_id}/diagnosis-exclusions/store', [DiagnosisExclusionController::class, 'storeExclusion']);
Route::put('corporates/diagnosis-exclusions/update_activation', [DiagnosisExclusionController::class, 'updateActivation']);
Route::put('corporates/diagnosis-exclusions/update_activation', [DiagnosisExclusionController::class, 'updateActivation']); // By Bagaskoro, edit status aktif
Route::delete('diagnosis-exclusions/{id}', [DiagnosisExclusionController::class, 'destroy']);
Route::post('corporates/{corporate_id}/diagnosis-exclusions/import', [DiagnosisExclusionController::class, 'import']);

View File

@@ -40,7 +40,7 @@ class AppServiceProvider extends ServiceProvider
public function boot()
{
Schema::defaultStringLength(191);
Str::macro('initials', fn($value, $sep = ' ', $glue = '') => trim(collect(explode($sep, $value))->map(function ($segment) {
return $segment[0] ?? '';
})->join($glue)));
@@ -51,7 +51,7 @@ class AppServiceProvider extends ServiceProvider
// });
Corporate::updated(function ($model) {
$this->logAuditTrail($model, 'updated');
});
@@ -60,7 +60,7 @@ class AppServiceProvider extends ServiceProvider
});
Member::updated(function ($model) {
$this->logAuditTrail($model, 'updated');
});
@@ -71,7 +71,7 @@ class AppServiceProvider extends ServiceProvider
// Corporate Service
CorporateService::updated(function ($model) {
$this->logAuditTrail($model, 'updated');
});
@@ -81,7 +81,7 @@ class AppServiceProvider extends ServiceProvider
// Corporate Plans
CorporatePlan::updated(function ($model) {
$this->logAuditTrail($model, 'updated');
});
@@ -91,7 +91,7 @@ class AppServiceProvider extends ServiceProvider
// Corporate Benefits
CorporateBenefit::updated(function ($model) {
$this->logAuditTrail($model, 'updated');
});
@@ -102,21 +102,20 @@ class AppServiceProvider extends ServiceProvider
// Corporate Exclusion
ExclusionRules::updated(function ($model) {
$this->logAuditTrailExclusion($model, 'updated');
$this->logAuditTrail($model, 'updated');
});
ExclusionRules::deleted(function ($model) {
$this->logAuditTrailExclusion($model, 'deleted');
$this->logAuditTrail($model, 'deleted');
});
ExclusionImport::updated(function ($model) {
$this->logAuditTrailExclusion($model, 'updated');
$this->logAuditTrail($model, 'updated');
});
ExclusionImport::deleted(function ($model) {
$this->logAuditTrailExclusion($model, 'deleted');
$this->logAuditTrail($model, 'deleted');
});
// ICD or exlusion

View File

@@ -1,7 +1,7 @@
GENERATE_SOURCEMAP=false
PORT=8083
PORT=8000
REACT_APP_HOST_API_URL="https://aso-api.linksehat.dev/api/internal"
REACT_APP_HOST_API_URL="http://127.0.0.1:8000/api/internal"
VITE_API_URL="https://aso-api.linksehat.dev/api/internal"
VITE_API_URL="http://127.0.0.1:8000/api/internal"

View File

@@ -21,7 +21,7 @@ export default function ThemeColorPresets({ children }: Props) {
...defaultTheme,
palette: {
...defaultTheme.palette,
primary: setColor,
// primary: setColor,
},
customShadows: {
...defaultTheme.customShadows,

View File

@@ -1,7 +1,8 @@
// form
import { useFormContext, Controller } from 'react-hook-form';
// @mui
import { Checkbox, FormControlLabel, FormGroup, FormControlLabelProps } from '@mui/material';
import { Checkbox, FormControlLabel, FormGroup, FormControlLabelProps, SxProps } from '@mui/material';
import { Theme } from '@mui/system';
// ----------------------------------------------------------------------
@@ -77,9 +78,10 @@ interface optionsCustomInterface {
interface RHFCustomMultiCheckboxProps {
name: string;
options: optionsCustomInterface[];
sx?: SxProps<Theme>
}
export function RHFCustomMultiCheckbox({ name, options, ...other }: RHFCustomMultiCheckboxProps) {
export function RHFCustomMultiCheckbox({ name, options, sx, ...other }: RHFCustomMultiCheckboxProps) {
const { control } = useFormContext();
return (
@@ -93,7 +95,7 @@ export function RHFCustomMultiCheckbox({ name, options, ...other }: RHFCustomMul
: [...field.value, option.value];
return (
<FormGroup>
<FormGroup sx={{ ...sx }}>
{options.map((option, index) => (
<FormControlLabel
key={index}

View File

@@ -0,0 +1,273 @@
/**
* Core
* ============================================
*/
import axios from '@/utils/axios';
import { useForm } from 'react-hook-form';
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { Box, IconButton, Typography, Grid, Card, TextField, Autocomplete, Button } from '@mui/material';
/**
* Components
* ============================================
*/
import Page from '../../../components/Page';
import { LoadingButton } from "@mui/lab";
import { FormProvider, RHFCustomMultiCheckbox, RHFTextField } from '@/components/hook-form';
import { enqueueSnackbar } from 'notistack';
/**
* Icon
* ============================================
*/
import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew';
export default function Divisions() {
const pageTitle = 'Edit Exclusion';
const navigate = useNavigate()
const { corporate_id, exclusion_id } = useParams(); // id di url
const [ plans, setPlans ] = useState<string[]>([]); // option untuk field plans
// checkbox option
// ====================================
const msc_checkbox_option = [
{
value: 'm',
label: 'Member',
},
{
value: 's',
label: 'Spouse',
},
{
value: 'c',
label: 'Child',
},
]
const gender_checkbox_option = [
{
value: 'male',
label: 'Male',
},
{
value: 'female',
label: 'Female',
},
]
// setup form
// ====================================
const defaultValues: any = {
msc: [],
gender: [],
min_age: '',
max_age: '',
plans: [],
};
const methods = useForm<any>({
defaultValues
});
const { setValue, handleSubmit, reset, watch, formState: { isDirty, isSubmitting } } = methods;
const formValues = watch();
// set form value
// =====================================
useEffect(() => {
axios.get(`corporates/${corporate_id}/diagnosis-exclusions/${exclusion_id}`)
.then((res) => {
// plan option
setPlans(res.data.data.plans);
// set value
let res_plan = res.data.data.exclusion.rules.plan;
if (res_plan.length == 1 && res_plan[0] == '') {
res_plan = [];
}
reset({
msc : res.data.data.exclusion.rules.msc[0].split(','),
gender : res.data.data.exclusion.rules.gender[0].split(','),
min_age : res.data.data.exclusion.value_rules.min_age,
max_age : res.data.data.exclusion.value_rules.max_age,
plans : res_plan,
})
})
}, [exclusion_id]);
// Submit Form
// =====================================
const submitHandler = async (data: any) => {
let one_row = {
msc: {
m: data.msc.includes('m'),
s: data.msc.includes('s'),
c: data.msc.includes('c'),
},
gender : {
male : data.gender.includes('male') == true ? 1 : 0,
female: data.gender.includes('female') == true ? 1 : 0,
},
min_age: data.min_age,
max_age: data.max_age,
plan : data.plans.join(','),
icd_id : exclusion_id,
}
return axios
.post(`/corporates/${corporate_id}/diagnosis-exclusions/store`, {
type: 'one_row',
icd_id: exclusion_id,
one_row
})
.then((res) => {
enqueueSnackbar('Exclusion Updated', {
variant: 'success',
});
reset({
msc : data.msc,
gender : data.gender,
min_age : data.min_age,
max_age : data.max_age,
plans : data.plans,
})
return true;
})
.catch((res) => {
enqueueSnackbar('Terjadi kesalahan pada server', {
variant: 'error',
});
return true;
})
}
return (
<Page title={pageTitle}>
<Box sx={{ display: 'flex', alignItems: 'center', pl: '12px', mb: '40px' }}>
<IconButton size='large' color='inherit' onClick={() => navigate(`/corporates/${corporate_id}/diagnosis-exclusions`)} >
<ArrowBackIosNew/>
</IconButton>
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
{pageTitle}
</Typography>
</Box>
<Box sx={{ px: '28px' }}>
<FormProvider methods={methods} onSubmit={handleSubmit(submitHandler)}>
<Card sx={{ padding: '24px' }}>
<Grid container spacing={6}>
{/* MSC row */}
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="body1" component="div" color={'GrayText'}>
MSC* :
</Typography>
</Grid>
<Grid item xs={12}>
<RHFCustomMultiCheckbox name="msc" options={msc_checkbox_option} sx={{ flexDirection: 'row' }} />
</Grid>
</Grid>
</Grid>
{/* Gender row */}
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="body1" component="div" color={'GrayText'}>
Gender* :
</Typography>
</Grid>
<Grid item xs={12}>
<RHFCustomMultiCheckbox name="gender" options={gender_checkbox_option} sx={{ flexDirection: 'row' }} />
</Grid>
</Grid>
</Grid>
{/* Age row */}
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="body1" component="div" color={'GrayText'}>
Age* :
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<Grid container spacing={2}>
<Grid item xs={3} md={3}>
<RHFTextField
id="min_age"
name='min_age'
/>
</Grid>
<Grid item xs={3} md={3}>
<RHFTextField
id="max_age"
name='max_age'
/>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
{/* Plan Section */}
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="body1" component="div" color={'GrayText'}>
Plan* :
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<Autocomplete
options={plans}
fullWidth
multiple
limitTags={5}
getOptionLabel={(option) => {
return option ?? false
}}
value={formValues.plans}
isOptionEqualToValue={(option, value) =>{
return option === value
}}
renderInput={(params) => (
<TextField {...params} label="Plan" variant="outlined" />
)}
onChange={(event,value) => {
setValue('plans', value, {shouldDirty: true});
}}
/>
</Grid>
</Grid>
</Grid>
{/* Button Cancle & Save */}
<Grid item xs={12} md={12}>
<Box display="flex" justifyContent={'flex-end'}>
<Box display="flex" gap={1}>
<Button variant="outlined" color="inherit" onClick={() => navigate(`/corporates/${corporate_id}/diagnosis-exclusions`)}>
Cancel
</Button>
<LoadingButton disabled={!isDirty} type="submit" variant="contained" loading={isSubmitting}>
Save Changes
</LoadingButton>
</Box>
</Box>
</Grid>
</Grid>
</Card>
</FormProvider>
</Box>
</Page>
);
}

View File

@@ -476,22 +476,36 @@ export default function List(props: any) {
active: row.active == 1 ? 0 : 1,
})
.then((res) => {
// setDataTableData({
// ...dataTableData,
// data: dataTableData.data.map((model) => {
// let updatedModel = model;
// if (row.id == model.id) {
// updatedModel.active = res.data.corporate.active;
// }
// return updatedModel;
// }),
// });
setDataTableData({
...dataTableData,
data: dataTableData.data.map((model) => {
let updatedModel = model;
if (model.id == row.id) {
updatedModel.active = row.active == 1 ? 0 : 1;
}
return updatedModel;
}),
});
})
.catch((error) => {
enqueueSnackbar(
error.response.data.message ?? error.message ?? 'Failed Processing Request',
{ variant: 'error' }
);
if (error.response.status == 400) {
let data = error.response.data.messages;
for (const key in data) {
enqueueSnackbar(
data[key][0],
{ variant: 'error' }
);
}
}
else {
enqueueSnackbar(
error.response.data.message ?? error.message ?? 'Failed Processing Request',
{ variant: 'error' }
);
}
});
};
@@ -539,7 +553,7 @@ export default function List(props: any) {
<FindInPageOutlined />
Detail
</MenuItem>
<MenuItem onClick={() => navigate(`/corporates/${corporate_id}/diagnosis-exclusions/history`)} >
<MenuItem onClick={() => navigate(`/corporates/${corporate_id}/diagnosis-exclusions/${row.id}/edit`)} >
<EditOutlined />
Edit
</MenuItem>
@@ -630,222 +644,6 @@ export default function List(props: any) {
</Card>
</Box>
) : null }
{/* {open == true ? (
openEdit == false ? (
<Box sx={{ borderBottom: 1 }}>
<div>
<Typography variant="body" sx={{ fontWeight: 'bold' }}>
Excluded Only for :
</Typography>
{row.rules.msc && (
<Typography variant="body" component="div">
MSC : {row.rules.msc.join(', ') ?? '-'}
</Typography>
)}
{row.rules.gender && (
<Typography variant="body" component="div">
Gender : {row.rules.gender.join(', ') ?? '-'}
</Typography>
)}
{(row.rules.min_age || row.rules.max_age) && (
<Typography variant="body" component="div">
Age : {row.rules.min_age ?? '-'} - {row.rules.max_age ?? '-'}
</Typography>
)}
{row.rules.plan && (
<Typography variant="body" component="div">
Plan : {row.rules.plan.join(', ') ?? '-'}
</Typography>
)}
</div>
</Box>
) : (
// <CollapseEdit row={row} index={index} plans={plans} />
<Box sx={{ borderBottom: 1, pb: 2 }}>
<Typography variant="body2" sx={{ fontWeight: 'bold', mb: 2 }}>
Edit Exclusion :
</Typography>
<Stack direction={'column'} spacing={2}>
<FormControl fullWidth>
<Grid container spacing={2}>
<Grid item xs={2} md={2}>
<Typography id="demo-simple-select-label">MSC</Typography>
</Grid>
<Grid item xs={10} md={10}>
<Stack direction={'row'} spacing={2}>
<Grid item xs={3} md={3}>
<FormControlLabel
control={
<Checkbox
checked={row.value_rules.msc?.m == '1'}
onChange={(event) => {
// handleConfigExclusion(event, row, 'm', 'msc');
handleChange(index, event, 'msc', row.id, 'm');
}}
/>
}
label="Member"
/>
</Grid>
<Grid item xs={3} md={3}>
<FormControlLabel
control={
<Checkbox
checked={row.value_rules.msc?.s == '1'}
onChange={(event) => {
// handleConfigExclusion(event, row, 's', 'msc');
handleChange(index, event, 'msc', row.id, 's');
}}
/>
}
label="Spouse"
/>
</Grid>
<Grid item xs={3} md={3}>
<FormControlLabel
control={
<Checkbox
checked={row.value_rules.msc?.c == '1'}
onChange={(event) => {
// handleConfigExclusion(event, row, 'c', 'msc');
handleChange(index, event, 'msc', row.id, 'c');
}}
/>
}
label="Child"
/>
</Grid>
</Stack>
</Grid>
</Grid>
</FormControl>
<FormControl fullWidth>
<Grid container spacing={2}>
<Grid item xs={2} md={2}>
<Typography id="demo-simple-select-label">Gender</Typography>
</Grid>
<Grid item xs={10} md={10}>
<Stack direction={'row'} spacing={2}>
<Grid item xs={3} md={3}>
<FormControlLabel
control={
<Checkbox
checked={row.value_rules.gender?.male == '1'}
onChange={(event) => {
// handleConfigExclusion(event, row, 'male', 'gender');
handleChange(index, event, 'gender', row.id, 'male');
}}
/>
}
label="Male"
/>
</Grid>
<Grid item xs={3} md={3}>
<FormControlLabel
control={
<Checkbox
checked={row.value_rules.gender?.female == '1'}
onChange={(event) => {
// handleConfigExclusion(event, row, 'female', 'gender');
handleChange(index, event, 'gender', row.id, 'female');
}}
/>
}
label="Female"
/>
</Grid>
</Stack>
</Grid>
</Grid>
</FormControl>
<FormControl fullWidth>
<Grid container spacing={2}>
<Grid item xs={2} md={2}>
<Typography id="demo-simple-select-label">Age</Typography>
</Grid>
<Grid item xs={10} md={10}>
<Stack direction={'row'} spacing={2}>
<Grid item xs={3} md={3}>
<TextField
id="outlined-number"
type="number"
defaultValue={row.value_rules.min_age ?? ''}
onChange={(event) => {
handleMinAge(event);
handleChange(index, event, 'min_age', row.id);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
handleConfigExclusion(event, row, minAge, 'min_age');
}
}}
/>
</Grid>
<Grid item xs={3} md={3}>
<TextField
id="outlined-number"
type="number"
defaultValue={row.value_rules.max_age ?? ''}
onChange={(event) => {
handleMaxAge(event);
handleChange(index, event, 'max_age', row.id);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
handleConfigExclusion(event, row, maxAge, 'max_age');
}
}}
/>
</Grid>
</Stack>
</Grid>
</Grid>
</FormControl>
<FormControl fullWidth>
<Grid container spacing={2}>
<Grid item xs={2} md={2}>
<Typography id="demo-simple-select-label">Plan</Typography>
</Grid>
<Grid item xs={10} md={10}>
<Stack direction={'row'} spacing={2}>
<Grid item xs={12} md={12}>
<Autocomplete
id="combo-box-demo"
options={plans}
multiple
limitTags={5}
fullWidth
getOptionLabel={(option) => option.label}
defaultValue={converToArray(row.value_rules.plan) || []}
isOptionEqualToValue={(option, value) =>
option.value === value.value
}
onChange={(event, value) => {
handlePlanChange(event, value);
handleChange(index, event, 'plan', row.id, value);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
handleConfigExclusion(event, row, valuePlan, 'plan');
}
}}
renderInput={(params) => (
<TextField {...params} label="Plan" variant="outlined" />
)}
/>
</Grid>
</Stack>
</Grid>
</Grid>
</FormControl>
</Stack>
</Box>
)
) : null} */}
</Collapse>
</TableCell>
</TableRow>

View File

@@ -165,6 +165,10 @@ export default function Router() {
path: ':corporate_id/diagnosis-exclusions',
element: <DiagnosisExclusions />,
},
{
path: ':corporate_id/diagnosis-exclusions/:exclusion_id/edit',
element: <EditDiagnosisExclusions />,
},
{
path: ':corporate_id/diagnosis-exclusions/history',
element: <DiagnosisExclusionsHistory />,
@@ -453,6 +457,9 @@ const CorporatePlansHistory = Loadable(lazy(() => import('../pages/Corporates/Pl
const DiagnosisExclusions = Loadable(
lazy(() => import('../pages/Corporates/DiagnosisExclusion/Index'))
);
const EditDiagnosisExclusions = Loadable(
lazy(() => import('../pages/Corporates/DiagnosisExclusion/Edit'))
);
const DiagnosisExclusionsHistory = Loadable(
lazy(() => import('../pages/Corporates/DiagnosisExclusion/History'))
);

View File

@@ -62,11 +62,11 @@ declare module '@mui/material' {
// SETUP COLORS
const PRIMARY = {
lighter: '#19BBBB', // #19BBBB
light: '#5BE584',
main: '#00AB55',
dark: '#007B55',
darker: '#005249',
lighter: '#D0FBEC',
light: '#70EAD5',
main: '#19BBBB',
dark: '#0C7186',
darker: '#043C59',
};
const SECONDARY = {
lighter: '#D6E4FF',