diff --git a/.all-contributorsrc b/.all-contributorsrc index f1976eec6..202612d32 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -169,6 +169,15 @@ "contributions": [ "bug" ] + }, + { + "login": "lsprr", + "name": "Luis Parra", + "avatar_url": "https://avatars.githubusercontent.com/u/16653744?v=4", + "profile": "https://luisparra.dev", + "contributions": [ + "a11y" + ] } ] } diff --git a/.env.example b/.env.example index 7ca014a84..ddc7f8b50 100644 --- a/.env.example +++ b/.env.example @@ -47,3 +47,6 @@ MAILGUN_SENDER=postmaster@mail.yourdomain.com # Base64 encoded PFX or p12 document signing certificate file ********************************************************************************************************************* PFX_BASE64= + +# Provide Pass pharse of above PFX or p12 document +PASS_PHRASE= diff --git a/.env.local_dev b/.env.local_dev index 7d0a4a251..6059c326e 100644 --- a/.env.local_dev +++ b/.env.local_dev @@ -51,3 +51,6 @@ MAILGUN_SENDER=postmaster@mail.yourdomain.com # Base64 encoded PFX or p12 document signing certificate file ********************************************************************************************************************* PFX_BASE64= + +# Provide Pass pharse of above PFX or p12 document +PASS_PHRASE= diff --git a/README.md b/README.md index d6e5a235b..200487ed5 100644 --- a/README.md +++ b/README.md @@ -124,6 +124,7 @@ We would like to thank all our contributors and users for their support and feed Andrew
Andrew

💻 Rishab
Rishab

💻 Maurizio Pillitu
Maurizio Pillitu

🐛 + Luis Parra
Luis Parra

️️️️♿️ diff --git a/apps/OpenSign/package.json b/apps/OpenSign/package.json index 0b354b9c7..f460f70f7 100644 --- a/apps/OpenSign/package.json +++ b/apps/OpenSign/package.json @@ -40,8 +40,8 @@ "web-vitals": "^2.1.4" }, "scripts": { - "start": "mf-cra start", - "start-prod": "serve -s build", + "start": "serve -s build", + "start-dev": "mf-cra start", "build": "rm -rf public/mfbuild && npm run micro && CI=false && mf-cra build", "micro": "cd ../../microfrontends/SignDocuments && npm install && npm run build", "test": "react-scripts test", diff --git a/apps/OpenSign/src/App.js b/apps/OpenSign/src/App.js index 76ef93ee9..500e00808 100644 --- a/apps/OpenSign/src/App.js +++ b/apps/OpenSign/src/App.js @@ -14,6 +14,7 @@ import PageNotFound from "./routes/PageNotFound"; import ForgetPassword from "./routes/ForgetPassword"; import ChangePassword from "./routes/ChangePassword"; import ReportMicroapp from "./components/ReportMicroapp"; +import LoadMf from "./routes/LoadMf"; function App() { const [isloading, setIsLoading] = useState(true); @@ -59,6 +60,7 @@ function App() { } /> } /> + } /> } /> {process.env.REACT_APP_ENABLE_SUBSCRIPTION && ( <> diff --git a/apps/OpenSign/src/components/fields/FileUpload.js b/apps/OpenSign/src/components/fields/FileUpload.js index 83a37eed7..226640d44 100644 --- a/apps/OpenSign/src/components/fields/FileUpload.js +++ b/apps/OpenSign/src/components/fields/FileUpload.js @@ -82,7 +82,7 @@ const FileUpload = (props) => { progress: (progressValue, loaded, total, { type }) => { if (type === "upload" && progressValue !== null) { const percentCompleted = Math.round((loaded * 100) / total); - console.log("percentCompleted ", percentCompleted); + // console.log("percentCompleted ", percentCompleted); setpercentage(percentCompleted); } } diff --git a/apps/OpenSign/src/components/sidebar/Menu.js b/apps/OpenSign/src/components/sidebar/Menu.js new file mode 100644 index 000000000..459451b23 --- /dev/null +++ b/apps/OpenSign/src/components/sidebar/Menu.js @@ -0,0 +1,21 @@ +import React from "react"; +import { NavLink } from "react-router-dom"; + +const Menu = ({ item, isOpen, closeSidebar }) => { + return ( +
  • + + + {item.title} + +
  • + ) +} + +export default Menu; \ No newline at end of file diff --git a/apps/OpenSign/src/components/sidebar/Sidebar.js b/apps/OpenSign/src/components/sidebar/Sidebar.js index 1d0ff7ed8..85ac268ec 100644 --- a/apps/OpenSign/src/components/sidebar/Sidebar.js +++ b/apps/OpenSign/src/components/sidebar/Sidebar.js @@ -1,115 +1,77 @@ import React, { useState, useEffect } from "react"; -import { Link, NavLink } from "react-router-dom"; + +import Menu from './Menu'; import Submenu from "./SubMenu"; -import dp from "../../assets/images/dp.png"; +import SocialMedia from './SocialMedia'; + import Parse from "parse"; +import dp from "../../assets/images/dp.png"; + const Sidebar = ({ isOpen, closeSidebar }) => { + const [menuList, setmenuList] = useState([]); let username = localStorage.getItem("username"); const image = localStorage.getItem("profileImg") || dp; const tenantname = localStorage.getItem("TenantName"); - const [menuList, setmenuList] = useState([]); useEffect(() => { if (localStorage.getItem("accesstoken")) { menuItem(); } }, []); + const menuItem = async () => { const parseBaseUrl = localStorage.getItem("baseUrl"); const parseAppId = localStorage.getItem("parseAppId"); + try { Parse.serverURL = parseBaseUrl; Parse.initialize(parseAppId); var sideMenu = Parse.Object.extend("w_menu"); var query = new Parse.Query(sideMenu); query.equalTo("objectId", localStorage.getItem("defaultmenuid")); - + const results = await query.first(); const resultjson = results.toJSON(); - // console.log("resultjson ", resultjson); let result = resultjson; setmenuList(result.menuItems); } catch (e) { console.error("Problem", e); } + }; return ( -
    - img + Profile
    -

    {username && username}

    -

    - {tenantname && tenantname} +

    {username}

    +

    + {tenantname}

    -
      - {menuList.length > 0 && ( - <> - {menuList.map((item) => - !item.children ? ( -
    • closeSidebar()}> - - - - {item.title} - - -
    • - ) : ( - - {item.children.map((item) => ( -
    • closeSidebar()} className="pl-6 md:pl-8 hover:bg-[#eef1f5] cursor-pointer"> - - - - {item.title} - - -
    • - ))} -
      - ) - )} - - )} -
    -
    - - - - - - - - - - - - -
    -
    + +
    + +
    + ); }; diff --git a/apps/OpenSign/src/components/sidebar/SocialMedia.js b/apps/OpenSign/src/components/sidebar/SocialMedia.js new file mode 100644 index 000000000..813c47e15 --- /dev/null +++ b/apps/OpenSign/src/components/sidebar/SocialMedia.js @@ -0,0 +1,27 @@ +import React from "react"; +import { NavLink } from "react-router-dom"; + +const SocialMedia = () => { + return ( + + + + OpenSign's Github + + + + OpenSign's LinkedIn + + + + OpenSign's Twitter + + + + OpenSign's Discord + + + ) +} + +export default SocialMedia; \ No newline at end of file diff --git a/apps/OpenSign/src/components/sidebar/SubMenu.js b/apps/OpenSign/src/components/sidebar/SubMenu.js index 6b3d59582..3b3adac42 100644 --- a/apps/OpenSign/src/components/sidebar/SubMenu.js +++ b/apps/OpenSign/src/components/sidebar/SubMenu.js @@ -1,30 +1,52 @@ import React, { useState } from "react"; +import { NavLink } from "react-router-dom"; -const Submenu = ({ icon, title, children }) => { - const [isOpen, setIsOpen] = useState(false); +const Submenu = ({ item, closeSidebar }) => { + const [submenuOpen, setSubmenuOpen] = useState(false); + const { title, icon, children } = item; const toggleSubmenu = () => { - setIsOpen(!isOpen); + setSubmenuOpen(!submenuOpen); }; return ( -
    +
  • - {isOpen &&
      {children}
    } -
  • + {submenuOpen && ( + + )} + ); }; diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 22cea983d..4bf02a818 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -1,9 +1,37 @@ -import React from "react"; +import React, { useState, useEffect, useMemo } from "react"; import pad from "../assets/images/pad.svg"; import { useNavigate } from "react-router-dom"; -const ReportTable = ({ ReportName, List, actions }) => { +const ReportTable = ({ + ReportName, + List, + actions, + setIsNextRecord, + isMoreDocs +}) => { const navigate = useNavigate(); + const [currentPage, setCurrentPage] = useState(1); + const docPerPage = 10; + // const pageNumbers = []; + + // For loop is used to calculate page numbers visible below table + // Initialize pageNumbers using useMemo to avoid unnecessary re-creation + const pageNumbers = useMemo(() => { + const calculatedPageNumbers = []; + for (let i = 1; i <= Math.ceil(List.length / docPerPage); i++) { + calculatedPageNumbers.push(i); + } + return calculatedPageNumbers; + }, [List]); + // below useEffect reset currenpage to 1 if user change route + useEffect(() => { + return () => setCurrentPage(1); + }, []); + + // `formatRow` is used to show data in poper manner like + // if data is of array type then it will join array items with "," + // if data is of object type then it Name values will be show in row + // if no data available it will show hyphen "-" const formatRow = (row) => { if (Array.isArray(row)) { let updateArr = row.map((x) => x.Name); @@ -14,24 +42,17 @@ const ReportTable = ({ ReportName, List, actions }) => { return "-"; } }; + // below useEffect is used to render next record if IsMoreDoc is true + // second last value of pageNumber array is same as currentPage + useEffect(() => { + if (isMoreDocs && pageNumbers[pageNumbers.length - 1] === currentPage) { + setIsNextRecord(true); + } + }, [isMoreDocs, pageNumbers, currentPage, setIsNextRecord]); + + // `handlemicroapp` is used to open microapp const handlemicroapp = (item, url) => { localStorage.removeItem("rowlevel"); - // const params = new URLSearchParams(url); - // let arr = []; - // for (let [key, value] of params.entries()) { - // if (key === "remoteUrl") { - // arr.push( - // `${key}=${window.btoa( - // window.location.origin + "/mfbuild/remoteEntry.js" - // )}` - // ); - // } else { - // arr.push(`${key}=${value}`); - // } - // } - // const remoteUrl = arr.join("&"); - // console.log("arr", remoteUrl); - navigate("/rpmf/" + url); localStorage.setItem("rowlevel", JSON.stringify(item)); // localStorage.setItem("rowlevelMicro"); @@ -39,6 +60,17 @@ const ReportTable = ({ ReportName, List, actions }) => { const handlebtn = () => { console.log("clicked"); }; + + // Get current list + const indexOfLastDoc = currentPage * docPerPage; + const indexOfFirstDoc = indexOfLastDoc - docPerPage; + // `currentLists` is total record render on current page + const currentLists = List.slice(indexOfFirstDoc, indexOfLastDoc); + + // Change page + const paginateFront = () => setCurrentPage(currentPage + 1); + const paginateBack = () => setCurrentPage(currentPage - 1); + return (

    {ReportName}

    @@ -60,8 +92,8 @@ const ReportTable = ({ ReportName, List, actions }) => { {List?.length > 0 ? ( <> - {List.map((item, index) => ( - + {currentLists.map((item, index) => ( + {index + 1} {item?.Name} {item?.Note || "-"} @@ -121,8 +153,46 @@ const ReportTable = ({ ReportName, List, actions }) => { )} +
    + {List.length > 10 && ( + <> + {currentPage > 1 && ( + + )} + + )} + {pageNumbers.map((x) => ( + + ))} + {isMoreDocs && ( + + )} + {List.length > 10 && ( + <> + {pageNumbers.includes(currentPage + 1) && ( + + )} + + )} +
    {List?.length <= 0 && ( -
    +
    img
    diff --git a/apps/OpenSign/src/routes/LoadMf.js b/apps/OpenSign/src/routes/LoadMf.js new file mode 100644 index 000000000..2f8a43a1f --- /dev/null +++ b/apps/OpenSign/src/routes/LoadMf.js @@ -0,0 +1,84 @@ +import { Suspense } from "react"; +import { useParams } from "react-router-dom"; +import useFederatedComponent from "mf-cra"; +import React from "react"; +import Title from "../components/Title"; +function RemoteApp({ app }) { + // console.log("app ", app); + const { Component: RemoteComponent } = useFederatedComponent(app); + // console.log("RemoteComponent ", RemoteComponent); + return ( + <> + + <Suspense + fallback={ + <div style={{ height: "300px" }}> + <div + style={{ + marginLeft: "45%", + marginTop: "150px", + fontSize: "45px", + color: "#3dd3e0" + }} + className="loader-37" + ></div> + </div> + } + > + <div + style={{ + boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.33)", + backgroundColor: "#ffffff", + width: "100%", + overflow: "hidden", + borderRadius: 3, + minHeight: "70vh" + }} + > + {RemoteComponent && <RemoteComponent />} + </div> + </Suspense> + </> + ); +} +function checkObjectProperties(obj) { + // Check if all three properties exist in the object + if ( + obj && + obj.remoteName !== undefined && + obj.remoteUrl !== undefined && + obj.moduleToLoad !== undefined + ) { + return true; // All properties are present + } else { + return false; // At least one property is missing + } +} +export default function RemoteAppContainer() { + const { remoteApp } = useParams(); + + const obj = { + remoteUrl: window.location.origin + "/mfbuild/remoteEntry.js", + moduleToLoad: "./AppRoutes", + remoteName: remoteApp + }; + // console.log("obj ", obj); + const isMf = checkObjectProperties(obj); + if (isMf) { + const appToLoad = obj; + return <RemoteApp app={appToLoad} />; + } else { + return ( + <div className="flex items-center justify-center h-screen w-full bg-white rounded"> + <div className="text-center"> + <h1 className="text-[60px] lg:text-[120px] font-semibold text-black"> + 404 + </h1> + <p className="text-[30px] lg:text-[50px] text-black"> + Page Not Found + </p> + </div> + </div> + ); + } +} diff --git a/apps/OpenSign/src/routes/Login.js b/apps/OpenSign/src/routes/Login.js index bada3d29d..ba0d74133 100644 --- a/apps/OpenSign/src/routes/Login.js +++ b/apps/OpenSign/src/routes/Login.js @@ -744,7 +744,7 @@ function Login(props) { }; const GetLoginData = async () => { - setState({...state, loading: true}) + setState({ ...state, loading: true }) try { Parse.serverURL = localStorage.getItem("baseUrl"); Parse.initialize(localStorage.getItem("parseAppId")); @@ -925,6 +925,7 @@ function Login(props) { <Title title={"Login Page"} /> {state.loading && ( <div + aria-live="assertive" style={{ position: "fixed", width: "100vw", @@ -936,63 +937,66 @@ function Login(props) { }} > <div + role="status" style={{ position: "fixed", fontSize: "50px", color: "#3ac9d6", top: "50%", - left: "45%" + left: "50%", + transform: 'translate(-50%, -50%)' }} className="loader-37" - ></div> + > + <span className="sr-only">Loading...</span> + </div> </div> )} {props.isloginVisible && props.isloginVisible ? ( - <div> + <div aria-labelledby="loginHeading" role="region"> <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"> {state.hideNav ? ( - <img src={image} width="100%" alt="" /> + <img src={image} width="100%" alt="The image displays the OpenSign logo with a stylized blue square with an open corner, accompanied by the tagline Seal the Deal, Openly." /> ) : ( - <img src={image} width="100%" alt="" /> + <img src={image} width="100%" alt="The image displays the OpenSign logo with a stylized blue square with an open corner, accompanied by the tagline Seal the Deal, Openly." /> )} </div> <div className="grid grid-cols-1 md:grid-cols-2 gap-x-2"> - <div className=""> + <div> <div> - <form onSubmit={handleSubmit}> - <h2 className="text-[30px] mt-6">Welcome Back !</h2> - <span className="text-[12px] text-[#878787]"> - Login to your account - </span> - <div className="shadow-md rounded my-4"> + <form onSubmit={handleSubmit} aria-label="Login Form"> + <h1 className="text-[30px] mt-6">Welcome Back!</h1> + <fieldset className="outline outline-1 outline-slate-300/50 my-4"> + <legend className="text-xs font-bold px-6 mt-4">Login to your account</legend> <div className="px-6 py-4"> - <label className="block text-xs">Username</label> + <label className="block text-xs" htmlFor="email">Username</label> <input + id="email" type="text" - className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" + className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs" name="email" value={state.email} onChange={handleChange} required /> <hr className="my-2 border-none" /> - <label className="block text-xs">Password</label> + <label className="block text-xs" htmlFor="password">Password</label> <div className="relative"> <input + id="password" type={state.passwordVisible ? "text" : "password"} - className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" + className="px-3 py-2 w-full border-[1px] border-gray-300 rounded text-xs" name="password" value={state.password} onChange={handleChange} required /> <span - className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${ - state.passwordVisible - ? "text-[#007bff]" - : "text-black" - }`} + className={`absolute top-[50%] right-[10px] -translate-y-[50%] cursor-pointer ${state.passwordVisible + ? "text-[#007bff]" + : "text-black" + }`} onClick={togglePasswordVisibility} > {state.passwordVisible ? ( @@ -1003,32 +1007,32 @@ function Login(props) { </span> </div> </div> - </div> - <div className="flex flex-row justify-between items-center text-xs px-4 py-2"> + </fieldset> + <div className="flex flex-row justify-between items-center px-4 py-2"> <div> - <label className="form-check-label"> + <label className="form-check-label inline-block cursor-pointer" htmlFor="rememberpassword"> <input type="checkbox" - className="form-check-input" + className="form-check-input mr-2" value="" + id="rememberpassword" /> - <span className="text-center pl-1"> - Remember Password - </span> + <span className="text-[13px] pl-1">Remember Password</span> </label> </div> <div> - <span className="text-[13px]"> - <NavLink to={`/forgetpassword`}> - Forgot Password ? - </NavLink> - </span> + <NavLink + to="/forgetpassword" + className="text-[13px] underline focus:outline-none focus:ring-2 focus:ring-blue-600" + > + Forgot Password? + </NavLink> </div> </div> <div className="flex flex-col md:flex-row justify-between items-stretch gap-8 text-center text-xs font-bold mt-2"> <button type="submit" - className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow uppercase" + className="rounded-sm bg-[#3ac9d6] text-white w-full py-3 shadow outline-none uppercase focus:ring-2 focus:ring-blue-600" disabled={state.loading} > {state.loading ? "Loading..." : "Login"} @@ -1093,7 +1097,7 @@ function Login(props) { }} > {props.appInfo.googleClietId && - props.appInfo.googleClietId !== "" ? ( + props.appInfo.googleClietId !== "" ? ( <GoogleSignInBtn GoogleCred={props.appInfo.googleClietId} thirdpartyLoginfn={thirdpartyLoginfn} @@ -1107,14 +1111,18 @@ function Login(props) { {!state.hideNav && ( <div className="self-center"> <div className="mx-auto md:w-[300px] lg:w-[500px]"> - <img src={login_img} alt="bisec" width="100%" /> + <img src={login_img} alt="The image illustrates a person from behind, seated at a desk with a four-monitor computer setup, in an environment with a light blue and white color scheme, featuring a potted plant to the right." width="100%" /> </div> </div> )} </div> </div> - <div id="snackbar" style={{ backgroundColor: state.toastColor }}> + <div + id="snackbar" + role="alert" + style={{ backgroundColor: state.toastColor }} + > {state.toastDescription} </div> </div> diff --git a/apps/OpenSign/src/routes/PlanSubscriptions.js b/apps/OpenSign/src/routes/PlanSubscriptions.js index 61784986e..740277932 100644 --- a/apps/OpenSign/src/routes/PlanSubscriptions.js +++ b/apps/OpenSign/src/routes/PlanSubscriptions.js @@ -64,7 +64,7 @@ const PlanSubscriptions = () => { style={{ backgroundColor: "white", overflowY: "auto", - maxHeight: "600px", + maxHeight: "100%", "--theme-color": "#7952b3", "--plan-width": 30 }} diff --git a/apps/OpenSign/src/routes/Report.js b/apps/OpenSign/src/routes/Report.js index d82dc3630..efec224ac 100644 --- a/apps/OpenSign/src/routes/Report.js +++ b/apps/OpenSign/src/routes/Report.js @@ -12,14 +12,37 @@ const Report = () => { const [isLoader, setIsLoader] = useState(true); const [reportName, setReportName] = useState(""); const [actions, setActions] = useState([]); + const [isNextRecord, setIsNextRecord] = useState(false); + const [isMoreDocs, setIsMoreDocs] = useState(true); + const abortController = new AbortController(); + + // below useEffect is call when id param change useEffect(() => { setReportName(""); + setList([]); getReportData(); + + // Function returned from useEffect is called on unmount + return () => { + setIsLoader(true); + setList([]); + setIsNextRecord(false); + // Here it'll abort the fetch + abortController.abort(); + }; // eslint-disable-next-line }, [id]); - const getReportData = async () => { - setIsLoader(true); + // below useEffect call when isNextRecord state is true and fetch next record + useEffect(() => { + if (isNextRecord) { + getReportData(List.length, 200); + } + // eslint-disable-next-line + }, [isNextRecord]); + + const getReportData = async (skipUserRecord = 0, limit = 200) => { + // setIsLoader(true); const json = reportJson(id); if (json) { setActions(json.actions); @@ -39,8 +62,11 @@ const Report = () => { "X-Parse-Session-Token": localStorage.getItem("accesstoken") }; try { - const url = `${serverURL}?where=${strParams}&keys=${strKeys}&order=${orderBy}&include=AuditTrail.UserPtr`; - const res = await axios.get(url, { headers: headers }); + const url = `${serverURL}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skipUserRecord}&limit=${limit}&include=AuditTrail.UserPtr`; + const res = await axios.get(url, { + headers: headers, + signal: abortController.signal // is used to cancel fetch query + }); if (id === "4Hhwbp482K") { const listData = res.data?.results.filter( (x) => x.Signers.length > 0 @@ -66,14 +92,35 @@ const Report = () => { } } } - setList(arr); + if (arr.length === 10) { + setIsMoreDocs(true); + } else { + setIsMoreDocs(false); + } + setList((prevRecord) => + prevRecord.length > 0 ? [...prevRecord, ...arr] : arr + ); } else { - setList(res.data?.results); + if (res.data.results.length === 10) { + setIsMoreDocs(true); + console.log("here"); + } else { + setIsMoreDocs(false); + } + setIsNextRecord(false); + setList((prevRecord) => + prevRecord.length > 0 + ? [...prevRecord, ...res.data.results] + : res.data.results + ); } setIsLoader(false); } catch (err) { - console.log("err ", err); - setIsLoader(false); + const isCancel = axios.isCancel(err); + if (!isCancel) { + console.log("err ", err); + setIsLoader(false); + } } } else { setIsLoader(false); @@ -106,6 +153,8 @@ const Report = () => { ReportName={reportName} List={List} actions={actions} + setIsNextRecord={setIsNextRecord} + isMoreDocs={isMoreDocs} /> ) : ( <div className="flex items-center justify-center h-screen w-full bg-white rounded"> diff --git a/apps/OpenSign/src/routes/Signup.js b/apps/OpenSign/src/routes/Signup.js index ea3481cb7..6378e6785 100644 --- a/apps/OpenSign/src/routes/Signup.js +++ b/apps/OpenSign/src/routes/Signup.js @@ -251,7 +251,7 @@ const Signup = (props) => { element.pageType ); setState({ loading: false }); - navigate("/login"); + navigate("/"); } else { extendedInfo.forEach((x) => { if (x.TenantId) { diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/SignPDF.min.cjs b/apps/OpenSignServer/cloud/parsefunction/pdf/SignPDF.min.cjs index 509e2dd53..b84ba4c9b 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/SignPDF.min.cjs +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/SignPDF.min.cjs @@ -1 +1 @@ -const signer=require("node-signpdf").default;class SignPDF{constructor(e,t){this.pdfDoc=e,this.certificate=t}async signPDF(){return signer.sign(this.pdfDoc,this.certificate,{passphrase:"emudhra"})}static unit8ToBuffer(e){var t=Buffer.alloc(e.byteLength),r=new Uint8Array(e);for(let e=0;e<t.length;++e)t[e]=r[e];return t}}module.exports=SignPDF; \ No newline at end of file +const signer=require("node-signpdf").default;class SignPDF{constructor(e,t){this.pdfDoc=e,this.certificate=t}async signPDF(){return signer.sign(this.pdfDoc,this.certificate, process.env.PASS_PHRASE ? {passphrase : process.env.PASS_PHRASE}: null)}static unit8ToBuffer(e){var t=Buffer.alloc(e.byteLength),r=new Uint8Array(e);for(let e=0;e<t.length;++e)t[e]=r[e];return t}}module.exports=SignPDF; \ No newline at end of file diff --git a/microfrontends/SignDocuments/src/Component/PageNotFound.js b/microfrontends/SignDocuments/src/Component/PageNotFound.js new file mode 100644 index 000000000..b9e912e32 --- /dev/null +++ b/microfrontends/SignDocuments/src/Component/PageNotFound.js @@ -0,0 +1,16 @@ +import React from "react"; + +const PageNotFound = () => { + return ( + <div className="flex items-center justify-center h-screen w-full bg-white rounded"> + <div className="text-center"> + <h1 className="text-[60px] lg:text-[120px] font-semibold text-black"> + 404 + </h1> + <p className="text-[30px] lg:text-[50px] text-black">Page Not Found</p> + </div> + </div> + ); +}; + +export default PageNotFound; diff --git a/microfrontends/SignDocuments/src/Component/component/emailComponent.js b/microfrontends/SignDocuments/src/Component/component/emailComponent.js index af6ecf0e4..b9bb36752 100644 --- a/microfrontends/SignDocuments/src/Component/component/emailComponent.js +++ b/microfrontends/SignDocuments/src/Component/component/emailComponent.js @@ -109,8 +109,17 @@ function EmailComponent({ const handleToPrint = async (event) => { event.preventDefault(); - const base64 = await getBase64FromUrl(pdfUrl); - printModule({ printable: base64, type: "pdf", base64: true }); + const pdf = await getBase64FromUrl(pdfUrl); + const isAndroidDevice = navigator.userAgent.match(/Android/i); + const isAppleDevice = (/iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) && !window.MSStream + if (isAndroidDevice || isAppleDevice) { + const byteArray = Uint8Array.from(atob(pdf).split('').map(char => char.charCodeAt(0))); + const blob = new Blob([byteArray], { type: 'application/pdf' }); + const blobUrl = URL.createObjectURL(blob); + window.open(blobUrl, '_blank'); + } else { + printModule({ printable: pdf, type: "pdf", base64: true }); + } }; diff --git a/microfrontends/SignDocuments/src/Component/component/header.js b/microfrontends/SignDocuments/src/Component/component/header.js index bccdd46f2..8ab8d6994 100644 --- a/microfrontends/SignDocuments/src/Component/component/header.js +++ b/microfrontends/SignDocuments/src/Component/component/header.js @@ -60,8 +60,16 @@ function Header({ event.preventDefault(); const pdf = await getBase64FromUrl(pdfUrl); - - printModule({ printable: pdf, type: "pdf", base64: true }); + const isAndroidDevice = navigator.userAgent.match(/Android/i); + const isAppleDevice = (/iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) && !window.MSStream + if (isAndroidDevice || isAppleDevice) { + const byteArray = Uint8Array.from(atob(pdf).split('').map(char => char.charCodeAt(0))); + const blob = new Blob([byteArray], { type: 'application/pdf' }); + const blobUrl = URL.createObjectURL(blob); + window.open(blobUrl, '_blank'); + } else { + printModule({ printable: pdf, type: "pdf", base64: true }); + } }; //handle download signed pdf diff --git a/microfrontends/SignDocuments/src/Component/login.js b/microfrontends/SignDocuments/src/Component/login.js index 9995e1970..1636beff7 100644 --- a/microfrontends/SignDocuments/src/Component/login.js +++ b/microfrontends/SignDocuments/src/Component/login.js @@ -116,7 +116,8 @@ function Login() { localStorage.setItem("accesstoken", _user.sessionToken); setLoading(false); //navigate user to on signature page - navigate(`/recipientSignPdf/${id}/${contactBookId}`); + // navigate(`/recipientSignPdf/${id}/${contactBookId}`); + navigate(`/loadmf/signmicroapp/recipientSignPdf/${id}/${contactBookId}`); } } catch (error) {} } else { diff --git a/microfrontends/SignDocuments/src/Component/placeHolderSign.js b/microfrontends/SignDocuments/src/Component/placeHolderSign.js index 80ef92e27..39db3656b 100644 --- a/microfrontends/SignDocuments/src/Component/placeHolderSign.js +++ b/microfrontends/SignDocuments/src/Component/placeHolderSign.js @@ -166,7 +166,7 @@ function PlaceHolderSign() { getDocumentDetails(); } }, []); - + //function for get document details const getDocumentDetails = async () => { await axios @@ -275,7 +275,7 @@ function PlaceHolderSign() { getSignerPos(item, monitor); } }; - + const getSignerPos = (item, monitor) => { const key = Math.floor(1000 + Math.random() * 9000); let filterSignerPos = signerPos.filter( @@ -396,7 +396,7 @@ function PlaceHolderSign() { blockColor: color[isSelectListId], placeHolder: xyPosArr }; - + setSignerPos((prev) => [...prev, placeHolderPos]); } }; @@ -626,7 +626,7 @@ function PlaceHolderSign() { setSignBtnPosition([]); setPageNumber((prevPageNumber) => prevPageNumber + offset); } - + //function for capture position on hover signature button const handleDivClick = (e) => { const divRect = e.currentTarget.getBoundingClientRect(); @@ -679,7 +679,7 @@ function PlaceHolderSign() { year: "numeric" }); let sender = signersdata.ExtUserPtr.Email; - const signerMail = signersdata.Signers; + const signerMail = signersdata.Signers; for (let i = 0; i < signerMail.length; i++) { try { @@ -698,7 +698,8 @@ function PlaceHolderSign() { "parseAppId" )}&${localStorage.getItem("_appName")}`; - let signPdf = `https://qik-ai-org.github.io/Sign-MicroappV2/#/login/${signersdata.objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`; + const hostUrl = window.location.origin + "/loadmf/signmicroapp"; + let signPdf = `${hostUrl}/login/${signersdata.objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`; const themeBGcolor = themeColor(); let params = { diff --git a/microfrontends/SignDocuments/src/Routes.js b/microfrontends/SignDocuments/src/Routes.js index e9883138b..605212871 100644 --- a/microfrontends/SignDocuments/src/Routes.js +++ b/microfrontends/SignDocuments/src/Routes.js @@ -8,6 +8,7 @@ import Login from "./Component/login"; import DraftDocument from "./Component/DraftDocument"; import PdfRequestFiles from "./Component/PdfRequestFiles"; import LegaDrive from "./Component/LegaDrive/LegaDrive"; +import PageNotFound from "./Component/PageNotFound"; // `AppRoutes` is used to define route path of app and // it expose to host app, check moduleFederation.config.js for more @@ -33,7 +34,10 @@ function AppRoutes() { {/*Add default signature of user route */} <Route path="/managesign" element={<ManageSign />} /> {/* login page route */} - <Route path="/login/:id/:userMail/:contactBookId/:serverUrl" element={<Login />} /> + <Route + path="/login/:id/:userMail/:contactBookId/:serverUrl" + element={<Login />} + /> {/* draft document route to handle and navigate route page accordiing to document status */} <Route path="/draftDocument" element={<DraftDocument />} /> {/* for user signature (need your sign route) with row level data */} @@ -42,6 +46,8 @@ function AppRoutes() { <Route path="/pdfRequestFiles/:docId" element={<PdfRequestFiles />} /> {/* lega drive route */} <Route path="/legadrive" element={<LegaDrive />} /> + {/* Page Not Found */} + <Route path="*" element={<PageNotFound />} /> </Routes> </div> );