diff --git a/apps/OpenSign/src/components/opensigndrive/DriveBody.js b/apps/OpenSign/src/components/opensigndrive/DriveBody.js index 213d3eac4..1a40f878b 100644 --- a/apps/OpenSign/src/components/opensigndrive/DriveBody.js +++ b/apps/OpenSign/src/components/opensigndrive/DriveBody.js @@ -385,6 +385,7 @@ function DriveBody(props) { flexDirection: "column", cursor: "pointer" }} + data-tut={props.dataTutSeventh} > no img checkPdfStatus(data)} + data-tut={props.dataTutSixth} /> {rename === data.objectId ? ( { e.stopPropagation(); diff --git a/apps/OpenSign/src/json/ReportJson.js b/apps/OpenSign/src/json/ReportJson.js index 813ba0f80..6e92edce5 100644 --- a/apps/OpenSign/src/json/ReportJson.js +++ b/apps/OpenSign/src/json/ReportJson.js @@ -19,7 +19,8 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "These are documents you have started but have not finalized for sending." + helpMsg: + "These are documents you have started but have not finalized for sending." }; // Need your sign report case "4Hhwbp482K": @@ -69,7 +70,8 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "This is a list of documents that have been signed by all parties." + helpMsg: + "This is a list of documents that have been signed by all parties." }; // declined documents report case "UPr2Fm5WY3": @@ -85,7 +87,8 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "This is a list of documents that have been declined by one of the signers." + helpMsg: + "This is a list of documents that have been declined by one of the signers." }; // Expired Documents report case "zNqBHXHsYH": @@ -101,7 +104,8 @@ export default function reportJson(id) { redirectUrl: "draftDocument" } ], - helpMsg: "This is a list of documents that have reached their expiration date." + helpMsg: + "This is a list of documents that have reached their expiration date." }; // Recently sent for signatures report show on dashboard case "d9k3UfYHBc": @@ -162,7 +166,8 @@ export default function reportJson(id) { } ], form: "ContactBook", - helpMsg: "This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document." + helpMsg: + "This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document." }; // template report case "6TeaPr321t": @@ -175,16 +180,23 @@ export default function reportJson(id) { btnColor: "#4bd396", textColor: "white", btnIcon: "fa fa-plus", - redirectUrl: "placeHolderSign" + redirectUrl: "placeHolderSign", + selector: "reactourSecond", + message: + "Click the ‘Use’ button to create a new document from an existing template." }, { btnLabel: "Edit", btnColor: "#00c9d5", textColor: "white", btnIcon: "fa fa-plus", - redirectUrl: "template" + redirectUrl: "template", + selector: "reactourThird", + message: + "Use the ‘Edit’ button to add signer roles, modify fields, and update your template. Changes will apply to all future documents created from this template but won’t affect existing documents." } ], + helpMsg: "This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step." }; diff --git a/apps/OpenSign/src/pages/Login.js b/apps/OpenSign/src/pages/Login.js index 9a7501545..0ffce338c 100644 --- a/apps/OpenSign/src/pages/Login.js +++ b/apps/OpenSign/src/pages/Login.js @@ -1072,11 +1072,13 @@ function Login() {
- The image displays the OpenSign logo with a stylized blue square with an open corner, accompanied by the tagline Seal the Deal, Openly. + {image && ( + The image displays the OpenSign logo with a stylized blue square with an open corner, accompanied by the tagline Seal the Deal, Openly. + )}
diff --git a/apps/OpenSign/src/pages/Opensigndrive.js b/apps/OpenSign/src/pages/Opensigndrive.js index 8c248f6cf..c405358e1 100644 --- a/apps/OpenSign/src/pages/Opensigndrive.js +++ b/apps/OpenSign/src/pages/Opensigndrive.js @@ -7,6 +7,10 @@ import { useNavigate } from "react-router-dom"; import Title from "../components/Title"; import Parse from "parse"; import ModalUi from "../primitives/ModalUi"; +import TourContentWithBtn from "../primitives/TourContentWithBtn"; +import Tour from "reactour"; +import axios from "axios"; + const DriveBody = React.lazy( () => import("../components/opensigndrive/DriveBody") ); @@ -42,6 +46,8 @@ function Opensigndrive() { const [error, setError] = useState(); const [folderLoader, setIsFolderLoader] = useState(false); const [isShowSort, setIsShowSort] = useState(false); + const [isTour, setIsTour] = useState(false); + const [tourStatusArr, setTourStatusArr] = useState([]); const [isLoading, setIsLoading] = useState({ isLoad: true, message: "This might take some time" @@ -56,6 +62,8 @@ function Opensigndrive() { const [loading, setLoading] = useState(false); const sortOrder = ["Ascending", "Descending"]; const sortingValue = ["Name", "Date"]; + const [isDontShow, setIsDontShow] = useState(false); + const [tourData, setTourData] = useState(); const orderName = { Ascending: "Ascending", Descending: "Descending", @@ -73,12 +81,94 @@ function Opensigndrive() { useEffect(() => { getPdfDocumentList(); + // eslint-disable-next-line }, [docId]); + const tourConfigs = [ + { + selector: '[data-tut="reactourFirst"]', + content: () => ( + + ), + position: "top", + style: { fontSize: "13px" } + }, + { + selector: '[data-tut="reactourSecond"]', + content: () => ( + + ), + position: "top", + style: { fontSize: "13px" } + }, + { + selector: '[data-tut="reactourThird"]', + content: () => ( + + ), + position: "top", + style: { fontSize: "13px" } + }, + { + selector: '[data-tut="reactourForth"]', + content: () => ( + + ), + position: "top", + style: { fontSize: "13px" } + }, + + { + selector: '[data-tut="reactourFifth"]', + content: () => ( + + ), + position: "bottom", + style: { fontSize: "13px" } + }, + { + selector: '[data-tut="reactourSixth"]', + content: () => ( + + ), + position: "bottom", + style: { fontSize: "13px" } + }, + { + selector: '[data-tut="reactourSeventh"]', + content: () => ( + + ), + position: "bottom", + style: { fontSize: "13px" } + } + ]; //function for get all pdf document list const getPdfDocumentList = async (disbaleLoading) => { setLoading(true); + checkTourStatus(); if (!disbaleLoading) { setIsLoading({ isLoad: true, message: "This might take some time" }); } @@ -87,6 +177,22 @@ function Opensigndrive() { if (driveDetails && driveDetails === "Error: Something went wrong!") { setHandleError("Error: Something went wrong!"); } else if (driveDetails && driveDetails.length > 0) { + let newTour = tourConfigs; + const isFolderExist = driveDetails.some( + (data) => data.Type === "Folder" + ); + if (!isFolderExist) { + newTour = newTour.filter( + (data) => data.selector !== '[data-tut="reactourSeventh"]' + ); + } + const isDocumentExist = driveDetails.some((data) => data.URL); + if (!isDocumentExist) { + newTour = newTour.filter( + (data) => data.selector !== '[data-tut="reactourSixth"]' + ); + } + setTourData(newTour); setSkip((prevSkip) => prevSkip + limit); sortingData(null, null, driveDetails, true); } @@ -358,9 +464,78 @@ function Opensigndrive() { setNewFolderName(""); setError(""); }; + + const handleDontShow = (isChecked) => { + setIsDontShow(isChecked); + }; + + const closeTour = async () => { + setIsTour(false); + if (isDontShow) { + const serverUrl = localStorage.getItem("baseUrl"); + const appId = localStorage.getItem("parseAppId"); + const extUserClass = localStorage.getItem("extended_class"); + const json = JSON.parse(localStorage.getItem("Extand_Class")); + const extUserId = json && json.length > 0 && json[0].objectId; + let updatedTourStatus = []; + if (tourStatusArr.length > 0) { + updatedTourStatus = [...tourStatusArr]; + const opensignTourIndex = tourStatusArr.findIndex( + (obj) => obj["opensignTour"] === false || obj["opensignTour"] === true + ); + if (opensignTourIndex !== -1) { + updatedTourStatus[opensignTourIndex] = { opensignTour: true }; + } else { + updatedTourStatus.push({ opensignTour: true }); + } + } else { + updatedTourStatus = [{ opensignTour: true }]; + } + await axios.put( + serverUrl + "classes/" + extUserClass + "/" + extUserId, + { + TourStatus: updatedTourStatus + }, + { + headers: { + "X-Parse-Application-Id": appId + } + } + ); + } + }; + //function to use check tour status of open sign drive + async function checkTourStatus() { + const currentUser = Parse.User.current(); + const cloudRes = await Parse.Cloud.run("getUserDetails", { + email: currentUser.get("email") + }); + const res = { data: cloudRes.toJSON() }; + if (res.data && res.data.TourStatus && res.data.TourStatus.length > 0) { + const tourStatus = res.data.TourStatus; + setTourStatusArr(tourStatus); + const filteredtourStatus = tourStatus.filter( + (obj) => obj["opensignTour"] + ); + if (filteredtourStatus.length > 0) { + const opensignTour = filteredtourStatus[0]["opensignTour"]; + // console.log("loginTour", loginTour); + if (opensignTour) { + setIsTour(false); + } else { + setIsTour(true); + } + } else { + setIsTour(true); + } + } else { + setIsTour(true); + } + } return (
+ <div> <ModalUi headerColor={"#dc3545"} @@ -523,7 +698,16 @@ function Opensigndrive() { ) : ( <> <div className="folderContainer"> + <Tour + onRequestClose={closeTour} + steps={tourData} + isOpen={isTour} + closeWithMask={false} + scrollOffset={-100} + rounded={5} + /> <div + data-tut="reactourFirst" onMouseEnter={(e) => handleMouseEnter(e)} ref={scrollRef} style={{ @@ -541,7 +725,7 @@ function Opensigndrive() { } onClick={() => setIsNewFol(!isNewFol)} > - <div className="sort"> + <div className="sort" data-tut="reactourSecond"> <i className="fa fa-plus-square" aria-hidden="true" @@ -602,7 +786,11 @@ function Opensigndrive() { className={isShowSort ? "dropdown show" : "dropdown"} onClick={() => setIsShowSort(!isShowSort)} > - <div className=" sort " data-toggle="dropdown"> + <div + data-tut="reactourThird" + className=" sort " + data-toggle="dropdown" + > <i className="fa fa-sort-amount-asc" aria-hidden="true" @@ -692,7 +880,11 @@ function Opensigndrive() { ></i> </div> ) : ( - <div className="sort" onClick={() => setIsList(!isList)}> + <div + data-tut="reactourForth" + className="sort" + onClick={() => setIsList(!isList)} + > <i className="fa fa-list" aria-hidden="true" @@ -717,24 +909,28 @@ function Opensigndrive() { <span style={{ fontWeight: "bold" }}>No Data Found!</span> </div> ) : ( - <React.Suspense fallback={<Loader />}> - <DriveBody - pdfData={pdfData} - setFolderName={setFolderName} - setIsLoading={setIsLoading} - setDocId={setDocId} - getPdfDocumentList={getPdfDocumentList} - isDocId={docId} - setPdfData={setPdfData} - isList={isList} - setIsAlert={setIsAlert} - setSkip={setSkip} - sortingData={sortingData} - /> - {loading && ( - <div style={{ textAlign: "center" }}>Loading...</div> - )} - </React.Suspense> + <div data-tut="reactourFifth"> + <React.Suspense fallback={<Loader />}> + <DriveBody + dataTutSixth="reactourSixth" + dataTutSeventh="reactourSeventh" + pdfData={pdfData} + setFolderName={setFolderName} + setIsLoading={setIsLoading} + setDocId={setDocId} + getPdfDocumentList={getPdfDocumentList} + isDocId={docId} + setPdfData={setPdfData} + isList={isList} + setIsAlert={setIsAlert} + setSkip={setSkip} + sortingData={sortingData} + /> + {loading && ( + <div style={{ textAlign: "center" }}>Loading...</div> + )} + </React.Suspense> + </div> )} </> )} diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index cac8d35cd..59cae9f50 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -853,9 +853,10 @@ function PlaceHolderSign() { }; setIsSendAlert(alert); } else if (filterPrefill.length === signersdata.length) { - const IsSignerNotExist = filterPrefill?.some((x) => !x.signerObjId); - if (IsSignerNotExist) { + const IsSignerNotExist = filterPrefill?.filter((x) => !x.signerObjId); + if (IsSignerNotExist && IsSignerNotExist?.length > 0) { setSignerExistModal(true); + setSelectWidgetId(IsSignerNotExist[0]?.placeHolder?.[0]?.pos?.[0]?.key); } else { saveDocumentDetails(); } @@ -997,7 +998,6 @@ function PlaceHolderSign() { </span> <RWebShare data={{ - // text: "Like humans, flamingos make friends for life", url: data.url, title: "Sign url" }} @@ -1220,22 +1220,12 @@ function PlaceHolderSign() { position: "top", style: { fontSize: "13px" } }, - { - selector: '[data-tut="reactourLinkUser"]', - content: () => ( - <TourContentWithBtn - message={`Use this icon to assign a new signer or change the existing one for the placeholder.`} - isChecked={handleDontShow} - /> - ), - position: "top", - style: { fontSize: "13px" } - }, + { selector: '[data-tut="reactourFour"]', content: () => ( <TourContentWithBtn - message={`Clicking "Send" button will share the document with all the recipients.It will also send out emails to everyone on the recipients list.`} + message={`Clicking "Send" will save the document. In the next step you can customize the emails to be sent out to the recipients or copy the signing links and share those with the recipients yourself.`} isChecked={handleDontShow} /> ), @@ -1549,6 +1539,15 @@ function PlaceHolderSign() { } }; + const signerAssignTour = [ + { + selector: '[data-tut="assignSigner"]', + content: + " Please assign a new signer to use this icon for the placeholder. ", + position: "top", + style: { fontSize: "13px" } + } + ]; return ( <> <Title title={state?.title ? state.title : "New Document"} /> @@ -1598,6 +1597,13 @@ function PlaceHolderSign() { closeWithMask={false} /> )} + <Tour + onRequestClose={() => setSignerExistModal(false)} + steps={signerAssignTour} + isOpen={signerExistModal} + rounded={5} + closeWithMask={false} + /> {/* this component used to render all pdf pages in left side */} <RenderAllPdfPage signPdfUrl={pdfDetails[0].URL} @@ -2029,7 +2035,7 @@ function PlaceHolderSign() { )} </DndProvider> <div> - <ModalUi + {/* <ModalUi headerColor={"#dc3545"} isOpen={signerExistModal} title={"Users required"} @@ -2057,7 +2063,7 @@ function PlaceHolderSign() { Close </button> </div> - </ModalUi> + </ModalUi> */} <ModalUi headerColor={"#dc3545"} isOpen={isAlreadyPlace.status} diff --git a/apps/OpenSign/src/pages/Report.js b/apps/OpenSign/src/pages/Report.js index 674591359..7d2d0fac1 100644 --- a/apps/OpenSign/src/pages/Report.js +++ b/apps/OpenSign/src/pages/Report.js @@ -6,6 +6,7 @@ import reportJson from "../json/ReportJson"; import { useParams } from "react-router-dom"; import Title from "../components/Title"; import PageNotFound from "./PageNotFound"; +import TourContentWithBtn from "../primitives/TourContentWithBtn"; const Report = () => { const { id } = useParams(); @@ -18,6 +19,8 @@ const Report = () => { const [isNextRecord, setIsNextRecord] = useState(false); const [isMoreDocs, setIsMoreDocs] = useState(true); const [form, setForm] = useState(""); + const [tourData, setTourData] = useState([]); + const [isDontShow, setIsDontShow] = useState(false); const abortController = new AbortController(); const docPerPage = 10; @@ -46,10 +49,46 @@ const Report = () => { // eslint-disable-next-line }, [isNextRecord]); + const handleDontShow = (isChecked) => { + setIsDontShow(isChecked); + }; const getReportData = async (skipUserRecord = 0, limit = 200) => { // setIsLoader(true); const json = reportJson(id); if (json) { + if (id === "6TeaPr321t") { + const tourConfig = [ + { + selector: "[data-tut=reactourFirst]", + content: () => ( + <TourContentWithBtn + message={ + "Click the ‘Add’ button to create a new template. Templates are reusable documents designed to quickly generate new documents with the same structure and varying signers. For example, an HR template for onboarding could have predefined roles like ‘HR Manager’ and ‘New Employee’. Each time you use the template, you can assign the ‘New Employee’ role to different incoming staff members, while the ‘HR Manager’ role remains constant, facilitating a seamless onboarding process for each recruit." + } + isChecked={handleDontShow} + /> + ), + position: "top", + style: { fontSize: "13px" } + } + ]; + json.actions.map((data) => { + const newConfig = { + selector: `[data-tut="${data?.selector}"]`, + content: () => ( + <TourContentWithBtn + message={data?.message} + isChecked={handleDontShow} + /> + ), + position: "top", + style: { fontSize: "13px" } + }; + tourConfig.push(newConfig); + }); + setTourData(tourConfig); + } + setActions(json.actions); setHeading(json.heading); setReportName(json.reportName); @@ -161,6 +200,8 @@ const Report = () => { docPerPage={docPerPage} form={form} report_help={reporthelp} + tourData={tourData} + isDontShow={isDontShow} /> ) : ( <PageNotFound prefix={"Report"} /> diff --git a/apps/OpenSign/src/pages/Signup.js b/apps/OpenSign/src/pages/Signup.js index 5443f6ecf..ec8f8461a 100644 --- a/apps/OpenSign/src/pages/Signup.js +++ b/apps/OpenSign/src/pages/Signup.js @@ -533,7 +533,9 @@ const Signup = () => { <div> <div className="md:m-10 lg:m-16 md:p-4 lg:p-10 p-5 bg-[#ffffff] md:border-[1px] md:border-gray-400 "> <div className="w-[250px] h-[66px] inline-block overflow-hidden"> - <img src={image} className="object-contain h-full" alt="logo" /> + {image && ( + <img src={image} className="object-contain h-full" alt="logo" /> + )} </div> <div className="grid grid-cols-1 md:grid-cols-2 gap-x-2"> <div className=""> diff --git a/apps/OpenSign/src/pages/TemplatePlaceholder.js b/apps/OpenSign/src/pages/TemplatePlaceholder.js index 67118f40f..2b54f2aab 100644 --- a/apps/OpenSign/src/pages/TemplatePlaceholder.js +++ b/apps/OpenSign/src/pages/TemplatePlaceholder.js @@ -765,7 +765,7 @@ const TemplatePlaceholder = () => { selector: '[data-tut="reactourSecond"]', content: () => ( <TourContentWithBtn - message={`Drag a widget placeholder onto the PDF to choose your desired signing location.`} + message={`Drag or click on a field to add it to the document.`} isChecked={handleDontShow} /> ), @@ -787,7 +787,7 @@ const TemplatePlaceholder = () => { selector: '[data-tut="reactourFour"]', content: () => ( <TourContentWithBtn - message={`Clicking "Save" button will save the template and will ask you if you want to create a new document using this template.`} + message={`Clicking ‘Save’ will store the current template. After saving, you’ll be prompted to create a new document from this template if you wish.`} isChecked={handleDontShow} /> ), diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 69aee4102..0ea1b96c5 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -3,10 +3,12 @@ import pad from "../assets/images/pad.svg"; import { useNavigate } from "react-router-dom"; import axios from "axios"; import ModalUi from "./ModalUi"; +import Tour from "reactour"; import AddSigner from "../components/AddSigner"; import { modalSubmitBtnColor, modalCancelBtnColor } from "../constant/const"; import Alert from "./Alert"; import Tooltip from "./Tooltip"; +import Parse from "parse"; const ReportTable = ({ ReportName, List, @@ -17,7 +19,9 @@ const ReportTable = ({ isMoreDocs, docPerPage, form, - report_help + report_help, + tourData, + isDontShow }) => { const navigate = useNavigate(); const [currentPage, setCurrentPage] = useState(1); @@ -27,6 +31,8 @@ const ReportTable = ({ const [isDocErr, setIsDocErr] = useState(false); const [isContactform, setIsContactform] = useState(false); const [isDeleteModal, setIsDeleteModal] = useState({}); + const [isTour, setIsTour] = useState(false); + const [tourStatusArr, setTourStatusArr] = useState([]); const startIndex = (currentPage - 1) * docPerPage; // For loop is used to calculate page numbers visible below table @@ -40,6 +46,7 @@ const ReportTable = ({ }, [List, docPerPage]); // below useEffect reset currenpage to 1 if user change route useEffect(() => { + checkTourStatus(); return () => setCurrentPage(1); }, []); @@ -146,8 +153,6 @@ const ReportTable = ({ } } ); - - // console.log("Res ", res.data); if (res.data && res.data.objectId) { setActLoader({}); setIsAlert(true); @@ -247,6 +252,74 @@ const ReportTable = ({ }; const handleCloseDeleteModal = () => setIsDeleteModal({}); + async function checkTourStatus() { + const currentUser = Parse.User.current(); + const cloudRes = await Parse.Cloud.run("getUserDetails", { + email: currentUser.get("email") + }); + const res = { data: cloudRes.toJSON() }; + if (res.data && res.data.TourStatus && res.data.TourStatus.length > 0) { + const tourStatus = res.data.TourStatus; + // console.log("res ", res.data.TourStatus); + setTourStatusArr(tourStatus); + const filteredtourStatus = tourStatus.filter( + (obj) => obj["templateTour"] + ); + if (filteredtourStatus.length > 0) { + const templateTour = filteredtourStatus[0]["templateTour"]; + + if (templateTour) { + setIsTour(false); + } else { + setIsTour(true); + } + } else { + setIsTour(true); + } + } else { + setIsTour(true); + } + } + + const closeTour = async () => { + // console.log("closeTour"); + setIsTour(false); + if (isDontShow) { + const serverUrl = localStorage.getItem("baseUrl"); + const appId = localStorage.getItem("parseAppId"); + const extUserClass = localStorage.getItem("extended_class"); + const json = JSON.parse(localStorage.getItem("Extand_Class")); + const extUserId = json && json.length > 0 && json[0].objectId; + // console.log("extUserId ", extUserId) + + let updatedTourStatus = []; + if (tourStatusArr.length > 0) { + updatedTourStatus = [...tourStatusArr]; + const templateTourIndex = tourStatusArr.findIndex( + (obj) => obj["templateTour"] === false || obj["templateTour"] === true + ); + if (templateTourIndex !== -1) { + updatedTourStatus[templateTourIndex] = { templateTour: true }; + } else { + updatedTourStatus.push({ templateTour: true }); + } + } else { + updatedTourStatus = [{ templateTour: true }]; + } + + await axios.put( + serverUrl + "classes/" + extUserClass + "/" + extUserId, + { + TourStatus: updatedTourStatus + }, + { + headers: { + "X-Parse-Application-Id": appId + } + } + ); + } + }; return ( <div className="p-2 overflow-x-scroll w-full bg-white rounded-md"> {isAlert && ( @@ -256,6 +329,16 @@ const ReportTable = ({ : "Record deleted successfully!"} </Alert> )} + {tourData && ReportName === "Templates" && ( + <Tour + onRequestClose={closeTour} + steps={tourData} + isOpen={isTour} + // rounded={5} + closeWithMask={false} + /> + )} + <div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]"> <div className="font-light"> {ReportName}{" "} @@ -267,6 +350,7 @@ const ReportTable = ({ </div> {ReportName === "Templates" && ( <i + data-tut="reactourFirst" onClick={() => navigate("/form/template")} className="fa-solid fa-square-plus text-sky-400 text-[25px]" ></i> @@ -280,6 +364,7 @@ const ReportTable = ({ </div> )} </div> + <table className="table-auto w-full border-collapse"> <thead className="text-[14px]"> <tr className="border-y-[1px]"> @@ -410,6 +495,7 @@ const ReportTable = ({ {actions?.length > 0 && actions.map((act, index) => ( <button + data-tut={act?.selector} key={index} onClick={() => act?.redirectUrl