From 050cf206f52c99b2f062fa0df60237374d91b89a Mon Sep 17 00:00:00 2001 From: deepvasoya Date: Thu, 8 May 2025 19:36:56 +0530 Subject: [PATCH] feat: clinic admin setup --- src/components/CustomFileUpload.jsx | 16 +- src/components/Table.jsx | 48 +-- src/constants/index.js | 4 + src/mock/users/index.js | 6 + src/services/users.service.js | 20 +- src/views/ClinicSetup/index.jsx | 277 ++++++++------ .../contractManagementStyles.js | 325 ++++++++++++++++ src/views/ContractManagement/index.jsx | 73 +++- src/views/MasterData/index.jsx | 359 +++++++++++++++++- src/views/Signup/YourDetailsForm.jsx | 32 +- src/views/User/index.jsx | 123 ++++-- 11 files changed, 1088 insertions(+), 195 deletions(-) create mode 100644 src/views/ContractManagement/contractManagementStyles.js diff --git a/src/components/CustomFileUpload.jsx b/src/components/CustomFileUpload.jsx index 6283ed6..a35b2cf 100644 --- a/src/components/CustomFileUpload.jsx +++ b/src/components/CustomFileUpload.jsx @@ -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); diff --git a/src/components/Table.jsx b/src/components/Table.jsx index 3d6aaac..4cdd32e 100644 --- a/src/components/Table.jsx +++ b/src/components/Table.jsx @@ -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 : ( - // { - // 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)} - // - // ) - // ) ?? [] - // } + renderRowActionMenuItems={({ row, closeMenu }) => + actions?.filter(action => !action.render)?.map((action, index) => + !(action?.renderAction?.(row) ?? true) ? null : ( + { + 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)} + + ) + ) ?? [] + } renderTopToolbarCustomActions={({ table }) => { const handleActive = () => { const data = table diff --git a/src/constants/index.js b/src/constants/index.js index 54d4116..abd74be 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -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', diff --git a/src/mock/users/index.js b/src/mock/users/index.js index ad3e2bc..edd5623 100644 --- a/src/mock/users/index.js +++ b/src/mock/users/index.js @@ -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", diff --git a/src/services/users.service.js b/src/services/users.service.js index e0ea9e0..ad585ab 100644 --- a/src/services/users.service.js +++ b/src/services/users.service.js @@ -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 ? { diff --git a/src/views/ClinicSetup/index.jsx b/src/views/ClinicSetup/index.jsx index d6c537c..fa85856 100644 --- a/src/views/ClinicSetup/index.jsx +++ b/src/views/ClinicSetup/index.jsx @@ -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" > <> */} @@ -864,7 +901,7 @@ If No -Pt who do not have a Medicare card / Private patients with or without I {renderStepContent(activeStep)} {/* Navigation Buttons */} - + - + {activeStep == 4 && ( )} diff --git a/src/views/ContractManagement/contractManagementStyles.js b/src/views/ContractManagement/contractManagementStyles.js new file mode 100644 index 0000000..af3349d --- /dev/null +++ b/src/views/ContractManagement/contractManagementStyles.js @@ -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', + }, + }, +})); diff --git a/src/views/ContractManagement/index.jsx b/src/views/ContractManagement/index.jsx index 8d01962..5684ad7 100644 --- a/src/views/ContractManagement/index.jsx +++ b/src/views/ContractManagement/index.jsx @@ -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 ( -
ContractManagement
- ) -} + const classes = useStyles(); + const theme = useTheme(); -export default ContractManagement \ No newline at end of file + const formik = useFormik({ + initialValues: { + companyPANImage: "", + }, + validationSchema: Yup.object({ + companyPANImage: Yup.string().required("Required"), + }), + onSubmit: (values) => { + console.log(values); + }, + }); + + const setUploadedFileUrl = () => {}; + + return ( + + + + + + + + + + + ); +}; + +export default ContractManagement; diff --git a/src/views/MasterData/index.jsx b/src/views/MasterData/index.jsx index ed841cb..3b470be 100644 --- a/src/views/MasterData/index.jsx +++ b/src/views/MasterData/index.jsx @@ -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 ( -
MasterDataManagement
- ) -} + // 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 ( + -export default MasterDataManagement \ No newline at end of file + + + + + + {/* Staff List Header with Add Button */} + + + Master Appointment Type List + + + + + + {/* Search Box */} + + setSearchQuery(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + }} + sx={{ + backgroundColor: '#fff', + '& .MuiOutlinedInput-root': { + borderRadius: 2, + }, + }} + /> + + + {/* Staff List Table */} + + + + + Sr. No. + Appointment Type + + + + {staffList.length > 0 ? ( + staffList + .filter( + (staff) => + `${staff.appointmentType}` + .toLowerCase() + .includes(searchQuery.toLowerCase()) + ) + .slice((page - 1) * rowsPerPage, page * rowsPerPage) + .map((staff, index) => ( + + + {(page - 1) * rowsPerPage + index + 1} + + {staff.appointmentType} + + )) + ) : ( + + + No Appointment Type added yet + + + )} + +
+
+ + {/* Pagination */} + {staffList.length > 0 && ( + + + + + + + + )} +
+ + {/* Add Staff Dialog */} + + + + + + Add New Appointment Type + + + + + + + + + + + + setAppointmentType(e.target.value)} + placeholder="Appointment Type" + required + InputLabelProps={{ + shrink: true, + }} + /> + + + + + + + + + {/* Notification */} + + + {notification.message} + + +
+ ); +}; + +export default MasterDataManagement; diff --git a/src/views/Signup/YourDetailsForm.jsx b/src/views/Signup/YourDetailsForm.jsx index 562d420..1d4f127 100644 --- a/src/views/Signup/YourDetailsForm.jsx +++ b/src/views/Signup/YourDetailsForm.jsx @@ -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} > - + + + {/* Personal Details Section */} @@ -1769,7 +1779,7 @@ function YourDetailsForm() { + {/* contract grid */} + + + + {/* terms and condition grid */} diff --git a/src/views/User/index.jsx b/src/views/User/index.jsx index 6a2b06b..9c26b40 100644 --- a/src/views/User/index.jsx +++ b/src/views/User/index.jsx @@ -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 ( + + {typesArray.length > 0 ? ( + typesArray.map((type, index) => { + const label = typeof type === 'string' + ? type + : type?.name || type?.type || String(type); + + return ( + + ); + }) + ) : ( + + )} + + ); + }, }, { 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) => ( - // handleToggleButton(rowData)} - // inputProps={{ "aria-label": "Status toggle" }} - // color="primary" - // /> - // ), - // }, - // ]} + actions={[ + { + onClick: editUser, + text: 'Edit', + icon: ( + + ), + // permissionName: "CREATE_USERS", + }, + ]} />