mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
@@ -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=
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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() {
|
||||
<Routes>
|
||||
<Route exact path="/" element={<Login />} />
|
||||
<Route exact path="/signup" element={<Signup />} />
|
||||
<Route exact path="/loadmf/:remoteApp/*" element={<LoadMf />} />
|
||||
<Route exact path="/forgetpassword" element={<ForgetPassword />} />
|
||||
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
|
||||
<>
|
||||
|
||||
@@ -93,7 +93,7 @@ const ReportTable = ({
|
||||
{List?.length > 0 ? (
|
||||
<>
|
||||
{currentLists.map((item, index) => (
|
||||
<tr className="border-t-[1px]" key={index}>
|
||||
<tr className="border-y-[1px]" key={index}>
|
||||
<td className="px-4 py-2">{index + 1}</td>
|
||||
<td className="px-4 py-2 font-semibold">{item?.Name} </td>
|
||||
<td className="px-4 py-2">{item?.Note || "-"}</td>
|
||||
@@ -153,7 +153,7 @@ const ReportTable = ({
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="flex flex-wrap items-center gap-2 p-2 border-t-[1px]">
|
||||
<div className="flex flex-wrap items-center gap-2 p-2 ">
|
||||
{List.length > 10 && (
|
||||
<>
|
||||
{currentPage > 1 && (
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<Title title={app.remoteName} />
|
||||
<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>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -64,7 +64,7 @@ const PlanSubscriptions = () => {
|
||||
style={{
|
||||
backgroundColor: "white",
|
||||
overflowY: "auto",
|
||||
maxHeight: "600px",
|
||||
maxHeight: "100%",
|
||||
"--theme-color": "#7952b3",
|
||||
"--plan-width": 30
|
||||
}}
|
||||
|
||||
@@ -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;
|
||||
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;
|
||||
@@ -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;
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user