diff --git a/src/components/CustomFileUpload.jsx b/src/components/CustomFileUpload.jsx index 7e2c3ab..4fa1f9c 100644 --- a/src/components/CustomFileUpload.jsx +++ b/src/components/CustomFileUpload.jsx @@ -116,10 +116,7 @@ const CustomFileUpload = forwardRef(function CustomFileUpload( try { setIsLoading(true); const response = await getPresignedUrl(filePayload); - - // Debug the response structure - console.log('API Response:', response); - + // Check if we have a valid response with the expected structure if (response?.data?.data?.Key) { // Use the Key from the response @@ -128,7 +125,6 @@ const CustomFileUpload = forwardRef(function CustomFileUpload( } else { // If the expected structure is not found, try to find the key in a different location // or use a fallback value - console.log('Response structure is different than expected'); // Try different possible paths to find the key const key = response?.data?.Key || @@ -136,7 +132,6 @@ const CustomFileUpload = forwardRef(function CustomFileUpload( response?.Key || value.name; // Fallback to the file name if key not found - console.log('Using key:', key); onUploadDone(documentName, key); // Try to find the API URL similarly diff --git a/src/config/api.js b/src/config/api.js index 2ac1711..84415af 100644 --- a/src/config/api.js +++ b/src/config/api.js @@ -1,9 +1,9 @@ -import axios from 'axios'; -import { API_BASE_URL } from '../common/envVariables'; -import { ERRORS, NOTIFICATION } from '../constants'; -import { pushNotification } from '../utils/notification'; -import { commonLogoutFunc } from '../utils/share'; -import store from '../redux/store'; +import axios from "axios"; +import { API_BASE_URL } from "../common/envVariables"; +import { ERRORS, NOTIFICATION } from "../constants"; +import { pushNotification } from "../utils/notification"; +import { commonLogoutFunc } from "../utils/share"; +import store from "../redux/store"; export const axiosInstance = axios.create({ baseURL: API_BASE_URL, @@ -20,10 +20,15 @@ export const axiosInstance = axios.create({ axiosInstance.interceptors.request.use( async function (config) { try { - const token = JSON.parse(localStorage.getItem('redux')); + const token = JSON.parse(localStorage.getItem("redux")); if (token?.login?.token) { config.headers.Authorization = `Bearer ${token?.login?.token}`; } + // if url is dev-ai-appointment.s3.amazonaws.com + if (config.url?.includes("dev-ai-appointment.s3.amazonaws.com")) { + config.headers.Authorization = null; + } + const state = store.getState(); const companyId = state?.loginAsCompanyAdmin?.companyId; // Extract companyId if (companyId) { @@ -58,7 +63,7 @@ axiosInstance.interceptors.response.use( console.log(error); if (!error?.response?.data) { pushNotification(error.message, NOTIFICATION.ERROR); - if (error.message !== 'Network Error') commonLogoutFunc(); + if (error.message !== "Network Error") commonLogoutFunc(); } else if ( error?.response?.data?.error && error?.response?.data?.error[0]?.message @@ -91,10 +96,10 @@ axiosInstance.interceptors.response.use( export const fileDownloadAxios = axios.create({ baseURL: API_BASE_URL, - responseType: 'blob', + responseType: "blob", crossDomain: true, headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, withCredentials: true, timeout: 300000, @@ -103,10 +108,16 @@ export const fileDownloadAxios = axios.create({ fileDownloadAxios.interceptors.request.use( async function (config) { try { - const token = JSON.parse(localStorage.getItem('redux')); + const token = JSON.parse(localStorage.getItem("redux")); if (token?.data?.data) { config.headers.Authorization = `${token?.data?.data}`; } + + // if url is dev-ai-appointment.s3.amazonaws.com + if (config.url?.includes("dev-ai-appointment.s3.amazonaws.com")) { + config.headers.Authorization = null; + } + const state = store.getState(); const companyId = state?.loginAsCompanyAdmin?.companyId; // Extract companyId if (companyId) { @@ -131,7 +142,7 @@ fileDownloadAxios.interceptors.response.use( async function (response) { // Handle error response (when server responds with JSON instead of a file) if ( - response?.headers['content-type']?.includes('application/json') && + response?.headers["content-type"]?.includes("application/json") && response?.data ) { try { @@ -140,14 +151,14 @@ fileDownloadAxios.interceptors.response.use( if (parsedError?.error) { pushNotification( - parsedError?.message || 'Download failed', + parsedError?.message || "Download failed", NOTIFICATION.ERROR ); return Promise.reject(parsedError); } } catch (err) { // eslint-disable-next-line no-console - console.error('Error parsing error response', err); + console.error("Error parsing error response", err); } } @@ -159,26 +170,26 @@ fileDownloadAxios.interceptors.response.use( if (!error?.response?.data) { pushNotification(error.message, NOTIFICATION.ERROR); - if (error.message !== 'Network Error') commonLogoutFunc(); + if (error.message !== "Network Error") commonLogoutFunc(); } else { // Handle error message from API - const contentType = error?.response?.headers?.['content-type']; + const contentType = error?.response?.headers?.["content-type"]; - if (contentType?.includes('application/json')) { + if (contentType?.includes("application/json")) { error.response.data.text().then((text) => { try { const parsedError = JSON.parse(text); pushNotification( - parsedError.message || 'Something went wrong', + parsedError.message || "Something went wrong", NOTIFICATION.ERROR ); } catch (err) { - pushNotification('Unknown error occurred', NOTIFICATION.ERROR); + pushNotification("Unknown error occurred", NOTIFICATION.ERROR); } }); } else { pushNotification( - error?.response?.data?.message || 'Download failed', + error?.response?.data?.message || "Download failed", NOTIFICATION.ERROR ); } diff --git a/src/layouts/mainLayout/components/Sidebar.jsx b/src/layouts/mainLayout/components/Sidebar.jsx index dc9fd49..168c0fa 100644 --- a/src/layouts/mainLayout/components/Sidebar.jsx +++ b/src/layouts/mainLayout/components/Sidebar.jsx @@ -21,9 +21,12 @@ import { CLINIC_STATUS, HIDE_MODULES } from "../../../constants"; import { isBSPortal } from "../../../utils/share"; import { hideAndShowFunctionality } from "../../../views/Signup/signupAction"; import { useStyles } from "../mainLayoutStyles"; - // Import the configuration from the separate file import { SIDEBAR_CONFIG } from "./sideBarConfig"; // Adjust path if necessary +import { + selectClinicStatus, + selectUserRole, +} from "../../../redux/userRoleSlice"; // NOTE: Removed the internal SIDEBAR_ITEMS definition. // We will now use the imported SIDEBAR_CONFIG. @@ -41,6 +44,7 @@ const Sidebar = ({ onClose, showCloseIcon }) => { const [childRoute, setchildRoute] = useState(""); const [combinedRoute, setcombinedRoute] = useState(""); const dispatch = useDispatch(); + const clinicStatus = useSelector(selectClinicStatus); // Assuming companyStatus is fetched or defined elsewhere correctly const companyStatus = "APPROVED"; // Example status, replace with actual logic if needed @@ -83,88 +87,85 @@ const Sidebar = ({ onClose, showCloseIcon }) => { const userRole = useSelector((state) => state.userRole.role); // Function to determine visibility and render a single sidebar item link - const checkVisibility = (item, i) => { - // Check if the user has the necessary role for this item - const hasRole = !item.roles || item.roles.includes(userRole); + // The checkVisibility function with fixed logic +const checkVisibility = (item, i) => { + // Check if the user has the necessary role for this item + const hasRole = !item.roles || item.roles.includes(userRole); - // Determine if the feature related to this item should be hidden - let hideFeature = - (companyStatus === CLINIC_STATUS.APPROVED && - HIDE_FUNCTIONALITY && - HIDE_MODULES.includes(item?.path)) || - (isSuperAdmin && HIDE_FUNCTIONALITY && HIDE_MODULES.includes(item?.path)); + // Only render if user has the required role + if (hasRole) { + // Determine if the link should be disabled + // FIXED LOGIC: If clinic status is rejected, only allow "/" and "/docs" paths + const isDisabled = (clinicStatus === "rejected" || clinicStatus === "under_review" || clinicStatus === "payment_due") && !(item.path == "" || item.path == "docs"); + + // Set the correct target path + const targetPath = isDisabled ? "#" : `/${item.path}`; + const isActive = activeLink === item.path; // Check if this link is the active one - // Only render if user has the required role - if (hasRole) { - // Determine if the link should be disabled - const isDisabled = - (!isSuperAdmin && companyStatus !== CLINIC_STATUS.APPROVED) || hideFeature; - const targetPath = isDisabled ? "#" : `/${item.path}`; - const isActive = activeLink === item.path; // Check if this link is the active one - - return ( - handleHideFeatures(hideFeature)} // This seems odd, clicking the box hides features? Review this logic. - key={item.path || i} // Use a stable key like item.path + return ( + handleHideFeatures(isDisabled)} // Pass isDisabled to handleHideFeatures + key={item.path || i} // Use a stable key like item.path + > + { + if (isDisabled) e.preventDefault(); + handleLinkClick(item.path); // Set active link on click + }} > - { - if (isDisabled) e.preventDefault(); - handleLinkClick(item.path); // Set active link on click - }} + - - - {isActive - ? React.createElement(item.activeIcon, { - className: classes.sidebarLinkIcons, - }) - : React.createElement(item.icon, { - className: classes.sidebarLinkIcons, - style: { color: "black" }, - })} - - - - - - ); - } - return null; // Return null if item shouldn't be rendered - }; + + {isActive + ? React.createElement(item.activeIcon, { + className: classes.sidebarLinkIcons, + }) + : React.createElement(item.icon, { + className: classes.sidebarLinkIcons, + style: { color: "black" }, + })} + + + + + + ); + } + return null; // Return null if item shouldn't be rendered +}; // Function to render a sidebar item that has children (accordion) const rendersideMenu = (item, i) => { // Check if the parent item has the necessary role const hasParentRole = !item.roles || item.roles.includes(userRole); - + // Only proceed if the parent item has the necessary role if (!hasParentRole) return null; - + // Filter visible children first const visibleChildren = item.children ?.filter((childItem) => { // Check if child has the necessary role - const hasChildRole = !childItem.roles || childItem.roles.includes(userRole); + const hasChildRole = + !childItem.roles || childItem.roles.includes(userRole); return hasChildRole; }) .filter(Boolean); // Filter out any null/undefined results diff --git a/src/layouts/mainLayout/components/sideBarConfig.js b/src/layouts/mainLayout/components/sideBarConfig.js index 4284e25..1592774 100644 --- a/src/layouts/mainLayout/components/sideBarConfig.js +++ b/src/layouts/mainLayout/components/sideBarConfig.js @@ -82,4 +82,13 @@ export const SIDEBAR_CONFIG = [ // Clinic admin can access call transcripts roles: [USER_ROLES.CLINIC_ADMIN] }, + { + text: 'Clinic Documents', + path: 'docs', + requireSaprateApp: false, + icon: ArticleOutlinedIcon, + activeIcon: ArticleIcon, + // Clinic admin can access call transcripts + roles: [USER_ROLES.CLINIC_ADMIN] + }, ]; diff --git a/src/redux/userRoleSlice.js b/src/redux/userRoleSlice.js index 6898a07..415487d 100644 --- a/src/redux/userRoleSlice.js +++ b/src/redux/userRoleSlice.js @@ -1,19 +1,22 @@ // Initial state const initialState = { role: null, + clinicStatus: null, }; // Action types const SET_USER_ROLE = 'userRole/SET_USER_ROLE'; // Action creators -export const setUserRole = (role) => ({ +export const setUserRole = (role, clinicStatus) => ({ type: SET_USER_ROLE, payload: role, + clinicStatus: clinicStatus, }); // Selector to get user role export const selectUserRole = (state) => state.userRole.role; +export const selectClinicStatus = (state) => state.userRole.clinicStatus; // Reducer export default function userRoleReducer(state = initialState, action) { @@ -22,6 +25,7 @@ export default function userRoleReducer(state = initialState, action) { return { ...state, role: action.payload, + clinicStatus: action.clinicStatus, }; default: return state; diff --git a/src/routes/index.js b/src/routes/index.js index 5d15106..6d7efa8 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -4,7 +4,7 @@ import ClinicsList from "../views/ClinicsList"; import Dashboard from "../views/Dashboard"; import StaffManagement from "../views/StaffManagement"; import ClinicDetails from "../views/ClinicDetails"; -import Login from '../views/Login'; +import Login from "../views/Login"; import YourDetailsForm from "../views/Signup/YourDetailsForm"; import MockPayment from "../views/MockPayment"; import Users from "../views/User"; @@ -13,6 +13,7 @@ import ClinicTranscripts from "../views/ClinicTranscripts"; import ContractManagement from "../views/ContractManagement"; import MasterDataManagement from "../views/MasterData"; import PaymentManagement from "../views/PaymentManagement"; +import ClinicDocUpdater from "../views/ClinicDocUpdate"; export const routesData = [ { @@ -28,6 +29,7 @@ export const routesData = [ { path: "/transcripts", component: ClinicTranscripts }, { path: "/masterData", component: MasterDataManagement }, { path: "/payment-management", component: PaymentManagement }, + { path: "/docs", component: ClinicDocUpdater }, ], isProtected: true, }, @@ -38,7 +40,7 @@ export const routesData = [ { path: "/auth/login", component: Login }, { path: "/auth/signup/payment", component: MockPayment }, { - path: 'signup/your-details', + path: "signup/your-details", component: YourDetailsForm, }, ], diff --git a/src/views/ClinicDocUpdate/index.jsx b/src/views/ClinicDocUpdate/index.jsx new file mode 100644 index 0000000..8305706 --- /dev/null +++ b/src/views/ClinicDocUpdate/index.jsx @@ -0,0 +1,197 @@ +import { Button, Grid, Paper, Typography } from "@mui/material"; +import { Box } from "@mui/system"; +import React, { useEffect, useState } from "react"; +import CustomFileUpload from "../../components/CustomFileUpload"; +import { useFormik } from "formik"; +import * as Yup from "yup"; +import { MAX_FILE_SIZE_IN_MB, MAX_FILES } from "../../constants"; +import { updateClinicDocs } from "../../services/clinics.service"; +import { useNavigate } from "react-router-dom"; + +const ClinicDocUpdater = () => { + const navigate = useNavigate(); + // Use state instead of ref for initial form data + const [initialFormData, setInitialFormData] = useState({ + companyABNImage: "", + contract: "", + logo: "", + clinicId: "", + }); + + // Track if data is loaded + const [isDataLoaded, setIsDataLoaded] = useState(false); + + useEffect(() => { + // Load user data from localStorage + try { + const user = JSON.parse(localStorage.getItem("user")); + + if (user && user.created_clinics ) { + setInitialFormData({ + companyABNImage: user.created_clinics.abn_doc || "", + contract: user.created_clinics.contract_doc || "", + logo: user.created_clinics.logo || "", + clinicId: user.created_clinics.id || "", + }); + } + } catch (error) { + console.error("Error loading user data:", error); + } finally { + setIsDataLoaded(true); + } + }, []); + + const validationSchema = Yup.object().shape({ + companyABNImage: Yup.string().required("Clinic ABN document is required"), + contract: Yup.string().required("Contract is required"), + logo: Yup.string().required("Logo is required"), + }); + + const handleFormSubmit = async () => { + console.log("Form values:", formik.values); + const payload = { + abn_doc: formik.values.companyABNImage, + contract_doc: formik.values.contract, + logo: formik.values.logo, + } + + const resp = await updateClinicDocs(formik.values.clinicId, payload); + console.log(resp); + + // clear localStorage + localStorage.removeItem("user"); + // redirect to login + navigate("/"); + }; + + const handleSubmit = async () => { + formik.handleSubmit(); + }; + + // Initialize formik only after data is loaded + const formik = useFormik({ + initialValues: initialFormData, + validationSchema, + onSubmit: handleFormSubmit, + enableReinitialize: true, // This is key - it will update form values when initialValues change + }); + + // Set uploaded file URL + const setUploadedFileUrl = (documentName, fileUrl) => { + console.log("Document Name:", documentName); + console.log("File URL:", fileUrl); + console.log("File URL Type:", typeof fileUrl); + + if (documentName && fileUrl !== undefined) { + formik.setFieldValue(documentName, fileUrl); + console.log("After setting value:", formik.values[documentName]); + } else { + console.error("Invalid parameters for setUploadedFileUrl:", { + documentName, + fileUrl, + }); + } + }; + + return ( + + + + + + Clinic Docs + + + {isDataLoaded ? ( +
+ + + + + + + + + + + + + + +
+ ) : ( + + Loading clinic documents... + + )} +
+
+
+ ); +}; + +export default ClinicDocUpdater; diff --git a/src/views/Dashboard/helper.js b/src/views/Dashboard/helper.js new file mode 100644 index 0000000..d3baddd --- /dev/null +++ b/src/views/Dashboard/helper.js @@ -0,0 +1,30 @@ +export const convertToSignupForm = (data) => { + return { + name: data.name, + email: data.email, + password: data.password, + mobileNumber: data.mobileNumber, + mobilePrefix: data.mobilePrefix, + companyName: data.companyName, + designation: data.designation, + businessPhonePrefix: data.businessPhonePrefix, + businessPhone: data.businessPhone, + emergencyBusinessPhone: data.emergencyBusinessPhone, + emergencyBusinessPhonePrefix: data.emergencyBusinessPhonePrefix, + businessFax: data.businessFax, + clinicLogo: data.clinicLogo, + businessEmail: data.businessEmail, + pincode: data.pincode, + state: data.state, + locality: data.locality, + country: data.country, + fullAddress: data.fullAddress, + companyABNImageNumber: data.companyABNImageNumber, + companyABNImage: data.companyABNImage, + termsAccepted: data.termsAccepted, + practiceManagementSystem: data.practiceManagementSystem, + practiceId: data.practiceId, + practiceName: data.practiceName, + contract: data.contract, + }; +}; diff --git a/src/views/Dashboard/index.jsx b/src/views/Dashboard/index.jsx index 4000f18..ea737e4 100644 --- a/src/views/Dashboard/index.jsx +++ b/src/views/Dashboard/index.jsx @@ -7,7 +7,12 @@ import SuperAdmin from "./components/SuperAdmin"; import { selectUserRole, setUserRole } from "../../redux/userRoleSlice"; import Totals from "./Tiles/Totals"; import { useStyles } from "./dashboardStyles"; -import { USER_ROLES } from "../../constants"; +import { NOTIFICATION, USER_ROLES } from "../../constants"; +import { getClinicsById } from "../../services/clinics.service"; +import { pushNotification } from "../../utils/notification"; +import { useNavigate } from "react-router-dom"; +import { updateFormDetails } from "../Signup/signupAction"; +import { convertToSignupForm } from "./helper"; function Dashboard() { const classes = useStyles(); @@ -31,7 +36,51 @@ function Dashboard() { const userRole = useSelector(selectUserRole); const user = useSelector((state) => state.login.user); + const navigation = useNavigate(); + + const getClinic = async () => { + setIsLoading(true); + const resp = await getClinicsById(user.created_clinics[0].id); + + + // if(resp?.data?.data?.error){ + // setIsActive(false); + // pushNotification("Error while fetching clinic data", NOTIFICATION.ERROR); + // navigation("/auth/signup/your-details?status=error"); + // } + + const status = resp?.data?.data?.clinic?.status; + + if(status == "rejected"){ + setIsActive(false); + + // update user state with latest clinic + localStorage.setItem("user", JSON.stringify({ + ...user, + created_clinics: resp?.data?.data?.clinic + })) + + dispatch(setUserRole(USER_ROLES.CLINIC_ADMIN, "rejected")); + + pushNotification(resp?.data?.data?.fileStatus?.reason || "Your clinic registration has been rejected", NOTIFICATION.WARNING); + navigation("/docs"); + } + + // if(status=="payment_due"){ + // setIsActive(false); + // pushNotification("Payment due", NOTIFICATION.WARNING); + // } + + // if(status=="requested_doc"){ + // setIsActive(false); + // pushNotification("Documents requested", NOTIFICATION.WARNING); + // } + + setIsLoading(false); + }; + useEffect(() => { + getClinic(); // Determine user role based on user data from login reducer if (user) { // Check if user is a super admin @@ -44,9 +93,9 @@ function Dashboard() { setIsActive(clinicStatus == "active"); if (isSuperAdmin) { - dispatch(setUserRole(USER_ROLES.SUPER_ADMIN)); + dispatch(setUserRole(USER_ROLES.SUPER_ADMIN, clinicStatus)); } else { - dispatch(setUserRole(USER_ROLES.CLINIC_ADMIN)); + dispatch(setUserRole(USER_ROLES.CLINIC_ADMIN, clinicStatus)); } } setIsLoading(false); diff --git a/src/views/Signup/YourDetailsForm.jsx b/src/views/Signup/YourDetailsForm.jsx index 7093827..f24ec19 100644 --- a/src/views/Signup/YourDetailsForm.jsx +++ b/src/views/Signup/YourDetailsForm.jsx @@ -94,6 +94,11 @@ function YourDetailsForm() { const selectedLocalityRef = useRef(); const [testConnection, setTestConnDone] = useState(false); + + const errorStatus = new URLSearchParams(window.location.search).get("status"); + + console.log(errorStatus) + useEffect(() => { setIsLoading(true); getLatestClinicId().then((res) => { @@ -209,6 +214,7 @@ function YourDetailsForm() { if (yourDetailsFormData) { defaultFormData.current = { ...yourDetailsFormData }; + console.log(yourDetailsFormData) // setLocalityOption([yourDetailsFormData?.locality]) selectedLocalityRef.current = yourDetailsFormData?.locality; }