feat: call transcripts
This commit is contained in:
+78
-28
@@ -31,6 +31,9 @@ import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { useStyles } from "./styles/tableStyles";
|
||||
import ProtectedComponent from "../components/ProtectedComponent";
|
||||
import { DesktopDatePicker, LocalizationProvider } from "@mui/x-date-pickers";
|
||||
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
|
||||
import { DateRangePicker } from "@mui/x-date-pickers-pro";
|
||||
|
||||
const Table = memo(
|
||||
forwardRef((props, ref) => {
|
||||
@@ -52,6 +55,7 @@ const Table = memo(
|
||||
getRowStyle = () => {},
|
||||
searchText,
|
||||
navigateTo,
|
||||
showDateFilters = false,
|
||||
} = props;
|
||||
const [data, setData] = useState([]);
|
||||
const [formattedColumns, setFormattedColumns] = useState([]);
|
||||
@@ -62,6 +66,7 @@ const Table = memo(
|
||||
const [columnFilters, setColumnFilters] = useState([]);
|
||||
const [globalFilter, setGlobalFilter] = useState("");
|
||||
const [sorting, setSorting] = useState([]);
|
||||
const [dateRange, setDateRange] = useState([null, null]);
|
||||
const tableRef = useRef();
|
||||
const [pagination, setPagination] = useState({
|
||||
pageIndex: options?.pageIndex ?? 0,
|
||||
@@ -69,6 +74,14 @@ const Table = memo(
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
if(dateRange){
|
||||
console.log(dateRange);
|
||||
|
||||
}
|
||||
},[dateRange])
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
reFetchData() {
|
||||
setIsLoading(true);
|
||||
@@ -149,12 +162,20 @@ const Table = memo(
|
||||
(sort) => `orderBy=${sort.id}&order=${sort.desc ? "DESC" : "ASC"}&`
|
||||
)
|
||||
.join("");
|
||||
let startDate = "";
|
||||
let endDate = "";
|
||||
if (dateRange) {
|
||||
startDate = dateRange[0];
|
||||
endDate = dateRange[1];
|
||||
}
|
||||
try {
|
||||
const response = await getData({
|
||||
pagination,
|
||||
filterString,
|
||||
globalFilter,
|
||||
sortingString,
|
||||
startDate,
|
||||
endDate,
|
||||
});
|
||||
setData(response.data);
|
||||
setRowCount(response.rowCount);
|
||||
@@ -178,6 +199,7 @@ const Table = memo(
|
||||
pagination.pageIndex,
|
||||
pagination.pageSize,
|
||||
sorting,
|
||||
dateRange,
|
||||
]);
|
||||
|
||||
const debouncedSearch = useMemo(
|
||||
@@ -229,6 +251,32 @@ const Table = memo(
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{showDateFilters && (
|
||||
<LocalizationProvider
|
||||
className={classes.searchContainer}
|
||||
dateAdapter={AdapterDateFns}
|
||||
>
|
||||
<DateRangePicker
|
||||
className={classes.searchDatePicker}
|
||||
renderInput={(props) => (
|
||||
<TextField {...props} variant="outlined" />
|
||||
)}
|
||||
slotProps={{
|
||||
field: {
|
||||
clearable: true,
|
||||
|
||||
}
|
||||
}}
|
||||
value={dateRange}
|
||||
onChange={(newValue) => {
|
||||
setDateRange(newValue);
|
||||
// column.setFilterValue(newValue);
|
||||
}}
|
||||
inputFormat="MMM d, yyyy"
|
||||
format="MMM d, yyyy"
|
||||
/>
|
||||
</LocalizationProvider>
|
||||
)}
|
||||
{!hideShowPerPage && (
|
||||
<Box className={classes.pageSizeDropdown}>
|
||||
<Typography className={classes.dropDownLabel}>Show:</Typography>
|
||||
@@ -287,11 +335,11 @@ const Table = memo(
|
||||
padding: "16px 8px",
|
||||
whiteSpace: "nowrap",
|
||||
justifyContent: "flex-start",
|
||||
'& .MuiTableSortLabel-root': {
|
||||
width: '100%',
|
||||
justifyContent: 'flex-start',
|
||||
}
|
||||
}
|
||||
"& .MuiTableSortLabel-root": {
|
||||
width: "100%",
|
||||
justifyContent: "flex-start",
|
||||
},
|
||||
},
|
||||
}}
|
||||
enableRowNumbers={enableRowNumbers}
|
||||
rowNumberMode={rowNumberMode}
|
||||
@@ -383,28 +431,30 @@ const Table = memo(
|
||||
className: classes?.tableCheckbox,
|
||||
}}
|
||||
renderRowActionMenuItems={({ row, closeMenu }) =>
|
||||
actions?.filter(action => !action.render)?.map((action, index) =>
|
||||
!(action?.renderAction?.(row) ?? true) ? null : (
|
||||
<MenuItem
|
||||
key={index}
|
||||
className={classes.menuItem}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
action.onClick && action.onClick(row);
|
||||
closeMenu();
|
||||
}}
|
||||
disabled={
|
||||
action?.isDisabledValue
|
||||
? action?.isDisabledValue ===
|
||||
row?.original?.[action?.rowKey]
|
||||
: false
|
||||
}
|
||||
>
|
||||
{action?.icon} {action?.text}{" "}
|
||||
{action.textFn && action.textFn(row)}
|
||||
</MenuItem>
|
||||
)
|
||||
) ?? []
|
||||
actions
|
||||
?.filter((action) => !action.render)
|
||||
?.map((action, index) =>
|
||||
!(action?.renderAction?.(row) ?? true) ? null : (
|
||||
<MenuItem
|
||||
key={index}
|
||||
className={classes.menuItem}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
action.onClick && action.onClick(row);
|
||||
closeMenu();
|
||||
}}
|
||||
disabled={
|
||||
action?.isDisabledValue
|
||||
? action?.isDisabledValue ===
|
||||
row?.original?.[action?.rowKey]
|
||||
: false
|
||||
}
|
||||
>
|
||||
{action?.icon} {action?.text}{" "}
|
||||
{action.textFn && action.textFn(row)}
|
||||
</MenuItem>
|
||||
)
|
||||
) ?? []
|
||||
}
|
||||
renderTopToolbarCustomActions={({ table }) => {
|
||||
const handleActive = () => {
|
||||
@@ -617,4 +667,4 @@ const CustomPagination = ({ table }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default Table;
|
||||
export default Table;
|
||||
|
||||
@@ -150,6 +150,31 @@ export const useStyles = makeStyles((theme) => ({
|
||||
opacity: 0.9,
|
||||
},
|
||||
},
|
||||
searchDatePicker: {
|
||||
margin: theme.spacing(2.0),
|
||||
backgroundColor: theme.palette.grey[0],
|
||||
borderRadius: theme.spacing(1.8),
|
||||
width: theme.spacing(32.0),
|
||||
'& .MuiOutlinedInput-root': {
|
||||
borderRadius: theme.spacing(1.8),
|
||||
},
|
||||
'&.MuiTextField-root': {
|
||||
width: theme.spacing(40.0),
|
||||
borderRadius: theme.spacing(1.8),
|
||||
},
|
||||
'& .MuiOutlinedInput-input::placeholder': {
|
||||
fontStyle: 'normal',
|
||||
fontSize: pxToRem(14),
|
||||
color: theme.palette.grey[10],
|
||||
opacity: 0.9,
|
||||
},
|
||||
'& .MuiPickersInputBase-root': {
|
||||
borderRadius: theme.spacing(1.4),
|
||||
},
|
||||
'& .MuiPickersSectionList-root': {
|
||||
padding: `12.5px !important`,
|
||||
},
|
||||
},
|
||||
searchIconImg: {
|
||||
width: theme.spacing(2.0),
|
||||
minWidth: theme.spacing(1.8),
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ axiosInstance.interceptors.request.use(
|
||||
|
||||
axiosInstance.interceptors.response.use(
|
||||
function (response) {
|
||||
if (response?.data && response?.data?.error !== null) {
|
||||
if (response?.data && typeof response?.data === "object" && 'error' in response.data && response?.data?.error !== null) {
|
||||
pushNotification(response?.data?.message, NOTIFICATION.ERROR);
|
||||
return Promise.reject(response);
|
||||
}
|
||||
|
||||
@@ -108,3 +108,7 @@ div::-webkit-scrollbar-thumb:hover {
|
||||
width: 4px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
div[style*="z-index: 100000"] {
|
||||
visibility: hidden;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { clinicsData, registeredClinicsData } from "../mock/clinics";
|
||||
export const getClinics = (params) => {
|
||||
let searchParams = new URLSearchParams();
|
||||
searchParams.append("limit", params?.pagination?.pageSize ?? 10);
|
||||
searchParams.append("page", params?.pagination.pageIndex+1 ?? 1);
|
||||
searchParams.append("page", params?.pagination.pageIndex + 1 ?? 1);
|
||||
searchParams.append("filter_type", params?.type ?? CLINIC_TYPE.REGISTERED);
|
||||
searchParams.append("search", params?.globalFilter ?? "");
|
||||
|
||||
@@ -39,7 +39,7 @@ export const getClinicsById = (id) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const generatePaymentSession = () =>{
|
||||
export const generatePaymentSession = () => {
|
||||
const url = `/stripe/create-payment-session`;
|
||||
return new Promise((resolve, reject) => {
|
||||
axiosInstance
|
||||
@@ -47,7 +47,7 @@ export const generatePaymentSession = () =>{
|
||||
.then((response) => resolve(response))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const updateClinicStatus = (data) => {
|
||||
const url = `/admin/clinic/status`;
|
||||
@@ -102,7 +102,7 @@ export const createClinicOffer = (data) => {
|
||||
export const getClinicOffer = (params) => {
|
||||
let searchParams = new URLSearchParams();
|
||||
searchParams.append("limit", params?.pagination?.pageSize ?? 10);
|
||||
searchParams.append("page", params?.pagination.pageIndex+1 ?? 1);
|
||||
searchParams.append("page", params?.pagination.pageIndex + 1 ?? 1);
|
||||
searchParams.append("search", params?.globalFilter ?? "");
|
||||
|
||||
const url = `/admin/clinic/offers?${searchParams.toString()}`;
|
||||
@@ -134,11 +134,10 @@ export const deleteClinicOffer = (id) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const getClinicDoctors = (params) => {
|
||||
let searchParams = new URLSearchParams();
|
||||
searchParams.append("limit", params?.pagination?.pageSize ?? 10);
|
||||
searchParams.append("page", params?.pagination.pageIndex+1 ?? 1);
|
||||
searchParams.append("page", params?.pagination.pageIndex + 1 ?? 1);
|
||||
searchParams.append("search", params?.globalFilter ?? "");
|
||||
|
||||
const url = `/clinic-doctors?${searchParams.toString()}`;
|
||||
@@ -148,7 +147,7 @@ export const getClinicDoctors = (params) => {
|
||||
.then((response) => resolve(response))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const createDoctor = (data) => {
|
||||
const url = `/clinic-doctors/`;
|
||||
@@ -179,3 +178,57 @@ export const deleteDoctor = (id) => {
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
};
|
||||
|
||||
export const getCallTranscripts = (params) => {
|
||||
console.log(params);
|
||||
let searchParams = new URLSearchParams();
|
||||
searchParams.append("limit", params?.pagination?.pageSize ?? 10);
|
||||
searchParams.append("page", params?.pagination.pageIndex + 1 ?? 1);
|
||||
searchParams.append("search", params?.globalFilter ?? "");
|
||||
if(params?.startDate){
|
||||
searchParams.append("startDate", params.startDate.toISOString());
|
||||
}
|
||||
if(params?.endDate){
|
||||
searchParams.append("endDate", params.endDate.toISOString());
|
||||
}
|
||||
|
||||
if (params?.sortingString) {
|
||||
const sortingParams = new URLSearchParams(params.sortingString);
|
||||
sortingParams.forEach((value, key) => {
|
||||
searchParams.append(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
const url = `/call-transcripts?${searchParams.toString()}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
axiosInstance
|
||||
.get(url)
|
||||
.then((response) => resolve(response))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
};
|
||||
|
||||
export const downloadCallTranscript = (id) => {
|
||||
const url = `/call-transcripts/${id}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
axiosInstance
|
||||
.get(url)
|
||||
.then((response) => resolve(response))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
};
|
||||
|
||||
export const downloadBulkCallTranscripts = (data) => {
|
||||
const url = `/call-transcripts/bulk-download`;
|
||||
return new Promise((resolve, reject) => {
|
||||
axiosInstance
|
||||
.post(url, data, {
|
||||
responseType: "blob",
|
||||
transitional: {
|
||||
forcedJSONParsing: false,
|
||||
},
|
||||
})
|
||||
.then((response) => resolve(response))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -242,7 +242,7 @@ const FileEvaluate = ({
|
||||
<Box className={classes.imageBox}>
|
||||
<img
|
||||
className={classes.image}
|
||||
src={file.file}
|
||||
src={file.fileURL}
|
||||
alt="Uploaded File"
|
||||
/>
|
||||
</Box>
|
||||
@@ -312,7 +312,7 @@ const FileEvaluate = ({
|
||||
<Box className={classes.imageBox}>
|
||||
<img
|
||||
className={classes.image}
|
||||
src={file.fileURL}
|
||||
src={file.file}
|
||||
alt="Uploaded File"
|
||||
/>
|
||||
<Box className={classes?.onImageButton}>
|
||||
@@ -360,7 +360,7 @@ const FileEvaluate = ({
|
||||
<Box className={classes.logoPreviewImageBox}>
|
||||
<img
|
||||
className={classes.image}
|
||||
src={file.fileURL}
|
||||
src={file.file}
|
||||
alt={file.documentType}
|
||||
/>
|
||||
<Box className={classes?.onImageButton}>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import { pxToRem } from '../../theme/typography';
|
||||
|
||||
export const useStyles = makeStyles((theme) => ({
|
||||
chipClass: {
|
||||
height: 'fit-content',
|
||||
minHeight: '30px',
|
||||
padding: '2px',
|
||||
alignItems: 'center',
|
||||
},
|
||||
statusColor: {
|
||||
color: theme.palette.primary.main,
|
||||
fontSize: pxToRem(10),
|
||||
},
|
||||
tabsBox: {
|
||||
display: 'flex',
|
||||
justifyContent: ' space-around',
|
||||
// width: '55%',
|
||||
marginTop: theme.spacing(0.5),
|
||||
marginRight: theme.spacing(5.0),
|
||||
alignItems: 'center',
|
||||
},
|
||||
secondaryButton: {
|
||||
width: '200px',
|
||||
height: '46px',
|
||||
borderRadius: '8px',
|
||||
justifyContent: 'space-evenly',
|
||||
fontSize: pxToRem(16),
|
||||
},
|
||||
tableActionIcons: {
|
||||
marginRight: theme.spacing(1.4),
|
||||
width: '15px',
|
||||
},
|
||||
companyNameTableColumn: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
},
|
||||
companyName: {
|
||||
marginLeft: theme.spacing(1),
|
||||
fontSize: pxToRem(14),
|
||||
objectFit: 'contain',
|
||||
width: '260px',
|
||||
},
|
||||
companyNameLink: {
|
||||
textDecoration: 'none',
|
||||
color: theme.palette.grey[10],
|
||||
'&:hover': {
|
||||
color: theme.palette.info.main,
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
},
|
||||
companyWebsiteLabel: {
|
||||
fontSize: pxToRem(12),
|
||||
},
|
||||
companyNameLogo: {
|
||||
height: '40px',
|
||||
width: '40px',
|
||||
borderRadius: theme.shape.borderRadiusComponent,
|
||||
objectFit: 'contain',
|
||||
},
|
||||
sendEmailStatus: {
|
||||
fontSize: pxToRem(14),
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
sendEmailLastSentMailDate: {
|
||||
fontSize: pxToRem(12),
|
||||
},
|
||||
addDiscountCodeLink: {
|
||||
fontSize: pxToRem(12),
|
||||
color: theme.palette.primary.main,
|
||||
},
|
||||
addDiscountCodeLabel: {
|
||||
fontSize: pxToRem(14),
|
||||
backgroundColor: theme.palette.common.white,
|
||||
color: theme.palette.common.black,
|
||||
},
|
||||
customModel: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '100%',
|
||||
},
|
||||
customModelBox: {
|
||||
paddingLeft: theme.spacing(5),
|
||||
paddingRight: theme.spacing(5),
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
// height: '100%',
|
||||
border: 'none',
|
||||
},
|
||||
newPlanTitleText: {
|
||||
fontSize: pxToRem(28),
|
||||
fontFamily: theme.fontFamily.bold,
|
||||
},
|
||||
newPlanSubTitleText: {
|
||||
fontSize: pxToRem(18),
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
addPlanSuccessIcon: {
|
||||
padding: theme.spacing(2),
|
||||
paddingTop: theme.spacing(1),
|
||||
},
|
||||
planAddedText: {
|
||||
fontSize: pxToRem(12),
|
||||
color: theme.palette.grey[54],
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
alignSelf: 'center',
|
||||
},
|
||||
verifyIcon: {
|
||||
marginLeft: theme.spacing(0.3),
|
||||
fontSize: pxToRem(12),
|
||||
color: theme.palette.grey[54],
|
||||
},
|
||||
}));
|
||||
@@ -1,400 +1,215 @@
|
||||
import DownloadIcon from '@mui/icons-material/Download';
|
||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||
import DownloadIcon from "@mui/icons-material/Download";
|
||||
import { Box, IconButton, LinearProgress, TextField } from "@mui/material";
|
||||
import { format } from "date-fns";
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns";
|
||||
import Table from "../../components/Table";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
IconButton,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TablePagination,
|
||||
TableRow,
|
||||
TableSortLabel,
|
||||
Typography,
|
||||
TextField,
|
||||
} from '@mui/material';
|
||||
import { DatePicker, LocalizationProvider } from '@mui/x-date-pickers';
|
||||
import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
|
||||
import { format } from 'date-fns';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
downloadBulkCallTranscripts,
|
||||
getCallTranscripts,
|
||||
} from "../../services/clinics.service";
|
||||
import { useStyles } from "./clinicTranscriptsStyles";
|
||||
import PageHeader from "../../components/PageHeader";
|
||||
import CustomBreadcrumbs from "../../components/CustomBreadcrumbs";
|
||||
import { DesktopDatePicker, LocalizationProvider } from "@mui/x-date-pickers";
|
||||
|
||||
// Sample data - replace with your actual data source
|
||||
const sampleCalls = [
|
||||
{
|
||||
id: 1,
|
||||
date: '2025-04-20',
|
||||
time: '09:30',
|
||||
callerNumber: '555-123-4567',
|
||||
patientName: 'John Smith',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
date: '2025-04-21',
|
||||
time: '10:15',
|
||||
callerNumber: '555-234-5678',
|
||||
patientName: 'Jane Doe',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
date: '2025-04-19',
|
||||
time: '14:45',
|
||||
callerNumber: '555-345-6789',
|
||||
patientName: 'Robert Johnson',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
date: '2025-04-22',
|
||||
time: '11:00',
|
||||
callerNumber: '555-456-7890',
|
||||
patientName: 'Emily Davis',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
date: '2025-04-20',
|
||||
time: '16:30',
|
||||
callerNumber: '555-567-8901',
|
||||
patientName: 'Michael Brown',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
date: '2025-04-19',
|
||||
time: '14:45',
|
||||
callerNumber: '555-345-6789',
|
||||
patientName: 'Robert Johnson',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
date: '2025-04-22',
|
||||
time: '11:00',
|
||||
callerNumber: '555-456-7890',
|
||||
patientName: 'Emily Davis',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
date: '2025-04-20',
|
||||
time: '16:30',
|
||||
callerNumber: '555-567-8901',
|
||||
patientName: 'Michael Brown',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
date: '2025-04-19',
|
||||
time: '14:45',
|
||||
callerNumber: '555-345-6789',
|
||||
patientName: 'Robert Johnson',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
date: '2025-04-22',
|
||||
time: '11:00',
|
||||
callerNumber: '555-456-7890',
|
||||
patientName: 'Emily Davis',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
date: '2025-04-20',
|
||||
time: '16:30',
|
||||
callerNumber: '555-567-8901',
|
||||
patientName: 'Michael Brown',
|
||||
callDuration: Math.floor(Math.random() * 60),
|
||||
},
|
||||
];
|
||||
|
||||
const CallListTable = () => {
|
||||
const [calls, setCalls] = useState([]);
|
||||
const [orderBy, setOrderBy] = useState('date');
|
||||
const [order, setOrder] = useState('desc');
|
||||
const [showFilters, setShowFilters] = useState(false);
|
||||
const [startDate, setStartDate] = useState(null);
|
||||
const [endDate, setEndDate] = useState(null);
|
||||
const classes = useStyles();
|
||||
const ref = useRef(null);
|
||||
|
||||
// Pagination state
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(5);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// Initialize with sample data
|
||||
useEffect(() => {
|
||||
setCalls(sampleCalls);
|
||||
}, []);
|
||||
const [data, setData] = useState([]);
|
||||
|
||||
// Handle sort request
|
||||
const handleRequestSort = (property) => {
|
||||
const isAsc = orderBy === property && order === 'asc';
|
||||
setOrder(isAsc ? 'desc' : 'asc');
|
||||
setOrderBy(property);
|
||||
};
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
const date = new Date(dateString);
|
||||
const day = date.getDate().toString().padStart(2, '0');
|
||||
const month = date.toLocaleString('en-US', { month: 'short' });
|
||||
const year = date.getFullYear();
|
||||
return `${day}-${month}-${year}`;
|
||||
};
|
||||
|
||||
// Handle download transcript
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const handleDownloadTranscript = (id) => {
|
||||
// TODO: Implement actual download functionality here
|
||||
};
|
||||
|
||||
// Apply filters
|
||||
const applyFilters = () => {
|
||||
let filteredCalls = [...sampleCalls];
|
||||
|
||||
if (startDate) {
|
||||
const startDateStr = format(startDate, 'yyyy-MM-dd');
|
||||
filteredCalls = filteredCalls.filter((call) => call.date >= startDateStr);
|
||||
// get data
|
||||
const getTranscrips = async (filters) => {
|
||||
try {
|
||||
const params = { ...filters };
|
||||
const resp = await getCallTranscripts(params);
|
||||
setData(resp?.data?.data?.data);
|
||||
return {
|
||||
data: resp?.data?.data?.data,
|
||||
rowCount: resp?.data?.data?.total || 0,
|
||||
};
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
data: [],
|
||||
rowCount: 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
if (endDate) {
|
||||
const endDateStr = format(endDate, 'yyyy-MM-dd');
|
||||
filteredCalls = filteredCalls.filter((call) => call.date <= endDateStr);
|
||||
const handleBulkDownload = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const keys = data.map((item) => item.id);
|
||||
const downloadResponse = await downloadBulkCallTranscripts(keys);
|
||||
// const blob = new Blob([downloadResponse?.data], { type: "application/zip" });
|
||||
const url = window.URL.createObjectURL(downloadResponse?.data);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = "transcripts.zip";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
window.URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
setCalls(filteredCalls);
|
||||
setPage(0); // Reset to first page after filtering
|
||||
};
|
||||
|
||||
// Reset filters
|
||||
const resetFilters = () => {
|
||||
setStartDate(null);
|
||||
setEndDate(null);
|
||||
setCalls(sampleCalls);
|
||||
setPage(0); // Reset to first page after clearing filters
|
||||
};
|
||||
const columns = useMemo(() => [
|
||||
{
|
||||
size: 100,
|
||||
header: "Sr. No.",
|
||||
Cell: (props) => {
|
||||
const tableState = props?.table?.getState();
|
||||
const serialNumber = (
|
||||
props?.row?.index +
|
||||
1 +
|
||||
tableState?.pagination?.pageIndex * tableState?.pagination?.pageSize
|
||||
)
|
||||
?.toString()
|
||||
?.padStart(1, "0");
|
||||
return <span>{serialNumber}</span>;
|
||||
},
|
||||
enableSorting: false,
|
||||
},
|
||||
{
|
||||
id: "call_duration",
|
||||
size: 200,
|
||||
accessorKey: "call_duration",
|
||||
header: "Call Duration",
|
||||
Cell: (props) => {
|
||||
const totalSeconds = parseInt(props.row.original.call_duration);
|
||||
const minutes = Math.floor(totalSeconds / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
return (
|
||||
<span>
|
||||
{minutes > 0 ? `${minutes} min ` : ""}
|
||||
{seconds > 0 || minutes === 0 ? `${seconds} sec` : ""}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
enableSorting: false,
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
id: "call_received_time",
|
||||
size: 200,
|
||||
accessorKey: "call_received_time",
|
||||
header: "Call Received Time",
|
||||
Cell: (props) => {
|
||||
const date = new Date(props.row.original.call_received_time);
|
||||
const formattedDate = format(date, "MMM d, yyyy h:mm a");
|
||||
return <span>{formattedDate}</span>;
|
||||
},
|
||||
enableSorting: false,
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
id: "patient_number",
|
||||
size: 200,
|
||||
accessorKey: "patient_number",
|
||||
header: "Patient Number",
|
||||
Cell: (props) => {
|
||||
return <span>{props.row.original.patient_number}</span>;
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
size: 20,
|
||||
accessorKey: "download",
|
||||
header: "",
|
||||
enableColumnFilter: false,
|
||||
enableSorting: false,
|
||||
Cell: (props) => {
|
||||
const { row } = props;
|
||||
const handleDownload = async () => {
|
||||
const a = document.createElement("a");
|
||||
a.href = row.original.transcript_key_id;
|
||||
a.download = "transcript.txt";
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
};
|
||||
return (
|
||||
<IconButton onClick={handleDownload}>
|
||||
<DownloadIcon />
|
||||
</IconButton>
|
||||
);
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
// {
|
||||
// id: "patient_name",
|
||||
// size: 150,
|
||||
// accessorKey: "patient_name",
|
||||
// header: "Patient Name",
|
||||
// Cell: (props) => {
|
||||
// return <span>{props.row.original.patient_name}</span>;
|
||||
// },
|
||||
// enableColumnFilter: false,
|
||||
// },
|
||||
]);
|
||||
|
||||
// Handle page change
|
||||
const handleChangePage = (event, newPage) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
const breadcrumbs = [
|
||||
{
|
||||
label: "Dashboard",
|
||||
path: "/",
|
||||
},
|
||||
{
|
||||
label: "Call Transcripts",
|
||||
path: "/transcripts",
|
||||
},
|
||||
];
|
||||
|
||||
// Handle rows per page change
|
||||
const handleChangeRowsPerPage = (event) => {
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
setPage(0);
|
||||
};
|
||||
|
||||
// Sort function
|
||||
const sortedCalls = calls.sort((a, b) => {
|
||||
if (orderBy === 'date') {
|
||||
const dateComparison = a.date.localeCompare(b.date);
|
||||
if (dateComparison !== 0)
|
||||
return order === 'asc' ? dateComparison : -dateComparison;
|
||||
|
||||
// If dates are equal, sort by time
|
||||
return order === 'asc'
|
||||
? a.time.localeCompare(b.time)
|
||||
: b.time.localeCompare(a.time);
|
||||
}
|
||||
|
||||
if (orderBy === 'time') {
|
||||
return order === 'asc'
|
||||
? a.time.localeCompare(b.time)
|
||||
: b.time.localeCompare(a.time);
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
// Get current page data
|
||||
const currentPageCalls = sortedCalls.slice(
|
||||
page * rowsPerPage,
|
||||
page * rowsPerPage + rowsPerPage
|
||||
);
|
||||
|
||||
// Custom render function for DatePicker to avoid renderInput prop issues
|
||||
const CustomDateField = ({ value, onChange, label }) => (
|
||||
<TextField
|
||||
label={label}
|
||||
value={value ? format(value, 'yyyy-MM-dd') : ''}
|
||||
onChange={(e) => {
|
||||
try {
|
||||
const dateStr = e.target.value;
|
||||
const date = new Date(dateStr);
|
||||
if (!isNaN(date.getTime())) {
|
||||
onChange(date);
|
||||
}
|
||||
} catch (error) {
|
||||
// Invalid date
|
||||
}
|
||||
}}
|
||||
placeholder="YYYY-MM-DD"
|
||||
size="small"
|
||||
sx={{ minWidth: 200 }}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Paper sx={{ width: '100%', mb: 2 }}>
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" component="div">
|
||||
Call Transcripts
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', gap: 2 }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<DownloadIcon />}
|
||||
onClick={() => {
|
||||
/* TODO: Implement bulk download */
|
||||
}}
|
||||
sx={{ bgcolor: '#f5365c', '&:hover': { bgcolor: '#d40639' } }}
|
||||
>
|
||||
Bulk Download
|
||||
</Button>
|
||||
<Button
|
||||
startIcon={<FilterListIcon />}
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
color={showFilters ? 'secondary' : 'primary'}
|
||||
sx={{ color: showFilters ? '#f5365c' : undefined }}
|
||||
>
|
||||
{showFilters ? 'Hide Filters' : 'Show Filters'}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{showFilters && (
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
display: 'flex',
|
||||
gap: 2,
|
||||
flexWrap: 'wrap',
|
||||
alignItems: 'center',
|
||||
<Box>
|
||||
<PageHeader
|
||||
pageTitle="Call Transcripts"
|
||||
addButtonIcon={<DownloadIcon />}
|
||||
addButtonTitle="Bulk Download"
|
||||
onAddButtonClick={handleBulkDownload}
|
||||
/>
|
||||
<CustomBreadcrumbs breadcrumbs={breadcrumbs} />
|
||||
<Box className={classes.tableMainDiv}>
|
||||
<Box className={classes.table}>
|
||||
{loading && <LinearProgress />}
|
||||
<Table
|
||||
tableRef={ref}
|
||||
options={{
|
||||
enableRowSelection: false,
|
||||
showTopBar: true,
|
||||
showFilters: true,
|
||||
}}
|
||||
>
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<Box>
|
||||
<CustomDateField
|
||||
label="Start Date (YYYY-MM-DD)"
|
||||
value={startDate}
|
||||
onChange={setStartDate}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<CustomDateField
|
||||
label="End Date (YYYY-MM-DD)"
|
||||
value={endDate}
|
||||
onChange={setEndDate}
|
||||
/>
|
||||
</Box>
|
||||
</LocalizationProvider>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={applyFilters}
|
||||
sx={{ bgcolor: '#f5365c', '&:hover': { bgcolor: '#d40639' } }}
|
||||
>
|
||||
Apply Filters
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
onClick={resetFilters}
|
||||
sx={{
|
||||
color: '#f5365c',
|
||||
borderColor: '#f5365c',
|
||||
'&:hover': { borderColor: '#d40639' },
|
||||
}}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<TableContainer>
|
||||
<Table sx={{ minWidth: 750 }} aria-labelledby="callListTable">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<TableSortLabel
|
||||
active={orderBy === 'date'}
|
||||
direction={orderBy === 'date' ? order : 'asc'}
|
||||
onClick={() => handleRequestSort('date')}
|
||||
>
|
||||
Date
|
||||
</TableSortLabel>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<TableSortLabel
|
||||
active={orderBy === 'time'}
|
||||
direction={orderBy === 'time' ? order : 'asc'}
|
||||
onClick={() => handleRequestSort('time')}
|
||||
>
|
||||
Received Time
|
||||
</TableSortLabel>
|
||||
</TableCell>
|
||||
<TableCell>Call Duration</TableCell>
|
||||
<TableCell>Caller Number</TableCell>
|
||||
<TableCell>Patient Name</TableCell>
|
||||
<TableCell align="center">Action</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{currentPageCalls.map((call) => (
|
||||
<TableRow key={call.id}>
|
||||
<TableCell component="th" scope="row">
|
||||
{formatDate(call.date)}
|
||||
</TableCell>
|
||||
<TableCell>{call.time}</TableCell>
|
||||
<TableCell>{call.callDuration} Sec</TableCell>
|
||||
<TableCell>{call.callerNumber}</TableCell>
|
||||
<TableCell>{call.patientName}</TableCell>
|
||||
<TableCell align="center">
|
||||
<IconButton
|
||||
color="primary"
|
||||
onClick={() => handleDownloadTranscript(call.id)}
|
||||
aria-label="download transcript"
|
||||
sx={{ color: '#f5365c' }}
|
||||
>
|
||||
<DownloadIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{currentPageCalls.length === 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={5} align="center">
|
||||
No calls found
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
component="div"
|
||||
count={sortedCalls.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
/>
|
||||
</Paper>
|
||||
topToolbarProps={{
|
||||
|
||||
}}
|
||||
renderTopToolbar={() => {
|
||||
return (
|
||||
<Box>
|
||||
<TextField
|
||||
variant="outlined"
|
||||
value={"123"}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}}
|
||||
showDateFilters
|
||||
columns={columns}
|
||||
getData={getTranscrips}
|
||||
searchText="Transcripts"
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ const SuperAdminTotals = ({ isLoading, data }) => {
|
||||
// navigate(path, { state: { tab } });
|
||||
};
|
||||
|
||||
const { totalAccounts, registrationRequest, rejected, registered } = data;
|
||||
const { totalAccounts, registrationRequest, rejected, registered, paymentDue } = data;
|
||||
|
||||
return (
|
||||
<ProtectedComponent>
|
||||
@@ -51,7 +51,7 @@ const SuperAdminTotals = ({ isLoading, data }) => {
|
||||
heading={`Payment Due`}
|
||||
isLoading={isLoading}
|
||||
viewAllClick={() => viewAllClick(false)}
|
||||
value={rejected}
|
||||
value={paymentDue}
|
||||
color={theme.palette.grey[57]}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
@@ -77,10 +77,10 @@ function Dashboard() {
|
||||
}
|
||||
|
||||
const newWindow = window.open(session?.data?.data, "_blank", "noopener,noreferrer");
|
||||
if (!newWindow || newWindow.closed || typeof newWindow.closed === 'undefined') {
|
||||
// Fallback in case popup is blocked
|
||||
window.location.href = session?.data?.data;
|
||||
}
|
||||
// if (!newWindow || newWindow.closed || typeof newWindow.closed === 'undefined') {
|
||||
// // Fallback in case popup is blocked
|
||||
// window.location.href = session?.data?.data;
|
||||
// }
|
||||
}
|
||||
|
||||
// if(status=="payment_due"){
|
||||
|
||||
@@ -395,10 +395,10 @@ function YourDetailsForm() {
|
||||
pushNotification(response?.data?.message, NOTIFICATION.SUCCESS);
|
||||
// open new page for stripe payment from url
|
||||
const newWindow = window.open(response?.data?.data, "_blank", "noopener,noreferrer");
|
||||
if (!newWindow || newWindow.closed || typeof newWindow.closed === 'undefined') {
|
||||
// Fallback in case popup is blocked
|
||||
window.location.href = response?.data?.data;
|
||||
}
|
||||
// if (!newWindow || newWindow.closed || typeof newWindow.closed === 'undefined') {
|
||||
// // Fallback in case popup is blocked
|
||||
// window.location.href = response?.data?.data;
|
||||
// }
|
||||
dispatch(resetFormData());
|
||||
navigate("/");
|
||||
} catch (error) {
|
||||
|
||||
@@ -234,7 +234,7 @@ function Users() {
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
label={label}
|
||||
label={label.charAt(0).toUpperCase() + label.slice(1)}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
sx={{
|
||||
@@ -295,7 +295,7 @@ function Users() {
|
||||
return (
|
||||
<Box>
|
||||
<PageHeader
|
||||
pageTitle="Doctors/Nurses List"
|
||||
pageTitle="Doctors/Nurses Management"
|
||||
addButtonTitle="Add Doctor/Nurse"
|
||||
onAddButtonClick={handleDialog}
|
||||
addButtonIcon={<AddIcon />}
|
||||
|
||||
Reference in New Issue
Block a user