backend dan penyesian upload file dinamis approval notifikasi

This commit is contained in:
2025-09-11 09:47:42 +07:00
parent 6a4aaff628
commit 3c9066edc6
9 changed files with 375 additions and 24 deletions

View File

@@ -0,0 +1,185 @@
import { Stack, Typography, Button, Paper, Grid, IconButton, TextField } from "@mui/material";
import MuiDialog from "@/components/MuiDialog";
import { fDate, fDateTimesecond } from '@/utils/formatTime';
import { ContentCopy, WhatsApp, Instagram, Facebook, Telegram } from "@mui/icons-material";
type DialogConfirmationType = {
openDialog: boolean;
setOpenDialog: any;
onSubmit?: void;
requestLog: any;
shareLink: boolean;
};
export default function DialogSendWa({
requestLog,
setOpenDialog,
openDialog,
shareLink = false,
}: DialogConfirmationType) {
const data = {
provider: requestLog?.provider || "LOG",
memberId: requestLog?.member_id || "-",
policyNumber: requestLog?.policy_number || "-",
name: requestLog?.name || "-",
submissionDate: requestLog?.submission_date ? fDateTimesecond(requestLog?.submission_date) : "-",
claimMethod: requestLog?.claim_method || "-",
serviceType: requestLog?.service_type || "-",
linkApproval: requestLog?.url_approval || "https://example.com/approval-link",
};
const getContent = () => (
<Stack spacing={2} sx={{ marginTop: 2, padding: 2 }}>
<Typography>Are you sure want to send this request ?</Typography>
<Paper variant="outlined" sx={{ p: 2 }}>
<Grid container spacing={1}>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Member ID
</Typography>
</Grid>
<Grid item xs={7}>
<Typography>{data.memberId}</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Policy Number
</Typography>
</Grid>
<Grid item xs={7}>
<Typography fontWeight="bold">{data.policyNumber}</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Name
</Typography>
</Grid>
<Grid item xs={7}>
<Typography>{data.name}</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Submission Date
</Typography>
</Grid>
<Grid item xs={7}>
<Typography>{data.submissionDate}</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Claim Method
</Typography>
</Grid>
<Grid item xs={7}>
<Typography>{data.claimMethod}</Typography>
</Grid>
<Grid item xs={5}>
<Typography variant="body2" color="textSecondary">
Service Type
</Typography>
</Grid>
<Grid item xs={7}>
<Typography>{data.serviceType}</Typography>
</Grid>
</Grid>
</Paper>
{shareLink ? (
<>
<Typography>Share this link only with authorized parties!</Typography>
{/* <Stack direction="row" spacing={2}>
<IconButton color="success">
<WhatsApp />
</IconButton>
<IconButton color="primary">
<Instagram />
</IconButton>
<IconButton color="primary">
<Telegram />
</IconButton>
<IconButton color="primary">
<Facebook />
</IconButton>
</Stack> */}
<Typography variant="body2">or copy link</Typography>
<Stack direction="row" spacing={1}>
<TextField
fullWidth
size="small"
value={data.linkApproval}
InputProps={{
readOnly: true,
}}
/>
<Button
variant="outlined"
onClick={() => navigator.clipboard.writeText(data.linkApproval)}
>
Copy
</Button>
</Stack>
</>
): null }
</Stack>
);
const getAction = () => {
if (shareLink) {
return (
<Stack direction="row" justifyContent="flex-end">
<Button variant="outlined" onClick={() => setOpenDialog(false)}>
Cancel
</Button>
</Stack>
);
}
const handleSend = () => {
const message = `*Request Approval*
Yth. Bapak/Ibu, Nama Penerima
Mohon persetujuan atas data berikut:
Provider: *${data.provider}*
Member ID: ${data.memberId}
Nama: ${data.name}
Policy Number: ${data.policyNumber}
Submission Date: ${data.submissionDate}
Claim Method: ${data.claimMethod}
Service Type: ${data.serviceType}
Silakan klik link berikut untuk approval:
${data.linkApproval}`;
const encodedMessage = encodeURIComponent(message);
const waUrl = `https://wa.me/6283807417196?text=${encodedMessage}`;
window.open(waUrl, "_blank");
};
return (
<Stack direction="row" justifyContent="space-between" spacing={2}>
<Button variant="outlined" onClick={() => setOpenDialog(false)}>
Cancel
</Button>
<Button variant="contained" onClick={handleSend}>
Send
</Button>
</Stack>
);
};
return (
<MuiDialog
title={{ name: "Confirmation", variant: "h4" }}
openDialog={openDialog}
setOpenDialog={setOpenDialog}
content={getContent()}
action={getAction()}
maxWidth="sm"
/>
);
}

View File

@@ -30,6 +30,7 @@ import { useFieldArray, useForm } from 'react-hook-form';
import { useNavigate, useParams, useLocation } from 'react-router-dom';
import { useEffect, useState, useRef, useMemo } from 'react';
import axios from '../../../utils/axios';
import { enqueueSnackbar } from 'notistack';
// pages
import ArrowBackIosIcon from '@mui/icons-material/ArrowBackIos';
import { DetailFinalLogType } from './Model/Types';
@@ -43,6 +44,7 @@ import { Delete, EditOutlined, ExpandMore } from '@mui/icons-material';
import {BenefitData } from '../FinalLog/Model/Types'
import AddIcon from '@mui/icons-material/Add';
import { LoadingButton } from '@mui/lab';
import { makeFormData } from '@/utils/jsonToFormData';
// Import Card Detail Final LOG
import CardDetail from '../Components/CardDetail';
@@ -67,6 +69,8 @@ import CardFile from '../Components/CardFile';
import DialogEditFinalLOG from './Components/DialogEditFinalLOG';
import DialogDeleteFileLog from './Components/DialogDeleteFileLog';
import DialogUploadFileFinalLog from './Components/DialogUploadFileFinalLog';
import DialogSendWa from './Components/DialogSendWa';
import { set } from 'nprogress';
// ----------------------------------------------------------------------
@@ -79,6 +83,7 @@ export default function Detail() {
const { themeStretch } = useSettings();
const [requestLog, setRequestLog] = useState<DetailFinalLogType>();
const [isReversal, setIsReversal] = useState(false);
const [submitLoading, setSubmitLoading] = useState(false);
const defaultValues: any = {nominal : 0};
const validationSchema = Yup.object().shape({nominal: Yup.number().typeError('Nominal harus berupa angka').required('Nominal harus diisi')})
@@ -91,7 +96,25 @@ export default function Detail() {
const { handleSubmit, reset, watch, setValue, formState: { isDirty, isSubmitting, errors } } = methods;
const onSubmit = async (data: any) => {
alert('Nominal: ' + data.nominal);
setSubmitLoading(true);
const formData = makeFormData({
request_logs_id: id,
approval_files: fileApprovals,
nominal: data.nominal,
});
axios
.post(`/customer-service/request/${id}/approval_files`, formData)
.then((response) => {
enqueueSnackbar('Berhasil membuat data', { variant: 'success' });
window.location.reload()
})
.catch(({ response }) => {
enqueueSnackbar('Something Went Wrong', { variant: 'error' });
})
.then(() => {
setSubmitLoading(false);
});
}
const { id } = useParams();
@@ -130,6 +153,8 @@ export default function Detail() {
const [openDialogEditDetail, setDialogDEditDetail] = useState(false);
const [openDialogBenefit, setDialogBenefit] = useState(false);
const [openDialogMedicine, setDialogMedicine] = useState(false);
const [openDialogSendWa, setDialogSendWa] = useState(false);
const [shareLink, setShareLink] = useState(false);
// Handel Delete Detail Benefit
const [idBenefitData, setIdBenefitData] = useState<number>();
@@ -175,16 +200,16 @@ export default function Detail() {
const fileDiagnosaInput = useRef<HTMLInputElement>(null);
const [fileDiagnosas, setFileDiagnosas] = useState<any>([]);
const [fileApprovals, setFileApproval] = useState<any>([]);
const handleDiagnosaInputChange = (event:any) => {
if (event.target.files[0]) {
setFileDiagnosas([...fileDiagnosas, ...event.target.files]);
setFileApproval([...fileApprovals, ...event.target.files]);
} else {
console.log('NO FILE');
}
};
const removeDiagnosaFiles = (filesState:any, index:any) => {
setFileDiagnosas(
const removeApprovalFiles = (filesState:any, index:any) => {
setFileApproval(
filesState.filter((file:any, fileIndex:any) => {
return fileIndex != index;
})
@@ -381,7 +406,7 @@ export default function Detail() {
Upload Tindakan Persetujuan
</Typography>
{fileDiagnosas?.map((file: any, index: number) => (
{fileApprovals?.map((file: any, index: number) => (
<Stack
key={index}
direction="row"
@@ -395,7 +420,7 @@ export default function Detail() {
icon="eva:trash-2-outline"
color="darkred"
sx={{ cursor: "pointer" }}
onClick={() => removeDiagnosaFiles(fileDiagnosas, index)}
onClick={() => removeApprovalFiles(fileApprovals, index)}
/>
</Stack>
))}
@@ -438,29 +463,60 @@ export default function Detail() {
label="Nominal"
required
placeholder="Nominal"
value={requestLog?.nominal || 0}
/>
<LoadingButton
{/* <LoadingButton
type="submit" // ✅ supaya ikut submit
variant="contained"
sx={{ marginTop: 2, p: 2, backgroundColor: "#19BBBB" }}
loading={false}
>
Simpan
</LoadingButton>
</LoadingButton> */}
<Stack direction="row" spacing={2} sx={{ mt: 6 }}>
{/* TOMBOL SIMPAN DI KIRI */}
<LoadingButton
type="submit"
variant="contained"
sx={{ p: 2, backgroundColor: "#19BBBB" }}
loading={false}
size='small'
>
Simpan
</LoadingButton>
{/* Ini adalah spacer untuk mendorong tombol berikutnya ke kanan */}
<Box sx={{ flexGrow: 1 }} />
{/* GRUP TOMBOL DI KANAN */}
<Stack direction="row" spacing={1.5} mt={2}>
<Button variant="contained" size="small" sx={{ p: 2, backgroundColor: "#19BBBB" }}
onClick={() => {setDialogSendWa(true); setShareLink(false); }}>
Kirim (WA Chatbot)
</Button>
<Button variant="contained" size="small" sx={{ p: 2, backgroundColor: "#19BBBB" }}
onClick={() => {setDialogSendWa(true); setShareLink(true); }}>
Share Link
</Button>
</Stack>
</Stack>
</FormProvider>
</Stack>
</FormProvider>
)}
{/* FILE YANG SUDAH TERUPLOAD */}
{requestLog?.files?.map((documentType, index) => (
{requestLog?.files
?.filter((document) => document.type === 'approval')
?.map((documentType, index) => (
<Stack
key={index}
direction="row"
justifyContent="space-between"
alignItems="center"
sx={{ mb: 2 }}
sx={{ mt: 2 }}
>
<a
href={documentType.url}
@@ -778,6 +834,16 @@ export default function Detail() {
requestLog={requestLog}
openDialog={openDialogEditDetail}
/>
<DialogSendWa
requestLog={requestLog}
openDialog={openDialogSendWa}
setOpenDialog={setDialogSendWa}
shareLink={shareLink}
/>
</Grid>
{/* Medicine */}
@@ -843,7 +909,9 @@ export default function Detail() {
) : null }
</Stack>
{requestLog?.files?.map((documentType, index) => (
{requestLog?.files
?.filter((document) => document.type !== 'approval')
?.map((documentType, index) => (
<Stack direction="row" alignItems="center" justifyContent="space-between" sx={{marginBottom: 2}} key={index}>
<Stack direction="column" spacing={2} >
<a

View File

@@ -66,6 +66,7 @@ export type DetailFinalLogType = {
files : file[],
member_usage_benefit : number,
corporate_id : number
nominal : number
}
export type Diagnosis = {