Merge remote-tracking branch 'origin/staging' into origin/production

This commit is contained in:
Linksehat Staging Server
2023-12-25 09:43:39 +07:00
577 changed files with 64446 additions and 14205 deletions

View File

@@ -0,0 +1,71 @@
/**
* Core
* ============================================
*/
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { Box, Grid, IconButton, Typography } from '@mui/material';
import { ArrowBackIosNew } from '@mui/icons-material';
/**
* Components
* ============================================
*/
// - Global -
import Page from '../../../components/Page';
// - Local -
/**
* Utils, Types, Functions
* ============================================
*/
import { getClaimList } from './Model/Functions';
import { ClaimListType, MemberDetailType } from './Model/Types';
import ClaimList from './Components/ClaimList';
export default function Claim() {
const navigate = useNavigate()
const { member_id } = useParams();
// State
// --------------------
const [memberDetail, setMemberDetail] = useState<MemberDetailType>();
const [claimList, setClaimList] = useState<ClaimListType[]>();
// Load Data
// -------------------
const loadDataTableData = async () => {
const response = await getClaimList(member_id??'');
setMemberDetail(response.member_detail);
setClaimList(response.claim_list);
}
useEffect(() => {
loadDataTableData();
}, [])
return (
<Page title={ `claims | ${memberDetail?.name??'_ _ _'}` } sx={{ px: 2 }}>
<Grid container gap={6}>
{/* back button */}
<Grid item xs={12}>
<Box sx={{ display: 'flex', alignItems: 'center'}}>
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/daily_monitoring`)} >
<ArrowBackIosNew/>
</IconButton>
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
{memberDetail?.name??'_ _ _'}
</Typography>
</Box>
</Grid>
{/* tabel claims */}
<Grid item xs={12}>
<ClaimList claim_list={claimList??null} />
</Grid>
</Grid>
</Page>
);
}

View File

@@ -0,0 +1,79 @@
/**
* Core
* ============================================
*/
import { Box, Paper, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from "@mui/material";
/**
* Component
* ============================================
*/
import ClaimListRow from "./ClaimListRow";
/**
* Types & Functions
* ============================================
*/
import { ClaimListType } from "../Model/Types";
type Props = {
claim_list: ClaimListType[] | null,
}
export default function ClaimList({ ...props }: Props) {
// Tabel Style
// --------------------
const TableHeadStyle = {
fontWeight: 'bold',
};
return (
<Box>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 250 }} size='medium' aria-label="collapsible table">
{/* Head Table */}
<TableHead>
<TableRow>
<TableCell style={TableHeadStyle} align="left" width={50} />
<TableCell style={TableHeadStyle} align="left" width={160}>Admission Date</TableCell>
<TableCell style={TableHeadStyle} align="left" width={160}>Discharge Date</TableCell>
<TableCell style={TableHeadStyle} align="left" width={200}>Code</TableCell>
<TableCell style={TableHeadStyle} align="left" width={'*'}>Service Type</TableCell>
<TableCell style={TableHeadStyle} align="left" width={200}>Status</TableCell>
<TableCell align="left" width={"10"} />
</TableRow>
</TableHead>
{/* Body Table */}
{props.claim_list == null ?
(
<TableBody>
<TableRow>
<TableCell colSpan={7} align="center">Loading</TableCell>
</TableRow>
</TableBody>
)
:
(
props.claim_list.length == 0 ?
(
<TableBody>
<TableRow>
<TableCell colSpan={7} align="center">No Data</TableCell>
</TableRow>
</TableBody>
)
:
(
<TableBody>
{props.claim_list.map((row: ClaimListType, index) => (
<ClaimListRow key={index} number={index+1} row={row} />
))}
</TableBody>
)
)}
</Table>
</TableContainer>
</Box>
)
}

View File

@@ -0,0 +1,108 @@
/**
* Core
* ============================================
*/
import React, { useState } from "react";
import { useNavigate } from "react-router";
import { Box, Collapse, MenuItem, TableCell, TableRow, Stack } from "@mui/material";
import Visibility from '@mui/icons-material/Visibility';
import AddIcon from '@mui/icons-material/Add';
/**
* Component
* ============================================
*/
// - Global -
import Label from "@/components/Label";
import TableMoreMenu from '@/components/table/TableMoreMenu';
/**
* Utils, Types, Functions
* ============================================
*/
import { fDate } from "@/utils/formatTime";
import { ClaimListType } from "../Model/Types";
type Props = {
row: ClaimListType,
number: number
}
export default function ClaimListRow ({ ...props }: Props) {
const navigate = useNavigate()
return (
<React.Fragment>
<TableRow hover sx={{ '& > td': { borderBottom: '1' } }}>
<TableCell align="left" />
<TableCell align="left">
{props.row.admission_date == null?
('-')
:
(
<Label
variant="ghost"
color="default"
>
{fDate(props.row.admission_date)}
</Label>
)}
</TableCell>
<TableCell align="left">
{props.row.discharge_date == null ?
('-')
:
(
<Label
variant="ghost"
color="default"
>
{fDate(props.row.discharge_date)}
</Label>
)}
</TableCell>
<TableCell align="left">{props.row.code}</TableCell>
<TableCell align="left">{props.row.service_name}</TableCell>
<TableCell align="left">
{props.row.discharge_date == null ?
(
<Label
variant="ghost"
color="warning"
>
On Monitor
</Label>
) :
(
<Label
variant="ghost"
color="success"
>
Close Monitor
</Label>
)
}
</TableCell>
<TableCell align="right" onClick={(e) => e.stopPropagation()}>
<Stack direction="row" justifyContent="flex-end" spacing={1}>
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate(`/case_management/daily_monitoring/${props.row.member_id}/claims/${props.row.claim_code}/list_monitoring`)}>
<Visibility />
View
</MenuItem>
<MenuItem onClick={() => navigate(`/case_management/daily_monitoring/${props.row.member_id}/claims/${props.row.claim_code}/add_monitoring`)}>
<AddIcon />
Daily Monitoring
</MenuItem>
</>
} />
</Stack>
</TableCell>
</TableRow>
</React.Fragment>
);
}

View File

@@ -0,0 +1,93 @@
/**
* Core
* ============================================
*/
import { useEffect, useState } from "react";
import { Box, Paper, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from "@mui/material";
/**
* Types & Functions
* ============================================
*/
import { getDailyMonitoringList } from "../Model/Functions";
import { DailyMonitoringListType } from "../Model/Types";
import DailyMonitoringListRow from "./DailyMonitoringListRow";
export default function DailyMonitoringList() {
// State
// --------------------
const [dataTableIsLoading, setDataTableLoading] = useState<boolean>(true);
const [dataTableData, setDataTableData] = useState<DailyMonitoringListType[]>([]);
// Tabel Style
// --------------------
const TableHeadStyle = {
fontWeight: 'bold',
};
// Load Data
// -------------------
const loadDataTableData = async () => {
setDataTableLoading(true);
const response = await getDailyMonitoringList();
setDataTableLoading(false);
setDataTableData(response);
}
useEffect(() => {
loadDataTableData();
}, [])
return (
<Box>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 250 }} size='medium' aria-label="collapsible table">
{/* Head Table */}
<TableHead>
<TableRow>
<TableCell style={TableHeadStyle} align="left" width={50} />
<TableCell style={TableHeadStyle} align="left" width={150}>Member ID</TableCell>
<TableCell style={TableHeadStyle} align="left" width={'*'}>Name</TableCell>
<TableCell style={TableHeadStyle} align="left" width={160}>Start Date</TableCell>
<TableCell style={TableHeadStyle} align="left" width={160}>End Date</TableCell>
<TableCell style={TableHeadStyle} align="left" width={160}>Admission Date</TableCell>
<TableCell style={TableHeadStyle} align="left" width={160}>Provider</TableCell>
<TableCell align="left" width={"10"} />
</TableRow>
</TableHead>
{/* Body Table */}
{dataTableIsLoading ?
(
<TableBody>
<TableRow>
<TableCell colSpan={6} align="center">Loading</TableCell>
</TableRow>
</TableBody>
)
:
(
dataTableData.length == 0 ?
(
<TableBody>
<TableRow>
<TableCell colSpan={6} align="center">No Data</TableCell>
</TableRow>
</TableBody>
)
:
(
<TableBody>
{dataTableData.map((row: DailyMonitoringListType, index) => (
<DailyMonitoringListRow key={index} number={index+1} row={row} />
))}
</TableBody>
)
)}
</Table>
</TableContainer>
</Box>
)
}

View File

@@ -0,0 +1,81 @@
/**
* Core
* ============================================
*/
import React, { useState } from "react";
import { useNavigate } from "react-router";
import { Box, Collapse, MenuItem, TableCell, TableRow, Stack } from "@mui/material";
import Visibility from '@mui/icons-material/Visibility';
/**
* Component
* ============================================
*/
// - Global -
import Label from "@/components/Label";
import TableMoreMenu from '@/components/table/TableMoreMenu';
/**
* Utils, Types, Functions
* ============================================
*/
import { fDate } from "@/utils/formatTime";
import { DailyMonitoringListType } from "../Model/Types";
type Props = {
row: DailyMonitoringListType,
number: number
}
export default function DailyMonitoringListRow ({ ...props }: Props) {
const navigate = useNavigate()
return (
<React.Fragment>
<TableRow hover sx={{ '& > td': { borderBottom: '1' } }}>
<TableCell align="left" />
<TableCell align="left">{props.row.member_id}</TableCell>
<TableCell align="left">{props.row.name}</TableCell>
<TableCell align="left">
<Label
variant="ghost"
color="default"
>
{fDate(props.row.startdate)}
</Label>
</TableCell>
<TableCell align="left">
<Label
variant="ghost"
color="default"
>
{fDate(props.row.enddate)}
</Label>
</TableCell>
<TableCell align="left">
<Label
variant="ghost"
color="default"
>
{fDate(props.row.addmision_date)}
</Label>
</TableCell>
<TableCell align="left">{props.row.provider}</TableCell>
<TableCell align="right" onClick={(e) => e.stopPropagation()}>
<Stack direction="row" justifyContent="flex-end" spacing={1}>
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate(`/case_management/daily_monitoring/${props.row.member_id}/claims`)}>
<Visibility />
View
</MenuItem>
</>
} />
</Stack>
</TableCell>
</TableRow>
</React.Fragment>
);
}

View File

@@ -0,0 +1,300 @@
/**
* Core
* ============================================
*/
import { useFieldArray, useForm } from 'react-hook-form';
import { useNavigate, useParams } from 'react-router-dom';
import { Box, IconButton, Typography, Grid, Card, Button } from '@mui/material';
import { LoadingButton } from "@mui/lab";
/**
* Components
* ============================================
*/
import Page from '@/components/Page';
import { FormProvider, RHFTextField } from '@/components/hook-form';
/**
* Icon
* ============================================
*/
import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew';
import AddIcon from '@mui/icons-material/Add';
import RemoveIcon from '@mui/icons-material/Remove';
/**
* Utils, Types, Functions
* ============================================
*/
import { AddMonitoringDetail } from '../Model/Functions';
import { DetailMonitoringListType} from '../Model/Types';
export default function DetailMonitoringList() {
const { member_id, claim_code } = useParams();
const navigate = useNavigate()
const pageTitle = claim_code??'_ _ _ _';
// setup form
// ====================================
const defaultValues: DetailMonitoringListType = {
id : '',
claim_code : '',
claim_id : '',
subject : '',
body_temperature: '',
sistole : '',
diastole : '',
respiration_rate: '',
complaints : '',
analysis : '',
medical_plan : [{
medical_plan_str: ''
}],
created_at : ''
};
const methods = useForm<any>({
defaultValues
});
const {fields, append, remove} = useFieldArray({name: 'medical_plan',control: methods.control})
const { handleSubmit, reset, formState: { isDirty, isSubmitting } } = methods;
// Submit Form
// =====================================
const submitHandler = async (data: DetailMonitoringListType) => {
console.log(claim_code);
const response = await AddMonitoringDetail(claim_code??'', data);
if (response == true) {
reset();
navigate('case_management/daily_monitoring/'+claim_code+'claims');
window.location.reload()
}
}
return (
<Page title={pageTitle}>
<Box sx={{ display: 'flex', alignItems: 'center', pl: '22px', mb: '40px' }}>
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/daily_monitoring/${member_id}/claims`)} >
<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}>
{/* Subject */}
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="body1" component="div">
Subject* :
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<RHFTextField
id="subject"
name='subject'
placeholder='Subjective'
/>
</Grid>
</Grid>
</Grid>
{/* Objectif */}
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="body1" component="div">
Objectif
</Typography>
</Grid>
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={3}>
<Typography variant="body1" component="div" color={'GrayText'}>
Body Temperature* :
</Typography>
</Grid>
<Grid item xs={3}>
<Typography variant="body1" component="div" color={'GrayText'}>
Sistole* :
</Typography>
</Grid>
<Grid item xs={3}>
<Typography variant="body1" component="div" color={'GrayText'}>
Diastole* :
</Typography>
</Grid>
<Grid item xs={3}>
<Typography variant="body1" component="div" color={'GrayText'}>
Respiration Rate* :
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={2}>
<RHFTextField
id="body_temperature"
name='body_temperature'
placeholder='Body Temperature'
/>
</Grid>
<Grid item xs={1} sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant="body1" component="div" color={'GrayText'}>
Cel
</Typography>
</Grid>
<Grid item xs={2}>
<RHFTextField
id="sistole"
name='sistole'
placeholder='Sistole'
/>
</Grid>
<Grid item xs={1} sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant="body1" component="div" color={'GrayText'}>
mm[Hg]
</Typography>
</Grid>
<Grid item xs={2}>
<RHFTextField
id="diastole"
name='diastole'
placeholder='Diastole'
/>
</Grid>
<Grid item xs={1} sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant="body1" component="div" color={'GrayText'}>
mm[Hg]
</Typography>
</Grid>
<Grid item xs={2}>
<RHFTextField
id="respiration_rate"
name='respiration_rate'
placeholder='Respiration Rate'
/>
</Grid>
<Grid item xs={1} sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant="body1" component="div" color={'GrayText'}>
/min
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
{/* Complaints */}
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="body1" component="div">
Complaints* :
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<RHFTextField
id="complaints"
name='complaints'
placeholder='Complaints'
/>
</Grid>
</Grid>
</Grid>
{/* Analysis */}
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="body1" component="div">
Analysis* :
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<RHFTextField
id="analysis"
name='analysis'
placeholder='Analysis'
/>
</Grid>
</Grid>
</Grid>
{/* Medical Plan */}
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="body1" component="div">
Medical Plan* :
</Typography>
</Grid>
<Grid item xs={12}>
{
fields.map((field,index) => {
return (
<Grid key={field.id} container sx={{ mb: 3 }}>
<Grid item xs={11}>
<RHFTextField
id="analysis"
name={`medical_plan.${index}.medical_plan_str`}
placeholder='Medical Plan'
/>
</Grid>
{
index == (fields.length-1) ?
(
<Grid item xs={1} sx={{ textAlign: 'center' }}>
<IconButton size='large' color='primary' onClick={() => append({medical_plan_str: ''})}>
<AddIcon />
</IconButton>
</Grid>
)
:
(
<Grid item xs={1} sx={{ textAlign: 'center' }}>
<IconButton size='large' color='error' onClick={() => remove(index)}>
<RemoveIcon />
</IconButton>
</Grid>
)
}
</Grid>
)
})
}
</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(`/case_management/daily_monitoring/${member_id}/claims`)}>
Cancel
</Button>
<LoadingButton disabled={!isDirty} type="submit" variant="contained" loading={isSubmitting}>
Save Changes
</LoadingButton>
</Box>
</Box>
</Grid>
</Grid>
</Card>
</FormProvider>
</Box>
</Page>
);
}

View File

@@ -0,0 +1,241 @@
/**
* Core
* ============================================
*/
import { useEffect, useState } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { useNavigate, useParams } from 'react-router-dom';
import { Box, IconButton, Typography, Grid, Card, List, ListItem } from '@mui/material';
import { LoadingButton } from "@mui/lab";
/**
* Components
* ============================================
*/
// - Global -
import Page from '@/components/Page';
import Label from "@/components/Label";
/**
* Icon
* ============================================
*/
import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew';
import FiberManualRecord from '@mui/icons-material/FiberManualRecord';
/**
* Utils, Types, Functions
* ============================================
*/
import { fDate } from "@/utils/formatTime";
import { getMonitoringDetailList } from '../Model/Functions';
import { DetailMonitoringListType } from '../Model/Types';
export default function DetailMonitoringList() {
const { member_id, claim_code } = useParams();
const navigate = useNavigate()
const pageTitle = claim_code??'_ _ _ _';
// State
// --------------------
const [detailMonitoringList, setDetailMonitoringList] = useState<DetailMonitoringListType[]>();
// Use Effect
// --------------------
useEffect(() => {
loadDataTableData();
}, [])
// Load Data
// -------------------
const loadDataTableData = async () => {
const response = await getMonitoringDetailList(claim_code??'');
setDetailMonitoringList(response);
}
return (
<Page title={pageTitle} sx={{ px: 2 }}>
<Grid container gap={6}>
{/* back button */}
<Grid item xs={12}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/daily_monitoring/${member_id}/claims`)} >
<ArrowBackIosNew/>
</IconButton>
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
{pageTitle}
</Typography>
</Box>
</Grid>
{/* tabel claims */}
<Grid item xs={12}>
<Grid container gap={4} sx={{ px: 2 }}>
{
detailMonitoringList?.map((row, index) => {
return (
<Grid key={index} item xs={12}>
<Card sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '32px', py: '24px'}}>
{/* card header */}
<Box sx={{ pb: '20px', mb: '20px', borderBottom: '1px solid rgba(0,0,0,0.125)' }}>
<Label
variant="ghost"
color="default"
>
{row.created_at ? fDate(row.created_at) : '-'}
</Label>
</Box>
{/* card body */}
<Grid container gap={4}>
<Grid item xs={12}>
<Grid container gap={1}>
<Grid item xs={12}>
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
Subject :
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="body2" color={"GrayText"}>
{row.subject}
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Grid container gap={1}>
<Grid item xs={12}>
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
Object :
</Typography>
</Grid>
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={6}>
<Typography variant="body2" color={"GrayText"}>
Body Temperature
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">
{row.body_temperature}
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2" color={"GrayText"}>
Sistole
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">
{row.sistole} mm[Hg]
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2" color={"GrayText"}>
Diastole
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">
{row.diastole} mm[Hg]
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2" color={"GrayText"}>
Respiration Rate
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">
{row.respiration_rate} / min
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Grid container gap={1}>
<Grid item xs={12}>
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
Subject :
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="body2" color={"GrayText"}>
{row.subject}
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Grid container gap={1}>
<Grid item xs={12}>
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
Analysis :
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="body2" color={"GrayText"}>
{row.analysis}
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Grid container gap={1}>
<Grid item xs={12}>
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
Complaints :
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="body2" color={"GrayText"}>
{row.complaints}
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Grid container gap={1}>
<Grid item xs={12}>
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
Medical Plan :
</Typography>
</Grid>
<Grid item xs={12}>
<List sx={{ color: 'GrayText' }}>
{
row.medical_plan.map((data, index) => {
return (
<ListItem key={index}>
<FiberManualRecord sx={{ fontSize: '8px', mr: '10px' }} /> {data.medical_plan_str}
</ListItem>
)
})
}
</List>
</Grid>
</Grid>
</Grid>
</Grid>
</Card>
</Grid>
)
})
}
</Grid>
</Grid>
</Grid>
</Page>
);
}

View File

@@ -0,0 +1,96 @@
import axios from '@/utils/axios';
import { enqueueSnackbar } from 'notistack';
import { DailyMonitoringListType, DetailMonitoringListType, ResponseListingClaimType } from "./Types";
/**
* Listing Daily Monitoring
*/
export const getDailyMonitoringList = async ( ): Promise<DailyMonitoringListType[]> => {
const response = await axios.get('/case_management/memberlist')
.then((res) =>{
return res.data.data.member_list;
})
.catch((res) => {
enqueueSnackbar("server error !", {
variant: 'error',
});
return [];
});
return response;
};
/**
* Listing Claim
*/
export const getClaimList = async ( member_id: string ): Promise<ResponseListingClaimType> => {
const response = await axios.get(`/case_management/claimlist/${member_id}`)
.then((res) =>{
return res.data.data;
})
.catch((res) => {
enqueueSnackbar("server error !", {
variant: 'error',
});
return null;
});
return response;
};
/**
* Add Monitoring Detail
*/
export const AddMonitoringDetail = async ( claim_code: string,data: DetailMonitoringListType ): Promise<boolean> => {
const response = await axios.post(`/case_management/daily_monitoring/detail/${claim_code}/add`, {
...data
})
.then((res) =>{
enqueueSnackbar(res.data.message, {
variant: 'success',
});
return true;
})
.catch((res) => {
if (res.response.status == 400) {
let arr_message = res.response.data.message;
for (const key in arr_message) {
enqueueSnackbar(arr_message[key][0], {
variant: 'warning',
});
}
}
else {
enqueueSnackbar("server error !", {
variant: 'error',
});
}
return false;
});
return response;
};
/**
* Get Monitoring Detail List
*/
export const getMonitoringDetailList = async ( claim_code: string ): Promise<DetailMonitoringListType[]> => {
const response = await axios.get(`/case_management/daily_monitoring/detail/${claim_code}/list`)
.then((res) =>{
return res.data.data.detail_list;
})
.catch((res) => {
enqueueSnackbar("server error !", {
variant: 'error',
});
return [];
});
return response;
};

View File

@@ -0,0 +1,63 @@
/**
* List Daily Monitoring
*/
export type DailyMonitoringListType = {
member_id : string,
name : string,
startdate : string,
enddate : string,
addmision_date : string,
provider : string
}
/**
* Response Listing Claim
*/
export type ResponseListingClaimType = {
member_detail : MemberDetailType,
claim_list : ClaimListType[],
}
/**
* Member Detail
*/
export type MemberDetailType = {
id : string,
member_id : string,
name : string,
}
/**
* List Claim
*/
export type ClaimListType = {
claim_id : number,
admission_date : string,
discharge_date : string,
claim_code : string,
claim_status : string,
service_type : string,
member_id : string
}
/**
* Detail Monitoring List
*/
export type DetailMonitoringListType = {
id : string|null,
claim_id : string|null,
claim_code : string,
subject : string,
body_temperature: string,
respiration_rate: string,
sistole : string,
diastole : string
analysis : string,
complaints : string,
medical_plan : MedicalPlanStrType[],
created_at : string|null
}
export type MedicalPlanStrType = {
medical_plan_str: string
}

View File

@@ -0,0 +1,48 @@
/**
* Core
* ============================================
*/
import { Box, Grid } from '@mui/material';
/**
* Components
* ============================================
*/
// - Global -
import Page from '../../../components/Page';
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
// - Local -
import DailyMonitoringList from './Components/DailyMonitoringList';
export default function DailyMonitoring() {
const pageTitle = "Daily Monitoring";
return (
<Page title={ pageTitle } sx={{ px: 2 }}>
<Grid container>
{/* page header */}
<Grid item xs={12}>
<HeaderBreadcrumbs
heading={ pageTitle }
sx={{ px: 1 }}
links={[
{
name: 'Dashboard',
href: '/dashboard',
},
{
name: pageTitle,
href: '/case_management/daily_monitoring',
},
]}
/>
</Grid>
{/* tabel daily monitoring */}
<Grid item xs={12}>
<DailyMonitoringList />
</Grid>
</Grid>
</Page>
);
}

View File

@@ -0,0 +1,30 @@
import { Card, Stack } from "@mui/material";
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
import Page from "../../../components/Page";
import List from "./List";
export default function Claims() {
const pageTitle = 'Inpatient Monitoring';
return (
<Page title={ pageTitle } sx={{ mx: 2}}>
<HeaderBreadcrumbs
heading={ pageTitle }
links={[
{ name: 'Dashboard', href: '/dashboard' },
{
name: 'Inpatient Monitoring',
href: '/inpatient_monitoring',
},
]}
/>
{/* <Stack> */}
<List />
{/* </Stack> */}
</Page>
);
}

View File

@@ -0,0 +1,582 @@
// @mui
import {
Box,
Button,
Card,
Collapse,
IconButton,
MenuItem,
Table,
TableBody,
TableCell,
TableRow,
TextField,
Typography,
Stack,
Menu,
ButtonGroup,
Link,
Chip,
TableHead,
Grid,
} from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add';
import UploadIcon from '@mui/icons-material/Upload';
import CancelIcon from '@mui/icons-material/Cancel';
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
// hooks
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';
import useSettings from '@/hooks/useSettings';
// components
import axios from '../../../utils/axios';
import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../../@types/paginated-data';
import DataTable from '../../../components/LaravelTable';
import { fCurrency } from '../../../utils/formatNumber';
import EditRoundedIcon from '@mui/icons-material/EditRounded';
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 { fDateTimesecond } from '@/utils/formatTime';
import { capitalizeFirstLetter } from '@/utils/formatString';
import Label from '@/components/Label';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import { Import } from '@/@types/claims';
import { FinalLogType } from '../../CustomerService/FinalLog/Model/Types';
// import LoadingButton from '@/theme/overrides/LoadingButton';
export default function List() {
const { themeColorPresets } = useSettings();
const [searchParams, setSearchParams] = useSearchParams();
const [importResult, setImportResult] = useState<Import>(null);
const navigate = useNavigate()
function SearchInput(props: any) {
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
const [searchText, setSearchText] = useState('');
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? '';
setSearchText(newSearchText);
};
const handleSearchSubmit = (event: any) => {
event.preventDefault();
props.onSearch({ search: searchText }); // Trigger to Parent
};
useEffect(() => {
// Trigger First Search
setSearchText(searchParams.get('search') ?? '');
}, []);
return (
<form onSubmit={handleSearchSubmit} style={{ width: '100%' }}>
<TextField
id="search-input"
ref={searchInput}
label="Search"
variant="outlined"
fullWidth
onChange={handleSearchChange}
value={searchText}
placeholder='Search Code or Name...'
/>
</form>
);
}
function ImportForm(props: any) {
// IMPORT
// Create Button Menu
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const createMenu = Boolean(anchorEl);
const importForm = useRef<HTMLInputElement>(null);
const [currentImportFileName, setCurrentImportFileName] = useState(null);
const [importLoading, setImportLoading] = useState(false);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleImportButton = () => {
if (importForm?.current) {
handleClose();
importForm.current ? importForm.current.click() : console.log('No File selected');
} else {
alert('No file selected');
}
};
const handleCancelImportButton = () => {
importForm.current.value = '';
importForm.current.dispatchEvent(new Event('change', { bubbles: true }));
};
const handleImportChange = (event: any) => {
if (event.target.files[0]) {
setCurrentImportFileName(event.target.files[0].name);
} else {
setCurrentImportFileName(null);
}
};
const handleUpload = () => {
if (importForm.current?.files.length) {
const formData = new FormData();
formData.append('file', importForm.current?.files[0]);
setImportLoading(true);
axios
.post(`claim-requests/import`, formData)
.then((response) => {
handleCancelImportButton();
loadDataTableData();
setImportResult(response.data);
// alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows');
setImportLoading(false);
})
.catch((response) => {
enqueueSnackbar(
'Looks like something went wrong. Please check your data and try again. ' +
response.message,
{ variant: 'error' }
);
setImportLoading(false);
});
} else {
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 handleGetData = (type :string) => {
axios.get(`corporates/${corporate_id}/data-plan-benefit`)
.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>
<input
type="file"
id="file"
ref={importForm}
style={{ display: 'none' }}
onChange={handleImportChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/>
{!currentImportFileName && (
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter} />
<Button
variant="outlined"
startIcon={<UploadIcon />}
sx={{ p: 1.8 }}
onClick={handleClick}
>
Import
</Button>
<Menu
id="import-button"
anchorEl={anchorEl}
open={createMenu}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
<MenuItem onClick={handleImportButton}>Import</MenuItem>
<MenuItem onClick={() => {handleGetTemplate('claim-request')}}>Download Template</MenuItem>
<MenuItem onClick={() => {handleGetData('data-plan-benefit')}}>Download Claim Request</MenuItem>
</Menu>
{/* <Button
variant="contained"
startIcon={<AddIcon />}
sx={{ p: 1.8 }}
onClick={() => {
navigate('/claim-requests/create');
}}
>
Create
</Button> */}
</Stack>
)}
{currentImportFileName && (
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<ButtonGroup variant="outlined" aria-label="outlined button group" fullWidth>
<Button onClick={handleImportButton} fullWidth>
{currentImportFileName ?? 'No File Selected'}
</Button>
<Button
onClick={handleCancelImportButton}
size="small"
fullWidth={false}
sx={{ p: 1.8 }}
>
<CancelIcon color="error" />
</Button>
</ButtonGroup>
<LoadingButton
id="upload-button"
variant="outlined"
startIcon={<UploadIcon />}
sx={{ p: 1.8 }}
onClick={handleUpload}
loading={importLoading}
>
Upload
</LoadingButton>
</Stack>
)}
{importResult && (
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
<Box sx={{ color: 'text.secondary' }}>
Last Import Result Report :{' '}
<a href={importResult.result_file?.url ?? '#'}>
{importResult.result_file?.name ?? '-'}
</a>
</Box>
</Stack>
)}
</div>
);
}
// Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = useState(true);
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>(
LaravelPaginatedDataDefault
);
const loadDataTableData = async (appliedFilter: any | null = null) => {
setDataTableLoading(true);
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
const response = await axios.get('/customer-service/request?final_log=1&service_code=IP', { params: filter });
// console.log(response.data);
setDataTableLoading(false);
setDataTableData(response.data);
};
const applyFilter = async (searchFilter: { search: string }) => {
await loadDataTableData(searchFilter);
setSearchParams(searchFilter);
};
const handlePageChange = (event: ChangeEvent, value: number): void => {
const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]);
loadDataTableData(filter);
setSearchParams(filter);
};
const handleApprove = (claimRequest) => {
axios
.post(`claim-requests/${claimRequest.id}/approve`)
.then((response) => {
enqueueSnackbar('Success Approve', { variant: 'success' });
loadDataTableData();
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' });
});
};
useEffect(() => {
loadDataTableData();
}, []);
const headStyle = {
fontWeight: 'bold',
};
// Called on every row to map the data to the columns
function createData(data: FinalLogType) {
return {
...data,
};
}
{
/* ------------------ TABLE ROW ------------------ */
}
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const [loadingApprove, setLoadingApprove] = React.useState(false);
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
{/* <TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell> */ }
{/* <TableCell align="left">
<Typography
// onClick={() => {
// handleShowClaim(row);
// }}
>
{row.id}
</Typography>
</TableCell> */}
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.member_name}</TableCell>
<TableCell align="left"><Label>{fDateTimesecond(row.submission_date)}</Label></TableCell>
<TableCell align="left">{row.service_name}</TableCell>
<TableCell align="left">{row.payment_type_name}</TableCell>
<TableCell align="left">
{ row.status_final_log == "requested" ?
(<Label variant='ghost' color='primary'>{capitalizeFirstLetter(row.status_final_log)}</Label>) :
row.status_final_log == "declined" ?
(<Label color='error'> {capitalizeFirstLetter(row.status_final_log)}</Label>)
:
(<Label color='success'> {capitalizeFirstLetter(row.status_final_log)}</Label>)
}
</TableCell>
<TableCell align="right">
<TableMoreMenu actions={
<>
{/* <MenuItem onClick={() => navigate(`/claim-requests/edit/${row.id}`)}>
<EditOutlinedIcon />
Edit
</MenuItem> */}
<MenuItem onClick={() => navigate ('/custormer-service/final-log/detail/'+row.id+'')}>
<FindInPageOutlinedIcon />
Detail
</MenuItem>
</>
} />
</TableCell>
{/* <TableCell>
<IconButton
onClick={() => {
handleShowClaim(row);
}}
>
<Iconify icon="eva:eye-fill" />
</IconButton>
</TableCell> */}
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={99}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ borderBottom: 1 }}>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
<Box>
<Typography fontWeight={600}>Berkas Hasil Penunjang</Typography>
{/* {row.files_by_type?.claim_kondisi &&
row.files_by_type?.claim_kondisi.map((file, index) => (
<Stack direction="row" key={index}>
<Typography sx={{ marginRight: 2 }}>-</Typography>{' '}
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))} */}
{row.files_by_type?.claim_kondisi && (
<>
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Kondisi</Typography>
{row.files_by_type?.claim_kondisi.map((file, index) => (
<Stack direction="row" key={index}>
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))}
</>
)}
{row.files_by_type?.claim_diagnosis && (
<>
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Diagnosa</Typography>
{row.files_by_type?.claim_diagnosis.map((file, index) => (
<Stack direction="row" key={index}>
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))}
</>
)}
{row.files_by_type?.claim_result && (
<>
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Hasil</Typography>
{row.files_by_type?.claim_result.map((file, index) => (
<Stack direction="row" key={index}>
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))}
</>
)}
{(!row.files_by_type?.claim_result && !row.files_by_type?.claim_diagnosis && !row.files_by_type?.claim_kondisi)&& <Typography>Tidak ada berkas</Typography>}
</Box>
</Stack>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
{
/* ------------------ END TABLE ROW ------------------ */
}
function TableContent() {
return (
<Table aria-label="collapsible table">
{/* ------------------ TABLE HEADER ------------------ */}
<TableHead>
<TableRow>
{/* <TableCell style={headStyle} align="left" /> */}
{/* <TableCell style={headStyle} align="left">
ID Request LOG
</TableCell> */}
<TableCell style={headStyle} align="left">
Code
</TableCell>
<TableCell style={headStyle} align="left">
Name
</TableCell>
<TableCell style={headStyle} align="left">
Date of Submission
</TableCell>
<TableCell style={headStyle} align="left">
Service Type
</TableCell>
<TableCell style={headStyle} align="left">
Claim Method
</TableCell>
<TableCell style={headStyle} align="left">
Status
</TableCell>
<TableCell style={headStyle} align="right"></TableCell>
</TableRow>
</TableHead>
{/* ------------------ END TABLE HEADER ------------------ */}
{/* ------------------ TABLE ROW ------------------ */}
{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>
)}
{/* ------------------ END TABLE ROW ------------------ */}
</Table>
);
}
// ---------------------------------------------------------
// 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 (
<Grid container>
<Grid item sm={12}>
<ImportForm />
</Grid>
<Grid item sm={12}>
<DataTable
isLoading={dataTableIsLoading}
lastRequest={0}
data={dataTableData}
handlePageChange={handlePageChange}
TableContent={<TableContent />}
/>
</Grid>
<Grid item sm={12}>
<DialogDetailClaim
openDialog={openDialogDetailClaim}
setOpenDialog={setOpenDialogDetailClaim}
title={{ name: 'Claim Request Detail' }}
data={{ claim: currentClaim, isLoading: loadingClaimDetail, handleDownloadLog }}
></DialogDetailClaim>
</Grid>
</Grid>
);
}

View File

@@ -0,0 +1,73 @@
/**
* Core
* ============================================
*/
import { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { Box, Grid, IconButton, Typography } from '@mui/material';
import { ArrowBackIosNew } from '@mui/icons-material';
/**
* Components
* ============================================
*/
// - Global -
import Page from '../../../components/Page';
// - Local -
/**
* Utils, Types, Functions
* ============================================
*/
import { getClaimList } from './Model/Functions';
import { ClaimListType, MemberDetailType } from './Model/Types';
import ClaimList from './Components/ClaimList';
export default function Claim() {
const navigate = useNavigate()
const { member_id } = useParams();
// State
// --------------------
const [memberDetail, setMemberDetail] = useState<MemberDetailType>();
const [claimList, setClaimList] = useState<ClaimListType[]>();
// Use Effect
// --------------------
useEffect(() => {
loadDataTableData();
}, [])
// Load Data
// -------------------
const loadDataTableData = async () => {
const response = await getClaimList(member_id??'');
setMemberDetail(response.member_detail);
setClaimList(response.claim_list);
}
return (
<Page title={ `claims | ${memberDetail?.name??'_ _ _'}` } sx={{ px: 2 }}>
<Grid container gap={6}>
{/* back button */}
<Grid item xs={12}>
<Box sx={{ display: 'flex', alignItems: 'center'}}>
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/laboratorium_result`)} >
<ArrowBackIosNew/>
</IconButton>
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
{memberDetail?.name??'_ _ _'}
</Typography>
</Box>
</Grid>
{/* tabel claims */}
<Grid item xs={12}>
<ClaimList claim_list={claimList??null} />
</Grid>
</Grid>
</Page>
);
}

View File

@@ -0,0 +1,79 @@
/**
* Core
* ============================================
*/
import { Box, Paper, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from "@mui/material";
/**
* Component
* ============================================
*/
import ClaimListRow from "./ClaimListRow";
/**
* Types & Functions
* ============================================
*/
import { ClaimListType } from "../Model/Types";
type Props = {
claim_list: ClaimListType[] | null,
}
export default function ClaimList({ ...props }: Props) {
// Tabel Style
// --------------------
const TableHeadStyle = {
fontWeight: 'bold',
};
return (
<Box>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 250 }} size='medium' aria-label="collapsible table">
{/* Head Table */}
<TableHead>
<TableRow>
<TableCell style={TableHeadStyle} align="left" width={50} />
<TableCell style={TableHeadStyle} align="left" width={160}>Admission Date</TableCell>
<TableCell style={TableHeadStyle} align="left" width={160}>Discharge Date</TableCell>
<TableCell style={TableHeadStyle} align="left" width={200}>Code</TableCell>
<TableCell style={TableHeadStyle} align="left" width={'*'}>Service Type</TableCell>
<TableCell style={TableHeadStyle} align="left" width={200}>Status</TableCell>
<TableCell align="left" width={"10"} />
</TableRow>
</TableHead>
{/* Body Table */}
{props.claim_list == null ?
(
<TableBody>
<TableRow>
<TableCell colSpan={7} align="center">Loading</TableCell>
</TableRow>
</TableBody>
)
:
(
props.claim_list.length == 0 ?
(
<TableBody>
<TableRow>
<TableCell colSpan={7} align="center">No Data</TableCell>
</TableRow>
</TableBody>
)
:
(
<TableBody>
{props.claim_list.map((row: ClaimListType, index) => (
<ClaimListRow key={index} number={index+1} row={row} />
))}
</TableBody>
)
)}
</Table>
</TableContainer>
</Box>
)
}

View File

@@ -0,0 +1,88 @@
/**
* Core
* ============================================
*/
import React, { useState } from "react";
import { useNavigate } from "react-router";
import { Box, Collapse, MenuItem, TableCell, TableRow, Stack } from "@mui/material";
import Visibility from '@mui/icons-material/Visibility';
import AddIcon from '@mui/icons-material/Add';
/**
* Component
* ============================================
*/
// - Global -
import Label from "@/components/Label";
import TableMoreMenu from '@/components/table/TableMoreMenu';
/**
* Utils, Types, Functions
* ============================================
*/
import { fDate } from "@/utils/formatTime";
import { ClaimListType } from "../Model/Types";
type Props = {
row: ClaimListType,
number: number
}
export default function ClaimListRow ({ ...props }: Props) {
const navigate = useNavigate()
return (
<React.Fragment>
<TableRow hover sx={{ '& > td': { borderBottom: '1' } }}>
<TableCell align="left" />
<TableCell align="left">
{props.row.admission_date == "0000-00-00 00:00:00" ?
('-')
:
(
<Label
variant="ghost"
color="default"
>
{fDate(props.row.admission_date)}
</Label>
)}
</TableCell>
<TableCell align="left">
{props.row.discharge_date == "0000-00-00 00:00:00" ?
('-')
:
(
<Label
variant="ghost"
color="default"
>
{fDate(props.row.discharge_date)}
</Label>
)}
</TableCell>
<TableCell align="left">{props.row.claim_code}</TableCell>
<TableCell align="left">{props.row.service_type}</TableCell>
<TableCell align="left">{props.row.claim_status}</TableCell>
<TableCell align="right" onClick={(e) => e.stopPropagation()}>
<Stack direction="row" justifyContent="flex-end" spacing={1}>
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate(`/case_management/laboratorium_result/${props.row.member_id}/claims/${props.row.claim_code}/list_lab_result`)}>
<Visibility />
View
</MenuItem>
<MenuItem onClick={() => navigate(`/case_management/laboratorium_result/${props.row.member_id}/claims/${props.row.claim_code}/add_lab_result`)}>
<AddIcon />
Laboratorium Result
</MenuItem>
</>
} />
</Stack>
</TableCell>
</TableRow>
</React.Fragment>
);
}

View File

@@ -0,0 +1,237 @@
/**
* Core
* ============================================
*/
import { useRef } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { useNavigate, useParams } from 'react-router-dom';
import { Box, IconButton, Typography, Grid, Card, Button, ButtonBase, Stack } from '@mui/material';
import { LoadingButton } from "@mui/lab";
/**
* Components
* ============================================
*/
import Page from '@/components/Page';
import { FormProvider, RHFDatepicker, RHFTextField } from '@/components/hook-form';
import RHFDatePickerV2 from '@/components/hook-form/RHFDatePickerV2';
/**
* Icon
* ============================================
*/
import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew';
import Iconify from '@/components/Iconify';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
/**
* Utils, Types, Functions
* ============================================
*/
import { AddLabResultDetail } from '../Model/Functions';
import { DetailLabResultListType} from '../Model/Types';
export default function DetailMonitoringList() {
const { member_id, claim_code } = useParams();
const navigate = useNavigate()
const pageTitle = claim_code??'_ _ _ _';
const fileInput = useRef<HTMLInputElement>(null);
// setup form
// ====================================
const defaultValues: DetailLabResultListType = {
id : '',
claim_id : '',
claim_code : '',
date : null,
location : '',
examination : '',
lab_result_file : [],
created_at : ''
};
const methods = useForm<any>({
defaultValues
});
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting } } = methods;
const formValues = watch();
// Handle File Input
// =====================================
const handleInputChange = (event: any) => {
if (event.target.files[0]) {
let arr_lab_result_file = formValues.lab_result_file;
arr_lab_result_file.push(event.target.files[0]);
setValue('lab_result_file', arr_lab_result_file)
}
else {
console.log('NO FILE');
}
};
// Handle Remove File
// =====================================
const handleRemoveFile = (target_index: number) => {
let arr_lab_result_file = formValues.lab_result_file.filter((file: any, index: number) =>{
if (target_index !== index) {
return file;
}
});
setValue('lab_result_file', arr_lab_result_file)
};
// Submit Form
// =====================================
const submitHandler = async (data: DetailLabResultListType) => {
const response = await AddLabResultDetail(claim_code??'', data);
if (response == true) {
reset(defaultValues);
}
}
return (
<Page title={pageTitle}>
<Box sx={{ display: 'flex', alignItems: 'center', pl: '22px', mb: '40px' }}>
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/laboratorium_result/${member_id}/claims`)} >
<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}>
{/* Date */}
<Grid item xs={6}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="body1" component="div">
Date* :
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<RHFDatePickerV2
label=''
name="date"
dateFormat='dd-MMMM-yyyy'
fullWidth
/>
</Grid>
</Grid>
</Grid>
{/* Location */}
<Grid item xs={6}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="body1" component="div">
Location* :
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<RHFTextField
id="location"
name='location'
placeholder='Location'
/>
</Grid>
</Grid>
</Grid>
{/* Examination */}
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Typography variant="body1" component="div">
Examination* :
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<RHFTextField
id="examination"
name='examination'
placeholder='Examination'
/>
</Grid>
</Grid>
</Grid>
{/* list file & button upload */}
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={12}>
{
formValues.lab_result_file.map((file: any, index: number) => (
<Stack direction="row" justifyContent={'space-between'} key={index} sx={{ mb: '16px' }}>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
</Stack>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => handleRemoveFile(index)}
sx={{cursor: 'pointer'}}
></Iconify>
</Stack>
))
}
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',bgcolor: '#919EAB52',borderRadius: '8px',width: '100%', height: '60px'}} onClick={() => fileInput.current?.click()}>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Upload Result
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileInput}
style={{ display: 'none' }}
multiple
onChange={handleInputChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
</ButtonBase>
</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(`/case_management/laboratorium_result/${member_id}/claims`)}>
Cancel
</Button>
<LoadingButton disabled={!isDirty} type="submit" variant="contained" loading={isSubmitting}>
Save Changes
</LoadingButton>
</Box>
</Box>
</Grid>
</Grid>
</Card>
</FormProvider>
</Box>
</Page>
)
}

View File

@@ -0,0 +1,165 @@
/**
* Core
* ============================================
*/
import { useEffect, useState } from 'react';
import { useFieldArray, useForm } from 'react-hook-form';
import { useNavigate, useParams } from 'react-router-dom';
import { Box, IconButton, Typography, Grid, Card, List, ListItem, Stack, Link } from '@mui/material';
import { LoadingButton } from "@mui/lab";
/**
* Components
* ============================================
*/
// - Global -
import Page from '@/components/Page';
import Label from "@/components/Label";
/**
* Icon
* ============================================
*/
import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
/**
* Utils, Types, Functions
* ============================================
*/
import { fDate } from "@/utils/formatTime";
import { getLabResultDetailList } from '../Model/Functions';
import { DetailLabResultListType } from '../Model/Types';
export default function DetailLabResultList() {
const { member_id, claim_code } = useParams();
const navigate = useNavigate()
const pageTitle = claim_code??'_ _ _ _';
// State
// --------------------
const [LabResultList, setLabResultList] = useState<DetailLabResultListType[]>();
// Use Effect
// --------------------
useEffect(() => {
loadDataTableData();
}, [])
// Load Data
// -------------------
const loadDataTableData = async () => {
const response = await getLabResultDetailList(claim_code??'');
setLabResultList(response);
}
return (
<Page title={pageTitle} sx={{ px: 2 }}>
<Grid container gap={6}>
{/* back button */}
<Grid item xs={12}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<IconButton size='large' color='inherit' onClick={() => navigate(`/case_management/laboratorium_result/${member_id}/claims`)} >
<ArrowBackIosNew/>
</IconButton>
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
{pageTitle}
</Typography>
</Box>
</Grid>
{/* tabel claims */}
<Grid item xs={12}>
<Grid container gap={4} sx={{ px: 2 }}>
{
LabResultList?.map((row, index) => {
return (
<Grid key={index} item xs={12}>
<Card sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '32px', py: '24px'}}>
{/* card header */}
<Box sx={{ pb: '20px', mb: '20px', borderBottom: '1px solid rgba(0,0,0,0.125)' }}>
<Label
variant="ghost"
color="default"
>
{row.created_at ? fDate(row.created_at) : '-'}
</Label>
</Box>
{/* card body */}
<Grid container gap={3}>
<Grid item xs={12}>
<Grid container spacing={1}>
<Grid item xs={6}>
<Typography variant="body2" color={"GrayText"}>
Date
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">
{row.date}
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2" color={"GrayText"}>
Location
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">
{row.location}
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2" color={"GrayText"}>
Examination
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">
{row.examination}
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Grid container gap={1}>
<Grid item xs={12}>
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
Document :
</Typography>
</Grid>
<Grid item xs={12}>
{
row.lab_result_file.map((data, index) => {
return (
<Stack direction="row" justifyContent={'space-between'} key={index} sx={{ mt: '16px' }}>
<Link href={data.lab_result_file_obj.path} underline='hover' target="_blank">
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<Typography variant="body2" gutterBottom>{data.lab_result_file_obj.original_name??'-'}</Typography>
</Stack>
</Link>
</Stack>
)
})
}
</Grid>
</Grid>
</Grid>
</Grid>
</Card>
</Grid>
)
})
}
</Grid>
</Grid>
</Grid>
</Page>
);
}

View File

@@ -0,0 +1,93 @@
/**
* Core
* ============================================
*/
import { useEffect, useState } from "react";
import { Box, Paper, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from "@mui/material";
/**
* Types & Functions
* ============================================
*/
import { getDailyMonitoringList } from "../Model/Functions";
import { LaboratoriumResultListType } from "../Model/Types";
import LaboratoriumListRow from "./LaboratoriumResultListRow";
export default function LaboratoriumResultList() {
// State
// --------------------
const [dataTableIsLoading, setDataTableLoading] = useState<boolean>(true);
const [dataTableData, setDataTableData] = useState<LaboratoriumResultListType[]>([]);
// Tabel Style
// --------------------
const TableHeadStyle = {
fontWeight: 'bold',
};
// Use Effect
// --------------------
useEffect(() => {
loadDataTableData();
}, [])
// Load Data
// -------------------
const loadDataTableData = async () => {
setDataTableLoading(true);
const response = await getDailyMonitoringList();
setDataTableLoading(false);
setDataTableData(response);
}
return (
<Box>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 250 }} size='medium' aria-label="collapsible table">
{/* Head Table */}
<TableHead>
<TableRow>
<TableCell style={TableHeadStyle} align="left" width={50} />
<TableCell style={TableHeadStyle} align="left" width={150}>Member ID</TableCell>
<TableCell style={TableHeadStyle} align="left" width={'*'}>Name</TableCell>
<TableCell style={TableHeadStyle} align="left" width={160}>Start Date</TableCell>
<TableCell style={TableHeadStyle} align="left" width={160}>End Date</TableCell>
<TableCell align="left" width={"10"} />
</TableRow>
</TableHead>
{/* Body Table */}
{dataTableIsLoading ?
(
<TableBody>
<TableRow>
<TableCell colSpan={6} align="center">Loading</TableCell>
</TableRow>
</TableBody>
)
:
(
dataTableData.length == 0 ?
(
<TableBody>
<TableRow>
<TableCell colSpan={6} align="center">No Data</TableCell>
</TableRow>
</TableBody>
)
:
(
<TableBody>
{dataTableData.map((row: LaboratoriumResultListType, index) => (
<LaboratoriumListRow key={index} number={index+1} row={row} />
))}
</TableBody>
)
)}
</Table>
</TableContainer>
</Box>
)
}

View File

@@ -0,0 +1,72 @@
/**
* Core
* ============================================
*/
import React, { useState } from "react";
import { useNavigate } from "react-router";
import { Box, Collapse, MenuItem, TableCell, TableRow, Stack } from "@mui/material";
import Visibility from '@mui/icons-material/Visibility';
/**
* Component
* ============================================
*/
// - Global -
import Label from "@/components/Label";
import TableMoreMenu from '@/components/table/TableMoreMenu';
/**
* Utils, Types, Functions
* ============================================
*/
import { fDate } from "@/utils/formatTime";
import { LaboratoriumResultListType } from "../Model/Types";
type Props = {
row: LaboratoriumResultListType,
number: number
}
export default function LaboratoriumResultListRow ({ ...props }: Props) {
const navigate = useNavigate()
return (
<React.Fragment>
<TableRow hover sx={{ '& > td': { borderBottom: '1' } }}>
<TableCell align="left" />
<TableCell align="left">{props.row.member_id}</TableCell>
<TableCell align="left">{props.row.name}</TableCell>
<TableCell align="left">
<Label
variant="ghost"
color="default"
>
{fDate(props.row.startdate)}
</Label>
</TableCell>
<TableCell align="left">
<Label
variant="ghost"
color="default"
>
{fDate(props.row.enddate)}
</Label>
</TableCell>
<TableCell align="right" onClick={(e) => e.stopPropagation()}>
<Stack direction="row" justifyContent="flex-end" spacing={1}>
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate(`/case_management/laboratorium_result/${props.row.member_id}/claims`)}>
<Visibility />
View
</MenuItem>
</>
} />
</Stack>
</TableCell>
</TableRow>
</React.Fragment>
);
}

View File

@@ -0,0 +1,100 @@
import axios from '@/utils/axios';
import { makeFormData } from '@/utils/jsonToFormData';
import { enqueueSnackbar } from 'notistack';
import { DetailLabResultListType, LaboratoriumResultListType, ResponseListingClaimType } from "./Types";
import { fDate } from '@/utils/formatTime';
/**
* Listing Daily Monitoring
*/
export const getDailyMonitoringList = async ( ): Promise<LaboratoriumResultListType[]> => {
const response = await axios.get('/case_management/memberlist')
.then((res) =>{
return res.data.data.member_list;
})
.catch((res) => {
enqueueSnackbar("server error !", {
variant: 'error',
});
return [];
});
return response;
};
/**
* Listing Claim
*/
export const getClaimList = async ( member_id: string ): Promise<ResponseListingClaimType> => {
const response = await axios.get(`/case_management/claimlist/${member_id}`)
.then((res) =>{
return res.data.data;
})
.catch((res) => {
enqueueSnackbar("server error !", {
variant: 'error',
});
return null;
});
return response;
};
/**
* Add Lab Result Detail
*/
export const AddLabResultDetail = async ( claim_code: string,data: DetailLabResultListType ): Promise<boolean> => {
data.date = data.date != '' && data.date != null ? fDate(data.date) : '';
const formData = makeFormData({...data});
const response = await axios.post(`/case_management/laboratorium_result/detail/${claim_code}/add`, formData)
.then((res) =>{
enqueueSnackbar(res.data.message, {
variant: 'success',
});
return true;
})
.catch((res) => {
if (res.response.status == 400) {
let arr_message = res.response.data.message;
for (const key in arr_message) {
enqueueSnackbar(arr_message[key][0], {
variant: 'warning',
});
}
}
else {
enqueueSnackbar("server error !", {
variant: 'error',
});
}
return false;
});
return response;
};
/**
* Get Lab Result Detail List
*/
export const getLabResultDetailList = async ( claim_code: string ): Promise<DetailLabResultListType[]> => {
const response = await axios.get(`/case_management/laboratorium_result/detail/${claim_code}/list`)
.then((res) =>{
return res.data.data.lab_result_list;
})
.catch((res) => {
enqueueSnackbar("server error !", {
variant: 'error',
});
return [];
});
return response;
};

View File

@@ -0,0 +1,57 @@
/**
* List Laboratorium
*/
export type LaboratoriumResultListType = {
member_id : string,
name : string,
startdate : string,
enddate : string,
}
/**
* Response Listing Claim
*/
export type ResponseListingClaimType = {
member_detail : MemberDetailType,
claim_list : ClaimListType[],
}
/**
* Member Detail
*/
export type MemberDetailType = {
id : string,
member_id : string,
name : string,
}
/**
* List Claim
*/
export type ClaimListType = {
claim_id : number,
admission_date : string,
discharge_date : string,
claim_code : string,
claim_status : string,
service_type : string,
member_id : string
}
/**
* Detail Lab Result List
*/
export type DetailLabResultListType = {
id : string|null,
claim_id : string|null,
claim_code : string,
date : string|null,
location : string,
examination : string,
lab_result_file : LabResultFileStrType[],
created_at : string|null
}
export type LabResultFileStrType = {
lab_result_file_obj: any
}

View File

@@ -0,0 +1,48 @@
/**
* Core
* ============================================
*/
import { Box, Grid } from '@mui/material';
/**
* Components
* ============================================
*/
// - Global -
import Page from '../../../components/Page';
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
// - Local -
import LaboratoriumResultList from './Components/LaboratoriumResultList';
export default function LaboratoriumResult() {
const pageTitle = "Laboratorium Result";
return (
<Page title={ pageTitle } sx={{ px: 2 }}>
<Grid container>
{/* page header */}
<Grid item xs={12}>
<HeaderBreadcrumbs
heading={ pageTitle }
sx={{ px: 1 }}
links={[
{
name: 'Dashboard',
href: '/dashboard',
},
{
name: pageTitle,
href: '/case_management/laboratorium_result',
},
]}
/>
</Grid>
{/* tabel daily monitoring */}
<Grid item xs={12}>
<LaboratoriumResultList />
</Grid>
</Grid>
</Page>
);
}

View File

@@ -0,0 +1,355 @@
/**
* Core
* ============================================
*/
import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router';
import { Box, FormControlLabel, Grid, Checkbox, Typography, CircularProgress , Button, styled, Stack, IconButton, Card} from '@mui/material';
import { LoadingButton } from '@mui/lab';
/**
* Components
* ============================================
*/
// - Global -
import Label from '@/components/Label';
// - Local -
import FormCreateSearch from './FormCreateSearch';
import FormCreateListChoose from './FormCreateListChoose';
import FormCreateBtnUpload from './FormCreateBtnUpload';
/**
* Icon, Utils, Types, Functions, theme, hook
* ============================================
*/
import { ArrowBackIosNew } from '@mui/icons-material';
import { fDateTimesecond } from '@/utils/formatTime';
import { MemberListType } from '../Model/Types';
import { addClaimRequest, getMemberList } from '../Model/Functions';
import palette from '@/theme/palette';
import FormCreateFilesUpload from './FormCreateFilesUpload';
import useLoadOnScroll from '@/hooks/useLoadOnScroll';
import useCollapseDrawer from '@/hooks/useCollapseDrawer';
import FormCreateBtnChoose from './FormCreateBtnChoose';
import axios from '../../../utils/axios';
export default function FormCreate() {
const navigate = useNavigate()
const defaultListChoosed:MemberListType[] = [];
// State
// -------------------------
const [keyword, setKeyword] = useState<string>('');
const [listChoosed, setListChoosed] = useState<MemberListType[]>([]);
const [isChoosed, setIsChoosed] = useState<boolean>(false);
const [formIsLoading, setFormIsLoading] = useState<boolean>(false);
// List Choose - auto Scroll
// -------------------------
const fetchFunction = async (page: number): Promise<MemberListType[]> => getMemberList(page, keyword)
const {data: MemberList, isLoading: scrollIsLoading, setData, resetLastPage, refetchData} = useLoadOnScroll<MemberListType>(fetchFunction);
// List Choose - Search
// -------------------------
const handleSearch = (keyword: string) => {
setData([])
resetLastPage()
setKeyword(keyword)
refetchData()
}
// Function - Clear Form
// -----------------------------
const clearForm = () => {
setListChoosed(defaultListChoosed);
setIsChoosed(false);
}
// Function - Choose Patien Type
// -----------------------------
const handleChoosePatienType = (data: MemberListType, type: string) => {
let newListChoosed = listChoosed.map((list) => {
if (data.id == list.id) {
list.patien_type = type
}
return list;
})
setListChoosed(newListChoosed)
}
// Function - Handle Btn Upload
// -----------------------------
const handleChangeInput = (data: MemberListType, type_file: 'kondisi'|'diagnosa'|'penunjang', file: any) => {
let newListChoosed = listChoosed.map((list) => {
if (data.id == list.id) {
if (type_file == 'kondisi') {
if (list.file_kondisi == undefined) {
list.file_kondisi = [file];
}
else {
list.file_kondisi.push(file);
}
}
if (type_file == 'diagnosa') {
if (list.file_diagnosa == undefined) {
list.file_diagnosa = [file];
}
else {
list.file_diagnosa.push(file);
}
}
if (type_file == 'penunjang') {
if (list.file_penunjang == undefined) {
list.file_penunjang = [file];
}
else {
list.file_penunjang.push(file);
}
}
}
return list;
})
setListChoosed(newListChoosed)
}
// Function - Handle Remove Fle
// -----------------------------
const handleRemoveFile = (data: MemberListType, type_file: 'kondisi'|'diagnosa'|'penunjang', target_index: number) => {
let newListChoosed = listChoosed.map((list) => {
if (data.id == list.id) {
if (type_file == 'kondisi') {
list.file_kondisi = list.file_kondisi?.filter((file: any, index: number) =>{
if (target_index !== index) {
return file;
}
});
}
if (type_file == 'diagnosa') {
list.file_diagnosa = list.file_diagnosa?.filter((file: any, index: number) =>{
if (target_index !== index) {
return file;
}
});
}
if (type_file == 'penunjang') {
list.file_penunjang = list.file_penunjang?.filter((file: any, index: number) =>{
if (target_index !== index) {
return file;
}
});
}
}
return list;
})
setListChoosed(newListChoosed)
}
// Function - Handle Submit Form
// -----------------------------
const handleSubmit = async () => {
setFormIsLoading(true)
let response = await addClaimRequest(listChoosed)
setFormIsLoading(false)
if (response == true) {
clearForm()
}
}
let isDirty = listChoosed.some((row) => {
if (row.patien_type == undefined) {
return true
}
})
return (
<Box>
{/* Back Button */}
<Box sx={{ display: 'flex', alignItems: 'center', mb: 5}}>
<IconButton size='large' color='inherit' onClick={() => isChoosed==false ? navigate(`/claim-requests`) : setIsChoosed(false)} >
<ArrowBackIosNew/>
</IconButton>
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
{'Create Claim Requests'}
</Typography>
</Box>
{/* Choose Section */}
<Grid container spacing={4} sx={{ px: 2, position: 'relative', display: isChoosed==false ? 'inherit' : 'none' }}>
{/* Search */}
<Grid item xs={12}>
<FormCreateSearch onEmpty={() => handleSearch('')} onSubmit={(keyword) => handleSearch(keyword)} />
</Grid>
<Grid item xs={12}>
<Grid container spacing={2}>
{/* List */}
<Grid item xs={12}>
<Grid container spacing={2}>
{
MemberList.map((row, index) => {
return (
<FormCreateListChoose
key={index}
data={row}
ListChoosed={listChoosed}
handleCheckedProp={(checked, data) => {
checked ? setListChoosed((prevData) => [...prevData, data]) : setListChoosed((items) => items.filter(item => item.id != data.id))
}}
/>
)
})
}
</Grid>
</Grid>
{/* Loading */}
<Grid item xs={12} sx={{ display: scrollIsLoading === false ? 'none' : 'flex', justifyContent: 'center', marginTop: '40px' }}>
<CircularProgress />
</Grid>
{/* Submit List */}
<Grid item xs={12}>
<FormCreateBtnChoose disabled={listChoosed.length==0} title={`Create Number Batch (${listChoosed.length})`} handleClickProp={() => setIsChoosed(true)} />
</Grid>
</Grid>
</Grid>
</Grid>
{/* Input Section */}
<Grid container spacing={10} sx={{ px: 2, display: isChoosed==true ? 'inherit' : 'none' }}>
{
listChoosed.map((row, index) => {
return (
<Grid key={index} item xs={12}>
<Grid container spacing={6}>
{/* Patien Name */}
<Grid item xs={12}>
<Card sx={{ border: '1px solid rgba(0,0,0,0.05)', display: 'flex', justifyContent: 'space-between', borderRadius: '12px', px: '24px', py: '16px' }}>
<Box>
<Typography variant="body2" sx={{ fontWeight: 600 }}>
{row.name}
</Typography>
<Typography variant="caption" color={palette.light.grey[500]} sx={{ fontWeight: 600 }}>
{row.member_id}
</Typography>
</Box>
<Label variant="ghost" color="default">
{fDateTimesecond(new Date())}
</Label>
</Card>
</Grid>
{/* Patien Type */}
<Grid item xs={12}>
<Grid container spacing={2}>
{row.service_type.map((r,i) => {
const code = r.code
return (
<Grid item xs={6}>
<Button
sx={{ padding: 2, width: '100%',border: row.patien_type === code ? '1px solid #19BBBB' : '1px solid #919EAB52' }}
variant="outlined"
color={row.patien_type === code ? 'primary' : 'inherit'}
onClick={() => {
handleChoosePatienType(row, code)
}}
>
{r.name}
</Button>
</Grid>
)
})}
</Grid>
</Grid>
{/* File Kondisi */}
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h6">Condition Document</Typography>
</Grid>
{row.file_kondisi && row.file_kondisi.map((file, index) => (
<Grid item xs={12} key={index}>
<FormCreateFilesUpload file={file} handleRemoveFileProp={() => handleRemoveFile(row, 'kondisi', index)} />
</Grid>
))}
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<FormCreateBtnUpload handleChangeInputProp={(file) => handleChangeInput(row, 'kondisi', file)} />
</Grid>
</Grid>
</Grid>
{/* File Diagnosa */}
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h6">Diagnosis Document</Typography>
</Grid>
{row.file_diagnosa && row.file_diagnosa.map((file, index) => (
<Grid item xs={12} key={index}>
<FormCreateFilesUpload file={file} handleRemoveFileProp={() => handleRemoveFile(row, 'diagnosa', index)} />
</Grid>
))}
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<FormCreateBtnUpload handleChangeInputProp={(file) => handleChangeInput(row, 'diagnosa', file)} />
</Grid>
</Grid>
</Grid>
{/* File Penunjang */}
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h6">Supporting Result Document</Typography>
</Grid>
{row.file_penunjang && row.file_penunjang.map((file, index) => (
<Grid item xs={12} key={index}>
<FormCreateFilesUpload file={file} handleRemoveFileProp={() => handleRemoveFile(row, 'penunjang', index)} />
</Grid>
))}
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<FormCreateBtnUpload handleChangeInputProp={(file) => handleChangeInput(row, 'penunjang', file)} />
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
)
})
}
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Box display="flex" gap={1}>
<Button variant="outlined" color="inherit" onClick={() => clearForm()}>
Cancel
</Button>
<LoadingButton disabled={isDirty} type="submit" variant="contained" loading={formIsLoading} onClick={() => handleSubmit()}>
Save Changes
</LoadingButton>
</Box>
</Grid>
</Grid>
</Box>
)
}

View File

@@ -0,0 +1,39 @@
import { styled, Button } from "@mui/material";
import useCollapseDrawer from "@/hooks/useCollapseDrawer";
/**
* Custom Style
* ============================================
*/
const DivCustom1 = styled('div')(({ theme }) => ({
background: 'white',
position: 'fixed',
left: '350px',
right: 0,
bottom: 0,
paddingLeft: '32px',
paddingRight: '32px',
paddingTop: '32px',
paddingBottom: '48px',
[theme.breakpoints.between('sm', 'lg')]: {
left: '0px',
},
}));
type Props = {
disabled: boolean,
title : string,
handleClickProp: () => void
}
export default function FormCreateBtnChoose ({disabled, title, handleClickProp}: Props) {
const { collapseClick } = useCollapseDrawer();
return (
<DivCustom1 sx={{ left: collapseClick ? '80px' : '350px' }}>
<Button variant="contained" color="primary" disabled={disabled} sx={{ width: '100%', p: '11px' }} onClick={handleClickProp}>
{title}
</Button>
</DivCustom1>
)
}

View File

@@ -0,0 +1,39 @@
import { useRef } from "react";
import { Box, ButtonBase, Typography } from "@mui/material";
import Iconify from "@/components/Iconify";
type Props = {
handleChangeInputProp: (event: any) => void
}
export default function FormCreateBtnUpload ({handleChangeInputProp}: Props) {
const fileInput = useRef<HTMLInputElement>(null);
return (
<ButtonBase sx={{ py: 5, border: '2px dashed #F9FAFB',bgcolor: '#919EAB52',borderRadius: '8px',width: '100%', height: '60px'}} onClick={() => fileInput.current?.click()}>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
py:'11px'
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="1.5em" />
<Typography variant="body1" fontWeight="bold" fontSize={'15px'}>
Upload Result
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileInput}
style={{ display: 'none' }}
multiple
onChange={(event) => handleChangeInputProp(event.target.files ? event.target.files[0] : {})}
accept="application/pdf"
/>
</ButtonBase>
)
}

View File

@@ -0,0 +1,25 @@
import Iconify from "@/components/Iconify";
import { ArrowBackIosNew, InsertDriveFile } from '@mui/icons-material';
import { Stack, Typography } from "@mui/material";
type Props = {
file: any,
handleRemoveFileProp: () => void,
}
export default function FormCreateFilesUpload({ file, handleRemoveFileProp }: Props) {
return (
<Stack direction="row" justifyContent={'space-between'} sx={{ mb: '16px' }}>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFile />
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
</Stack>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {handleRemoveFileProp()}}
sx={{cursor: 'pointer'}}
></Iconify>
</Stack>
)
}

View File

@@ -0,0 +1,78 @@
/**
* Core
* ============================================
*/
import { useEffect, useState } from 'react';
import { Box, FormControlLabel, Grid, Checkbox, Typography, Card} from '@mui/material';
/**
* Components
* ============================================
*/
// - Global -
import Label from '@/components/Label';
// - Local -
/**
* Icon, Utils, Types, Functions, theme, hook
* ============================================
*/
import { fDateTimesecond } from '@/utils/formatTime';
import { MemberListType } from '../Model/Types';
import palette from '@/theme/palette';
/**
* Props
* =====================================================
*/
type Props = {
data: MemberListType,
ListChoosed: MemberListType[],
handleCheckedProp: (checked: boolean, data: MemberListType) => void,
};
export default function FormCreateListChoose({data, ListChoosed, handleCheckedProp}: Props) {
const [isChoosed, setIsChoosed] = useState<boolean>(false)
useEffect(() => {
setIsChoosed(false);
ListChoosed.forEach(list => {
if (list.id == data.id) {
setIsChoosed(true);
}
})
}, [ListChoosed])
return (
<Grid item xs={12}>
<Card sx={{
border: '0px solid rgba(0,0,0,0.125)', px: '24px', py: '16px', borderRadius: '12px', display: 'flex', justifyContent: 'space-between',
bgcolor: (theme) => {
return isChoosed ? palette.light.primary.lighter : palette.light.background.default
}
}}>
<Box sx={{ display: 'flex', alignItems: 'center', px: '8px'}}>
<FormControlLabel
label=""
control={<Checkbox onChange={(event, checked) => handleCheckedProp(checked, data)} />}
checked={isChoosed}
/>
<Box>
<Typography variant="body2" sx={{ fontWeight: 600 }}>
{data.name}
</Typography>
<Typography variant="caption" color={palette.light.grey[500]} sx={{ fontWeight: 600 }}>
{data.member_id}
</Typography>
</Box>
</Box>
<Label variant="ghost" color="default">
{fDateTimesecond(new Date())}
</Label>
</Card>
</Grid>
)
}

View File

@@ -0,0 +1,70 @@
/**
* Core
* ============================================
*/
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { Grid } from '@mui/material';
/**
* Components
* ============================================
*/
// - Global -
import { FormProvider, RHFTextField } from '@/components/hook-form';
// - Local -
/**
* Icon, Utils, Types, Functions
* ============================================
*/
import { Search } from '@mui/icons-material';
import { SearchType } from '../Model/Types';
type Props = {
onSubmit: (keyword: string) => void,
onEmpty: () => void,
};
const FormCreateSearch = ({ onSubmit, onEmpty }: Props) => {
const defaultValuesSearchForm = {
keyword: ''
};
const methodsSearchForm = useForm<SearchType>({
defaultValues: defaultValuesSearchForm
});
const { handleSubmit, formState: { isDirty } } = methodsSearchForm;
// search on submit
const onSubmitSearch = (data: SearchType ) => {
onSubmit(data.keyword);
}
// search on empty
useEffect(() => {
if (isDirty === false) {
onEmpty()
}
},[isDirty])
return (
<FormProvider methods={methodsSearchForm} onSubmit={handleSubmit(onSubmitSearch)}>
<Grid container direction={"row"}>
<Grid item xs={12}>
<RHFTextField
name="keyword"
placeholder="Search..."
autoComplete='off'
fullWidth
InputProps={{ startAdornment: <Search /> }}
sx={{ input: { paddingLeft: '14px' } }}
/>
</Grid>
</Grid>
</FormProvider>
)
}
export default FormCreateSearch

View File

@@ -0,0 +1,456 @@
import * as Yup from 'yup';
import { useSnackbar } from 'notistack';
import { useNavigate } from 'react-router-dom';
import { yupResolver } from '@hookform/resolvers/yup';
import { Controller, useForm } from 'react-hook-form';
import React, { useRef, useEffect, useMemo, useState } from 'react';
import axios from '../../../utils/axios';
import { FormProvider, RHFTextField } from '../../../components/hook-form';
import { makeFormData } from '@/utils/jsonToFormData';
import {
Autocomplete,
Button,
Grid,
Stack,
Table,
TableBody,
TableCell,
TableRow,
TextField,
Typography,
useTheme,
List,
ListItem,
IconButton,
ListItemAvatar,
Avatar,
ListItemText,
Card,
InputAdornment,
Divider,
ButtonBase,
Box,
} from '@mui/material';
import Iconify from '../../../components/Iconify';
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
import { LoadingButton } from '@mui/lab';
import { fCurrency } from '../../../utils/formatNumber';
import MemberSelectDialog from '../../../components/dialogs/MemberSelectDialog';
import { Add, ArrowBackIosNew, DeleteOutline } from '@mui/icons-material';
import { ClaimRequest, Files } from '@/@types/claims';
import { fDateTimesecond } from '@/utils/formatTime';
interface FormValuesProps extends Partial<ClaimRequest> {
taxes: boolean;
inStock: boolean;
}
type Props = {
isEdit: boolean;
currentClaim?: ClaimRequest;
};
export default function FormEdit({ isEdit, currentClaim }: Props) {
const navigate = useNavigate();
const { enqueueSnackbar } = useSnackbar();
const EditClaimSchema = Yup.object().shape({
organization_id: Yup.string().required('Code Provider is required'),
});
const defaultValues = useMemo(
() => ({
id: currentClaim?.id || '-',
code: currentClaim?.code || '-',
member_name: currentClaim?.member?.name || '-',
date: currentClaim?.submission_date ? fDateTimesecond(currentClaim?.submission_date) : '-',
claim_method: currentClaim?.payment_type || '-',
service_type: currentClaim?.service_code || '-',
organization_id: currentClaim?.organization?.code || '-',
}),
[currentClaim]
);
useEffect(() => {
if (isEdit && currentClaim) {
reset(defaultValues);
}
if (!isEdit) {
reset(defaultValues);
}
// setFileKondisis(currentClaim?.files_by_type?.claim_diagnosis);
// setFileDiagnosas(currentClaim?.files_by_type?.claim_diagnosis);
setFileHasilPenunjangCurrent(currentClaim?.files_by_type?.claim_result);
}, [isEdit, currentClaim]);
const methods = useForm<FormValuesProps>({
resolver: yupResolver(EditClaimSchema),
defaultValues,
});
const {
reset,
watch,
control,
setValue,
getValues,
setError,
handleSubmit,
formState: { isSubmitting },
} = methods;
const values = watch();
const [isCheckingLimit, setIsCheckingLimit] = useState(false);
const [isEligible, setIsEligible] = useState(false);
const [memberBenefits, setMemberBenefits] = useState([]);
const [diagnosisOption, setDiagnosisOption] = useState([]);
const [isMemberDialogOpen, setIsMemberDialogOpen] = useState(false);
const [member, setMember] = useState({})
// ----------------------------------------------------------------------
// Files Result Kondisi
const fileKondisiInput = useRef<HTMLInputElement>(null);
const [fileKondisis, setFileKondisis] = useState<Files>([]);
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 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 Hasil Penunjang
const fileHasilPenunjangInput = useRef<HTMLInputElement>(null);
const [fileHasilPenunjangs, setFileHasilPenunjangs] = useState([]);
const [fileHasilPenunjangsCurrent, setFileHasilPenunjangCurrent] = useState([]);
const handleResultInputChange = (event) => {
if (event.target.files[0]) {
setFileHasilPenunjangs([...fileHasilPenunjangs, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeFiles = (filesState, index) => {
setFileHasilPenunjangs(
filesState.filter((file, fileIndex) => {
return fileIndex != index;
})
);
};
const onSubmit = async (data: FormValuesProps) => {
try {
// const formData = new FormData();
// formData.append('result_files', fileHasilPenunjangs);
// formData.append('diagnosa_files', fileDiagnosaInput);
// formData.append('kondisi_files', fileKondisiInput);
// formData.append('provider_code', data.organization_id);
// formData.append('_method', 'PUT');
const formData = makeFormData({
result_files: fileHasilPenunjangs,
diagnosa_files: fileDiagnosas,
kondisi_files: fileKondisis,
provider_code: data.organization_id,
_method: 'PUT'
});
const response = await axios.put(`/claim-requests/${data.id}`, formData);
reset();
enqueueSnackbar('Claim Request Updated Successfully!', { variant: 'success' });
navigate('/claim-requests');
} catch (error: any) {
if (error && error.response.status === 422) {
for (const [key, value] of Object.entries(error.response.data.errors)) {
// setError(key, { message: value[0] });
enqueueSnackbar('Failed Processing Request', { variant: 'error' });
}
} else {
enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' });
}
}
};
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
<Box sx={{ display: 'flex', alignItems: 'center'}}>
<IconButton size='large' color='inherit' onClick={() => navigate(`/claim-requests`)} >
<ArrowBackIosNew/>
</IconButton>
<Typography variant="h5" sx={{ marginLeft: '24px' }}>
{'Edit Claim Requests'}
</Typography>
</Box>
</Stack>
<Card sx={{paddingX:2, paddingY:2}}>
<Grid container spacing={2}>
<Grid item xs={5}>
<Typography variant="subtitle1">Code*</Typography>
</Grid>
<Grid item xs={7}>
<Typography variant="subtitle1">Name*</Typography>
</Grid>
<Grid item xs={5}>
<RHFTextField name="code" label="Code" disabled/>
</Grid>
<Grid item xs={7}>
<RHFTextField name="member_name" label="Name" disabled/>
</Grid>
{/* <input type="hidden" name="id"/> */}
<Grid item xs={12}></Grid>
<Grid item xs={3}>
<Typography variant="subtitle1">Date of Submission*</Typography>
</Grid>
<Grid item xs={3}>
<Typography variant="subtitle1">Claim Method*</Typography>
</Grid>
<Grid item xs={3}>
<Typography variant="subtitle1">Service Type*</Typography>
</Grid>
<Grid item xs={3}>
<Typography variant="subtitle1">Code Provider*</Typography>
</Grid>
<Grid item xs={3}>
<RHFTextField InputProps={{endAdornment: (
<InputAdornment position="end">
<CalendarTodayIcon />
</InputAdornment>
), }}
name="date" label="Date of Submission" disabled/>
</Grid>
<Grid item xs={3}>
<RHFTextField name="claim_method" label="Claim Method" disabled/>
</Grid>
<Grid item xs={3}>
<RHFTextField name="service_type" label="Service Type*" disabled/>
</Grid>
<Grid item xs={3}>
<RHFTextField name="organization_id" label="Code Provider*"/>
</Grid>
{/* -------------------------------Upload Dokumen Kondisi------------------------------- */}
<React.Fragment>
<Grid item xs={12}>
<Typography variant='h6'> Condition Document</Typography>
</Grid>
<Grid item xs={12}>
{fileKondisis &&
fileKondisis.map((file, index) => (
<Stack sx={{marginTop: 2}} 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>
))}
</Grid>
<Grid item xs={12}>
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%', height: '60px'}} onClick={() => fileKondisiInput.current?.click()}>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Add File
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileKondisiInput}
style={{ display: 'none' }}
multiple
onChange={handleKondisiInputChange}
accept="application/pdf"
/>
</ButtonBase>
</Grid>
</React.Fragment>
{/* -------------------------------Upload Dokumen Diagnosa------------------------------- */}
<React.Fragment>
<Grid item xs={12}>
<Typography variant='h6'> Diagnosis Document</Typography>
</Grid>
<Grid item xs={12}>
{fileDiagnosas &&
fileDiagnosas.map((file, index) => (
<Stack sx={{marginTop: 2}} 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>
))}
</Grid>
<Grid item xs={12}>
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%', height: '60px'}} onClick={() => fileDiagnosaInput.current?.click()}>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Add Result
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileDiagnosaInput}
style={{ display: 'none' }}
multiple
onChange={handleDiagnosaInputChange}
accept="application/pdf"
/>
</ButtonBase>
</Grid>
</React.Fragment>
{/* -------------------------------Upload Result Hasil Penunjang------------------------------- */}
<React.Fragment>
<Grid item xs={12}>
<Typography variant='h6'> Supporting Result Document</Typography>
</Grid>
<Grid item xs={12}>
{fileHasilPenunjangs &&
fileHasilPenunjangs.map((file, index) => (
<Stack sx={{marginTop: 2}} 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>
))}
</Grid>
<Grid item xs={12}>
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%', height: '60px'}} onClick={() => fileHasilPenunjangInput.current?.click()}>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Add Result
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileHasilPenunjangInput}
style={{ display: 'none' }}
multiple
onChange={handleResultInputChange}
accept="application/pdf"
/>
</ButtonBase>
</Grid>
</React.Fragment>
</Grid>
</Card>
<Grid container marginTop={3}>
<Grid item xs={12} md={12} >
<Stack direction="row" alignItems="center" justifyContent="flex-end">
<Button
sx={{
margin: 1
}}
type="submit"
variant="contained"
size="large"
color='inherit'
onClick={() => navigate(`/claim-requests`)}
>
Cancel
</Button>
<LoadingButton
type="submit"
variant="contained"
size="large"
loading={isSubmitting}
>
Update
</LoadingButton>
</Stack>
</Grid>
</Grid>
</FormProvider>
);
}

View File

@@ -1,8 +1,10 @@
import * as Yup from 'yup';
import { Box, IconButton } from '@mui/material';
import { ArrowBackIosNew } from '@mui/icons-material';
import { yupResolver } from '@hookform/resolvers/yup';
import { Autocomplete, Button, Card, Collapse, Container, Divider, Grid, 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 { useParams } from 'react-router-dom';
import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
import { FormProvider, RHFCheckbox, RHFSelect, RHFTextField } from '../../components/hook-form';
import Page from '../../components/Page';
@@ -15,49 +17,46 @@ import { enqueueSnackbar } from 'notistack';
import { LoadingButton } from '@mui/lab';
import { fCurrency } from '../../utils/formatNumber';
import Iconify from '../../components/Iconify';
import Form from './Form';
import { ClaimRequest } from '@/@types/claims';
import FormEdit from './Components/FormEdit';
import FormCreate from './Components/FormCreate';
export default function ClaimsCreateUpdate() {
const { themeStretch } = useSettings();
const { id } = useParams();
const isEdit = id ? true : false;
const { themeStretch } = useSettings();
const { id } = useParams();
const [currentClaim, setCurrentClaim] = useState();
const isEdit = id ? true : false;
const [currentClaim, setCurrentClaim] = useState<ClaimRequest>();
useEffect(() => {
if (isEdit) {
axios.get('/claim-requests/' + id).then((res) => {
console.log('Yeet', res.data);
setCurrentClaim(res.data.data);
});
console.log(currentClaim)
}
}, [id]);
useEffect(() => {
if (isEdit) {
axios.get('/claims/' + id).then((res) => {
// console.log('Yeet', res.data);
setCurrentClaim(res.data);
});
}
}, [id]);
return (
<Page title={isEdit ? `Edit Claim : ${currentClaim?.id}` : "Create New Claim"}>
<Page title={isEdit ? `Edit Claim Request` : "Create New Claim"}>
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center">
<HeaderBreadcrumbs
heading={
!isEdit
? 'Create New Claim'
: `Edit Claim : ${currentClaim?.code}`
}
links={[
{ name: 'Dashboard', href: '/dashboard' },
{
name: 'Claim',
href: '/claims',
},
{ name: !isEdit ? 'Create' : currentClaim?.id ?? '' },
]}
/>
</Stack>
{
id == undefined
?
(
<FormCreate />
)
:
(
<FormEdit isEdit={isEdit} currentClaim={currentClaim} />
)
}
<Form isEdit={isEdit} currentClaim={currentClaim} />
</Container>
</Page>
);

View File

@@ -0,0 +1,306 @@
// mui
import { Container, Grid, Stack, Typography, Card, TextField, Divider, ButtonBase, Box, IconButton } from '@mui/material';
// components
import Page from '../../components/Page';
// utils
import useSettings from '../../hooks/useSettings';
// react
import { useNavigate, useParams, useLocation } from 'react-router-dom';
import { useEffect, useState, useRef } from 'react';
import axios from '../../utils/axios';
// pages
import DetailTimeline from '../../pages/ClaimRequests/DetailTimeline';
import DetailStepper from '../../pages/ClaimRequests/DetailStepper';
import { format } from 'date-fns';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import Button from '@mui/material/Button';
import AddIcon from '@mui/icons-material/Add';
import RemoveIcon from '@mui/icons-material/Remove';
import { DatePicker, LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import Iconify from '@/components/Iconify';
import { fPostFormat } from '@/utils/formatTime';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
import DownloadIcon from '@mui/icons-material/Download';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { fDateTimesecond } from '@/utils/formatTime';
import { makeFormData } from '@/utils/jsonToFormData';
import { enqueueSnackbar } from 'notistack';
// ----------------------------------------------------------------------
export default function Detail() {
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const code = queryParams.get('code');
const navigate = useNavigate();
const { themeStretch } = useSettings();
const [data, setData] = useState();
const [dataDialog, setDataDialog] = useState();
const [document, setDocument] = useState(null);
const { id } = useParams();
useEffect(() => {
axios
.get('/claim-requests/detail/'+id)
.then((response) => {
setData(response.data);
setDataDialog(response.data.data.dialog_submits);
setDocument(response.data.data.documents);
})
.catch((error) => {
console.error(error);
});
}, []);
const [isInvoiceVisible, setInvoiceVisibility] = useState(false);
const handleInvoice = () => {
setInvoiceVisibility(!isInvoiceVisible);
}
const currentDate = new Date();
const formattedCurrentDate = format(currentDate, 'dd MMM yyyy');
const [dateInvoice, setDateInvoice] = useState(currentDate);
const fileInvoiceInput = useRef<HTMLInputElement>(null);
const [fileInvoices, setFileInvoices] = useState([]);
const handleInvoiceInputChange = (event) => {
if (event.target.files[0]) {
setFileInvoices([...fileInvoices, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeInvoiceFiles = (filesState, index) => {
setFileInvoices(
filesState.filter((file, fileIndex) => {
return fileIndex != index;
})
);
};
const date = dateInvoice ? fPostFormat(dateInvoice, 'yyyy-MM-dd') : null;
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
const handleCloseDialogSubmit = () => {
setOpenDialogSubmit(false);
}
const handleSubmitData = () => {
// if(fileInvoices.length > 0)
// {
//submit data
axios
.post('claim-requests/'+id+'/approve')
.then((response) => {
enqueueSnackbar('Success Submit Claim Request', { variant: 'success' });
setOpenDialogSubmit(false);
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something went wrong!', { variant: 'error' });
});
//Upload file invoices
const formData = makeFormData({
date:date,
invoice_files: fileInvoices,
});
axios
.post('claim-requests/'+id+'/invoice-files', formData)
.then((response) => {
enqueueSnackbar(response.data.message ?? 'Success upload invoice', { variant: 'success' });
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' });
});
// }
// else
// {
// enqueueSnackbar('Please upload file invoice, before submit', { variant: 'warning' });
// }
setTimeout(() =>
{
window.location.reload();
}, 5000);
};
const check_invoice = document?.find((dataInvoice) => dataInvoice.type === 'claim-invoice');
return (
<Page title='Detail'>
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center" sx={{ marginBottom: 3 }}>
<ArrowBackIosIcon onClick={() => navigate(-1)} sx={{cursor:'pointer'}}/>
<Typography variant="h5" sx={{marginLeft:2}}>{(data && data.data) ? data.data.status.code : ''}</Typography>
{data ? (
<Stack direction="row" spacing={2} ml="auto">
<Typography variant="body2" sx={{color: '#757575'}}>Submission Date</Typography>
<Typography variant="body2" fontWeight="bold">{(data && data.data) ? format(new Date(data.data.status.submission_date), "d MMM yyyy") : ''}</Typography>
</Stack>
) : ''}
</Stack>
{data ? (
<Grid container spacing={2}>
<Grid item xs={12} md={12}>
<DetailStepper data={data}/>
</Grid>
<Grid item xs={12} md={12}>
<Stack direction="row" alignItems="center">
<Typography variant="subtitle1">Format Claim</Typography>
<Button variant="outlined" color="primary" startIcon={< DownloadIcon/>} sx={{marginLeft: 'auto'}}>
<Typography variant="button" display="block">Import</Typography>
</Button>
</Stack>
</Grid>
{check_invoice ? (
<Grid item xs={12} md={12}>
<Stack direction="row" alignItems="center">
<Typography variant="subtitle1">Request Claim</Typography>
<Button variant="outlined" color="primary" startIcon={ isInvoiceVisible ? < RemoveIcon/> : < AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => handleInvoice()}>
<Typography variant="button" display="block">Invoice</Typography>
</Button>
</Stack>
</Grid>
) : ''}
<Grid item xs={12} md={12} sx={{display : isInvoiceVisible ? '' : 'none',}}>
<Card sx={{padding: 2}}>
<Stack direction="column" spacing={2}>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<DatePicker
label="Invoice Date"
value={dateInvoice}
onChange={(newValue) => {
setDateInvoice(newValue);
}}
inputFormat="dd MMM yyyy"
renderInput={(params) => <TextField sx={{width:'40%'}} {...params} defaultValue={formattedCurrentDate} required/>}
/>
</LocalizationProvider>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileInvoices &&
fileInvoices.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
</Stack>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeInvoiceFiles(fileInvoices, index);
}}
sx={{cursor: 'pointer'}}
></Iconify>
</Stack>
))}
</Stack>
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%', height: '60px'}} onClick={() => fileInvoiceInput.current?.click()}>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Upload Invoice
</Typography>
</Box>
<input
type="file"
id="file"
ref={fileInvoiceInput}
style={{ display: 'none' }}
multiple
onChange={handleInvoiceInputChange}
accept="application/pdf"
/>
</ButtonBase>
</Stack>
</Card>
</Grid>
<Grid item xs={12} md={12}>
<DetailTimeline data={data}/>
</Grid>
<Grid item xs={12} md={12}>
<Stack direction="row" padding={4}>
{dataDialog && dataDialog.status === 'requested' ? (
<>
<Button variant="outlined" sx={{color: '#212B36', marginLeft: 'auto', borderColor: '#919EAB52'}} >Cancel</Button>
<Button sx={{backgroundColor: '#19BBBB', marginLeft: 1}} variant="contained" onClick={()=> setOpenDialogSubmit(true)}>Submit</Button>
</>
) : ''}
{/* Dialog Submits */}
<Dialog open={openDialogSubmit} onClose={handleCloseDialogSubmit} fullWidth={true}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" alignItems='center' spacing={1}>
<Typography variant="h6">Confirmation</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogSubmit}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
{dataDialog ? (
<Stack spacing={2} padding={2}>
<Typography variant='body1'>Are you sure to submit this claim ?</Typography>
<Card sx={{padding:2}} >
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Code</Typography>
<Typography variant='subtitle2' sx={{width: '70%'}}>{dataDialog.code}</Typography>
</Stack>
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Name</Typography>
<Typography variant='subtitle2' sx={{width: '70%'}}>{dataDialog.name}</Typography>
</Stack>
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Date Submission</Typography>
<Typography variant='subtitle2' sx={{width: '70%'}}>{fDateTimesecond(dataDialog.submission_date)}</Typography>
</Stack>
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Claim Method</Typography>
<Typography variant='subtitle2' sx={{width: '70%'}}>Service Type</Typography>
</Stack>
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Service Type</Typography>
<Typography variant='subtitle2' sx={{width: '70%'}}>
{dataDialog.service_code === 'IP' ? 'Inpatient' : 'Outpatient'}
</Typography>
</Stack>
</Card>
</Stack>
) : ''}
</DialogContent>
<DialogActions>
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialogSubmit}>Cancel</Button>
<Button sx={{backgroundColor: '#19BBBB'}} onClick={handleSubmitData} variant="contained">Submit</Button>
</DialogActions>
</Dialog>
</Stack>
</Grid>
</Grid>
) : ''}
</Container>
</Page>
);
}

View File

@@ -0,0 +1,58 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Stepper from '@mui/material/Stepper';
import Step from '@mui/material/Step';
import StepLabel from '@mui/material/StepLabel';
import { useEffect, useState } from 'react';
import ClearIcon from '@mui/icons-material/Clear';
const steps = [
'Request',
'Review',
'Approval',
'Decline',
];
export default function HorizontalLinearAlternativeLabelStepper({data}) {
const [active, setActive] = useState(0);
const [status, SetStatus] = useState(null);
let updatedSteps = [...steps];
useEffect(() => {
if (data && data.data) {
if (data.data.status.status === 'requested') {
setActive(1);
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
}
else if (data.data.status.status === 'reviewed') {
setActive(2);
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
}
else if (data.data.status.status === 'approved')
{
setActive(3);
updatedSteps = updatedSteps.filter(step => step !== 'Decline');
}
else if(data.data.status.status === 'declined')
{
setActive(4)
updatedSteps = updatedSteps.filter(step => step !== 'Approval');
}
}
SetStatus(updatedSteps);
}, [data]);
return (
<Box sx={{ width: '100%', marginBottom: 2 }}>
<Stepper activeStep={active} alternativeLabel>
{status?.map((label) => (
<Step key={label}>
<StepLabel icon={label==='Decline' ? <ClearIcon sx={{ color: 'white', backgroundColor: 'red', borderRadius: '50%' }} /> : ''}>{label}</StepLabel>
</Step>
))}
</Stepper>
</Box>
);
}

View File

@@ -0,0 +1,426 @@
import * as React from 'react';
import Timeline from '@mui/lab/Timeline';
import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
import TimelineSeparator from '@mui/lab/TimelineSeparator';
import TimelineConnector from '@mui/lab/TimelineConnector';
import TimelineContent from '@mui/lab/TimelineContent';
import TimelineDot from '@mui/lab/TimelineDot';
import {Typography, Card, Stack, ButtonBase, Box, Divider} from '@mui/material';
import { styled } from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import Button from '@mui/material/Button';
import AddIcon from '@mui/icons-material/Add';
import Iconify from '../../components/Iconify';
import { useEffect, useState, useRef } from 'react';
import { format } from 'date-fns';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
import DescriptionIcon from '@mui/icons-material/Description';
import { LoadingButton } from '@mui/lab';
import axios from '../../utils/axios';
import { makeFormData } from '@/utils/jsonToFormData';
import { enqueueSnackbar } from 'notistack';
import { useParams} from 'react-router-dom';
const Item1 = styled(Paper)(({ theme }) => ({
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
backgroundColor: '#919EAB29',
color: '#637381',
width: 'fit-content',
marginRight: 'auto',
}));
const Item2 = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: theme.palette.text.secondary,
width: 'fit-content',
marginLeft: 'auto',
}));
export default function NoOppositeContent({data}) {
const [timeline, setTimeline] = useState(null);
const [requestFile, setRequestFile] = useState(null);
const [document, setDocument] = useState(null);
useEffect(() => {
if (data && data.data) {
setTimeline(data.data.timeline);
setRequestFile(data.data.request_files);
setDocument(data.data.documents);
}
}, [data]);
// Diagnosis
const fileRequestDocumentInputDiagnosis = useRef<HTMLInputElement>(null);
const [fileDiagnosis, setFileDiagnosis] = useState([]);
const handleRequestDocumentInputChangeDiagnosis = (event) => {
if (event.target.files[0]) {
setFileDiagnosis([...fileDiagnosis, ...event.target.files]);
}
};
const removeFileDiagnois = (filesState, index) => {
setFileDiagnosis(
filesState.filter((file, fileIndex) => {
return fileIndex != index;
})
);
};
// Kondisi
const fileRequestDocumentInputKondisi = useRef<HTMLInputElement>(null);
const [fileKondisi, setFileKondisi] = useState([]);
const handleRequestDocumentInputChangeKondisi = (event) => {
if (event.target.files[0]) {
setFileKondisi([...fileKondisi, ...event.target.files]);
}
};
const removeFileKondisi = (filesState, index) => {
setFileKondisi(
filesState.filter((file, fileIndex) => {
return fileIndex != index;
})
);
};
// Result
const fileRequestDocumentInputResult = useRef<HTMLInputElement>(null);
const [fileResult, setFileResult] = useState([]);
const handleRequestDocumentInputChangeResult = (event) => {
if (event.target.files[0]) {
setFileResult([...fileResult, ...event.target.files]);
}
};
const removeFileResult = (filesState, index) => {
setFileResult(
filesState.filter((file, fileIndex) => {
return fileIndex != index;
})
);
};
const { id } = useParams();
const [submitLoading, setSubmitLoading] = useState(false);
const submitRequestFiles = () => {
setSubmitLoading(true);
const formData = makeFormData({
fileDiagnosis: fileDiagnosis,
fileKondisis: fileKondisi,
fileResults: fileResult
});
axios
.post('claim-requests/'+id+'/request-files', formData)
.then((response) => {
window.location.reload();
})
.catch(({ response }) => {
enqueueSnackbar(response.data.message ?? 'Something Went Wrong', { variant: 'error' });
});
}
const submitButton = requestFile?.find((dataRequestFile) => dataRequestFile.check_files === null);
return (
<>
{timeline?.map((dataTimeline, index) => (
<Timeline
sx={{
[`& .${timelineItemClasses.root}:before`]: {
flex: 0,
padding: 0,
},
}}
key={index}
>
<Typography variant="body2" gutterBottom fontWeight="bold">{dataTimeline.date ? format(new Date(dataTimeline.date), "d MMM yyyy") : ''}</Typography>
<TimelineItem>
<TimelineSeparator>
<TimelineDot />
<TimelineConnector />
</TimelineSeparator>
<TimelineContent spacing={3}>
<Card sx={{ borderRadius: '6px', paddingY: 2 }}>
<Stack sx={{marginLeft: 2, marginRight: 2, marginTop: 2 }}>
<Stack direction="row" sx={{marginBottom: 2, paddingBottom: 2, borderBottom: '1px solid #919EAB52' }}>
<Item1>{dataTimeline.date ? format(new Date(dataTimeline.date), "HH : mm") : ''}</Item1>
<Item2 sx={{backgroundColor: dataTimeline.txt_status_backgroundColor, color: dataTimeline.txt_status_color}}>{dataTimeline.txt_status}</Item2>
</Stack>
<Stack direction="row" spacing={2} sx={{marginBottom: 2}}>
<Typography variant="body2" gutterBottom>Detail:</Typography>
<Typography variant="body2" gutterBottom>{dataTimeline.description}</Typography>
</Stack>
{dataTimeline.status === 'reviewed' && requestFile ? (
<>
{submitButton ? (
<Typography variant="body2" gutterBottom>Request Document</Typography>
) : (
<Typography sx={{color: '#19BBBB'}} variant="body2" gutterBottom>Request Document Success Uploaded</Typography>
)}
{/* Diagnosis */}
{requestFile?.map((dataRequestFile, index) => {
if(dataRequestFile.type !== 'claim-diagnosis' || dataRequestFile.check_files !== null){
return null;
}
return (
<Stack spacing={2} sx={{marginBottom: 2}} key={index}>
<Typography variant="body2" gutterBottom fontWeight="bold">
Diagnosis
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileDiagnosis &&
fileDiagnosis.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
</Stack>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeFileDiagnois(fileDiagnosis, index);
}}
sx={{cursor: 'pointer'}}
></Iconify>
</Stack>
))}
</Stack>
<ButtonBase
sx={{
p: 4,
border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%',
height: '60px',
}}
onClick={() => fileRequestDocumentInputDiagnosis.current?.click()}
>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Add Result
</Typography>
</Box>
<input
type="file"
id={`file-${index}`}
ref={fileRequestDocumentInputDiagnosis}
style={{ display: 'none' }}
multiple
onChange={(event) => handleRequestDocumentInputChangeDiagnosis(event)}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
</ButtonBase>
</Stack>
);
})}
{/* Kondisi */}
{requestFile?.map((dataRequestFile, index) => {
if(dataRequestFile.type !== 'claim-kondisi' || dataRequestFile.check_files !== null){
return null;
}
return (
<Stack spacing={2} sx={{marginBottom: 2}} key={index}>
<Typography variant="body2" gutterBottom fontWeight="bold">
Condition
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileKondisi &&
fileKondisi.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
</Stack>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeFileKondisi(fileKondisi, index);
}}
sx={{cursor: 'pointer'}}
></Iconify>
</Stack>
))}
</Stack>
<ButtonBase
sx={{
p: 4,
border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%',
height: '60px',
}}
onClick={() => fileRequestDocumentInputKondisi.current?.click()}
>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Add Result
</Typography>
</Box>
<input
type="file"
id={`file-${index}`}
ref={fileRequestDocumentInputKondisi}
style={{ display: 'none' }}
multiple
onChange={(event) => handleRequestDocumentInputChangeKondisi(event)}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
</ButtonBase>
</Stack>
);
})}
{/* Supporting Result */}
{requestFile?.map((dataRequestFile, index) => {
if(dataRequestFile.type !== 'claim-result' || dataRequestFile.check_files !== null){
return null;
}
return (
<Stack spacing={2} sx={{marginBottom: 2}} key={index}>
<Typography variant="body2" gutterBottom fontWeight="bold">
Supporting Result
</Typography>
<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2 }}
>
{fileResult &&
fileResult.map((file, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<Typography variant="body2" gutterBottom>{file.name ? file.name : '-'}</Typography>
</Stack>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeFileResult(fileResult, index);
}}
sx={{cursor: 'pointer'}}
></Iconify>
</Stack>
))}
</Stack>
<ButtonBase
sx={{
p: 4,
border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%',
height: '60px',
}}
onClick={() => fileRequestDocumentInputResult.current?.click()}
>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Add Result
</Typography>
</Box>
<input
type="file"
id={`file-${index}`}
ref={fileRequestDocumentInputResult}
style={{ display: 'none' }}
multiple
onChange={(event) => handleRequestDocumentInputChangeResult(event)}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain, application/pdf"
/>
</ButtonBase>
</Stack>
);
})}
{submitButton ? (
<LoadingButton
variant="contained"
sx={{ marginTop: 2, p: 2, backgroundColor: '#19BBBB' }}
onClick={() => {
submitRequestFiles();
}}
loading={submitLoading}
>
Submit
</LoadingButton>
) : ''}
</>
) : ''}
</Stack>
</Card>
{dataTimeline.status === 'requested' ? (
<Card sx={{marginTop: 2 }}>
<Stack sx={{marginLeft: 2, marginRight: 2, marginTop: 2 }}>
<Stack direction="row" spacing={2} sx={{marginBottom: 2, paddingBottom: 2, borderBottom: '1px solid #919EAB52' }} alignItems="center">
<DescriptionIcon />
<Typography variant="Subtitle2" sx={{fontWeight: 'bold'}}>Documents</Typography>
</Stack>
<Stack direction="column" spacing={2} sx={{marginBottom: 2}}>
{document?.map((dataDocument, index) => (
<Stack direction="column" spacing={2} key={index}>
<Typography variant="Subtitle2" gutterBottom>
{dataDocument.type === 'claim-diagnosis' ?
'Diagnosis'
: dataDocument.type === 'claim-kondisi' ?
'Condition'
: dataDocument.type === 'claim-result' ?
'Supporting Result'
: dataDocument.type === 'claim-invoice' ?
'Invoice'
: ''}
</Typography>
<Stack direction="row" spacing={1} sx={{color: '#19BBBB'}}>
<InsertDriveFileIcon />
<a
href={dataDocument.path}
style={{ cursor: 'pointer', textDecoration: 'underline', color: '#19BBBB' }}
target="_blank"
>
<Typography variant="body2" gutterBottom>{dataDocument.original_name ? dataDocument.original_name : '-'}</Typography>
</a>
</Stack>
</Stack>
))}
</Stack>
</Stack>
</Card>
) : ''}
</TimelineContent>
</TimelineItem>
</Timeline>
))}
</>
);
}

View File

@@ -1,596 +0,0 @@
import * as Yup from 'yup';
import { useSnackbar } from 'notistack';
import { useNavigate } from 'react-router-dom';
import { yupResolver } from '@hookform/resolvers/yup';
import { Controller, useForm } from 'react-hook-form';
import React, { useEffect, useMemo, useState } from 'react';
import axios from '../../utils/axios';
import { FormProvider, RHFTextField } from '../../components/hook-form';
import {
Autocomplete,
Button,
Grid,
Stack,
Table,
TableBody,
TableCell,
TableRow,
TextField,
Typography,
useTheme,
List,
ListItem,
IconButton,
ListItemAvatar,
Avatar,
ListItemText,
} from '@mui/material';
import Iconify from '../../components/Iconify';
import { LoadingButton } from '@mui/lab';
import { fCurrency } from '../../utils/formatNumber';
import MemberSelectDialog from '../../components/dialogs/MemberSelectDialog';
import { Add, DeleteOutline } from '@mui/icons-material';
type Props = {
isEdit: boolean;
currentClaim?: any;
};
export default function ClaimForm({ isEdit, currentClaim }: Props) {
const navigate = useNavigate();
const { enqueueSnackbar } = useSnackbar();
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'),
// file: Yup.boolean().required('Corporate Status is required'),
});
const defaultValues = useMemo(
() => ({
member: currentClaim?.member || {},
member_id: currentClaim?.member_id || null,
diagnosis_id: currentClaim?.diagnosis_id || null,
total_claim: currentClaim?.total_claim || 0,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentClaim]
);
const methods = useForm<any>({
resolver: yupResolver(NewCorporateSchema),
defaultValues,
});
const {
reset,
watch,
control,
setValue,
getValues,
setError,
handleSubmit,
formState: { isSubmitting },
} = methods;
const values = watch();
const [isCheckingLimit, setIsCheckingLimit] = useState(false);
const [isEligible, setIsEligible] = useState(false);
const [memberBenefits, setMemberBenefits] = useState([]);
const [diagnosisOption, setDiagnosisOption] = useState([]);
const [isMemberDialogOpen, setIsMemberDialogOpen] = useState(false);
const [member, setMember] = useState({})
useEffect(() => {
console.log('defaultValues', defaultValues);
if (isEdit && currentClaim) {
reset(defaultValues);
setMember(defaultValues.member)
}
if (!isEdit) {
reset(defaultValues);
setMember(defaultValues.member)
}
}, [isEdit, currentClaim]);
const fileSelected = (event, type) => {
const files = event.target.files;
const currentFiles = getValues(`uploaded_files.${type}`) ?? [];
setValue(`uploaded_files.${type}`, [...currentFiles, ...files]);
console.log('currentFiles', getValues('uploaded_files'));
};
const memberSelected = (member) => {
setMember(member)
};
const checkLimit = async () => {
console.log('CHECKING LIMIT');
};
const onSubmit = async (data: any) => {
try {
if (!isEdit) {
const response = await axios.post('/claims', data);
} else {
const response = await axios.put('/claims/' + currentClaim?.id ?? '', data);
}
reset();
enqueueSnackbar(
!isEdit ? 'Organizations Created Successfully!' : 'Organizations Udpated Successfully!',
{ variant: 'success' }
);
navigate('/claims');
} catch (error: any) {
if (error && error.response.status === 422) {
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 ascent = document?.querySelector('ascent');
if (ascent != null) {
ascent.innerHTML = '';
}
};
function generate(files, element: React.ReactElement) {
return files.map((value) =>
React.cloneElement(element, {
key: value,
})
);
}
const headStyle = {};
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={3}>
<Typography variant="h6">Member</Typography>
<Stack spacing={2} direction="row">
<Grid item xs={12}>
<RHFTextField
name="member_id"
label="Member"
variant="outlined"
fullWidth
value={member?.name || ''}
InputProps={{
readOnly: true,
}}
onClick={() => {
if (!isEdit) setIsMemberDialogOpen(true);
if (isEdit) enqueueSnackbar('Cannot Change Member', { variant: 'error' });
}}
/>
</Grid>
{/* <Grid item xs={2}>
<Button variant="outlined" fullWidth sx={{ p: 1.8 }} onClick={() => {
setIsMemberDialogOpen(true)
}}>
{member ? 'Change' : 'Search'}
</Button>
</Grid> */}
</Stack>
{member?.id && (
<Stack>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Table border="light-700">
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left">
Name
</TableCell>
<TableCell align="left">{member?.full_name}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
DOB
</TableCell>
<TableCell align="left">
{member?.birth_date} ({member?.age + ' years'})
</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Marital Status
</TableCell>
<TableCell align="left">{member?.marital_status}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Record Type
</TableCell>
<TableCell align="left">{member?.record_type}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Principal ID
</TableCell>
<TableCell align="left">
{member?.principal_id} (
{member?.relation_with_principal})
</TableCell>
</TableRow>
</TableBody>
</Table>
</Grid>
<Grid item xs={12} md={6}>
<Table border="light-700">
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left">
Plan
</TableCell>
<TableCell align="left">{member?.current_plan?.code}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Active
</TableCell>
<TableCell align="left">
{member?.current_plan?.start} -{' '}
{member?.current_plan?.end} (Active)
</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Corporate Limit
</TableCell>
<TableCell align="left">
{fCurrency(0)} / {fCurrency(member?.current_plan?.limit_rules)}
</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Plan Usage
</TableCell>
<TableCell align="left">
{fCurrency(0)} / {fCurrency(member?.current_plan?.limit_rules)}
</TableCell>
</TableRow>
</TableBody>
</Table>
</Grid>
</Grid>
</Stack>
)}
<Controller
name="benefit"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
options={memberBenefits}
getOptionLabel={(option) =>
option ? `#${option.id} (${option.code}) ${option.description}` : ''
}
value={value || ''}
onChange={(event: any, newValue: any) => {
setValue('benefit_id', newValue?.id);
onChange(newValue);
}}
renderInput={(params) => (
<TextField
name="benefit"
{...params}
label="Benefit"
variant="outlined"
fullWidth
// onKeyPress={(event) => {
// if (event.key === 'Enter')
// searchDiagnosis(event.target.value)
// }}
/>
)}
/>
)}
/>
<Controller
name="diagnosis"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
options={diagnosisOption}
getOptionLabel={(option) => (option ? `(${option.code}) ${option.name}` : '')}
value={value || ''}
onChange={(event: any, newValue: any) => {
setValue('diagnosis_id', newValue?.id);
// setValue('diagnosis', newValue)
onChange(newValue);
}}
renderInput={(params) => (
<TextField
name="diagnosis"
{...params}
label="Diagnosis"
variant="outlined"
fullWidth
onKeyPress={(event) => {
if (event.key === 'Enter') searchDiagnosis(event.target.value);
}}
/>
)}
/>
)}
/>
{isCheckingLimit && (
<Stack
sx={{
backgroundColor: 'gray',
paddingY: 1,
paddingX: 1.5,
mb: 2,
borderRadius: '3-xl',
}}
>
{/* Checking */}
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:info-circle"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Please Wait, Checking Eligibilty
</Typography>
</Typography>
</Stack>
)}
{false && isCheckingLimit == false && isEligible == null && (
<Stack
sx={{
backgroundColor: 'gray',
paddingY: 1,
paddingX: 1.5,
mb: 2,
borderRadius: '3-xl',
}}
>
{/* No Data Selected */}
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:info-circle"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Please Select Diagnosis !
</Typography>
</Typography>
</Stack>
)}
{!isCheckingLimit && isEligible !== null && isEligible && (
<Stack
sx={{
backgroundColor: '#B2E8E8',
paddingY: 1,
paddingX: 1.5,
mb: 2,
borderRadius: '3-xl',
}}
>
{/* Eligible */}
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:lock-alt"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Diagnosis is Eligible
</Typography>
</Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}>
125.000.000 / 125.000.000
</Typography>
</Stack>
)}
{!isCheckingLimit && isEligible !== null && !isEligible && (
<Stack
sx={{
backgroundColor: '#B2E8E8',
paddingY: 1,
paddingX: 1.5,
mb: 2,
borderRadius: '3-xl',
}}
>
{/* Not Eligible */}
{/* <Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:lock-alt"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Not Eligible
</Typography>
</Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}>
125.000.000 / 125.000.000
</Typography> */}
</Stack>
)}
<RHFTextField type="number" name="total_claim" label="Total Claim" />
{isEdit && (
<React.Fragment>
<Typography variant="h6">Documents</Typography>
<List>
{(getValues('uploaded_files.invoice') && getValues('uploaded_files.invoice').length
? getValues('uploaded_files.invoice')
: []
).map((file, index) => (
<ListItem
secondaryAction={
<IconButton edge="end" aria-label="delete">
<DeleteOutline />
</IconButton>
}
>
<ListItemAvatar>
<Avatar>
{/* <FileIcon /> */}
I
</Avatar>
</ListItemAvatar>
<ListItemText primary={file.name} secondary={file.type} />
</ListItem>
))}
</List>
<Button
variant="outlined"
startIcon={<Add />}
component="label"
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
>
Invoice
<input
name="invoice"
hidden
accept="image/*,application/pdf"
multiple
type="file"
onChange={(event) => {
fileSelected(event, 'invoice');
}}
/>
</Button>
<List>
{(getValues('uploaded_files.prescription') && getValues('uploaded_files.prescription').length
? getValues('uploaded_files.prescription')
: []
).map((file, index) => (
<ListItem
secondaryAction={
<IconButton edge="end" aria-label="delete">
<DeleteOutline />
</IconButton>
}
>
<ListItemAvatar>
<Avatar>
{/* <FileIcon /> */}
P
</Avatar>
</ListItemAvatar>
<ListItemText primary={file.name} secondary={file.type} />
</ListItem>
))}
</List>
<Button
variant="outlined"
startIcon={<Add />}
component="label"
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
>
Prescription
<input
name="prescription"
hidden
accept="image/*,application/pdf"
multiple
type="file"
onChange={(event) => {
fileSelected(event, 'prescription');
}}
/>
</Button>
<List>
{(getValues('uploaded_files.diagnosis') && getValues('uploaded_files.diagnosis').length
? getValues('uploaded_files.diagnosis')
: []
).map((file, index) => (
<ListItem
secondaryAction={
<IconButton edge="end" aria-label="delete">
<DeleteOutline />
</IconButton>
}
>
<ListItemAvatar>
<Avatar>
{/* <FileIcon /> */}
DR
</Avatar>
</ListItemAvatar>
<ListItemText primary={file.name} secondary={file.type} />
</ListItem>
))}
</List>
<Button
variant="outlined"
startIcon={<Add />}
component="label"
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
>
Doctor Result
<input
name="invoice"
hidden
accept="image/*,application/pdf"
multiple
type="file"
onChange={(event) => {
fileSelected(event, 'diagnosis');
}}
/>
</Button>
</React.Fragment>
)}
{isEligible === true ? (
<LoadingButton
onClick={handleSubmit(onSubmit)}
variant="contained"
color="success"
style={{ color: '#ffffff' }}
size="large"
fullWidth={true}
loading={isCheckingLimit}
>
Create Claim
</LoadingButton>
) : (
<LoadingButton
onClick={checkLimit}
variant="outlined"
size="large"
fullWidth={true}
loading={isCheckingLimit}
>
Check Limit
</LoadingButton>
)}
</Stack>
<MemberSelectDialog
openDialog={isMemberDialogOpen}
setOpenDialog={setIsMemberDialogOpen}
onSelect={memberSelected}
></MemberSelectDialog>
</FormProvider>
);
}

View File

@@ -17,12 +17,17 @@ import {
ButtonGroup,
Link,
Chip,
TableHead,
Grid,
} from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add';
import UploadIcon from '@mui/icons-material/Upload';
import CancelIcon from '@mui/icons-material/Cancel';
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
// hooks
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';
@@ -38,12 +43,19 @@ import { enqueueSnackbar } from 'notistack';
import { Divider } from '@mui/material';
import Iconify from '@/components/Iconify';
import DialogDetailClaim from '@/components/dialogs/DialogDetailClaim';
import { fDateTimesecond } from '@/utils/formatTime';
import { capitalizeFirstLetter } from '@/utils/formatString';
import Label from '@/components/Label';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import { Import } from '@/@types/claims';
// import LoadingButton from '@/theme/overrides/LoadingButton';
export default function List() {
const { themeColorPresets } = useSettings();
const [searchParams, setSearchParams] = useSearchParams();
const [importResult, setImportResult] = useState(null);
const [importResult, setImportResult] = useState<Import>(null);
const navigate = useNavigate()
function SearchInput(props: any) {
// SEARCH
@@ -75,6 +87,7 @@ export default function List() {
fullWidth
onChange={handleSearchChange}
value={searchText}
placeholder='Search Code or Name...'
/>
</form>
);
@@ -87,26 +100,174 @@ export default function List() {
const createMenu = Boolean(anchorEl);
const importForm = useRef<HTMLInputElement>(null);
const [currentImportFileName, setCurrentImportFileName] = useState(null);
const [importLoading, setImportLoading] = useState(false);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleImportButton = () => {
if (importForm?.current) {
handleClose();
importForm.current ? importForm.current.click() : console.log('No File selected');
} else {
alert('No file selected');
}
};
const handleCancelImportButton = () => {
importForm.current.value = '';
importForm.current.dispatchEvent(new Event('change', { bubbles: true }));
};
const handleImportChange = (event: any) => {
if (event.target.files[0]) {
setCurrentImportFileName(event.target.files[0].name);
} else {
setCurrentImportFileName(null);
}
};
const handleUpload = () => {
if (importForm.current?.files.length) {
const formData = new FormData();
formData.append('file', importForm.current?.files[0]);
setImportLoading(true);
axios
.post(`claim-requests/import`, formData)
.then((response) => {
handleCancelImportButton();
loadDataTableData();
setImportResult(response.data);
// alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows');
setImportLoading(false);
})
.catch((response) => {
enqueueSnackbar(
'Looks like something went wrong. Please check your data and try again. ' +
response.message,
{ variant: 'error' }
);
setImportLoading(false);
});
} else {
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 handleGetData = (type :string) => {
axios.get(`corporates/${corporate_id}/data-plan-benefit`)
.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>
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter} />
{/* <Button
variant="outlined"
startIcon={<AddIcon />}
sx={{ p: 1.8 }}
onClick={() => {
navigate('/claims/create');
}}
>
Create
</Button> */}
</Stack>
<input
type="file"
id="file"
ref={importForm}
style={{ display: 'none' }}
onChange={handleImportChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/>
{!currentImportFileName && (
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter} />
<Button
variant="outlined"
startIcon={<UploadIcon />}
sx={{ p: 1.8 }}
onClick={handleClick}
>
Import
</Button>
<Menu
id="import-button"
anchorEl={anchorEl}
open={createMenu}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
<MenuItem onClick={handleImportButton}>Import</MenuItem>
<MenuItem onClick={() => {handleGetTemplate('claim-request')}}>Download Template</MenuItem>
<MenuItem onClick={() => {handleGetData('data-plan-benefit')}}>Download Claim Request</MenuItem>
</Menu>
<Button
variant="contained"
startIcon={<AddIcon />}
sx={{ p: 1.8 }}
onClick={() => {
navigate('/claim-requests/create');
}}
>
Create
</Button>
</Stack>
)}
{currentImportFileName && (
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<ButtonGroup variant="outlined" aria-label="outlined button group" fullWidth>
<Button onClick={handleImportButton} fullWidth>
{currentImportFileName ?? 'No File Selected'}
</Button>
<Button
onClick={handleCancelImportButton}
size="small"
fullWidth={false}
sx={{ p: 1.8 }}
>
<CancelIcon color="error" />
</Button>
</ButtonGroup>
<LoadingButton
id="upload-button"
variant="outlined"
startIcon={<UploadIcon />}
sx={{ p: 1.8 }}
onClick={handleUpload}
loading={importLoading}
>
Upload
</LoadingButton>
</Stack>
)}
{importResult && (
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
<Box sx={{ color: 'text.secondary' }}>
Last Import Result Report :{' '}
<a href={importResult.result_file?.url ?? '#'}>
{importResult.result_file?.name ?? '-'}
</a>
</Box>
</Stack>
)}
</div>
);
}
@@ -176,45 +337,46 @@ export default function List() {
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
{/* <TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell>
</TableCell> */ }
<TableCell align="left">
<Typography
onClick={() => {
handleShowClaim(row);
}}
color={themeColorPresets}
// onClick={() => {
// handleShowClaim(row);
// }}
>
{row.code}
</Typography>
</TableCell>
<TableCell align="left">{row.member?.full_name}</TableCell>
<TableCell align="left">{row.member?.full_name}</TableCell>
<TableCell align="left">{row.submission_date}</TableCell>
<TableCell align="left"><Label>{fDateTimesecond(row.submission_date)}</Label></TableCell>
<TableCell align="left">{row.service_name}</TableCell>
<TableCell align="left">{row.payment_type_name}</TableCell>
<TableCell align="right">
<Chip label={row.status} />
<TableCell align="left">
{ row.status == "requested" ?
(<Label variant='ghost' color='primary'>{capitalizeFirstLetter(row.status)}</Label>) :
(<Label color='success'> {capitalizeFirstLetter(row.status)}</Label>)
}
</TableCell>
<TableCell align="right">
{row.status == 'requested' && (
<LoadingButton
loading={loadingApprove}
variant="outlined"
size="small"
onClick={() => {
handleApprove(row);
}}
>
Ajukan Claim <Iconify icon="eva:arrow-forward-outline"></Iconify>
</LoadingButton>
)}
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate(`/claim-requests/edit/${row.id}`)}>
<EditOutlinedIcon />
Edit
</MenuItem>
<MenuItem onClick={() => navigate ('/claim-requests/detail/'+row.id+'')}>
<FindInPageOutlinedIcon />
Detail
</MenuItem>
</>
} />
</TableCell>
{/* <TableCell>
<IconButton
onClick={() => {
handleShowClaim(row);
@@ -236,17 +398,58 @@ export default function List() {
>
<Box>
<Typography fontWeight={600}>Berkas Hasil Penunjang</Typography>
{row.files_by_type?.result &&
row.files_by_type?.result.map((file, index) => (
{/* {row.files_by_type?.claim_kondisi &&
row.files_by_type?.claim_kondisi.map((file, index) => (
<Stack direction="row" key={index}>
<Typography sx={{ marginRight: 2 }}>-</Typography>{' '}
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))}
))} */}
{!row.files_by_type?.result && <Typography>Tidak ada berkas</Typography>}
{row.files_by_type?.claim_kondisi && (
<>
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Kondisi</Typography>
{row.files_by_type?.claim_kondisi.map((file, index) => (
<Stack direction="row" key={index}>
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))}
</>
)}
{row.files_by_type?.claim_diagnosis && (
<>
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Diagnosa</Typography>
{row.files_by_type?.claim_diagnosis.map((file, index) => (
<Stack direction="row" key={index}>
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))}
</>
)}
{row.files_by_type?.claim_result && (
<>
<Typography fontWeight={600} sx={{ marginRight: 4 }}> - Hasil</Typography>
{row.files_by_type?.claim_result.map((file, index) => (
<Stack direction="row" key={index}>
<a href={file.url} target="_blank">
{file.name}
</a>
</Stack>
))}
</>
)}
{(!row.files_by_type?.claim_result && !row.files_by_type?.claim_diagnosis && !row.files_by_type?.claim_kondisi)&& <Typography>Tidak ada berkas</Typography>}
</Box>
</Stack>
</Box>
@@ -264,9 +467,9 @@ export default function List() {
return (
<Table aria-label="collapsible table">
{/* ------------------ TABLE HEADER ------------------ */}
<TableBody>
<TableHead>
<TableRow>
<TableCell style={headStyle} align="left" />
{/* <TableCell style={headStyle} align="left" /> */}
<TableCell style={headStyle} align="left">
Code
</TableCell>
@@ -274,23 +477,20 @@ export default function List() {
Name
</TableCell>
<TableCell style={headStyle} align="left">
Nomor Polis
</TableCell>
<TableCell style={headStyle} align="left">
Tanggal Pengajuan
Date of Submission
</TableCell>
<TableCell style={headStyle} align="left">
Service Type
</TableCell>
<TableCell style={headStyle} align="left">
Claim Type
Claim Method
</TableCell>
<TableCell style={headStyle} align="left">
Status
</TableCell>
<TableCell style={headStyle} align="right"></TableCell>
</TableRow>
</TableBody>
</TableHead>
{/* ------------------ END TABLE HEADER ------------------ */}
{/* ------------------ TABLE ROW ------------------ */}
@@ -346,23 +546,28 @@ export default function List() {
function handleDownloadLog() {}
return (
<Card>
<ImportForm />
<Grid container>
<Grid item sm={12}>
<ImportForm />
</Grid>
<DataTable
isLoading={dataTableIsLoading}
lastRequest={0}
data={dataTableData}
handlePageChange={handlePageChange}
TableContent={<TableContent />}
/>
<DialogDetailClaim
openDialog={openDialogDetailClaim}
setOpenDialog={setOpenDialogDetailClaim}
title={{ name: 'Claim Request Detail' }}
data={{ claim: currentClaim, isLoading: loadingClaimDetail, handleDownloadLog }}
></DialogDetailClaim>
</Card>
<Grid item sm={12}>
<DataTable
isLoading={dataTableIsLoading}
lastRequest={0}
data={dataTableData}
handlePageChange={handlePageChange}
TableContent={<TableContent />}
/>
</Grid>
<Grid item sm={12}>
<DialogDetailClaim
openDialog={openDialogDetailClaim}
setOpenDialog={setOpenDialogDetailClaim}
title={{ name: 'Claim Request Detail' }}
data={{ claim: currentClaim, isLoading: loadingClaimDetail, handleDownloadLog }}
></DialogDetailClaim>
</Grid>
</Grid>
);
}

View File

@@ -0,0 +1,79 @@
import axios from '@/utils/axios';
import { enqueueSnackbar } from 'notistack';
import { MemberListType } from './Types';
import { makeFormData } from '@/utils/jsonToFormData';
/**
* Listing Member
*/
export const getMemberList = async ( page: number, keyword: string ): Promise<MemberListType[]> => {
const response = await axios.get(`/claim-requests/list-member?page=${page}&keyword=${keyword}`)
.then((res) =>{
return res.data.data.member_list;
})
.catch((res) => {
enqueueSnackbar("server error !", {
variant: 'error',
});
return [];
});
return response;
};
/**
* Add Claim Request
*/
export const addClaimRequest = async ( data: MemberListType[] ): Promise<boolean> => {
// Mapping
const formData = new FormData();
data.map((row, index) => {
formData.append(`member_id[${index}]`, row.id.toString());
formData.append(`service_code[${index}]`, row.patien_type??'');
if (row.file_kondisi != undefined) {
row.file_kondisi.forEach((file, file_index) => {
console.log(file);
formData.append(`file_kondisi[member_${row.id}][${file_index}]`, file);
});
}
if (row.file_diagnosa != undefined) {
row.file_diagnosa.forEach((file, file_index) => {
console.log(file);
formData.append(`file_diagnosa[member_${row.id}][${file_index}]`, file);
});
}
if (row.file_penunjang != undefined) {
row.file_penunjang.forEach((file, file_index) => {
console.log(file);
formData.append(`file_penunjang[member_${row.id}][${file_index}]`, file);
});
}
})
// Axios
const response = await axios.post(`/claim-requests`, formData)
.then((res) =>{
enqueueSnackbar("Berhasil membuat data !", {
variant: 'success',
});
return true;
})
.catch((res) => {
enqueueSnackbar("server error !", {
variant: 'error',
});
return false;
});
return response;
};

View File

@@ -0,0 +1,25 @@
/**
* Search Type
*/
export type SearchType = {
keyword: string,
}
/**
* Member List
*/
export type MemberListType = {
id : string,
member_id : string,
name : string,
service_type : ServiceType[],
patien_type? : string,
file_kondisi? : any[],
file_diagnosa? : any[],
file_penunjang? : any[],
}
export type ServiceType = {
code : string
name : string
}

View File

@@ -28,31 +28,26 @@ export default function ClaimsCreateUpdate() {
useEffect(() => {
if (isEdit) {
axios.get('/claims/' + id).then((res) => {
// console.log('Yeet', res.data);
setCurrentClaim(res.data);
});
axios.get(`/claims/${id}/edit`).then((res) => {
console.log('Yeet', res.data.data);
setCurrentClaim(res.data.data);
});;
}
}, [id]);
return (
<Page title={isEdit ? `Edit Claim : ${currentClaim?.id}` : "Create New Claim"}>
<Page title={'Edit Claim Management'}>
<Container maxWidth={themeStretch ? false : 'xl'}>
<Stack direction="row" alignItems="center">
<HeaderBreadcrumbs
heading={
!isEdit
? 'Create New Claim'
: `Edit Claim : ${currentClaim?.code}`
}
heading={`Edit Claim Management`}
links={[
{ name: 'Dashboard', href: '/dashboard' },
{
name: 'Claim',
href: '/claims',
name: 'Claim Management',
},
{ name: !isEdit ? 'Create' : currentClaim?.id ?? '' },
]}
/>
</Stack>

File diff suppressed because it is too large Load Diff

View File

@@ -24,16 +24,18 @@ import {
ListItemAvatar,
Avatar,
ListItemText,
Card,
} from '@mui/material';
import Iconify from '../../components/Iconify';
import { LoadingButton } from '@mui/lab';
import { fCurrency } from '../../utils/formatNumber';
import MemberSelectDialog from '../../components/dialogs/MemberSelectDialog';
import { Add, DeleteOutline } from '@mui/icons-material';
import { ClaimsEdit } from '@/@types/claims';
type Props = {
isEdit: boolean;
currentClaim?: any;
currentClaim?: ClaimsEdit;
};
export default function ClaimForm({ isEdit, currentClaim }: Props) {
@@ -42,18 +44,27 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
const { enqueueSnackbar } = useSnackbar();
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'),
benefit_desc: Yup.string().required('Benefit Desc is required'),
amount_incurred: Yup.string().required('Amount Incurred is required'),
amount_approved: Yup.number().required('Amount Approved is required'),
amount_not_approved: Yup.number().required('Amount Not Approved is required'),
excess_paid: Yup.number().required('Excess Paid is required'),
// file: Yup.boolean().required('Corporate Status is required'),
});
const defaultValues = useMemo(
() => ({
member: currentClaim?.member || {},
plan_id: currentClaim?.plan_id || null,
payor_id: currentClaim?.payor_id || null,
corporate_id: currentClaim?.corporate_id || null,
policy_number: currentClaim?.policy_number || null,
member_id: currentClaim?.member_id || null,
diagnosis_id: currentClaim?.diagnosis_id || null,
total_claim: currentClaim?.total_claim || 0,
benefit_code: currentClaim?.benefit_code || '-',
benefit_desc: currentClaim?.benefit_desc || '-',
amount_incurred: currentClaim?.amount_incurred || 0,
amount_approved: currentClaim?.amount_approved || 0,
amount_not_approved: currentClaim?.amount_not_approved || 0,
excess_paid: currentClaim?.excess_paid || 0,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[currentClaim]
@@ -88,11 +99,11 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
console.log('defaultValues', defaultValues);
if (isEdit && currentClaim) {
reset(defaultValues);
setMember(defaultValues.member)
// setMember(defaultValues.member)
}
if (!isEdit) {
reset(defaultValues);
setMember(defaultValues.member)
// setMember(defaultValues.member)
}
}, [isEdit, currentClaim]);
@@ -105,13 +116,13 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
console.log('currentFiles', getValues('uploaded_files'));
};
const memberSelected = (member) => {
setMember(member)
};
// const memberSelected = (member) => {
// setMember(member)
// };
const checkLimit = async () => {
console.log('CHECKING LIMIT');
};
// const checkLimit = async () => {
// console.log('CHECKING LIMIT');
// };
const onSubmit = async (data: any) => {
try {
@@ -122,7 +133,7 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
}
reset();
enqueueSnackbar(
!isEdit ? 'Organizations Created Successfully!' : 'Organizations Udpated Successfully!',
!isEdit ? 'Organizations Created Successfully!' : 'Claim Udpated Successfully!',
{ variant: 'success' }
);
navigate('/claims');
@@ -154,443 +165,86 @@ export default function ClaimForm({ isEdit, currentClaim }: Props) {
const headStyle = {};
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={3}>
<Typography variant="h6">Member</Typography>
<Stack spacing={2} direction="row">
<Grid item xs={12}>
<RHFTextField
name="member_id"
label="Member"
variant="outlined"
fullWidth
value={member?.name || ''}
InputProps={{
readOnly: true,
}}
onClick={() => {
if (!isEdit) setIsMemberDialogOpen(true);
if (isEdit) enqueueSnackbar('Cannot Change Member', { variant: 'error' });
}}
/>
</Grid>
{/* <Grid item xs={2}>
<Button variant="outlined" fullWidth sx={{ p: 1.8 }} onClick={() => {
setIsMemberDialogOpen(true)
}}>
{member ? 'Change' : 'Search'}
</Button>
</Grid> */}
</Stack>
{member?.id && (
<Stack>
<Grid container spacing={2}>
<Grid item xs={12} md={6}>
<Table border="light-700">
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left">
Name
</TableCell>
<TableCell align="left">{member?.full_name}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
DOB
</TableCell>
<TableCell align="left">
{member?.birth_date} ({member?.age + ' years'})
</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Marital Status
</TableCell>
<TableCell align="left">{member?.marital_status}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Record Type
</TableCell>
<TableCell align="left">{member?.record_type}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Principal ID
</TableCell>
<TableCell align="left">
{member?.principal_id} (
{member?.relation_with_principal})
</TableCell>
</TableRow>
</TableBody>
</Table>
</Grid>
<Grid item xs={12} md={6}>
<Table border="light-700">
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left">
Plan
</TableCell>
<TableCell align="left">{member?.current_plan?.code}</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Active
</TableCell>
<TableCell align="left">
{member?.current_plan?.start} -{' '}
{member?.current_plan?.end} (Active)
</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Corporate Limit
</TableCell>
<TableCell align="left">
{fCurrency(0)} / {fCurrency(member?.current_plan?.limit_rules)}
</TableCell>
</TableRow>
<TableRow>
<TableCell style={headStyle} align="left">
Plan Usage
</TableCell>
<TableCell align="left">
{fCurrency(0)} / {fCurrency(member?.current_plan?.limit_rules)}
</TableCell>
</TableRow>
</TableBody>
</Table>
</Grid>
<Card sx={{paddingX:2, paddingY:3}}>
<Grid container spacing={2}>
{/* Baris ke 1 */}
<Grid item xs={3}>
<Typography marginBottom={2} variant="subtitle1">Plan ID*</Typography>
<RHFTextField name="plan_id" disabled />
</Grid>
<Grid item xs={3}>
<Typography marginBottom={2} variant="subtitle1">Payor ID*</Typography>
<RHFTextField name="payor_id" disabled />
</Grid>
<Grid item xs={3}>
<Typography marginBottom={2} variant="subtitle1">Corporate ID*</Typography>
<RHFTextField name="corporate_id"disabled />
</Grid>
<Grid item xs={3}>
<Typography marginBottom={2} variant="subtitle1">Policy Number*</Typography>
<RHFTextField name="policy_number" disabled />
</Grid>
</Stack>
)}
<Controller
name="benefit"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
options={memberBenefits}
getOptionLabel={(option) =>
option ? `#${option.id} (${option.code}) ${option.description}` : ''
}
value={value || ''}
onChange={(event: any, newValue: any) => {
setValue('benefit_id', newValue?.id);
onChange(newValue);
}}
renderInput={(params) => (
<TextField
name="benefit"
{...params}
label="Benefit"
variant="outlined"
fullWidth
// onKeyPress={(event) => {
// if (event.key === 'Enter')
// searchDiagnosis(event.target.value)
// }}
/>
)}
/>
)}
/>
{/* Baris ke 2 */}
<Grid item xs={3}>
<Typography marginBottom={2} variant="subtitle1">Memeber ID*</Typography>
<RHFTextField name="member_id" disabled />
</Grid>
<Grid item xs={3}>
<Typography marginBottom={2} variant="subtitle1">Benefit Code*</Typography>
<RHFTextField name="benefit_code" disabled />
</Grid>
<Grid item xs={6}>
<Typography marginBottom={2} variant="subtitle1">Benefit Desc*</Typography>
<RHFTextField name="benefit_desc" label="Benefit Desc" />
</Grid>
<Controller
name="diagnosis"
control={control}
render={({ field: { onChange, value } }) => (
<Autocomplete
options={diagnosisOption}
getOptionLabel={(option) => (option ? `(${option.code}) ${option.name}` : '')}
value={value || ''}
onChange={(event: any, newValue: any) => {
setValue('diagnosis_id', newValue?.id);
// setValue('diagnosis', newValue)
onChange(newValue);
}}
renderInput={(params) => (
<TextField
name="diagnosis"
{...params}
label="Diagnosis"
variant="outlined"
fullWidth
onKeyPress={(event) => {
if (event.key === 'Enter') searchDiagnosis(event.target.value);
}}
/>
)}
/>
)}
/>
{isCheckingLimit && (
<Stack
sx={{
backgroundColor: 'gray',
paddingY: 1,
paddingX: 1.5,
mb: 2,
borderRadius: '3-xl',
}}
>
{/* Checking */}
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:info-circle"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Please Wait, Checking Eligibilty
</Typography>
</Typography>
</Stack>
)}
{false && isCheckingLimit == false && isEligible == null && (
<Stack
sx={{
backgroundColor: 'gray',
paddingY: 1,
paddingX: 1.5,
mb: 2,
borderRadius: '3-xl',
}}
>
{/* No Data Selected */}
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:info-circle"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Please Select Diagnosis !
</Typography>
</Typography>
</Stack>
)}
{!isCheckingLimit && isEligible !== null && isEligible && (
<Stack
sx={{
backgroundColor: '#B2E8E8',
paddingY: 1,
paddingX: 1.5,
mb: 2,
borderRadius: '3-xl',
}}
>
{/* Eligible */}
<Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:lock-alt"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Diagnosis is Eligible
</Typography>
</Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}>
125.000.000 / 125.000.000
</Typography>
</Stack>
)}
{!isCheckingLimit && isEligible !== null && !isEligible && (
<Stack
sx={{
backgroundColor: '#B2E8E8',
paddingY: 1,
paddingX: 1.5,
mb: 2,
borderRadius: '3-xl',
}}
>
{/* Not Eligible */}
{/* <Typography sx={{ typography: 'caption', display: 'flex', alignItems: 'center' }}>
<Iconify
icon="bxs:lock-alt"
width={12}
height={13}
sx={{ color: '#424242', marginRight: '6px' }}
/>
<Typography variant="caption" component="span">
Not Eligible
</Typography>
</Typography>
<Typography sx={{ typography: 'caption', color: '#637381' }}>
125.000.000 / 125.000.000
</Typography> */}
</Stack>
)}
<RHFTextField type="number" name="total_claim" label="Total Claim" />
{isEdit && (
<React.Fragment>
<Typography variant="h6">Documents</Typography>
<List>
{(getValues('uploaded_files.invoice') && getValues('uploaded_files.invoice').length
? getValues('uploaded_files.invoice')
: []
).map((file, index) => (
<ListItem
secondaryAction={
<IconButton edge="end" aria-label="delete">
<DeleteOutline />
</IconButton>
}
>
<ListItemAvatar>
<Avatar>
{/* <FileIcon /> */}
I
</Avatar>
</ListItemAvatar>
<ListItemText primary={file.name} secondary={file.type} />
</ListItem>
))}
</List>
<Button
variant="outlined"
startIcon={<Add />}
component="label"
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
>
Invoice
<input
name="invoice"
hidden
accept="image/*,application/pdf"
multiple
type="file"
onChange={(event) => {
fileSelected(event, 'invoice');
{/* Baris ke 3 */}
<Grid item xs={3}>
<Typography marginBottom={2} variant="subtitle1">Amount Incurred*</Typography>
<RHFTextField name="amount_incurred" label="Amount Incurred" type="number" />
</Grid>
<Grid item xs={3}>
<Typography marginBottom={2} variant="subtitle1">Amount Approved*</Typography>
<RHFTextField name="amount_approved" label="Amount Approved" type="number" />
</Grid>
<Grid item xs={3}>
<Typography marginBottom={2} variant="subtitle1">Amount Not Approved*</Typography>
<RHFTextField name="amount_not_approved" label="Amount Not Approved" type="number" />
</Grid>
<Grid item xs={3}>
<Typography marginBottom={2} variant="subtitle1">Excess Paid*</Typography>
<RHFTextField name="excess_paid" label="Excess Paid*" type="number" />
</Grid>
</Grid>
</Card>
<Grid container marginTop={2}>
<Grid item xs={12} md={12} >
<Stack direction="row" alignItems="center" justifyContent="flex-end">
<Button
sx={{
margin: 1
}}
/>
</Button>
<List>
{(getValues('uploaded_files.prescription') && getValues('uploaded_files.prescription').length
? getValues('uploaded_files.prescription')
: []
).map((file, index) => (
<ListItem
secondaryAction={
<IconButton edge="end" aria-label="delete">
<DeleteOutline />
</IconButton>
}
>
<ListItemAvatar>
<Avatar>
{/* <FileIcon /> */}
P
</Avatar>
</ListItemAvatar>
<ListItemText primary={file.name} secondary={file.type} />
</ListItem>
))}
</List>
<Button
variant="outlined"
startIcon={<Add />}
component="label"
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
>
Prescription
<input
name="prescription"
hidden
accept="image/*,application/pdf"
multiple
type="file"
onChange={(event) => {
fileSelected(event, 'prescription');
}}
/>
</Button>
<List>
{(getValues('uploaded_files.diagnosis') && getValues('uploaded_files.diagnosis').length
? getValues('uploaded_files.diagnosis')
: []
).map((file, index) => (
<ListItem
secondaryAction={
<IconButton edge="end" aria-label="delete">
<DeleteOutline />
</IconButton>
}
>
<ListItemAvatar>
<Avatar>
{/* <FileIcon /> */}
DR
</Avatar>
</ListItemAvatar>
<ListItemText primary={file.name} secondary={file.type} />
</ListItem>
))}
</List>
<Button
variant="outlined"
startIcon={<Add />}
component="label"
sx={{ paddingY: 2, width: '100%', ':hover': { border: 'none' } }}
>
Doctor Result
<input
name="invoice"
hidden
accept="image/*,application/pdf"
multiple
type="file"
onChange={(event) => {
fileSelected(event, 'diagnosis');
}}
/>
</Button>
</React.Fragment>
)}
{isEligible === true ? (
<LoadingButton
onClick={handleSubmit(onSubmit)}
variant="contained"
color="success"
style={{ color: '#ffffff' }}
size="large"
fullWidth={true}
loading={isCheckingLimit}
>
Create Claim
</LoadingButton>
) : (
<LoadingButton
onClick={checkLimit}
variant="outlined"
size="large"
fullWidth={true}
loading={isCheckingLimit}
>
Check Limit
</LoadingButton>
)}
</Stack>
<MemberSelectDialog
openDialog={isMemberDialogOpen}
setOpenDialog={setIsMemberDialogOpen}
onSelect={memberSelected}
></MemberSelectDialog>
</FormProvider>
type="submit"
variant="contained"
size="large"
color='inherit'
onClick={() => navigate(`/claims`)}
>
Cancel
</Button>
<LoadingButton
type="submit"
variant="contained"
size="large"
// fullWidth={true}
loading={isSubmitting}
>
Save
</LoadingButton>
</Stack>
</Grid>
</Grid>
</FormProvider>
);
}

View File

@@ -16,13 +16,10 @@ import {
Menu,
ButtonGroup,
Tooltip,
TableHead,
} from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add';
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
import AssessmentIcon from '@mui/icons-material/Assessment';
import UploadIcon from '@mui/icons-material/Upload';
import CancelIcon from '@mui/icons-material/Cancel';
// hooks
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom';
@@ -35,6 +32,13 @@ import EditRoundedIcon from '@mui/icons-material/EditRounded';
import { Chip } from '@mui/material';
import Iconify from '@/components/Iconify';
import { enqueueSnackbar } from 'notistack';
import { fDate } from '../../utils/formatTime';
import { Claims } from '@/@types/claims';
import Label from '@/components/Label';
import { capitalizeFirstLetter } from '@/utils/formatString';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import Edit from '@mui/icons-material/Edit';
import { Download } from '@mui/icons-material';
export default function List() {
const [searchParams, setSearchParams] = useSearchParams();
@@ -83,10 +87,16 @@ export default function List() {
fullWidth
onChange={handleSearchChange}
value={searchText}
placeholder='Search Code or Member ID...'
/>
<Tooltip title="Benefit Usage Report">
<Button variant="outlined" startIcon={<AssessmentIcon />} sx={{ p: 1.8 }} onClick={handleGetData}/>
</Tooltip>
<Button
variant="contained"
startIcon={<Download />}
onClick={() => handleGetData('DO')}
sx={{ p: 1.8 }}
>
Export
</Button>
</Stack>
</form>
);
@@ -152,7 +162,7 @@ export default function List() {
};
// Called on every row to map the data to the columns
function createData(data: any): any {
function createData(data: Claims): Claims {
return {
...data,
};
@@ -168,41 +178,44 @@ export default function List() {
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
{/* <TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.member?.full_name}</TableCell>
<TableCell align="left">{row.plan?.code}</TableCell>
<TableCell align="left">{row.claim_request?.service?.name}</TableCell>
<TableCell align="left">
({row.diagnoses[0]?.icd?.code}) {row.diagnoses[0]?.icd?.name}
</TableCell>
<TableCell align="left">{fCurrency(row.total_claim)}</TableCell>
</TableCell> */}
<TableCell align="left">{row.claim_request?.code}</TableCell>
{/* <TableCell align="left">{row.code}</TableCell> */}
<TableCell align="left">{row.member?.current_plan?.code}</TableCell>
<TableCell align="left">{row.member?.current_corporate?.payor_id}</TableCell>
<TableCell align="left">{row.member?.current_corporate?.code}</TableCell>
<TableCell align="left">{row.member?.current_corporate?.current_policy?.code}</TableCell>
<TableCell align="left">{row.member?.member_id}</TableCell>
<TableCell align="left">{row.benefit_desc}</TableCell>
<TableCell align="center">
{row.status == 'draft' && (<Chip label='Draft' color="default" variant="outlined" />)}
{row.status == 'requested' && (<Chip label='Requested' color="primary" />)}
{row.status == 'received' && (<Chip label='Received' color="success" variant='outlined' />)}
{row.status == 'approved' && (<Chip label='Approved' color="success" />)}
{row.status == 'postpone' && (<Chip label='Postpone' color="primary" variant="outlined" />)}
{row.status == 'paid' && (<Chip label='Paid' color="warning" />)}
{row.status == 'declined' && (<Chip label='Declined' color="error" />)}
{row.status == 'draft' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
{row.status == 'requested' && (<Label color='primary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
{row.status == 'received' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
{row.status == 'approved' && (<Label color='success' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
{row.status == 'postpone' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
{row.status == 'paid' && (<Label color='secondary' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
{row.status == 'declined' && (<Label color='error' variant='ghost'>{capitalizeFirstLetter(row.status)}</Label>)}
</TableCell>
<TableCell align="right">
{['approved', 'paid'].includes(row.status) && (
<Iconify icon="eva:eye-fill" onClick={(e) => {
navigate('/claims/' + row.id);
}}></Iconify>
)}
{!['approved', 'paid'].includes(row.status) && (
<Iconify icon="eva:edit-outline" onClick={(e) => {
navigate('/claims/' + row.id);
}}></Iconify>
)}
</TableCell>
<TableMoreMenu actions={
<>
<MenuItem onClick={() =>navigate(`/claims/edit/${row.id}`) }>
<Edit />
Edit
</MenuItem>
<MenuItem onClick={() => navigate('/claims/detail/'+row.id+'') }>
<FindInPageOutlinedIcon />
Detail
</MenuItem>
</>
} />
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
@@ -227,35 +240,38 @@ export default function List() {
return (
<Table aria-label="collapsible table">
{/* ------------------ TABLE HEADER ------------------ */}
<TableBody>
<TableHead>
<TableRow>
<TableCell style={headStyle} align="left" />
{/* <TableCell style={headStyle} align="left" /> */}
<TableCell style={headStyle} align="left">
Code
</TableCell>
<TableCell style={headStyle} align="left">
Member Name
Plan ID
</TableCell>
<TableCell style={headStyle} align="left">
Plan
Payor ID
</TableCell>
<TableCell style={headStyle} align="left">
Benefit
Corporate ID
</TableCell>
<TableCell style={headStyle} align="left">
Diagnosis
Policy Number
</TableCell>
<TableCell style={headStyle} align="left">
Total Claim
Member ID
</TableCell>
<TableCell style={headStyle} align="left">
Benefit Desc
</TableCell>
<TableCell style={headStyle} align="left">
Status
</TableCell>
<TableCell style={headStyle} align="right">
Action
<TableCell style={headStyle} align="left">
</TableCell>
</TableRow>
</TableBody>
</TableHead>
{/* ------------------ END TABLE HEADER ------------------ */}
{/* ------------------ TABLE ROW ------------------ */}

View File

@@ -0,0 +1,58 @@
import axios from "@/utils/axios";
import { enqueueSnackbar } from "notistack";
import { BenefitConfigurationListType } from "./Types";
/**
* Get Benefit Configuration List
*/
export const getBenefitConfigurationList = async ( claim_id: string ): Promise<BenefitConfigurationListType[]> => {
const response = await axios.get(`/claims/${claim_id}/benefit-configuration`)
.then((res) =>{
return res.data.data.benefit_list;
})
.catch((res) => {
enqueueSnackbar("server error !", {
variant: 'error',
});
return [];
});
return response;
};
/**
* Edit Benefit Configuration
*/
export const editBenefitConfiguration = async ( data: BenefitConfigurationListType ): Promise<boolean> => {
const response = await axios.put(`/claims/benefit-configuration/edit/${data.claim_service_benefits_id}`, {
...data
})
.then((res) =>{
enqueueSnackbar(res.data.message, {
variant: 'success',
});
return true;
})
.catch((res) => {
if (res.response.status == 400) {
let arr_message = res.response.data.message;
for (const key in arr_message) {
enqueueSnackbar(arr_message[key][0], {
variant: 'warning',
});
}
}
else {
enqueueSnackbar("server error !", {
variant: 'error',
});
}
return false;
});
return response;
};

View File

@@ -0,0 +1,11 @@
/**
* Benefit Configuration List Type
*/
export type BenefitConfigurationListType = {
claim_service_benefits_id: number,
benefit_name: string,
amount_incurred: number,
amount_approved: number,
amount_not_approved: number,
excess_paid: number
}

View File

@@ -38,6 +38,7 @@ import ClaimDetail from './components/ClaimDetail';
import DialogHistoryPerawatan from './components/DialogHistoryPerawatan';
import { IconButton } from '@mui/material';
import { Tooltip } from '@mui/material';
import { useSelector } from 'react-redux';
export default function ClaimsCreateUpdate() {
const { themeStretch } = useSettings();
@@ -48,6 +49,10 @@ export default function ClaimsCreateUpdate() {
const [currentClaim, setCurrentClaim] = useState();
const [documents, setDocuments] = useState([]);
const claimsHistoryData = useSelector((state) => state.claimsHistory)
console.log(claimsHistoryData)
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
@@ -66,6 +71,12 @@ export default function ClaimsCreateUpdate() {
setClaimItems([...claimItems, ...items]);
};
useEffect(() => {
if(claimsHistoryData) {
setClaimItems(claimsHistoryData)
}
},[claimsHistoryData])
const handleSaveClaimItems = () => {
console.log('Storing ', claimItems);
setLoadingClaimItems(true);
@@ -305,6 +316,9 @@ export default function ClaimsCreateUpdate() {
</LoadingButton>
)}
</Stack>
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography>Admission Date : {currentClaim?.claim_request?.submission_date}</Typography>
</Stack>
</Paper>
<Box sx={{ flexGrow: 1 }}>
@@ -347,7 +361,7 @@ export default function ClaimsCreateUpdate() {
<Typography variant="body2" fontWeight={600}>
Nomor Polis
</Typography>
<Typography variant="body2">{currentClaim?.member?.full_name}</Typography>
<Typography variant="body2">{currentClaim?.member?.current_policy?.code}</Typography>
</Grid>
<Grid item xs={12} md={6}>

View File

@@ -0,0 +1,218 @@
import * as Yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { useEffect, useState } from 'react';
import { Box, Grid } from '@mui/material';
import { LoadingButton } from '@mui/lab';
import Button from '@mui/material/Button';
import { styled } from '@mui/material/styles';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
import IconButton from '@mui/material/IconButton';
import CloseIcon from '@mui/icons-material/Close';
import Typography from '@mui/material/Typography';
import { useForm } from 'react-hook-form';
import { FormProvider } from '@/components/hook-form';
import RHFTextFieldMoney from '@/components/hook-form/v2/RHFTextFieldMoney';
/**
* Custom Style
* =====================================================
*/
const BootstrapDialog = styled(Dialog)(({ theme }) => ({
}));
/**
* Utils, Types, Functions
* ============================================
*/
import palette from '@/theme/palette';
import { BenefitConfigurationListType } from '../Model/Types';
import { editBenefitConfiguration } from '../Model/Functions';
/**
* Props
* =====================================================
*/
type Props = {
data?: BenefitConfigurationListType,
isOpen: boolean,
handleCancleProp: () => void,
handleSuccessProp: () => void,
};
export default function BenefitConfigurationDialog({ ...props }: Props) {
// setup form
// ====================================
const defaultValues: BenefitConfigurationListType = {
claim_service_benefits_id: 0,
benefit_name: '',
amount_incurred: 0,
amount_approved: 0,
amount_not_approved: 0,
excess_paid: 0,
};
const validationSchema = Yup.object().shape({
amount_incurred : Yup.string().typeError('').required(''),
amount_approved : Yup.string().typeError('').required(''),
amount_not_approved : Yup.string().typeError('').required(''),
excess_paid : Yup.string().typeError('').required(''),
});
const methods = useForm<any>({
resolver: yupResolver(validationSchema),
defaultValues
});
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods;
console.log(errors);
console.log(watch());
// Submit Form
// =====================================
const submitHandler = async (data: BenefitConfigurationListType) => {
let response = await editBenefitConfiguration(data);
if (response == true) {
props.handleSuccessProp()
props.handleCancleProp()
}
}
// Set Value Form
// =====================================
useEffect(() => {
setValue('claim_service_benefits_id', props.data?.claim_service_benefits_id)
setValue('amount_incurred', props.data?.amount_incurred)
setValue('amount_approved', props.data?.amount_approved)
setValue('amount_not_approved', props.data?.amount_not_approved)
setValue('excess_paid', props.data?.excess_paid)
}, [props.data])
return (
<Dialog
onClose={props.handleCancleProp}
aria-labelledby="customized-dialog-title"
open={props.isOpen}
maxWidth={'md'}
>
<FormProvider methods={methods} onSubmit={handleSubmit(submitHandler)}>
<DialogTitle sx={{ m: 0, p: 2, background: palette.light.primary.main, color: palette.light.grey[0], display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} id="customized-dialog-title">
<Typography variant="body2" sx={{ fontWeight: 'bold' }}>
Client Benefit Configuration
</Typography>
<IconButton
aria-label="close"
onClick={props.handleCancleProp}
sx={{color: (theme) => theme.palette.grey[0]}}
>
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent sx={{ p: '0px' }}>
<Box sx={{ py: '24px', px: '32px'}}>
<Box sx={{ p: '24px', border: '1px solid rgba(0,0,0,0.125)', borderRadius: '12px'}}>
<Grid container spacing={3}>
{/* Benefit Name */}
<Grid item xs={12}>
<Typography variant="body2" sx={{ fontWeight: 'bold'}}>
{props.data?.benefit_name}
</Typography>
</Grid>
<Grid item xs={12}>
<Grid container spacing={3}>
<Grid item xs={3}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="body2" component="div">
Amount Incurred*
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<RHFTextFieldMoney
id="amount_incurred"
name='amount_incurred'
placeholder='Amount Incurred'
/>
</Grid>
</Grid>
</Grid>
<Grid item xs={3}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="body2" component="div">
Amount Approved*
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<RHFTextFieldMoney
id="amount_approved"
name='amount_approved'
placeholder='Amount Approved'
/>
</Grid>
</Grid>
</Grid>
<Grid item xs={3}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="body2" component="div">
Amount Not Approved*
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<RHFTextFieldMoney
id="amount_not_approved"
name='amount_not_approved'
placeholder='Amount Not Approved'
/>
</Grid>
</Grid>
</Grid>
<Grid item xs={3}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="body2" component="div">
Excess Paid*
</Typography>
</Grid>
<Grid item xs={12} sx={{display: 'flex', gap: 1}}>
<RHFTextFieldMoney
id="excess_paid"
name='excess_paid'
placeholder='Excess Paid'
/>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</Box>
</Box>
</DialogContent>
<DialogActions>
<Button variant='outlined' onClick={props.handleCancleProp} aria-label="close">
Cancle
</Button>
<LoadingButton disabled={!isDirty} type="submit" variant="contained" loading={isSubmitting}>
Save Changes
</LoadingButton>
</DialogActions>
</FormProvider>
</Dialog>
);
}

View File

@@ -0,0 +1,168 @@
/**
* Core
* ============================================
*/
import { useEffect, useState } from 'react';
import { useParams } from 'react-router';
import { Box, Typography, Grid, MenuItem } from '@mui/material';
/**
* Components
* ============================================
*/
// - Global -
import MoreMenu from '@/components/MoreMenu';
/**
* Icon
* ============================================
*/
import { EditOutlined } from '@mui/icons-material';
/**
* Utils, Types, Functions
* ============================================
*/
import { BenefitConfigurationListType } from '../Model/Types';
import { getBenefitConfigurationList } from '../Model/Functions';
import palette from '@/theme/palette';
import BenefitConfigurationDialog from './BenefitConfigurationDialog';
import { fNumber } from '@/utils/formatNumber';
export default function BenefitConfigurationList() {
const { id: claim_id } = useParams();
// State
// --------------------
const [BenefitConfigurationList, setBenefitConfigurationList] = useState<BenefitConfigurationListType[]>();
const [BenefitConfigurationData, setBenefitConfigurationData] = useState<BenefitConfigurationListType>();
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false);
// Use Effect
// --------------------
useEffect(() => {
loadDataTableData();
}, [])
// Load Data
// -------------------
const loadDataTableData = async () => {
const response = await getBenefitConfigurationList(claim_id??'');
setBenefitConfigurationList(response);
}
return (
<Box>
{/* row list */}
{
BenefitConfigurationList?.map((row, index) => {
return (
<Box key={index} sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '24px', py: '20px', marginBottom: '24px', borderRadius: '12px'}}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={6}>
<Typography variant="body2" sx={{ fontWeight: 'bold'}}>
{row.benefit_name}
</Typography>
</Grid>
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
<MoreMenu actions={
<>
<MenuItem onClick={() => {
setIsDialogOpen(true)
setBenefitConfigurationData(row)
}}
>
<EditOutlined />
Edit
</MenuItem>
</>
} />
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Box sx={{ py: '8px', px: '12px', background: palette.light.grey[50012], borderRadius: '6px'}}>
<Grid container spacing={1}>
{/* Amount Incurred */}
<Grid item xs={3}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
Amount Incurred
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
{fNumber(row.amount_incurred)}
</Typography>
</Grid>
</Grid>
</Grid>
{/* Amount Approved */}
<Grid item xs={3}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
Amount Approved
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
{fNumber(row.amount_approved)}
</Typography>
</Grid>
</Grid>
</Grid>
{/* Amount Not Approved */}
<Grid item xs={3}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
Amount Not Approved
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
{fNumber(row.amount_not_approved)}
</Typography>
</Grid>
</Grid>
</Grid>
{/* Excess Paid* */}
<Grid item xs={3}>
<Grid container>
<Grid item xs={12}>
<Typography variant="caption">
Excess Paid*
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
{fNumber(row.excess_paid)}
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</Box>
</Grid>
</Grid>
</Box>
)
})
}
{/* Dialog */}
<BenefitConfigurationDialog data={BenefitConfigurationData} isOpen={isDialogOpen} handleCancleProp={() => setIsDialogOpen(false)} handleSuccessProp={() => loadDataTableData()} />
</Box>
);
}

View File

@@ -6,6 +6,8 @@ import { Stack } from '@mui/material';
import { enqueueSnackbar } from 'notistack';
import React, { useState } from 'react';
import FormHistoryPerawatan from './FormHistoryPerawatan';
import { useDispatch } from 'react-redux';
import { claimsHistoryAction } from '@/store/claimsHistorySlice';
type DialogHistoryPerawatanType = {
openDialog: boolean;
@@ -18,6 +20,7 @@ type DialogHistoryPerawatanType = {
export default function DialogHistoryPerawatan({ openDialog, setOpenDialog, onSubmit, claim, encounter } : DialogHistoryPerawatanType) {
const isEdit = encounter?.id != null
const dispatch = useDispatch()
// const benefits = member?.current_plan?.benefits ?? [];
// const [selectedBenefits, setSelectedBenefits] = useState([]);
@@ -38,6 +41,10 @@ export default function DialogHistoryPerawatan({ openDialog, setOpenDialog, onSu
.then((res) => {
enqueueSnackbar(res.data.message, {variant: 'success'})
setOpenDialog(false);
window.location.reload(); // tolong benerin ya
// axios.get(`claims/${claim.id}`).then((res) => {
// dispatch(claimsHistoryAction.setClaims(res.data.data.encounter))
// })
})
.catch((err) => {
enqueueSnackbar(err.message, {variant: 'error'})
@@ -47,6 +54,10 @@ export default function DialogHistoryPerawatan({ openDialog, setOpenDialog, onSu
.then((res) => {
enqueueSnackbar(res.data.message, {variant: 'success'})
setOpenDialog(false);
window.location.reload(); // tolong benerin ya
// axios.get(`claims/${claim.id}`).then((res) => {
// dispatch(claimsHistoryAction.setClaims(res.data.data.encounter))
// })
})
.catch((err) => {
enqueueSnackbar(err.message, {variant: 'error'})

View File

@@ -97,9 +97,6 @@ export default function Documents({ files }) {
<Stack sx={{px: 2}}>
<Typography variant="body2">Dokumen Diagnosa</Typography>
</Stack>
<Stack sx={{px: 2}}>
<Typography variant="body2">Dokumen Diagnosa</Typography>
</Stack>
</Stack>

View File

@@ -2,21 +2,43 @@ import * as Yup from 'yup';
import { yupResolver } from "@hookform/resolvers/yup";
import { Card, Collapse, Divider, Grid, Stack, Typography } from "@mui/material";
import { useForm } from "react-hook-form";
import { useParams } from "react-router-dom";
import { useNavigate, useParams } 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 CorporateTabNavigations from "../CorporateTabNavigations";
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import DivisionsList from "./List";
import { useMemo, useState } from 'react';
import FormEdit from "./Form";
import { useEffect, useMemo, useState } from 'react';
import { Benefit } from '@/@types/corporates';
import axios from '@/utils/axios';
export default function Divisions() {
const { themeStretch } = useSettings();
const { corporate_id } = useParams();
const { corporate_id, benefit_id } = useParams();
const [ currentCorporateBenefit, setCurrentCorporateBenefit ] = useState<Benefit>();
const navigate = useNavigate();
const isEdit = !!benefit_id;
useEffect(() => {
console.log(benefit_id);
if (isEdit) {
axios.get('/corporates/'+corporate_id+'/corporate-benefits/'+benefit_id+'/edit')
.then((res) => {
setCurrentCorporateBenefit(res.data);
})
.catch((err) => {
if (err.response.status === 404) {
navigate('/404');
}
})
}
}, [corporate_id, benefit_id]);
const NewDivisionSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
@@ -51,97 +73,12 @@ export default function Divisions() {
console.log(data);
};
const [open, setOpen] = useState(false);
const benefits = [
{
'category' : 'General Practitioner',
'childs' : [
{
'name' : 'External Doctor Online',
'code' : 'gp-external-doctor-online'
},
{
'name' : 'External Doctor Offline',
'code' : 'gp-external-doctor-offline'
},
{
'name' : 'Internal Doctor Online',
'code' : 'gp-internal-doctor-online'
},
{
'name' : 'Internal Doctor Offline',
'code' : 'gp-internal-doctor-offline'
},
]
},
{
'category' : 'Specialist',
'childs' : [
{
'name' : 'External Doctor Online',
'code' : 'sp-external-doctor-online'
},
{
'name' : 'External Doctor Offline',
'code' : 'sp-external-doctor-offline'
},
{
'name' : 'Internal Doctor Online',
'code' : 'sp-internal-doctor-online'
},
{
'name' : 'Internal Doctor Offline',
'code' : 'sp-internal-doctor-offline'
},
]
},
{
'category' : 'Medicines',
'childs' : [
{
'name' : 'Vitamins',
'code' : 'medicines-vitamins'
},
{
'name' : 'Delivery Fee',
'code' : 'medicines-delivery-fee'
},
]
},
];
const products = [
{
'name' : 'Inpatient',
'code' : 'IP',
},
{
'name' : 'Outpatient',
'code' : 'OP',
},
{
'name' : 'Dental',
'code' : 'DT',
},
{
'name' : 'Dental',
'code' : 'DTL',
},
{
'name' : 'Matternity',
'code' : 'MT',
},
{
'name' : 'Special Benefit',
'code' : 'SB',
},
];
return (
<Page title="Create Benefit">
<HeaderBreadcrumbs
{/* <HeaderBreadcrumbs
heading={'Create Benefit'}
links={[
{ name: 'Dashboard', href: '/dashboard' },
@@ -162,9 +99,9 @@ export default function Divisions() {
href: '/corporates/'+id+'/benefits/create',
},
]}
/>
/> */}
{/*
<Grid container spacing={2}>
<Grid item xs={12}>
<Card sx={{ p: 2 }}>
@@ -235,7 +172,17 @@ export default function Divisions() {
</FormProvider>
</Card>
</Grid>
</Grid>
</Grid> */}
<Stack direction="row" alignItems="center">
<ArrowBackIosIcon
onClick={() => navigate(`/corporates/${corporate_id}/benefit`)}
sx={{ cursor: 'pointer' }}
/>
<Typography variant="h5" sx={{ marginRight:2, flexGrow: 1 }}>
Edit Plan
</Typography>
</Stack>
<FormEdit isEdit={true} currentCorporateBenefit={currentCorporateBenefit}/>
</Page>
);
}

View File

@@ -1,7 +1,7 @@
import * as Yup from 'yup';
import { LoadingButton } from '@mui/lab';
import { Box, Card, Grid, Stack, Typography } from '@mui/material';
import { CorporatePlan } from '../../../@types/corporates';
import { Box, Button, Card, Grid, Stack, Typography } from '@mui/material';
import { Benefit } from '../../../@types/corporates';
import { FormProvider, RHFSwitch, RHFTextField } from '../../../components/hook-form';
import { useEffect, useMemo } from 'react';
import { useForm } from 'react-hook-form';
@@ -12,39 +12,36 @@ import axios from '../../../utils/axios';
type Props = {
isEdit: boolean;
currentCorporatePlan?: CorporatePlan;
currentCorporateBenefit?: Benefit;
};
export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Props) {
export default function CorporateBenefitForm({ isEdit, currentCorporateBenefit }: Props) {
const { enqueueSnackbar } = useSnackbar();
const navigate = useNavigate();
const { corporate_id } = useParams();
const { corporate_id, benefit_id } = useParams();
const NewCorporatePlanSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
code: Yup.string().required('Corporate Code is required'),
const NewCorporateBenefitSchema = Yup.object().shape({
budget: Yup.string().required('Budget is required'),
});
const defaultValues = useMemo(
() => ({
name: currentCorporatePlan?.name || '',
code: currentCorporatePlan?.code || '',
active: currentCorporatePlan?.active === 1 ? true : false,
budget: currentCorporateBenefit?.budget || '',
}),
[currentCorporatePlan]
[currentCorporateBenefit]
);
useEffect(() => {
if (isEdit && currentCorporatePlan) {
if (isEdit && currentCorporateBenefit) {
reset(defaultValues);
}
if (!isEdit) {
reset(defaultValues);
}
}, [isEdit, currentCorporatePlan]);
}, [isEdit, currentCorporateBenefit]);
const methods = useForm({
resolver: yupResolver(NewCorporatePlanSchema),
resolver: yupResolver(NewCorporateBenefitSchema),
defaultValues,
});
@@ -81,12 +78,12 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
});
} else {
await axios
.put('/corporate/' + corporate_id + '/divisions/' + currentCorporatePlan?.id, data)
.put('/corporates/' + corporate_id + '/corporate-benefits/' + benefit_id, data)
.then((res) => {
enqueueSnackbar('Division updated successfully', { variant: 'success' });
})
.then((res) => {
navigate('/corporate/' + corporate_id + '/divisions/', { replace: true });
navigate('/corporates/' + corporate_id + '/benefits', { replace: true });
})
.catch(({ response }) => {
enqueueSnackbar('Update Failed : ' + response.data.message, { variant: 'error' });
@@ -95,30 +92,44 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
};
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Box sx={{ margin: 1, pb: 2, pl: 4 }}>
<Grid container>
<Grid item xs={6} sx={{ padding: 2 }}>
<Grid item xs={12} sx={{ padding: 2 }}>
<Grid container>
<Stack direction="row" alignItems="center" sx={{ width: '100%' }}>
<Typography variant="subtitle2" sx={{ mr: 2 }}>
ASO/Budget
</Typography>
<RHFTextField name="budget" />
</Stack>
</Grid>
</Grid>
<Grid item xs={6} sx={{ padding: 2 }}>
<Grid container>
<Stack direction="row" alignItems="center" sx={{ width: '100%' }}>
<Typography variant="subtitle2" sx={{ mr: 2 }}>
ASO/Budget
</Typography>
<RHFTextField name="budget" />
<RHFTextField name="budget" type='number'/>
</Stack>
</Grid>
</Grid>
</Grid>
<Stack direction="row" alignItems="center" justifyContent="flex-end">
<Button
sx={{marginTop:2}}
type="submit"
variant="contained"
size="large"
color='inherit'
onClick={() => navigate(`/corporates/${corporate_id}/benefits`)}
>
Cancel
</Button>
<LoadingButton
type="submit"
variant="contained"
size="large"
// fullWidth={true}
loading={isSubmitting}
sx={{marginTop:2, marginLeft:2}}
>
Save
</LoadingButton>
</Stack>
</Box>
</FormProvider>
);

View File

@@ -35,19 +35,19 @@ export default function Divisions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Benefit',
href: '/corporate/' + corporate_id + '/benefits',
href: '/corporates/' + corporate_id + '/benefits',
},
]}
/>
<Card>
{/* <Card> */}
<CorporateTabNavigations position={'benefits'} />
<DivisionsList />
</Card>
{/* </Card> */}
</Page>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -85,7 +85,7 @@ export default function CustomizedAccordions() {
(panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false);
};
const pageTitle = 'Audittrail Corporate';
const pageTitle = 'Corporate Dashboard';
const { themeStretch } = useSettings();
@@ -121,11 +121,11 @@ export default function CustomizedAccordions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id + '/plans',
href: '/corporates/' + corporate_id + '/benefits',
},
{
name: 'Audittrail Corporate',
href: '/corporate/' + corporate_id + '/plans',
name: 'Benefit',
href: '/corporates/' + corporate_id + '/benefits',
},
]}
/>
@@ -155,6 +155,7 @@ export default function CustomizedAccordions() {
if (key !== 'reason') {
return null; // Melewati iterasi saat key adalah 'deleted_by'
}
renderedValue = item.new_values[key];
const field = key.charAt(0).toUpperCase() + key.slice(1);

View File

@@ -33,11 +33,11 @@ export default function Divisions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Claim History',
href: '/corporate/' + corporate_id + '/claim-histories',
href: '/corporates/' + corporate_id + '/claim-histories',
},
]}
/>

View File

@@ -8,6 +8,9 @@ import axios from '../../../utils/axios';
import { useSnackbar } from 'notistack';
import CorporatePlanForm from './Form';
import { CorporatePlan } from '../../../@types/corporates';
import { Stack } from "@mui/system";
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import { Typography } from "@mui/material";
@@ -36,8 +39,8 @@ export default function PlanCreate() {
return (
<Page title="Create Corporate Plan">
<HeaderBreadcrumbs
heading={'Create Corporate Plan'}
{/* <HeaderBreadcrumbs
heading={ 'Edit Plan'}
links={[
{
name: 'Corporates',
@@ -56,7 +59,16 @@ export default function PlanCreate() {
href: '/corporates/'+corporate_id+'/corporate-plans/'+id,
},
]}
/>
/> */}
<Stack direction="row" alignItems="center">
<ArrowBackIosIcon
onClick={() => navigate(`/corporates/${corporate_id}/plans`)}
sx={{ cursor: 'pointer' }}
/>
<Typography variant="h5" sx={{ marginRight:2, flexGrow: 1 }}>
Edit Plan
</Typography>
</Stack>
<CorporatePlanForm isEdit={isEdit} currentCorporatePlan={currentCorporatePlan}/>
</Page>

View File

@@ -1,6 +1,6 @@
import * as Yup from 'yup';
import { LoadingButton } from '@mui/lab';
import { Card, Grid, Stack, Typography } from '@mui/material';
import { Card, Grid, Stack, Typography, Button } from '@mui/material';
import { CorporatePlan } from '../../../@types/corporates';
import { FormProvider, RHFEditor, RHFSwitch, RHFTextField } from '../../../components/hook-form';
import { useEffect, useMemo, useState } from 'react';
@@ -22,16 +22,22 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
const { corporate_id } = useParams();
const NewCorporatePlanSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
code: Yup.string().required('Corporate Code is required'),
service: Yup.string().required('Corporate Service is required'),
plan: Yup.string().required('Corporate Plan is required'),
type: Yup.string().required('Corporate Type is required'),
limit: Yup.string().required('Corporate Limit is required'),
});
const defaultValues = useMemo(
() => ({
name: currentCorporatePlan?.name || '',
// name: currentCorporatePlan?.name || '',
code: currentCorporatePlan?.code || '',
active: currentCorporatePlan?.active || true,
description: currentCorporatePlan?.description || '',
// active: currentCorporatePlan?.active || true,
type: currentCorporatePlan?.type || '',
limit: currentCorporatePlan?.limit_rules || '',
service: currentCorporatePlan?.service_code || '',
plan: currentCorporatePlan?.corporate_plan_id || '',
}),
[currentCorporatePlan]
);
@@ -62,14 +68,15 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
} = methods;
const onSubmit = async (data: any) => {
console.log(data);
if (!isEdit) {
await axios
.post('/corporate/' + corporate_id + '/corporate-plans', data)
.post('/corporates/' + corporate_id + '/corporate-plans', data)
.then((res) => {
enqueueSnackbar('Corporate Plan created successfully', { variant: 'success' });
})
.then((res) => {
navigate('/corporate/' + corporate_id + '/corporate-plans', { replace: true });
navigate(`/corporates/${corporate_id}/plans`, { replace: true });
})
.catch(({ response }) => {
if (response.status === 422) {
@@ -83,12 +90,12 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
});
} else {
await axios
.put('/corporate/' + corporate_id + '/corporate-plans/' + currentCorporatePlan?.id, data)
.put('/corporates/' + corporate_id + '/corporate-plans/' + currentCorporatePlan?.id, data)
.then((res) => {
enqueueSnackbar('Corporate Plan updated successfully', { variant: 'success' });
})
.then((res) => {
navigate('/corporate/' + corporate_id + '/corporate-plans/', { replace: true });
navigate('/corporates/' + corporate_id + '/plans', { replace: true });
})
.catch(({ response }) => {
enqueueSnackbar('Update Failed : ' + response.data.message, { variant: 'error' });
@@ -98,12 +105,34 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Grid container spacing={2}>
<Grid item xs={8}>
<Card sx={{ p: 2 }}>
<Card sx={{ p: 2, marginTop: 2 }}>
<Grid container spacing={4} paddingX={2} paddingY={2}>
<Grid item xs={3}>
<Typography variant="subtitle1">Service*</Typography>
<RHFTextField name="service" label="service" sx={{marginTop:2}}/>
</Grid>
<Grid item xs={3}>
<Typography variant="subtitle1">Plan*</Typography>
<RHFTextField name="plan" label="plan" sx={{marginTop:2}} />
</Grid>
<Grid item xs={3}>
<Typography variant="subtitle1">Code*</Typography>
<RHFTextField name="code" label="code" sx={{marginTop:2}} />
</Grid>
<Grid item xs={3}>
<Typography variant="subtitle1">Type*</Typography>
<RHFTextField name="type" label="type" sx={{marginTop:2}} />
</Grid>
<Grid item xs={6}>
<Typography variant="subtitle1">Plan Limit*</Typography>
<RHFTextField name="limit" label="limit" sx={{marginTop:2}} />
</Grid>
{/* <Grid item xs={3}>
<Stack spacing={3}>
<Typography variant="h6">Corporate Plan Detail</Typography>
<RHFTextField name="name" label="Name" />
<RHFTextField name="code" label="Code" />
@@ -122,17 +151,35 @@ export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Prop
fullWidth={true}
loading={isSubmitting}
>
Create Corporate Plan
{ isEdit ? 'Update' : 'Create'} Corporate Plan
</LoadingButton>
</Stack>
</Card>
</Grid> */}
</Grid>
<Grid item xs={4}>
<Card sx={{ p: 2 }}>
<RHFSwitch name="active" label="Active" />
</Card>
</Grid>
</Grid>
</Card>
<Stack direction="row" alignItems="center" justifyContent="flex-end">
<Button
sx={{marginTop:2}}
type="submit"
variant="contained"
size="large"
color='inherit'
onClick={() => navigate(`/corporates/${corporate_id}/plans`)}
>
Cancel
</Button>
<LoadingButton
type="submit"
variant="contained"
size="large"
// fullWidth={true}
loading={isSubmitting}
sx={{marginTop:2, marginLeft:2}}
>
Save
</LoadingButton>
</Stack>
</FormProvider>
);
}

View File

@@ -32,11 +32,11 @@ export default function Divisions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Corporate Plan',
href: '/corporate/' + corporate_id + '/corporate-plans',
href: '/corporates/' + corporate_id + '/corporate-plans',
},
]}
/>

View File

@@ -44,10 +44,10 @@ export default function CorporateTabNavigations({ position }: Props) {
path: 'benefits',
label: 'Benefit',
},
{
path: 'divisions',
label: 'Division',
},
// {
// path: 'divisions',
// label: 'Division',
// },
{
path: 'members',
label: 'Member List',
@@ -61,15 +61,16 @@ export default function CorporateTabNavigations({ position }: Props) {
label: 'Hospital',
},
{
path: 'formularium',
path: 'formulariums',
label: 'Formularium',
},
{
path: 'claim-history',
label: 'Claim History',
},
// {
// path: 'claim-history',
// label: 'Claim History',
// },
];
useEffect(() => {
console.log(position);
let currentIndex = mainTabItems.findIndex((item) => item.path === position);
setCurrentTab(currentIndex);
}, []);
@@ -89,7 +90,7 @@ export default function CorporateTabNavigations({ position }: Props) {
<Tab
key={index}
onClick={() => {
navigate('/corporate/' + corporate_id + '/' + mainTabItems[index].path);
navigate('/corporates/' + corporate_id + '/' + mainTabItems[index].path);
}}
label={tabItem.label}
/>

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

@@ -0,0 +1,211 @@
// @mui
import {
Box,
Button,
Card,
Collapse,
Container,
FormControl,
Grid,
IconButton,
InputLabel,
MenuItem,
OutlinedInput,
Paper,
Select,
SelectChangeEvent,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Typography,
Badge,
Stack,
} from '@mui/material';
import * as React from 'react';
import { useParams } from 'react-router-dom';
import { styled } from '@mui/material/styles';
import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp';
import MuiAccordion, { AccordionProps } from '@mui/material/Accordion';
import { useContext, useEffect, useState } from 'react';
import MuiAccordionSummary, {
AccordionSummaryProps,
} from '@mui/material/AccordionSummary';
import useSettings from '../../../hooks/useSettings';
import axios from '../../../utils/axios';
import { ConfiguredCorporateContext } from '@/contexts/ConfiguredCorporateContext';
import MuiAccordionDetails from '@mui/material/AccordionDetails';
import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs';
import { Corporate } from '@/@types/corporates';
import { fDate, fDateTime } from '@/utils/formatTime';
const Accordion = styled((props: AccordionProps) => (
<MuiAccordion disableGutters elevation={0} square {...props} />
))(({ theme }) => ({
border: `1px solid ${theme.palette.divider}`,
'&:not(:last-child)': {
borderBottom: 0,
},
'&:before': {
display: 'none',
},
}));
const AccordionSummary = styled((props: AccordionSummaryProps) => (
<MuiAccordionSummary
expandIcon={<ArrowForwardIosSharpIcon sx={{ fontSize: '0.9rem' }} />}
{...props}
/>
))(({ theme }) => ({
backgroundColor:
theme.palette.mode === 'dark'
? 'rgba(255, 255, 255, .05)'
: 'rgba(0, 0, 0, .03)',
flexDirection: 'row-reverse',
'& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': {
transform: 'rotate(90deg)',
},
'& .MuiAccordionSummary-content': {
marginLeft: theme.spacing(1),
},
}));
const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
padding: theme.spacing(2),
borderTop: '1px solid rgba(0, 0, 0, .125)',
}));
export default function CustomizedAccordions() {
const [expanded, setExpanded] = React.useState<string | false>('panel1');
const handleChange =
(panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false);
};
const pageTitle = 'History Corporate Exclusions';
const { themeStretch } = useSettings();
const { corporate_id, id } = useParams();
const [corporate, setCorporate] = useState<Corporate | null>();
const [ currentCorporate, setCurrentCorporate ] = useState<Corporate>();
const configuredCorporateContext = useContext(ConfiguredCorporateContext);
useEffect(() => {
setCorporate(configuredCorporateContext.currentCorporate);
const model = 'App\\Models\\ExclusionImport';
const url = `/audittrail/${corporate_id}?model=${model}`;
axios.get(url)
.then((res) => {
setCurrentCorporate(res.data);
})
.catch((error) => {
console.error('Terjadi kesalahan:', error);
});
}, [configuredCorporateContext]);
return (
<div>
<HeaderBreadcrumbs
heading={pageTitle}
links={[
{
name: 'Corporates',
href: '/corporates',
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id + '/diagnosis-exclusions',
},
{
name: 'History Corporate Exclusion',
href: '/corporate/' + corporate_id + '/plans',
},
]}
/>
{currentCorporate?.data.map((item, index) => (
<Accordion
key={index}
expanded={expanded === `panel${index}`}
onChange={handleChange(`panel${index}`)}
>
<AccordionSummary
aria-controls={`panel${index}d-content`}
id={`panel${index}d-header`}
>
<Typography>{`Data has ${item.action} by ${item.user_id} on ${fDateTime(item.updated_at)}`}</Typography>
</AccordionSummary>
<AccordionDetails>
<TableHead>
<TableRow>
<TableCell align="center">Field</TableCell>
<TableCell align="center">Old Value</TableCell>
<TableCell align="center">New Values</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Object.entries(item.old_values).map(([key, value]) => {
let renderedValue;
if (key === 'deleted_by' || key === 'created_by' || key === 'updated_by' || key === 'file_path') {
return null; // Melewati iterasi saat key adalah 'deleted_by'
}
switch (key) {
case 'welcome_message':
renderedValue = item.new_values[key].replace(/<[^>]*>/g, '');
value = value.replace(/<[^>]*>/g, '');
break;
case 'help_text':
renderedValue = item.new_values[key].replace(/<[^>]*>/g, '');
value = value.replace(/<[^>]*>/g, '');
break;
case 'active':
renderedValue = item.new_values[key] == 1 ? 'Active' : 'Inactive';
value = value == 1 ? 'Active' : 'Inactive';
break;
case 'created_at':
renderedValue = fDateTime(item.new_values[key]);
value = fDateTime(value);
break;
case 'updated_at':
renderedValue = fDateTime(item.new_values[key]);
value = fDateTime(value);
break;
case 'updated_at':
renderedValue = fDateTime(item.new_values[key]);
value = fDateTime(value);
break;
case 'delete_at':
renderedValue = fDateTime(item.new_values[key]);
value = fDateTime(value);
break;
default:
renderedValue = item.new_values[key];
break;
}
const field = key.charAt(0).toUpperCase() + key.slice(1);
if (value != renderedValue){
return (
<TableRow key={key} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
<TableCell>{`${field}`}</TableCell>
<TableCell align="center">{`${value}`}</TableCell>
<TableCell align="center">{renderedValue}</TableCell>
</TableRow>
);
}
})}
</TableBody>
</AccordionDetails>
</Accordion>
))}
</div>
);
}

View File

@@ -36,11 +36,11 @@ export default function Divisions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Diagnosis Exclusion',
href: '/corporate/' + corporate_id + '/diagnosis-exclusions',
href: '/corporates/' + corporate_id + '/diagnosis-exclusions',
},
]}
/>

View File

@@ -1,4 +1,5 @@
// @mui
import * as Yup from 'yup';
import {
Autocomplete,
Box,
@@ -38,14 +39,17 @@ import {
} from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add';
import GetApp from '@mui/icons-material/GetApp';
import UploadIcon from '@mui/icons-material/Upload';
import CancelIcon from '@mui/icons-material/Cancel';
// hooks
import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react';
import useSettings from '../../../hooks/useSettings';
import { useParams, useSearchParams } from 'react-router-dom';
import { Link, useParams, useSearchParams, useNavigate } from 'react-router-dom';
// components
import { yupResolver } from '@hookform/resolvers/yup';
import { useForm } from 'react-hook-form';
import axios from '../../../utils/axios';
import { LaravelPaginatedData } from '../../../@types/paginated-data';
import { Icd } from '../../../@types/diagnosis';
@@ -53,6 +57,14 @@ import BasePagination from '../../../components/BasePagination';
import { enqueueSnackbar } from 'notistack';
import { Icon } from '@iconify/react';
import { LoadingButton } from '@mui/lab';
import HistoryIcon from '@mui/icons-material/History';
import CachedIcon from '@mui/icons-material/Cached';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import { EditOutlined, FindInPageOutlined } from '@mui/icons-material';
import Label from '@/components/Label';
import { display } from '@mui/system';
import DialogUpdateStatus from '@/components/DialogUpdateStatus'
import { FormProvider, RHFSelect } from '@/components/hook-form';
export default function List(props: any) {
const { themeStretch } = useSettings();
@@ -81,7 +93,7 @@ export default function List(props: any) {
}, [searchParams]);
return (
<form onSubmit={handleSearchSubmit} style={{ width: '100%' }}>
<form onSubmit={handleSearchSubmit} style={{ width: '90%' }}>
<TextField
id="search-input"
ref={searchInput}
@@ -145,7 +157,7 @@ export default function List(props: any) {
handleCancelImportButton();
loadDataTableData();
setImportResult(response.data);
setImportLoading(false);
// alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows');
})
@@ -155,14 +167,14 @@ export default function List(props: any) {
response.message,
{ variant: 'error' }
);
setImportLoading(false);
})
} else {
enqueueSnackbar('No File Selected', { variant: 'warning' });
}
};
const handleGetTemplate = (type :string) => {
axios.get('corporates/import-document-example/' + type)
.then((response) => {
@@ -190,17 +202,19 @@ export default function List(props: any) {
<SearchInput onSearch={applyFilter} />
{/* <h1>kjasndkjandskjasndkjansdkjansd</h1> */}
<Button
id="import-button"
variant="outlined"
startIcon={<AddIcon />}
sx={{ p: 1.8 }}
aria-controls={createMenu ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={createMenu ? 'true' : undefined}
onClick={handleClick}
id="import-button"
variant="contained"
startIcon={<GetApp />}
sx={{ p: 1.8,width: '200px' }}
aria-controls={createMenu ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={createMenu ? 'true' : undefined}
onClick={handleClick}
color='primary'
>
Import
Import
</Button>
<Menu
id="import-button"
anchorEl={anchorEl}
@@ -264,6 +278,14 @@ export default function List(props: any) {
...icd,
};
}
type DataContent = {
service: string;
id: number;
status: number;
code: string,
name: string,
rules: string,
};
const plans = props?.data.map((plan: any) => {
return {
value: plan.code,
@@ -297,6 +319,16 @@ export default function List(props: any) {
const { row, index, data } = props;
}
const [isDialogOpen, setDialogOpen] = useState(false)
let titles = {
name: 'Update Status',
icon: '-'
}
const [dataValue, setDataValue] = useState('');
const [dataDescription, setDescriptionValue] = useState('');
const [url, setUrl] = useState('');
function Row(props: {
row: ReturnType<typeof createData>;
data: any;
@@ -329,9 +361,6 @@ export default function List(props: any) {
const [openEdit, setOpenEdit] = React.useState(false);
console.log('open', open);
console.log('openEdit', openEdit);
// const [plans, setPlans] = useState([]);
const plans = data;
@@ -461,290 +490,217 @@ export default function List(props: any) {
console.log('exclusions', exclusions);
// const handleActivate = (row: any) => {
// axios
// .put(`/corporates/diagnosis-exclusions/update_activation`, {
// id: row.id,
// active: row.active == 1 ? 0 : 1,
// })
// .then((res) => {
// 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) => {
// 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' }
// );
// }
// });
// };
const handleActivate = (isOpen: boolean, dataValue: DataContent) => {
console.log(dataValue)
setDialogOpen(isOpen)
setDataValue(dataValue)
setDescriptionValue('Are you sure to inactive this service ?')
setUrl(url)
};
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell>
<TableCell align="left">{row.service_code}</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">{Object.keys(row.rules).length ? 'With Rules' : 'All'}</TableCell>
<TableCell align="left" onClick={() => {if(open==true) setOpen(!open)}}/>
<TableCell align="left" onClick={() => {if(open==true) setOpen(!open)}}>
{row.service_code}
</TableCell>
<TableCell align="left" onClick={() => {if(open==true) setOpen(!open)}}>
{row.code}
</TableCell>
<TableCell align="left" onClick={() => {if(open==true) setOpen(!open)}}>
{row.name}
</TableCell>
<TableCell align="left" onClick={() => {if(open==true) setOpen(!open)}}>
{Object.keys(row.rules).length ? 'With Rules' : 'All'}
</TableCell>
<TableCell align="left" onClick={() => {if(open==true) setOpen(!open)}}>
{row.active == 1 && (
<Label
variant="ghost"
color="success"
size="small"
>
Active
</Label>
)}
{row.active != 1 && (
<Label
variant="ghost"
color="error"
size="small"
>
Inactive
</Label>
)}
</TableCell>
<TableCell align="center">
{openEdit ? (
<Button
variant="contained"
color="success"
size="small"
sx={{
color: '#fff',
}}
onClick={(event) => {
setOpenEdit(!openEdit);
if (open == false) {
setOpen(true);
}
handleConfigExclusion(event, row, '', 'one_row');
}}
>
Save
</Button>
) : (
<Button
variant="outlined"
color="success"
size="small"
onClick={() => {
setOpenEdit(true);
setOpen(true);
}}
>
Edit
</Button>
)}
<Button
variant="outlined"
color="error"
size="small"
sx={{ ml: 2 }}
onClick={() => {
setOpenDelete(true);
}}
>
Delete
</Button>
</TableCell>
<TableCell align="left">
<TableMoreMenu actions={
<>
<MenuItem onClick={() => setOpen(!open)}>
<FindInPageOutlined />
Detail
</MenuItem>
<MenuItem onClick={() => navigate(`/corporates/${corporate_id}/diagnosis-exclusions/${row.id}/edit`)} >
<EditOutlined />
Edit
</MenuItem>
<MenuItem onClick={() => handleActivate(true, {
code: row.code,
name: row.name,
rules: Object.keys(row.rules).length ? 'With Rules' : 'All',
id:row.id,
status: row.active,
service: row.service_code
})
} >
<CachedIcon />
Update Status
</MenuItem>
</>
} />
</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={99}>
<Collapse in={open} timeout="auto" unmountOnExit>
{open == true ? (
openEdit == false ? (
<Box sx={{ borderBottom: 1 }}>
{Object.keys(row.rules).length ? (
<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>
) : (
<Typography variant="body2" gutterBottom component="div">
Excluded for All
</Typography>
)}
</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}
{open == true ? (
<Box sx={{ padding: '24px' }}>
<Card sx={{ padding: '24px' }}>
<Grid container spacing={6}>
<Grid item xs={8}>
<Typography variant="body1" sx={{ fontWeight: 'bold'}}>
Excluded Only for :
</Typography>
</Grid>
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={3}>
<Typography variant="body1" component="div" color={'GrayText'}>
MSC :
</Typography>
</Grid>
<Grid item xs={9} sx={{ display: 'flex', gap: 1 }}>
{row?.rules?.msc && row?.rules?.msc[0] ? (
row?.rules?.msc[0].split(',').map((text, i) => {
let labelMSC: string = text;
switch (labelMSC) {
case 'm':
labelMSC = 'Member';
break;
case 'c':
labelMSC = 'Child';
break;
case 's':
labelMSC = 'Spouse';
break;
default:
labelMSC = 'Member';
}
return (
<Typography key={i} component="div">
<Label>{labelMSC}</Label>
</Typography>
);
})
) : '-'}
</Grid>
<Grid item xs={3}>
<Typography variant="body1" component="div" color={'GrayText'}>
Gender :
</Typography>
</Grid>
<Grid item xs={9} sx={{display: 'flex', gap: 1}}>
{row?.rules?.gender && row?.rules?.gender[0] ? (
row?.rules?.gender[0].split(',').map((text,i) => {
const capitalizedText = text.charAt(0).toUpperCase() + text.slice(1);
return (
<Typography key={i} component="div">
<Label>{capitalizedText}</Label>
</Typography>
)
})
) : '-'}
</Grid>
<Grid item xs={3}>
<Typography variant="body1" component="div" color={'GrayText'}>
Min Age :
</Typography>
</Grid>
<Grid item xs={9} sx={{display: 'flex', gap: 1}}>
<Typography component="div">
{row?.rules?.min_age && row?.rules?.min_age[0] ? row?.rules?.min_age[0] : '-'}
</Typography>
</Grid>
<Grid item xs={3}>
<Typography variant="body1" component="div" color={'GrayText'}>
Max Age :
</Typography>
</Grid>
<Grid item xs={9} sx={{display: 'flex', gap: 1}}>
<Typography component="div">
{row?.rules?.max_age && row?.rules?.max_age[0] ? row?.rules?.max_age[0] : '-'}
</Typography>
</Grid>
<Grid item xs={3}>
<Typography variant="body1" component="div" color={'GrayText'}>
Plan :
</Typography>
</Grid>
<Grid item xs={9} sx={{display: 'flex', gap: 1}}>
<Typography component="div">
{row?.rules?.plan && row?.rules?.plan[0] ? row?.rules?.plan[0] : '-'}
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</Card>
</Box>
) : null }
</Collapse>
</TableCell>
</TableRow>
@@ -843,6 +799,18 @@ export default function List(props: any) {
fontWeight: 'bold',
};
const onSubmit = async (row : any) => {
try {
handleUpdate(dataValue.status, dataValue.id, row.reason)
} catch (error: any) {
console.log('data gagal');
}
const ascent = document?.querySelector('ascent');
if (ascent != null) {
ascent.innerHTML = '';
}
};
const applyFilter = async (searchFilter: any) => {
await loadDataTableData({ search: searchFilter });
setSearchParams({ search: searchFilter });
@@ -858,6 +826,138 @@ export default function List(props: any) {
loadDataTableData();
}, []);
const navigate = useNavigate()
const NewCorporateSchema = Yup.object().shape({
reason: Yup.string().required('Reason Edit is required'),
});
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema),
});
const {
reset,
handleSubmit,
formState: { isSubmitting },
} = methods;
const handleUpdate = (active: number, id: number, reason:string) => {
console.log(active)
axios
.put(`/corporates/diagnosis-exclusions/update_activation`, {
id: id,
active: active,
reason: reason
})
.then((res) => {
window.location.reload();
});
}
const getContent = () => (
<>
<Stack paddingX={2} paddingY={2}>
<Typography variant='subtitle1'>Are you sure to {dataValue?.status == 1 ? 'inactive' : 'active'} this benefit ?</Typography>
</Stack>
<Card>
<Grid container paddingX={2} paddingY={2}>
<Grid item xs={5} md={5}>
<Typography variant='inherit'>Service</Typography>
</Grid>
<Grid item xs={7}>
<Typography variant='subtitle1'>{dataValue?.service}</Typography>
</Grid>
<Grid item xs={5} md={5} marginTop={2}>
<Typography variant='inherit'>Code</Typography>
</Grid>
<Grid item xs={7} marginTop={2}>
<Typography variant='subtitle1'>{dataValue?.code}</Typography>
</Grid>
<Grid item xs={5} md={5} marginTop={2}>
<Typography variant='inherit'>Name</Typography>
</Grid>
<Grid item xs={7} marginTop={2}>
<Typography variant='subtitle1'>{dataValue?.name}</Typography>
</Grid>
<Grid item xs={5} md={5} marginTop={2}>
<Typography variant='inherit'>Rules</Typography>
</Grid>
<Grid item xs={7} marginTop={2}>
<Typography variant='subtitle1'>{dataValue?.rules}</Typography>
</Grid>
</Grid>
</Card>
<Typography marginTop={5} marginBottom={3} variant='subtitle1'>
Reason for update*
</Typography>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<RHFSelect
name="reason"
label="Reason for update"
>
<option value=""></option>
<option value="Agreement changed">Agreement changed</option>
<option value="Endorsement">Endorsement</option>
<option value="Renewal">Renewal</option>
<option value="Worng Setting">Worng Setting</option>
</RHFSelect>
<Stack
alignItems="center"
justifyContent="flex-end"
direction={{ xs: 'column', md: 'row' }}
spacing={2}
marginTop={5}
>
<Stack direction="row" spacing={1}>
<Button
sx={{
boxShadow: 'none',
}}
variant="outlined"
size="medium"
fullWidth={true}
onClick={() => setDialogOpen(false)}
>
Cancel
</Button>
{dataValue?.status == 1?
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)'}}
type="submit"
variant="contained"
size="medium"
fullWidth={true}
loading={isSubmitting}
color='error'
>
Inactive
</LoadingButton>
:
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)'}}
type="submit"
variant="contained"
size="medium"
fullWidth={true}
loading={isSubmitting}
color='success'
>
Active
</LoadingButton>
}
</Stack>
</Stack>
</FormProvider>
</>
);
return (
<Stack>
<ImportForm />
@@ -866,9 +966,9 @@ export default function List(props: any) {
{/* The Main Table */}
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
<TableHead>
<TableRow>
<TableCell style={headStyle} align="left" />
<TableCell align="left" width={50} />
<TableCell style={headStyle} align="left">
Service
</TableCell>
@@ -881,15 +981,25 @@ export default function List(props: any) {
<TableCell style={headStyle} align="left">
Rules
</TableCell>
<TableCell style={headStyle} align="center">
Action
<TableCell style={headStyle} align="left">
Status
</TableCell>
<TableCell style={headStyle} align="right">
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate(`/corporates/${corporate_id}/diagnosis-exclusions/history`)}>
<HistoryIcon />
History
</MenuItem>
</>
} />
</TableCell>
</TableRow>
</TableBody>
</TableHead>
{dataTableIsLoading ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
<TableCell colSpan={7} align="center">
Loading
</TableCell>
</TableRow>
@@ -897,7 +1007,7 @@ export default function List(props: any) {
) : dataTableData.data.length == 0 ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
<TableCell colSpan={7} align="center">
No Data
</TableCell>
</TableRow>
@@ -920,6 +1030,16 @@ export default function List(props: any) {
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange} />
</Card>
<DialogUpdateStatus
openDialog={isDialogOpen}
setOpenDialog={setDialogOpen}
title={titles}
data={dataValue}
description={dataDescription}
content={getContent()}
// maxWidth='50px'
/>
</Stack>
);
}

View File

@@ -0,0 +1,189 @@
import * as Yup from 'yup';
import { enqueueSnackbar, useSnackbar } from 'notistack';
import { useNavigate } from 'react-router-dom';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
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, Button, Grid, Stack, Typography, Chip, Autocomplete } from '@mui/material';
import { CorporateService } from '../../@types/corporates';
// components
import { FormProvider, RHFTextField, RHFSwitch, RHFSelect } from '../../components/hook-form';
import axios from '../../utils/axios';
import { LaravelPaginatedData } from '../../@types/paginated-data';
// import { Contact } from '../../../../@types/contact';
import { Link, useParams, useSearchParams } from 'react-router-dom';
// @mui
// components
import MuiDialog from '../../components/MuiDialog';
// React
import { ReactElement } from 'react';
// ----------------------------------------------------------------------
const HeaderStyle = styled('header')(({ theme }) => ({
display: 'flex',
alignItems: 'center',
padding: theme.spacing(2),
justifyContent: 'space-between',
}));
type DataContent = {
info: string;
date: string;
time: string;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
id: number;
};
type FormValuesProps = {
value: string;
active: boolean;
};
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
const DialogUpdateStatus = ({ title, openDialog, setOpenDialog, id }: MuiDialogProps) => {
const NewCorporateSchema = Yup.object().shape({
reason: Yup.string().required('Corporate Status is required'),
});
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema),
});
const {
reset,
handleSubmit,
formState: { isSubmitting },
} = methods;
useEffect(() => {
if (openDialog === false) {
reset();
}
}, [openDialog, reset]);
const handleActivate = (id: number, status: string) => {
axios
.put(`/corporates/${id}/activation`, {
active: status == 'active',
})
.then((res) => {
console.log(res)
enqueueSnackbar(
'Succes',
{ variant: 'success' }
);
})
.catch((error) => {
// console.log('asdasd', error.response.data.message)
enqueueSnackbar(
error.response.data.message ?? error.message ?? 'Failed Processing Request',
{ variant: 'error' }
);
});
};
const onSubmit = async (row : ReturnType<typeof createData>) => {
try {
handleActivate(1, 'active')
} catch (error: any) {
}
const ascent = document?.querySelector('ascent');
if (ascent != null) {
ascent.innerHTML = '';
}
};
function createData(corporateService: CorporateService): CorporateService {
return {
...corporateService,
};
}
const getContent = (props: { row: ReturnType<typeof createData> }) => (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={3}>
<Box sx={{ width: '100%', typography: 'body1', p: 2, mt: 1 }}>
<Grid item xs={12}>
<RHFSelect
name="reason"
label="Reason for update"
>
<option value=""></option>
<option value="Agreement changed">Agreement changed</option>
<option value="Endorsement">Endorsement</option>
<option value="Renewal">Renewal</option>
<option value="Worng Setting">Worng Setting</option>
</RHFSelect>
</Grid>
<Box sx={{ pt: 5 }}>
<Stack
alignItems="center"
justifyContent="flex-end"
direction={{ xs: 'column', md: 'row' }}
// sx={{ textAlign: { xs: 'center', md: 'left' } }}
spacing={2}
>
<Stack direction="row" spacing={1}>
<Button
sx={{
boxShadow: 'none',
}}
variant="outlined"
size="medium"
fullWidth={true}
onClick={() => setOpenDialog(false)}
>
Cancel
</Button>
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)' }}
type="submit"
variant="contained"
size="medium"
fullWidth={true}
loading={isSubmitting}
>
Save
</LoadingButton>
</Stack>
</Stack>
</Box>
</Box>
</Stack>
</FormProvider>
);
return (
<MuiDialog
title={title}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
maxWidth="sm"
/>
);
};
export default DialogUpdateStatus;

View File

@@ -3,18 +3,29 @@ import { useNavigate, useParams } from "react-router-dom";
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
import Page from "../../../components/Page";
import useSettings from "../../../hooks/useSettings";
import { useEffect, useMemo, useState } from 'react';
import {useContext, useEffect, useMemo, useState } from 'react';
import axios from '../../../utils/axios';
import { useSnackbar } from 'notistack';
import CorporatePlanForm from './Form';
import { CorporatePlan } from '../../../@types/corporates';
import { Corporate } from "@/@types/corporates";
import { ConfiguredCorporateContext } from "@/contexts/ConfiguredCorporateContext";
export default function PlanCreate() {
const { themeStretch } = useSettings();
const { corporate_id, id } = useParams();
const [corporate, setCorporate] = useState<Corporate|null>();
const configuredCorporateContext = useContext(ConfiguredCorporateContext);
useEffect(() => {
setCorporate(configuredCorporateContext.currentCorporate);
}, [configuredCorporateContext])
const [ currentCorporatePlan, setCurrentCorporatePlan ] = useState<CorporatePlan>();
const navigate = useNavigate();
const isEdit = !!id;
@@ -44,16 +55,16 @@ export default function PlanCreate() {
href: '/corporates',
},
{
name: 'Corporate Name',
href: '/corporates/'+corporate_id,
name: corporate?.name ?? '-',
href: '/corporate/'+corporate_id,
},
{
name: 'Division',
href: '/corporates/'+corporate_id+'/divisions',
href: '/corporate/'+corporate_id+'/divisions',
},
{
name: !isEdit ? 'Create' : 'Edit',
href: '/corporates/'+corporate_id+'/divisions/'+id,
href: '/corporate/'+corporate_id+'/divisions/'+id,
},
]}
/>

View File

@@ -36,11 +36,11 @@ export default function Divisions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Division',
href: '/corporate/' + corporate_id + '/divisions',
href: '/corporates/' + corporate_id + '/divisions',
},
]}
/>

View File

@@ -41,6 +41,14 @@ import axios from '../../../utils/axios';
import { CorporatePlan } from '../../../@types/corporates';
import { LaravelPaginatedData } from '../../../@types/paginated-data';
import BasePagination from '../../../components/BasePagination';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import HistoryIcon from '@mui/icons-material/History';
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
import CachedOutlinedIcon from '@mui/icons-material/CachedOutlined';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { enqueueSnackbar } from 'notistack';
export default function PlanList() {
const { themeStretch } = useSettings();
@@ -97,74 +105,33 @@ export default function PlanList() {
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell>
<TableCell align="left">{row.id}</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">{row.description}</TableCell>
<TableCell align="right">
<Button variant="outlined" color="success" size="small">
Active
</Button>
</TableCell>
<TableCell align="right">
<Link to={`/corporate/${row.corporate_id}/divisions/${row.id}/edit`}>
<Button variant="outlined" color="success" size="small">
Edit
</Button>
</Link>
</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={10}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ borderBottom: 1 }}>
<Typography variant="body2" gutterBottom component="div">
No Extra Data
</Typography>
</Box>
{false && (
<Box sx={{ margin: 1 }}>
<Typography variant="h6" gutterBottom component="div">
Rules
</Typography>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow>
<TableCell>Date</TableCell>
<TableCell>Customer</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Total price ($)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{/* {row.history ? row.history.map((historyRow) => ( */}
<TableRow key={row.id}>
<TableCell component="th" scope="row">
{row.start} - {row.end}
</TableCell>
<TableCell>{row.start}</TableCell>
<TableCell align="right">{row.start}</TableCell>
<TableCell align="right">{row.start}</TableCell>
</TableRow>
{/* ))
: (
<TableRow>
<TableCell colSpan={8}>No Data</TableCell>
</TableRow>
)
} */}
</TableBody>
</Table>
</Box>
)}
</Collapse>
<TableCell align="center">{row.id ? row.id : '-'}</TableCell>
<TableCell align="left">{row.code ? row.code : '-'}</TableCell>
<TableCell align="left">{row.name ? row.name : '-'}</TableCell>
<TableCell align="left">{row.description ? row.description : '-'}</TableCell>
<TableCell align="left">
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate ('')}>
<FindInPageOutlinedIcon />
Details
</MenuItem>
<MenuItem onClick={() => handleEditData(row)}>
<EditOutlinedIcon />
Edit
</MenuItem>
<MenuItem onClick={() => handleEditDataStatus(row)}>
<CachedOutlinedIcon />
Update Status
</MenuItem>
<MenuItem onClick={() => navigate ('')}>
<HistoryIcon />
History
</MenuItem>
</>
}
/>
</TableCell>
</TableRow>
</React.Fragment>
@@ -194,7 +161,6 @@ export default function PlanList() {
const response = await axios.get('/corporates/' + corporate_id + '/divisions', {
params: filter,
});
// console.log(response.data);
setDataTableLoading(false);
setDataTableData(response.data);
@@ -219,48 +185,147 @@ export default function PlanList() {
loadDataTableData();
}, []);
// Dialog for devisions
const [openDialog, setOpenDialog] = useState(false);
//validation dialog
const [nameField, setNameField] = useState('');
const [nameFieldError, setNameFieldError] = useState('');
const [codeField, setCodeField] = useState('');
const [codeFieldError, setCodeFieldError] = useState('');
const [descriptionField, setDescriptionField] = useState('');
// ID for edit data
const [idField, setIdField] = useState('');
const isRequiredFieldsFilled = () => {
return nameField.trim() !== '' && codeField.trim() !== '';
};
const handleAddData = () => {
setOpenDialog(true);
}
const handleCloseDialog = () => {
setOpenDialog(false);
setNameField('');
setCodeField('');
setDescriptionField('');
setIdField('');
}
const handleSaveData = () => {
const addData = {
code: codeField,
name: nameField,
description: descriptionField,
};
// Check conditions add or update data
if(idField)
{
// Update data
axios
.put('/corporates/'+corporate_id+'/divisions/'+idField+'', addData)
.then((response) => {
enqueueSnackbar('Data saved successfully', { variant: 'success' });
loadDataTableData();
handleCloseDialog();
})
.catch((error) => {
enqueueSnackbar('Failed to add data', { variant: 'error' });
});
}
else{
// Save data
axios
.post('/corporates/'+corporate_id+'/divisions', addData)
.then((response) => {
enqueueSnackbar('Data saved successfully', { variant: 'success' });
loadDataTableData();
handleCloseDialog();
})
.catch((error) => {
enqueueSnackbar('Failed to add data', { variant: 'error' });
});
}
}
//Edit data
const handleEditData = (data) => {
setIdField(data.id)
setNameField(data.name);
setCodeField(data.code);
setDescriptionField(data.description);
setOpenDialog(true);
}
// End dialog for devisions
// Dialog for update status devisions
const [openDialogStatus, setOpenDialogStatus] = useState(false);
// Active for update status
const [activeField, setActiveField] = useState('');
const handleCloseDialogStatus = () => {
setOpenDialogStatus(false);
setNameField('');
setCodeField('');
setDescriptionField('');
setIdField('');
setActiveField('');
}
const handleSaveDataStatus = () => {
}
const handleEditDataStatus = (data) => {
setIdField(data.id)
setNameField(data.name);
setCodeField(data.code);
setDescriptionField(data.description);
setActiveField(data.active);
setOpenDialogStatus(true);
}
// End dialog for update status devisions
return (
<Stack>
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter} />
<Link to={`/corporate/${corporate_id}/divisions/create`}>
<Button
component="button"
id="upload-button"
variant="outlined"
startIcon={<AddIcon />}
sx={{ p: 1.8 }}
>
Create
</Button>
</Link>
<Button
component="button"
id="upload-button"
startIcon={<AddIcon />}
sx={{ p: 1.8, color: '#FFFFFF', backgroundColor: '#19BBBB', width: '125px', height: '48px' }}
onClick={handleAddData}
>
Create
</Button>
</Stack>
<Card>
{/* The Main Table */}
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
<TableHead>
<TableRow>
<TableCell style={headStyle} align="left" />
<TableCell style={headStyle} align="left">
ID
<TableCell sx={{width: '10%'}} align="center">
<Typography variant='subtitle2'>ID</Typography>
</TableCell>
<TableCell style={headStyle} align="left">
Code
<TableCell sx={{width: '10%'}} align="left">
<Typography variant='subtitle2'>Code</Typography>
</TableCell>
<TableCell style={headStyle} align="left">
Name
<TableCell sx={{width: '20%'}} align="left">
<Typography variant='subtitle2'>Name</Typography>
</TableCell>
<TableCell style={headStyle} align="left">
Description
<TableCell sx={{width: '50%'}} align="left">
<Typography variant='subtitle2'>Description</Typography>
</TableCell>
<TableCell sx={{width: '10%'}} align="left">
</TableCell>
</TableRow>
</TableBody>
</TableHead>
{dataTableIsLoading ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
<TableCell colSpan={4} align="center">
Loading
</TableCell>
</TableRow>
@@ -268,7 +333,7 @@ export default function PlanList() {
) : dataTableData.data.length == 0 ? (
<TableBody>
<TableRow>
<TableCell colSpan={8} align="center">
<TableCell colSpan={4} align="center">
No Data
</TableCell>
</TableRow>
@@ -284,6 +349,101 @@ export default function PlanList() {
</TableContainer>
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange} />
</Card>
{/* Dialog */}
<Dialog open={openDialog} onClose={handleCloseDialog} fullWidth={true}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" alignItems='center' spacing={1}>
{idField ? (
<>
<Typography variant="h6">Edit Data</Typography>
</>
): (
<>
<Typography variant="h6">Create Data</Typography>
</>
)}
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialog}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2} sx={{marginTop: 2}}>
<TextField
label="Code"
required
value={codeField ? codeField : ''}
onChange={(e) =>{
setCodeField(e.target.value);
setCodeFieldError(e.target.value.trim() === '' ? 'This field is required' : '');
}}
fullWidth
inputProps={{ maxLength: 50 }}
error={!!codeFieldError}
helperText={codeFieldError}
/>
<TextField
label="Name"
required
value={nameField ? nameField : ''}
onChange={(e) =>{
setNameField(e.target.value);
setNameFieldError(e.target.value.trim() === '' ? 'This field is required' : '');
}}
fullWidth
inputProps={{ maxLength: 50 }}
error={!!nameFieldError}
helperText={nameFieldError}
/>
<TextField
label="Description"
value={descriptionField ? descriptionField : ''}
onChange={(e) =>{
setDescriptionField(e.target.value);
}}
fullWidth
inputProps={{ maxLength: 255 }}
/>
</Stack>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseDialog}>Cancel</Button>
<Button onClick={handleSaveData} variant="contained" color="primary" disabled={!isRequiredFieldsFilled()}>Save</Button>
</DialogActions>
</Dialog>
{/* Dialog Update Status */}
<Dialog open={openDialogStatus} onClose={handleCloseDialogStatus} fullWidth={true}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" alignItems='center' spacing={1}>
<Typography variant="h6">Update Status</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogStatus}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2} padding={2}>
<Typography variant='body1'>Are you sure to inactive this division ?</Typography>
<Card>
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Code</Typography>
<Typography variant='subtitle2' sx={{width: '70%'}}>{codeField}</Typography>
</Stack>
</Card>
</Stack>
</DialogContent>
<DialogActions>
<Button onClick={handleCloseDialogStatus}>Cancel</Button>
<Button onClick={handleSaveDataStatus} variant="contained" color="error">Inactive</Button>
</DialogActions>
</Dialog>
</Stack>
);
}

View File

@@ -10,14 +10,19 @@ import { styled } from '@mui/material/styles';
import { LoadingButton } from '@mui/lab';
import {
Box,
Button,
Card,
FormControl,
FormControlLabel,
FormHelperText,
FormLabel,
Grid,
InputLabel,
Menu,
MenuItem,
OutlinedInput,
Radio,
RadioGroup,
Select,
SelectChangeEvent,
Stack,
@@ -44,6 +49,8 @@ import { fCurrency } from '../../utils/formatNumber';
import RHFAutocomplete from '../../components/hook-form/RHFAutocomplete';
import UploadImage from '../../components/UploadImage';
import { fPostFormat } from '@/utils/formatTime';
import AddIcon from '@mui/icons-material/Add';
import Link from '@/theme/overrides/Link';
const LabelStyle = styled(Typography)(({ theme }) => ({
...theme.typography.subtitle2,
@@ -117,12 +124,14 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
NewCorporateSchema = Yup.object().shape({
isEdited: Yup.boolean(),
name: Yup.string().required('Name is required'),
code: Yup.string()
.required('Corporate Code is required')
.test('unique-code', 'Code must be unique', async function (value) {
const existingCodes = await getExistingCodes();
return !existingCodes.includes(value);
}),
}),
// payor_id: Yup.string().required('Payor is required'),
active: Yup.boolean().required('Corporate Status is required'),
type: Yup.string().required('Type is required'),
welcome_message: Yup.string().required('Welcome Message is required'),
@@ -487,9 +496,10 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
<Card sx={{ p: 3 }}>
<Stack spacing={3}>
<Grid item xs={12}>
<Typography variant="h5">Corporate Profile</Typography>
<Typography variant="h5" color={'primary'}>Corporate Profile</Typography>
</Grid>
<Typography variant='subtitle1' color="#637381">Corporate Profile*</Typography>
<RHFSelect name="type" label="Type" placeholder="Type">
<option value="" />
{types.map((option, index) => (
@@ -516,10 +526,13 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
))}
</RHFSelect>
)}
<Typography variant='subtitle1' color="#637381">Corporate Code*</Typography>
<RHFTextField name="code" label="Corporate Code" disabled={isDisabled} />
<Typography variant='subtitle1' color="#637381">Corporate Name*</Typography>
<RHFTextField name="name" label="Corporate Name" disabled={isDisabled} />
<Typography variant='subtitle1' color="#637381">Payor ID*</Typography>
<RHFTextField name="payor_id" label="Payor ID" disabled={isDisabled} />
{isEdit && (
@@ -534,15 +547,15 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
)}
<Stack spacing={1}>
<Typography variant="subtitle2" sx={{ color: 'text.secondary' }}>
Welcome Message
<Typography variant="subtitle1" color="#637381">
Welcome Message *
</Typography>
<RHFEditor name="welcome_message" />
<RHFEditor name="welcome_message" placeholder="Akun anda telah terverifikasi"/>
</Stack>
<Stack spacing={1}>
<Typography variant="subtitle2" sx={{ color: 'text.secondary' }}>
Help Text
<Typography variant="subtitle1" color="#637381">
Help Text *
</Typography>
<RHFEditor name="help_text" />
</Stack>
@@ -614,27 +627,30 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
{/* <Card sx={{ p:3, mb:3, background: 'gray', color: 'white' }}><Typography>Policy Detail</Typography></Card> */}
<Card sx={{ p: 3 }}>
<Stack spacing={3} mt={2}>
<Typography variant="h5">Policy Detail</Typography>
<Typography variant="h5" color={'primary'}>Policy Detail</Typography>
<input type="hidden" name="policy_id" />
<Stack spacing={1}>
<Typography variant="subtitle2">Policy Number*</Typography>
<RHFTextField name="policy_code" label="Policy Number" />
{!currentCorporate?.id && (
<Typography variant="caption">Will be generated if empty</Typography>
)}
</Stack>
{/* <Typography>Minimal Deposit Policy Level</Typography> */}
<Stack direction="row" spacing={2}>
<Grid item xs={12} md={6}>
<RHFDatepicker name="policy_start" label="Start Date" />
<Typography variant="subtitle2" sx={{marginBottom: '10px'}}>Tanggal Mulai Kerjasama*</Typography>
<RHFDatepicker name="policy_start" label="Start Date *" />
</Grid>
<Grid item xs={12} md={6}>
<RHFDatepicker name="policy_end" label="End Date" />
<Typography variant="subtitle2" sx={{marginBottom: '10px'}}>Tanggal Akhir Kerjasama*</Typography>
<RHFDatepicker name="policy_end" label="End Date *" />
</Grid>
</Stack>
<Typography variant="subtitle2">Deposit Intial Fund*</Typography>
<RHFTextField
name="policy_total_premi"
label={'Deposit Intial Fund (' + fCurrency(values.policy_total_premi) + ')'}
@@ -643,7 +659,7 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="subtitle2" sx={{ color: 'text.secondary' }}>
<Typography variant="subtitle2">
Minimal Deposit Policy Level
</Typography>
</Grid>
@@ -667,7 +683,7 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="subtitle2" sx={{ color: 'text.secondary' }}>
<Typography variant="subtitle2">
Minimal Alert Level
</Typography>
</Grid>
@@ -691,7 +707,7 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="subtitle2" sx={{ color: 'text.secondary' }}>
<Typography variant="subtitle2">
Stop Service Level
</Typography>
</Grid>
@@ -716,16 +732,106 @@ export default function CorporateForm({ isEdit, currentCorporate }: Props) {
</Grid>
)}
<Grid item xs={12} md={4}>
<LoadingButton
type="submit"
variant="contained"
size="large"
fullWidth={true}
loading={isSubmitting}
>
{!isEdit ? 'Save New Corporate' : 'Save Corporate'}
</LoadingButton>
{/* Type contrack */}
{/* <Grid item xs={12} md={12}>
<Card sx={{ p: 3 }}>
<Stack direction="row" spacing={2}>
<Grid item xs={12} md={6}>
<Typography variant="h5" color="#19BBBB">Tipe Pembayaran</Typography>
</Grid>
<Grid container item xs={12} md={12} justifyContent="flex-end">
<Button
size='small'
variant="contained"
startIcon={<AddIcon sx={{ color: 'black', fontWeight: 'bold' }} />}
sx={{ p: 1.8, typography: 'subtitle2', backgroundColor: '#DFE3E8' }}
>
<Typography variant="subtitle2" color="black">Contract</Typography>
</Button>
</Grid>
</Stack>
<Grid item xs={12} md={12}>
<Typography variant="h5">#1</Typography>
</Grid>
<Stack direction="row" spacing={4} sx={{marginTop: '24px'}}>
<Grid item xs={12} md={6}>
<Typography variant="subtitle1" >Tanggal Mulai Kontrak*</Typography>
</Grid>
<Grid container item xs={12} md={6}>
<Typography variant="subtitle1">Tanggal Akhir Kontrak*</Typography>
</Grid>
</Stack>
<Stack direction="row" spacing={4} sx={{marginTop: '24px'}}>
<Grid item xs={12} md={6}>
<RHFDatepicker name="contract_start" label="Start Date" />
</Grid>
<Grid item xs={12} md={6}>
<RHFDatepicker name="contract_start" label="End Date" />
</Grid>
</Stack>
<Grid item xs={12} md={12} sx={{marginTop: '24px'}}>
<Typography variant="subtitle2">Tipe Pembayaran</Typography>
<RadioGroup
aria-labelledby="demo-radio-buttons-group-label"
defaultValue="female"
name="type_payment"
>
<FormControlLabel value="1" control={<Radio />} label="Deposit" />
<FormControlLabel value="2" control={<Radio />} label="Hutang" />
<FormControlLabel value="3" control={<Radio />} label="Penagihan Hutang" />
</RadioGroup>
</Grid>
<Stack direction="row" spacing={2}>
<Grid container item xs={12} md={12} justifyContent="flex-end">
<Button
size='small'
variant="contained"
startIcon={<AddIcon sx={{ color: 'black', fontWeight: 'bold' }} />}
sx={{ p: 1.8, typography: 'subtitle2', backgroundColor: '#FFFFFF', marginRight: '20px' }}
>
<Typography variant="subtitle2" color="black">Cancel</Typography>
</Button>
<LoadingButton
type="submit"
variant="contained"
size="small"
loading={isSubmitting}
>
<Typography variant="subtitle2" color="black">Save</Typography>
</LoadingButton>
</Grid>
</Stack>
</Card>
</Grid> */}
<Grid item xs={12} md={12} >
<Stack direction="row" alignItems="center" justifyContent="flex-end">
<Button
sx={{
margin: 1
}}
type="submit"
variant="contained"
size="large"
color='inherit'
onClick={() => navigate(`/corporates`)}
>
Cancel
</Button>
<LoadingButton
type="submit"
variant="contained"
size="large"
// fullWidth={true}
loading={isSubmitting}
>
Save
</LoadingButton>
</Stack>
</Grid>
</Grid>
</FormProvider>

View File

@@ -1,10 +1,10 @@
import { Card, Grid } from '@mui/material';
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 CorporateTabNavigations from '../CorporateTabNavigations';
import List from './List';
import List from './New/List';
import { useContext, useEffect, useState } from 'react';
import { ConfiguredCorporateContext } from '@/contexts/ConfiguredCorporateContext';
@@ -24,9 +24,9 @@ export default function CorporateFormularium() {
}, [configuredCorporateContext]);
return (
<Page title="Formularium">
<Page title="Corporate Formularium">
<HeaderBreadcrumbs
heading={'Formularium'}
heading={'Corporate Formularium'}
links={[
{
name: 'Corporates',
@@ -34,17 +34,17 @@ export default function CorporateFormularium() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Formularium',
href: '/corporate/' + corporate_id + '/formularium',
href: '/corporates/' + corporate_id + '/formulariums',
},
]}
/>
<Card>
<CorporateTabNavigations position={'formularium'} />
<CorporateTabNavigations position={'formulariums'} />
<List />
</Card>
</Page>

View File

@@ -0,0 +1,355 @@
// @mui
import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Input, Grid } from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add';
import UploadIcon from '@mui/icons-material/Upload';
import CancelIcon from '@mui/icons-material/Cancel';
// hooks
import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react';
import useSettings from '../../../hooks/useSettings';
import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
// components
import axios from '../../../utils/axios';
import { CorporatePlan } from '../../../@types/corporates';
import { LaravelPaginatedData } from '../../../@types/paginated-data';
import BasePagination from '../../../components/BasePagination';
import { enqueueSnackbar } from 'notistack';
export default function PlanList() {
const { themeStretch } = useSettings();
const { corporate_id } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate();
// Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = useState(true);
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
});
function SearchInput(props: any) {
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
// const filterForm = useRef();
const [searchText, setSearchText] = useState("");
const [searchStatus, setSearchStatus] = useState("active");
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? ''
setSearchText(newSearchText);
}
const handleStatusChange = (event: any) => {
const newSearchStatus = event.target.value ?? ''
console.log('changing to', newSearchStatus)
setSearchStatus(newSearchStatus)
// console.log(searchStatus);
const searchFilter = {
"search" : searchText,
"status" : newSearchStatus
};
props.onSearch(searchFilter);
}
const handleSubmit = (event?: any) => {
event?.preventDefault();
const searchFilter = {
"search" : searchText,
"status" : searchStatus
};
props.onSearch(searchFilter); // Trigger to Parent
}
useEffect(() => {
setSearchText(searchParams.get('search') ?? '');
setSearchStatus(searchParams.get('status') ?? searchStatus ?? 'active');
}, [searchParams])
return (
<form id="search-form" onSubmit={handleSubmit} style={{ width: '100%' }}>
<Grid container spacing={2}>
<Grid item xs={9}>
<TextField id="search-input" ref={searchInput} label="Search" variant="outlined" fullWidth onChange={handleSearchChange} value={searchText}/>
</Grid>
<Grid item xs={3}>
<Select
label="Status"
value={searchStatus}
onChange={handleStatusChange}
sx={{ width: '100%' }}
>
<MenuItem value="active">Active</MenuItem>
<MenuItem value="inactive">Inactive</MenuItem>
<MenuItem value="all">All</MenuItem>
</Select>
</Grid>
{/* ITS FUCKING MAGIC, SUBMIT BY ENTER WORKING IF THIS BUTTON IS AVAILABLE */}
<Button type='submit' variant="outlined" sx={{ p: 1.8, display: 'none' }}>Search</Button>
</Grid>
</form>
);
}
// Called on every row to map the data to the columns
function createData( plan: CorporatePlan ): CorporatePlan {
return {
...plan,
}
}
const handleInactiveAction = (formularium : any) => {
enqueueSnackbar('Fuck yuo'), { variant: 'error' };
axios
.put('/corporates/'+corporate_id+'/formulariums/'+formularium.id+'/activate')
.then(() => {
setDataTableData(
{
...dataTableData,
data: dataTableData.data.map((item: any) => {
if (item.id === formularium.id) {
return {
...item,
status: 'active'
}
}
return item;
})
})
})
.catch((error) => {
console.log(error)
enqueueSnackbar(error.data?.message ?? (error.message ?? 'Failed Processing Request'), { variant: 'error' });
})
}
const handleActiveAction = (formularium : any) => {
axios
.put('/corporates/'+corporate_id+'/formulariums/'+formularium.id+'/deactivate')
.then(() => {
setDataTableData(
{
...dataTableData,
data: dataTableData.data.map((item: any) => {
if (item.id === formularium.id) {
return {
...item,
status: 'inactive'
}
}
return item;
})
})
})
.catch((error) => {
enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' });
})
}
// Generate the every row of the table
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.atc_code}</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">{row.category_name}</TableCell>
<TableCell align="left">{row.uom}</TableCell>
{/* <TableCell align="left">{row.items_count}</TableCell> */}
<TableCell align="right">{( row.status == 'active' ?
<Button variant="outlined" onClick={() => { handleActiveAction(row) }} color="success" size="small">Active</Button> :
<Button variant="outlined" onClick={() => { handleInactiveAction(row) }} color="error" size="small">Tidak Digunakan</Button>
)}</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell />
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ pb: 2 }}>
<Typography sx={{ fontWeight: '600', mb: 1 }}>Detail</Typography>
<Grid container sx={{ pb: 2, mb: 2, borderBottom: 1 }}>
<Grid item xs={6}>
<Grid container>
<Grid item xs={4}>
Description
</Grid>
<Grid item xs={8}>
: {row.description ?? '-'}
</Grid>
<Grid item xs={4}>
General Indication
</Grid>
<Grid item xs={8}>
: {row.general_indication ?? '-'}
</Grid>
<Grid item xs={4}>
Composition
</Grid>
<Grid item xs={8}>
: {row.composition ?? '-'}
</Grid>
</Grid>
</Grid>
<Grid item xs={6}>
<Grid container>
<Grid item xs={4}>
Kategori Obat
</Grid>
<Grid item xs={8}>
: {row.kategori_obat ?? '-'}
</Grid>
<Grid item xs={4}>
BPOM Registration
</Grid>
<Grid item xs={8}>
: {row.bpom_registration ?? '-'}
</Grid>
<Grid item xs={4}>
Classifications
</Grid>
<Grid item xs={8}>
: {row.classifications ?? '-'}
</Grid>
<Grid item xs={4}>
Cat For
</Grid>
<Grid item xs={8}>
: {row.cat_for ?? '-'}
</Grid>
<Grid item xs={4}>
Class
</Grid>
<Grid item xs={8}>
: {row.class ?? '-'}
</Grid>
<Grid item xs={4}>
Manufacturer
</Grid>
<Grid item xs={8}>
: {row.manufacturer ?? '-'}
</Grid>
</Grid>
</Grid>
</Grid>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
const loadDataTableData = async (appliedFilter : any | null = null) => {
setDataTableLoading(true);
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
const response = await axios.get('/corporates/'+corporate_id+'/formulariums', { params: filter });
// console.log(response.data);
setDataTableLoading(false);
setDataTableData(response.data);
}
const headStyle = {
fontWeight: 'bold',
};
const applyFilter = async (searchFilter: any) => {
await loadDataTableData(searchFilter);
setSearchParams(searchFilter);
}
const handlePageChange = (event : ChangeEvent, value: number) => {
const filter = Object.fromEntries([...searchParams.entries(), ["page", value]]);
loadDataTableData(filter);
setSearchParams(filter);
}
useEffect(() => {
loadDataTableData();
}, [])
return (
<Stack>
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter}/>
</Stack>
<Card>
{/* The Main Table */}
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left" />
<TableCell style={headStyle} align="left">Code</TableCell>
<TableCell style={headStyle} align="left">ATC Code</TableCell>
<TableCell style={headStyle} align="left">Name</TableCell>
<TableCell style={headStyle} align="left">Category Name</TableCell>
<TableCell style={headStyle} align="left">UOM</TableCell>
<TableCell style={headStyle} align="center">Status</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.code} row={row} />
))}
</TableBody>
)
)}
</Table>
</TableContainer>
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange}/>
</Card>
</Stack>
);
}

View File

@@ -1,5 +1,5 @@
// @mui
import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Input, Grid } from '@mui/material';
import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Grid, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Pagination } from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add';
@@ -8,23 +8,307 @@ import CancelIcon from '@mui/icons-material/Cancel';
// hooks
import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react';
import useSettings from '../../../hooks/useSettings';
import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
// components
import axios from '../../../utils/axios';
import { CorporatePlan } from '../../../@types/corporates';
import { LaravelPaginatedData } from '../../../@types/paginated-data';
import { Icd } from '../../../@types/diagnosis';
import BasePagination from '../../../components/BasePagination';
import { enqueueSnackbar } from 'notistack';
export default function PlanList() {
export default function List() {
const navigate = useNavigate();
const { themeStretch } = useSettings();
const { corporate_id } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate();
const [importResult, setImportResult] = useState(null);
function SearchInput(props: any) {
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
const [searchText, setSearchText] = useState("");
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? ''
setSearchText(newSearchText);
}
const handleSearchSubmit = (event: any) => {
event.preventDefault();
props.onSearch(searchText); // Trigger to Parent
}
useEffect(() => { // Trigger First Search
setSearchText(searchParams.get('search') ?? '');
}, [searchParams])
return (
<form onSubmit={handleSearchSubmit} style={{ width: '100%' }}>
<TextField id="search-input" ref={searchInput} label="Search" variant="outlined" fullWidth onChange={handleSearchChange} value={searchText}/>
</form>
);
}
function ImportForm(props: any) {
// IMPORT
// Create Button Menu
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const createMenu = Boolean(anchorEl);
const importForm = useRef<HTMLInputElement>(null)
const [currentImportFileName, setCurrentImportFileName] = useState(null)
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleImportButton = () => {
if (importForm?.current) {
handleClose();
importForm.current ? importForm.current.click() : console.log('No File selected');
} else {
alert('No file selected')
}
}
const handleCancelImportButton = () => {
importForm.current.value = "";
importForm.current.dispatchEvent(new Event("change", { bubbles: true }));
}
const 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 handleImportChange = (event: any) => {
if (event.target.files[0]) {
setCurrentImportFileName(event.target.files[0].name)
} else {
setCurrentImportFileName(null);
}
}
const handleFormulariumList = async (appliedFilter = null) => {
axios.get(`corporates/${corporate_id}/formulariums/list`).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();
enqueueSnackbar('Download Success', { variant: 'succes' })
})
.catch(response => {
enqueueSnackbar('Looks like something went wrong. Please check your data and try again. ' + response.message, { variant: 'error' })
})
;
}
const handleUpload = () => {
if (importForm.current?.files.length) {
const formData = new FormData();
formData.append("file", importForm.current?.files[0])
axios.post(`corporates/${corporate_id}/formulariums/import`, formData )
.then(response => {
handleCancelImportButton();
loadDataTableData();
setImportResult(response.data)
// alert('Succesfully read '+ response.data.total_successed_row + ' with ' + response.data.total_failed_row + ' failed rows');
})
.catch(response => {
enqueueSnackbar('Looks like something went wrong. Please check your data and try again. ' + response.message, { variant: 'error' })
})
} else {
enqueueSnackbar('No File Selected', { variant: 'warning' })
}
}
return (
<div>
<input type='file' id='file' ref={importForm} style={{ display: 'none' }} onChange={handleImportChange} accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain" />
{( !currentImportFileName && <Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter}/>
{/* <h1>kjasndkjandskjasndkjansdkjansd</h1> */}
<Button
id="import-button"
variant='outlined'
startIcon={<AddIcon />} sx={{ p: 1.8 }}
aria-controls={createMenu ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={createMenu ? 'true' : undefined}
onClick={handleClick}
>
Import
</Button>
<Menu
id="import-button"
anchorEl={anchorEl}
open={createMenu}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
{/* <MenuItem onClick={() => {navigate(`/master/formularium/create/${formularium_template_id}`)} }>Create</MenuItem> */}
<MenuItem onClick={handleImportButton}>Import</MenuItem>
<MenuItem onClick={() => {handleGetTemplate('master-formularium-corporate')}}>Download Template</MenuItem>
<MenuItem onClick={handleFormulariumList}>Download Formularium</MenuItem>
</Menu>
</Stack>
)}
{( currentImportFileName && <Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<ButtonGroup variant="outlined" aria-label="outlined button group" fullWidth>
<Button onClick={handleImportButton} fullWidth>{currentImportFileName ?? "No File Selected"}</Button>
<Button onClick={handleCancelImportButton} size="small" fullWidth={false} sx={{ p: 1.8 }}><CancelIcon color="error"/></Button>
</ButtonGroup>
<Button
id="upload-button"
variant='outlined'
startIcon={<UploadIcon />} sx={{ p: 1.8 }}
onClick={handleUpload}
>
Upload
</Button>
</Stack>
)}
{( importResult &&
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
<Box sx={{ color: "text.secondary" }}>Last Import Result Report : <a href={importResult.result_file?.url ?? "#"}>{importResult.result_file?.name ?? "-"}</a></Box>
</Stack>
)}
</div>
);
}
// Called on every row to map the data to the columns
function createData( icd: Icd ): Icd {
return {
...icd,
}
}
// Generate the every row of the table
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.atc_code}</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">{row.category_name}</TableCell>
<TableCell align="left">{row.uom}</TableCell>
<TableCell align="left">{row.active}</TableCell>
{/* <TableCell align="right"><Button variant="outlined" color="success" size="small">Active</Button></TableCell>
<TableCell align="right"><Button variant="outlined" color="error" size="small">Disable</Button></TableCell> */}
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell />
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ pb: 2 }}>
<Typography sx={{ fontWeight: '600', mb: 1 }}>Detail</Typography>
<Grid container sx={{ pb: 2, mb: 2, borderBottom: 1 }}>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Description
</Grid>
<Grid item xs={6}>
: {row.description ?? '-'}
</Grid>
<Grid item xs={6}>
General Indication
</Grid>
<Grid item xs={6}>
: {row.general_indication ?? '-'}
</Grid>
<Grid item xs={6}>
Composition
</Grid>
<Grid item xs={6}>
: {row.composition ?? '-'}
</Grid>
</Grid>
</Grid>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Kategori Obat
</Grid>
<Grid item xs={6}>
: {row.kategori_obat ?? '-'}
</Grid>
<Grid item xs={6}>
BPOM Registration
</Grid>
<Grid item xs={6}>
: {row.bpom_registration ?? '-'}
</Grid>
<Grid item xs={6}>
Classifications
</Grid>
<Grid item xs={6}>
: {row.classifications ?? '-'}
</Grid>
<Grid item xs={6}>
Cat For
</Grid>
<Grid item xs={6}>
: {row.cat_for ?? '-'}
</Grid>
<Grid item xs={6}>
Class
</Grid>
<Grid item xs={6}>
: {row.class ?? '-'}
</Grid>
<Grid item xs={6}>
Manufacturer
</Grid>
<Grid item xs={6}>
: {row.manufacturer ?? '-'}
</Grid>
</Grid>
</Grid>
</Grid>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
// 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: [],
@@ -39,169 +323,12 @@ export default function PlanList() {
to: 0,
total: 0
});
function SearchInput(props: any) {
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
// const filterForm = useRef();
const [searchText, setSearchText] = useState("");
const [searchStatus, setSearchStatus] = useState("active");
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? ''
setSearchText(newSearchText);
}
const handleStatusChange = (event: any) => {
const newSearchStatus = event.target.value ?? ''
console.log('changing to', newSearchStatus)
setSearchStatus(newSearchStatus)
// console.log(searchStatus);
const searchFilter = {
"search" : searchText,
"status" : newSearchStatus
};
props.onSearch(searchFilter);
}
const handleSubmit = (event?: any) => {
event?.preventDefault();
const searchFilter = {
"search" : searchText,
"status" : searchStatus
};
props.onSearch(searchFilter); // Trigger to Parent
}
useEffect(() => {
setSearchText(searchParams.get('search') ?? '');
setSearchStatus(searchParams.get('status') ?? searchStatus ?? 'active');
}, [searchParams])
return (
<form id="search-form" onSubmit={handleSubmit} style={{ width: '100%' }}>
<Grid container spacing={2}>
<Grid item xs={9}>
<TextField id="search-input" ref={searchInput} label="Search" variant="outlined" fullWidth onChange={handleSearchChange} value={searchText}/>
</Grid>
<Grid item xs={3}>
<Select
label="Status"
value={searchStatus}
onChange={handleStatusChange}
sx={{ width: '100%' }}
>
<MenuItem value="active">Active</MenuItem>
<MenuItem value="inactive">Inactive</MenuItem>
<MenuItem value="all">All</MenuItem>
</Select>
</Grid>
{/* ITS FUCKING MAGIC, SUBMIT BY ENTER WORKING IF THIS BUTTON IS AVAILABLE */}
<Button type='submit' variant="outlined" sx={{ p: 1.8, display: 'none' }}>Search</Button>
</Grid>
</form>
);
}
// Called on every row to map the data to the columns
function createData( plan: CorporatePlan ): CorporatePlan {
return {
...plan,
}
}
const handleInactiveAction = (formularium : any) => {
enqueueSnackbar('Fuck yuo'), { variant: 'error' };
axios
.put('/corporates/'+corporate_id+'/formulariums/'+formularium.id+'/activate')
.then(() => {
setDataTableData(
{
...dataTableData,
data: dataTableData.data.map((item: any) => {
if (item.id === formularium.id) {
return {
...item,
status: 'active'
}
}
return item;
})
})
})
.catch((error) => {
console.log(error)
enqueueSnackbar(error.data?.message ?? (error.message ?? 'Failed Processing Request'), { variant: 'error' });
})
}
const handleActiveAction = (formularium : any) => {
axios
.put('/corporates/'+corporate_id+'/formulariums/'+formularium.id+'/deactivate')
.then(() => {
setDataTableData(
{
...dataTableData,
data: dataTableData.data.map((item: any) => {
if (item.id === formularium.id) {
return {
...item,
status: 'inactive'
}
}
return item;
})
})
})
.catch((error) => {
enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' });
})
}
// Generate the every row of the table
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell align="left">{row.id}</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">{row.items_count}</TableCell>
<TableCell align="right">{( row.status == 'active' ?
<Button variant="outlined" onClick={() => { handleActiveAction(row) }} color="success" size="small">Active</Button> :
<Button variant="outlined" onClick={() => { handleInactiveAction(row) }} color="error" size="small">Tidak Digunakan</Button>
)}</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={10}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ borderBottom: 1 }}>
<Typography variant="body2" gutterBottom component="div">
No Extra Data
</Typography>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
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('/corporates/'+corporate_id+'/formulariums', { params: filter });
const response = await axios.get(`corporates/${corporate_id}/formulariums`, { params: filter });
// console.log(response.data);
setDataTableLoading(false);
@@ -212,9 +339,9 @@ export default function PlanList() {
fontWeight: 'bold',
};
const applyFilter = async (searchFilter: any) => {
await loadDataTableData(searchFilter);
setSearchParams(searchFilter);
const applyFilter = async (searchFilter: string) => {
await loadDataTableData({ "search" : searchFilter });
setSearchParams({ "search" : searchFilter });
}
const handlePageChange = (event : ChangeEvent, value: number) => {
@@ -223,28 +350,26 @@ export default function PlanList() {
setSearchParams(filter);
}
useEffect(() => {
loadDataTableData();
}, [])
return (
<Stack>
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter}/>
</Stack>
<ImportForm />
<Card>
{/* The Main Table */}
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
<TableRow>
<TableCell style={headStyle} align="left" width={50}>#</TableCell>
<TableCell style={headStyle} align="left" />
<TableCell style={headStyle} align="left">Code</TableCell>
<TableCell style={headStyle} align="left">ATC Code</TableCell>
<TableCell style={headStyle} align="left">Name</TableCell>
<TableCell style={headStyle} align="left">Total Item</TableCell>
<TableCell style={headStyle} align="right">Status</TableCell>
<TableCell style={headStyle} align="left">Category Name</TableCell>
<TableCell style={headStyle} align="left">UOM</TableCell>
<TableCell style={headStyle} align="left">Status</TableCell>
</TableRow>
</TableBody>
{dataTableIsLoading ?
@@ -265,13 +390,14 @@ export default function PlanList() {
) : (
<TableBody>
{dataTableData.data.map(row => (
<Row key={row.code} row={row} />
<Row key={row.id} row={row} />
))}
</TableBody>
)
)}
</Table>
</TableContainer>
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange}/>
</Card>
</Stack>

View File

@@ -0,0 +1,229 @@
// @mui
import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Grid, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Pagination } from '@mui/material';
import { LoadingButton } from "@mui/lab";
import { CachedOutlined, FindInPageOutlined } from '@mui/icons-material';
// hooks
import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react';
import useSettings from '../../../../hooks/useSettings';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
// components
import axios from '../../../../utils/axios';
import Label from '@/components/Label';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import InfoDetail from "./InfoDetail";
import { DetailCorpFormularium } from "./Types";
import DialogUpdateStatus from '@/components/DialogUpdateStatus'
import { RHFSelect, FormProvider } from '@/components/hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { useForm } from 'react-hook-form';
import * as Yup from 'yup';
import { CorporateFormulariumList } from "./Types";
type ParamsDetail = {
props: DetailCorpFormularium,
formularium: CorporateFormulariumList,
}
export default function CategoryDetail({props, formularium} : ParamsDetail) {
type FormValuesProps = {
value: string;
active: boolean;
};
const { corporate_id } = useParams();
const [open, setOpen] = React.useState(false);
const [isDialogOpen, setDialogOpen] = useState(false)
let titles = {
name: 'Update Status',
icon: '-'
}
const [dataValue, setDataValue] = useState<CorporateFormulariumList | null>(null);
const [dataDescription, setDescriptionValue] = useState('');
const [url, setUrl] = useState('');
const handleActivate = (isOpen: boolean, dataValue: CorporateFormulariumList) => {
console.log(dataValue)
setDialogOpen(isOpen)
setDataValue(dataValue)
setDescriptionValue('Are you sure to inactive this formularium ?')
setUrl(url)
};
const NewCorporateSchema = Yup.object().shape({
reason: Yup.string().required('Reason Edit is required'),
});
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema)
})
const {
reset,
handleSubmit,
formState: { isSubmitting },
} = methods;
const onSubmit = async (row : any) => {
try {
handleUpdate(dataValue?.active, dataValue?.id, row.reason)
} catch (error: any) {
console.log('data gagal');
}
const ascent = document?.querySelector('ascent');
if (ascent != null) {
ascent.innerHTML = '';
}
};
const handleUpdate = (active: string, id: number, reason: string) => {
if (active == "Active") {
active = "0"
} else {
active = "1"
}
axios
.put(`/corporates/${corporate_id}/formulariums-update-status/${id}`, {
active: active,
})
.then((res) => {
console.log(res.data.message)
window.location.reload();
});
}
const getContent = () => (
<>
<Stack paddingX={2} paddingY={2}>
<Typography variant='subtitle1'>Are you sure to {dataValue?.active === "Active" ? 'inactive' : 'active'} this Formularium ?</Typography>
<Stack>
<Card>
<Grid container paddingX={2} paddingY={2}>
<Grid item xs={5} md={5}>
<Typography variant='inherit'>Formularium Name</Typography>
</Grid>
<Grid item xs={7}>
<Typography variant='subtitle1'>{dataValue?.category}</Typography>
</Grid>
</Grid>
</Card>
<Typography marginTop={5} marginBottom={3} variant='subtitle1'>Reason for Update</Typography>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<RHFSelect
name='reason'
label="Reason for update"
>
<option value=""></option>
<option value="Agreement changed">Agreement changed</option>
<option value="Endorsement">Endorsement</option>
<option value="Renewal">Renewal</option>
<option value="Wrong Setting">Wrong Setting</option>
</RHFSelect>
<Stack
alignItems="center"
justifyContent="flex-end"
direction={{xs: 'column', md: 'row'}}
spacing={2}
marginTop={5}
>
<Stack direction="row" spacing={1}>
<Button
sx={{ boxShadow: 'none'}}
variant='outlined'
size='medium'
fullWidth={true}
onClick={() => setDialogOpen(false)}
>
Cancel
</Button>
{dataValue?.active == "Active" ?
<LoadingButton
sx={{boxShadow: '0px 2px 4px rgba(0,0,0,0.1)'}}
type='submit'
variant='contained'
size='medium'
fullWidth={true}
loading={isSubmitting}
color='error'
>
Inactive
</LoadingButton> :
<LoadingButton
sx={{boxShadow: '0px 2px 4px rgba(0,0,0,0.1)'}}
type='submit'
variant='contained'
size='medium'
fullWidth={true}
loading={isSubmitting}
color='success'
>
Active
</LoadingButton>
}
</Stack>
</Stack>
</FormProvider>
</Stack>
</Stack>
</>
);
return (
<React.Fragment>
<TableBody>
<TableRow>
<TableCell align='left' width={50}/>
<TableCell align='left'>{props.code}</TableCell>
<TableCell align='left'>{props.name}</TableCell>
<TableCell align='left' width={100}>
{formularium.active == "Active" ? (
<Label color='success'>Active</Label>
): (
<Label color='error'>Inactive</Label>
)}
</TableCell>
<TableCell align='center' width={100}>
<TableMoreMenu actions={
<>
<MenuItem onClick={() => setOpen(!open)}>
<FindInPageOutlined />
Detail
</MenuItem>
<MenuItem onClick={() => handleActivate(true, {
id: formularium.id,
formulaurium_category_id: formularium.formulaurium_category_id,
category: formularium.category,
description: formularium.description,
active: formularium.active
})
}>
<CachedOutlined />
Update Status
</MenuItem>
</>
} />
</TableCell>
</TableRow>
</TableBody>
<TableBody>
<TableRow>
<TableCell colSpan={5} style={{paddingBottom:0, paddingTop:0}}>
<Collapse in={open} timeout='auto' unmountOnExit>
<InfoDetail {...props}/>
</Collapse>
</TableCell>
</TableRow>
</TableBody>
<DialogUpdateStatus
openDialog={isDialogOpen}
setOpenDialog={setDialogOpen}
description={dataDescription}
title={titles}
data={dataValue}
content={getContent()}
/>
</React.Fragment>
)
}

View File

@@ -0,0 +1,82 @@
import TableMoreMenu from "@/components/table/TableMoreMenu"
import { FindInPageOutlined } from "@mui/icons-material"
import HistoryIcon from '@mui/icons-material/History';
import { Collapse, Grid, MenuItem, Paper, Table, TableCell, TableContainer, TableHead, TableRow } from "@mui/material"
import React, { useEffect } from "react";
import CategoryDetail from "./CategoryDetail";
import { CorporateFormulariumList, DetailCorpFormularium, RespDetailFormularium } from "./Types";
import axios from "@/utils/axios";
import { useNavigate, useParams } from "react-router-dom";
const CategoryRow: React.FC<CorporateFormulariumList> = (props) => {
const [open, setOpen] = React.useState(false);
const { corporate_id } = useParams();
const navigate = useNavigate();
const [dataTableIsLoading, setDataTableLoading] = React.useState(true);
const [dataTableData, setDataTableData] = React.useState<RespDetailFormularium | null>(null)
const [dataRow, setDataRow] = React.useState<DetailCorpFormularium[] | null>(null)
const loadDataTableData = async (formulaurium_category_id: number) => {
setDataTableLoading(true);
const resp = await axios.get(`/corporates/${corporate_id}/formulariums/${formulaurium_category_id}`);
console.log(resp.data);
setDataTableLoading(false);
setDataTableData(resp.data);
setDataRow(resp.data.data);
}
return (
<React.Fragment>
<TableRow>
<TableCell align="left" width={50}/>
<TableCell align="left">{props.category}</TableCell>
<TableCell align="left">{props.description}</TableCell>
<TableCell align="center" width={100}>
<TableMoreMenu actions = {
<>
<MenuItem onClick={async () => { loadDataTableData(props.formulaurium_category_id); setOpen(!open)}}>
<FindInPageOutlined />
Detail
</MenuItem>
<MenuItem onClick={() => navigate(`/corporates/${corporate_id}/formulariums/${props.id}/history`)}>
<HistoryIcon />
History
</MenuItem>
</>
} />
</TableCell>
</TableRow>
<TableRow >
<TableCell colSpan={5} align="center" style={{paddingBottom: 0, paddingTop: 0}}>
<Collapse in={open} timeout='auto' unmountOnExit>
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell align="left" width={50} />
<TableCell align="left">Code</TableCell>
<TableCell align="left">Name</TableCell>
<TableCell align="left" width={100}>Status</TableCell>
<TableCell align="center" width={100}></TableCell>
</TableRow>
</TableHead>
{dataTableIsLoading ? (
<TableCell colSpan={5} align="center">Loading</TableCell>
) : dataTableData?.data.length == 0 ? (
<TableCell colSpan={5} align="center">No Data</TableCell>
) : (
dataRow?.map(item => (
<CategoryDetail props={item} formularium={props}/>
))
)}
</Table>
</TableContainer>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
)
}
export default CategoryRow

View File

@@ -0,0 +1,54 @@
import { Card, Grid } 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 CorporateTabNavigations from '../../CorporateTabNavigations';
import { useContext, useEffect, useState } from 'react';
import { ConfiguredCorporateContext } from '@/contexts/ConfiguredCorporateContext';
import { Corporate } from '@/@types/corporates';
import CorporateFormulariumCreateForm from "./Form"
export default function CorporateFormulariumCreate() {
const { themeStretch } = useSettings();
const { corporate_id } = useParams();
const [corporate, setCorporate] = useState<Corporate | null>();
const configuredCorporateContext = useContext(ConfiguredCorporateContext);
useEffect(() => {
setCorporate(configuredCorporateContext.currentCorporate);
}, [configuredCorporateContext]);
return(
<Page title='Create Formularium'>
<HeaderBreadcrumbs
heading={'Create Formularium'}
links={[
{
name: 'Corporates',
href: '/corporates'
},
{
name: corporate?.name ?? "-",
href: '/corporates/' + corporate_id,
},
{
name: 'Formularium',
href: '/corporates/' + corporate_id + '/formulariums',
},
{
name: 'Create Formularium',
href: '/corporates/' + corporate_id + '/formulariums/create',
},
]}
/>
<CorporateFormulariumCreateForm />
</Page>
)
}

View File

@@ -0,0 +1,130 @@
import * as Yup from 'yup';
import { useForm } from "react-hook-form";
import { FormProvider, RHFSelect } from "../../../../components/hook-form";
import { Button, Card, Grid, Typography } from "@mui/material";
import { Stack, fontWeight } from "@mui/system";
import { useNavigate, useParams } from "react-router";
import React, { useEffect, useMemo } from "react";
import { DetailCorpFormularium } from "./Types";
import axios from "@/utils/axios";
import { LoadingButton } from "@mui/lab";
import { enqueueSnackbar } from "notistack";
import { yupResolver } from '@hookform/resolvers/yup';
export default function CorporateFormulariumCreateForm() {
const navigate = useNavigate();
const { corporate_id } = useParams();
const NewCorporaeFormulariumSchema = Yup.object().shape({
id: Yup.string().required('required'),
});
const methods = useForm({
resolver: yupResolver(NewCorporaeFormulariumSchema),
});
const onSubmit = async (data: any) => {
await axios
.post(`/corporates/${corporate_id}/formulariums`, data)
.then((res) => {
console.log(res.data.message)
enqueueSnackbar('Formularium created successfully', { variant: 'success'})
})
.then((res) => {
navigate(`/corporates/${corporate_id}/formulariums/`, { replace: true })
})
.catch(({ response }) => {
if (response.status === 422) {
for (const [key, value] of Object.entries(response.data.errors)) {
setError(key, {message: value[0]});
enqueueSnackbar(value[0] ?? 'Failed Processing Request', { variant: 'error' });
}
}
else {
enqueueSnackbar('Create Failed: '+ response.data.message, { variant: "error"})
}
});
};
const {
setError,
handleSubmit,
formState: { isSubmitting }
} = methods;
// Data Dummy
const options = ["Dummy 1", "Dummy 2", "Dummy 3"];
const [dataDropdownIsLoading, setDataDropdownLoading] = React.useState(true);
const [dataDropdownData, setDataDropdownData] = React.useState<DetailCorpFormularium[] | null>(null)
const loadDataDropdown = async () => {
setDataDropdownLoading(true);
const resp = await axios.get(`corporates/${corporate_id}/formulariums-create`);
console.log(resp.data);
setDataDropdownLoading(false);
setDataDropdownData(resp.data.data)
};
useEffect(() => {
loadDataDropdown();
}, [])
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Grid item xs={12}>
<Card sx={{p:2}}>
<Stack spacing={3}>
<Typography variant="h6">Formularium Name*</Typography>
<RHFSelect name="id" label="Category" placeholder="Category" fullWidth={true}>
<option value=""/>
{dataDropdownData?.map((item, index) => (
<option key={index} value={item.id}>
{item.name}
</option>
))}
</RHFSelect>
</Stack>
</Card>
<Stack direction="row" spacing={4} sx={{marginTop: '40px'}}>
<Grid container item xs={12} md={12} justifyContent="flex-end" sx={{p:2}}>
<Button
variant="outlined"
onClick={() => navigate(`/corporates/${corporate_id}/formulariums/`)}
sx={{
p: 1,
fontWeight: 'bold',
color: 'black',
outlineColor: 'black',
marginRight: '20px',
width: 100
}}
>
Cancel
</Button>
<LoadingButton
loading={isSubmitting}
type='submit'
variant="contained"
sx={{
p: 1,
fontWeight: 'bold',
backgroundColor: '#19BBBB',
color: '#FFF',
"&:hover":{
backgroundColor: '#19BBBB', color: '#FFF',
},
width: 100
}}
>
Create
</LoadingButton>
</Grid>
</Stack>
</Grid>
</FormProvider>
)
}

View File

@@ -0,0 +1,218 @@
// @mui
import {
Box,
Button,
Card,
Collapse,
Container,
FormControl,
Grid,
IconButton,
InputLabel,
MenuItem,
OutlinedInput,
Paper,
Select,
SelectChangeEvent,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Typography,
Badge,
Stack,
} from '@mui/material';
import * as React from 'react';
import { useParams } from 'react-router-dom';
import { styled } from '@mui/material/styles';
import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp';
import MuiAccordion, { AccordionProps } from '@mui/material/Accordion';
import { useContext, useEffect, useState } from 'react';
import MuiAccordionSummary, {
AccordionSummaryProps,
} from '@mui/material/AccordionSummary';
import useSettings from '../../../../hooks/useSettings';
import axios from '../../../../utils/axios';
import { ConfiguredCorporateContext } from '@/contexts/ConfiguredCorporateContext';
import MuiAccordionDetails from '@mui/material/AccordionDetails';
import HeaderBreadcrumbs from '../../../../components/HeaderBreadcrumbs';
import { Corporate } from '@/@types/corporates';
import { fDate, fDateTime } from '@/utils/formatTime';
const Accordion = styled((props: AccordionProps) => (
<MuiAccordion disableGutters elevation={0} square {...props} />
))(({ theme }) => ({
border: `1px solid ${theme.palette.divider}`,
'&:not(:last-child)': {
borderBottom: 0,
},
'7:before': {
display: 'none',
},
}));
const AccordionSummary = styled((props: AccordionSummaryProps) => (
<MuiAccordionSummary expandIcon={<ArrowForwardIosSharpIcon sx={{ fontSize: '0.9rem'}} />}
{...props}
/>
))(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark'
? 'rgba(255,255,255, .85)'
: 'rgba(0,0,0, .03)',
flexDirection: 'row-reverse',
'& .MuiAccordionSummary-expandIconWrapper.Mui-expanded' : {
transform: 'rotate(90deg)',
},
'& .MuiAccordionSummary-content': {
marginLeft: theme.spacing(1),
},
}));
const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
padding: theme.spacing(2),
borderTop: '1px solid rgba(0, 0, 0, .125)',
}));
export default function CustomizedAccordions() {
const [expanded, setExpanded] = React.useState<String | false>('panel1');
const handleChange = (panel: string) => (event: React.SyntheticEvent, newExpanded: boolean) => {
setExpanded(newExpanded ? panel : false);
};
const pageTitle = 'Corporate Formularium History'
const { id } = useParams();
const { corporate_id } = useParams();
const [corporate, setCorporate] = useState<Corporate | null>();
const [currentCorporate, setCurrentCorporate] = useState<Corporate>();
const configuredCorporateContext = useContext(ConfiguredCorporateContext);
useEffect(() => {
setCorporate(configuredCorporateContext.currentCorporate);
const model = 'App\\Models\\CorporateFormularium';
const url = `/audittrail/${id}?model=${model}`;
axios.get(url)
.then((res) => {
setCurrentCorporate(res.data);
})
.catch((error) => {
console.error('Terjadi kesalahan', error)
});
}, [configuredCorporateContext]);
return (
<div>
<HeaderBreadcrumbs
heading={pageTitle}
links={[
{
name: 'Corporates',
href: '/corporates',
},
{
name: corporate?.name ?? '-',
href: '/corporates/' + corporate_id,
},
{
name: 'Formularium',
href: '/corporates/' + corporate_id + '/formulariums',
},
{
name: 'History',
href: '/corporates/' + corporate_id + '/formulariums'
},
]}
/>
{currentCorporate?.data.map((item, index) => (
<Accordion
key={index}
expanded={expanded === `panel${index}`}
onChange={handleChange(`panel${index}`)}
>
<AccordionSummary
aria-controls={`panel${index}d-content`}
id={`panel${index}d-header`}
>
<Typography>{`Data has ${item.action} by ${item.user_id} on ${fDateTime(item.updated_at)}`}</Typography>
</AccordionSummary>
<AccordionDetails>
<TableContainer component={Paper}>
<Table aria-label='collapsible table'>
<TableHead>
<TableRow>
<TableCell align='center'>Field</TableCell>
<TableCell align='center'>Old Value</TableCell>
<TableCell align='center'>New Value</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Object.entries(item.old_values).map(([key, value]) => {
let renderedValue;
if (key === 'deleted_by' ||
key === 'deleted_at' ||
key === 'created_by' ||
key === 'created_at' ||
key === 'updated_by' ||
key === 'description'
) {
return null; // Melewati iterasi saat key adalah 'deleted_by'
}
switch (key) {
case 'welcome_message':
renderedValue = item.new_values[key].replace(/<[^>]*>/g, '');
value = value.replace(/<[^>]*>/g, '');
break;
case 'help_text':
renderedValue = item.new_values[key].replace(/<[^>]*>/g, '');
value = value.replace(/<[^>]*>/g, '');
break;
case 'active':
renderedValue = item.new_values[key] == 1 ? 'Active' : 'Inactive';
value = value == 1 ? 'Active' : 'Inactive';
break;
case 'created_at':
renderedValue = fDateTime(item.new_values[key]);
value = fDateTime(value);
break;
case 'updated_at':
renderedValue = fDateTime(item.new_values[key]);
value = fDateTime(value);
break;
case 'updated_at':
renderedValue = fDateTime(item.new_values[key]);
value = fDateTime(value);
break;
case 'delete_at':
renderedValue = fDateTime(item.new_values[key]);
value = fDateTime(value);
break;
default:
renderedValue = item.new_values[key];
break;
}
const field = key.charAt(0).toUpperCase() + key.slice(1);
if (value == renderedValue) {
return null
} else {
return (
<TableRow key={key} sx={{ '&:last-child td, &:last-child th': { border: 0 } }}>
<TableCell align="center">{`${field}`}</TableCell>
<TableCell align="center">{`${value}`}</TableCell>
<TableCell align="center">{renderedValue}</TableCell>
</TableRow>
);
}
})}
</TableBody>
</Table>
</TableContainer>
</AccordionDetails>
</Accordion>
))}
</div>
);
}

View File

@@ -0,0 +1,48 @@
import { Collapse, TableCell, TableRow, Card, Box, Grid, Typography } from "@mui/material";
import { DetailCorpFormularium } from "./Types";
export default function InfoDetail(props: DetailCorpFormularium) {
return(
<Card sx={{paddingX:2, paddingY:2, marginBottom:3}}>
<Box sx={{margin: 1, pb: 2}}>
<Grid container>
<Grid item>
<Typography sx={{ fontWeight: '600', mb: 1}}>Detail</Typography>
<Grid container>
<Grid item xs={2}>Description</Grid>
<Grid item xs={10}> : {props.description}</Grid>
<Grid item xs={2}>General Indication</Grid>
<Grid item xs={10}> : {props.general_indication}</Grid>
<Grid item xs={2}>Composition</Grid>
<Grid item xs={10}> : {props.composition}</Grid>
<Grid item xs={2}>Kategori Obat</Grid>
<Grid item xs={10}> : {props.kategori_obat}</Grid>
<Grid item xs={2}>BPOM Registration</Grid>
<Grid item xs={10}> : {props.bpom_registration}</Grid>
<Grid item xs={2}>Classification</Grid>
<Grid item xs={10}> : {props.classifications}</Grid>
<Grid item xs={2}>Cat For</Grid>
<Grid item xs={10}> : {props.cat_for}</Grid>
<Grid item xs={2}>Class</Grid>
<Grid item xs={10}> : {props.class}</Grid>
<Grid item xs={2}>Manufacturer</Grid>
<Grid item xs={10}> : {props.manufacturer}</Grid>
</Grid>
</Grid>
</Grid>
</Box>
</Card>
)
}

View File

@@ -0,0 +1,179 @@
// @mui
import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Grid, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup, Pagination } from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
// hooks
import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react';
import useSettings from '../../../../hooks/useSettings';
import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
// components
import axiosInstance from '../../../../utils/axios';
import axios from '../../../../utils/axios';
import { LaravelPaginatedData } from '../../../../@types/paginated-data';
import BasePagination from '../../../../components/BasePagination';
import CategoryRow from './CategoryRow'
import {CorporateFormulariumList} from "./Types";
export default function List() {
const navigate = useNavigate();
const { corporate_id } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const [dataRow, setDataRow] = useState<CorporateFormulariumList[] | null>(null);
// Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = React.useState(true);
const [dataTableData, setDataTableData] = React.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,
});
// Load Data
const loadDataTableData = async (appliedFilter: any | null = null) => {
setDataTableLoading(true);
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
const resp = await axios.get(`/corporates/${corporate_id}/formulariums`, {params: filter});
setDataTableLoading(false);
setDataTableData(resp.data);
setDataRow(resp.data.data);
}
const applyFilter = async (searchFilter: any) => {
await loadDataTableData({ search: searchFilter });
setSearchParams({ search: searchFilter });
console.log("here")
};
const handlePageChange = (event: ChangeEvent, value: number) => {
const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]);
loadDataTableData(filter);
setSearchParams(filter);
}
useEffect(() => {
loadDataTableData();
console.log(dataTableData);
console.log(dataRow);
}, []);
function SearchInput(props: any) {
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
const [searchText, setSearchText] = useState("");
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? ''
setSearchText(newSearchText);
}
const handleSearchSubmit = (event: any) => {
event.preventDefault();
props.onSearch(searchText); // Trigger to Parent
}
useEffect(() => { // Trigger First Search
setSearchText(searchParams.get('search') ?? '');
}, [searchParams])
return (
<form onSubmit={handleSearchSubmit} style={{ width: '100%' }}>
<TextField
id="search-input"
ref={searchInput}
label="Search"
variant="outlined"
fullWidth
onChange={handleSearchChange}
value={searchText}
/>
</form>
);
}
function SearchCreate(props: any) {
return (
<div>
<Stack direction={'row'} spacing={2} sx={{p:2}}>
<SearchInput onSearch={applyFilter}/>
<Button
id='create-button'
variant='contained'
startIcon={<AddIcon />}
onClick={() => navigate(`/corporates/${corporate_id}/formulariums/create`)}
sx={{
p: 1.8,
backgroundColor: '#19BBBB', color: '#FFF',
"&:hover":{
backgroundColor: '#19BBBB', color: '#FFF',
},
width: '200px'
}}
>
Create
</Button>
</Stack>
</div>
)
}
const headStyle = {
fontWeight: 'bold',
};
return(
<React.Fragment>
<Stack>
<SearchCreate />
<Card>
{/* The Main Table */}
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell align='left' width={50}></TableCell>
<TableCell style={headStyle} align="left">Formularium Name</TableCell>
<TableCell style={headStyle} align="left">Description</TableCell>
<TableCell style={headStyle} align="center" width={100}></TableCell>
</TableRow>
</TableHead>
{dataTableIsLoading ? (
<TableBody>
<TableRow>
<TableCell colSpan={4} align="center">
Loading
</TableCell>
</TableRow>
</TableBody>
) : dataTableData.data.length == 0 ? (
<TableBody>
<TableRow>
<TableCell colSpan={4} align="center">
No Data
</TableCell>
</TableRow>
</TableBody>
) : (
<TableBody>
{dataRow?.map(item => (
<CategoryRow {...item}/>
))}
</TableBody>
)}
</Table>
</TableContainer>
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange} />
</Card>
</Stack>
</React.Fragment>
)
}

View File

@@ -0,0 +1,57 @@
export type CorporateFormularium = {
current_page: number
data: CorporateFormulariumList[]
first_page_url: string
from: number
last_page: number
last_page_url: string
links: CorporateFormulariumPaginationLinks[]
next_page_url: any
per_page: number
total: number
}
export type CorporateFormulariumList = {
id: number
formulaurium_category_id: number
category: string
description: string
active: string
};
export type CorporateFormulariumPaginationLinks = {
url?: string
label: string
active: boolean
}
export type RespDetailFormularium = {
status: number
message: string
data: DetailCorpFormularium[]
}
export type DetailCorpFormularium = {
id: number
code: string
name: string
description: string
manufacturer: string
category_name: string
kategori_obat: string
uom: string
general_indication: string
composition: string
atc_code: string
class: string
bpom_registration: string
classifications: string
cat_for: string
created_at: string
updated_at: string
deleted_at: any
created_by: number
updated_by: number
deleted_by: any
formularium_template_id: number
}

View File

@@ -121,11 +121,11 @@ export default function CustomizedAccordions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Audittrail Corporate',
href: '/corporate/' + corporate_id + '/plans',
href: '/corporates/' + corporate_id + '/plans',
},
]}
/>
@@ -139,7 +139,7 @@ export default function CustomizedAccordions() {
aria-controls={`panel${index}d-content`}
id={`panel${index}d-header`}
>
<Typography>{`Data has ${item.action} by ${item.user_id} on ${fDateTime(item.updated_at)}`}</Typography>
<Typography variant='subtitle1'>{`Data has ${item.action} by ${item.user_id} on ${fDateTime(item.updated_at)}`}</Typography>
</AccordionSummary>
<AccordionDetails>
<TableHead>
@@ -157,12 +157,13 @@ export default function CustomizedAccordions() {
}
switch (key) {
case 'welcome_message':
renderedValue = item.new_values[key].replace(/<[^>]*>/g, '');
value = value.replace(/<[^>]*>/g, '');
// renderedValue = item.new_values[key].replace(/<[^>]*>/g, '');
renderedValue = item.new_values[key];
value = value;
break;
case 'help_text':
renderedValue = item.new_values[key].replace(/<[^>]*>/g, '');
value = value.replace(/<[^>]*>/g, '');
renderedValue = item.new_values[key];
value = value;
break;
case 'active':
renderedValue = item.new_values[key] == 1 ? 'Active' : 'Inactive';

View File

@@ -1,64 +1,63 @@
import { useNavigate, useParams } from "react-router-dom";
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
import Page from "../../../components/Page";
import useSettings from "../../../hooks/useSettings";
import { useEffect, useMemo, useState } from 'react';
import axios from '../../../utils/axios';
import { useSnackbar } from 'notistack';
import CorporatePlanForm from './Form';
import { CorporatePlan } from '../../../@types/corporates';
import { useContext, useEffect, useMemo, useState } from 'react';
import CorporateHospitalForm from './Form';
import { Hospital } from '../../../@types/corporates';
import { Corporate } from "@/@types/corporates";
import { ConfiguredCorporateContext } from "@/contexts/ConfiguredCorporateContext";
import { Stack, Typography } from '@mui/material';
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
export default function PlanCreate() {
const { themeStretch } = useSettings();
export default function HospitalCreate() {
const { corporate_id, id } = useParams();
const [ currentCorporatePlan, setCurrentCorporatePlan ] = useState<CorporatePlan>();
const navigate = useNavigate();
const [corporate, setCorporate] = useState<Corporate|null>();
const configuredCorporateContext = useContext(ConfiguredCorporateContext);
const isEdit = !!id;
useEffect(() => {
if (isEdit) {
axios.get('/corporates/'+corporate_id+'/divisions/'+id+'/edit')
.then((res) => {
setCurrentCorporatePlan(res.data);
})
.catch((err) => {
if (err.response.status === 404) {
navigate('/404');
}
})
}
}, [corporate_id, id]);
setCorporate(configuredCorporateContext.currentCorporate);
}, [corporate_id, id, configuredCorporateContext]);
return (
<Page title="Create Corporate Division">
<HeaderBreadcrumbs
heading={'Create Corporate Division'}
links={[
{
name: 'Corporates',
href: '/corporates',
},
{
name: 'Corporate Name',
href: '/corporates/'+corporate_id,
},
{
name: 'Division',
href: '/corporates/'+corporate_id+'/divisions',
},
{
name: !isEdit ? 'Create' : 'Edit',
href: '/corporates/'+corporate_id+'/divisions/'+id,
},
]}
/>
<CorporatePlanForm isEdit={isEdit} currentCorporatePlan={currentCorporatePlan}/>
<Page title="Create Hospital">
{isEdit ? (
<Stack direction="row" alignItems="center" sx={{ marginBottom: 3 }}>
<ArrowBackIosIcon sx={{cursor:'pointer'}} onClick={() => navigate(-1)}/>
<Typography variant="h5" sx={{marginLeft:2}}>Edit Hospital</Typography>
</Stack>
) : (
<HeaderBreadcrumbs
heading={'Create Hospital'}
links={[
{
name: 'Corporates',
href: '/corporates',
},
{
name: corporate?.name ?? '-',
href: '/corporates/'+corporate_id,
},
{
name: 'Hospital',
href: '/corporates/'+corporate_id+'/hospitals',
},
{
name: !isEdit ? 'Create' : 'Edit',
href: '/corporates/'+corporate_id+'/hospitals/create',
},
]}
/>
)}
<CorporateHospitalForm isEdit={isEdit} />
</Page>
);
}

View File

@@ -1,129 +1,171 @@
import * as Yup from 'yup';
import { LoadingButton } from "@mui/lab";
import { Card, Grid, Stack, Typography } from "@mui/material";
import { CorporatePlan } from "../../../@types/corporates";
import { FormProvider, RHFSwitch, RHFTextField } from "../../../components/hook-form";
import { useEffect, useMemo } from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { Button, Card, Grid, Stack, Typography, FormControl, InputLabel, Select, FormHelperText, MenuItem } from "@mui/material";
import { useEffect, useMemo, useState } from 'react';
import { useSnackbar } from 'notistack';
import { useNavigate, useParams } from 'react-router-dom';
import axios from '../../../utils/axios';
type Props = {
isEdit: boolean;
currentCorporatePlan?: CorporatePlan;
};
export default function CorporatePlanForm({ isEdit, currentCorporatePlan }: Props) {
export default function CorporateHospitalForm({ isEdit }: Props) {
const { enqueueSnackbar } = useSnackbar();
const navigate = useNavigate();
const { corporate_id } = useParams();
const NewCorporatePlanSchema = Yup.object().shape({
name: Yup.string().required('Name is required'),
code: Yup.string().required('Corporate Code is required'),
});
const defaultValues = useMemo(
() => ({
name: currentCorporatePlan?.name || '',
code: currentCorporatePlan?.code || '',
active: currentCorporatePlan?.active === 1 ? true : false,
}),
[currentCorporatePlan]
);
const { corporate_id, id, organization_id } = useParams();
const [dataHospital, setDataHospital] = useState(null);
let idHospital = organization_id && isEdit ? organization_id : 0;
const [indexData, setIndexData] = useState(idHospital);
const [updateData, setUpdateData] = useState(null);
const [dataDefault, setDataDefault] = useState(null);
useEffect(() => {
if (isEdit && currentCorporatePlan) {
reset(defaultValues);
axios.get('/corporates/' + corporate_id + '/hospitals/data')
.then((res) => {
setDataHospital(res.data);
const data = {
corporate_id : corporate_id ? corporate_id : null,
organization_id : res.data[0] ? res.data[0].id : null,
code : res.data[0] ? res.data[0].code : null,
name : res.data[0] ? res.data[0].name : null,
}
setDataDefault(data);
})
}, [isEdit]);
const [addData, setAddData] = useState(dataDefault);
const handlePageChange = (index:any) => {
setIndexData(index);
const data = {
corporate_id : corporate_id ? corporate_id : null,
organization_id : dataHospital ? dataHospital[index].id : null,
code : dataHospital ? dataHospital[index].code : null,
name : dataHospital ? dataHospital[index].name : null,
}
if (!isEdit) {
reset(defaultValues);
setAddData(data);
}
const handleSaveData = () => {
//Save data
axios
.post('/corporates/'+corporate_id+'/hospitals/save', (addData ? addData : dataDefault))
.then((response) => {
if(response.data)
{
enqueueSnackbar('Data saved successfully', { variant: 'success' });
}
})
.catch((error) => {
enqueueSnackbar('Failed to add data', { variant: 'error' });
});
}
const handlePageChangeUpdate = (id: any) => {
setIndexData(id);
const foundData = dataHospital?.find(item => item.id === id);
const dataUpdate = {
corporate_id : corporate_id ? corporate_id : null,
organization_id : dataHospital ? foundData.id : null,
code : dataHospital ? foundData.code : null,
name : dataHospital ? foundData.name : null,
}
}, [isEdit, currentCorporatePlan]);
setUpdateData(dataUpdate);
}
const methods = useForm({
resolver: yupResolver(NewCorporatePlanSchema),
defaultValues,
});
const {
reset,
watch,
control,
setValue,
getValues,
setError,
handleSubmit,
formState: { isSubmitting },
} = methods;
const onSubmit = async (data: any) => {
if (!isEdit) {
await axios
.post('/corporate/' + corporate_id + '/divisions', data)
.then((res) => {
enqueueSnackbar('Division created successfully', { variant: 'success' });
})
.then((res) => {
navigate('/corporate/' + corporate_id + '/divisions', { replace: true });
})
.catch(({ response }) => {
if (response.status === 422) {
for (const [key, value] of Object.entries(response.data.errors)) {
setError(key, { message: value[0] });
enqueueSnackbar(value[0] ?? 'Failed Processing Request', { variant: 'error' });
}
}
else {
enqueueSnackbar('Create Failed : '+ response.data.message, { variant: 'error' });
const handleUpdateData = () => {
//Update data
if(updateData)
{
axios
.put('/corporates/'+corporate_id+'/hospitals/'+id+'/edit', updateData)
.then((response) => {
if(response.data)
{
enqueueSnackbar('Data updated successfully', { variant: 'success' });
navigate(-1);
window.history.replaceState(null, '', '/corporates/'+corporate_id+'/hospitals');
}
})
.catch((error) => {
enqueueSnackbar('Failed to update data', { variant: 'error' });
});
} else {
await axios
.put('/corporate/' + corporate_id + '/divisions/' + currentCorporatePlan?.id , data)
.then((res) => {
enqueueSnackbar('Division updated successfully', { variant: 'success' });
})
.then((res) => {
navigate('/corporate/' + corporate_id + '/divisions/' , { replace: true });
})
.catch(({ response }) => {
enqueueSnackbar('Update Failed : '+ response.data.message, { variant: 'error' });
});
}
};
else
{
enqueueSnackbar('Data has not changed.', { variant: 'error' });
}
}
return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Grid container spacing={2}>
<Grid item xs={8}>
<Card sx={{ p: 2 }}>
<Stack spacing={3}>
<Stack>
<Card sx={{ p: 2 }}>
<Stack spacing={2} direction='column'>
<Typography variant='subtitle1'>Hospital *</Typography>
{dataHospital && dataHospital.length > 0 ? (
isEdit ? (
<FormControl>
<InputLabel htmlFor="id_hospital" required>
Hospital
</InputLabel>
<Select
id="id_hospital"
value={dataHospital && dataHospital.length > 0 ? indexData : ''}
fullWidth
label="Hospital"
onChange={(e) => {
handlePageChangeUpdate(e.target.value);
}}
>
{dataHospital?.map((data, index) => (
<MenuItem key={index} value={data.id}>{data.name}</MenuItem>
))}
</Select>
<FormHelperText style={{ color: 'red' }}></FormHelperText>
</FormControl>
) : (
<FormControl>
<InputLabel htmlFor="id_hospital" required>
Hospital
</InputLabel>
<Select
id="id_hospital"
value={dataHospital && dataHospital.length > 0 ? indexData : ''}
fullWidth
label="Hospital"
onChange={(e) => {
handlePageChange(e.target.value);
}}
>
{dataHospital?.map((data, index) => (
<MenuItem key={index} value={index}>{data.name}</MenuItem>
))}
</Select>
<FormHelperText style={{ color: 'red' }}></FormHelperText>
</FormControl>
)
) : (
<Typography variant='body2' style={{ position: 'relative', margin: 'auto', width: 'fit-content' }}>
Loading
</Typography>
<Typography variant="h6">Division Detail</Typography>
<RHFTextField name="name" label="Name" />
<RHFTextField name="code" label="Code" />
<LoadingButton type="submit" variant="contained" size="large" fullWidth={true} loading={isSubmitting}>
{ isEdit? 'Update' : 'Create' }
</LoadingButton>
</Stack>
</Card>
</Grid>
<Grid item xs={4}>
<Card sx={{ p:2 }}>
<RHFSwitch name="active" label="Active" />
</Card>
</Grid>
</Grid>
</FormProvider>
)}
<Stack direction='row' spacing={2} justifyContent='flex-end'>
<Button variant="outlined" sx={{color: '#212B36'}} onClick={() => navigate(-1)}>Cancel</Button>
{isEdit ? (
<Button sx={{backgroundColor: '#19BBBB'}} variant="contained" onClick={() => handleUpdateData()}>Save</Button>
):(
<Button sx={{backgroundColor: '#19BBBB'}} variant="contained" onClick={() => handleSaveData()}>Save</Button>
)}
</Stack>
</Stack>
</Card>
</Stack>
);
}

View File

@@ -0,0 +1,158 @@
import { useNavigate, useParams } from "react-router-dom";
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
import Page from "../../../components/Page";
import { useContext, useEffect, useState } from 'react';
import { Hospital } from '../../../@types/corporates';
import { Corporate } from "@/@types/corporates";
import { ConfiguredCorporateContext } from "@/contexts/ConfiguredCorporateContext";
import {
Stack,
Typography,
Card,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Collapse,
Box,
Tab,
} from '@mui/material';
import axios from '@/utils/axios';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import { fDate, fDateTime } from '@/utils/formatTime';
export default function HospitalHistory() {
const { corporate_id, id } = useParams();
const navigate = useNavigate();
const [corporate, setCorporate] = useState<Corporate|null>();
const [ currentHospital, setCurrentHospital ] = useState<Hospital>();
const configuredCorporateContext = useContext(ConfiguredCorporateContext);
useEffect(() => {
setCorporate(configuredCorporateContext.currentCorporate);
const model = 'App\\Models\\CorporateHospital';
const url = `/audittrail/${id}?model=${model}`;
axios.get(url)
.then((res) => {
setCurrentHospital(res.data);
})
.catch((error) => {
console.error('Terjadi kesalahan:', error);
});
}, [corporate_id, id, configuredCorporateContext]);
const [openRows, setOpenRows] = useState({});
const handleRowToggle = (index) => {
setOpenRows((prevOpenRows) => ({
...prevOpenRows,
[index]: !prevOpenRows[index],
}));
};
return (
<Page title="History Hospital">
<HeaderBreadcrumbs
heading={'History Hospital'}
links={[
{
name: 'Corporates',
href: '/corporates',
},
{
name: corporate?.name ?? '-',
href: '/corporates/'+corporate_id,
},
{
name: 'Hospital',
href: '/corporates/'+corporate_id+'/hospitals',
},
{
name: 'History',
href: '/corporates/'+corporate_id+'/hospitals/history',
},
]}
/>
<Card>
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
{/* Condition Table Body */}
{currentHospital?.data.map((item, index) => (
<TableBody key={index}>
<TableRow sx={{backgroundColor: '#919EAB29'}}>
<TableCell align="left" sx={{fontWeight: 'bold', width: '95%'}}><Typography variant="subtitle1">Data has {item.action} by {item.user_id} on {fDateTime(item.updated_at)}</Typography></TableCell>
<TableCell align="left" sx={{width: '5%'}}>
{openRows[index] ? (
<KeyboardArrowDownIcon sx={{ cursor: 'pointer' }} onClick={() => handleRowToggle(index)} />
) : (
<KeyboardArrowRightIcon sx={{ cursor: 'pointer' }} onClick={() => handleRowToggle(index)} />
)}
</TableCell>
</TableRow>
<TableRow sx={{display: openRows[index] ? '' : 'none',}}>
<TableCell colSpan={2}>
{/* COLLAPSIBLE ROW */}
<Collapse in={openRows[index]} timeout="auto" unmountOnExit>
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell align="left">
<Typography sx={{width: '25%'}} variant="subtitle2">Field</Typography>
</TableCell>
<TableCell align="left">
<Typography sx={{width: '25%'}} variant="subtitle2">Old Value</Typography>
</TableCell>
<TableCell align="left">
<Typography sx={{width: '50%'}} variant="subtitle2">New Value</Typography>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Object.entries(item.old_values).map(([key, value]) => {
let renderedValue;
if (key === 'code' || key === 'name') {
renderedValue = item.new_values[key];
const field = key.charAt(0).toUpperCase() + key.slice(1);
return (
<TableRow key={key}>
<TableCell align="left">
<Typography variant="body2">{field ? field : '-'}</Typography>
</TableCell>
<TableCell align="left">
<Typography variant="body2">{value ? value : '-'}</Typography>
</TableCell>
<TableCell align="left">
<Typography variant="body2">{renderedValue ? renderedValue : ''}</Typography>
</TableCell>
</TableRow>
);
}
else
{
return null;
}
})}
</TableBody>
</Table>
</TableContainer>
</Collapse>
</TableCell>
</TableRow>
</TableBody>
))}
</Table>
</TableContainer>
</Card>
</Page>
);
}

View File

@@ -1,32 +1,33 @@
import { Card, Grid, Typography } 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 CorporateTabNavigations from '../CorporateTabNavigations';
import DivisionsList from './List';
import { Corporate } from "@/@types/corporates";
import { ConfiguredCorporateContext } from "@/contexts/ConfiguredCorporateContext";
import { Card } from "@mui/material";
import { useContext, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
import Page from "../../../components/Page";
import useSettings from "../../../hooks/useSettings";
import CorporateTabNavigations from "../CorporateTabNavigations";
import List from "./List";
import { useContext, useEffect, useState } from 'react';
import { ConfiguredCorporateContext } from '@/contexts/ConfiguredCorporateContext';
import { Corporate } from '@/@types/corporates';
export default function Divisions() {
const { themeStretch } = useSettings();
export default function hospitals() {
const { corporate_id } = useParams();
const [corporate, setCorporate] = useState<Corporate | null>();
const [corporate, setCorporate] = useState<Corporate|null>();
const configuredCorporateContext = useContext(ConfiguredCorporateContext);
useEffect(() => {
setCorporate(configuredCorporateContext.currentCorporate);
}, [configuredCorporateContext]);
}, [configuredCorporateContext])
return (
<Page title="Hospitals">
<Page title="Hospital">
<HeaderBreadcrumbs
heading={'Hospitals'}
heading={'Hospital'}
links={[
{
name: 'Corporates',
@@ -34,18 +35,18 @@ export default function Divisions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Hospitals',
href: '/corporate/' + corporate_id + '/hospitals',
name: 'Hospital',
href: '/corporates/' + corporate_id + '/hospitals',
},
]}
/>
<Card>
<CorporateTabNavigations position={'hospitals'} />
<Typography sx={{ m: 4 }}>Feature Not Implemented Yet</Typography>
<List />
</Card>
</Page>
);

View File

@@ -1,124 +1,164 @@
// @mui
import { Box, Button, Card, Collapse, IconButton, InputLabel, MenuItem, OutlinedInput, Paper, Select, SelectChangeEvent, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, TextField, Typography, Badge, Tab, Tabs, CardHeader, Stack, Menu, ButtonGroup } from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import {
Button,
Card,
IconButton,
MenuItem,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
TextField,
Typography,
Stack,
Collapse,
Box,
FormControl,
InputLabel,
Select,
FormHelperText,
Menu,
ButtonGroup
} from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
import UploadIcon from '@mui/icons-material/Upload';
import CancelIcon from '@mui/icons-material/Cancel';
// hooks
import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react';
import useSettings from '../../../hooks/useSettings';
import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
// components
import axios from '../../../utils/axios';
import { CorporatePlan } from '../../../@types/corporates';
import { Hospital } from '../../../@types/corporates';
import { LaravelPaginatedData } from '../../../@types/paginated-data';
import BasePagination from '../../../components/BasePagination';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import HistoryIcon from '@mui/icons-material/History';
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
import CachedOutlinedIcon from '@mui/icons-material/CachedOutlined';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import { enqueueSnackbar } from 'notistack';
import Label from '../../../components/Label';
import DownloadIcon from '@mui/icons-material/Download';
import { LoadingButton } from '@mui/lab';
import CancelIcon from '@mui/icons-material/Cancel';
import UploadIcon from '@mui/icons-material/Upload';
export default function PlanList() {
const { themeStretch } = useSettings();
export default function HospitalList() {
const { corporate_id } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate();
function SearchInput(props: any) {
// SEARCH
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
const [searchText, setSearchText] = useState("");
const [searchText, setSearchText] = useState('');
const handleSearchChange = (event: any) => {
const newSearchText = event.target.value ?? ''
const newSearchText = event.target.value ?? '';
setSearchText(newSearchText);
}
};
const handleSubmit = (event: any) => {
event.preventDefault();
props.onSearch(searchText); // Trigger to Parent
}
};
useEffect(() => {
// console.log('Search Input: useEffect')
useEffect(() => {
setSearchText(searchParams.get('search') ?? '');
}, [searchParams])
}, [searchParams]);
return (
<form onSubmit={handleSubmit} style={{ width: '100%' }}>
<TextField id="search-input" ref={searchInput} label="Search" variant="outlined" fullWidth onChange={handleSearchChange} value={searchText}/>
<TextField
id="search-input"
ref={searchInput}
label="Search"
variant="outlined"
fullWidth
onChange={handleSearchChange}
value={searchText}
/>
</form>
);
}
// Called on every row to map the data to the columns
function createData( plan: CorporatePlan ): CorporatePlan {
function createData(hospital: Hospital): Hospital {
return {
...plan,
}
...hospital,
};
}
// Generate the every row of the table
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const style1 = {
color: '#637381'
}
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
<TableRow sx={{ '& > *': open ? {borderBottom: 'unset'} : {}, cursor: open ? 'pointer' : '' }} onClick={() => {if(open==true) setOpen(!open)}}>
<TableCell align="left">{row.code ? row.code : '-'}</TableCell>
<TableCell align="left">{row.name ? row.name : '-'}</TableCell>
<TableCell align="left">
{row.active === 1 ? (
<Label color='success' >
Active
</Label>
) : (
<Label color='error'>
Inactive
</Label>
)}
</TableCell>
<TableCell align="left">
<TableMoreMenu actions={
<>
<MenuItem onClick={() => setOpen(!open)}>
<FindInPageOutlinedIcon />
Details
</MenuItem>
<MenuItem onClick={() => handleEditData(row)}>
<EditOutlinedIcon />
Edit
</MenuItem>
<MenuItem onClick={() => handleEditDataStatus(row)}>
<CachedOutlinedIcon />
Update Status
</MenuItem>
<MenuItem onClick={() => navigate ('/corporates/'+corporate_id+'/hospitals/'+row.id+'/history')}>
<HistoryIcon />
History
</MenuItem>
</>
}
/>
</TableCell>
<TableCell align="left">{row.id}</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">{row.description}</TableCell>
<TableCell align="right"><Button variant="outlined" color="success" size="small">Active</Button></TableCell>
<TableCell align="right"><Link to={`/corporates/${row.corporate_id}/divisions/${row.id}/edit`}><Button variant="outlined" color="success" size="small">Edit</Button></Link></TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={10}>
<TableRow sx={{display: open ? '' : 'none', cursor: open ? 'pointer' : ''}} onClick={() => {if(open==true) setOpen(!open)}}>
<TableCell colSpan={4}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ borderBottom: 1 }}>
<Typography variant="body2" gutterBottom component="div">
No Extra Data
</Typography>
</Box>
{false && <Box sx={{ margin: 1 }}>
<Typography variant="h6" gutterBottom component="div">
Rules
</Typography>
<Table size="small" aria-label="purchases">
<TableHead>
<TableRow>
<TableCell>Date</TableCell>
<TableCell>Customer</TableCell>
<TableCell align="right">Amount</TableCell>
<TableCell align="right">Total price ($)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{/* {row.history ? row.history.map((historyRow) => ( */}
<TableRow key={row.id}>
<TableCell component="th" scope="row">{row.start} - {row.end}</TableCell>
<TableCell>{row.start}</TableCell>
<TableCell align="right">{row.start}</TableCell>
<TableCell align="right">{row.start}</TableCell>
</TableRow>
{/* ))
: (
<TableRow>
<TableCell colSpan={8}>No Data</TableCell>
</TableRow>
)
} */}
</TableBody>
</Table>
</Box>}
<Card sx={{padding:2}}>
<Box sx={{ pb: 2 }}>
<Typography variant='subtitle1'>Detail</Typography>
<Stack marginTop={2}>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '50%'}}>Code:</Typography>
<Typography variant='body2' sx={{width: '50%'}}>{row.code ? row.code : '-'}</Typography>
</Stack>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '50%'}}>Hospital Name:</Typography>
<Typography variant='body2' sx={{width: '50%'}}>{row.name ? row.name : '-'}</Typography>
</Stack>
</Stack>
</Box>
</Card>
</Collapse>
</TableCell>
</TableRow>
@@ -131,104 +171,390 @@ export default function PlanList() {
const [dataTableData, setDataTableData] = React.useState<LaravelPaginatedData>({
current_page: 1,
data: [],
path: "",
first_page_url: "",
path: '',
first_page_url: '',
last_page: 1,
last_page_url: "",
next_page_url: "",
prev_page_url: "",
last_page_url: '',
next_page_url: '',
prev_page_url: '',
per_page: 10,
from: 0,
to: 0,
total: 0
total: 0,
});
const loadDataTableData = async (appliedFilter : any | null = null) => {
const loadDataTableData = async (appliedFilter: any | null = null) => {
setDataTableLoading(true);
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
const response = await axios.get('/corporates/'+corporate_id+'/divisions', { params: filter });
// console.log(response.data);
// Get Data Hospitals
const response = await axios.get('/corporates/' + corporate_id + '/hospitals', {
params: filter,
});
setDataTableLoading(false);
setDataTableData(response.data);
}
const headStyle = {
fontWeight: 'bold',
};
const applyFilter = async (searchFilter: any) => {
await loadDataTableData({ "search" : searchFilter });
setSearchParams({ "search" : searchFilter });
}
await loadDataTableData({ search: searchFilter });
setSearchParams({ search: searchFilter });
};
const handlePageChange = (event : ChangeEvent, value: number) => {
const filter = Object.fromEntries([...searchParams.entries(), ["page", value]]);
const handlePageChange = (event: ChangeEvent, value: number) => {
const filter = Object.fromEntries([...searchParams.entries(), ['page', value]]);
loadDataTableData(filter);
setSearchParams(filter);
}
};
useEffect(() => {
loadDataTableData();
}, [])
}, []);
//validation dialog
const [nameField, setNameField] = useState('');
const [codeField, setCodeField] = useState('');
// ID for edit data
const [idField, setIdField] = useState('');
const handleAddData = () => {
navigate('/corporates/'+corporate_id+'/hospitals/create');
}
//Edit data
const handleEditData = (data : any) => {
navigate('/corporates/'+corporate_id+'/hospitals/edit/'+data.id+'/'+data.organization_id);
}
// End dialog for hospitals
// Dialog for update status hospitals
const [openDialogStatus, setOpenDialogStatus] = useState(false);
const [activeField, setActiveField] = useState(0);
const [reasonUpdate,setReasonUpdate] = useState('Agreement changed');
const handleCloseDialogUpdate = () => {
setOpenDialogStatus(false);
setNameField('');
setCodeField('');
setIdField('');
setActiveField(activeField);
}
const handleSaveUpdateData = () => {
let activeValue = 0;
if(activeField === 1)
{
activeValue = 0;
}
else
{
activeValue = 1;
}
const updateData = {
reason: reasonUpdate,
active : activeValue,
id: idField,
};
axios
.put('/hospitals/'+idField+'/activation', updateData)
.then((response) => {
enqueueSnackbar('Data updated successfully', { variant: 'success' });
loadDataTableData();
handleCloseDialogUpdate();
})
.catch((error) => {
enqueueSnackbar('Failed to add data', { variant: 'error' });
});
}
const handleEditDataStatus = (data: any) => {
setIdField(data.id);
setNameField(data.name);
setCodeField(data.code);
setActiveField(data.active);
setOpenDialogStatus(true);
}
// End dialog for update status devisions
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const createMenu = Boolean(anchorEl);
const importHospital = useRef<HTMLInputElement>(null);
const [currentImportFileName, setCurrentImportFileName] = useState(null);
const [importLoading, setImportLoading] = useState(false);
const [importResult, setImportResult] = useState(null);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleImportButton = () => {
if (importHospital?.current) {
handleClose();
importHospital.current ? importHospital.current.click() : console.log('No File selected');
} else {
alert('No file selected');
}
};
const handleCancelImportButton = () => {
if(importHospital.current)
{
importHospital.current.value = '';
importHospital.current.dispatchEvent(new Event('change', { bubbles: true }));
}
};
const handleImportChange = (event: any) => {
if (event.target.files[0]) {
setCurrentImportFileName(event.target.files[0].name);
} else {
setCurrentImportFileName(null);
}
};
const handleUpload = () => {
if(importHospital.current && importHospital.current.files)
{
if (importHospital.current?.files.length) {
const formData = new FormData();
formData.append('file', importHospital.current?.files[0]);
setImportLoading(true);
axios
.post('corporates/'+corporate_id+'/hospitals/import', formData)
.then((response) => {
handleCancelImportButton();
loadDataTableData();
setImportResult(response.data);
setImportLoading(false);
enqueueSnackbar('Success Import Hospitals', { variant: 'success' });
})
.catch((response) => {
enqueueSnackbar(
'Looks like something went wrong. Please check your data and try again. ' +
response.message,
{ variant: 'error' }
);
setImportLoading(false);
});
} else {
enqueueSnackbar('No File Selected', { variant: 'warning' });
}
}
};
const handleGetTemplate = () => {
axios.get('corporates/hospitals/download-template').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 (
<Stack>
<input
type="file"
id="file"
ref={importHospital}
style={{ display: 'none' }}
onChange={handleImportChange}
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
/>
{!currentImportFileName && (
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter}/>
<Link to={`/corporates/${corporate_id}/divisions/create`}>
<Button
component="button"
id="upload-button"
variant='outlined'
startIcon={<AddIcon />} sx={{ p: 1.8 }}
>
Create
</Button>
</Link>
<SearchInput onSearch={applyFilter} />
<Button
id="import-button"
startIcon={<DownloadIcon />}
sx={{ p: 1.8, color: '#FFFFFF', backgroundColor: '#19BBBB', width: '125px', height: '48px' }}
aria-controls={createMenu ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={createMenu ? 'true' : undefined}
onClick={handleClick}
>
Import
</Button>
<Menu
id="import-button"
anchorEl={anchorEl}
open={createMenu}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
<MenuItem onClick={handleAddData}>
<Typography variant='body2'>Create</Typography>
</MenuItem>
<MenuItem onClick={handleImportButton}>
<Typography variant='body2'>Import</Typography>
</MenuItem>
<MenuItem
onClick={() => {
handleGetTemplate();
}}
>
<Typography variant='body2'> Download Template</Typography>
</MenuItem>
{/* <MenuItem onClick={handleICDList}>
<Typography variant='body2'>Download ICD</Typography>
</MenuItem> */}
</Menu>
</Stack>
)}
{currentImportFileName && (
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<ButtonGroup variant="outlined" aria-label="outlined button group" fullWidth>
<Button onClick={handleImportButton} fullWidth>
{currentImportFileName ?? 'No File Selected'}
</Button>
<Button
onClick={handleCancelImportButton}
size="small"
fullWidth={false}
sx={{ p: 1.8 }}
>
<CancelIcon color="error" />
</Button>
</ButtonGroup>
<Card>
<LoadingButton
id="upload-button"
variant="outlined"
startIcon={<UploadIcon />}
sx={{ p: 1.8 }}
onClick={handleUpload}
loading={importLoading}
>
Upload
</LoadingButton>
</Stack>
)}
{importResult && (
<Stack direction={'row'} sx={{ px: 2, pb: 2 }}>
<Box sx={{ color: 'text.secondary' }}>
Last Import Result :{' '}
<Box sx={{ color: 'success.main', display: 'inline' }}>
{importResult.data.total_success_row ?? 0}
</Box>{' '}
Row Processed,{' '}
<Box sx={{ color: 'error.main', display: 'inline' }}>
{importResult.data.total_failed_row}
</Box>{' '}
Failed
{importResult.data.failed_rows.map((row, index) => (
<Typography variant='body' key={index} color="error"> [Code={row.code ? row.code : 'Required'},Name={row.name ? row.name : 'Required'}]</Typography>
))}
</Box>
</Stack>
)}
<Card>
{/* The Main Table */}
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
{/* Table Head */}
<TableHead>
<TableRow>
<TableCell style={headStyle} align="left" />
<TableCell style={headStyle} align="left">ID</TableCell>
<TableCell style={headStyle} align="left">Code</TableCell>
<TableCell style={headStyle} align="left">Name</TableCell>
<TableCell style={headStyle} align="left">Description</TableCell>
<TableCell sx={{width: '20%'}} align="left">
<Typography variant='subtitle2'>Code</Typography>
</TableCell>
<TableCell sx={{width: '50%'}} align="left">
<Typography variant='subtitle2'>Hospital</Typography>
</TableCell>
<TableCell sx={{width: '20%'}} align="left">
<Typography variant='subtitle2'>Status</Typography>
</TableCell>
<TableCell sx={{width: '10%'}} align="left">
</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.code} row={row} />
))}
</TableBody>
)
</TableHead>
{/* Condition Table Body */}
{dataTableIsLoading ? (
<TableBody>
<TableRow>
<TableCell colSpan={4} align="center">
Loading
</TableCell>
</TableRow>
</TableBody>
) : dataTableData.data.length == 0 ? (
<TableBody>
<TableRow>
<TableCell colSpan={4} 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>
{/* Paginations */}
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange} />
</Card>
{/* Dialog Update Status */}
<Dialog open={openDialogStatus} onClose={handleCloseDialogUpdate} fullWidth={true}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" alignItems='center' spacing={1}>
<Typography variant="h6">Update Status</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogUpdate}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2} padding={2}>
<Typography variant='body1'>Are you sure to {activeField == 1 ? 'Inactive' : 'Active'} this hospital ?</Typography>
<Card sx={{padding:2}} >
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Code</Typography>
<Typography variant='subtitle2' sx={{width: '70%'}}>{codeField}</Typography>
</Stack>
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Hospital Name</Typography>
<Typography variant='subtitle2' sx={{width: '70%'}}>{nameField}</Typography>
</Stack>
</Card>
</Stack>
<Stack spacing={2} padding={2}>
<Typography variant='subtitle1'>Reason for update*</Typography>
<FormControl>
<InputLabel htmlFor="reason" required>
Reason for update
</InputLabel>
<Select
id="reason"
value={reasonUpdate}
fullWidth
label="Reason for update"
onChange={(e) => {
setReasonUpdate(e.target.value);
}}
>
<MenuItem value="Agreement changed">Agreement changed</MenuItem>
<MenuItem value="Endorsement">Endorsement</MenuItem>
<MenuItem value="Renewal">Renewal</MenuItem>
<MenuItem value="Worng Setting">Worng Setting</MenuItem>
</Select>
<FormHelperText style={{ color: 'red' }}></FormHelperText>
</FormControl>
</Stack>
</DialogContent>
<DialogActions>
<Button variant="outlined" sx={{color: '#212B36'}} onClick={handleCloseDialogUpdate}>Cancel</Button>
<Button sx={{backgroundColor: activeField == 0 ? '#19BBBB' : '#FF4842'}} onClick={handleSaveUpdateData} variant="contained">{activeField == 1 ? 'Inactive' : 'Active'}</Button>
</DialogActions>
</Dialog>
</Stack>
);
}
}

View File

@@ -24,21 +24,33 @@ import {
Typography,
Badge,
Stack,
Dialog,
} from '@mui/material';
import * as Yup from 'yup';
// icon
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import PublishIcon from '@mui/icons-material/Publish';
import AddIcon from '@mui/icons-material/Add';
import HistoryIcon from '@mui/icons-material/History';
import MoreVertIcon from '@mui/icons-material/MoreVert';
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';
import CachedOutlinedIcon from '@mui/icons-material/CachedOutlined';
// hooks
import useSettings from '../../hooks/useSettings';
// components
import Page from '../../components/Page';
import DialogUpdateStatus from '../../components/DialogUpdateStatus';
import axios from '../../utils/axios';
import useAuth from '../../hooks/useAuth';
import { Link, NavLink as RouterLink, useSearchParams } from 'react-router-dom';
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
import { useForm } from 'react-hook-form'
import { Link, NavLink as RouterLink, useNavigate, useSearchParams } from 'react-router-dom';
import React, { ChangeEvent, ReactElement, useEffect, useRef, useState } from 'react';
import { Theme, useTheme } from '@mui/material/styles';
import { Corporate } from '../../@types/corporates';
import { LaravelPaginatedData } from '../../@types/paginated-data';
@@ -47,10 +59,45 @@ import BasePagination from '../../components/BasePagination';
import { fCurrency } from '../../utils/formatNumber';
import { enqueueSnackbar } from 'notistack';
import { fDate } from '@/utils/formatTime';
import Popover from '@mui/material/Popover';
import ButtonStyles from '../../theme/overrides/Button';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import Iconify from '@/components/Iconify';
import Label from '@/components/Label';
import { FormProvider, RHFSelect } from '@/components/hook-form';
import { LoadingButton } from '@mui/lab';
import { yupResolver } from '@hookform/resolvers/yup';
export default function Corporates() {
const { themeStretch } = useSettings();
const [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate()
// Type
type DataContent = {
code: string;
name: string;
id: string;
status: string|number;
};
type MuiDialogProps = {
title?: {
name?: string;
icon?: string;
};
openDialog: boolean;
setOpenDialog: Function;
content?: ReactElement;
data?: DataContent[];
};
type FormValuesProps = {
value: string;
active: boolean;
};
// Called on every row to map the data to the columns
function createData(corporate: Corporate): Corporate {
@@ -58,6 +105,7 @@ export default function Corporates() {
...corporate,
};
}
// Dummy Default Data
const [dataTableIsLoading, setDataTableLoading] = React.useState(true);
@@ -80,7 +128,7 @@ export default function Corporates() {
setDataTableLoading(true);
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
const response = await axios.get('/corporates', { params: filter });
// console.log(response.data);
console.log(response.data);
setDataTableLoading(false);
setDataTableData(response.data);
@@ -150,6 +198,8 @@ export default function Corporates() {
typeof value === 'string' ? value.split(',') : value
);
};
// END FILTER SELECT
// Component Search Input
@@ -181,7 +231,8 @@ export default function Corporates() {
ref={searchInput}
label="Search"
variant="outlined"
fullWidth
// fullWidth
style={{ width: '85%' }}
onChange={handleSearchChange}
value={searchText}
/>
@@ -246,8 +297,8 @@ export default function Corporates() {
Import
</Button> */}
<Link to={'/corporates/create'}>
<Button variant="outlined" startIcon={<AddIcon />} sx={{ p: 1.8 }}>
Create
<Button variant="contained" startIcon={<AddIcon sx={{}} />} sx={{ p: 1.8, typography: 'subtitle2' }}>
New Corporate
</Button>
</Link>
{/* </Grid> */}
@@ -262,75 +313,200 @@ export default function Corporates() {
// Component Row
// Generate the every row of the table
const [isDialogOpen, setDialogOpen] = useState(false)
let titles = {
name: 'Update Status',
icon: '-'
}
const [dataValue, setDataValue] = useState();
const [dataDescription, setDescriptionValue] = useState('');
const NewCorporateSchema = Yup.object().shape({
reason: Yup.string().required('Reason Edit is required'),
});
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema),
});
const {
reset,
handleSubmit,
formState: { isSubmitting },
} = methods;
const onSubmit = async (row : any) => {
try {
console.log(dataValue)
handleUpdate(dataValue.id, dataValue.status, row.reason)
} catch (error: any) {
console.log('data gagal');
}
const ascent = document?.querySelector('ascent');
if (ascent != null) {
ascent.innerHTML = '';
}
};
const handleUpdate = (id: string, active: number, reason: string) => {
axios
.put(`/corporates/${id}/activation`, {
active: active,
reason: reason
})
.then((res) => {
window.location.reload();
});
}
const getContent = () => (
<>
<Stack paddingX={2} paddingY={2}>
<Typography variant='subtitle1'>Are you sure to {dataValue?.status == 1 ? 'inactive' : 'active'} this service ?</Typography>
</Stack>
<Card>
<Grid container paddingX={2} paddingY={2}>
<Grid item xs={4} md={4}>
<Typography variant='inherit'>Code</Typography>
</Grid>
<Grid item xs={8}>
<Typography variant='subtitle1'>{dataValue?.code}</Typography>
</Grid>
<Grid item xs={4} md={4} marginTop={2}>
<Typography variant='inherit'>Corporate Name</Typography>
</Grid>
<Grid item xs={8} marginTop={2}>
<Typography variant='subtitle1'>{dataValue?.name}</Typography>
</Grid>
</Grid>
</Card>
<Typography marginTop={5} marginBottom={3} variant='subtitle1'>
Reason for update*
</Typography>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<RHFSelect
name="reason"
label="Reason for update"
>
<option value=""></option>
<option value="Agreement changed">Agreement changed</option>
<option value="Endorsement">Endorsement</option>
<option value="Renewal">Renewal</option>
<option value="Worng Setting">Worng Setting</option>
</RHFSelect>
<Stack
alignItems="center"
justifyContent="flex-end"
direction={{ xs: 'column', md: 'row' }}
spacing={2}
marginTop={5}
>
<Stack direction="row" spacing={1}>
<Button
sx={{
boxShadow: 'none',
}}
variant="outlined"
size="medium"
fullWidth={true}
onClick={() => setDialogOpen(false)}
>
Cancel
</Button>
{dataValue?.status == 1 ?
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)'}}
type="submit"
variant="contained"
size="medium"
fullWidth={true}
loading={isSubmitting}
color='error'
>
Inactive
</LoadingButton>
:
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)'}}
type="submit"
variant="contained"
size="medium"
fullWidth={true}
loading={isSubmitting}
color='success'
>
Active
</LoadingButton>
}
</Stack>
</Stack>
</FormProvider>
</>
);
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const navigate = useNavigate()
const handleActivate = (model: any, status: string) => {
axios
.put(`/corporates/${row.id}/activation`, {
// service_code: service.service_code,
active: status == 'active',
})
.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;
}),
});
})
.catch((error) => {
// console.log('asdasd', error.response.data.message)
enqueueSnackbar(
error.response.data.message ?? error.message ?? 'Failed Processing Request',
{ variant: 'error' }
);
});
const handleActivate = (isOpen: boolean, dataValue: DataContent) => {
setDialogOpen(isOpen)
setDataValue(dataValue)
setDescriptionValue('Are you sure to inactive this coporate ?')
};
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableRow >
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{/* <IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</IconButton> */}
</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">
{row.active == 1 && (
<Button
variant="outlined"
color="success"
size="small"
onClick={() => {
handleActivate(row, 'inactive');
}}
>
<Label color='success'>
Active
</Button>
</Label>
// <Button
// variant="outlined"
// color="success"
// size="small"
// onClick={() => {
// handleActivate(row, 'inactive');
// }}
// >
// Active
// </Button>
)}
{row.active != 1 && (
<Button
variant="outlined"
color="error"
size="small"
onClick={() => {
handleActivate(row, 'active');
}}
>
<Label color='error'>
Inactive
</Button>
</Label>
// <Button
// variant="outlined"
// color="error"
// size="small"
// onClick={() => {
// handleActivate(row, 'active');
// }}
// >
// Inactive
// </Button>
)}
</TableCell>
<TableCell align="right">
<Stack direction="row" justifyContent="flex-end" spacing={1}>
<TableCell align="right" sx={{borderBottom: 'unset'}}>
{/* <Stack direction="row" justifyContent="flex-end" spacing={1}>
<Link to={`/corporates/${row.id}/edit`}>
<Button variant="outlined" color="primary" size="small">
Edit
@@ -344,147 +520,151 @@ export default function Corporates() {
<Link to={`/corporate/${row.id}/corporate-history`}>
<HistoryIcon />
</Link>
</Stack>
</Stack> */}
<TableMoreMenu actions={
<>
<MenuItem onClick={() => setOpen(!open)}>
<FindInPageOutlinedIcon />
Detail
</MenuItem>
<MenuItem onClick={() => navigate(`/corporates/${row.id}/edit`)}>
<EditOutlinedIcon />
Edit
</MenuItem>
<MenuItem onClick={() => navigate(`/corporates/${row.id}`)}>
<SettingsOutlinedIcon />
Config
</MenuItem>
<MenuItem onClick={() => navigate(`/corporates/${row.id}/corporate-history`)}>
<HistoryIcon />
History
</MenuItem>
<MenuItem onClick={() => handleActivate(true, {code: row.code, name: row.name, id:row.id, status: row.active})}>
<CachedOutlinedIcon />
Update Status
</MenuItem>
</>
} />
</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
{/* COLLAPSIBLE Detail ROW */}
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={9999}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ margin: 1, borderBottom: 1, pb: 2 }}>
<Typography sx={{ fontWeight: '600', mb: 1 }}>Current Policy Detail</Typography>
<Grid container>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Policy Code
</Grid>
<Grid item xs={6}>
: {row.current_policy?.code}
</Grid>
<Card sx={{paddingX: 2, paddingY: 2, marginBottom:3}}>
<Box sx={{ margin: 1, pb: 2 }}>
<Grid container>
<Grid item xs={6}>
<Typography sx={{ fontWeight: '600', mb: 1 }}>Current Policy Detail</Typography>
<Grid container>
<Grid item xs={4}>
Policy Code :
</Grid>
<Grid item xs={6}>
{row.current_policy?.code}
</Grid>
<Grid item xs={6}>
Number of Plan
</Grid>
<Grid item xs={6}>
: {row.corporate_plans_count}
</Grid>
<Grid item xs={4}>
Number of Plan :
</Grid>
<Grid item xs={6}>
{row.corporate_plans_count}
</Grid>
<Grid item xs={6}>
Number of Benefit
</Grid>
<Grid item xs={6}>
: {row.corporate_benefits_count}
</Grid>
</Grid>
</Grid>
<Grid item xs={4}>
Number of Benefit :
</Grid>
<Grid item xs={6}>
{row.corporate_benefits_count}
</Grid>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Period
</Grid>
<Grid item xs={6}>
<Box sx={{ display: 'flex', placeContent: 'space-between' }}>
<span>: {fDate(row.current_policy?.start)}</span>-
<span>{fDate(row.current_policy?.end)}</span>
</Box>
</Grid>
<Grid item xs={6}>
Total Premi
</Grid>
<Grid item xs={6}>
{row.current_policy ? (
<Grid item xs={4}>
Period :
</Grid>
<Grid item xs={6}>
<Box sx={{ display: 'flex', placeContent: 'space-between' }}>
<span>
: {fCurrency(row.current_policy?.total_premi).split(' ')[0]}
</span>
<span>{fCurrency(row.current_policy?.total_premi).split(' ')[1]}</span>
{/* <span>: {fDate(row.current_policy?.start)}</span>- */}
{/* <span>{fDate(row.current_policy?.end)}</span> */}
</Box>
) : (
'-'
)}
</Grid>
</Grid>
<Grid item xs={6}>
Minimal Deposit
</Grid>
<Grid item xs={6}>
{row.current_policy ? (
<Box sx={{ display: 'flex', placeContent: 'space-between' }}>
<span>
: {fCurrency(row.current_policy?.minimal_deposit_net).split(' ')[0]}
</span>
<span>
{fCurrency(row.current_policy?.minimal_deposit_net).split(' ')[1]}
</span>
</Box>
) : (
'-'
)}
</Grid>
<Grid item xs={4}>
Total Premi :
</Grid>
<Grid item xs={6}>
{row.current_policy ? (
<Box sx={{ display: 'flex', placeContent: 'space-between' }}>
<span>
{fCurrency(row.current_policy?.total_premi).split(' ')[0]}
</span>
<span>{fCurrency(row.current_policy?.total_premi).split(' ')[1]}</span>
</Box>
) : (
'-'
)}
</Grid>
<Grid item xs={6}>
Corporate Limit
</Grid>
<Grid item xs={6}>
{row.current_policy ? (
<Box sx={{ display: 'flex', placeContent: 'space-between' }}>
<span>
: {fCurrency(row.current_policy?.limit_balance).split(' ')[0]}
</span>
<span>
{fCurrency(row.current_policy?.limit_balance).split(' ')[1]}
</span>
</Box>
) : (
'-'
)}
<Grid item xs={4}>
Minimal Deposit :
</Grid>
<Grid item xs={6}>
{row.current_policy ? (
<Box sx={{ display: 'flex', placeContent: 'space-between' }}>
<span>
{fCurrency(row.current_policy?.minimal_deposit_net).split(' ')[0]}
</span>
<span>
{fCurrency(row.current_policy?.minimal_deposit_net).split(' ')[1]}
</span>
</Box>
) : (
'-'
)}
</Grid>
<Grid item xs={4}>
Corporate Limit :
</Grid>
<Grid item xs={6}>
{row.current_policy ? (
<Box sx={{ display: 'flex', placeContent: 'space-between' }}>
<span>
{fCurrency(row.current_policy?.limit_balance).split(' ')[0]}
</span>
<span>
{fCurrency(row.current_policy?.limit_balance).split(' ')[1]}
</span>
</Box>
) : (
'-'
)}
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Typography sx={{ fontWeight: '600', mb: 1, mt: 2 }}>Member Detail</Typography>
<Grid container>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Total Member
</Grid>
<Grid item xs={6}>
: {row.employees_count}
<Grid item xs={6}>
<Typography sx={{ fontWeight: '600', mb: 1 }}>Member Detail</Typography>
<Grid container>
<Grid item xs={6}>
Total Member :
</Grid>
<Grid item xs={6}>
{row.employees_count}
</Grid>
<Grid item xs={6}>
Total Claim This Month :
</Grid>
<Grid item xs={6}>
0
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={6}>
<Grid Grid container>
<Grid item xs={6}>
Total Claim This Month
</Grid>
<Grid item xs={6}>
: 0
</Grid>
</Grid>
</Grid>
</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 ({row.sub_corporates.length})
</Grid>
<Grid item xs={6}>
: {row.sub_corporates?.map((corp) => corp.name).join(', ')}
</Grid>
</Grid>
</Grid>
</Grid> */}
</Box>
</Box>
</Card>
</Collapse>
</TableCell>
</TableRow>
@@ -499,6 +679,10 @@ export default function Corporates() {
heading={'Coporate List'}
links={[
{ name: 'Dashboard', href: '/dashboard' },
{
name: 'Station Benefit & Membership',
href: '/corporates',
},
{
name: 'Corporates',
href: '/corporates',
@@ -508,27 +692,27 @@ export default function Corporates() {
<SearchInput onSearch={applyFilter} />
<Card>
{/* <Card> */}
{/* The Main Table */}
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
<TableHead>
<TableRow>
<TableCell style={headStyle} align="left" width={50} />
<TableCell style={headStyle} align="left">
<TableCell align="left" width={50} />
<TableCell align="left">
Code
</TableCell>
<TableCell style={headStyle} align="left">
<TableCell align="left">
Name
</TableCell>
<TableCell style={headStyle} align="left" width={100}>
<TableCell align="left" width={100}>
Status
</TableCell>
<TableCell style={headStyle} align="center" width={100}>
Action
<TableCell align="center" width={100}>
{/* Action */}
</TableCell>
</TableRow>
</TableBody>
</TableHead>
{dataTableIsLoading ? (
<TableBody>
<TableRow>
@@ -555,8 +739,17 @@ export default function Corporates() {
</Table>
</TableContainer>
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange} />
</Card>
{/* </Card> */}
</Container>
<DialogUpdateStatus
openDialog={isDialogOpen}
setOpenDialog={setDialogOpen}
title={titles}
data={dataValue}
description={dataDescription}
content={getContent()}
/>
</Page>
);
}

View File

@@ -0,0 +1,166 @@
import { useNavigate, useParams } from "react-router-dom";
import HeaderBreadcrumbs from "../../../components/HeaderBreadcrumbs";
import Page from "../../../components/Page";
import { useContext, useEffect, useState } from 'react';
import { Member } from '../../../@types/corporates';
import { Corporate } from "@/@types/corporates";
import { ConfiguredCorporateContext } from "@/contexts/ConfiguredCorporateContext";
import {
Stack,
Typography,
Card,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Collapse,
Box,
Tab,
} from '@mui/material';
import axios from '@/utils/axios';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import { fDate, fDateTime } from '@/utils/formatTime';
export default function MemberHistory() {
const { corporate_id, member_id } = useParams();
const navigate = useNavigate();
const [corporate, setCorporate] = useState<Corporate|null>();
const [ currentMember, setCurrentMember ] = useState<Member>();
const configuredCorporateContext = useContext(ConfiguredCorporateContext);
useEffect(() => {
setCorporate(configuredCorporateContext.currentCorporate);
const model = 'App\\Models\\Member';
const url = `/audittrail/${member_id}?model=${model}`;
axios.get(url)
.then((res) => {
setCurrentMember(res.data);
})
.catch((error) => {
console.error('Terjadi kesalahan:', error);
});
}, [corporate_id, member_id, configuredCorporateContext]);
const [openRows, setOpenRows] = useState({});
const handleRowToggle = (index) => {
setOpenRows((prevOpenRows) => ({
...prevOpenRows,
[index]: !prevOpenRows[index],
}));
};
return (
<Page title="History Member">
<HeaderBreadcrumbs
heading={'History Member'}
links={[
{
name: 'Corporates',
href: '/corporates',
},
{
name: corporate?.name ?? '-',
href: '/corporates/'+corporate_id,
},
{
name: 'Member',
href: '/corporates/'+corporate_id+'/members',
},
{
name: 'History',
href: '/corporates/'+corporate_id+'/members/history',
},
]}
/>
<Card>
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
{/* Condition Table Body */}
{currentMember?.data.map((item, index) => (
<TableBody key={index}>
<TableRow sx={{backgroundColor: '#919EAB29'}}>
<TableCell align="left" sx={{fontWeight: 'bold', width: '95%'}}><Typography variant="subtitle1">Data has {item.action} by {item.user_id} on {fDateTime(item.updated_at)}</Typography></TableCell>
<TableCell align="left" sx={{width: '5%'}}>
{openRows[index] ? (
<KeyboardArrowDownIcon sx={{ cursor: 'pointer' }} onClick={() => handleRowToggle(index)} />
) : (
<KeyboardArrowRightIcon sx={{ cursor: 'pointer' }} onClick={() => handleRowToggle(index)} />
)}
</TableCell>
</TableRow>
<TableRow sx={{display: openRows[index] ? '' : 'none',}}>
<TableCell colSpan={2}>
{/* COLLAPSIBLE ROW */}
<Collapse in={openRows[index]} timeout="auto" unmountOnExit>
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell align="left">
<Typography sx={{width: '25%'}} variant="subtitle2">Field</Typography>
</TableCell>
<TableCell align="left">
<Typography sx={{width: '25%'}} variant="subtitle2">Old Value</Typography>
</TableCell>
<TableCell align="left">
<Typography sx={{width: '50%'}} variant="subtitle2">New Value</Typography>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Object.entries(item.old_values).map(([key, value]) => {
let renderedValue;
if (key === 'reason' || key === 'updated_at') {
switch (key) {
case 'updated_at':
renderedValue = fDateTime(item.new_values[key]);
value = fDateTime(value);
break;
default:
renderedValue = item.new_values[key];
break;
}
const field = key.charAt(0).toUpperCase() + key.slice(1);
return (
<TableRow key={key}>
<TableCell align="left">
<Typography variant="body2">{field ? field : '-'}</Typography>
</TableCell>
<TableCell align="left">
<Typography variant="body2">{value ? value : '-'}</Typography>
</TableCell>
<TableCell align="left">
<Typography variant="body2">{renderedValue ? renderedValue : ''}</Typography>
</TableCell>
</TableRow>
);
}
else{
return null;
}
})}
</TableBody>
</Table>
</TableContainer>
</Collapse>
</TableCell>
</TableRow>
</TableBody>
))}
</Table>
</TableContainer>
</Card>
</Page>
);
}

View File

@@ -33,11 +33,11 @@ export default function Divisions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Member',
href: '/corporate/' + corporate_id + '/members',
href: '/corporates/' + corporate_id + '/members',
},
]}
/>

View File

@@ -29,6 +29,9 @@ import {
Grid,
Tooltip,
Divider,
ButtonBase,
FormControl,
FormHelperText,
} from '@mui/material';
import Iconify from '@/components/Iconify';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
@@ -40,7 +43,7 @@ import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
// hooks
import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react';
import useSettings from '../../../hooks/useSettings';
import {Link, useParams, useSearchParams } from 'react-router-dom';
import {Link, useParams, useNavigate, useSearchParams } from 'react-router-dom';
// components
import axios from '../../../utils/axios';
import { Plan } from '../../../@types/corporates';
@@ -52,8 +55,16 @@ import { LoadingButton } from '@mui/lab';
import DialogLog from './sections/DialogLog';
import HistoryIcon from '@mui/icons-material/History';
import { makeFormData } from '@/utils/jsonToFormData';
import DownloadIcon from '@mui/icons-material/Download';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
import CachedOutlinedIcon from '@mui/icons-material/CachedOutlined';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import Label from '../../../components/Label';
export default function CorporatePlanList({handleSubmitSuccess}) {
const navigate = useNavigate();
// Files MCU
const fileMcuInput = useRef<HTMLInputElement>(null);
const [fileMcus, setFileMcus] = useState([]);
@@ -196,17 +207,17 @@ export default function CorporatePlanList({handleSubmitSuccess}) {
setTimeout(() => {
loadDataTableData();
}, 2000);
enqueueSnackbar(responseData.message ?? 'Berhasil tambah file MemberID '+member_id+', silahkan lihat dilaporan', { variant: 'success' });
enqueueSnackbar(responseData.message ?? 'Berhasil tambah file Member ID '+member_id+', silahkan lihat dilaporan', { variant: 'success' });
handleSubmitSuccess();
}
})
.catch(({ response }) => {
const responseData = response?.data;
if(responseData)
{
enqueueSnackbar(responseData.message ?? 'Something Went Wrong', { variant: 'error' });
}
if(responseData)
{
enqueueSnackbar(responseData.message ?? 'Something Went Wrong', { variant: 'error' });
}
})
.then(() => {
setSubmitLoading(false);
@@ -319,12 +330,10 @@ export default function CorporatePlanList({handleSubmitSuccess}) {
{!currentImportFileName && (
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter} />
{/* <h1>kjasndkjandskjasndkjansdkjansd</h1> */}
<Button
id="import-button"
variant="outlined"
startIcon={<AddIcon />}
sx={{ p: 1.8 }}
startIcon={<DownloadIcon />}
sx={{ p: 1.8, color: '#FFFFFF', backgroundColor: '#19BBBB', width: '125px', height: '48px' }}
aria-controls={createMenu ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={createMenu ? 'true' : undefined}
@@ -341,15 +350,19 @@ export default function CorporatePlanList({handleSubmitSuccess}) {
'aria-labelledby': 'basic-button',
}}
>
<MenuItem onClick={handleImportButton}>Import</MenuItem>
<MenuItem onClick={handleImportButton}>
<Typography variant='body2'>Import</Typography>
</MenuItem>
<MenuItem
onClick={() => {
handleGetTemplate('member');
}}
>
Download Template
<Typography variant='body2'> Download Template</Typography>
</MenuItem>
<MenuItem onClick={handleMemberList}>
<Typography variant='body2'>Download Member</Typography>
</MenuItem>
<MenuItem onClick={handleMemberList}>Download Member</MenuItem>
</Menu>
</Stack>
)}
@@ -412,17 +425,14 @@ export default function CorporatePlanList({handleSubmitSuccess}) {
}
const [columns, setColumns] = React.useState([
{ id: 'member_id', label: 'MemberID', minWidth: 100, align: 'left' },
// { id: 'principal_id', label: 'Mapping ID', minWidth: 100, align: 'left' },
// { id: 'nik', label: 'NIK', minWidth: 100, align: 'left' },
// { id: 'current_policy.policy_number', label: 'Policy Number', minWidth: 100, align: 'left' },
{ id: 'effective_date', label: 'Effective Date', minWidth: 100, align: 'left' },
{ id: 'name', label: 'Name', minWidth: 100, align: 'left' },
// { id: 'nric', label: 'NRIC', minWidth: 100, align: 'left' },
// { id: 'email', label: 'E-mail', minWidth: 100, align: 'left' },
{ id: 'plan_id', label: 'PlanID', minWidth: 100, align: 'left' },
{ id: 'activation_date', label: 'Activation Date', minWidth: 100, align: 'left' },
{ id: 'termination_date', label: 'Termination Date', minWidth: 100, align: 'left' },
{ id: 'member_id', label: 'Member ID', minWidth: 100, align: 'left', width: '15%' },
{ id: 'effective_date', label: 'Effective Date', minWidth: 100, align: 'left', width: '15%' },
{ id: 'name', label: 'Name', minWidth: 100, align: 'left', width: '20%' },
{ id: 'plan_id', label: 'Plan', minWidth: 100, align: 'left', width: '10%' },
{ id: 'activation_date', label: 'Activation Date', minWidth: 100, align: 'left', width: '15%' },
{ id: 'termination_date', label: 'Termination Date', minWidth: 100, align: 'left', width: '15%' },
{id: 'status', label: 'Status', minWidth: 100, align: 'left', width: '5%' },
{id: 'action', label: '', minWidth: 100, align: 'left', width: '5%' },
]);
// Generate the every row of the table
@@ -432,17 +442,9 @@ export default function CorporatePlanList({handleSubmitSuccess}) {
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`, {
// service_code: service.service_code,
active: status == 'active',
})
.then((res) => {
@@ -458,284 +460,227 @@ export default function CorporatePlanList({handleSubmitSuccess}) {
});
})
.catch((error) => {
// console.log('asdasd', error.response.data.message)
enqueueSnackbar(
error.response.data.message ?? error.message ?? 'Failed Processing Request',
{ variant: 'error' }
);
});
};
const style1 = {
color: '#637381'
}
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
<TableRow key={row.id || index} sx={{ '& > *': open ? {borderBottom: 'unset'} : {}, cursor: open ? 'pointer' : '' }} onClick={() => {if(open==true) setOpen(!open)}}>
<TableCell align="left">
<Typography variant='body2'>{row.member_id ? row.member_id : '-'}</Typography>
</TableCell>
<TableCell align="left">{row.member_id}</TableCell>
<TableCell align="left">{row.members_effective_date}</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">{row.current_plan?.code}</TableCell>
<TableCell align="left">{row.activation_date}</TableCell>
<TableCell align="left">{row.terminated_date}</TableCell>
<TableCell align="center">
{row.active == 1 && (
<Button
variant="outlined"
color="success"
size="small"
onClick={() => {
// handleActivate(row, 'inactive');
clickHandler('edit');
setEdit({id: row.id, service_code: row.service_code, status: 'inactive'});
}}
>
<TableCell align="left">
<Typography variant='body2'>{row.members_effective_date ? row.members_effective_date : '-'}</Typography>
</TableCell>
<TableCell align="left">
<Typography variant='body2'>{row.name ? row.name : '-'}</Typography>
</TableCell>
<TableCell align="left">
<Typography variant='body2'>
{/* {row.current_plan?.code} */}
{row.current_plans
? row.current_plans.map((plan, index) => (
<>
{plan.code}
{index < row.current_plans.length - 1 && ', '}
</>
))
: '-'}
</Typography>
</TableCell>
<TableCell align="left">
<Typography variant='body2'>{row.activation_date ? row.activation_date : '-'}</Typography>
</TableCell>
<TableCell align="left">
<Typography variant='body2'>{row.terminated_date ? row.terminated_date : '-'}</Typography>
</TableCell>
<TableCell align="left">
<Typography variant='body2'>
{row.active === 1 ? (
<Label color='success' >
Active
</Button>
)}
{row.active != 1 && (
<Button
variant="outlined"
color="error"
size="small"
onClick={() => {
// handleActivate(row, 'active');
clickHandler('edit');
setEdit({id: row.id, service_code: row.service_code, status: 'active'});
}}
>
</Label>
) : (
<Label color='error'>
Inactive
</Button>
</Label>
)}
</Typography>
</TableCell>
<TableCell align="right">
<Tooltip title="History">
<Link to={`/corporate/${corporate_id}/members/${row.id}/history`}>
<HistoryIcon />
</Link>
</Tooltip>
<TableCell align='left'>
<TableMoreMenu actions={
<>
<MenuItem onClick={() => setOpen(!open)}>
<FindInPageOutlinedIcon />
Details
</MenuItem>
<MenuItem onClick={() => handleEditDataStatus(row)}>
<CachedOutlinedIcon />
Update Status
</MenuItem>
<MenuItem onClick={() => navigate ('/corporates/'+corporate_id+'/members/'+row.id+'/history')}>
<HistoryIcon />
History
</MenuItem>
</>
}
/>
</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow>
<TableCell />
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={15}>
<TableRow sx={{display: open ? '' : 'none', cursor: open ? 'pointer' : ''}} onClick={() => {if(open==true) setOpen(!open)}}>
<TableCell colSpan={8}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Box sx={{ pb: 2 }}>
<Typography sx={{ fontWeight: '600', mb: 1 }}>Detail</Typography>
<Grid container sx={{ pb: 2, mb: 2, borderBottom: 1 }}>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Mapping ID
</Grid>
<Grid item xs={6}>
: {row.principal_id ?? '-'}
</Grid>
<Grid item xs={6}>
Policy Number
</Grid>
<Grid item xs={6}>
: {row.current_policy?.code ?? '-'}
</Grid>
<Grid item xs={6}>
NRIC
</Grid>
<Grid item xs={6}>
: {row.nric ?? '-'}
</Grid>
<Grid item xs={6}>
NIK
</Grid>
<Grid item xs={6}>
: {row.employeds[0]?.nik ?? '-'}
</Grid>
<Grid item xs={6}>
Email
</Grid>
<Grid item xs={6}>
: {row.email ?? '-'}
</Grid>
<Grid item xs={6}>
Phone
</Grid>
<Grid item xs={6}>
: {row.person?.phone ?? '-'}
</Grid>
</Grid>
</Grid>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Birth Date
</Grid>
<Grid item xs={6}>
: {row.birth_date ?? '-'}
</Grid>
<Grid item xs={6}>
Gender
</Grid>
<Grid item xs={6}>
: {row.gender ?? '-'}
</Grid>
<Grid item xs={6}>
Marital Status
</Grid>
<Grid item xs={6}>
: {row.marital_status ?? '-'}
</Grid>
<Grid item xs={6}>
Language
</Grid>
<Grid item xs={6}>
: {row.language ?? '-'}
</Grid>
<Grid item xs={6}>
Race
</Grid>
<Grid item xs={6}>
: {row.race ?? '-'}
</Grid>
<Grid item xs={6}>
Relationship
</Grid>
<Grid item xs={6}>
: {row.relation_with_principal ?? '-'}
</Grid>
</Grid>
</Grid>
</Grid>
<Typography sx={{ fontWeight: '600', mb: 1 }}>Claim History</Typography>
<Grid container sx={{ pb: 2, mb: 2, borderBottom: 1 }}>
<Grid item xs={6}>
<Grid container>
<Grid item xs={6}>
Requested
</Grid>
<Grid item xs={6}>
: {row.total_claims?.requested}
</Grid>
<Grid item xs={6}>
Pending
</Grid>
<Grid item xs={6}>
: {row.total_claims?.received}
</Grid>
<Grid item xs={6}>
Approved
</Grid>
<Grid item xs={6}>
: {row.total_claims?.approved}
</Grid>
<Grid item xs={6}>
Declined
</Grid>
<Grid item xs={6}>
: {row.total_claims?.declined}
</Grid>
<Grid item xs={6}>
Paid
</Grid>
<Grid item xs={6}>
: {row.total_claims?.paid}
</Grid>
</Grid>
</Grid>
</Grid>
<Typography sx={{ fontWeight: '600', mb: 1 }}>File History</Typography>
<Grid container sx={{ pb: 2, mb: 2, borderBottom: 1 }}>
<Grid item xs={12}>
<Grid container>
<Grid item xs={12}>
{row.file_mcu_names
? row.file_mcu_names.split(',').map((fileName, index) => (
<div key={index}>{fileName}</div>
))
: '-'}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid spacing={1}>
<Stack sx={{ marginTop: 1}}>
<LoadingButton
id="upload-button"
variant="outlined"
startIcon={<InsertDriveFileIcon />}
// sx={{ p: 1.8 }}
// onClick={() => {handleDownloadLog(row)}}
onClick={() => {
setDialogLogOpen(true);
}}
loading={loadingLog}
>
Download LOG
</LoadingButton>
<Card sx={{padding:2}}>
<Box sx={{ pb: 2 }}>
<Typography variant='subtitle1'>Detail</Typography>
<Stack marginTop={2}>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Mapping ID:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.principal_id ? row.principal_id : '-'}</Typography>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Birth Date:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.birth_date ? row.birth_date : '-'}</Typography>
</Stack>
{/* -------------------------------Upload Dokumen MCU------------------------------- */}
<Stack sx={{ marginTop: 1}}>
{/*<Stack
divider={<Divider orientation="horizontal" flexItem />}
spacing={1}
sx={{ marginY: 2}}
>
{fileMcus &&
fileMcus
.filter((datas) => datas.id === row.id)
.map((datas, index) => (
<Stack direction="row" justifyContent={'space-between'} key={index}>
<Typography sx={{ color: "text.secondary" }}>{datas.file.name}</Typography>
<Iconify
icon="eva:trash-2-outline"
color={'darkred'}
onClick={() => {
removeMcuFiles(datas.id, index);
}}
sx={{ cursor: 'pointer' }}
></Iconify>
</Stack>
))}
</Stack>*/}
<input
type="file"
id={`file-${row.id}`}
ref={fileMcuInput}
style={{ display: 'none' }}
onChange={(event) => {
handleMcuInputChange(row.id, row.member_id)(event);
}}
accept="application/pdf"
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileMcuInput.current.click();
}}
>
<Iconify icon="eva:plus-fill" />
Add Result
</LoadingButton>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Policy Number:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.current_policy?.code ? row.current_policy?.code : '-'}</Typography>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Gender:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.gender ? row.gender : '-'}</Typography>
</Stack>
</Grid>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>NRIC:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.nric ? row.nric : '-'}</Typography>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Marital Status:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.marital_status ? row.marital_status : '-'}</Typography>
</Stack>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>NIK:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.employeds[0]?.nik ? row.employeds[0]?.nik : '-'}</Typography>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Language:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.language ? row.language : '-'}</Typography>
</Stack>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Email:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.email ? row.email : '-'}</Typography>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Race:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.race ? row.race : '-'}</Typography>
</Stack>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Phone Number:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.person?.phone ? row.person?.phone : '-'}</Typography>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Relationship:</Typography>
<Typography variant='body2' sx={{width: '25%'}}>{row.relation_with_principal ? row.relation_with_principal : '-'}</Typography>
</Stack>
</Stack>
<Typography variant='subtitle1' marginTop={2}>Claim History</Typography>
<Stack marginTop={2}>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Requested:</Typography>
<Typography variant='body2' sx={{width: '75%'}}>{row.total_claims?.requested ? row.total_claims?.requested : '-'}</Typography>
</Stack>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Pending:</Typography>
<Typography variant='body2' sx={{width: '75%'}}>{row.total_claims?.received ? row.total_claims?.received : '-'}</Typography>
</Stack>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Approved:</Typography>
<Typography variant='body2' sx={{width: '75%'}}>{row.total_claims?.approved ? row.total_claims?.approved : '-'}</Typography>
</Stack>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Declined:</Typography>
<Typography variant='body2' sx={{width: '75%'}}>{row.total_claims?.declined ? row.total_claims?.declined : '-'}</Typography>
</Stack>
<Stack direction='row' spacing={1}>
<Typography variant='body2' sx={{color: style1.color, width: '25%'}}>Paid:</Typography>
<Typography variant='body2' sx={{width: '75%'}}>{row.total_claims?.paid ? row.total_claims?.paid : '-'}</Typography>
</Stack>
</Stack>
<Typography variant='subtitle1' marginTop={2}>Files History</Typography>
<Stack marginTop={2}>
{row.file_mcu_names
? row.file_mcu_names.split(',').map((fileName, index) => (
<>
<Stack direction='row' spacing={1} alignItems='center'>
<InsertDriveFileIcon />
<Typography key={index} variant='body2' sx={{width: '100%'}}>{fileName}</Typography>
</Stack>
</>
))
: '-'}
</Stack>
<Stack marginTop={2}>
<Stack direction='row' spacing={1} alignItems='center'>
<ButtonBase sx={{ p: 4, border: '2px dashed #F9FAFB',
bgcolor: '#919EAB52',
borderRadius: '8px',
width: '100%', height: '60px'}} onClick={() => fileMcuInput.current?.click()}>
<Box
sx={{
display: 'flex',
placeItems: 'center',
gap: 1,
placeContent: 'center',
}}
>
<Iconify icon="icon-park-outline:upload-one" fontSize="3em" />
<Typography variant="body1" fontWeight="bold">
Upload Result
</Typography>
</Box>
<input
type="file"
id={`file-${row.id}`}
ref={fileMcuInput}
style={{ display: 'none' }}
onChange={(event) => {
handleMcuInputChange(row.id, row.member_id)(event);
}}
accept="application/pdf"
/>
</ButtonBase>
</Stack>
</Stack>
</Box>
</Card>
<Stack marginTop={2}>
<Stack direction='row' alignItems='center' spacing={1} justifyContent="flex-end">
<LoadingButton
id="upload-button"
sx={{ p: 1.8, color: '#FFFFFF', backgroundColor: '#19BBBB', width: '158px', height: '48px' }}
startIcon={<DownloadIcon />}
// sx={{ p: 1.8 }}
// onClick={() => {handleDownloadLog(row)}}
onClick={() => {
setDialogLogOpen(true);
}}
loading={loadingLog}
>
Download LOG
</LoadingButton>
</Stack>
</Stack>
<DialogLog
title={{
name: `Generate LOG - ${row.full_name}`,
}}
openDialog={dialogLogOpen}
setOpenDialog={setDialogLogOpen}
data={{ member: row }}
></DialogLog>
<DialogLog
title={{
name: `Generate LOG - ${row.full_name}`,
}}
openDialog={dialogLogOpen}
setOpenDialog={setDialogLogOpen}
data={{ member: row }}
></DialogLog>
</Box>
</Collapse>
</TableCell>
</TableRow>
@@ -746,33 +691,73 @@ export default function CorporatePlanList({handleSubmitSuccess}) {
const headStyle = {
fontWeight: 'bold',
};
const [reasonUpdate,setReasonUpdate] = useState('Agreement changed');
const [nameUpdate, setNameUpdate] = useState('');
const [memberIdUpdate, setMemberIdUpdate] = useState('');
const [activeUpdate, setActiveUpdate] = useState(0);
const [idUpdate, setIdUpdate] = useState('');
const [openDialogStatus, setOpenDialogStatus] = useState(false);
const handleCloseDialogUpdate = () => {
setNameUpdate('');
setMemberIdUpdate('');
setActiveUpdate(activeUpdate);
setIdUpdate('');
setOpenDialogStatus(false);
}
const handleSaveUpdateData = () => {
let activeValue = 0;
if(activeUpdate === 1)
{
activeValue = 0;
}
else
{
activeValue = 1;
}
const updateData = {
reason: reasonUpdate,
active : activeValue,
id: idUpdate,
};
axios
.put('/members/'+idUpdate+'/activation', updateData)
.then((response) => {
enqueueSnackbar('Data updated successfully', { variant: 'success' });
loadDataTableData();
handleCloseDialogUpdate();
})
.catch((error) => {
enqueueSnackbar('Failed to add data', { variant: 'error' });
});
}
const handleEditDataStatus = (data:any) => {
setNameUpdate(data.name);
setMemberIdUpdate(data.member_id);
setActiveUpdate(data.active);
setIdUpdate(data.id);
setOpenDialogStatus(true);
}
return (
<Stack>
<ImportForm />
<Card>
{/* The Main Table */}
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
<TableHead>
<TableRow>
<TableCell style={headStyle} align="left" />
{columns.map((column, index) => (
<TableCell style={headStyle} key={index} align={column.align}>
{column.label}
<TableCell style={{ minWidth: column.minWidth, width: column.width }} key={index} align={column.align}>
<Typography variant='subtitle2'>{column.label}</Typography>
</TableCell>
))}
<TableCell style={headStyle} align="center">
Status
</TableCell>
<TableCell style={headStyle} align="center">
Action
</TableCell>
{/* <TableCell style={headStyle} align="center">
Action
</TableCell> */}
<TableCell align="center"></TableCell>
</TableRow>
</TableBody>
</TableHead>
{dataTableIsLoading ? (
<TableBody>
<TableRow>
@@ -801,6 +786,62 @@ export default function CorporatePlanList({handleSubmitSuccess}) {
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange} />
</Card>
{/* Dialog Update Status */}
<Dialog open={openDialogStatus} onClose={handleCloseDialogUpdate} fullWidth={true}>
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" alignItems='center' spacing={1}>
<Typography variant="h6">Update Status</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogUpdate}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2} padding={2}>
<Typography variant='body1'>Are you sure to {activeUpdate == 1 ? 'Inactive' : 'Active'} this member ?</Typography>
<Card sx={{padding:2}} >
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Member ID</Typography>
<Typography variant='subtitle2' sx={{width: '70%'}}>{memberIdUpdate}</Typography>
</Stack>
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '30%'}}>Name</Typography>
<Typography variant='subtitle2' sx={{width: '70%'}}>{nameUpdate}</Typography>
</Stack>
</Card>
</Stack>
<Stack spacing={2} padding={2}>
<Typography variant='subtitle1'>Reason for update*</Typography>
<FormControl>
<InputLabel htmlFor="reason" required>
Reason for update
</InputLabel>
<Select
id="reason"
value={reasonUpdate}
fullWidth
label="Reason for update"
onChange={(e) => {
setReasonUpdate(e.target.value);
}}
>
<MenuItem value="Agreement changed">Agreement changed</MenuItem>
<MenuItem value="Endorsement">Endorsement</MenuItem>
<MenuItem value="Renewal">Renewal</MenuItem>
<MenuItem value="Worng Setting">Worng Setting</MenuItem>
</Select>
<FormHelperText style={{ color: 'red' }}></FormHelperText>
</FormControl>
</Stack>
</DialogContent>
<DialogActions>
<Button variant="outlined" sx={{color: '#212B36'}} onClick={handleCloseDialogUpdate}>Cancel</Button>
<Button sx={{backgroundColor: activeUpdate == 0 ? '#19BBBB' : '#FF4842'}} onClick={handleSaveUpdateData} variant="contained">{activeUpdate == 1 ? 'Inactive' : 'Active'}</Button>
</DialogActions>
</Dialog>
{isDialog === 'edit' && (
<DialogLog
data={edit}

View File

@@ -76,6 +76,8 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP
const { id, service_code, status } = data;
const [memberId, setMemberId] = useState(data.member.id);
const isEdit = id ? true : false;
const NewCorporateSchema = Yup.object().shape({
@@ -103,10 +105,11 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP
// const { plan_id } = useParams();
const handleActivate = (model: any, status: string) => {
axios
.put(`/members/${id}/activation`, {
.put(`/members/${memberId}/activation`, {
// service_code: service.service_code,
active: status == 'active',
reason: model.reason
reason: model.reason,
id: memberId,
})
.then((res) => {
// Memuat ulang halaman saat ini
@@ -133,7 +136,7 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP
const data = {
service_code : service_code,
reason : row.reason,
id : id,
id : memberId,
}
handleActivate(data, status)
} catch (error: any) {
@@ -157,18 +160,18 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP
<Stack spacing={3}>
<Box sx={{ width: '100%', typography: 'body1', p: 2, mt: 1 }}>
<Grid item xs={12}>
<Typography variant='subtitle1' sx={{marginBottom: 2}}>Reason for update*</Typography>
<RHFSelect
name="reason"
label="Reason for update"
>
<option value=""></option>
<option value="Agreement changed">Agreement changed</option>
<option value="Endorsement">Endorsement</option>
<option value="Renewal">Renewal</option>
<option value="Worng Setting">Worng Setting</option>
</RHFSelect>
name="reason"
label="Reason for update"
>
<option value=""></option>
<option value="Agreement changed">Agreement changed</option>
<option value="Endorsement">Endorsement</option>
<option value="Renewal">Renewal</option>
<option value="Worng Setting">Worng Setting</option>
</RHFSelect>
</Grid>
<Box sx={{ pt: 5 }}>
<Stack
alignItems="center"
@@ -181,6 +184,7 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP
<Button
sx={{
boxShadow: 'none',
color: '#212B36'
}}
variant="outlined"
size="medium"
@@ -190,7 +194,7 @@ const DialogTopUpLimit = ({ title, openDialog, setOpenDialog, data }: MuiDialogP
Cancel
</Button>
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)' }}
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)', backgroundColor: '#19BBBB' }}
type="submit"
variant="contained"
size="medium"

View File

@@ -32,11 +32,11 @@ export default function Divisions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Plan',
href: '/corporate/' + corporate_id + '/plans',
href: '/corporates/' + corporate_id + '/plans',
},
]}
/>

View File

@@ -1,4 +1,5 @@
// @mui
import * as Yup from 'yup';
import {
Box,
Button,
@@ -28,17 +29,22 @@ import {
ButtonGroup,
Grid,
Tooltip,
Autocomplete,
} from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add';
import UploadIcon from '@mui/icons-material/Upload';
import CancelIcon from '@mui/icons-material/Cancel';
import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';
import CachedOutlinedIcon from '@mui/icons-material/CachedOutlined';
import { Controller, useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import HistoryIcon from '@mui/icons-material/History';
// hooks
import React, { ChangeEvent, Component, useEffect, useRef, useState } from 'react';
import useSettings from '../../../hooks/useSettings';
import { Link, useParams, useSearchParams } from 'react-router-dom';
import { Form, useNavigate, Link, useParams, useSearchParams } from 'react-router-dom';
// components
import axios from '../../../utils/axios';
import { Plan } from '../../../@types/corporates';
@@ -47,6 +53,14 @@ import BasePagination from '../../../components/BasePagination';
import { enqueueSnackbar } from 'notistack';
import { LoadingButton } from '@mui/lab';
import DialogLog from './sections/DialogLog';
import { FormProvider, RHFSelect } from '@/components/hook-form';
import { Download, Edit } from '@mui/icons-material';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import Label from '@/components/Label';
import DialogUpdateStatus from '@/components/DialogUpdateStatus';
import {Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
export default function CorporatePlanList() {
const { themeStretch } = useSettings();
@@ -54,6 +68,7 @@ export default function CorporatePlanList() {
const [searchParams, setSearchParams] = useSearchParams();
const [importResult, setImportResult] = useState(null);
const navigate = useNavigate()
const [openDialog, setOpenDialog] = useState(false);
const [dialogTitle, setDialogTitle] = useState('');
const [isDialog, setIsDialog] = useState('');
@@ -69,6 +84,19 @@ export default function CorporatePlanList() {
}
};
// filter
const defaultValue = [
{
value: '-',
label: '-'
}
];
const [serviceCode, setServiceCode] = useState(defaultValue);
const [type, setType] = useState(defaultValue);
const [code, setCode] = useState(defaultValue);
const [codePlan, setCodePlan] = useState(defaultValue);
function SearchInput(props: any) {
// SEARCH
const searchInput = useRef<HTMLInputElement>(null);
@@ -85,7 +113,6 @@ export default function CorporatePlanList() {
};
useEffect(() => {
// console.log('Search Input: useEffect')
setSearchText(searchParams.get('search') ?? '');
}, [searchParams]);
@@ -113,6 +140,8 @@ export default function CorporatePlanList() {
const [currentImportFileName, setCurrentImportFileName] = useState(null);
const [importLoading, setImportLoading] = useState(false);
const { control } = useForm();
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
@@ -195,6 +224,40 @@ export default function CorporatePlanList() {
})
}
const handleFilter = (event: React.SyntheticEvent<Element, Event>, newValue: { value: string, label: string }[], name:string) => { //
const serviceCodeArray :string[] = [];
const codeArray :string[] = [];
const typeArray :string[] = [];
const planArray :string[] = [];
if (name == 'service_code'){
newValue.map(row => {
serviceCodeArray.push(row.value);
})
}
if (name == 'code'){
newValue.map(row => {
codeArray.push(row.value);
})
}
if (name == 'type'){
newValue.map(row => {
typeArray.push(row.value);
})
}
if (name == 'plan'){
newValue.map(row => {
planArray.push(row.value);
})
}
let data = {
service_code : serviceCodeArray,
code : codeArray,
type : typeArray,
plan : planArray,
}
loadDataTableDataFilter(data)
}
return (
<div>
<input
@@ -207,33 +270,113 @@ export default function CorporatePlanList() {
/>
{!currentImportFileName && (
<Stack direction={'row'} spacing={2} sx={{ p: 2 }}>
<SearchInput onSearch={applyFilter} />
<Grid container spacing={2}>
<Grid item xs={3.5}>
<SearchInput onSearch={applyFilter} />
</Grid>
<Grid item xs={2}>
<Autocomplete
id="combo-box-demo"
options={serviceCode}
multiple
limitTags={1}
onChange={(event, newValue) => handleFilter(event, newValue, 'service_code')}
fullWidth
getOptionLabel={(option) => option.label}
isOptionEqualToValue={(option, value) =>
option.value === value.value
}
renderInput={(params) => (
<TextField {...params} label="Service" variant="outlined" />
)}
/>
</Grid>
<Grid item xs={1.5}>
<Autocomplete
id="combo-box-demo"
options={codePlan}
onChange={(event, newValue) => handleFilter(event, newValue, 'plan')}
multiple
limitTags={1}
fullWidth
getOptionLabel={(option) => option.label}
isOptionEqualToValue={(option, value) =>
option.value === value.value
}
renderInput={(params) => (
<TextField {...params} label="Plan" variant="outlined" />
)}
/>
</Grid>
<Grid item xs={1.5}>
<Autocomplete
id="combo-box-demo"
options={code}
onChange={(event, newValue) => handleFilter(event, newValue, 'code')}
multiple
limitTags={1}
fullWidth
getOptionLabel={(option) => option.label}
isOptionEqualToValue={(option, value) =>
option.value === value.value
}
renderInput={(params) => (
<TextField {...params} label="Code" variant="outlined" />
)}
/>
</Grid>
<Grid item xs={1.5}>
<Autocomplete
id="combo-box-demo"
options={type}
multiple
limitTags={1}
fullWidth
getOptionLabel={(option) => option.label}
isOptionEqualToValue={(option, value) =>
option.value === value.value
}
onChange={(event, newValue) => handleFilter(event, newValue, 'type')}
renderInput={(params) => (
<TextField {...params} label="Type" variant="outlined" />
)}
/>
</Grid>
<Grid item xs={1.5}>
<Button
id="import-button"
variant="contained"
startIcon={<Download />}
fullWidth={true}
sx={{ p: 1.8 }}
aria-controls={createMenu ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={createMenu ? 'true' : undefined}
onClick={handleClick}
>
Import
</Button>
<Menu
id="import-button"
anchorEl={anchorEl}
open={createMenu}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
<MenuItem onClick={handleImportButton}>Import</MenuItem>
<MenuItem onClick={() => {handleGetTemplate('plan-benefit')}}>Download Template</MenuItem>
<MenuItem onClick={() => {handleGetData('data-plan-benefit')}}>Download Plans & Benefit</MenuItem>
<MenuItem onClick={() => {setDialogDeleteOpen(true)}}>Delete All Import</MenuItem>
</Menu>
</Grid>
</Grid>
{/* <h1>kjasndkjandskjasndkjansdkjansd</h1> */}
<Button
id="import-button"
variant="outlined"
startIcon={<AddIcon />}
sx={{ p: 1.8 }}
aria-controls={createMenu ? 'basic-menu' : undefined}
aria-haspopup="true"
aria-expanded={createMenu ? 'true' : undefined}
onClick={handleClick}
>
Import
</Button>
<Menu
id="import-button"
anchorEl={anchorEl}
open={createMenu}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
<MenuItem onClick={handleImportButton}>Import</MenuItem>
<MenuItem onClick={() => {handleGetTemplate('plan-benefit')}}>Download Template</MenuItem>
<MenuItem onClick={() => {handleGetData('data-plan-benefit')}}>Download Plans & Benefit</MenuItem>
</Menu>
</Stack>
)}
@@ -286,47 +429,51 @@ export default function CorporatePlanList() {
};
}
type DataContent = {
code: string;
name: string;
id: number;
status: string|number;
};
type FormValuesProps = {
value: string;
active: boolean;
};
// Generate the every row of the table
const [isDialogOpen, setDialogOpen] = useState(false)
let titles = {
name: 'Update Status',
icon: '-'
}
const [dataValue, setDataValue] = useState('');
const [dataDescription, setDescriptionValue] = useState('');
const [url, setUrl] = useState('');
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const handleActivate = (model: any, status: string) => {
axios
.put(`/plans/${row.id}/activation`, {
// service_code: service.service_code,
active: status == 'active',
})
.then((res) => {
setDataTableData({
...dataTableData,
data: dataTableData.data.map((model) => {
let updatedModel = model;
if (row.id == model.id) {
updatedModel.active = res.data.plan.active;
}
return updatedModel;
}),
});
})
.catch((error) => {
// console.log('asdasd', error.response.data.message)
enqueueSnackbar(
error.response.data.message ?? error.message ?? 'Failed Processing Request',
{ variant: 'error' }
);
});
const handleActivate = (isOpen: boolean, dataValue: DataContent) => {
setDialogOpen(isOpen)
setDataValue(dataValue)
setDescriptionValue('Are you sure to inactive this service ?')
setUrl(url)
};
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
<TableRow>
{/* <TableCell> */}
{/* <IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</IconButton> */}
{/* </TableCell> */}
<TableCell sx={{ borderBottom: '1px solid rgba(145, 158, 171, 0.24)' }} align="left">
{row.service_code}
</TableCell>
<TableCell align="left">{row.service_code}</TableCell>
<TableCell align="left">{row.corporate_plan_id}</TableCell>
<TableCell align="left">{row.code}</TableCell>
<TableCell align="left">{row.type}</TableCell>
@@ -334,41 +481,27 @@ export default function CorporatePlanList() {
<TableCell align="left">{row.limit_rules}</TableCell>
<TableCell align="center">
{row.active == 1 && (
<Button
variant="outlined"
color="success"
size="small"
onClick={() => {
// handleActivate(row, 'inactive');
clickHandler('edit');
setEdit({id: row.id, service_code: row.service_code, status: 'inactive'});
}}
>
Active
</Button>
)}
{row.active != 1 && (
<Button
variant="outlined"
color="error"
size="small"
onClick={() => {
// handleActivate(row, 'active');
clickHandler('edit');
setEdit({id: row.id, service_code: row.service_code, status: 'active'});
}}
>
Inactive
</Button>
)}
{row.active == 1 ?
<Label color='success'>Active</Label> :
<Label color='error'>Inactive</Label>}
</TableCell>
<TableCell align="center">
<Tooltip title="History">
<Link to={`/corporate/${corporate_id}/plans/${row.id}/history`}>
<HistoryIcon />
</Link>
</Tooltip>
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate(`/corporates/${corporate_id}/corporate-plans/${row.id}/edit`)}>
<Edit />
Edit
</MenuItem>
<MenuItem onClick={() => navigate(`/corporates/${corporate_id}/plans/${row.id}/history`)}>
<HistoryIcon />
History
</MenuItem>
<MenuItem onClick={() => handleActivate(true, {code: row.code, name: row.service_code, id:row.id, status: row.active})}>
<CachedOutlinedIcon />
Update Status
</MenuItem>
</>
} />
</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
@@ -679,16 +812,153 @@ export default function CorporatePlanList() {
setDataTableLoading(true);
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
const response = await axios.get('/corporates/' + corporate_id + '/plans', { params: filter });
// console.log(response.data);
setDataTableLoading(false);
setDataTableData(response.data);
const data = response.data.data;
const serviceCodeArray :string[] = [];
const codeArray :string[] = [];
const typeArray :string[] = [];
const planArray :string[] = [];
data.forEach((row: any) => {
if (!serviceCodeArray.includes(row.service_code)) {
serviceCodeArray.push(row.service_code);
}
if (!codeArray.includes(row.code)) {
codeArray.push(row.code);
}
if (!typeArray.includes(row.type)) {
typeArray.push(row.type);
}
if (!planArray.includes(row.corporate_plan_id)) {
planArray.push(row.corporate_plan_id);
}
});
const optionServiceCode = serviceCodeArray.map((value) => {
return {
value: value,
label: value
};
});
const optionCode = codeArray.map((value) => {
return {
value: value,
label: value
};
});
const optionType = typeArray.map((value) => {
return {
value: value,
label: value
};
});
const optionPlan = planArray.map((value) => {
return {
value: value,
label: value
};
});
setServiceCode(optionServiceCode)
setType(optionType)
setCode(optionCode)
setCodePlan(optionPlan)
};
const loadDataTableDataFilter = async (appliedFilter = null) => {
setDataTableLoading(true);
let filter = appliedFilter
const response = await axios.post('/corporates/' + corporate_id + '/corporate-plans/filter',filter);
setDataTableLoading(false);
setDataTableData(response.data);
const data = response.data.data;
const serviceCodeArray :string[] = [];
const codeArray :string[] = [];
const typeArray :string[] = [];
const planArray :string[] = [];
// data.forEach((row: any) => {
// if (!serviceCodeArray.includes(row.service_code)) {
// serviceCodeArray.push(row.service_code);
// }
// if (!codeArray.includes(row.code)) {
// codeArray.push(row.code);
// }
// if (!typeArray.includes(row.type)) {
// typeArray.push(row.type);
// }
// if (!planArray.includes(row.corporate_plan_id)) {
// planArray.push(row.corporate_plan_id);
// }
// });
// const optionServiceCode = serviceCodeArray.map((value) => {
// return {
// value: value,
// label: value
// };
// });
// const optionCode = codeArray.map((value) => {
// return {
// value: value,
// label: value
// };
// });
// const optionType = typeArray.map((value) => {
// return {
// value: value,
// label: value
// };
// });
// const optionPlan = planArray.map((value) => {
// return {
// value: value,
// label: value
// };
// });
// setServiceCode(optionServiceCode)
// setType(optionType)
// setCode(optionCode)
// setCodePlan(optionPlan)
};
const headStyle = {
fontWeight: 'bold',
};
const onSubmit = async (row : any) => {
try {
handleUpdate(dataValue.status, dataValue.id, row.reason)
} catch (error: any) {
console.log('data gagal');
}
const ascent = document?.querySelector('ascent');
if (ascent != null) {
ascent.innerHTML = '';
}
};
const applyFilter = async (searchFilter: string) => {
await loadDataTableData({ search: searchFilter });
setSearchParams({ search: searchFilter });
@@ -704,17 +974,146 @@ export default function CorporatePlanList() {
loadDataTableData();
}, []);
const NewCorporateSchema = Yup.object().shape({
reason: Yup.string().required('Reason Edit is required'),
});
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema),
});
const {
reset,
handleSubmit,
formState: { isSubmitting },
} = methods;
const handleUpdate = (active: number, id: number, reason:string) => {
console.log(active)
axios
.put(`/plans/${id}/activation`, {
active: active,
reason: reason
})
.then((res) => {
window.location.reload();
});
}
const handleDeleteAllImport = () => {
axios
.post(`/corporates/${corporate_id}/delete-import-plan-benefit`)
.then((res) => {
window.location.reload();
});
}
const [isOpenDialogDelete, setDialogDeleteOpen] = useState(false);
const handleCloseDialogDelete = () => {
setDialogDeleteOpen(false);
};
const getContent = () => (
<>
<Stack paddingX={2} paddingY={2}>
<Typography variant='subtitle1'>Are you sure to {dataValue?.status == 1 ? 'inactive' : 'active'} this service ?</Typography>
</Stack>
<Card>
<Grid container paddingX={2} paddingY={2}>
<Grid item xs={4} md={4}>
<Typography variant='inherit'>Code</Typography>
</Grid>
<Grid item xs={8}>
<Typography variant='subtitle1'>{dataValue?.code}</Typography>
</Grid>
<Grid item xs={4} md={4} marginTop={2}>
<Typography variant='inherit'>Service Name</Typography>
</Grid>
<Grid item xs={8} marginTop={2}>
<Typography variant='subtitle1'>{dataValue?.name}</Typography>
</Grid>
</Grid>
</Card>
<Typography marginTop={5} marginBottom={3} variant='subtitle1'>
Reason for update*
</Typography>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<RHFSelect
name="reason"
label="Reason for update"
>
<option value=""></option>
<option value="Agreement changed">Agreement changed</option>
<option value="Endorsement">Endorsement</option>
<option value="Renewal">Renewal</option>
<option value="Worng Setting">Worng Setting</option>
</RHFSelect>
<Stack
alignItems="center"
justifyContent="flex-end"
direction={{ xs: 'column', md: 'row' }}
spacing={2}
marginTop={5}
>
<Stack direction="row" spacing={1}>
<Button
sx={{
boxShadow: 'none',
}}
variant="outlined"
size="medium"
fullWidth={true}
onClick={() => setDialogOpen(false)}
>
Cancel
</Button>
{dataValue?.status == 1?
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)'}}
type="submit"
variant="contained"
size="medium"
fullWidth={true}
loading={isSubmitting}
color='error'
>
Inactive
</LoadingButton>
:
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)'}}
type="submit"
variant="contained"
size="medium"
fullWidth={true}
loading={isSubmitting}
color='success'
>
Active
</LoadingButton>
}
</Stack>
</Stack>
</FormProvider>
</>
);
return (
<Stack>
<ImportForm />
<Card>
{/* <Card> */}
{/* The Main Table */}
<TableContainer component={Paper}>
<TableContainer>
<Table aria-label="collapsible table">
<TableBody>
<TableHead>
<TableRow>
<TableCell style={headStyle} align="left" />
{/* <TableCell style={headStyle} align="left" /> */}
<TableCell style={headStyle} align="left">
Service
</TableCell>
@@ -737,7 +1136,7 @@ export default function CorporatePlanList() {
Action
</TableCell>
</TableRow>
</TableBody>
</TableHead>
{dataTableIsLoading ? (
<TableBody>
<TableRow>
@@ -765,7 +1164,7 @@ export default function CorporatePlanList() {
</TableContainer>
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange} />
</Card>
{/* </Card> */}
{isDialog === 'edit' && (
<DialogLog
@@ -775,6 +1174,38 @@ export default function CorporatePlanList() {
title={{ name: 'Reason For Update' }}
/>
)}
<DialogUpdateStatus
openDialog={isDialogOpen}
setOpenDialog={setDialogOpen}
title={titles}
data={dataValue}
description={dataDescription}
content={getContent()}
// maxWidth='50px'
/>
<Dialog open={isOpenDialogDelete} onClose={handleCloseDialogDelete} fullWidth={true} >
<DialogTitle sx={{ backgroundColor: '#19BBBB', color: '#FFF', padding: 2 }}>
<Stack direction="row" alignItems="center" justifyContent="space-between">
<Stack direction="row" alignItems='center' spacing={1}>
<Typography variant="h6">Delete Import Benefit</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={handleCloseDialogDelete}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2} sx={{marginTop: 2, padding: 2}}>
<Typography variant='subtitle1'>Are you sure delete all data benefit and plan?</Typography>
</Stack>
</DialogContent>
<DialogActions>
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialogDelete}>Cancel</Button>
<Button sx={{backgroundColor: '#19BBBB'}} variant="contained" onClick={() => handleDeleteAllImport()}>Delete</Button>
</DialogActions>
</Dialog>
</Stack>
);
}

View File

@@ -121,11 +121,11 @@ export default function CustomizedAccordions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id + '/plans',
href: '/corporates/' + corporate_id + '/plans',
},
{
name: 'Audittrail Corporate',
href: '/corporate/' + corporate_id + '/plans',
name: 'Corporate Dashboard',
href: '/corporates/' + corporate_id + '/plans',
},
]}
/>

File diff suppressed because it is too large Load Diff

View File

@@ -34,11 +34,11 @@ export default function Divisions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id,
href: '/corporates/' + corporate_id,
},
{
name: 'Services',
href: '/corporate/' + corporate_id + '/services',
href: '/corporates/' + corporate_id + '/services',
},
]}
/>

View File

@@ -30,17 +30,21 @@ import {
Checkbox,
FormControlLabel,
Tooltip,
Divider,
Grid,
} from '@mui/material';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
import AddIcon from '@mui/icons-material/Add';
import UploadIcon from '@mui/icons-material/Upload';
import CancelIcon from '@mui/icons-material/Cancel';
import SettingsOutlinedIcon from '@mui/icons-material/SettingsOutlined';
import CachedOutlinedIcon from '@mui/icons-material/CachedOutlined';
import HistoryIcon from '@mui/icons-material/History';
// hooks
import React, { ChangeEvent, Component, useEffect, useMemo, useRef, useState } from 'react';
import useSettings from '../../../hooks/useSettings';
import { Link, useParams, useSearchParams } from 'react-router-dom';
import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom';
// components
import axios from '../../../utils/axios';
import { LaravelPaginatedData } from '../../../@types/paginated-data';
@@ -48,11 +52,17 @@ import { Icd } from '../../../@types/diagnosis';
import BasePagination from '../../../components/BasePagination';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { RHFCheckbox } from '../../../components/hook-form';
import { CheckBox } from '@mui/icons-material';
import { FormProvider, RHFTextField, RHFSwitch, RHFSelect } from '../../../components/hook-form';
import { Add, CheckBox } from '@mui/icons-material';
import { CorporateService } from '../../../@types/corporates';
import { number } from 'yup/lib/locale';
import DialogLog from './sections/DialogLog';
import TableMoreMenu from '@/components/table/TableMoreMenu';
import Label from '@/components/Label';
import DialogUpdateStatus from '@/components/DialogUpdateStatus';
import palette from '@/theme/palette';
import { enqueueSnackbar } from 'notistack';
import { LoadingButton } from '@mui/lab';
export default function List() {
const { themeStretch } = useSettings();
@@ -154,9 +164,39 @@ export default function List() {
}
// Generate the every row of the table
type DataContent = {
code: string;
name: string;
id: string;
status: string|number;
};
type DataType = {
code: string;
name: string;
id: string;
}
type FormValuesProps = {
value: string;
active: boolean;
};
const [isDialogOpen, setDialogOpen] = useState(false)
let titles = {
name: 'Update Status',
icon: '-'
}
const [dataValue, setDataValue] = useState('');
const [dataDescription, setDescriptionValue] = useState('');
const [url, setUrl] = useState('');
// const { id, service_code, status } = data;
function Row(props: { row: ReturnType<typeof createData> }) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const navigate = useNavigate()
const handleConfigChange = (event: ChangeEvent<HTMLInputElement>, service: any) => {
console.log(event.target.name, event.target.checked, service);
@@ -168,85 +208,67 @@ export default function List() {
});
};
const handleActivate = (service: any, status: string) => {
axios
.put(`/corporates/${corporate_id}/services/${service.service_code}`, {
service_code: service.service_code,
status,
reason:service.reason
})
.then((res) => {
setDataTableData({
...dataTableData,
data: dataTableData.data.map((service) => {
let updatedService = service;
if (row.id == service.id) {
updatedService.status = res.data.status;
}
return updatedService;
}),
});
});
// const handleActivate = (service: any, status: string) => {
// axios
// .put(`/corporates/${corporate_id}/services/${service.service_code}`, {
// service_code: service.service_code,
// status,
// reason:service.reason
// })
// .then((res) => {
// setDataTableData({
// ...dataTableData,
// data: dataTableData.data.map((service) => {
// let updatedService = service;
// if (row.id == service.id) {
// updatedService.status = res.data.status;
// }
// return updatedService;
// }),
// });
// });
// };
const handleActivate = (isOpen: boolean, dataValue: DataContent) => {
setDialogOpen(isOpen)
setDataValue(dataValue)
setDescriptionValue('Are you sure to inactive this service ?')
setUrl(url)
};
return (
<React.Fragment>
<TableRow sx={{ '& > *': { borderBottom: 'unset' } }}>
{/* <TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowDownIcon /> : <KeyboardArrowRightIcon />}
</IconButton>
</TableCell> */}
<TableRow>
<TableCell>
</TableCell>
<TableCell align="left">{row.service_code}</TableCell>
<TableCell align="left">{row.name}</TableCell>
<TableCell align="left">
{row.status == 'active' ?
<Label color='success'>{row.status}</Label> :
<Label color='error'>{row.status}</Label>}
</TableCell>
<TableCell align="right">
{row.status == 'active' && (
<Button
variant="outlined"
color="success"
size="small"
onClick={() => {
// handleActivate(row, 'inactive', 'test');
clickHandler('edit');
setEdit({id: row.id, service_code: row.service_code, status: 'inactive'});
}}
>
Active
</Button>
)}
{row.status == 'inactive' && (
<Button
variant="outlined"
color="error"
size="small"
onClick={() => {
clickHandler('edit');
setEdit({id: row.id, service_code: row.service_code, status: 'active'});
}}
>
Inactive
</Button>
)}
</TableCell>
<TableCell align="right" width='10%'>
<Link to={`/corporate/${corporate_id}/services/${row.service_code}`}>
<Button variant="outlined" color="primary" size="small">
Config
</Button>
</Link>
</TableCell>
<TableCell width='1%'>
<Tooltip title="History">
<Link to={`/corporate/${corporate_id}/services/${row.id}/history`} >
<HistoryIcon/>
</Link>
</Tooltip>
<TableCell align="right" sx={{borderBottom: 'unset'}}>
<TableMoreMenu actions={
<>
<MenuItem onClick={() => navigate(`/corporates/${row.corporate_id}/services/${row.service_code}`)}>
<SettingsOutlinedIcon />
Config
</MenuItem>
<MenuItem onClick={() => navigate(`/corporates/${corporate_id}/services/${row.id}/history`)}>
<HistoryIcon />
History
</MenuItem>
<MenuItem onClick={() => handleActivate(true, {code: row.service_code, name: row.name, id:corporate_id, status: row.status})}>
<CachedOutlinedIcon />
Update Status
</MenuItem>
</>
} />
</TableCell>
</TableRow>
{/* COLLAPSIBLE ROW */}
{false && (
@@ -474,7 +496,7 @@ export default function List() {
<TableHead>
<TableRow>
<TableCell colSpan={4} sx={{ py: 1 }}>
Free Admin Fee
Admin Fee
</TableCell>
</TableRow>
</TableHead>
@@ -682,6 +704,20 @@ export default function List() {
fontWeight: 'bold',
};
const onSubmit = async (row : any) => {
try {
handleUpdate(dataValue.status, dataValue.code, row.reason)
} catch (error: any) {
console.log('data gagal');
}
const ascent = document?.querySelector('ascent');
if (ascent != null) {
ascent.innerHTML = '';
}
};
const applyFilter = async (searchFilter: any) => {
await loadDataTableData({ search: searchFilter });
setSearchParams({ search: searchFilter });
@@ -697,32 +733,146 @@ export default function List() {
loadDataTableData();
}, []);
const NewCorporateSchema = Yup.object().shape({
reason: Yup.string().required('Reason Edit is required'),
});
const methods = useForm<FormValuesProps>({
resolver: yupResolver(NewCorporateSchema),
});
const {
reset,
handleSubmit,
formState: { isSubmitting },
} = methods;
const handleUpdate = (active: string, service_code: string, reason:string) => {
console.log(active)
axios
.put(`/corporates/${corporate_id}/services/${service_code}`, {
service_code: service_code,
status: active,
reason: reason
})
.then((res) => {
window.location.reload();
});
}
const getContent = () => (
<>
<Stack paddingX={2} paddingY={2}>
<Typography variant='subtitle1'>Are you sure to {dataValue?.status == 'active' ? 'inactive' : 'active'} this service ?</Typography>
</Stack>
<Card>
<Grid container paddingX={2} paddingY={2}>
<Grid item xs={4} md={4}>
<Typography variant='inherit'>Code</Typography>
</Grid>
<Grid item xs={8}>
<Typography variant='subtitle1'>{dataValue?.code}</Typography>
</Grid>
<Grid item xs={4} md={4} marginTop={2}>
<Typography variant='inherit'>Service Name</Typography>
</Grid>
<Grid item xs={8} marginTop={2}>
<Typography variant='subtitle1'>{dataValue?.name}</Typography>
</Grid>
</Grid>
</Card>
<Typography marginTop={5} marginBottom={3} variant='subtitle1'>
Reason for update*
</Typography>
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<RHFSelect
name="reason"
label="Reason for update"
>
<option value=""></option>
<option value="Agreement changed">Agreement changed</option>
<option value="Endorsement">Endorsement</option>
<option value="Renewal">Renewal</option>
<option value="Worng Setting">Worng Setting</option>
</RHFSelect>
<Stack
alignItems="center"
justifyContent="flex-end"
direction={{ xs: 'column', md: 'row' }}
spacing={2}
marginTop={5}
>
<Stack direction="row" spacing={1}>
<Button
sx={{
boxShadow: 'none',
}}
variant="outlined"
size="medium"
fullWidth={true}
onClick={() => setDialogOpen(false)}
>
Cancel
</Button>
{dataValue?.status == 'active' ?
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)'}}
type="submit"
variant="contained"
size="medium"
fullWidth={true}
loading={isSubmitting}
color='error'
>
Inactive
</LoadingButton>
:
<LoadingButton
sx={{ boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)'}}
type="submit"
variant="contained"
size="medium"
fullWidth={true}
loading={isSubmitting}
color='success'
>
Active
</LoadingButton>
}
</Stack>
</Stack>
</FormProvider>
</>
);
return (
<Stack>
<SearchForm />
<Card>
{/* The Main Table */}
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableBody>
<TableHead>
<TableRow>
{/* <TableCell style={headStyle} align="left" width={10}/> */}
<TableCell style={headStyle} align="left">
<TableCell align="left" width={50} />
<TableCell align="left">
Code
</TableCell>
<TableCell style={headStyle} align="left">
<TableCell align="left">
Service
</TableCell>
<TableCell style={headStyle} align="right" width={30}>
<TableCell align="left" width={100}>
Status
</TableCell>
<TableCell style={headStyle} align="center" width={30} colSpan={2}>
Action
<TableCell align="center" width={100}>
{/* Action */}
</TableCell>
</TableRow>
</TableBody>
</TableHead>
{dataTableIsLoading ? (
<TableBody>
<TableRow>
@@ -750,16 +900,27 @@ export default function List() {
</TableContainer>
<BasePagination paginationData={dataTableData} onPageChange={handlePageChange} />
</Card>
{isDialog === 'edit' && (
{/* {isDialog === 'edit' && (
<DialogLog
data={edit}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
title={{ name: 'Reason For Update' }}
/>
)}
)} */}
<DialogUpdateStatus
openDialog={isDialogOpen}
setOpenDialog={setDialogOpen}
title={titles}
data={dataValue}
description={dataDescription}
content={getContent()}
// maxWidth='50px'
/>
</Stack>
);
}

View File

@@ -121,11 +121,11 @@ export default function CustomizedAccordions() {
},
{
name: corporate?.name ?? '-',
href: '/corporate/' + corporate_id + '/services',
href: '/corporates/' + corporate_id + '/services',
},
{
name: 'Audittrail Corporate',
href: '/corporate/' + corporate_id + '/benefits',
href: '/corporates/' + corporate_id + '/benefits',
},
]}
/>

View File

@@ -19,6 +19,10 @@ import HeaderBreadcrumbs from '../../components/HeaderBreadcrumbs';
import CorporateTabNavigations from './CorporateTabNavigations';
import { fCurrency } from '../../utils/formatNumber';
import { ConfiguredCorporateContext } from '@/contexts/ConfiguredCorporateContext';
import Iconify from '@/components/Iconify';
import { LoadingButton } from '@mui/lab';
import { makeFormData } from '@/utils/jsonToFormData';
import { enqueueSnackbar } from 'notistack';
export default function Corporates() {
const { themeStretch } = useSettings();
@@ -29,11 +33,95 @@ export default function Corporates() {
useEffect(() => {
setCorporate(configuredCorporateContext.currentCorporate);
}, [configuredCorporateContext])
getFilesDoc(corporate_id);
}, [configuredCorporateContext, corporate_id])
const headStyle = {
fontWeight: 'bold',
};
};
// Upload Docs
const fileDocsInput = useRef<HTMLInputElement>(null);
const [fileDocs, setFileDocs] = useState([]);
const [showAll, setShowAll] = useState(false);
const [isActive, setIsActive] = useState(false);
const handleDocsInputChange = (corporate_id) => (event) => {
if(event.target.files[0] && corporate_id)
{
const updatedFiles = Array.from(event.target.files).map((file) => ({
file
}));
submitUploadDocs(corporate_id, updatedFiles);
}
};
function submitUploadDocs(corporate_id, files)
{
if(files.length > 0)
{
files.map((file, index) => {
const formData = makeFormData(
{
corporate_id : corporate_id,
result_files : file['file']
}
);
axios
.post('/add-files-doc', formData)
.then((response) => {
getFilesDoc(corporate_id);
enqueueSnackbar(response.data.message, { variant: 'success' });
})
.catch((error) => {
enqueueSnackbar(error.response.data.errors[0], { variant: 'error' });
});
});
}
}
function getFilesDoc(corporate_id)
{
axios
.post('/get-files-doc',{corporate_id:corporate_id})
.then((response) => {
setFileDocs(response.data.data);
if(response.data.data[0]['status_download'] == 1)
{
setIsActive(!isActive);
}
else
{
setIsActive(isActive);
}
})
.catch((error) => {
enqueueSnackbar(error.response.data.errors[0], { variant: 'error' });
});
}
const toggleButton = () => {
setIsActive(!isActive);
let statusDownload = 0;
if(!isActive)
{
statusDownload = 1;
}
else
{
statusDownload = 0;
}
axios
.post('/update-status-files-doc', {status_download : statusDownload, corporate_id : corporate_id})
.then((response) => {
enqueueSnackbar(response.data.message, { variant: 'success' });
})
.catch((error) => {
enqueueSnackbar(error.response.data.errors[0], { variant: 'error' });
});
};
// End Upload Docs
const style1 = {px:4, marginTop: 2};
const style2 = {color: '#919EAB', width: '50%'};
const style3 = {color: '#212B36', width: '50%'};
return (
<Page title="Dashboard">
@@ -50,65 +138,140 @@ export default function Corporates() {
},
]}
/>
{/* <Container maxWidth={themeStretch ? false : 'xl'}> */}
<Card>
<Stack spacing="3">
<Stack spacing="2">
<CorporateTabNavigations position=""/>
<Grid container spacing={3}>
<Grid item md={6} sx={{ p:2 }}>
<Typography sx={{...headStyle, px:3, fontSize:'24px'}}>Current Policy </Typography>
<Table>
<TableBody>
<TableRow>
<TableCell sx={headStyle}>Policy Name</TableCell>
<TableCell>{corporate?.current_policy?.code}</TableCell>
</TableRow>
<TableRow>
<TableCell sx={headStyle}>Total Premi</TableCell>
<TableCell>{fCurrency(corporate?.current_policy?.total_premi)}</TableCell>
</TableRow>
<TableRow>
<TableCell sx={headStyle}>Stop Service</TableCell>
<TableCell>{fCurrency(corporate?.current_policy?.minimal_stop_service_net)}</TableCell>
</TableRow>
<TableRow>
<TableCell sx={headStyle}>Balance</TableCell>
<TableCell>{fCurrency(corporate?.current_policy?.limit_balance)}</TableCell>
</TableRow>
</TableBody>
</Table>
<Grid>
<Grid sx={{ p:2 }}>
<Typography variant='subtitle1' sx={{...headStyle, px:4, marginTop: 2}}>Current Policy </Typography>
<Stack spacing={2} direction='row' sx={{...style1}}>
<Typography variant='body2' sx={{...style2}}>Policy Name</Typography>
<Typography variant='body2' sx={{...style3}}>{corporate?.current_policy?.code}</Typography>
</Stack>
<Stack spacing={2} direction='row' sx={{...style1}}>
<Typography variant='body2' sx={{...style2}}>Total Premi</Typography>
<Typography variant='body2' sx={{...style3}}>{fCurrency(corporate?.current_policy?.total_premi)}</Typography>
</Stack>
<Stack spacing={2} direction='row' sx={{...style1}}>
<Typography variant='body2' sx={{...style2}}>Stop Service</Typography>
<Typography variant='body2' sx={{...style3}}>{fCurrency(corporate?.current_policy?.minimal_stop_service_net)}</Typography>
</Stack>
<Stack spacing={2} direction='row' sx={{...style1}}>
<Typography variant='body2' sx={{...style2}}>Balance</Typography>
<Typography variant='body2' sx={{...style3}}>{fCurrency(corporate?.current_policy?.limit_balance)}</Typography>
</Stack>
</Grid>
<Grid item md={6} sx={{ p:2 }}>
<Typography sx={{...headStyle, px:3, fontSize:'24px'}}>Claims</Typography>
<Table>
<TableBody>
<TableRow>
<TableCell sx={headStyle}>Number Of Claim</TableCell>
<TableCell>{corporate?.current_policy?.code}</TableCell>
</TableRow>
<TableRow>
<TableCell sx={headStyle}>Total Usage This Year</TableCell>
<TableCell>{fCurrency((corporate?.current_policy?.total_premi ?? 0) - (corporate?.current_policy?.limit_balance ?? 0))}</TableCell>
</TableRow>
</TableBody>
</Table>
<Grid sx={{ p:2 }}>
<Typography variant='subtitle1' sx={{...headStyle, px:4, marginTop: 2}}>Claims</Typography>
<Stack spacing={2} direction='row' sx={{...style1}}>
<Typography variant='body2' sx={{...style2}}>Number Of Claim</Typography>
<Typography variant='body2' sx={{...style3}}>{corporate?.current_policy?.code}</Typography>
</Stack>
<Stack spacing={2} direction='row' sx={{...style1}}>
<Typography variant='body2' sx={{...style2}}>Total Usage This Year</Typography>
<Typography variant='body2' sx={{...style3}}>{fCurrency((corporate?.current_policy?.total_premi ?? 0) - (corporate?.current_policy?.limit_balance ?? 0))}</Typography>
</Stack>
</Grid>
<Grid sx={{ p:2 }}>
<Typography variant='subtitle1' sx={{...headStyle, px:4, marginTop: 2}}>Docs (Terms & Conditions)</Typography>
{fileDocs.length > 0 && (
<Stack spacing={2} direction='row' sx={{...style1}} alignItems="center">
<Stack direction='row' sx={{width: '50%'}} alignItems="center">
<Typography variant='body2' sx={{color: '#919EAB', marginRight: 2}}>ASO members can download or not?</Typography>
<Button
variant="outlined"
color={isActive ? "success" : "error"}
size="small"
onClick={toggleButton}
>
<Typography variant='body3' sx={{fontWeight: 'bold'}}>{isActive ? 'Active' : 'Inactive'}</Typography>
</Button>
</Stack>
{!showAll && (
<Stack direction='row' sx={{width: '50%'}} alignItems="center">
<input
type="file"
id={`fileDocsID`}
ref={fileDocsInput}
style={{ display: 'none' }}
onChange={(event) => {
handleDocsInputChange(corporate_id)(event);
}}
accept="application/pdf"
multiple
/>
<Button
variant="outlined"
onClick={() => {
fileDocsInput.current.click();
}}
sx={{ width: 'fit-content', color: '#19BBBB' }}
>
<Stack alignItems="center" direction="row" spacing={1}>
<Iconify icon="icon-park-outline:upload-one" fontSize="2em" sx={{color: '#19BBBB'}} />
<Typography variant='body3' sx={{fontWeight: 'bold', color: '#19BBBB'}}>Update File</Typography>
</Stack>
</Button>
</Stack>
)}
</Stack>
)}
{fileDocs.slice(0, showAll ? fileDocs.length : 1).map((file, index) => (
<Stack spacing={2} direction='row' sx={{...style1}}>
<a
href={file.path}
style={{ cursor: 'pointer', textDecoration: 'underline', color: '#19BBBB' }}
target="_blank"
>
<Typography variant='body2' sx={{color: '#19BBBB'}}>{file.original_name}</Typography>
</a>
</Stack>
))}
{!showAll && fileDocs.length > 1 && (
<Stack spacing={2} direction='row' sx={{...style1}}>
<Typography variant='body2' onClick={() => setShowAll(true)} style={{ color: '#19BBBB', cursor: 'pointer' }}>Lihat Semua Data</Typography>
</Stack>
)}
{showAll && (
<Stack spacing={2} direction='row' sx={{...style1}}>
<Typography variant='body2' onClick={() => setShowAll(false)} style={{ color: '#19BBBB', cursor: 'pointer' }}>Sembunyikan Data</Typography>
</Stack>
)}
{fileDocs.length <= 0 && (
<Stack spacing={2} direction='row' sx={{...style1}} alignItems="center">
<Typography variant='body2' sx={{...style2}}>Please add a Terms & Conditions document</Typography>
<Stack direction='row' sx={{width: '50%'}} alignItems="center">
<input
type="file"
id={`fileDocsID`}
ref={fileDocsInput}
style={{ display: 'none' }}
onChange={(event) => {
handleDocsInputChange(corporate_id)(event);
}}
accept="application/pdf"
// multiple
/>
<LoadingButton
variant="outlined"
onClick={() => {
fileDocsInput.current.click();
}}
>
<Stack alignItems="center" direction="row" spacing={1}>
<Iconify icon="icon-park-outline:upload-one" fontSize="2em" sx={{color: '#19BBBB'}} />
<Typography variant='body3' sx={{fontWeight: 'bold', color: '#19BBBB'}}>Upload File</Typography>
</Stack>
</LoadingButton>
</Stack>
</Stack>
)}
</Grid>
</Grid>
</Stack>
</Card>

View File

@@ -0,0 +1,214 @@
import { Card, Grid, MenuItem, Typography } from "@mui/material";
import { Stack } from '@mui/material';
import { BenefitData, DetailFinalLogType } from "../FinalLog/Model/Types";
import { useEffect, useState, useRef, useMemo } from 'react';
import { Box } from "@mui/material";
import { fDate, fDateTimesecond, toTitleCase } from "@/utils/formatTime";
import Label from '@/components/Label';
import AddIcon from '@mui/icons-material/Add';
import { Button } from "@mui/material";
import MoreMenu from "@/components/MoreMenu";
import { Delete, EditOutlined } from "@mui/icons-material";
import { fNumber } from "@/utils/formatNumber";
import palette from "@/theme/palette";
import DialogBenefit from "../FinalLog/Components/DialogBenefit";
import DialogEditBenefit from "../FinalLog/Components/DialogEditBenefit";
import DialogDelete from "../FinalLog/Components/DialogDelete";
type CardDetail = {
requestLog: DetailFinalLogType|undefined;
}
const style1 = {
color: '#919EAB',
width: '30%'
}
const style2 = {
width: '70%'
}
const marginBottom1 = {
marginBottom: 1,
}
const marginBottom2 = {
marginBottom: 2,
}
const [openDialogBenefit, setDialogBenefit] = useState(false);
// Handle Edit Detail Benefit
const [openDialogEditBenefit, setDialogEditBenefit] = useState(false)
const [BenefitConfigurationData, setBenefitConfigurationData] = useState<BenefitData>();
// Handel Delete Detail Benefit
const [idBenefitData, setIdBenefitData] = useState<number>();
const [openDialogDeleteBenefit, setDialogDeleteBenefit] = useState(false)
export default function CardBenefit({requestLog} : CardDetail ) {
return (
<Card sx={{padding:2}} >
<Stack direction="row" alignItems="center" sx={{marginBottom: 4}}>
<Typography variant='subtitle1' sx={{color: '#19BBBB'}} gutterBottom>Benefit</Typography>
<Button variant="outlined" startIcon={<AddIcon/>} sx={{marginLeft: 'auto'}} onClick={() => {
setDialogBenefit(true);
}} >
<Typography variant="button" display="block">Benefit</Typography>
</Button>
</Stack>
{requestLog?.benefit_data?.map((item, index) => (
<Box key={index} sx={{ border: '1px solid rgba(0,0,0,0.125)', px: '24px', py: '20px', marginBottom: '24px', borderRadius: '12px'}}>
<Grid container spacing={2}>
<Grid item xs={12}>
<Grid container spacing={2}>
<Grid item xs={6}>
<Typography variant="body2" sx={{ fontWeight: 'bold'}}>
{item.benefit?.description}
</Typography>
</Grid>
<Grid item xs={6} sx={{ display: 'flex', placeContent: 'end' }}>
<MoreMenu actions={
<>
<MenuItem onClick={() => {
setDialogEditBenefit(true)
setIdBenefitData(item.id)
setBenefitConfigurationData(item)
}}
>
<EditOutlined />
Edit
</MenuItem>
<MenuItem onClick={() => {
setIdBenefitData(item.id)
setDialogDeleteBenefit(true)
}}
>
<Delete color='error'/>
Delete
</MenuItem>
</>
} />
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Box sx={{ py: '8px', px: '12px', background: palette.light.grey[50012], borderRadius: '6px'}}>
<Grid container spacing={1}>
{/* Amount Incurred */}
<Grid item xs={2}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
Amount Incurred
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
{fNumber(item.amount_incurred)}
</Typography>
</Grid>
</Grid>
</Grid>
{/* Amount Approved */}
<Grid item xs={2}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
Amount Approved
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
{fNumber(item.amount_approved)}
</Typography>
</Grid>
</Grid>
</Grid>
{/* Amount Not Approved */}
<Grid item xs={3}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
Amount Not Approved
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
{fNumber(item.amount_not_approved)}
</Typography>
</Grid>
</Grid>
</Grid>
{/* Excess Paid* */}
<Grid item xs={2}>
<Grid container sx={{ borderRight: `0.5px solid ${palette.light.grey[400]}` }}>
<Grid item xs={12}>
<Typography variant="caption">
Excess Paid*
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
{fNumber(item.excess_paid)}
</Typography>
</Grid>
</Grid>
</Grid>
{/* Keterangan* */}
<Grid item xs={3}>
<Grid container>
<Grid item xs={12}>
<Typography variant="caption">
Keterangan*
</Typography>
</Grid>
<Grid item xs={12}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>
{item.keterangan}
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</Box>
</Grid>
</Grid>
</Box>
))}
<DialogBenefit
requestLog={requestLog}
openDialog={openDialogBenefit}
setOpenDialog={setDialogBenefit}
/>
{/* Dialog Edit */}
<DialogEditBenefit
id={idBenefitData}
data={BenefitConfigurationData}
openDialog={openDialogEditBenefit}
setOpenDialog={setDialogEditBenefit}
>
</DialogEditBenefit>
{/* Dialog Delete */}
<DialogDelete
id={idBenefitData}
openDialog={openDialogDeleteBenefit}
setOpenDialog={setDialogDeleteBenefit}
/>
</Card>
)
}

View File

@@ -0,0 +1,57 @@
import { Card, Typography } from "@mui/material";
import { Stack } from '@mui/material';
import { DetailFinalLogType } from "../FinalLog/Model/Types";
import { fDate, fDateTimesecond, toTitleCase } from "@/utils/formatTime";
type CardDetail = {
requestLog: DetailFinalLogType|undefined;
}
const style1 = {
color: '#919EAB',
width: '30%'
}
const style2 = {
width: '70%'
}
const marginBottom1 = {
marginBottom: 1,
}
const marginBottom2 = {
marginBottom: 2,
}
export default function CardDetail({requestLog} : CardDetail ) {
return (
<Card sx={{padding:2}} >
<Typography variant='subtitle1' sx={{color: '#19BBBB', marginBottom: 4}} gutterBottom>Detail</Typography>
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Member ID</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.member_id}</Typography>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Policy Number</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.policy_number}</Typography>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Name</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.name}</Typography>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Date Of Birth</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.date_of_birth ? fDate(requestLog?.date_of_birth) : '-'}</Typography>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Marital Status</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.marital_status}</Typography>
</Stack>
<Stack direction='row' spacing={2} sx={marginBottom1}>
<Typography variant='subtitle2' sx={style1} gutterBottom>Submission Date</Typography>
<Typography variant='subtitle2' sx={style2} gutterBottom>{requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : '-'}</Typography>
</Stack>
</Card>
)
}

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