304 lines
11 KiB
TypeScript
Executable File
304 lines
11 KiB
TypeScript
Executable File
// @mui
|
|
import { Box, Button, Card, Collapse, IconButton, MenuItem, Table, TableBody, TableCell, TableRow, TextField, Typography, Stack, Menu, ButtonGroup } 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, useEffect, useRef, useState } from 'react';
|
|
import { useSearchParams } from 'react-router-dom';
|
|
// components
|
|
import axios from '../../utils/axios';
|
|
import { LaravelPaginatedData, LaravelPaginatedDataDefault } from '../../@types/paginated-data';
|
|
import DataTable from './LaravelTable';
|
|
|
|
export default function List() {
|
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
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({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}/>
|
|
</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 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])
|
|
axios.post(`corporates/${corporate_id}/import-plan-benefit`, 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={handleImportButton}>Import</MenuItem>
|
|
<MenuItem onClick={handleClose}>Download Template</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>
|
|
);
|
|
}
|
|
|
|
// 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('/master/drugs', { 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);
|
|
}
|
|
|
|
useEffect(() => {
|
|
loadDataTableData();
|
|
}, [])
|
|
|
|
const headStyle = {
|
|
fontWeight: 'bold',
|
|
};
|
|
|
|
// Called on every row to map the data to the columns
|
|
function createData( data: Icd ): Icd {
|
|
return {
|
|
...data,
|
|
}
|
|
}
|
|
|
|
{/* ------------------ TABLE ROW ------------------ */}
|
|
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.type}</TableCell>
|
|
<TableCell align="left">{row.code}</TableCell>
|
|
<TableCell align="left">{row.name}</TableCell>
|
|
<TableCell align="left">{row.version}</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 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 ------------------ */}
|
|
<TableBody>
|
|
<TableRow>
|
|
<TableCell style={headStyle} align="left" />
|
|
<TableCell style={headStyle} align="left">Type</TableCell>
|
|
<TableCell style={headStyle} align="left">Code</TableCell>
|
|
<TableCell style={headStyle} align="left">Name</TableCell>
|
|
<TableCell style={headStyle} align="left">Version</TableCell>
|
|
<TableCell style={headStyle} align="right">Status</TableCell>
|
|
<TableCell style={headStyle} align="right">Action</TableCell>
|
|
</TableRow>
|
|
</TableBody>
|
|
{/* ------------------ 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>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<Card>
|
|
<ImportForm />
|
|
|
|
<DataTable
|
|
isLoading={dataTableIsLoading}
|
|
lastRequest={0}
|
|
data={dataTableData}
|
|
handlePageChange={handlePageChange}
|
|
|
|
TableContent={<TableContent />}
|
|
/>
|
|
</Card>
|
|
);
|
|
}
|