GRAB X APOTEK X Hospital PORTAL

This commit is contained in:
ivan-sim
2024-10-03 16:14:11 +07:00
parent 15138f7edf
commit 0758a70434
19 changed files with 2107 additions and 49 deletions

View File

@@ -25,5 +25,5 @@ export default function Logo({ disabledLink = false, sx }: Props) {
return <>{logo}</>;
}
return <RouterLink to="/">{logo}</RouterLink>;
return <RouterLink to="#">{logo}</RouterLink>;
}

View File

@@ -24,12 +24,21 @@ import {
Typography,
LinearProgress,
linearProgressClasses,
Collapse,
Divider,
IconButton,
Modal,
CircularProgress
} from '@mui/material';
import { Dialog, DialogTitle, DialogContent, DialogActions } from '@mui/material';
import { visuallyHidden } from '@mui/utils';
import { DatePicker, LocalizationProvider, MobileDatePicker } from '@mui/x-date-pickers';
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
import { fDate, fDateSuffix, fDateTime, fDateTimeWithAge } from '../utils/formatTime';
/* ---------------------------------- axios --------------------------------- */
import axios from '../utils/axios';
/* ---------------------------------- react --------------------------------- */
@@ -50,6 +59,10 @@ import GetAppIcon from '@mui/icons-material/GetApp';
import { LanguageContext } from '@/contexts/LanguageContext';
import CancelIcon from '@mui/icons-material/Cancel';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import CloseIcon from '@mui/icons-material/Close';
import InfoIcon from '@mui/icons-material/Info';
import { enqueueSnackbar } from 'notistack';
import useAuth from '@/hooks/useAuth';
/* --------------------------------- styled --------------------------------- */
const BorderLinearProgress = styled(LinearProgress)(({ theme }) => ({
@@ -79,6 +92,9 @@ export default function Table<T>({
searchs,
exportReport,
selected,
openRowId, // Receive the currently opened row ID
setOpenRowId, // Receive the function to set the opened row ID
reloadData,
}: TableListProps<T>) {
/* ------------------------------- handle sort ------------------------------ */
const handleRequestSort = async (event: React.MouseEvent<unknown>, property: string) => {
@@ -209,6 +225,114 @@ export default function Table<T>({
params.setAppliedParams(parameters);
};
/* -------------------------------------------------------------------------- */
//============================== APOTEK =======================================//
const {user} = useAuth();
const formattedRoleName = user?.role.name;
// List of statuses for 'apotek'
const allowedStatusesForApotek = ['waiting_pharmacy', 'order_prepared'];
const allowedStatusesForCSLMS = ['ready', 'waiting_for_courir', 'package_picked_up', 'package_on_delivery'];
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(true);
const handleClick = () => {
// handleClickKonfirmasi(row); // Call the function passed from parent
setOpen(true); // Show modal
setLoading(true); // Show loading animation
// Simulate finding driver (3 seconds delay)
setTimeout(() => {
setLoading(false); // Hide loading after 3 seconds
}, 3000);
};
const [openDialogStatus, setOpenDialogStatus] = useState(false);
const [dialogIDRow, setDialogIDRow] = useState<number | null>(null);
const [dialogIDRowDriver, setDialogIDRowDriver] = useState<number | null>(null);
const [txtStatusDriver, setTxtStatusDriver] = useState('');
const [txtIDDriver, setTxtIDDriver] = useState('');
const handleClose = (row:any) => {
setDialogIDRowDriver(row.id === dialogIDRowDriver ? null : row.id);
if (reloadData) {
reloadData();
}
};
const handleCloseDialogUpdate = () => {
setOpenDialogStatus(false);
}
const handleEditDataStatus = (data: any) => {
setOpenDialogStatus(true);
}
const [isDisabled, setIsDisabled] = useState(false);
const handleClickKonfirmasi = (row: any) => {
setTxtStatusDriver('');
setTxtIDDriver('');
if(row.sStatus === 'ready')
{
//close
setDialogIDRow(row.id === dialogIDRow ? null : row.id);
//get driver
setDialogIDRowDriver(row.id === dialogIDRowDriver ? null : row.id);
// setOpen(true); // Show modal
setLoading(true); // Show loading animation
// Simulate finding driver (3 seconds delay)
// setTimeout(() => {
// setLoading(false); // Hide loading after 3 seconds
// }, 3000);
const updateData = {
sStatus: row.sStatus
};
axios
.put(`/put-driver-prescription-orders/${row.nID_orders}`, updateData)
.then((response) => {
setTxtStatusDriver(response?.data?.data?.status);
setTxtIDDriver(response?.data?.data?.deliveryID);
setLoading(false);
// enqueueSnackbar(response?.data?.meta?.message, { variant: 'success' });
// Call reloadData to refresh the table
})
.catch((error) => {
const errorMessage = error.response?.data?.meta?.message || 'Failed to update status';
enqueueSnackbar(errorMessage, { variant: 'error' });
setLoading(false);
setIsDisabled(false); // Re-enable the button after success
});
}
else
{
putPrescriptionOrders(row);
}
}
const putPrescriptionOrders = (row: any) => {
setIsDisabled(true); // Disable button after clicking
const updateData = {
sStatus: row.sStatus
};
axios
.put(`/put-prescription-orders/${row.nID_orders}`, updateData)
.then((response) => {
enqueueSnackbar(response?.data?.meta?.message, { variant: 'success' });
setDialogIDRow(row.id === dialogIDRow ? null : row.id);
setIsDisabled(false); // Re-enable the button after success
// Call reloadData to refresh the table
if (reloadData) {
reloadData();
}
})
.catch((error) => {
const errorMessage = error.response?.data?.meta?.message || 'Failed to update status';
enqueueSnackbar(errorMessage, { variant: 'error' });
setIsDisabled(false); // Re-enable the button after success
});
}
return (
// <Card>
<Grid container>
@@ -416,6 +540,7 @@ export default function Table<T>({
</TableRow>
) : rows && rows.length >= 1 ? (
rows.map((row, rowIndex) => (
<>
<TableRow key={rowIndex}>
{!selected.useSelected ? (
''
@@ -440,6 +565,378 @@ export default function Table<T>({
</TableCell>
))}
</TableRow>
{/* COLLAPSIBLE ROW */}
<TableRow
sx={{ cursor: 'pointer' }} // Use pointer cursor always, or conditionally based on your preference
>
<TableCell colSpan={6}>
<Collapse in={openRowId === row.id} timeout="auto" unmountOnExit>
<Card sx={{padding:2}}>
{/* Icon inside a Box for spacing and alignment */}
<Stack direction="row" padding={2} justifyContent="space-between" alignItems="center">
<Stack direction="row">
<Box sx={{ display: 'flex', alignItems: 'center', marginRight: 1 }}>
<InfoIcon sx={{ color: row.sStatus == 'waiting_pharmacy' ? '#D3D3D3' : '#00E676', fontSize: 24 }} /> {/* Adjust size and color */}
</Box>
<Typography variant='subtitle2' sx={{ color: '#00E676' }}>
{row.status}
</Typography>
</Stack>
{row.nDeliveryID && (
<Box sx={{ ml: 'auto' }}>
<Typography variant='subtitle2'>
Grab Delivery ID: {row.nDeliveryID}
</Typography>
</Box>
)}
</Stack>
<Box onClick={() => {setOpenRowId(openRowId === row.id ? null : row.id);}} sx={{ padding: '20px', maxWidth: '1200px', margin: '0 auto', backgroundColor: '#ffffff', borderRadius: '8px' }}>
<Grid container spacing={2}>
{/* Row 1 */}
<Grid item xs={12} md={6}>
<Typography variant="h6" fontWeight="bold">{localeData.txtProviderDoctorInformation}</Typography>
<Grid container>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtNamaDokter} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.nama_dokter}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtSpesialisasiDokter} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.spesialis}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">SIP :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.sip}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtNoPonsel} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.no_ponsel_dokter}</strong></Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="h6" fontWeight="bold">{localeData.txtInformasiPasien}</Typography>
<Grid container>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtNamaPasien} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.pasien}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtTanggalLahirUmur} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.tgl_lahir_pasien ? fDateTimeWithAge(row.tgl_lahir_pasien) : ''}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtJenisKelamin} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.jenis_kelamin_pasien}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtTinggi} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.tinggi_berat}</strong></Typography>
</Grid>
</Grid>
</Grid>
{/* Divider */}
<Grid item xs={12}>
<Divider />
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="h6" fontWeight="bold">{localeData.txtInformasiAsuransi}</Typography>
<Grid container>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtNomorKartu} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.no_polis}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtAsuransi} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.perusahaan_asuransi}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtPerusahaan} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.nama_perusahaan}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtTipeAsuransi} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.kode_produk}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtKelasAsuransi} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.kelas_asuransi}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtTipeMember} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.tipe_member}</strong></Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="h6" fontWeight="bold">{localeData.txtInformasiOrder}</Typography>
<Grid container>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtNamaPenerima} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.pasien}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtAlamatPenerima} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.alamat_penerima}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtPengiriman} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.pengiriman}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtTotal} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.total_kirim}</strong></Typography>
</Grid>
</Grid>
</Grid>
{/* Divider */}
<Grid item xs={12}>
<Divider />
</Grid>
{/* Row 2 */}
<Grid item xs={12} md={6}>
<Typography variant="h6" fontWeight="bold">{localeData.txtInformasiResep}</Typography>
<Grid container>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtNomorResep} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.no_resep}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtNomorRefDokter} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.noref_dokter}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtTanggalTerbitResep} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.tanggal}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtTanggalKedaluwarsa} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.valid_tanggal}</strong></Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Typography variant="h6" fontWeight="bold">{localeData.txtInformasiKonsultasi}</Typography>
<Grid container>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtNomorPenjamin} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.nomor_penjamin}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtTanggalKonsultasi} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.tgl_livechat}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtAlergi} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.alergi_desc}</strong></Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2">{localeData.txtDiagnosis} :</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="body2"><strong>{row.diagnosa}</strong></Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</Box>
<Box onClick={() => {setOpenRowId(openRowId === row.id ? null : row.id);}} sx={{ padding: '20px', maxWidth: '1200px', margin: '20px auto', backgroundColor: '#ffffff', borderRadius: '8px' }}>
<TableHead>
<TableRow>
<TableCell>{localeData.txtObat}</TableCell>
<TableCell align="left">{localeData.txtSigna}</TableCell>
<TableCell align="left">{localeData.txtWaktu}</TableCell>
<TableCell align="left">{localeData.txtDurasi}</TableCell>
<TableCell align="left">{localeData.txtJumlah}</TableCell>
<TableCell align="left">{localeData.txtCatatanObat}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{row.prescription_items ?
(
row.prescription_items.map((row : any, rowIndex: any) => (
<TableRow key={rowIndex}>
<TableCell>{row.sItemName}</TableCell>
<TableCell align="left">{row.sSigna}</TableCell>
<TableCell align="left">{row.sTiming}</TableCell>
<TableCell align="left">{row.sDuration}</TableCell>
<TableCell align="left">{row.nQty}</TableCell>
<TableCell align="left">{row.sNote}</TableCell>
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={headCells?.length} align="center">
{localeData.txtDataNotFound}
</TableCell>
</TableRow>
)}
</TableBody>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', padding: '20px', maxWidth: '1200px', margin: '0 auto' }}>
{/* Close Button on the Left */}
<Button variant="outlined" color="inherit" onClick={() => setOpenRowId(null)}>
{localeData.txtButtonClose}
</Button>
{/* Accept Button on the Right */}
{row.button_accept && formattedRoleName === 'admin-apotek' && allowedStatusesForApotek.includes(row.sStatus) ? (
<Button variant="contained" color="primary" onClick={() => setDialogIDRow(row.id === dialogIDRow ? null : row.id)}>
{row.button_accept}
</Button>
) : ''}
{row.button_accept && formattedRoleName === 'cs-lms' && allowedStatusesForCSLMS.includes(row.sStatus) ? (
<Button variant="contained" color="primary" onClick={() => setDialogIDRow(row.id === dialogIDRow ? null : row.id)}>
{row.button_accept}
</Button>
) : ''}
</Box>
{/* Dialog Update Status */}
<Dialog open={dialogIDRow === row.id} 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">{localeData.txtConfirmation}</Typography>
</Stack>
<IconButton sx={{ color: '#FFF' }} onClick={() => setDialogIDRow(row.id === dialogIDRow ? null : row.id)}>
<CloseIcon />
</IconButton>
</Stack>
</DialogTitle>
<DialogContent>
<Stack spacing={2} padding={2}>
<Typography variant='body1'>{localeData.txtDialogConfirmation}</Typography>
<Card sx={{padding:2}} >
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '50%'}}>{localeData.txtNomorResep}</Typography>
<Typography variant='subtitle2' sx={{width: '50%'}}>{row.no_resep}</Typography>
</Stack>
<Stack direction='row' spacing={2}>
<Typography variant='subtitle2' sx={{color: '#919EAB', width: '50%'}}>{localeData.txtTanggalTerbitResep}</Typography>
<Typography variant='subtitle2' sx={{width: '50%'}}>{row.tanggal}</Typography>
</Stack>
</Card>
</Stack>
</DialogContent>
<DialogActions>
<Button sx={{backgroundColor: '#19BBBB'}} variant="contained" onClick={() => handleClickKonfirmasi(row)} disabled={isDisabled}>{localeData.txtOK}</Button>
</DialogActions>
</Dialog>
{/* Modal for fullscreen display */}
<Modal open={dialogIDRowDriver === row.id} onClose={() => handleClose(row)}>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
// backgroundColor: 'rgba(0, 0, 0, 0.8)', // semi-transparent background
color: '#fff',
textAlign: 'center',
}}
>
{loading ? (
<>
<CircularProgress color="inherit" />
<Typography variant="h6" sx={{ mt: 2 }}>
{localeData.txtFindingDriver}
</Typography>
</>
) : (
<>
<Typography variant="h4">Info!</Typography>
<Typography variant="h6" sx={{ mt: 2 }}>
{txtIDDriver ? (
`${localeData.txtDriverFound} ${txtIDDriver} status ${txtStatusDriver}.`
) : (
// You can add an alternative UI or message here, or leave it empty
`Failed to get driver, please check the message info.`
)}
</Typography>
<Button
sx={{ mt: 4, backgroundColor: '#19BBBB' }}
variant="contained"
onClick={() => handleClose(row)}
>
{localeData.txtOK}
</Button>
</>
)}
</Box>
</Modal>
</Card>
</Collapse>
</TableCell>
</TableRow>
</>
))
) : (
<TableRow>

View File

@@ -28,26 +28,42 @@ export default function NavSectionVertical({
isCollapse = false,
...other
}: NavSectionProps) {
return (
<Box {...other}>
{navConfig.map((group, index) => (
<List key={index} disablePadding sx={{ px: 2 }}>
<ListSubheaderStyle
key={index}
sx={{
...(isCollapse && {
opacity: 0,
}),
}}
>
{group.subheader}
</ListSubheaderStyle>
return (
<Box {...other}>
{navConfig.length === 0 ? ( // Check if navConfig is empty
<List disablePadding sx={{ px: 2, display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%' }}>
<ListSubheaderStyle
sx={{
...(isCollapse && {
opacity: 0,
}),
}}
>
{/* Optional: You can put a title or leave it empty */}
</ListSubheaderStyle>
<div style={{ textAlign: 'center', marginTop: '50px' }}>Loading...</div> {/* Adjust marginTop as needed */}
</List>
) : (
navConfig.map((group, index) => (
<List key={index} disablePadding sx={{ px: 2 }}>
<ListSubheaderStyle
sx={{
...(isCollapse && {
opacity: 0,
}),
}}
>
{group.subheader}
</ListSubheaderStyle>
{group.items.map((list) => (
<NavListRoot key={list.title} list={list} isCollapse={isCollapse} />
))}
</List>
))
)}
</Box>
);
{group.items.map((list) => (
<NavListRoot key={list.title} list={list} isCollapse={isCollapse} />
))}
</List>
))}
</Box>
);
}