feat: clinic offers
This commit is contained in:
parent
f949253b77
commit
a17366037b
|
|
@ -24,7 +24,7 @@ function Header() {
|
||||||
{user?.userType == USER_ROLES.SUPER_ADMIN.toLowerCase() ? null : `Welcome to ${user?.created_clinics?.[0]?.name}`}
|
{user?.userType == USER_ROLES.SUPER_ADMIN.toLowerCase() ? null : `Welcome to ${user?.created_clinics?.[0]?.name}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box className={classes.profilDiv}>
|
<Box className={classes.profilDiv}>
|
||||||
<NotificationSection />
|
{/* <NotificationSection /> */}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
zIndex:
|
zIndex:
|
||||||
|
|
|
||||||
|
|
@ -84,3 +84,49 @@ export const getClinicsDashboardStatsById = () => {
|
||||||
.catch((err) => reject(err));
|
.catch((err) => reject(err));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const createClinicOffer = (data) => {
|
||||||
|
const url = `/admin/clinic/offer`;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axiosInstance
|
||||||
|
.post(url, data)
|
||||||
|
.then((response) => resolve(response))
|
||||||
|
.catch((err) => reject(err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getClinicOffer = (params) => {
|
||||||
|
|
||||||
|
let searchParams = new URLSearchParams();
|
||||||
|
searchParams.append("size", params?.pagination?.pageSize ?? 10);
|
||||||
|
searchParams.append("page", params?.pagination.pageIndex ?? 0);
|
||||||
|
searchParams.append("search", params?.globalFilter ?? "");
|
||||||
|
|
||||||
|
const url = `/admin/clinic/offers?${searchParams.toString()}`;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axiosInstance
|
||||||
|
.get(url)
|
||||||
|
.then((response) => resolve(response))
|
||||||
|
.catch((err) => reject(err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateClinicOffer = (data) => {
|
||||||
|
const url = `/admin/clinic/offer`;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axiosInstance
|
||||||
|
.put(url, data)
|
||||||
|
.then((response) => resolve(response))
|
||||||
|
.catch((err) => reject(err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteClinicOffer = (id) => {
|
||||||
|
const url = `/admin/clinic/offer/${id}`;
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axiosInstance
|
||||||
|
.delete(url)
|
||||||
|
.then((response) => resolve(response))
|
||||||
|
.catch((err) => reject(err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { axiosInstance } from "../config/api";
|
||||||
|
|
||||||
|
export const getMasterData = (params) => {
|
||||||
|
let searchParams = new URLSearchParams();
|
||||||
|
searchParams.append("search", params?.globalFilter ?? "");
|
||||||
|
|
||||||
|
const url = `/admin/master-data?${searchParams.toString()}`;
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axiosInstance
|
||||||
|
.get(url)
|
||||||
|
.then((response) => resolve(response))
|
||||||
|
.catch((err) => reject(err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const setMasterData = (data) => {
|
||||||
|
const url = "/admin/master-data";
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axiosInstance
|
||||||
|
.post(url, data)
|
||||||
|
.then((response) => resolve(response))
|
||||||
|
.catch((err) => reject(err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateMasterData = (data) => {
|
||||||
|
const url = "/admin/master-data";
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axiosInstance
|
||||||
|
.put(url, data)
|
||||||
|
.then((response) => resolve(response))
|
||||||
|
.catch((err) => reject(err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -9,3 +9,5 @@ export const passwordNumberRegex = /\d/;
|
||||||
|
|
||||||
// passwordSpacesRegex.js
|
// passwordSpacesRegex.js
|
||||||
export const passwordSpacesRegex = /^\S.*\S$/;
|
export const passwordSpacesRegex = /^\S.*\S$/;
|
||||||
|
|
||||||
|
export const emailRegex = /^\S+@\S+\.\S+$/;
|
||||||
|
|
@ -249,7 +249,7 @@ const ClinicsList = () => {
|
||||||
[
|
[
|
||||||
// ..............sro number column......................
|
// ..............sro number column......................
|
||||||
{
|
{
|
||||||
size: 60,
|
size: 100,
|
||||||
header: "Sr. No.",
|
header: "Sr. No.",
|
||||||
Cell: (props) => {
|
Cell: (props) => {
|
||||||
const tableState = props?.table?.getState();
|
const tableState = props?.table?.getState();
|
||||||
|
|
@ -330,68 +330,6 @@ const ClinicsList = () => {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// ........................... sent email column.............................
|
|
||||||
// {
|
|
||||||
// enableSorting: true,
|
|
||||||
// accessorKey: 'hasActiveCustomPlan',
|
|
||||||
// minSize: 250,
|
|
||||||
// header: 'Sent Mail',
|
|
||||||
// Cell: ({ row }) => (
|
|
||||||
// <>
|
|
||||||
// {row?.original?.plans?.[0]?.status ===
|
|
||||||
// PLAN_STATUS_TYPE.ACTIVATED && (
|
|
||||||
// <div>
|
|
||||||
// <Link
|
|
||||||
// onClick={() => handleRegisterEmailSent(row)}
|
|
||||||
// className={classes.sendEmailStatus}
|
|
||||||
// >
|
|
||||||
// <u>Resend Subscription Mail</u>
|
|
||||||
// </Link>
|
|
||||||
// <div className={classes.sendEmailLastSentMailDate}>
|
|
||||||
// Last Sent On:{' '}
|
|
||||||
// {format(
|
|
||||||
// new Date(row?.original?.lastCustomPlanEmailSent),
|
|
||||||
// 'dd MMM yyyy'
|
|
||||||
// )}
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// )}
|
|
||||||
// </>
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
|
|
||||||
// ................add plan column..............................
|
|
||||||
// {
|
|
||||||
// enableSorting: true,
|
|
||||||
// isBold: true,
|
|
||||||
// size: 120,
|
|
||||||
// accessorKey: 'hasActiveCustomPlan',
|
|
||||||
// header: 'Custom Plan',
|
|
||||||
// Cell: ({ row }) => (
|
|
||||||
// <>
|
|
||||||
// {row?.original?.plans?.[0]?.status ===
|
|
||||||
// PLAN_STATUS_TYPE.ACTIVATED ? (
|
|
||||||
// <Box>
|
|
||||||
// <div className={classes.planAddedText}>
|
|
||||||
// Plan Added <VerifiedIcon className={classes.verifyIcon} />
|
|
||||||
// </div>
|
|
||||||
// </Box>
|
|
||||||
// ) : (
|
|
||||||
// <Tooltip>
|
|
||||||
// <Link
|
|
||||||
// to={{
|
|
||||||
// pathname: `/plans/${row.original.id}/custom-create`,
|
|
||||||
// search: `?tab=${type}`,
|
|
||||||
// }}
|
|
||||||
// className={classes.addDiscountCodeLink}
|
|
||||||
// >
|
|
||||||
// <u>ADD PLAN</u>
|
|
||||||
// </Link>
|
|
||||||
// </Tooltip>
|
|
||||||
// )}
|
|
||||||
// </>
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
].filter(Boolean),
|
].filter(Boolean),
|
||||||
[type]
|
[type]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,61 +1,38 @@
|
||||||
import AddIcon from "@mui/icons-material/Add";
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
|
import DescriptionIcon from '@mui/icons-material/Description';
|
||||||
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
|
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import PersonAddIcon from "@mui/icons-material/PersonAdd";
|
import PersonAddIcon from "@mui/icons-material/PersonAdd";
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
|
||||||
import {
|
import {
|
||||||
Alert,
|
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Container,
|
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
Divider,
|
Divider,
|
||||||
IconButton,
|
IconButton,
|
||||||
InputAdornment,
|
|
||||||
Paper,
|
|
||||||
Snackbar,
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import React, { useState } from "react";
|
import React, { useMemo, useRef, useState } from "react";
|
||||||
import CustomBreadcrumbs from "../../components/CustomBreadcrumbs";
|
import CustomBreadcrumbs from "../../components/CustomBreadcrumbs";
|
||||||
import PageHeader from "../../components/PageHeader";
|
import PageHeader from "../../components/PageHeader";
|
||||||
|
import { pushNotification } from "../../utils/notification";
|
||||||
|
import { NOTIFICATION } from "../../constants";
|
||||||
|
import { useStyles } from "./masterDataStyles";
|
||||||
|
import Table from "../../components/Table";
|
||||||
|
import { getMasterData, setMasterData } from "../../services/masterData.services";
|
||||||
|
|
||||||
const MasterDataManagement = () => {
|
const MasterDataManagement = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
const ref = useRef(null);
|
||||||
// State for form fields
|
// State for form fields
|
||||||
const [appointmentType, setAppointmentType] = useState("");
|
const [type, setType] = useState("");
|
||||||
const queryParams = new URLSearchParams(location.search);
|
|
||||||
|
|
||||||
// State for staff list
|
// State for dialog
|
||||||
const [staffList, setStaffList] = useState([]);
|
|
||||||
|
|
||||||
// State for staff dialog
|
|
||||||
const [openDialog, setOpenDialog] = useState(false);
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
|
||||||
// State for search
|
// Handle dialog open/close
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
|
||||||
|
|
||||||
// State for pagination
|
|
||||||
const [page, setPage] = useState(1);
|
|
||||||
const rowsPerPage = 10;
|
|
||||||
|
|
||||||
// State for notification
|
|
||||||
const [notification, setNotification] = useState({
|
|
||||||
open: false,
|
|
||||||
message: "",
|
|
||||||
severity: "success",
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle staff dialog open/close
|
|
||||||
const handleOpenDialog = () => {
|
const handleOpenDialog = () => {
|
||||||
setOpenDialog(true);
|
setOpenDialog(true);
|
||||||
};
|
};
|
||||||
|
|
@ -63,38 +40,50 @@ const MasterDataManagement = () => {
|
||||||
const handleCloseDialog = () => {
|
const handleCloseDialog = () => {
|
||||||
setOpenDialog(false);
|
setOpenDialog(false);
|
||||||
// Clear form
|
// Clear form
|
||||||
setAppointmentType("");
|
setType("");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle form submission
|
// Handle form submission
|
||||||
const handleSubmit = (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// Add new staff member
|
const payload = {
|
||||||
const newStaff = {
|
type,
|
||||||
id: staffList.length + 1,
|
|
||||||
appointmentType,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
setStaffList([...staffList, newStaff]);
|
const response = await setMasterData(payload);
|
||||||
|
|
||||||
|
if (response?.data?.data) {
|
||||||
|
pushNotification("Master Data created successfully!", NOTIFICATION.SUCCESS);
|
||||||
|
} else {
|
||||||
|
pushNotification(response?.data?.message, NOTIFICATION.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
// Close dialog
|
// Close dialog
|
||||||
handleCloseDialog();
|
handleCloseDialog();
|
||||||
|
|
||||||
// Show success notification
|
|
||||||
setNotification({
|
|
||||||
open: true,
|
|
||||||
message: "Staff member added successfully!",
|
|
||||||
severity: "success",
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle notification close
|
const getData = async (filters) => {
|
||||||
const handleCloseNotification = () => {
|
try {
|
||||||
setNotification({
|
// Remove the type parameter since it's not defined
|
||||||
...notification,
|
let params = {
|
||||||
open: false,
|
...filters
|
||||||
});
|
};
|
||||||
|
|
||||||
|
const resp = await getMasterData(params);
|
||||||
|
console.log('API Response:', resp);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: resp?.data?.data?.data,
|
||||||
|
rowCount: resp?.data?.data?.total || 0,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching admins:', error);
|
||||||
|
return {
|
||||||
|
data: [],
|
||||||
|
rowCount: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ...................breadcrumbs array........................
|
// ...................breadcrumbs array........................
|
||||||
|
|
@ -109,244 +98,144 @@ const MasterDataManagement = () => {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
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(2, "0");
|
||||||
|
return <span>{serialNumber}</span>;
|
||||||
|
},
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "type", // Add an id to match the accessorKey
|
||||||
|
size: 280,
|
||||||
|
accessorKey: "type",
|
||||||
|
header: "Type",
|
||||||
|
enableColumnFilter: false,
|
||||||
|
}
|
||||||
|
// Removed the empty object that was causing the error
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
<Box>
|
||||||
<PageHeader pageTitle="Master Data Management" hideAddButton />
|
<PageHeader
|
||||||
|
pageTitle="Master Data Management"
|
||||||
|
// hideAddButton
|
||||||
|
addButtonIcon={<AddIcon />}
|
||||||
|
onAddButtonClick={handleOpenDialog}
|
||||||
|
/>
|
||||||
<CustomBreadcrumbs breadcrumbs={breadcrumbs} />
|
<CustomBreadcrumbs breadcrumbs={breadcrumbs} />
|
||||||
|
<Box className={classes.tableMainDiv}>
|
||||||
<Paper elevation={3} sx={{ p: 0, mb: 4, overflow: "hidden" }}>
|
<Box>
|
||||||
{/* Staff List Header with Add Button */}
|
<Table
|
||||||
<Box
|
hideTopToolbar
|
||||||
sx={{
|
columns={columns}
|
||||||
display: "flex",
|
getData={getData}
|
||||||
justifyContent: "space-between",
|
options={{
|
||||||
alignItems: "center",
|
enableRowSelection: true,
|
||||||
p: 3,
|
showTopBar: false,
|
||||||
}}
|
showFilters: true,
|
||||||
>
|
|
||||||
<Typography variant="h6" component="h2" sx={{ fontWeight: "bold" }}>
|
|
||||||
Master Appointment Type List
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="error"
|
|
||||||
startIcon={<AddIcon />}
|
|
||||||
onClick={handleOpenDialog}
|
|
||||||
sx={{
|
|
||||||
borderRadius: 50,
|
|
||||||
textTransform: "none",
|
|
||||||
backgroundColor: "#ff3366",
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: "#e61653",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add Appointment Type
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
{/* Search Box */}
|
|
||||||
<Box sx={{ px: 3, pb: 2 }}>
|
|
||||||
<TextField
|
|
||||||
placeholder="Search Appointment Type here"
|
|
||||||
fullWidth
|
|
||||||
size="small"
|
|
||||||
value={searchQuery}
|
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
|
||||||
InputProps={{
|
|
||||||
startAdornment: (
|
|
||||||
<InputAdornment position="start">
|
|
||||||
<SearchIcon color="action" />
|
|
||||||
</InputAdornment>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
backgroundColor: "#fff",
|
|
||||||
"& .MuiOutlinedInput-root": {
|
|
||||||
borderRadius: 2,
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
|
searchText="Master Data"
|
||||||
|
showSearchBox={true}
|
||||||
|
ref={ref}
|
||||||
|
// actions={[
|
||||||
|
// {
|
||||||
|
// onClick: (row) => {
|
||||||
|
|
||||||
|
// },
|
||||||
|
// text: "Remove",
|
||||||
|
// icon: (
|
||||||
|
// <DescriptionIcon />
|
||||||
|
// // <img
|
||||||
|
// // src={TransactionHistoryIcon}
|
||||||
|
// // className={classes.tableActionIcons}
|
||||||
|
// // alt="transaction history"
|
||||||
|
// // />
|
||||||
|
// ),
|
||||||
|
// },
|
||||||
|
// ]}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
{/* Staff List Table */}
|
<Box className={classes.tableMainDiv}>
|
||||||
<TableContainer>
|
<Dialog
|
||||||
<Table>
|
open={openDialog}
|
||||||
<TableHead>
|
onClose={handleCloseDialog}
|
||||||
<TableRow sx={{ backgroundColor: "#f5f5f5" }}>
|
maxWidth="sm"
|
||||||
<TableCell sx={{ fontWeight: "bold" }}>Sr. No.</TableCell>
|
fullWidth
|
||||||
<TableCell sx={{ fontWeight: "bold" }}>
|
>
|
||||||
Appointment Type
|
<DialogTitle
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{staffList.length > 0 ? (
|
|
||||||
staffList
|
|
||||||
.filter((staff) =>
|
|
||||||
`${staff.appointmentType}`
|
|
||||||
.toLowerCase()
|
|
||||||
.includes(searchQuery.toLowerCase())
|
|
||||||
)
|
|
||||||
.slice((page - 1) * rowsPerPage, page * rowsPerPage)
|
|
||||||
.map((staff, index) => (
|
|
||||||
<TableRow key={staff.id}>
|
|
||||||
<TableCell>
|
|
||||||
{(page - 1) * rowsPerPage + index + 1}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{staff.appointmentType}</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell
|
|
||||||
colSpan={2}
|
|
||||||
align="center"
|
|
||||||
sx={{ py: 5, color: "#666" }}
|
|
||||||
>
|
|
||||||
No Appointment Type added yet
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
|
|
||||||
{/* Pagination */}
|
|
||||||
{staffList.length > 0 && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
alignItems: "center",
|
||||||
p: 2,
|
justifyContent: "space-between",
|
||||||
borderTop: "1px solid #eee",
|
pb: 1,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||||
onClick={() => setPage((prev) => Math.max(prev - 1, 1))}
|
<PersonAddIcon sx={{ mr: 1, color: "#0a2d6b" }} />
|
||||||
disabled={page === 1}
|
<Typography
|
||||||
startIcon={<ArrowBackIosNewIcon fontSize="small" />}
|
variant="h6"
|
||||||
sx={{ mx: 1, color: "#666" }}
|
component="span"
|
||||||
>
|
sx={{ fontWeight: "bold", color: "#0a2d6b" }}
|
||||||
Previous
|
>
|
||||||
</Button>
|
Add Appointment Type
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<IconButton onClick={handleCloseDialog} size="small">
|
||||||
|
<CloseIcon />
|
||||||
|
</IconButton>
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<DialogContent>
|
||||||
|
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 1 }}>
|
||||||
|
<TextField
|
||||||
|
label="Appointment Type"
|
||||||
|
fullWidth
|
||||||
|
margin="normal"
|
||||||
|
value={type}
|
||||||
|
onChange={(e) => setType(e.target.value)}
|
||||||
|
placeholder="Appointment Type"
|
||||||
|
required
|
||||||
|
InputLabelProps={{
|
||||||
|
shrink: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
<DialogActions sx={{ px: 3, pb: 3 }}>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
disableElevation
|
color="error"
|
||||||
|
fullWidth
|
||||||
|
onClick={handleSubmit}
|
||||||
sx={{
|
sx={{
|
||||||
mx: 1,
|
py: 1.5,
|
||||||
minWidth: "36px",
|
backgroundColor: "#ff3366",
|
||||||
backgroundColor: "#f0f0f0",
|
|
||||||
color: "#333",
|
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
backgroundColor: "#e0e0e0",
|
backgroundColor: "#e61653",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{page}
|
Add Appointment Type
|
||||||
</Button>
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
<Button
|
</Dialog>
|
||||||
onClick={() => setPage((prev) => prev + 1)}
|
</Box>
|
||||||
disabled={page * rowsPerPage >= staffList.length}
|
</Box>
|
||||||
endIcon={<ArrowForwardIosIcon fontSize="small" />}
|
|
||||||
sx={{ mx: 1, color: "#666" }}
|
|
||||||
>
|
|
||||||
Next
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Paper>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Add Staff Dialog */}
|
|
||||||
<Dialog
|
|
||||||
open={openDialog}
|
|
||||||
onClose={handleCloseDialog}
|
|
||||||
maxWidth="sm"
|
|
||||||
fullWidth
|
|
||||||
>
|
|
||||||
<DialogTitle
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
pb: 1,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
|
||||||
<PersonAddIcon sx={{ mr: 1, color: "#0a2d6b" }} />
|
|
||||||
<Typography
|
|
||||||
variant="h6"
|
|
||||||
component="span"
|
|
||||||
sx={{ fontWeight: "bold", color: "#0a2d6b" }}
|
|
||||||
>
|
|
||||||
Add New Appointment Type
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
<IconButton onClick={handleCloseDialog} size="small">
|
|
||||||
<CloseIcon />
|
|
||||||
</IconButton>
|
|
||||||
</DialogTitle>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<DialogContent>
|
|
||||||
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 1 }}>
|
|
||||||
<TextField
|
|
||||||
label="Appointment Type"
|
|
||||||
fullWidth
|
|
||||||
margin="normal"
|
|
||||||
value={appointmentType}
|
|
||||||
onChange={(e) => setAppointmentType(e.target.value)}
|
|
||||||
placeholder="Appointment Type"
|
|
||||||
required
|
|
||||||
InputLabelProps={{
|
|
||||||
shrink: true,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</DialogContent>
|
|
||||||
|
|
||||||
<DialogActions sx={{ px: 3, pb: 3 }}>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
color="error"
|
|
||||||
fullWidth
|
|
||||||
onClick={handleSubmit}
|
|
||||||
sx={{
|
|
||||||
py: 1.5,
|
|
||||||
backgroundColor: "#ff3366",
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: "#e61653",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add Appointment Type
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
{/* Notification */}
|
|
||||||
<Snackbar
|
|
||||||
open={notification.open}
|
|
||||||
autoHideDuration={4000}
|
|
||||||
onClose={handleCloseNotification}
|
|
||||||
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
|
|
||||||
>
|
|
||||||
<Alert
|
|
||||||
onClose={handleCloseNotification}
|
|
||||||
severity={notification.severity}
|
|
||||||
sx={{ width: "100%" }}
|
|
||||||
>
|
|
||||||
{notification.message}
|
|
||||||
</Alert>
|
|
||||||
</Snackbar>
|
|
||||||
</Container>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,10 +1,10 @@
|
||||||
import AddIcon from "@mui/icons-material/Add";
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
|
import EditIcon from "@mui/icons-material/Edit";
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Container,
|
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
|
@ -13,113 +13,173 @@ import {
|
||||||
IconButton,
|
IconButton,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Paper,
|
Paper,
|
||||||
Snackbar,
|
|
||||||
Alert,
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
|
Divider,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import React, { useState } from "react";
|
import React, { useMemo, useRef, useState } from "react";
|
||||||
import CustomBreadcrumbs from "../../components/CustomBreadcrumbs";
|
import CustomBreadcrumbs from "../../components/CustomBreadcrumbs";
|
||||||
import PageHeader from "../../components/PageHeader";
|
import PageHeader from "../../components/PageHeader";
|
||||||
|
import { useStyles } from "./paymentStyles";
|
||||||
|
import Table from "../../components/Table";
|
||||||
|
import { getAdmins } from "../../services/auth.services";
|
||||||
|
import { pushNotification } from "../../utils/notification";
|
||||||
|
import { NOTIFICATION } from "../../constants";
|
||||||
|
import { emailRegex } from "../../utils/regex";
|
||||||
|
import {
|
||||||
|
createClinicOffer,
|
||||||
|
getClinicOffer,
|
||||||
|
} from "../../services/clinics.service";
|
||||||
|
|
||||||
const PaymentManagement = () => {
|
const PaymentManagement = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
const ref = useRef(null);
|
||||||
|
const tableRef = useRef(null);
|
||||||
|
|
||||||
// State for payment dialog
|
// State for payment dialog
|
||||||
const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);
|
const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);
|
||||||
const [paymentData, setPaymentData] = useState({
|
const [paymentData, setPaymentData] = useState({
|
||||||
clinicName: "",
|
clinicEmail: "",
|
||||||
setupFeeWaived: false,
|
setupFeeWaived: false,
|
||||||
specialOffer: false,
|
specialOffer: false,
|
||||||
configurationMonth: 3
|
configurationMonth: 3,
|
||||||
});
|
});
|
||||||
|
|
||||||
// State for payments list
|
// State for payments list
|
||||||
const [paymentsList, setPaymentsList] = useState([]);
|
const [paymentsList, setPaymentsList] = useState([]);
|
||||||
const [editPaymentIndex, setEditPaymentIndex] = useState(null);
|
const [editPaymentIndex, setEditPaymentIndex] = useState(null);
|
||||||
|
|
||||||
// State for notification
|
const handleSubmit = () => {};
|
||||||
const [notification, setNotification] = useState({
|
|
||||||
open: false,
|
const columns = useMemo(() => [
|
||||||
message: "",
|
{
|
||||||
severity: "success",
|
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(2, "0");
|
||||||
|
return <span>{serialNumber}</span>;
|
||||||
|
},
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
size: 260,
|
||||||
|
accessorKey: "clinic_email",
|
||||||
|
header: "Clinic Email",
|
||||||
|
enableColumnFilter: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
size: 200,
|
||||||
|
accessorKey: "setup_fees_waived",
|
||||||
|
header: "Setup Fees Waived",
|
||||||
|
Cell: (props) => (props.renderedCellValue ? "Yes" : "No"),
|
||||||
|
enableColumnFilter: false,
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
size: 200,
|
||||||
|
accessorKey: "special_offer_for_month",
|
||||||
|
header: "Special Offer For Month",
|
||||||
|
enableColumnFilter: false,
|
||||||
|
enableSorting: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const getData = async (filters) => {
|
||||||
|
try {
|
||||||
|
let params = {
|
||||||
|
...filters,
|
||||||
|
};
|
||||||
|
|
||||||
|
const resp = await getClinicOffer(params);
|
||||||
|
console.log("API Response:", resp);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: resp?.data?.data?.data,
|
||||||
|
rowCount: resp?.data?.total || 0,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching admins:", error);
|
||||||
|
return {
|
||||||
|
data: [],
|
||||||
|
rowCount: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Handle payment dialog submission
|
// Handle payment dialog submission
|
||||||
const handleAddPayment = () => {
|
const handleAddPayment = async () => {
|
||||||
// Validate input
|
// Validate input
|
||||||
if (!paymentData.clinicName.trim()) {
|
if (!paymentData.clinicEmail.trim()) {
|
||||||
setNotification({
|
pushNotification("Please enter a clinic email", NOTIFICATION.ERROR);
|
||||||
open: true,
|
|
||||||
message: "Please enter a clinic name",
|
|
||||||
severity: "error",
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (editPaymentIndex !== null) {
|
if (!emailRegex.test(paymentData.clinicEmail)) {
|
||||||
// Update existing payment
|
pushNotification("Please enter a valid clinic email", NOTIFICATION.ERROR);
|
||||||
const updatedPayments = [...paymentsList];
|
return;
|
||||||
updatedPayments[editPaymentIndex] = {...paymentData};
|
|
||||||
setPaymentsList(updatedPayments);
|
|
||||||
|
|
||||||
setNotification({
|
|
||||||
open: true,
|
|
||||||
message: "Payment configuration updated successfully",
|
|
||||||
severity: "success",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Add new payment
|
|
||||||
setPaymentsList([...paymentsList, {...paymentData}]);
|
|
||||||
|
|
||||||
setNotification({
|
|
||||||
open: true,
|
|
||||||
message: "Payment configuration added successfully",
|
|
||||||
severity: "success",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// either setup fees waived or special offer for month should be true
|
||||||
|
if (!paymentData.setupFeeWaived && !paymentData.specialOffer) {
|
||||||
|
pushNotification(
|
||||||
|
"Please select either setup fees waived or special offer for month",
|
||||||
|
NOTIFICATION.ERROR
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
clinic_email: paymentData.clinicEmail,
|
||||||
|
setup_fees_waived: paymentData.setupFeeWaived,
|
||||||
|
special_offer_for_month: paymentData.specialOffer
|
||||||
|
? paymentData.configurationMonth.toString()
|
||||||
|
: "0",
|
||||||
|
};
|
||||||
|
|
||||||
|
const resp = await createClinicOffer(payload);
|
||||||
|
console.log("API Response:", resp);
|
||||||
|
|
||||||
// Reset form and close dialog
|
// Reset form and close dialog
|
||||||
setPaymentData({
|
setPaymentData({
|
||||||
clinicName: "",
|
clinicEmail: "",
|
||||||
setupFeeWaived: false,
|
setupFeeWaived: false,
|
||||||
specialOffer: false,
|
specialOffer: false,
|
||||||
configurationMonth: 3
|
configurationMonth: 3,
|
||||||
});
|
});
|
||||||
setEditPaymentIndex(null);
|
setEditPaymentIndex(null);
|
||||||
setPaymentDialogOpen(false);
|
setPaymentDialogOpen(false);
|
||||||
|
|
||||||
|
// Refresh the table data
|
||||||
|
if (tableRef.current) {
|
||||||
|
tableRef.current.reFetchData();
|
||||||
|
pushNotification("Payment configuration added successfully", NOTIFICATION.SUCCESS);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle edit payment
|
// Handle edit payment
|
||||||
const handleEditPayment = (index) => {
|
const handleEditPayment = (index) => {
|
||||||
setEditPaymentIndex(index);
|
setEditPaymentIndex(index);
|
||||||
setPaymentData({...paymentsList[index]});
|
setPaymentData({ ...paymentsList[index] });
|
||||||
setPaymentDialogOpen(true);
|
setPaymentDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle delete payment
|
// Handle delete payment
|
||||||
const handleDeletePayment = (index) => {
|
const handleDeletePayment = (index) => {
|
||||||
const updatedPayments = [...paymentsList];
|
const updatedPayments = [...paymentsList];
|
||||||
updatedPayments.splice(index, 1);
|
updatedPayments.splice(index, 1);
|
||||||
setPaymentsList(updatedPayments);
|
setPaymentsList(updatedPayments);
|
||||||
|
|
||||||
setNotification({
|
|
||||||
open: true,
|
|
||||||
message: "Payment configuration deleted successfully",
|
|
||||||
severity: "success",
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle notification close
|
pushNotification(
|
||||||
const handleCloseNotification = () => {
|
"Payment configuration deleted successfully",
|
||||||
setNotification({
|
NOTIFICATION.SUCCESS
|
||||||
...notification,
|
);
|
||||||
open: false,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Breadcrumbs
|
// Breadcrumbs
|
||||||
|
|
@ -129,202 +189,229 @@ const PaymentManagement = () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
<>
|
||||||
<PageHeader pageTitle="Payment Management" hideAddButton />
|
<Box>
|
||||||
|
<PageHeader
|
||||||
<CustomBreadcrumbs breadcrumbs={breadcrumbs} />
|
pageTitle="Payment Management"
|
||||||
|
addButtonIcon={<AddIcon />}
|
||||||
<Paper elevation={3} sx={{ p: 0, mb: 4, overflow: "hidden" }}>
|
onAddButtonClick={() => setPaymentDialogOpen(true)}
|
||||||
{/* Payment Management Header with Add Button */}
|
/>
|
||||||
<Box
|
<CustomBreadcrumbs breadcrumbs={breadcrumbs} />
|
||||||
sx={{
|
<Box className={classes.tableMainDiv}>
|
||||||
display: "flex",
|
<Box>
|
||||||
justifyContent: "space-between",
|
<Table
|
||||||
alignItems: "center",
|
ref={tableRef}
|
||||||
p: 3,
|
hideTopToolbar
|
||||||
}}
|
columns={columns}
|
||||||
>
|
getData={getData}
|
||||||
<Typography variant="h6" component="h2" sx={{ fontWeight: "bold" }}>
|
options={{
|
||||||
Payment Configurations
|
enableRowSelection: true,
|
||||||
</Typography>
|
showTopBar: false,
|
||||||
|
showFilters: true,
|
||||||
<Button
|
}}
|
||||||
variant="contained"
|
searchText="Staff"
|
||||||
color="error"
|
showSearchBox={true}
|
||||||
startIcon={<AddIcon />}
|
actions={[
|
||||||
onClick={() => setPaymentDialogOpen(true)}
|
{
|
||||||
sx={{
|
onClick: (row) => handleEditPayment(row),
|
||||||
borderRadius: 50,
|
text: "Edit",
|
||||||
textTransform: "none",
|
icon: <EditIcon />,
|
||||||
backgroundColor: "#ff3366",
|
},
|
||||||
"&:hover": {
|
]}
|
||||||
backgroundColor: "#e61653",
|
/>
|
||||||
},
|
</Box>
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add Payment
|
|
||||||
</Button>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Payment List Table */}
|
{/* Improved Dialog Box */}
|
||||||
<TableContainer>
|
<Dialog
|
||||||
<Table sx={{ minWidth: 650 }} aria-label="payment table">
|
open={paymentDialogOpen}
|
||||||
<TableHead sx={{ backgroundColor: "#f5f5f5" }}>
|
onClose={() => setPaymentDialogOpen(false)}
|
||||||
<TableRow>
|
maxWidth="sm"
|
||||||
<TableCell sx={{ fontWeight: "bold" }}>Clinic Name</TableCell>
|
fullWidth
|
||||||
<TableCell sx={{ fontWeight: "bold" }}>Setup Fee Waived</TableCell>
|
PaperProps={{
|
||||||
<TableCell sx={{ fontWeight: "bold" }}>Special Offer</TableCell>
|
sx: {
|
||||||
<TableCell sx={{ fontWeight: "bold" }}>Configuration Month</TableCell>
|
borderRadius: "12px",
|
||||||
<TableCell sx={{ fontWeight: "bold" }}>Actions</TableCell>
|
overflow: "hidden",
|
||||||
</TableRow>
|
},
|
||||||
</TableHead>
|
}}
|
||||||
<TableBody>
|
>
|
||||||
{paymentsList.length > 0 ? (
|
<DialogTitle
|
||||||
paymentsList.map((payment, index) => (
|
|
||||||
<TableRow key={index}>
|
|
||||||
<TableCell>{payment.clinicName}</TableCell>
|
|
||||||
<TableCell>{payment.setupFeeWaived ? "Yes" : "No"}</TableCell>
|
|
||||||
<TableCell>{payment.specialOffer ? "Yes" : "No"}</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
{payment.specialOffer ? payment.configurationMonth : "-"}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => handleEditPayment(index)}
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
color="error"
|
|
||||||
onClick={() => handleDeletePayment(index)}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<TableRow>
|
|
||||||
<TableCell colSpan={5} align="center">
|
|
||||||
No payment configurations found
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
)}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
</Paper>
|
|
||||||
|
|
||||||
{/* Payment Dialog */}
|
|
||||||
<Dialog
|
|
||||||
open={paymentDialogOpen}
|
|
||||||
onClose={() => setPaymentDialogOpen(false)}
|
|
||||||
maxWidth="sm"
|
|
||||||
fullWidth
|
|
||||||
>
|
|
||||||
<DialogTitle>
|
|
||||||
{editPaymentIndex !== null ? "Edit Payment" : "Add New Payment"}
|
|
||||||
<IconButton
|
|
||||||
aria-label="close"
|
|
||||||
onClick={() => setPaymentDialogOpen(false)}
|
|
||||||
sx={{
|
sx={{
|
||||||
position: 'absolute',
|
padding: "16px 24px",
|
||||||
right: 8,
|
backgroundColor: (theme) => theme.palette.primary.main,
|
||||||
top: 8,
|
color: "white",
|
||||||
color: (theme) => theme.palette.grey[500],
|
fontWeight: "bold",
|
||||||
|
position: "relative",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CloseIcon />
|
{editPaymentIndex !== null
|
||||||
</IconButton>
|
? "Edit Payment Configuration"
|
||||||
</DialogTitle>
|
: "Add New Payment"}
|
||||||
<DialogContent>
|
<IconButton
|
||||||
<Box sx={{ mt: 2 }}>
|
aria-label="close"
|
||||||
<TextField
|
onClick={() => setPaymentDialogOpen(false)}
|
||||||
autoFocus
|
sx={{
|
||||||
margin="dense"
|
position: "absolute",
|
||||||
id="clinicName"
|
right: 8,
|
||||||
label="Clinic Name"
|
top: 8,
|
||||||
type="text"
|
color: "white",
|
||||||
fullWidth
|
}}
|
||||||
variant="outlined"
|
>
|
||||||
value={paymentData.clinicName}
|
<CloseIcon />
|
||||||
onChange={(e) => setPaymentData({...paymentData, clinicName: e.target.value})}
|
</IconButton>
|
||||||
sx={{ mb: 2 }}
|
</DialogTitle>
|
||||||
/>
|
|
||||||
|
<DialogContent sx={{ padding: "24px 24px 16px" }}>
|
||||||
<FormControlLabel
|
<Box component="form" onSubmit={handleSubmit} sx={{ mt: 1 }}>
|
||||||
control={
|
<Typography variant="subtitle2" gutterBottom sx={{ mb: 1 }}>
|
||||||
<Checkbox
|
Clinic Information
|
||||||
checked={paymentData.setupFeeWaived}
|
</Typography>
|
||||||
onChange={(e) => setPaymentData({...paymentData, setupFeeWaived: e.target.checked})}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Setup fee waived"
|
|
||||||
sx={{ mb: 1 }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
checked={paymentData.specialOffer}
|
|
||||||
onChange={(e) => setPaymentData({...paymentData, specialOffer: e.target.checked})}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label="Special Offer: First 3 months free"
|
|
||||||
sx={{ mb: 1 }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{paymentData.specialOffer && (
|
|
||||||
<TextField
|
<TextField
|
||||||
select
|
autoFocus
|
||||||
margin="dense"
|
margin="dense"
|
||||||
id="configurationMonth"
|
id="clinicEmail"
|
||||||
label="Configuration Month"
|
label="Clinic Email"
|
||||||
|
type="text"
|
||||||
fullWidth
|
fullWidth
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
value={paymentData.configurationMonth}
|
value={paymentData.clinicEmail}
|
||||||
onChange={(e) => setPaymentData({...paymentData, configurationMonth: e.target.value})}
|
onChange={(e) =>
|
||||||
sx={{ mt: 1 }}
|
setPaymentData({
|
||||||
>
|
...paymentData,
|
||||||
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((month) => (
|
clinicEmail: e.target.value,
|
||||||
<MenuItem key={month} value={month}>
|
})
|
||||||
{month}
|
}
|
||||||
</MenuItem>
|
sx={{ mb: 3 }}
|
||||||
))}
|
/>
|
||||||
</TextField>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={() => setPaymentDialogOpen(false)} color="primary">
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button onClick={handleAddPayment} variant="contained" color="primary">
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
{/* Notification Snackbar */}
|
<Typography variant="subtitle2" gutterBottom sx={{ mb: 1 }}>
|
||||||
<Snackbar
|
Payment Options
|
||||||
open={notification.open}
|
</Typography>
|
||||||
autoHideDuration={6000}
|
|
||||||
onClose={handleCloseNotification}
|
<Paper
|
||||||
anchorOrigin={{ vertical: "top", horizontal: "right" }}
|
elevation={0}
|
||||||
>
|
sx={{
|
||||||
<Alert
|
backgroundColor: (theme) => theme.palette.grey[50],
|
||||||
onClose={handleCloseNotification}
|
p: 2,
|
||||||
severity={notification.severity}
|
mb: 2,
|
||||||
sx={{ width: "100%" }}
|
border: "1px solid",
|
||||||
>
|
borderColor: (theme) => theme.palette.grey[200],
|
||||||
{notification.message}
|
borderRadius: "8px",
|
||||||
</Alert>
|
}}
|
||||||
</Snackbar>
|
>
|
||||||
</Container>
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={paymentData.setupFeeWaived}
|
||||||
|
onChange={(e) =>
|
||||||
|
setPaymentData({
|
||||||
|
...paymentData,
|
||||||
|
setupFeeWaived: e.target.checked,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Box>
|
||||||
|
<Typography variant="body1">Setup fee waived</Typography>
|
||||||
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
No initial setup fee will be charged
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
sx={{ mb: 1, display: "flex", alignItems: "flex-start" }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Divider sx={{ my: 1.5 }} />
|
||||||
|
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
checked={paymentData.specialOffer}
|
||||||
|
onChange={(e) =>
|
||||||
|
setPaymentData({
|
||||||
|
...paymentData,
|
||||||
|
specialOffer: e.target.checked,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Box>
|
||||||
|
<Typography variant="body1">Special Offer</Typography>
|
||||||
|
<Typography variant="caption" color="text.secondary">
|
||||||
|
First 3 months free of charge
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
sx={{ display: "flex", alignItems: "flex-start" }}
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{paymentData.specialOffer && (
|
||||||
|
<TextField
|
||||||
|
select
|
||||||
|
margin="dense"
|
||||||
|
id="configurationMonth"
|
||||||
|
label="Configuration Month"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={paymentData.configurationMonth}
|
||||||
|
onChange={(e) =>
|
||||||
|
setPaymentData({
|
||||||
|
...paymentData,
|
||||||
|
configurationMonth: parseInt(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sx={{ mt: 1 }}
|
||||||
|
helperText="Number of months for special configuration"
|
||||||
|
>
|
||||||
|
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map((month) => (
|
||||||
|
<MenuItem key={month} value={month}>
|
||||||
|
{month} {month === 1 ? "month" : "months"}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</TextField>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
<DialogActions
|
||||||
|
sx={{
|
||||||
|
padding: "16px 24px",
|
||||||
|
borderTop: "1px solid",
|
||||||
|
borderColor: (theme) => theme.palette.grey[200],
|
||||||
|
justifyContent: "space-between",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onClick={() => setPaymentDialogOpen(false)}
|
||||||
|
sx={{
|
||||||
|
color: (theme) => theme.palette.grey[700],
|
||||||
|
fontWeight: "medium",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleAddPayment}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
sx={{
|
||||||
|
borderRadius: "8px",
|
||||||
|
px: 3,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{editPaymentIndex !== null ? "Update" : "Save"}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PaymentManagement;
|
export default PaymentManagement;
|
||||||
|
|
|
||||||
|
|
@ -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,28 +1,15 @@
|
||||||
import AddIcon from "@mui/icons-material/Add";
|
import AddIcon from "@mui/icons-material/Add";
|
||||||
import ArrowBackIosNewIcon from "@mui/icons-material/ArrowBackIosNew";
|
|
||||||
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
|
|
||||||
import CloseIcon from "@mui/icons-material/Close";
|
import CloseIcon from "@mui/icons-material/Close";
|
||||||
import PersonAddIcon from "@mui/icons-material/PersonAdd";
|
import PersonAddIcon from "@mui/icons-material/PersonAdd";
|
||||||
import SearchIcon from "@mui/icons-material/Search";
|
|
||||||
import {
|
import {
|
||||||
Alert,
|
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Container,
|
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
Divider,
|
Divider,
|
||||||
IconButton,
|
IconButton,
|
||||||
InputAdornment,
|
|
||||||
Paper,
|
|
||||||
Snackbar,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableContainer,
|
|
||||||
TableHead,
|
|
||||||
TableRow,
|
|
||||||
TextField,
|
TextField,
|
||||||
Typography,
|
Typography,
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
@ -32,12 +19,10 @@ import PageHeader from "../../components/PageHeader";
|
||||||
import { createAdmin, getAdmins } from "../../services/auth.services";
|
import { createAdmin, getAdmins } from "../../services/auth.services";
|
||||||
import { pushNotification } from "../../utils/notification";
|
import { pushNotification } from "../../utils/notification";
|
||||||
import { NOTIFICATION } from "../../constants";
|
import { NOTIFICATION } from "../../constants";
|
||||||
import { useTheme } from "@emotion/react";
|
|
||||||
import { useStyles } from "./staffStyles";
|
import { useStyles } from "./staffStyles";
|
||||||
import Table from "../../components/Table";
|
import Table from "../../components/Table";
|
||||||
|
|
||||||
const StaffManagement = () => {
|
const StaffManagement = () => {
|
||||||
const theme = useTheme();
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const ref = useRef(null);
|
const ref = useRef(null);
|
||||||
// State for form fields
|
// State for form fields
|
||||||
|
|
@ -46,26 +31,9 @@ const StaffManagement = () => {
|
||||||
const [email, setEmail] = useState("");
|
const [email, setEmail] = useState("");
|
||||||
const [emailError, setEmailError] = useState("");
|
const [emailError, setEmailError] = useState("");
|
||||||
|
|
||||||
// State for staff list
|
|
||||||
const [staffList, setStaffList] = useState([]);
|
|
||||||
|
|
||||||
// State for dialog
|
// State for dialog
|
||||||
const [openDialog, setOpenDialog] = useState(false);
|
const [openDialog, setOpenDialog] = useState(false);
|
||||||
|
|
||||||
// State for search
|
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
|
||||||
|
|
||||||
// State for pagination
|
|
||||||
const [page, setPage] = useState(1);
|
|
||||||
const rowsPerPage = 10;
|
|
||||||
|
|
||||||
// State for notification
|
|
||||||
const [notification, setNotification] = useState({
|
|
||||||
open: false,
|
|
||||||
message: "",
|
|
||||||
severity: "success",
|
|
||||||
});
|
|
||||||
|
|
||||||
// Email validation function
|
// Email validation function
|
||||||
const validateEmail = (email) => {
|
const validateEmail = (email) => {
|
||||||
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
|
@ -113,14 +81,6 @@ const StaffManagement = () => {
|
||||||
handleCloseDialog();
|
handleCloseDialog();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle notification close
|
|
||||||
const handleCloseNotification = () => {
|
|
||||||
setNotification({
|
|
||||||
...notification,
|
|
||||||
open: false,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getData = async (filters) => {
|
const getData = async (filters) => {
|
||||||
try {
|
try {
|
||||||
// Remove the type parameter since it's not defined
|
// Remove the type parameter since it's not defined
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue