feat: clinic admin setup
This commit is contained in:
parent
4717c67cca
commit
050cf206f5
|
|
@ -77,13 +77,19 @@ const CustomFileUpload = forwardRef(function CustomFileUpload(
|
|||
|
||||
useEffect(() => {
|
||||
const makeFullUrlIfNeeded = (url) => {
|
||||
// Return early if url is undefined or empty
|
||||
if (!url) {
|
||||
setOldUploadedFileUrl('');
|
||||
setFileExtension('');
|
||||
setImageName('');
|
||||
return;
|
||||
}
|
||||
|
||||
const isHttp = url.startsWith('http://') || url.startsWith('https://');
|
||||
if (!isHttp) {
|
||||
setOldUploadedFileUrl(url ? `${IMAGE_LOCATION_BASE_URL}${url}` : '');
|
||||
setFileExtension(
|
||||
uploadedFileUrl ? uploadedFileUrl.split('.').pop() : ''
|
||||
);
|
||||
setImageName(uploadedFileUrl ? uploadedFileUrl.split('/').pop() : '');
|
||||
setOldUploadedFileUrl(`${IMAGE_LOCATION_BASE_URL}${url}`);
|
||||
setFileExtension(url.split('.').pop() || '');
|
||||
setImageName(url.split('/').pop() || '');
|
||||
return;
|
||||
}
|
||||
const urlObject = new URL(url);
|
||||
|
|
|
|||
|
|
@ -382,30 +382,30 @@ const Table = memo(
|
|||
muiSelectAllCheckboxProps={{
|
||||
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>
|
||||
// )
|
||||
// ) ?? []
|
||||
// }
|
||||
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>
|
||||
)
|
||||
) ?? []
|
||||
}
|
||||
renderTopToolbarCustomActions={({ table }) => {
|
||||
const handleActive = () => {
|
||||
const data = table
|
||||
|
|
|
|||
|
|
@ -84,6 +84,10 @@ export const NOT_ALLOWED_BLANK_SPACE_REGEX = /^(?! +$)[\s\S]+$/;
|
|||
|
||||
export const HEX_COLOR_CODE_REGEX = /^#([A-Fa-f0-9]{6})$/;
|
||||
|
||||
export const CLINIC_GREETINGS_LENGTH = 1000;
|
||||
|
||||
export const CLINIC_SCENARIOS_LENGTH = 5000;
|
||||
|
||||
export const FILE_TYPE = [
|
||||
'.jpeg',
|
||||
'.jpg',
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ export const userDataMock = {
|
|||
name: "test",
|
||||
email: "test@gmail.com",
|
||||
userType: "Manager",
|
||||
appointmentTypes: ["Adult Immunisation", "Child Immunisation"],
|
||||
specialities: "Doctor",
|
||||
createdAt: "2025-01-01",
|
||||
status: "Active",
|
||||
|
|
@ -15,6 +16,7 @@ export const userDataMock = {
|
|||
name: "John Doe",
|
||||
email: "johndoe@example.com",
|
||||
userType: "Employee",
|
||||
appointmentTypes: ["Adult Immunisation"],
|
||||
specialities: "Engineer",
|
||||
createdAt: "2024-11-15",
|
||||
status: "Inactive",
|
||||
|
|
@ -24,6 +26,7 @@ export const userDataMock = {
|
|||
name: "Jane Smith",
|
||||
email: "janesmith@example.com",
|
||||
userType: "Admin",
|
||||
appointmentTypes: ["Adult Immunisation"],
|
||||
specialities: "Manager",
|
||||
createdAt: "2024-12-01",
|
||||
status: "Active",
|
||||
|
|
@ -32,6 +35,7 @@ export const userDataMock = {
|
|||
id: 4,
|
||||
name: "Alice Brown",
|
||||
email: "alicebrown@example.com",
|
||||
appointmentTypes: ["Adult Immunisation"],
|
||||
userType: "Manager",
|
||||
specialities: "HR",
|
||||
createdAt: "2023-06-22",
|
||||
|
|
@ -51,6 +55,7 @@ export const userDataMock = {
|
|||
name: "Charlie Black",
|
||||
email: "charlieblack@example.com",
|
||||
userType: "Admin",
|
||||
appointmentTypes: ["Adult Immunisation"],
|
||||
specialities: "IT Support",
|
||||
createdAt: "2023-07-05",
|
||||
status: "Inactive",
|
||||
|
|
@ -60,6 +65,7 @@ export const userDataMock = {
|
|||
name: "Eve White",
|
||||
email: "evewhite@example.com",
|
||||
userType: "Manager",
|
||||
appointmentTypes: ["Adult Immunisation"],
|
||||
specialities: "Finance",
|
||||
createdAt: "2025-03-30",
|
||||
status: "Active",
|
||||
|
|
|
|||
|
|
@ -28,14 +28,20 @@ export const getUsers = (params) => {
|
|||
};
|
||||
|
||||
export const getUserById = (id) => {
|
||||
const url = `/users/${id}`;
|
||||
return new Promise((resolve, reject) => {
|
||||
axiosInstance
|
||||
.get(url)
|
||||
.then((response) => resolve(response))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
const data = userDataMock;
|
||||
return data.data.records.find((item) => item.id === id)
|
||||
};
|
||||
|
||||
// export const getUserById = (id) => {
|
||||
// const url = `/users/${id}`;
|
||||
// return new Promise((resolve, reject) => {
|
||||
// axiosInstance
|
||||
// .get(url)
|
||||
// .then((response) => resolve(response))
|
||||
// .catch((err) => reject(err));
|
||||
// });
|
||||
// };
|
||||
|
||||
export const getRoles = ({ page }) =>
|
||||
page == 0
|
||||
? {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import SendIcon from '@mui/icons-material/Send';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import * as React from 'react';
|
||||
import { pushNotification } from '../../utils/notification';
|
||||
import { NOTIFICATION_TYPE } from '../Notifications/notificationConstant';
|
||||
import SendIcon from "@mui/icons-material/Send";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import FormControl from "@mui/material/FormControl";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import InputLabel from "@mui/material/InputLabel";
|
||||
import * as React from "react";
|
||||
import { pushNotification } from "../../utils/notification";
|
||||
import { NOTIFICATION_TYPE } from "../Notifications/notificationConstant";
|
||||
import {
|
||||
Dialog,
|
||||
DialogActions,
|
||||
|
|
@ -21,33 +21,37 @@ import {
|
|||
Stepper,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
|
||||
} from "@mui/material";
|
||||
import WarningAmberIcon from "@mui/icons-material/WarningAmber";
|
||||
import {
|
||||
CLINIC_GREETINGS_LENGTH,
|
||||
CLINIC_SCENARIOS_LENGTH,
|
||||
} from "../../constants";
|
||||
|
||||
// Integration software options
|
||||
const integrationOptions = ['BP Software', 'Medical Director'];
|
||||
const integrationOptions = ["BP Software", "Medical Director"];
|
||||
|
||||
// Steps for the stepper
|
||||
const steps = [
|
||||
'General Information',
|
||||
'AI Receptionist Setup',
|
||||
'Scenarios Setup',
|
||||
'General Info Setup',
|
||||
'Integration Settings',
|
||||
"General Information",
|
||||
"AI Receptionist Setup",
|
||||
"Scenarios Setup",
|
||||
"General Info Setup",
|
||||
"Integration Settings",
|
||||
];
|
||||
|
||||
const voiceModels = [
|
||||
{ id: 'stream', name: 'Stream', gender: 'female' },
|
||||
{ id: 'sandy', name: 'Sandy', gender: 'female' },
|
||||
{ id: 'breeze', name: 'Breeze', gender: 'female' },
|
||||
{ id: 'wolf', name: 'Wolf', gender: 'male' },
|
||||
{ id: 'stan', name: 'Sten', gender: 'male' },
|
||||
{ id: 'blaze', name: 'Blaze', gender: 'male' },
|
||||
{ id: "stream", name: "Stream", gender: "female" },
|
||||
{ id: "sandy", name: "Sandy", gender: "female" },
|
||||
{ id: "breeze", name: "Breeze", gender: "female" },
|
||||
{ id: "wolf", name: "Wolf", gender: "male" },
|
||||
{ id: "stan", name: "Sten", gender: "male" },
|
||||
{ id: "blaze", name: "Blaze", gender: "male" },
|
||||
];
|
||||
|
||||
const voiceModelGender = [
|
||||
{ id: 'male', name: 'Male' },
|
||||
{ id: 'female', name: 'Female' },
|
||||
{ id: "male", name: "Male" },
|
||||
{ id: "female", name: "Female" },
|
||||
];
|
||||
|
||||
export default function ClinicSetup() {
|
||||
|
|
@ -56,6 +60,7 @@ export default function ClinicSetup() {
|
|||
const [openDialog, setOpenDialog] = React.useState(false);
|
||||
const [audioContext, setAudioContext] = React.useState(null);
|
||||
const [testConnDone, setTestConnDone] = React.useState(false);
|
||||
const [confirmPhoneNumber, setConfirmPhoneNumber] = React.useState(false);
|
||||
|
||||
// Completed steps state
|
||||
const [completed, setCompleted] = React.useState({});
|
||||
|
|
@ -63,13 +68,13 @@ export default function ClinicSetup() {
|
|||
// Form state
|
||||
const [formData, setFormData] = React.useState({
|
||||
// General Information
|
||||
clinicPhone: '159875654',
|
||||
clinicAddress: '1 Wilkinson Road, Para Hills SA 5096',
|
||||
otherInfo: '',
|
||||
clinicPhone: "159875654",
|
||||
clinicAddress: "1 Wilkinson Road, Para Hills SA 5096",
|
||||
otherInfo: "",
|
||||
|
||||
// Greetings Setup
|
||||
clinicGreetings:
|
||||
'Welcome to 365 days medical center group, para hills clinic. We are here to help you.',
|
||||
"Welcome to 365 days medical center group, para hills clinic. We are here to help you.",
|
||||
clinicScenarios: `Booking for care plan /Care plan Review
|
||||
Existing patients- Book with regular GP and Nurse on same day ( if not possible book different day, F2F with nurse for care plan and another days F2F consultation with Dr to sign of care plan )
|
||||
New patients- At the moment we are not taking New patients on long term care but tell them they can see GP and can discuss with GP before booking for care plan .
|
||||
|
|
@ -141,12 +146,12 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
- Other pets must remain outside the building except for veterinary consultations (available Tuesdays and Thursdays)`,
|
||||
|
||||
// Integration Settings
|
||||
integrationSoftware: '',
|
||||
practiceId: '',
|
||||
practiceName: '',
|
||||
integrationSoftware: "",
|
||||
practiceId: "",
|
||||
practiceName: "",
|
||||
|
||||
// Voice Configuration
|
||||
voice: '',
|
||||
voice: "",
|
||||
voiceGender: voiceModelGender[1].id,
|
||||
});
|
||||
|
||||
|
|
@ -157,12 +162,22 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
// reset form data to initial state
|
||||
setFormData(
|
||||
Object.keys(formData).reduce((acc, key) => {
|
||||
acc[key] = '';
|
||||
acc[key] = "";
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
};
|
||||
|
||||
const handleConfirmPhoneNumber = () => {
|
||||
setConfirmPhoneNumber(true);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if(formData.clinicPhone=="159875654"){
|
||||
setConfirmPhoneNumber(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Initialize audio context when component mounts
|
||||
function initAudioContext() {
|
||||
|
|
@ -172,8 +187,8 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
|
||||
if (!window.AudioContext) {
|
||||
pushNotification(
|
||||
'Your browser does not support AudioContext and cannot play back audio.',
|
||||
'error'
|
||||
"Your browser does not support AudioContext and cannot play back audio.",
|
||||
"error"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
|
@ -189,7 +204,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
if (context && context.state !== 'closed') {
|
||||
if (context && context.state !== "closed") {
|
||||
context.close();
|
||||
}
|
||||
};
|
||||
|
|
@ -209,6 +224,11 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
|
||||
// Handle next button click
|
||||
const handleNext = () => {
|
||||
if (!confirmPhoneNumber) {
|
||||
pushNotification("Please confirm phone number", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const newActiveStep =
|
||||
isLastStep() && !allStepsCompleted()
|
||||
? // It's the last step, but not all steps have been completed,
|
||||
|
|
@ -221,30 +241,30 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
// Listen to voice using rime.ai API
|
||||
const listenVoice = () => {
|
||||
if (!audioContext) {
|
||||
pushNotification('Audio context not initialized', 'error');
|
||||
pushNotification("Audio context not initialized", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.clinicGreetings || formData.clinicGreetings.length === 0) {
|
||||
pushNotification(
|
||||
'Please enter clinic greetings to listen preview',
|
||||
'error'
|
||||
"Please enter clinic greetings to listen preview",
|
||||
"error"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: 'audio/mp3',
|
||||
Authorization: 'Bearer ElD2r2Dn9Lsl5qB5TupaGcKlWSEf2llo9CkHi2OrOOU',
|
||||
'Content-Type': 'application/json',
|
||||
Accept: "audio/mp3",
|
||||
Authorization: "Bearer ElD2r2Dn9Lsl5qB5TupaGcKlWSEf2llo9CkHi2OrOOU",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
speaker: formData.voice || 'wolf',
|
||||
speaker: formData.voice || "wolf",
|
||||
text: formData.clinicGreetings,
|
||||
modelId: 'mist',
|
||||
lang: 'eng',
|
||||
modelId: "mist",
|
||||
lang: "eng",
|
||||
samplingRate: 22050,
|
||||
speedAlpha: 1.0,
|
||||
reduceLatency: false,
|
||||
|
|
@ -252,9 +272,9 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
};
|
||||
|
||||
// Show loading state
|
||||
pushNotification('Generating audio...', 'info');
|
||||
pushNotification("Generating audio...", "info");
|
||||
|
||||
fetch('https://users.rime.ai/v1/rime-tts', options)
|
||||
fetch("https://users.rime.ai/v1/rime-tts", options)
|
||||
.then((response) => {
|
||||
// Check if the response is successful
|
||||
if (!response.ok) {
|
||||
|
|
@ -269,12 +289,12 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
arrayBuffer,
|
||||
(buffer) => playAudioBuffer(buffer, audioContext),
|
||||
(error) => {
|
||||
pushNotification('Failed to decode audio data', 'error');
|
||||
pushNotification("Failed to decode audio data", "error");
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch((err) => {
|
||||
pushNotification(`Error: ${err.message}`, 'error');
|
||||
pushNotification(`Error: ${err.message}`, "error");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -291,7 +311,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
source.start(0);
|
||||
|
||||
// Notify user
|
||||
pushNotification('Playing audio...', 'success');
|
||||
pushNotification("Playing audio...", "success");
|
||||
}
|
||||
|
||||
// Handle back button click
|
||||
|
|
@ -301,20 +321,24 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
|
||||
// Handle step click
|
||||
const handleStep = (step) => () => {
|
||||
// If trying to skip ahead with errors in current step, validate first
|
||||
if (step > activeStep) {
|
||||
if (validateStep()) {
|
||||
setActiveStep(step);
|
||||
} else {
|
||||
// Alert user about validation errors
|
||||
pushNotification(
|
||||
'Please complete the current step before proceeding',
|
||||
NOTIFICATION_TYPE.ERROR
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Allow going backward anytime
|
||||
// First check if phone number is confirmed for any step navigation
|
||||
if (!confirmPhoneNumber) {
|
||||
pushNotification(
|
||||
"Please confirm phone number before navigating between steps",
|
||||
"error"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Then check if the step is completed
|
||||
if (completed[step]) {
|
||||
setActiveStep(step);
|
||||
} else {
|
||||
// Alert user about validation errors
|
||||
pushNotification(
|
||||
"Please complete the current step before proceeding",
|
||||
"error"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -322,7 +346,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
const handleTestConnection = () => {
|
||||
// logic here
|
||||
setTestConnDone(true);
|
||||
pushNotification('Test connection successful', 'success');
|
||||
pushNotification("Test connection successful", "success");
|
||||
};
|
||||
|
||||
// Form validation state
|
||||
|
|
@ -357,8 +381,8 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
// Validate only if software is selected
|
||||
if (formData.integrationSoftware) {
|
||||
const integrationErrors = {
|
||||
practiceId: formData.practiceId.trim() === '',
|
||||
practiceName: formData.practiceName.trim() === '',
|
||||
practiceId: formData.practiceId.trim() === "",
|
||||
practiceName: formData.practiceName.trim() === "",
|
||||
};
|
||||
setErrors(integrationErrors);
|
||||
return !Object.values(integrationErrors).some((isError) => isError);
|
||||
|
|
@ -387,6 +411,10 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
|
||||
if (name === "clinicPhone" && value != formData.clinicPhone) {
|
||||
setConfirmPhoneNumber(false);
|
||||
}
|
||||
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[name]: value,
|
||||
|
|
@ -419,14 +447,14 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
const validateForm = () => {
|
||||
// Check all steps
|
||||
const generalInfoValid =
|
||||
formData.clinicPhone.trim() !== '' &&
|
||||
formData.clinicAddress.trim() !== '';
|
||||
formData.clinicPhone.trim() !== "" &&
|
||||
formData.clinicAddress.trim() !== "";
|
||||
|
||||
let integrationValid = true;
|
||||
if (formData.integrationSoftware) {
|
||||
integrationValid =
|
||||
formData.practiceId.trim() !== '' &&
|
||||
formData.practiceName.trim() !== '';
|
||||
formData.practiceId.trim() !== "" &&
|
||||
formData.practiceName.trim() !== "";
|
||||
}
|
||||
|
||||
return generalInfoValid && integrationValid;
|
||||
|
|
@ -437,39 +465,39 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
e.preventDefault();
|
||||
|
||||
if (validateForm()) {
|
||||
console.log('Form submitted:', formData);
|
||||
console.log("Form submitted:", formData);
|
||||
// Here you would typically send the data to your backend
|
||||
pushNotification('Clinic setup updated successfully', 'success');
|
||||
pushNotification("Clinic setup updated successfully", "success");
|
||||
// Reset the form after submission
|
||||
handleReset();
|
||||
} else {
|
||||
// Find the first step with errors and go to it
|
||||
if (
|
||||
formData.clinicPhone.trim() === '' ||
|
||||
formData.clinicAddress.trim() === ''
|
||||
formData.clinicPhone.trim() === "" ||
|
||||
formData.clinicAddress.trim() === ""
|
||||
) {
|
||||
setActiveStep(0);
|
||||
setErrors({
|
||||
...errors,
|
||||
clinicPhone: formData.clinicPhone.trim() === '',
|
||||
clinicAddress: formData.clinicAddress.trim() === '',
|
||||
clinicPhone: formData.clinicPhone.trim() === "",
|
||||
clinicAddress: formData.clinicAddress.trim() === "",
|
||||
});
|
||||
} else if (
|
||||
formData.integrationSoftware &&
|
||||
(formData.practiceId.trim() === '' ||
|
||||
formData.practiceName.trim() === '')
|
||||
(formData.practiceId.trim() === "" ||
|
||||
formData.practiceName.trim() === "")
|
||||
) {
|
||||
setActiveStep(3);
|
||||
setErrors({
|
||||
...errors,
|
||||
practiceId: formData.practiceId.trim() === '',
|
||||
practiceName: formData.practiceName.trim() === '',
|
||||
practiceId: formData.practiceId.trim() === "",
|
||||
practiceName: formData.practiceName.trim() === "",
|
||||
});
|
||||
}
|
||||
|
||||
// Show an alert
|
||||
pushNotification(
|
||||
'Please fill in all required fields before submitting',
|
||||
"Please fill in all required fields before submitting",
|
||||
NOTIFICATION_TYPE.ERROR
|
||||
);
|
||||
}
|
||||
|
|
@ -492,32 +520,32 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
display="flex"
|
||||
>
|
||||
<TextField
|
||||
style={{ width: '40%' }}
|
||||
type="number"
|
||||
style={{ width: "40%" }}
|
||||
label="Clinic Phone Number"
|
||||
name="clinicPhone"
|
||||
value={formData.clinicPhone}
|
||||
onChange={handleChange}
|
||||
helperText={
|
||||
errors.clinicPhone
|
||||
? 'Phone number is required'
|
||||
: 'Unique single number for your clinic'
|
||||
? "Phone number is required"
|
||||
: "Unique single number for your clinic"
|
||||
}
|
||||
variant="outlined"
|
||||
error={errors.clinicPhone}
|
||||
/>
|
||||
<Button
|
||||
style={{ marginLeft: '10px' }}
|
||||
style={{ marginLeft: "10px" }}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<SendIcon />}
|
||||
href="https://login.twilio.com/u/signup"
|
||||
target="_blank"
|
||||
onClick={handleConfirmPhoneNumber}
|
||||
>
|
||||
Configure Phone Number
|
||||
Confirm
|
||||
</Button>
|
||||
<>
|
||||
<Button
|
||||
style={{ marginLeft: '10px' }}
|
||||
style={{ marginLeft: "10px" }}
|
||||
variant="outlined"
|
||||
color="error"
|
||||
startIcon={<WarningAmberIcon />}
|
||||
|
|
@ -532,7 +560,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{'Confirm Disable AI Receptionist'}
|
||||
{"Confirm Disable AI Receptionist"}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
|
|
@ -553,7 +581,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
<Button
|
||||
onClick={() => {
|
||||
pushNotification(
|
||||
'AI Receptionist disabled successfully',
|
||||
"AI Receptionist disabled successfully",
|
||||
NOTIFICATION_TYPE.SUCCESS
|
||||
);
|
||||
setOpenDialog(false);
|
||||
|
|
@ -579,7 +607,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
variant="outlined"
|
||||
error={errors.clinicAddress}
|
||||
helperText={
|
||||
errors.clinicAddress ? 'Clinic address is required' : ''
|
||||
errors.clinicAddress ? "Clinic address is required" : ""
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
|
|
@ -610,15 +638,15 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
multiline
|
||||
rows={3}
|
||||
variant="outlined"
|
||||
helperText={`${formData.clinicGreetings.length}/200 characters`}
|
||||
inputProps={{ maxLength: 200 }}
|
||||
helperText={`${formData.clinicGreetings.length}/${CLINIC_GREETINGS_LENGTH} characters`}
|
||||
inputProps={{ maxLength: CLINIC_GREETINGS_LENGTH }}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
maxWidth: '40%',
|
||||
display: "flex",
|
||||
maxWidth: "40%",
|
||||
// marginTop: '10px',
|
||||
justifyContent: 'space-between',
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
|
|
@ -650,10 +678,10 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
onChange={handleChange}
|
||||
value={
|
||||
formData.voice ||
|
||||
(formData.voiceGender === 'male'
|
||||
? voiceModels.find((model) => model.gender === 'male')
|
||||
(formData.voiceGender === "male"
|
||||
? voiceModels.find((model) => model.gender === "male")
|
||||
.id
|
||||
: voiceModels.find((model) => model.gender === 'female')
|
||||
: voiceModels.find((model) => model.gender === "female")
|
||||
.id)
|
||||
}
|
||||
displayEmpty
|
||||
|
|
@ -677,7 +705,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
return (
|
||||
<>
|
||||
<TextField
|
||||
style={{ marginTop: '10px' }}
|
||||
style={{ marginTop: "10px" }}
|
||||
fullWidth
|
||||
label="Scenarios"
|
||||
name="clinicScenarios"
|
||||
|
|
@ -685,9 +713,9 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
onChange={handleChange}
|
||||
multiline
|
||||
rows={8}
|
||||
helperText={`${formData.clinicScenarios.length}/1500 characters`}
|
||||
helperText={`${formData.clinicScenarios.length}/${CLINIC_SCENARIOS_LENGTH} characters`}
|
||||
variant="outlined"
|
||||
inputProps={{ maxLength: 3000 }}
|
||||
inputProps={{ maxLength: CLINIC_SCENARIOS_LENGTH }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
@ -696,7 +724,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
return (
|
||||
<>
|
||||
<TextField
|
||||
style={{ marginTop: '10px' }}
|
||||
style={{ marginTop: "10px" }}
|
||||
fullWidth
|
||||
label="General Info"
|
||||
name="clinicOthers"
|
||||
|
|
@ -782,8 +810,8 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
error={!!formData.integrationSoftware && errors.practiceId}
|
||||
helperText={
|
||||
!!formData.integrationSoftware && errors.practiceId
|
||||
? 'Practice ID is required'
|
||||
: ''
|
||||
? "Practice ID is required"
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
|
|
@ -799,8 +827,8 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
error={!!formData.integrationSoftware && errors.practiceName}
|
||||
helperText={
|
||||
!!formData.integrationSoftware && errors.practiceName
|
||||
? 'Practice Name is required'
|
||||
: ''
|
||||
? "Practice Name is required"
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
|
|
@ -808,17 +836,17 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
);
|
||||
|
||||
default:
|
||||
return 'Unknown step';
|
||||
return "Unknown step";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box component="form" onSubmit={handleSubmit} sx={{ width: '100%' }}>
|
||||
<Box component="form" onSubmit={handleSubmit} sx={{ width: "100%" }}>
|
||||
<Paper elevation={0} sx={{ p: 2 }}>
|
||||
<Typography
|
||||
variant="h4"
|
||||
component="h1"
|
||||
sx={{ fontWeight: 'bold', mb: 3 }}
|
||||
sx={{ fontWeight: "bold", mb: 3 }}
|
||||
>
|
||||
Clinic Setup
|
||||
</Typography>
|
||||
|
|
@ -828,12 +856,21 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
{steps.map((label, index) => {
|
||||
const stepProps = {};
|
||||
const labelProps = {};
|
||||
// Determine if this step is clickable
|
||||
const isClickable = confirmPhoneNumber && completed[index];
|
||||
|
||||
return (
|
||||
<Step key={label} {...stepProps} completed={completed[index]}>
|
||||
<StepLabel
|
||||
{...labelProps}
|
||||
onClick={handleStep(index)}
|
||||
sx={{ cursor: 'pointer' }}
|
||||
sx={{
|
||||
cursor: isClickable ? "pointer" : "not-allowed",
|
||||
opacity: isClickable ? 1 : 0.7,
|
||||
"& .MuiStepLabel-label": {
|
||||
color: isClickable ? "text.primary" : "text.disabled",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</StepLabel>
|
||||
|
|
@ -843,14 +880,14 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
</Stepper>
|
||||
|
||||
{/* Step Content */}
|
||||
<Box sx={{ mt: 2, mb: 2, minHeight: '300px' }}>
|
||||
<Box sx={{ mt: 2, mb: 2, minHeight: "300px" }}>
|
||||
{allStepsCompleted() ? (
|
||||
<React.Fragment>
|
||||
<Typography sx={{ mt: 2, mb: 1 }}>
|
||||
All steps completed - Setup is ready to be saved.
|
||||
</Typography>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', pt: 2 }}>
|
||||
<Box sx={{ flex: '1 1 auto' }} />
|
||||
<Box sx={{ display: "flex", flexDirection: "row", pt: 2 }}>
|
||||
<Box sx={{ flex: "1 1 auto" }} />
|
||||
{/* <Button onClick={handleReset} sx={{ mr: 1 }}>
|
||||
Reset
|
||||
</Button> */}
|
||||
|
|
@ -864,7 +901,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
{renderStepContent(activeStep)}
|
||||
|
||||
{/* Navigation Buttons */}
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', pt: 2, mt: 4 }}>
|
||||
<Box sx={{ display: "flex", flexDirection: "row", pt: 2, mt: 4 }}>
|
||||
<Button
|
||||
color="inherit"
|
||||
disabled={activeStep === 0}
|
||||
|
|
@ -874,7 +911,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
>
|
||||
Back
|
||||
</Button>
|
||||
<Box sx={{ flex: '1 1 auto' }} />
|
||||
<Box sx={{ flex: "1 1 auto" }} />
|
||||
{activeStep == 4 && (
|
||||
<Button
|
||||
onClick={handleTestConnection}
|
||||
|
|
@ -900,7 +937,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I
|
|||
color="primary"
|
||||
onClick={handleComplete}
|
||||
>
|
||||
{isLastStep() ? 'Finish' : 'Complete Step'}
|
||||
{isLastStep() ? "Finish" : "Complete Step"}
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,325 @@
|
|||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import { pxToRem } from '../../theme/typography';
|
||||
|
||||
export const useStyles = makeStyles((theme) => ({
|
||||
appBarStyle: {
|
||||
backgroundColor: theme.palette.grey[28],
|
||||
},
|
||||
contentBox: {
|
||||
marginTop: '3%',
|
||||
},
|
||||
mainContent: {
|
||||
marginTop: theme.spacing(4),
|
||||
marginBottom: theme.spacing(4),
|
||||
marginLeft: theme.spacing(4),
|
||||
marginRight: theme.spacing(4),
|
||||
width: '100%',
|
||||
overflowX: 'scroll',
|
||||
'&::-webkit-scrollbar': {
|
||||
height: '6px',
|
||||
},
|
||||
},
|
||||
logoBox: {
|
||||
display: 'flex',
|
||||
width: '100%',
|
||||
marginLeft: '1%',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
width: 'auto',
|
||||
},
|
||||
},
|
||||
logo: {
|
||||
maxHeight: '61px',
|
||||
objectFit: 'contain',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
whitePaper: {
|
||||
width: '100%',
|
||||
marginBottom: theme.spacing(0.3),
|
||||
paddingBottom: theme.spacing(0.7),
|
||||
borderRadius: `${theme.spacing(2)} ${theme.spacing(2)} ${theme.spacing(
|
||||
1
|
||||
)} ${theme.spacing(1)}`,
|
||||
},
|
||||
inputLabel: {
|
||||
marginBottom: theme.spacing(0.5),
|
||||
fontSize: pxToRem(12),
|
||||
color: theme.palette.grey[10],
|
||||
},
|
||||
textAreaLast: {
|
||||
width: '-webkit-fill-available',
|
||||
'&.MuiButtonBase-root': {
|
||||
padding: theme.spacing(0.8),
|
||||
},
|
||||
},
|
||||
helperText: {
|
||||
color: theme.palette.error.main,
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
formRoot: {
|
||||
paddingTop: theme.spacing(3.2),
|
||||
},
|
||||
formPassswordHeading: {
|
||||
fontSize: pxToRem(14),
|
||||
fontWeight: '600',
|
||||
},
|
||||
passwordTextFiled: {
|
||||
marginTop: theme.spacing(0),
|
||||
},
|
||||
icon: {
|
||||
cursor: 'pointer',
|
||||
paddingLeft: theme.spacing(3),
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '50px',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
width: '50px',
|
||||
},
|
||||
[theme.breakpoints.up('lg')]: {
|
||||
width: '50px',
|
||||
},
|
||||
[theme.breakpoints.up('xl')]: {
|
||||
width: '50px',
|
||||
},
|
||||
},
|
||||
formPasswordHeadingRoot: {
|
||||
paddingTop: theme.spacing(2),
|
||||
paddingLeft: theme.spacing(2),
|
||||
width: '100%',
|
||||
},
|
||||
subjectText: {
|
||||
fontSize: pxToRem(12),
|
||||
position: 'absolute',
|
||||
right: pxToRem(8),
|
||||
top: '90%',
|
||||
transform: 'translateY(-90%)',
|
||||
},
|
||||
// --- AddForm Style ---
|
||||
screenViewGridItem: {
|
||||
marginTop: theme.spacing(1),
|
||||
},
|
||||
paperContainerBox: {
|
||||
width: '100%',
|
||||
},
|
||||
countryCodeBox: {
|
||||
padding: pxToRem(12),
|
||||
paddingRight: pxToRem(20),
|
||||
paddingLeft: pxToRem(16),
|
||||
borderRadius: pxToRem(10),
|
||||
backgroundColor: theme.palette.grey[20],
|
||||
},
|
||||
countryCodeLabel: {
|
||||
marginBottom: theme.spacing(0),
|
||||
opacity: 1,
|
||||
fontFamily: 'Inter-Regular',
|
||||
},
|
||||
mobileNumberInput: {
|
||||
fontFamily: 'Inter-Regular',
|
||||
'& .MuiInputBase-root': {
|
||||
paddingLeft: theme.spacing(0),
|
||||
border: 0,
|
||||
},
|
||||
'& .MuiOutlinedInput-input': {
|
||||
paddingLeft: theme.spacing(0),
|
||||
},
|
||||
},
|
||||
passwordCheckListTitle: {
|
||||
color: theme.palette.grey[20],
|
||||
fontSize: pxToRem(12),
|
||||
fontStyle: 'italic',
|
||||
textAlign: 'left',
|
||||
marginBottom: theme.spacing(0.4),
|
||||
},
|
||||
emailNote: {
|
||||
color: theme.palette.grey[20],
|
||||
fontSize: pxToRem(14),
|
||||
fontStyle: 'italic',
|
||||
textAlign: 'left',
|
||||
marginBottom: theme.spacing(0.4),
|
||||
},
|
||||
passwordCheckList: {
|
||||
fontSize: pxToRem(14),
|
||||
paddingTop: theme.spacing(0.2),
|
||||
fontStyle: 'italic',
|
||||
color: theme.palette.grey[600],
|
||||
},
|
||||
stateTextField: {
|
||||
'& .Mui-disabled': {
|
||||
WebkitTextFillColor: theme.palette.grey[10], // Change to the appropriate color value
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
termsAndConditionErrorMessage: {
|
||||
color: theme.palette.error.main,
|
||||
fontStyle: 'italic',
|
||||
fontSize: pxToRem(12),
|
||||
marginTop: theme.spacing(0.5),
|
||||
},
|
||||
checkBox: {
|
||||
marginTop: theme.spacing(3),
|
||||
paddingRight: theme.spacing(1),
|
||||
},
|
||||
checkBoxLabel: {
|
||||
marginTop: theme.spacing(3.2),
|
||||
marginLeft: theme.spacing(1.2),
|
||||
fontSize: pxToRem(16),
|
||||
color: theme.palette.common.black,
|
||||
},
|
||||
linkText: {
|
||||
color: theme.palette.grey[53],
|
||||
},
|
||||
placeholderText: {
|
||||
fontStyle: 'italic',
|
||||
fontSize: pxToRem(14),
|
||||
opacity: 0.5,
|
||||
color: theme.palette.grey[10],
|
||||
},
|
||||
// logo style
|
||||
reactCrop: {
|
||||
/* Set a minimum height and width */
|
||||
minHeight: '200px',
|
||||
minWidth: '200px',
|
||||
},
|
||||
dropZoneOuterBox: {
|
||||
width: '100%',
|
||||
},
|
||||
inputTitle: {
|
||||
'&.MuiTypography-body1': {
|
||||
fontSize: pxToRem(12),
|
||||
color: theme.palette.grey[10],
|
||||
},
|
||||
marginBottom: theme.spacing(0.6),
|
||||
marginLeft: theme.spacing(0.6),
|
||||
color: theme.palette.grey[10],
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
imgButton: {
|
||||
color: theme?.palette?.primary?.main,
|
||||
fontSize: pxToRem(12),
|
||||
fontWeight: 600,
|
||||
fontFamily: theme?.fontFamily?.Regular,
|
||||
backgroundColor: 'transparent',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
margin: `${theme.spacing(0)} ${theme.spacing(0.8)}`,
|
||||
padding: theme.spacing(0),
|
||||
outline: 'none',
|
||||
},
|
||||
logoUploadErrorBox: {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
errorText: {
|
||||
color: theme.palette.error.main,
|
||||
fontSize: pxToRem(12),
|
||||
fontStyle: 'italic',
|
||||
},
|
||||
addButton: {
|
||||
border: `1px dashed ${theme.palette.primary.main}`,
|
||||
width: '100%',
|
||||
height: '130px',
|
||||
backgroundColor: theme.palette.common.white,
|
||||
borderRadius: theme.spacing(1),
|
||||
marginBottom: theme.spacing(1.48),
|
||||
},
|
||||
addButtonContent: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
cursor: 'pointer',
|
||||
width: '100%',
|
||||
},
|
||||
addText: {
|
||||
opacity: 1,
|
||||
fontFamily: theme?.fontFamily?.medium,
|
||||
fontSize: pxToRem(14),
|
||||
height: '16px',
|
||||
fontWeight: 500,
|
||||
lineHeight: 'normal',
|
||||
letterSpacing: 'normal',
|
||||
marginTop: theme?.spacing(1),
|
||||
},
|
||||
addIcon: {
|
||||
width: '26px',
|
||||
height: '24px',
|
||||
},
|
||||
addSubtext: {
|
||||
fontSize: pxToRem(12),
|
||||
color: theme.palette.grey[10],
|
||||
opacity: '0.6',
|
||||
textAlign: 'center',
|
||||
padding: `${theme.spacing(0.5)} ${theme.spacing(7)}`,
|
||||
[theme.breakpoints.down('md')]: {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
[theme.breakpoints.down('lg')]: {
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
previewBox: {
|
||||
display: 'flex',
|
||||
justifyContent: 'end',
|
||||
alignItems: 'start',
|
||||
width: '153px',
|
||||
height: '111px',
|
||||
borderRadius: theme.spacing(0.4),
|
||||
backgroundSize: 'cover',
|
||||
},
|
||||
formSectionHeadingMargin: {
|
||||
marginTop: theme.spacing(1.5),
|
||||
},
|
||||
|
||||
termsAndConditionDrawer: {
|
||||
'& .MuiDrawer-paper': {
|
||||
maxWidth: pxToRem(550),
|
||||
boxShadow: '-10px 0px 10px -2px rgba(0,0,0,0.1)',
|
||||
},
|
||||
[theme.breakpoints.down('md')]: {
|
||||
width: '100%',
|
||||
},
|
||||
},
|
||||
verifyIcon: {
|
||||
fontSize: pxToRem(20),
|
||||
color: theme.palette.grey[54],
|
||||
},
|
||||
chipOuter: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
paddingLeft: theme.spacing(3.2),
|
||||
},
|
||||
chip: {
|
||||
minWidth: '170px',
|
||||
height: '42px',
|
||||
padding: theme.spacing(1.5),
|
||||
margin: theme.spacing(1),
|
||||
marginRight: theme.spacing(1.5),
|
||||
justifyContent: 'space-between',
|
||||
borderRadius: '24px',
|
||||
fontSize: pxToRem(14),
|
||||
},
|
||||
billingUserOuterSection: {
|
||||
marginTop: theme.spacing(3.0),
|
||||
},
|
||||
billingUserEmailSection: {
|
||||
padding: `${theme.spacing(2.2)} ${theme.spacing(3.2)}`,
|
||||
},
|
||||
saveUserButton: {
|
||||
marginLeft: theme.spacing(1.0),
|
||||
width: '150px',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-evenly',
|
||||
borderRadius: '8px',
|
||||
height: '47px',
|
||||
// opacity: 0.5,
|
||||
},
|
||||
billingUserEmailWrapper: {
|
||||
display: 'flex',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
flexDirection: 'column',
|
||||
gap: '15px',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
|
@ -1,9 +1,70 @@
|
|||
import React from 'react'
|
||||
import { Box } from "@mui/system";
|
||||
import React from "react";
|
||||
import { useFormik } from "formik";
|
||||
import * as Yup from "yup";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
import PageHeader from "../../components/PageHeader";
|
||||
import CustomFileUpload from "../../components/CustomFileUpload";
|
||||
import { MAX_FILE_SIZE_IN_MB, MAX_FILES } from "../../constants";
|
||||
import { useStyles } from "./contractManagementStyles";
|
||||
import { Grid, Paper } from "@mui/material";
|
||||
|
||||
const ContractManagement = () => {
|
||||
return (
|
||||
<div>ContractManagement</div>
|
||||
)
|
||||
}
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
|
||||
export default ContractManagement
|
||||
const formik = useFormik({
|
||||
initialValues: {
|
||||
companyPANImage: "",
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
companyPANImage: Yup.string().required("Required"),
|
||||
}),
|
||||
onSubmit: (values) => {
|
||||
console.log(values);
|
||||
},
|
||||
});
|
||||
|
||||
const setUploadedFileUrl = () => {};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Paper
|
||||
className={classes.formRoot}
|
||||
sx={{ padding: theme.spacing(2), height: "70vh" }}
|
||||
>
|
||||
<PageHeader
|
||||
pageTitle="Contract Management"
|
||||
infiniteDropdown
|
||||
hideAddButton={true}
|
||||
/>
|
||||
<Grid
|
||||
container
|
||||
spacing={theme.spacing(2.3)}
|
||||
padding={`0 ${theme.spacing(3.2)}`}
|
||||
className={classes.formRoot}
|
||||
sx={{ display: "flex", flexDirection: "column" }}
|
||||
>
|
||||
<Grid item md={4} sm={12} xs={12}>
|
||||
<CustomFileUpload
|
||||
label="Contract*"
|
||||
documentName="sd"
|
||||
onUploadDone={setUploadedFileUrl}
|
||||
maxFileSizeInMb={MAX_FILE_SIZE_IN_MB}
|
||||
maxFiles={MAX_FILES}
|
||||
uploadedFileUrl={formik.values.companyPANImage}
|
||||
errorMessage={
|
||||
formik.errors.companyPANImage && formik.touched.companyPANImage
|
||||
? formik.errors.companyPANImage
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContractManagement;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,354 @@
|
|||
import React from 'react'
|
||||
|
||||
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 PersonAddIcon from '@mui/icons-material/PersonAdd';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
Button,
|
||||
Container,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Divider,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
Paper,
|
||||
Snackbar,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import CustomBreadcrumbs from '../../components/CustomBreadcrumbs';
|
||||
import PageHeader from '../../components/PageHeader';
|
||||
const MasterDataManagement = () => {
|
||||
return (
|
||||
<div>MasterDataManagement</div>
|
||||
)
|
||||
}
|
||||
// State for form fields
|
||||
const [appointmentType, setAppointmentType] = useState('');
|
||||
const queryParams = new URLSearchParams(location.search);
|
||||
|
||||
// State for staff list
|
||||
const [staffList, setStaffList] = useState([]);
|
||||
|
||||
// State for dialog
|
||||
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',
|
||||
});
|
||||
|
||||
// Handle dialog open/close
|
||||
const handleOpenDialog = () => {
|
||||
setOpenDialog(true);
|
||||
};
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
setOpenDialog(false);
|
||||
// Clear form
|
||||
setAppointmentType('');
|
||||
};
|
||||
|
||||
// Handle form submission
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Add new staff member
|
||||
const newStaff = {
|
||||
id: staffList.length + 1,
|
||||
appointmentType,
|
||||
};
|
||||
|
||||
setStaffList([...staffList, newStaff]);
|
||||
|
||||
// Close dialog
|
||||
handleCloseDialog();
|
||||
|
||||
// Show success notification
|
||||
setNotification({
|
||||
open: true,
|
||||
message: 'Staff member added successfully!',
|
||||
severity: 'success',
|
||||
});
|
||||
};
|
||||
|
||||
// Handle notification close
|
||||
const handleCloseNotification = () => {
|
||||
setNotification({
|
||||
...notification,
|
||||
open: false,
|
||||
});
|
||||
};
|
||||
|
||||
// ...................breadcrumbs array........................
|
||||
const breadcrumbs = [
|
||||
{
|
||||
label: 'Dashboard',
|
||||
path: '/',
|
||||
},
|
||||
{
|
||||
label: 'Master Data Management',
|
||||
path: '/masterData',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Container maxWidth="lg" sx={{ py: 4 }}>
|
||||
|
||||
export default MasterDataManagement
|
||||
<PageHeader
|
||||
pageTitle="Master Data Management"
|
||||
hideAddButton
|
||||
/>
|
||||
|
||||
<CustomBreadcrumbs breadcrumbs={breadcrumbs} />
|
||||
|
||||
<Paper elevation={3} sx={{ p: 0, mb: 4, overflow: 'hidden' }}>
|
||||
{/* Staff List Header with Add Button */}
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
p: 3,
|
||||
}}
|
||||
>
|
||||
<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,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* Staff List Table */}
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow sx={{ backgroundColor: '#f5f5f5' }}>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Sr. No.</TableCell>
|
||||
<TableCell sx={{ fontWeight: 'bold' }}>Appointment Type</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={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
p: 2,
|
||||
borderTop: '1px solid #eee',
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
onClick={() => setPage((prev) => Math.max(prev - 1, 1))}
|
||||
disabled={page === 1}
|
||||
startIcon={<ArrowBackIosNewIcon fontSize="small" />}
|
||||
sx={{ mx: 1, color: '#666' }}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
disableElevation
|
||||
sx={{
|
||||
mx: 1,
|
||||
minWidth: '36px',
|
||||
backgroundColor: '#f0f0f0',
|
||||
color: '#333',
|
||||
'&:hover': {
|
||||
backgroundColor: '#e0e0e0',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{page}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
onClick={() => setPage((prev) => prev + 1)}
|
||||
disabled={page * rowsPerPage >= staffList.length}
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
export default MasterDataManagement;
|
||||
|
|
|
|||
|
|
@ -144,6 +144,9 @@ function YourDetailsForm() {
|
|||
practiceManagementSystem: "",
|
||||
practiceId: "",
|
||||
practiceName: "",
|
||||
|
||||
// contract management
|
||||
contract: "",
|
||||
});
|
||||
|
||||
// Field references for focus and validation
|
||||
|
|
@ -176,6 +179,9 @@ function YourDetailsForm() {
|
|||
fullAddress: useRef(null),
|
||||
companyPANImage: useRef(null),
|
||||
companyTANNumber: useRef(null),
|
||||
|
||||
// contract management
|
||||
contract: useRef(null),
|
||||
};
|
||||
|
||||
if (yourDetailsFormData) {
|
||||
|
|
@ -262,6 +268,7 @@ function YourDetailsForm() {
|
|||
companyTANImage: Yup.string().required(
|
||||
"Clinic MEDICARE document is required"
|
||||
),
|
||||
contract: Yup.string().required("Contract is required"),
|
||||
termsAccepted: Yup.boolean()
|
||||
.oneOf([true], "You must accept the terms and conditions")
|
||||
.required("You must accept the terms and conditions"),
|
||||
|
|
@ -551,6 +558,7 @@ function YourDetailsForm() {
|
|||
documentType: "PAN",
|
||||
},
|
||||
],
|
||||
contract: inputData.contract || "",
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
|
@ -644,7 +652,9 @@ function YourDetailsForm() {
|
|||
padding={`0 ${theme.spacing(3.2)}`}
|
||||
className={classes.formRoot}
|
||||
>
|
||||
<TextField disabled />
|
||||
<Grid item md={4} sm={6} xs={12}>
|
||||
<TextField value={1} disabled />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* Personal Details Section */}
|
||||
|
|
@ -1769,7 +1779,7 @@ function YourDetailsForm() {
|
|||
<Grid item md={4} sm={12} xs={12}>
|
||||
<CustomFileUpload
|
||||
label="Add ABN Image*"
|
||||
documentName="sd"
|
||||
documentName="companyPANImage"
|
||||
onUploadDone={setUploadedFileUrl}
|
||||
maxFileSizeInMb={MAX_FILE_SIZE_IN_MB}
|
||||
maxFiles={MAX_FILES}
|
||||
|
|
@ -1783,6 +1793,24 @@ function YourDetailsForm() {
|
|||
/>
|
||||
</Grid>
|
||||
|
||||
{/* contract grid */}
|
||||
<Grid item md={4} sm={12} xs={12}>
|
||||
<CustomFileUpload
|
||||
label="Add Contract*"
|
||||
documentName="contract"
|
||||
onUploadDone={setUploadedFileUrl}
|
||||
maxFileSizeInMb={MAX_FILE_SIZE_IN_MB}
|
||||
maxFiles={MAX_FILES}
|
||||
uploadedFileUrl={formik.values.contract}
|
||||
errorMessage={
|
||||
formik.errors.contract &&
|
||||
formik.touched.contract
|
||||
? formik.errors.contract
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
{/* terms and condition grid */}
|
||||
<Grid item md={12}>
|
||||
<Box display="flex">
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import { LoadingButton } from "@mui/lab";
|
||||
import { Box, Button, Grid, MenuItem, Select, Switch, TextField } from "@mui/material";
|
||||
import { Box, Button, Chip, Grid, MenuItem, Select, Switch, TextField } from "@mui/material";
|
||||
import { useFormik } from "formik";
|
||||
import React, { useMemo, useRef, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import * as Yup from "yup";
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
|
||||
/* ----------------- Custom Imports ----------------- */
|
||||
import PageHeader from "../../components/PageHeader";
|
||||
|
|
@ -13,11 +14,7 @@ import CustomModal from "../Modal/Modal";
|
|||
import { useStyles } from "./userStyles";
|
||||
|
||||
/* ----------------- Assets ----------------- */
|
||||
import SendIcon from "@mui/icons-material/Send";
|
||||
import AddIcon from "@mui/icons-material/Add";
|
||||
import DeleteIcon from "@mui/icons-material/Delete";
|
||||
import EditIcon from "@mui/icons-material/Edit";
|
||||
import RemoveIcon from "@mui/icons-material/Remove";
|
||||
import {
|
||||
getUsers,
|
||||
deleteUserById,
|
||||
|
|
@ -98,6 +95,36 @@ function Users() {
|
|||
return { data: resp?.data?.records, rowCount: resp?.data?.totalCount };
|
||||
};
|
||||
|
||||
const editUser = async (row) => {
|
||||
try {
|
||||
const userData = await getUserById(row?.original?.id);
|
||||
const formData = {
|
||||
userName: userData?.name,
|
||||
email: userData?.email,
|
||||
mobile: userData?.mobile,
|
||||
isEditUser: true,
|
||||
};
|
||||
// const updatedCheckboxes = roles.reduce(
|
||||
// (acc, role) => ({
|
||||
// ...acc,
|
||||
// [role?.id]: userData?.roles.some(
|
||||
// (roleData) => roleData?.id === role?.id
|
||||
// ),
|
||||
// }),
|
||||
// {}
|
||||
// );
|
||||
|
||||
// setSelectedCheckboxes(updatedCheckboxes);
|
||||
// formik.setValues(formData);
|
||||
// toggle();
|
||||
// setIsEditUser(true);
|
||||
// setUserId(row?.original?.id);
|
||||
} catch ({ resp }) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(resp?.data?.message);
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleButton = async (row) => {
|
||||
try {
|
||||
// Replace this with your actual API call to update user status
|
||||
|
|
@ -204,14 +231,63 @@ function Users() {
|
|||
isBold: true,
|
||||
},
|
||||
{
|
||||
accessorFn: ({ mobile }) => mobile || NOT_AVAILABLE_TEXT,
|
||||
accessorFn: ({ userType }) => userType || NOT_AVAILABLE_TEXT,
|
||||
accessorKey: "userType",
|
||||
header: "User Type",
|
||||
},
|
||||
{
|
||||
accessorFn: ({ email }) => email || NOT_AVAILABLE_TEXT,
|
||||
accessorKey: "specialities",
|
||||
header: "Specialties",
|
||||
accessorKey: "appointmentTypes",
|
||||
header: "Appointment Types",
|
||||
Cell: ({ row }) => {
|
||||
const appointmentTypes = row?.original?.appointmentTypes;
|
||||
|
||||
// Convert to array if it's a string (comma-separated values)
|
||||
const typesArray = Array.isArray(appointmentTypes) && appointmentTypes.length > 0
|
||||
? appointmentTypes
|
||||
: typeof appointmentTypes === 'string' && appointmentTypes.trim() !== ''
|
||||
? appointmentTypes.split(',').map(type => type.trim())
|
||||
: [];
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
|
||||
{typesArray.length > 0 ? (
|
||||
typesArray.map((type, index) => {
|
||||
const label = typeof type === 'string'
|
||||
? type
|
||||
: type?.name || type?.type || String(type);
|
||||
|
||||
return (
|
||||
<Chip
|
||||
key={index}
|
||||
label={label}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
sx={{
|
||||
margin: '2px',
|
||||
borderRadius: '16px',
|
||||
backgroundColor: '#f5f5f5',
|
||||
'& .MuiChip-label': { fontSize: '0.75rem' }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Chip
|
||||
label={NOT_AVAILABLE_TEXT}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
sx={{
|
||||
margin: '2px',
|
||||
borderRadius: '16px',
|
||||
backgroundColor: '#f0f0f0',
|
||||
color: '#757575',
|
||||
'& .MuiChip-label': { fontSize: '0.75rem' }
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
|
|
@ -362,26 +438,25 @@ function Users() {
|
|||
columns={columns}
|
||||
getData={getData}
|
||||
options={{ enableRowSelection: false }}
|
||||
// showAction={true}
|
||||
showAction={true}
|
||||
hideShowPerPage={true}
|
||||
showSearchBox={true}
|
||||
ref={ref}
|
||||
searchText={"user"}
|
||||
getRowStyle={getRowStyle}
|
||||
// actions={[
|
||||
// {
|
||||
// title: "Action",
|
||||
// field: "isActive",
|
||||
// render: (rowData) => (
|
||||
// <Switch
|
||||
// checked={rowData.isActive}
|
||||
// onChange={() => handleToggleButton(rowData)}
|
||||
// inputProps={{ "aria-label": "Status toggle" }}
|
||||
// color="primary"
|
||||
// />
|
||||
// ),
|
||||
// },
|
||||
// ]}
|
||||
actions={[
|
||||
{
|
||||
onClick: editUser,
|
||||
text: 'Edit',
|
||||
icon: (
|
||||
<EditIcon
|
||||
// className={classes.tableActionIcons}
|
||||
alt="Edit"
|
||||
/>
|
||||
),
|
||||
// permissionName: "CREATE_USERS",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue