Download zip
This commit is contained in:
@@ -34,6 +34,9 @@ use Modules\Internal\Transformers\RequestLogResource;
|
||||
|
||||
use App\Models\RequestLog;
|
||||
|
||||
use ZipArchive;
|
||||
use File;
|
||||
|
||||
use PDF;
|
||||
|
||||
class ClaimController extends Controller
|
||||
@@ -122,6 +125,96 @@ class ClaimController extends Controller
|
||||
return response()->json(Helper::paginateResources($results));
|
||||
}
|
||||
|
||||
public function filesProvider(Request $request)
|
||||
{
|
||||
$limit = $request->has('per_page') ? $request->input('per_page') : 50;
|
||||
$results = DB::table('request_logs')
|
||||
->leftJoin('members', 'request_logs.member_id', '=', 'members.id')
|
||||
->join('files', 'request_logs.id', '=', 'files.fileable_id')
|
||||
// ->leftJoin('member_plans', 'member_plans.member_id', '=', 'members.id')
|
||||
->when($request->input('search'), function ($query, $search) {
|
||||
$query->where(function ($query) use ($search) {
|
||||
$query->orWhere('members.name', 'like', "%" . $search . "%");
|
||||
$query->orWhere('request_logs.code', 'like', "%" . $search . "%");
|
||||
$query->orWhere('members.member_id', 'like', "%" . $search . "%");
|
||||
});
|
||||
})
|
||||
->when($request->has('orderBy'), function ($query) use ($request) {
|
||||
$orderBy = $request->orderBy;
|
||||
$direction = $request->order ?? 'asc';
|
||||
|
||||
$query->orderBy($orderBy, $direction);
|
||||
})
|
||||
->when($request->input('start_date') , function ($query, $start_date) {
|
||||
$query->where(function ($query) use ($start_date) {
|
||||
$query->where('request_logs.created_at', '>=', $start_date. ' 00:00:00');
|
||||
});
|
||||
})
|
||||
->when($request->input('end_date') , function ($query, $end_date) {
|
||||
$query->where(function ($query) use ($end_date) {
|
||||
$query->where('request_logs.created_at', '<=', $end_date. ' 23:59:59');
|
||||
});
|
||||
})
|
||||
->when($request->input('provider') , function ($query, $provider) {
|
||||
$query->where(function ($query) use ($provider) {
|
||||
$query->where('request_logs.organization_id', '=', $provider);
|
||||
});
|
||||
})
|
||||
->where('files.fileable_type', '=', 'App\Models\RequestLog')
|
||||
->where('request_logs.final_log', '=', '1')
|
||||
->where('request_logs.status_final_log', '=', 'approved')
|
||||
->select(
|
||||
'files.original_name as files',
|
||||
'files.id',
|
||||
'files.id AS id_log',
|
||||
'request_logs.code as code',
|
||||
'members.name',
|
||||
'request_logs.created_at',
|
||||
DB::raw('
|
||||
(SELECT organizations.name FROM organizations WHERE organizations.id = request_logs.organization_id LIMIT 1) AS provider
|
||||
'),
|
||||
'request_logs.status_final_log as status',
|
||||
DB::raw("CONCAT('" . env('APP_URL') . "/storage/', path) as path")
|
||||
)
|
||||
->paginate($limit);
|
||||
|
||||
|
||||
|
||||
return response()->json(Helper::paginateResources($results));
|
||||
}
|
||||
|
||||
public function downloadZip(Request $request)
|
||||
{
|
||||
$selectedRows = $request->selectedRows; // asumsi $selectedRows berisi array ID file yang dipilih
|
||||
$files = [];
|
||||
|
||||
// Ambil path file dari database atau sumber lain sesuai dengan $selectedRows
|
||||
$data = DB::table('files')
|
||||
->whereIn('id', $selectedRows)
|
||||
->select('path')
|
||||
->get();
|
||||
|
||||
foreach ($data as $value) {
|
||||
$files[] = storage_path('app/public/' . $value->path);
|
||||
}
|
||||
|
||||
$zipFileName = 'downloaded_files.zip';
|
||||
$zip = new ZipArchive();
|
||||
|
||||
if ($zip->open(storage_path('app/public/' . $zipFileName), ZipArchive::CREATE | ZipArchive::OVERWRITE)) {
|
||||
foreach ($files as $file) {
|
||||
$zip->addFile($file, basename($file));
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
|
||||
// Mengembalikan response berupa URL file zip
|
||||
return response()->json(['file_url' => env('APP_URL').Storage::url($zipFileName)], 200);
|
||||
} else {
|
||||
return response()->json(['message' => 'Gagal membuat file ZIP.'], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function downloadTemplate()
|
||||
{
|
||||
return Helper::responseJson([
|
||||
|
||||
@@ -238,6 +238,8 @@ Route::prefix('internal')->group(function () {
|
||||
Route::post('claims/{claim_id}/set-final-encounter', [ClaimEncounterController::class, 'setFinalEncounter']);
|
||||
|
||||
Route::get('claims', [ClaimController::class, 'index']);
|
||||
Route::get('claims-files-provider', [ClaimController::class, 'filesProvider']);
|
||||
Route::post('download-zip', [ClaimController::class, 'downloadZip']);
|
||||
Route::get('claims/download-template', [ClaimController::class, 'downloadTemplate']);
|
||||
Route::post('claims/import', [ClaimController::class, 'import']);
|
||||
Route::post('claims/exportFiled/', [ClaimController::class, 'exportFiled']);
|
||||
|
||||
@@ -95,6 +95,7 @@ const navConfig = [
|
||||
{
|
||||
title: 'REPORT',
|
||||
children: [
|
||||
{ title: 'Files Provider', path: 'report/files-provider'},
|
||||
{ title: 'Letter of Guarantee', path: '/report/logs' },
|
||||
{ title: 'Appointment', path: '/report/appointments' },
|
||||
{ title: 'Live Chat', path: '/report/live-chat' },
|
||||
|
||||
35
frontend/dashboard/src/pages/Report/FilesProvider/Index.tsx
Normal file
35
frontend/dashboard/src/pages/Report/FilesProvider/Index.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Card, Grid, Container } from '@mui/material';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import HeaderBreadcrumbs from '../../../components/HeaderBreadcrumbs';
|
||||
import Page from '../../../components/Page';
|
||||
import useSettings from '../../../hooks/useSettings';
|
||||
import List from './List';
|
||||
|
||||
export default function Index() {
|
||||
const { themeStretch } = useSettings();
|
||||
|
||||
const { id } = useParams();
|
||||
|
||||
const pageTitle = 'Files Provider';
|
||||
return (
|
||||
<Page title={pageTitle}>
|
||||
<Container maxWidth={themeStretch ? false : 'xl'}>
|
||||
<HeaderBreadcrumbs
|
||||
heading={pageTitle}
|
||||
links={[
|
||||
{
|
||||
name: 'Report',
|
||||
href: '#',
|
||||
},
|
||||
{
|
||||
name: 'Files Provider',
|
||||
href: '/report/files-provider',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<List />
|
||||
</Container>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
999
frontend/dashboard/src/pages/Report/FilesProvider/List.tsx
Normal file
999
frontend/dashboard/src/pages/Report/FilesProvider/List.tsx
Normal file
@@ -0,0 +1,999 @@
|
||||
// @mui
|
||||
import {
|
||||
Box,
|
||||
Grid,
|
||||
Button,
|
||||
Card,
|
||||
Collapse,
|
||||
IconButton,
|
||||
MenuItem,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableRow,
|
||||
TextField,
|
||||
Typography,
|
||||
Stack,
|
||||
Menu,
|
||||
ButtonGroup,
|
||||
Tooltip,
|
||||
TableHead,
|
||||
Checkbox,
|
||||
InputAdornment,
|
||||
TableSortLabel,
|
||||
FormControl
|
||||
} from '@mui/material';
|
||||
import { visuallyHidden } from '@mui/utils';
|
||||
|
||||
import { DesktopDatePicker, LocalizationProvider } from '@mui/x-date-pickers';
|
||||
import { fDateOnly } from '@/utils/formatTime';
|
||||
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import FindInPageOutlinedIcon from '@mui/icons-material/FindInPageOutlined';
|
||||
import AssessmentIcon from '@mui/icons-material/Assessment';
|
||||
// hooks
|
||||
import React, { ChangeEvent, useEffect, useRef, useState } from 'react';
|
||||
import { Link, Navigate, useNavigate, useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { LoadingButton } from '@mui/lab';
|
||||
// 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 { Chip } from '@mui/material';
|
||||
import Iconify from '@/components/Iconify';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { fDate, fDateTime } 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';
|
||||
import { Add, Search } from '@mui/icons-material';
|
||||
import Autocomplete from '@mui/material/Autocomplete';
|
||||
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
|
||||
import UploadIcon from '@mui/icons-material/Upload';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
|
||||
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
|
||||
|
||||
export default function List() {
|
||||
const [selectAll, setSelectAll] = useState(false);
|
||||
const [selectedRows, setSelectedRows] = useState([]);
|
||||
const [providers, setProviders] = useState(null);
|
||||
// const [searchText, setSearchText] = useState('');
|
||||
const [order, setOrder] = useState<Order>('desc');
|
||||
const [orderBy, setOrderBy] = useState('created_at');
|
||||
const [perPage, setPerPage] = useState<number>(0);
|
||||
|
||||
const handleChange = (event, newValue) => {
|
||||
// Jika newValue tidak undefined, atur nilai dataProvider
|
||||
if (newValue !== undefined) {
|
||||
setDataProvider(newValue.service_code);
|
||||
} else {
|
||||
// Jika tidak ada yang dipilih, set dataProvider menjadi string kosong
|
||||
setDataProvider(null);
|
||||
}
|
||||
};
|
||||
// Dummy data
|
||||
const dummyServices = [
|
||||
{ service_code: '1', name: 'Service 1' },
|
||||
{ service_code: '2', name: 'Service 2' },
|
||||
{ service_code: '3', name: 'Service 3' },
|
||||
// tambahkan data lain sesuai kebutuhan
|
||||
];
|
||||
|
||||
|
||||
|
||||
const handleSelectAll = () => {
|
||||
setSelectAll(!selectAll);
|
||||
if (!selectAll) {
|
||||
const requestedIds = dataTableData.data
|
||||
.filter(row => row.status === 'approved') // Memfilter baris dengan status 'requested'
|
||||
.map(row => row.id); // Mengambil hanya ID dari baris-baris yang memenuhi kondisi
|
||||
setSelectedRows(requestedIds);
|
||||
} else {
|
||||
setSelectedRows([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleRowSelect = (id) => {
|
||||
if (selectedRows.includes(id)) {
|
||||
setSelectedRows(selectedRows.filter(rowId => rowId !== id));
|
||||
} else {
|
||||
setSelectedRows([...selectedRows, id]);
|
||||
}
|
||||
};
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [startDate, setStartDate] = useState(null);
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [endDate, setEndDate] = useState(null);
|
||||
const navigate = useNavigate();
|
||||
const [dataProvider, setDataProvider] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (startDate !== null || endDate !== null || dataProvider !== null
|
||||
|| order !== null || orderBy !== null || perPage !== 0) {
|
||||
loadDataTableData();
|
||||
getProvider();
|
||||
}
|
||||
}, [startDate, endDate, dataProvider, order, orderBy, perPage]);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isLoadingImport, setIsLoadingImport] = useState(false);
|
||||
const handleExportReport = async () => {
|
||||
|
||||
|
||||
const year = startDate?.getFullYear();
|
||||
const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate = year && month && day ? `${year}-${month}-${day}` : '';
|
||||
|
||||
const year1 = endDate?.getFullYear();
|
||||
const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : '';
|
||||
|
||||
|
||||
|
||||
var filter = Object.fromEntries([...searchParams.entries()]);
|
||||
setIsLoading(true)
|
||||
await axios
|
||||
.get('/claims/export-claim-management',{
|
||||
params: {
|
||||
search: searchText,
|
||||
start_date: formattedDate ? formattedDate : null,
|
||||
end_date:formattedDate1,
|
||||
provider: dataProvider,
|
||||
order: order,
|
||||
orderBy: orderBy,
|
||||
page: perPage,
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
enqueueSnackbar('Data berhasil di Export', {
|
||||
variant: 'success',
|
||||
anchorOrigin: { horizontal: 'right', vertical: 'top' },
|
||||
});
|
||||
setIsLoading(false)
|
||||
|
||||
document.location.href = res.data.data.file_url;
|
||||
})
|
||||
.catch((err) =>
|
||||
enqueueSnackbar('Data Gagal di Export', {
|
||||
variant: 'error',
|
||||
anchorOrigin: { horizontal: 'right', vertical: 'top' },
|
||||
})
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
function SearchInput(props: any) {
|
||||
// SEARCH
|
||||
const searchInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
|
||||
|
||||
const handleSearchChange = (event: any) => {
|
||||
const newSearchText = event.target.value ?? '';
|
||||
setSearchText(newSearchText);
|
||||
};
|
||||
|
||||
const handleSearchSubmit = (event: any) => {
|
||||
event.preventDefault();
|
||||
props.onSearch({ search: searchText }); // Trigger to Parent
|
||||
};
|
||||
|
||||
const handleGetData = (type :string) => {
|
||||
axios.get(`claims/1/data-claim`)
|
||||
.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();
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Trigger First Search
|
||||
// setSearchText(searchParams.get('search') ?? '');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSearchSubmit} style={{ width: '100%' }}>
|
||||
<Stack direction={'row'} spacing={2} sx={{ mb: 2 }}>
|
||||
<TextField
|
||||
id="search-input"
|
||||
ref={searchInput}
|
||||
label="Search"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
onChange={handleSearchChange}
|
||||
value={searchText}
|
||||
placeholder='Search Code or Member ID...'
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<Download />}
|
||||
onClick={() => handleGetData('DO')}
|
||||
sx={{ p: 1.8 }}
|
||||
>
|
||||
Export
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
function ImportForm(props: any) {
|
||||
// IMPORT
|
||||
// Create Button Menu
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
|
||||
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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const searchInput = useRef<HTMLInputElement>(null);
|
||||
|
||||
|
||||
//handle search
|
||||
const handleSearchChange = (event: any) => {
|
||||
const newSearchText = event.target.value ?? '';
|
||||
setSearchText(newSearchText);
|
||||
};
|
||||
|
||||
const handleSearchSubmit = (event: any) => {
|
||||
event.preventDefault();
|
||||
loadDataTableData();
|
||||
};
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Trigger First Search
|
||||
//setSearchText(searchText);
|
||||
}, []);
|
||||
|
||||
const item = [
|
||||
{
|
||||
id: '',
|
||||
value: '',
|
||||
name: 'Semua',
|
||||
},
|
||||
];
|
||||
|
||||
// const handleClick = () => {
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// Dummy Default Data
|
||||
const [dataTableIsLoading, setDataTableLoading] = useState(true);
|
||||
const [dataTableData, setDataTableData] = useState<LaravelPaginatedData>(
|
||||
LaravelPaginatedDataDefault
|
||||
);
|
||||
|
||||
|
||||
|
||||
const loadDataTableData = async (appliedFilter: any | null = null) => {
|
||||
setDataTableLoading(true);
|
||||
const year = startDate?.getFullYear();
|
||||
const month = (startDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day = startDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate = year && month && day ? `${year}-${month}-${day}` : '';
|
||||
|
||||
const year1 = endDate?.getFullYear();
|
||||
const month1 = (endDate?.getMonth() + 1).toString().padStart(2, '0'); // Tambahkan 1 karena bulan dimulai dari 0, dan padStart untuk memastikan 2 digit
|
||||
const day1 = endDate?.getDate().toString().padStart(2, '0'); // padStart untuk memastikan 2 digit
|
||||
|
||||
const formattedDate1 = year1 && month1 && day1 ? `${year1}-${month1}-${day1}` : '';
|
||||
|
||||
const filter = appliedFilter ? appliedFilter : Object.fromEntries([...searchParams.entries()]);
|
||||
const response = await axios.get('/claims-files-provider', {
|
||||
params: {
|
||||
search: searchText,
|
||||
start_date: formattedDate ? formattedDate : null,
|
||||
end_date:formattedDate1,
|
||||
provider: dataProvider,
|
||||
order: order,
|
||||
orderBy: orderBy,
|
||||
page: perPage,
|
||||
}
|
||||
});
|
||||
|
||||
setDataTableLoading(false);
|
||||
|
||||
setDataTableData(response.data);
|
||||
};
|
||||
|
||||
const getProvider = async () => {
|
||||
const response = await axios.get('/claims/get-provider');
|
||||
setProviders(response.data)
|
||||
}
|
||||
|
||||
const applyFilter = async (searchFilter: { search: string }) => {
|
||||
await loadDataTableData(searchFilter);
|
||||
setSearchParams(searchFilter);
|
||||
};
|
||||
|
||||
const handlePageChange = (event: ChangeEvent, value: number): void => {
|
||||
setPerPage(value);
|
||||
};
|
||||
|
||||
const [openDialogSubmit, setOpenDialogSubmit] = useState(false);
|
||||
const handleCloseDialogSubmit = () => {
|
||||
setOpenDialogSubmit(false);
|
||||
}
|
||||
|
||||
function toTitleCase(str: string | null) {
|
||||
return str.replace(/\w\S*/g, function(txt) {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
const [approve, setApprove] = useState('');
|
||||
|
||||
const [reasonDecline, setReasonDecline] = useState('');
|
||||
|
||||
const handleReasonDeclineChange = (event) => {
|
||||
setReasonDecline(event.target.value);
|
||||
// Tambahkan logika yang diperlukan di sini
|
||||
};
|
||||
|
||||
const handleSubmitData = async () => {
|
||||
try {
|
||||
const response = await axios.post('download-zip', { selectedRows: selectedRows });
|
||||
const fileUrl = response.data.file_url; // Perbaikan disini
|
||||
enqueueSnackbar('Data berhasil di download', { variant: 'success' });
|
||||
window.open(fileUrl, '_blank');
|
||||
setOpenDialogSubmit(false);
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000); // Reload the page after 5 seconds
|
||||
} catch (error) {
|
||||
enqueueSnackbar('Data Gagal di download', { variant: 'error' });
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmitData1 = () => {
|
||||
//approve or decline
|
||||
if (!reasonDecline && approve == 'decline') {
|
||||
enqueueSnackbar('Mohon isi alasan', { variant: 'warning' });
|
||||
return false;
|
||||
}
|
||||
Promise.all(selectedRows.map(send_bulk))
|
||||
.then(() => {
|
||||
enqueueSnackbar('All requests processed successfully', { variant: 'success' });
|
||||
setOpenDialogSubmit(false);
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 5000); // Reload the page after 5 seconds
|
||||
})
|
||||
.catch((error) => {
|
||||
enqueueSnackbar(error.response?.data?.message ?? 'Something went wrong!', { variant: 'error' });
|
||||
});
|
||||
};
|
||||
|
||||
function send_bulk(id) {
|
||||
return axios.post(`claims/${id}/${approve}`, { reasonDecline: reasonDecline });
|
||||
}
|
||||
|
||||
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const createMenu = Boolean(anchorEl);
|
||||
const importClaimManagement = 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 (importClaimManagement?.current) {
|
||||
handleClose();
|
||||
importClaimManagement.current ? importClaimManagement.current.click() : console.log('No File selected');
|
||||
} else {
|
||||
alert('No file selected');
|
||||
}
|
||||
};
|
||||
const handleCancelImportButton = () => {
|
||||
if(importClaimManagement.current)
|
||||
{
|
||||
importClaimManagement.current.value = '';
|
||||
importClaimManagement.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(importClaimManagement.current && importClaimManagement.current.files)
|
||||
{
|
||||
if (importClaimManagement.current?.files.length) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', importClaimManagement.current?.files[0]);
|
||||
setImportLoading(true);
|
||||
axios
|
||||
.post('claims/import', formData)
|
||||
.then((response) => {
|
||||
handleCancelImportButton();
|
||||
loadDataTableData();
|
||||
setImportResult(response.data);
|
||||
setImportLoading(false);
|
||||
enqueueSnackbar('Success Import Claim Managemenet', { 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('claims/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();
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
const handleExportReportFiled = async () => {
|
||||
|
||||
await axios
|
||||
.post('claims/exportFiled', { params: importResult?.data.result_rows })
|
||||
.then((res) => {
|
||||
enqueueSnackbar('Data berhasil di Export', {
|
||||
variant: 'success',
|
||||
anchorOrigin: { horizontal: 'right', vertical: 'top' },
|
||||
});
|
||||
setIsLoading(false)
|
||||
|
||||
document.location.href = res.data.data.file_url;
|
||||
})
|
||||
.catch((err) =>
|
||||
enqueueSnackbar('Data Gagal di Export', {
|
||||
variant: 'error',
|
||||
anchorOrigin: { horizontal: 'right', vertical: 'top' },
|
||||
})
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// useEffect(() => {
|
||||
// loadDataTableData();
|
||||
// getProvider();
|
||||
// }, []);
|
||||
|
||||
const headStyle = {
|
||||
fontWeight: 'bold',
|
||||
};
|
||||
const headCells = [
|
||||
{
|
||||
id: 'created_at',
|
||||
align: 'left',
|
||||
label: 'Date Submission',
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'code',
|
||||
align: 'left',
|
||||
label: 'Code',
|
||||
isSort: true,
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
align: 'left',
|
||||
label: 'Name',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'provider',
|
||||
align: 'left',
|
||||
label: 'Provider',
|
||||
isSort: false,
|
||||
},
|
||||
{
|
||||
id: 'files',
|
||||
align: 'left',
|
||||
label: 'Nama File',
|
||||
isSort: false,
|
||||
},
|
||||
];
|
||||
|
||||
const orders = {
|
||||
order: order,
|
||||
setOrder: setOrder,
|
||||
orderBy: orderBy,
|
||||
setOrderBy: setOrderBy,
|
||||
};
|
||||
const createSortHandler = (property: string) => (event: React.MouseEvent<unknown>) => {
|
||||
handleRequestSort(event, property);
|
||||
};
|
||||
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
|
||||
const isAsc = orders?.orderBy === property && orders?.order === 'asc';
|
||||
|
||||
orders?.setOrder(isAsc ? 'desc' : 'asc');
|
||||
orders?.setOrderBy(property);
|
||||
};
|
||||
// Called on every row to map the data to the columns
|
||||
function createData(data: Claims): Claims {
|
||||
return {
|
||||
...data,
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
/* ------------------ TABLE ROW ------------------ */
|
||||
}
|
||||
function Row(props: { row: ReturnType<typeof createData>, isSelected: boolean, onSelect: (id: string) => void }) {
|
||||
const { row, isSelected, onSelect } = props;
|
||||
// Memperbaiki destrukturisasi props
|
||||
|
||||
const handleRowCheckboxChange = () => {
|
||||
onSelect(row.id); // Panggil fungsi onSelect dari komponen induk dengan id baris saat checkbox di baris diklik
|
||||
};
|
||||
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const test = 1000;
|
||||
|
||||
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">
|
||||
<Checkbox checked={isSelected} onChange={handleRowCheckboxChange} />
|
||||
</TableCell>
|
||||
<TableCell align="left">{row?.created_at ? fDateTime(row?.created_at) : ''}</TableCell>
|
||||
<TableCell align="left">{row?.code}</TableCell>
|
||||
{/* <TableCell align="left">{row.code}</TableCell> */}
|
||||
<TableCell align="left">{row?.name}</TableCell>
|
||||
<TableCell align="left">{row?.provider}</TableCell>
|
||||
<TableCell align='left'>
|
||||
<a
|
||||
href={row.path}
|
||||
style={{ cursor: 'pointer', textDecoration: 'none', color: '#19BBBB' }}
|
||||
target="_blank"
|
||||
>
|
||||
<Typography variant="body2" gutterBottom>{row.files ? row.files : '-'}</Typography>
|
||||
</a>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/* COLLAPSIBLE ROW */}
|
||||
<TableRow>
|
||||
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={99}>
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
{/* <Box sx={{ borderBottom: 1 }}>
|
||||
<Typography variant="body2" gutterBottom component="div">
|
||||
Description : {row.description}
|
||||
</Typography>
|
||||
</Box> */}
|
||||
</Collapse>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
{
|
||||
/* ------------------ END TABLE ROW ------------------ */
|
||||
}
|
||||
|
||||
|
||||
|
||||
function TableContent() {
|
||||
return (
|
||||
<Table aria-label="collapsible table">
|
||||
{/* ------------------ TABLE HEADER ------------------ */}
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{selectedRows.length > 0 ? (
|
||||
<>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={2}>
|
||||
<Stack direction="row">
|
||||
<Checkbox checked={selectAll} onChange={handleSelectAll} />
|
||||
{selectedRows.length > 0 ? selectedRows.length : '0'} <Typography variant='subtitle2'>Selected</Typography>
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={6}>
|
||||
|
||||
</TableCell>
|
||||
{/* <TableCell style={{ backgroundColor: '#D1F1F1' }} align="right" colSpan={2}>
|
||||
<Button variant="text" color="error" startIcon={<CancelIcon />} onClick={() => {setOpenDialogSubmit(true);
|
||||
setApprove('decline');}}>
|
||||
<Typography variant='subtitle2'>Decline</Typography>
|
||||
</Button>
|
||||
</TableCell> */}
|
||||
<TableCell style={{ backgroundColor: '#D1F1F1' }} align="left" colSpan={4}>
|
||||
<Button variant="text" color="primary" startIcon={<CheckCircleIcon />} onClick={() => {setOpenDialogSubmit(true);
|
||||
setApprove('approve');}}>
|
||||
<Typography variant='subtitle2'>Download</Typography>
|
||||
</Button>
|
||||
</TableCell>
|
||||
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TableCell style={headStyle} align="left">
|
||||
<Checkbox checked={selectAll} onChange={handleSelectAll} />
|
||||
</TableCell>
|
||||
{headCells &&
|
||||
headCells.map((headCell, index) => (
|
||||
<TableCell
|
||||
key={index}
|
||||
sortDirection={orders?.orderBy === headCell.id ? orders.order : false}
|
||||
// @ts-ignore
|
||||
align={headCell.align}
|
||||
sx={{ padding: 2 }}
|
||||
width={headCell.width ? headCell.width : 'auto'}
|
||||
>
|
||||
{headCell.isSort ? (
|
||||
<TableSortLabel
|
||||
active={orders?.orderBy === headCell.id}
|
||||
direction={orders?.orderBy === headCell.id ? orders.order : 'asc'}
|
||||
onClick={createSortHandler(headCell.id)}
|
||||
>
|
||||
{headCell.label}
|
||||
{orders?.orderBy === headCell.id ? (
|
||||
<Box component="span" sx={visuallyHidden}>
|
||||
{orders.order === 'desc' ? 'sorted descending' : 'sorted ascending'}
|
||||
</Box>
|
||||
) : null}
|
||||
</TableSortLabel>
|
||||
) : (
|
||||
headCell.label
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</>
|
||||
|
||||
)}
|
||||
|
||||
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
{/* ------------------ END TABLE HEADER ------------------ */}
|
||||
|
||||
{/* ------------------ TABLE ROW ------------------ */}
|
||||
{dataTableIsLoading ? (
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={11} align="center">
|
||||
Loading
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
) : dataTableData.data.length === 0 ? (
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={11} align="center">
|
||||
No Data
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
) : (
|
||||
<TableBody>
|
||||
{dataTableData.data.map((row) => (
|
||||
<Row key={row.id} row={row} isSelected={selectedRows.includes(row.id)} onSelect={handleRowSelect} />
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
{/* ------------------ END TABLE ROW ------------------ */}
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
sx={{ p: 2, justifyContent: 'space-between', alignItems: 'center' }}
|
||||
>
|
||||
<Grid item xs={12} md={12} lg={12}>
|
||||
<form style={{ width: '100%' }}>
|
||||
<Grid container spacing={1} sx={{ justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<input
|
||||
type="file"
|
||||
id="file"
|
||||
ref={importClaimManagement}
|
||||
style={{ display: 'none' }}
|
||||
onChange={handleImportChange}
|
||||
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel, text/plain"
|
||||
/>
|
||||
{!currentImportFileName && (
|
||||
<>
|
||||
<Grid item xs={12} md={4}>
|
||||
<TextField
|
||||
id="search-input"
|
||||
ref={searchInput}
|
||||
variant="outlined"
|
||||
value={searchText}
|
||||
fullWidth
|
||||
onChange={handleSearchChange}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSearchSubmit(event);
|
||||
}
|
||||
}}
|
||||
InputProps={{
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<Search />
|
||||
</InputAdornment>
|
||||
),
|
||||
placeholder: 'Search Code or Name',
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={5} display="flex" sx={{ gap: '16px' }}>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DesktopDatePicker
|
||||
value={startDate}
|
||||
inputFormat="dd/MM/yyyy"
|
||||
onChange={(value) => {
|
||||
|
||||
// loadDataTableData();
|
||||
setStartDate(value);
|
||||
}}
|
||||
renderInput={(params) => <TextField {...params} fullWidth label="Start" />}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<DesktopDatePicker
|
||||
value={endDate}
|
||||
inputFormat="dd/MM/yyyy"
|
||||
onChange={(value) => {
|
||||
setEndDate(value);
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
fullWidth
|
||||
label="End"
|
||||
// error={!!error}
|
||||
// helperText={error?.message}
|
||||
// {...other}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={3}>
|
||||
{
|
||||
providers ? (
|
||||
<Autocomplete
|
||||
id="provider"
|
||||
options={providers}
|
||||
getOptionLabel={(option) => option.name || ''}
|
||||
value={providers.find((item) => item.id === dataProvider) || null}
|
||||
onChange={(event, value) => {
|
||||
if (value) {
|
||||
setDataProvider(value.id);
|
||||
} else {
|
||||
setDataProvider(null);
|
||||
}
|
||||
}}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label="Provider"
|
||||
fullWidth
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
):(
|
||||
<>
|
||||
<Typography variant='body2' align='center'>Loading...</Typography>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</Grid>
|
||||
{/* <Grid item xs={12} md={3} display="flex" sx={{ gap: '16px' }}>
|
||||
<FormControl >
|
||||
<LoadingButton
|
||||
id="upload-button"
|
||||
variant="outlined"
|
||||
startIcon={<UploadIcon />}
|
||||
sx={{ p: 1.8 }}
|
||||
loading={isLoadingImport}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<Typography variant="inherit" sx={{ marginLeft: 1 }}>
|
||||
Import
|
||||
</Typography>
|
||||
</LoadingButton>
|
||||
<Menu
|
||||
id="import-button"
|
||||
anchorEl={anchorEl}
|
||||
open={createMenu}
|
||||
onClose={handleClose}
|
||||
MenuListProps={{
|
||||
'aria-labelledby': 'basic-button',
|
||||
}}
|
||||
>
|
||||
<MenuItem onClick={handleImportButton}>
|
||||
<Typography variant='body2'>Import</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
handleGetTemplate();
|
||||
}}
|
||||
>
|
||||
<Typography variant='body2'> Download Template</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</FormControl>
|
||||
<FormControl >
|
||||
<LoadingButton
|
||||
id="upload-button"
|
||||
variant="contained"
|
||||
startIcon={<Download />}
|
||||
sx={{ p: 1.8 }}
|
||||
onClick={handleExportReport}
|
||||
loading={isLoading}
|
||||
>
|
||||
<Typography variant="inherit" sx={{ marginLeft: 1 }}>
|
||||
Export
|
||||
</Typography>
|
||||
</LoadingButton>
|
||||
</FormControl>
|
||||
</Grid> */}
|
||||
</>
|
||||
)}
|
||||
{currentImportFileName && (
|
||||
<Grid item xs={12} md={12}>
|
||||
<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>
|
||||
</Grid>
|
||||
)}
|
||||
{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'}]</Typography>
|
||||
))} */}
|
||||
Report:
|
||||
<u onClick={handleExportReportFiled} style={{cursor:'pointer'}}>Download Data Result Import</u>
|
||||
</Box>
|
||||
</Stack>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<DataTable
|
||||
isLoading={dataTableIsLoading}
|
||||
lastRequest={0}
|
||||
data={dataTableData}
|
||||
handlePageChange={handlePageChange}
|
||||
TableContent={<TableContent />}
|
||||
/>
|
||||
<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>
|
||||
|
||||
<Stack spacing={2} padding={2}>
|
||||
<Typography variant='body1'>Are you sure to Download this files selected ?</Typography>
|
||||
{approve == "decline" ? (
|
||||
<Stack direction='row' spacing={2} marginTop={2}>
|
||||
<TextField
|
||||
id="outlined-multiline-static"
|
||||
label="Reason decline"
|
||||
multiline
|
||||
rows={4} // Tentukan jumlah baris yang diinginkan
|
||||
defaultValue=""
|
||||
onChange={handleReasonDeclineChange}
|
||||
variant="outlined"
|
||||
sx={{width:'100%'}}
|
||||
// fullWidth // Gunakan ini jika Anda ingin input memenuhi lebar Stack
|
||||
/>
|
||||
</Stack>
|
||||
): ''}
|
||||
</Stack>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button variant="outlined" sx={{color: '#212B36', borderColor: '#919EAB52'}} onClick={handleCloseDialogSubmit}>Cancel</Button>
|
||||
<Button sx={{backgroundColor: (approve === 'decline' ? '' : '#19BBBB'), color: (approve === 'decline' ? '#FF4842' : ''), borderColor: '#FF4842'}} onClick={handleSubmitData} variant={(approve === 'decline' ? 'outlined' : 'contained')}>{(approve === "decline" ? 'Decline' : 'Download')}</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -418,6 +418,10 @@ export default function Router() {
|
||||
path: 'report/appointments/:id/edit',
|
||||
element: <AppointmentCreate />,
|
||||
},
|
||||
{
|
||||
path: 'report/files-provider',
|
||||
element: <FilesProvider />,
|
||||
},
|
||||
{
|
||||
path: 'report/live-chat',
|
||||
element: <Livechat />,
|
||||
@@ -662,6 +666,8 @@ const Appointment = Loadable(lazy(() => import('../pages/Report/Appointments/Ind
|
||||
const AppointmentCreate = Loadable(lazy(() => import('../pages/Report/Appointments/Create')));
|
||||
const AppointmentShow = Loadable(lazy(() => import('../pages/Report/Appointments/Show')));
|
||||
|
||||
const FilesProvider = Loadable(lazy(() => import('../pages/Report/FilesProvider/Index')));
|
||||
|
||||
const Livechat = Loadable(lazy(() => import('../pages/Report/Livechat/Index')));
|
||||
const LivechatCreate = Loadable(lazy(() => import('../pages/Report/Livechat/Create')));
|
||||
const LivechatShow = Loadable(lazy(() => import('../pages/Report/Livechat/Show')));
|
||||
|
||||
Reference in New Issue
Block a user