Merge pull request #402 from OpenSignLabs/staging

v1.2.0-beta
This commit is contained in:
Amol
2024-02-16 19:57:43 +05:30
committed by GitHub
99 changed files with 9209 additions and 8895 deletions
+2 -1
View File
@@ -38,10 +38,11 @@
7. [Acknowledgments](#acknowledgments)
---
Please star ⭐ the repo to support us! 😀
### Introduction
Welcome to OpenSign, an open-source document e-signing solution designed to provide a secure, reliable, and free alternative to commercial platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Developed under the OpenSignLabs organization, our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
Welcome to OpenSign, the premier open-source docusign alternative - document e-signing solution designed to provide a secure, reliable, and free alternative to commercial platforms like DocuSign, PandaDoc, SignNow, Adobe Sign, Smartwaiver, SignRequest, HelloSign & Zoho sign. Developed under the OpenSignLabs organization, our mission is to democratize the e-signing process, making it accessible and straightforward for everyone.
---
-4
View File
@@ -20,10 +20,6 @@ module.exports = {
"react-router-dom": {
singleton: true,
requiredVersion: dependencies["react-router-dom"]
},
"rjsf-conditionals": {
singleton: true,
requiredVersion: dependencies["rjsf-conditionals"]
}
}
};
-206
View File
@@ -9,9 +9,6 @@
"version": "0.1.0",
"dependencies": {
"@reduxjs/toolkit": "^1.9.7",
"@rjsf/core": "^5.13.0",
"@rjsf/utils": "^5.13.0",
"@rjsf/validator-ajv8": "^5.13.0",
"axios": "^1.5.1",
"daisyui": "^3.9.2",
"file-saver": "^2.0.5",
@@ -39,7 +36,6 @@
"reactour": "^1.19.2",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"rjsf-conditionals": "github:prafullnavkar-nxg/rjsf-conditionals",
"serve": "^14.2.1",
"styled-components": "^4.4.1",
"web-vitals": "^2.1.4"
@@ -3921,85 +3917,6 @@
"react": ">=16.14.0"
}
},
"node_modules/@rjsf/core": {
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.13.0.tgz",
"integrity": "sha512-rCpJGR0yPP/ip9LKcr3SmDMkbLx4QIaRA+ag0rcalSw1XLXBSzh53SpfgaB2HN++1xhUvWtIUERRHpWjQp1E7w==",
"dependencies": {
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"markdown-to-jsx": "^7.3.2",
"nanoid": "^3.3.6",
"prop-types": "^15.8.1"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"@rjsf/utils": "^5.12.x",
"react": "^16.14.0 || >=17"
}
},
"node_modules/@rjsf/utils": {
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.13.0.tgz",
"integrity": "sha512-tG2OuOJUJZ0W7VMZceD0I2SOjfMRRT1tRtG+SKbdNqhtH/gpg40aOMUj9cWgSQnYISEkNZjZq/z7NWln5RxW6A==",
"dependencies": {
"json-schema-merge-allof": "^0.8.1",
"jsonpointer": "^5.0.1",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"react-is": "^18.2.0"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"react": "^16.14.0 || >=17"
}
},
"node_modules/@rjsf/utils/node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
},
"node_modules/@rjsf/validator-ajv8": {
"version": "5.13.0",
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.13.0.tgz",
"integrity": "sha512-8j0xLsxJA/k1UADeDYZ2aMVrswvOCEYNC++YchoAgWRHqDiaGAUyRbbk7oxMi6QUXnhnlCIepzNeTclHnSfPXQ==",
"dependencies": {
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"@rjsf/utils": "^5.12.x"
}
},
"node_modules/@rjsf/validator-ajv8/node_modules/ajv": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@rjsf/validator-ajv8/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/@rollup/plugin-babel": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
@@ -6965,27 +6882,6 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/compute-gcd": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/compute-gcd/-/compute-gcd-1.2.1.tgz",
"integrity": "sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==",
"dependencies": {
"validate.io-array": "^1.0.3",
"validate.io-function": "^1.0.2",
"validate.io-integer-array": "^1.0.0"
}
},
"node_modules/compute-lcm": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/compute-lcm/-/compute-lcm-1.1.2.tgz",
"integrity": "sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==",
"dependencies": {
"compute-gcd": "^1.2.1",
"validate.io-array": "^1.0.3",
"validate.io-function": "^1.0.2",
"validate.io-integer-array": "^1.0.0"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -7676,14 +7572,6 @@
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
},
"node_modules/deepcopy": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/deepcopy/-/deepcopy-2.1.0.tgz",
"integrity": "sha512-8cZeTb1ZKC3bdSCP6XOM1IsTczIO73fdqtwa2B0N15eAz7gmyhQo+mc5gnFuulsgN3vIQYmTgbmQVKalH1dKvQ==",
"dependencies": {
"type-detect": "^4.0.8"
}
},
"node_modules/deepmerge": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
@@ -13418,27 +13306,6 @@
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
"integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="
},
"node_modules/json-schema-compare": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz",
"integrity": "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==",
"dependencies": {
"lodash": "^4.17.4"
}
},
"node_modules/json-schema-merge-allof": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz",
"integrity": "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==",
"dependencies": {
"compute-lcm": "^1.1.2",
"json-schema-compare": "^0.2.2",
"lodash": "^4.17.20"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
@@ -13934,21 +13801,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
},
"node_modules/lodash.isequalwith": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz",
"integrity": "sha512-dcZON0IalGBpRmJBmMkaoV7d3I80R2O+FrzsZyHdNSFrANq/cgDqKQNmAHE8UEj4+QYWwwhkQOVdLHiAopzlsQ=="
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
@@ -14310,17 +14167,6 @@
"tmpl": "1.0.5"
}
},
"node_modules/markdown-to-jsx": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.3.2.tgz",
"integrity": "sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q==",
"engines": {
"node": ">= 10"
},
"peerDependencies": {
"react": ">= 0.14.0"
}
},
"node_modules/mdn-data": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
@@ -18202,26 +18048,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/rjsf-conditionals": {
"version": "1.5.0",
"resolved": "git+ssh://git@github.com/prafullnavkar-nxg/rjsf-conditionals.git#439aa44e3ece077b1c8098278f86f454ad7185cf",
"license": "Apache-2.0",
"dependencies": {
"@rjsf/validator-ajv8": "^5.13.0",
"deepcopy": "^2.0.0",
"lodash.isequalwith": "^4.4.0",
"selectn": "^1.1.2"
},
"engines": {
"node": ">=10"
},
"peerDependencies": {
"@rjsf/core": ">=2.0.0",
"core-js": "^2.5.7",
"prop-types": "^15.7.2",
"react": "^18.2.0"
}
},
"node_modules/rollup": {
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
@@ -20423,38 +20249,6 @@
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A=="
},
"node_modules/validate.io-array": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz",
"integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg=="
},
"node_modules/validate.io-function": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/validate.io-function/-/validate.io-function-1.0.2.tgz",
"integrity": "sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ=="
},
"node_modules/validate.io-integer": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz",
"integrity": "sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==",
"dependencies": {
"validate.io-number": "^1.0.3"
}
},
"node_modules/validate.io-integer-array": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/validate.io-integer-array/-/validate.io-integer-array-1.0.0.tgz",
"integrity": "sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==",
"dependencies": {
"validate.io-array": "^1.0.3",
"validate.io-integer": "^1.0.4"
}
},
"node_modules/validate.io-number": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz",
"integrity": "sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg=="
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
-4
View File
@@ -4,9 +4,6 @@
"private": true,
"dependencies": {
"@reduxjs/toolkit": "^1.9.7",
"@rjsf/core": "^5.13.0",
"@rjsf/utils": "^5.13.0",
"@rjsf/validator-ajv8": "^5.13.0",
"axios": "^1.5.1",
"daisyui": "^3.9.2",
"file-saver": "^2.0.5",
@@ -34,7 +31,6 @@
"reactour": "^1.19.2",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"rjsf-conditionals": "github:prafullnavkar-nxg/rjsf-conditionals",
"serve": "^14.2.1",
"styled-components": "^4.4.1",
"web-vitals": "^2.1.4"
+1
View File
@@ -0,0 +1 @@
v1.1.1-beta
+24 -91
View File
@@ -7,7 +7,7 @@ import Signup from "./routes/Signup";
import Form from "./routes/Form";
import Report from "./routes/Report";
import Dashboard from "./routes/Dashboard";
import PlanSubscriptions from "./routes/PlanSubscriptions";
import Subscriptions from "./routes/PlanSubscriptions";
import HomeLayout from "./layout/HomeLayout";
import UserProfile from "./routes/UserProfile";
import PageNotFound from "./routes/PageNotFound";
@@ -15,7 +15,10 @@ import ForgetPassword from "./routes/ForgetPassword";
import ChangePassword from "./routes/ChangePassword";
import ReportMicroapp from "./components/ReportMicroapp";
import LoadMf from "./routes/LoadMf";
import GenerateToken from "./routes/GenerateToken";
import ValidateRoute from "./primitives/ValidateRoute";
import Webhook from "./routes/Webhook";
import Validate from "./primitives/Validate";
function App() {
const [isloading, setIsLoading] = useState(true);
@@ -59,104 +62,34 @@ function App() {
) : (
<BrowserRouter>
<Routes>
<Route
exact
path="/"
element={
<ValidateRoute>
<Login />
</ValidateRoute>
}
/>
<Route
exact
path="/signup"
element={
<ValidateRoute>
<Signup />
</ValidateRoute>
}
/>
<Route element={<ValidateRoute />}>
<Route exact path="/" element={<Login />} />
<Route exact path="/signup" element={<Signup />} />
</Route>
<Route element={<Validate />}>
<Route exact path="/load/:remoteApp/*" element={<LoadMf />} />
</Route>
<Route exact path="/loadmf/:remoteApp/*" element={<LoadMf />} />
<Route exact path="/forgetpassword" element={<ForgetPassword />} />
{process.env.REACT_APP_ENABLE_SUBSCRIPTION && (
<>
<Route exact path="/pgsignup" element={<Pgsignup />} />
<Route
exact
path="/subscription"
element={<PlanSubscriptions />}
/>
<Route exact path="/subscription" element={<Subscriptions />} />
</>
)}
<Route
exact
path="/changepassword"
element={
<HomeLayout>
<ChangePassword />
</HomeLayout>
}
/>
<Route
path="/mf/:remoteApp/*"
element={
<HomeLayout>
<Microapp />
</HomeLayout>
}
/>
<Route
path="/asmf/:remoteApp/*"
element={
<HomeLayout>
<Microapp />
</HomeLayout>
}
/>
<Route
path="/rpmf/:remoteApp/*"
element={
<HomeLayout>
<ReportMicroapp />
</HomeLayout>
}
/>
<Route
path="/form/:id"
element={
<HomeLayout>
<Form />
</HomeLayout>
}
/>
<Route
path="/report/:id"
element={
<HomeLayout>
<Report />
</HomeLayout>
}
/>
<Route
path="/dashboard/:id"
element={
<HomeLayout>
<Dashboard />
</HomeLayout>
}
/>
<Route
path="/profile"
element={
<HomeLayout>
<UserProfile />
</HomeLayout>
}
/>
<Route element={<HomeLayout />}>
<Route path="/changepassword" element={<ChangePassword />} />
<Route path="/mf/:remoteApp/*" element={<Microapp />} />
<Route path="/asmf/:remoteApp/*" element={<Microapp />} />
<Route path="/rpmf/:remoteApp/*" element={<ReportMicroapp />} />
<Route path="/form/:id" element={<Form />} />
<Route path="/report/:id" element={<Report />} />
<Route path="/dashboard/:id" element={<Dashboard />} />
<Route path="/profile" element={<UserProfile />} />
<Route path="/generatetoken" element={<GenerateToken />} />
<Route path="/webhook" element={<Webhook />} />
</Route>
<Route path="*" element={<PageNotFound />} />
{/* <Route exact path="/ForgotPassword" element={<ForgotPassword />} /> */}
</Routes>
</BrowserRouter>
)}
+283
View File
@@ -0,0 +1,283 @@
import React, { useState } from "react";
import Parse from "parse";
import axios from "axios";
import Title from "./Title";
import Alert from "../primitives/Alert";
const AddUser = (props) => {
const [name, setName] = useState("");
const [phone, setPhone] = useState("");
const [email, setEmail] = useState("");
const [isLoader, setIsLoader] = useState(false);
const [isUserExist, setIsUserExist] = useState(false);
const parseBaseUrl = localStorage.getItem("baseUrl");
const parseAppId = localStorage.getItem("parseAppId");
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const checkUserExist = async () => {
try {
const res = await Parse.Cloud.run("getUserDetails", {
email: email,
userId: Parse.User.current().id
});
if (res) {
return true;
} else {
return false;
}
} catch (err) {
console.log("err", err);
}
};
// Define a function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
setIsLoader(true);
const res = await checkUserExist();
if (res) {
setIsUserExist(true);
setIsLoader(false);
setTimeout(() => {
setIsUserExist(false);
}, 1000);
} else {
try {
const contactQuery = new Parse.Object("contracts_Users");
contactQuery.set("Name", name);
contactQuery.set("Phone", phone);
contactQuery.set("Email", email);
contactQuery.set("UserRole", "contracts_User");
if (localStorage.getItem("TenetId")) {
contactQuery.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: localStorage.getItem("TenetId")
});
}
try {
const _users = Parse.Object.extend("User");
const _user = new _users();
_user.set("name", name);
_user.set("username", email);
_user.set("email", email);
_user.set("phone", phone);
_user.set("password", phone);
const user = await _user.save();
if (user) {
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_User",
userId: user.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", user);
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
setName("");
setPhone("");
setEmail("");
}
} catch (err) {
console.log("err ", err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run("getUserId", params);
const roleurl = `${parseBaseUrl}functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessionToken: localStorage.getItem("accesstoken")
};
const body = {
appName: localStorage.getItem("_appName"),
roleName: "contracts_User",
userId: userRes.id
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = Parse.User.current();
contactQuery.set(
"CreatedBy",
Parse.User.createWithoutData(currentUser.id)
);
contactQuery.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: userRes.id
});
const acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(true);
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
if (props.details) {
props.details({
value: parseData[props.valueKey],
label: parseData[props.displayKey]
});
}
if (props.closePopup) {
props.closePopup();
}
if (props.handleUserData) {
props.handleUserData(parseData);
}
setIsLoader(false);
setName("");
setPhone("");
setEmail("");
}
}
} catch (err) {
// console.log("err", err);
setIsLoader(false);
alert("something went wrong!");
}
}
};
// Define a function to handle the "add yourself" checkbox
const handleReset = () => {
setName("");
setPhone("");
setEmail("");
};
return (
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
<Title title={"Add User"} />
{isUserExist && <Alert type="danger">User already exists!</Alert>}
{isLoader && (
<div className="fixed inset-0 flex justify-center items-center bg-black bg-opacity-30 z-50">
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
)}
<div className="w-full mx-auto">
<form onSubmit={handleSubmit}>
<h1 className="text-[20px] font-semibold mb-4">Add User</h1>
<div className="mb-3">
<label
htmlFor="name"
className="block text-xs text-gray-700 font-semibold"
>
Name
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
required
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="email"
className="block text-xs text-gray-700 font-semibold"
>
Email
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="mb-3">
<label
htmlFor="phone"
className="block text-xs text-gray-700 font-semibold"
>
Phone
<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
<input
type="text"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
required
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<div className="flex items-center mt-3 gap-2 text-white">
<button
type="submit"
className="bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none"
>
Submit
</button>
<div
type="button"
onClick={() => handleReset()}
className="bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
>
Reset
</div>
</div>
</form>
</div>
</div>
);
};
export default AddUser;
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import axios from "axios";
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
const AppendFormInForm = (props) => {
const [name, setName] = useState("");
@@ -293,14 +294,16 @@ const AppendFormInForm = (props) => {
<div className="mt-4 flex justify-start">
<button
type="submit"
className="bg-[#1ab6ce] text-sm text-white px-4 py-2 rounded shadow focus:outline-none"
style={{ backgroundColor: modalSubmitBtnColor }}
className="mr-2 px-[20px] py-1.5 text-white rounded shadow-md text-center focus:outline-none "
>
Submit
</button>
<button
type="button"
onClick={() => handleReset()}
className="bg-[#188ae2] text-sm text-white px-4 py-2 rounded ml-2 shadow focus:outline-none"
style={{ backgroundColor: modalCancelBtnColor }}
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
>
Reset
</button>
+9 -2
View File
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from "react";
import Package from "../../package.json";
import axios from "axios";
import { openInNewTab } from "../constant/Utils";
const Footer = () => {
const [showButton, setShowButton] = useState(false);
const [version, setVersion] = useState("");
@@ -37,12 +38,18 @@ const Footer = () => {
}, []);
const appName = "OpenSign™";
const openUrl = () => {
openInNewTab(
"https://github.com/OpenSignLabs/OpenSign/releases/tag/" + version
);
};
return (
<>
<div className="bg-[#222c3c] text-[#98a6ba] text-center text-[13px] py-3">
All Rights Reserved &copy; {new Date().getFullYear()} &nbsp;
{appName} ( version: {version ? version : `${Package.version} `})
<span onClick={openUrl} className="hover:underline cursor-pointer">
{appName} ( version: {version ? version : `${Package.version} `})
</span>
</div>
<button
className={`${
+7
View File
@@ -4,6 +4,7 @@ import FullScreenButton from "./FullScreenButton";
import { useNavigate } from "react-router-dom";
import Parse from "parse";
import { useWindowSize } from "../hook/useWindowSize";
import { openInNewTab } from "../constant/Utils";
const Header = ({ showSidebar }) => {
const navigation = useNavigate();
const { width } = useWindowSize();
@@ -118,6 +119,12 @@ const Header = ({ showSidebar }) => {
<ul>
<li
className="hover:bg-gray-100 rounded-t-lg py-1 px-2 cursor-pointer font-normal"
onClick={() => openInNewTab("https://docs.opensignlabs.com")}
>
<i className="fa-solid fa-book"></i> Docs
</li>
<li
className="hover:bg-gray-100 py-1 px-2 cursor-pointer font-normal"
onClick={() => {
setIsOpen(false);
navigation("/profile");
+89 -111
View File
@@ -2,6 +2,8 @@ import React, { useState, useRef } from "react";
import Parse from "parse";
import jwtDecode from "jwt-decode";
import { useScript } from "../hook/useScript";
import ModalUi from "../primitives/ModalUi";
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
/*
* `GoogleSignInBtn`as it's name indicates it render google sign in button
@@ -231,118 +233,94 @@ const GoogleSignInBtn = ({
</div>
)}
<div ref={googleBtn} className="text-sm"></div>
{isModal && (
<div
className="modal fade show"
id="exampleModal"
tabIndex="-1"
role="dialog"
style={{ display: "block", zIndex: 1 }}
>
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Sign up form</h5>
<span>
<span></span>
</span>
</div>
<div className="modal-body">
<form>
<div className="form-group">
<label
htmlFor="Phone"
style={{ display: "flex" }}
className="col-form-label"
>
Phone{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="tel"
className="form-control"
id="Phone"
value={userDetails.Phone}
onChange={(e) =>
setUserDetails({
...userDetails,
Phone: e.target.value
})
}
required
/>
</div>
<div className="form-group">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="col-form-label"
>
Company{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="form-control"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
required
/>
</div>
<div className="form-group">
<label
htmlFor="JobTitle"
style={{ display: "flex" }}
className="col-form-label"
>
Job Title{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="form-control"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
required
/>
</div>
<div>
<button
type="button"
className="bg-[#17a2b8] p-2 text-white rounded"
onClick={() => handleSubmitbtn()}
style={{ marginRight: 10 }}
>
Sign up
</button>
<button
type="button"
className="bg-[#6c757d] p-2 text-white rounded"
onClick={handleCloseModal}
style={{ width: 90 }}
>
Cancel
</button>
</div>
</form>
</div>
</div>
<ModalUi showClose={false} isOpen={isModal} title="Sign up form">
<form className="px-4 py-3">
<div className="mb-3">
<label
htmlFor="Phone"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Phone <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="tel"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="Phone"
value={userDetails.Phone}
onChange={(e) =>
setUserDetails({
...userDetails,
Phone: e.target.value
})
}
required
/>
</div>
</div>
)}
<div className="mb-3">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Company <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
required
/>
</div>
<div className="mb-3">
<label
htmlFor="JobTitle"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Job Title <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
required
/>
</div>
<div>
<button
type="button"
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
onClick={() => handleSubmitbtn()}
style={{ marginRight: 10, backgroundColor: modalSubmitBtnColor }}
>
Sign up
</button>
<button
type="button"
className="p-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
onClick={handleCloseModal}
style={{ width: 90, backgroundColor: modalCancelBtnColor }}
>
Cancel
</button>
</div>
</form>
</ModalUi>
</div>
);
};
@@ -1,391 +0,0 @@
import React, { useState, useEffect } from "react";
import Form from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
import Parse from "parse";
import "../styles/loader.css";
import LayoutField from "./fields/Rjsf-layout";
import TimeWidget from "./fields/TimeWidget";
import Level1Dropdown from "./fields/Level1Dropdown";
import HiddenField from "./fields/HiddenField";
import MultiSelectField from "./fields/MultiSelectField";
import TreeWidget from "../components/TreeWidget";
import { formJson } from "../json/FormJson";
const TreeEditForm = (props) => {
const widget = {
TimeWidget: TimeWidget
};
const fields = {
layout: LayoutField,
Level1Dropdown: Level1Dropdown,
HiddenField: HiddenField,
MultiSelectField: MultiSelectField,
FolderComponent: TreeWidget
};
const [schema, setschema] = useState({});
const [ui_schema, setui_schema] = useState({});
const [title, settitle] = useState("");
const [schemaState, setschemaState] = useState({});
const [formData, setformData] = useState({});
const [active, setactive] = useState(true);
const [loading, setloading] = useState(false);
const [_validate, set_validate] = useState(null);
const [noValidate, setnoValidate] = useState(false);
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const getForm = async (id) => {
setloading(true);
try {
//get json form data (schema ,uischema)
const results = formJson(id);
try {
let _record = Parse.Object.extend(results.class);
var query1 = new Parse.Query(_record);
await query1.get(props.objectId).then(
(x) => {
try {
if (x) {
let result = x.toJSON();
let new_result = result;
for (let [key, value] of Object.entries(result)) {
if (value["__type"] === "Date") {
let todayTime1 = new Date(value.iso);
var month = String(todayTime1.getMonth() + 1);
var day = String(todayTime1.getDate());
var year = String(todayTime1.getFullYear());
if (month.length < 2) month = "0" + month;
if (day.length < 2) day = "0" + day;
let date1 = year + "-" + month + "-" + day;
let bindVar = date1;
if (!results.jsonSchema.properties[key].format) {
bindVar = todayTime1.toISOString();
}
new_result[key] = bindVar;
}
}
setschemaState(results.jsonSchema);
setformData(new_result);
}
} catch (error) {
//alert(error.message);
setloading(false);
}
},
(error) => {
console.log("err", error);
setloading(false);
}
);
setschema(results.jsonSchema);
setui_schema(results.uiSchema);
set_validate(results.validFunction);
setnoValidate(results.noValidate);
settitle(results.class);
setloading(false);
setactive(true);
} catch (error) {
setloading(false);
}
} catch (e) {
setloading(false);
console.error("Problem", e);
}
};
const wrap = (s) => "{ return " + s + " };";
const dynamicValidate = (formData, errors) => {
try {
let body = atob(_validate);
let res = new Function(wrap(body))
.call(null)
.call(null, formData, errors);
return res;
} catch (error) {
console.log(error);
}
};
const handleSubmit = async ({ formData }) => {
setactive(false);
setloading(true);
let RowData = formData;
RowData &&
Object.entries(RowData).forEach(([key, value]) => {
if (typeof value === "string") {
RowData[key] = value.trim();
}
});
let _scanData = schemaState;
if (_scanData.dependencies) {
Object.keys(_scanData.dependencies).forEach((key) => {
if (_scanData.dependencies[key].oneOf) {
_scanData.dependencies[key].oneOf.forEach((val) => {
Object.keys(val.properties).forEach((k) => {
if (typeof val.properties[k] === "object") {
if (val.properties[k].format === "date") {
if (RowData[k]) {
let newdate = new Date(RowData[k]);
RowData[k] = newdate;
}
}
if (val.properties[k].component === "HtmlEditor") {
if (RowData[k]) {
let newHtml = RowData[k]
.replace(/<p[^>]*>/g, "")
.replace(/<\/p>/g, " ");
RowData[k] = newHtml;
}
}
if (val.properties[k].component === "DateTime") {
if (RowData[k]) {
let newDate11 = new Date(RowData[k]);
RowData[k] = newDate11;
}
}
if (val.properties[k].component === "CurrencyInput") {
if (val.properties[k].currencyColumn) {
RowData[`${val.properties[k].currencyColumn}`] =
val.properties[k].defaultcurrency;
}
}
if (val.properties[k].type === "string") {
if (typeof RowData[k] === "string")
RowData[k] = RowData[k].trim();
}
if (val.properties[k].data !== undefined) {
if (val.properties[k].data.isPointer) {
let pointer = undefined;
if (val.properties[k].data.class) {
if (RowData[k]) {
pointer = {
__type: "Pointer",
className: val.properties[k].data.class,
objectId: RowData[k]
};
}
} else {
if (RowData[k]) {
pointer = {
__type: "Pointer",
className: localStorage.getItem("extended_class"),
objectId: RowData[k]
};
}
}
RowData[k] = pointer;
}
if (val.properties[k].data.FolderTypeValue) {
if (RowData[k]) {
let obj = {
__type: "Pointer",
className: val.properties[k].data.ClassName,
objectId: RowData[k]
};
RowData[k] = obj;
}
}
}
}
});
});
}
});
}
let temp = [];
let _dd = _scanData.properties;
let allowed = [];
if (_dd) {
Object.keys(_dd).forEach(function (k) {
allowed.push(k);
if (RowData[k]) {
let pointer = {
__type: "Pointer",
className: _dd[k].class,
objectId: RowData[k]
};
RowData[k] = pointer;
}
if (_dd[k].format === "date") {
if (RowData[k]) {
let newdate = new Date(RowData[k]);
if (!isNaN(newdate.getTime())) {
RowData[k] = newdate;
}
}
}
if (_dd[k].component === "HtmlEditor") {
if (RowData[k]) {
let newHtml = RowData[k]
.replace(/<p[^>]*>/g, "")
.replace(/<\/p>/g, " ");
RowData[k] = newHtml;
}
}
if (_dd[k].component === "DateTime") {
if (RowData[k]) {
let newdate = new Date(RowData[k]);
if (!isNaN(newdate.getTime())) {
RowData[k] = newdate;
}
}
}
if (_dd[k].format === "date-time") {
let newdate = new Date(RowData[k]);
RowData[k] = newdate;
}
if (_dd[k].component === "CurrencyInput") {
if (_dd[k].currencyColumn) {
RowData[`${_dd[k].currencyColumn}`] = _dd[k].defaultcurrency;
}
}
if (_dd[k].data !== undefined) {
if (_dd[k].data.isPointer) {
let pointer = undefined;
if (typeof RowData[k] === "object") {
if (RowData[k]) {
if (RowData[k].objectId !== "Select") {
let obj = {
__type: "Pointer",
className: RowData[k].className,
objectId: RowData[k].objectId
};
pointer = obj;
}
}
} else if (_dd[k].data.class) {
if (RowData[k]) {
pointer = {
__type: "Pointer",
className: _dd[k].data.class,
objectId: RowData[k]
};
}
} else {
if (RowData[k]) {
pointer = {
__type: "Pointer",
className: localStorage.getItem("extended_class"),
objectId: RowData[k]
};
}
}
RowData[k] = pointer;
}
if (_dd[k].data.FolderTypeValue) {
if (RowData[k]) {
let obj = {
__type: "Pointer",
className: _dd[k].data.ClassName,
objectId: RowData[k]
};
RowData[k] = obj;
}
}
}
if (_dd[k].type === "string") {
if (typeof RowData[k] === "string") RowData[k] = RowData[k].trim();
}
});
}
const filtered = Object.keys(RowData)
.filter((key) => allowed.includes(key))
.reduce((obj, key) => {
obj[key] = RowData[key];
return obj;
}, {});
try {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
if (temp.length !== 0) {
let userPointer = {
__type: "Pointer",
className: "_User",
objectId: currentUser.id
};
filtered[[temp[0]]] = userPointer;
}
var data = Parse.Object.extend(title);
var query = new Parse.Query(data);
query.get(props.objectId).then(async (object) => {
object.save(filtered).then(
() => {
try {
alert("Record updated successfully");
props.HideView(false);
setloading(false);
setactive(true);
} catch (error) {
console.log("err", error);
}
},
(error) => {
console.log("err", error);
setloading(false);
setactive(true);
}
);
});
} catch (error) {
console.log("err", error);
alert(error.message);
setloading(false);
setactive(true);
}
};
useEffect(() => {
if (props.FormId) {
getForm(props.FormId);
}
// eslint-disable-next-line
}, [props.FormId]);
if (loading) {
return (
<div
className="loader-01"
style={{
marginTop: "50px",
marginLeft: "50%",
color: "rgb(0,28,28)",
fontSize: "35px"
}}
></div>
);
}
return (
<Form
schema={schema}
uiSchema={ui_schema}
formData={formData}
showErrorList={false}
widgets={widget}
fields={fields}
validate={noValidate && dynamicValidate}
onSubmit={handleSubmit}
validator={validator}
>
<div>
{active ? (
<button className="bg-[#1ab6ce] px-3 py-2 rounded text-white text-sm" type="submit">
Update
</button>
) : (
<button className="bg-[#1ab6ce] px-3 py-2 rounded text-white text-sm" type="submit" disabled>
Update
</button>
)}
</div>
</Form>
);
};
export default TreeEditForm;
@@ -1,237 +0,0 @@
import React, { useEffect, useState } from "react";
import Form from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
import LayoutField from "./fields/Rjsf-layout";
import Level1Dropdown from "./fields/Level1Dropdown";
import HiddenField from "./fields/HiddenField";
import MultiSelectField from "./fields/MultiSelectField";
import "../styles/loader.css";
import "../styles/form.css";
import FileUpload from "./fields/FileUpload";
import parse from "html-react-parser";
import TimeWidget from "./fields/TimeWidget";
import { formJson } from "../json/FormJson";
function TreeFormComponent(props) {
const widget = {
TimeWidget: TimeWidget
};
const fields = {
layout: LayoutField,
FileUpload: FileUpload,
Level1Dropdown: Level1Dropdown,
HiddenField: HiddenField,
MultiSelectField: MultiSelectField
};
const [link, setLink] = useState("");
const [help, setHelp] = useState("");
const toastDescription = "";
const toastColor = "#5cb85c";
const [loading, setLoding] = useState(true);
const [schemaState, setSchemaState] = useState({});
const [formData, setFormData] = useState({});
const [ui_schema, setUi_schema] = useState({});
const active = true;
console.log("new form", props);
const getForm = async (id) => {
setLoding(true);
try {
// get json from data(jsonschema, uischema)
const results = formJson(id);
// console.log("result", results);
if (results) {
let parentObject = {},
childObject = {};
const resultjson = results;
for (let [key, value] of Object.entries(
resultjson.jsonSchema.properties
)) {
if (key === props.ParentField) {
if (props.ParentValue[key]) {
parentObject = { [key]: props.ParentValue[key] };
}
} else {
if (props.ParentValue && typeof props.ParentValue === "object") {
Object.entries(props.ParentValue).forEach(([k, value]) => {
if (k === key) {
let newO = { [k]: value };
parentObject = { ...parentObject, ...newO };
}
});
}
}
if (props.ChildField) {
Object.entries(props.ChildField).forEach(([k, value]) => {
if (k === key) {
let newO = { [k]: value };
childObject = { ...childObject, ...newO };
}
});
}
if (typeof value === "object") {
for (let [k, v] of Object.entries(value)) {
if (k === "format" && v === "date") {
let today = new Date();
let date =
today.getFullYear() +
"-" +
("0" + (today.getMonth() + 1)).slice(-2) +
"-" +
("0" + today.getDate()).slice(-2);
value.default = date;
}
}
}
}
if (props.ParentField) {
setFormData({ ...parentObject, ...childObject });
}
let txt, link;
if (resultjson.help) {
if (resultjson.help.htmlbody) {
txt = resultjson.help.htmlbody;
}
if (resultjson.help.link) {
link = resultjson.help.link;
}
}
setHelp(txt);
setLink(link);
props.ClassName(resultjson.class);
props.SchemaState(resultjson.jsonSchema);
setSchemaState(resultjson.jsonSchema);
setUi_schema(resultjson.uiSchema);
setLoding(false);
} else {
alert("form not found");
setLoding(false);
}
} catch (e) {
console.error("Problem", e);
setLoding(false);
}
};
useEffect(() => {
if (!props.IsEdit) {
getForm(props.Id);
}
// console.log("props tree", props);
// eslint-disable-next-line
}, []);
let formView = (
<React.Fragment>
<Form
schema={schemaState}
uiSchema={ui_schema}
showErrorList={false}
widgets={widget}
fields={fields}
formData={formData}
onSubmit={props.handleSubmit}
validator={validator}
>
{active ? (
<button className="btn btn-info submiBtn" type="submit">
{props.IsEdit ? "update folder" : "create folder"}
</button>
) : (
<button className="btn btn-info submiBtn " type="submit" disabled>
submitting...
</button>
)}
</Form>
</React.Fragment>
);
if (loading) {
formView = (
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
);
}
return (
<React.Fragment>
<div className="row">
<div className="col-md-12">
<div className="card">
<div className="card-body no-padding height-9">
{help ? (
<div className="dropdown" style={{ marginTop: "-30px" }}>
<i
className="far fa-question-circle dropdown-toggle hovereffect"
aria-hidden="true"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
style={{
fontSize: "18px",
color: "purple",
cursor: "pointer !important",
position: "relative",
top: "40px",
left: "98%"
}}
></i>
<div
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
style={{
marginleft: "-121px",
margintop: "-14px",
position: "absolute",
padding: "10px",
width: "300px",
top: "102px!important"
}}
>
{parse(`
${help}
`)}
<br />
{link ? (
<a
onClick={(e) => {
e.preventDefault();
window.location.href = link;
}}
target="_blank"
className="btn btn-xs btn-primary"
>
Read more..
</a>
) : null}
</div>
</div>
) : null}
<div style={{ fontSize: "13px" }}>{formView}</div>
</div>
</div>
</div>
</div>
<div id="snackbar" style={{ backgroundColor: toastColor }}>
{toastDescription}
</div>
</React.Fragment>
);
}
export default TreeFormComponent;
-895
View File
@@ -1,895 +0,0 @@
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import LabelField from "./fields/LabelField";
import Parse from "parse";
import axios from "axios";
import "../styles/spinner.css";
import TreeFormComponent from "./TreeFormComponent";
// import TreeEditForm from "./TreeEditForm";
import "../styles/modal.css";
import Modal from "react-modal";
const TreeWidget = (props) => {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [isAddField, setIsAddField] = useState(false);
const [folderList, setFolderList] = useState([]);
const [tabList, setTabList] = useState([]);
const [appId, setAppId] = useState(undefined);
const [selectedFolder, setSelectedFolder] = useState(undefined);
const [loader, setLoader] = useState(false);
const [className, setClassName] = useState("");
const [schemaState, setSchemaState] = useState({});
const [TabURL, setTabURL] = useState("");
const [editable, setEditable] = useState(false);
// const [editId, setEditId] = useState("");
const [defaultState, setDefaultState] = useState(false);
const [isShowModal, setIsShowModal] = useState(false);
const selectFolderHandle = async () => {
setIsShowModal(true);
setIsAddField(false);
setDefaultState(false);
setFolderList([]);
setLoader(true);
if (tabList.length > 0) {
let len = tabList.length - 1;
selectedItemList(tabList[len]);
} else {
try {
var test;
if (props.schema.parent) {
try {
if (props.parent[props.schema.parent]) {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
let res;
if (localStorage.getItem("Extand_Class")) {
let data = JSON.parse(localStorage.getItem("Extand_Class"));
res = data[0];
} else {
res = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
if (res) res = res.toJSON();
}
if (res) {
let json = res;
setAppId(json.AppId);
}
// eslint-disable-next-line
let reg = /(\#.*?\#)/gi;
let str = props.schema.data.Query;
if (str.includes("#")) {
test = str.replace(reg, props.parent[props.schema.parent]);
} else {
test = str;
}
} else {
if (props.child[props.schema.parent]) {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
let res;
if (localStorage.getItem("Extand_Class")) {
let data = JSON.parse(localStorage.getItem("Extand_Class"));
res = data[0];
} else {
res = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
if (res) res = res.toJSON();
}
if (res) {
let json = res;
setAppId(json.AppId);
}
// eslint-disable-next-line
let reg = /(\#.*?\#)/gi;
let str = props.schema.data.Query;
if (str.includes("#")) {
test = str.replace(reg, props.child[props.schema.parent]);
} else {
test = str;
}
} else {
alert(`Please select ${props.schema.parent}`);
setLoader(false);
return;
}
}
} catch (error) {
console.log("error ", error);
}
} else {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
// eslint-disable-next-line
let reg = /(\#.*?\#)/gi;
let str = props.schema.data.Query;
let res;
if (localStorage.getItem("Extand_Class")) {
let data = JSON.parse(localStorage.getItem("Extand_Class"));
res = data[0];
} else {
// emp = Parse.Object.extend(localStorage.getItem("extended_class"));
// q = new Parse.Query(emp);
// q.equalTo("UserId", currentUser);
// res = await q.first();
// if (res) res = res.toJSON();
const currentUser = Parse.User.current();
res = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
if (res) res = res.toJSON();
}
if (res) {
let json = res;
setAppId(json.AppId);
}
if (str.includes("#")) {
if (res) {
let json = res.toJSON();
let output = str.match(reg);
if (output.length === 1) {
output = output.join();
output = output.substring(1, output.length - 1);
output = output.split(".");
if (output.length === 1) {
let out = output[0];
if (json[out]) {
if (typeof json[out] === "object") {
test = str.replace(reg, JSON.stringify(json[out]));
} else {
test = str.replace(reg, json[out]);
}
} else {
test = str.replace(reg, currentUser.id);
}
} else if (output.length === 2) {
let out1 = json[output[0]][output[1]];
if (out1) {
test = str.replace(reg, out1);
}
}
}
}
} else {
test = str;
}
}
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?${test}`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
await axios
.get(url, { headers: headers })
.then((res) => {
if (res) {
setFolderList(res.data.results);
setLoader(false);
}
})
.catch((err) => {
setLoader(false);
console.log(err);
});
} catch (error) {
setLoader(false);
}
}
};
const selectedItemList = async (folder) => {
setLoader(true);
setFolderList([]);
try {
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?where={"${props.schema.data.ParentFolderField}":{"__type":"Pointer","className":"${props.schema.data.ClassName}","objectId":"${folder.objectId}"}}`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
await axios.get(url, { headers: headers }).then((result) => {
if (result) {
setFolderList(result.data.results);
setLoader(false);
}
});
} catch (error) {
setLoader(false);
}
};
const removeTabListItem = async (e, i) => {
e.preventDefault();
setEditable(false);
setLoader(true);
setFolderList([]);
let list = tabList.filter((itm, j) => {
if (j <= i) {
return itm;
}
});
setTabList(list);
setIsAddField(false);
try {
let _len = list.length - 1;
let url = `${parseBaseUrl}classes/${props.schema.data.ClassName}?where={"${props.schema.data.ParentFolderField}":{"__type":"Pointer","className":"${props.schema.data.ClassName}","objectId":"${list[_len].objectId}"}}`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
await axios.get(url, { headers: headers }).then((result) => {
if (result) {
setFolderList(result.data.results);
setLoader(false);
}
setLoader(false);
});
} catch (error) {
setLoader(false);
}
};
const onDeleteFolder = async (e) => {
e.preventDefault();
if (folderList.length > 0) {
alert(
"Folder should not delete. To delete first delete sub items in it."
);
} else if (window.confirm(`Are you sure you want to delete this record`)) {
try {
setLoader(true);
let len = tabList.length - 1;
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
var deleted = Parse.Object.extend(props.schema.data.ClassName);
var query = new Parse.Query(deleted);
await query.get(tabList[len].objectId).then(
(del) => {
del.destroy().then(
(y) => {
if (y) {
setLoader(false);
let list = tabList.filter((itm, j) => {
if (j !== len) return itm;
});
setTabList(list);
if (list.length > 0) {
let len = list.length - 1;
selectedItemList(list[len]);
} else {
selectFolderHandle();
}
alert("Folder deleted successfully.");
}
},
(error) => {
alert(error.message);
setLoader(false);
}
);
},
(error) => {
console.log("error ", error);
// The object was not retrieved successfully.
setLoader(false);
}
);
} catch (error) {
setLoader(false);
console.log(error);
}
}
};
const onSubmitResult = async (e) => {
setIsShowModal(false);
e.preventDefault();
//console.log("tabList ", tabList);
if (tabList.length > 0) {
try {
let len = tabList.length - 1;
setSelectedFolder({
Topic: tabList[len][`${props.schema.data.FolderNameField}`],
description: tabList[len][`${props.schema.data.FolderDescription}`]
});
props.onChange(tabList[len]["objectId"]);
let url = "Folders";
tabList.forEach((t) => {
url = url + " / " + t[`${props.schema.data.FolderNameField}`];
});
setTabURL(url);
setIsAddField(false);
if (!props.formData) {
setTabList([]);
}
} catch (error) {
setIsAddField(false);
alert(error.message);
}
} else {
setIsAddField(false);
}
};
const editFolderDisplay = async (formData) => {
try {
let objectId = formData.objectId;
let className = formData.className;
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const fldr = Parse.Object.extend(className);
let query = new Parse.Query(fldr);
query.equalTo("objectId", objectId);
const result = await query.first();
let resultJson = result.toJSON();
let CustomTabList = [];
if (resultJson) {
CustomTabList.push(resultJson);
if (resultJson[`${props.schema.data.ParentFolderField}`]) {
let tabsRes = resultJson[`${props.schema.data.ParentFolderField}`];
let looping = false;
do {
let fldttr = Parse.Object.extend(tabsRes.className);
let query2 = new Parse.Query(fldttr);
query2.equalTo("objectId", tabsRes.objectId);
let custome_res = await query2.first();
if (custome_res) {
let tabsData = custome_res.toJSON();
CustomTabList.push(tabsData);
if (tabsData[`${props.schema.data.ParentFolderField}`]) {
tabsRes = tabsData[`${props.schema.data.ParentFolderField}`];
looping = true;
} else {
looping = false;
}
}
} while (looping);
}
let url = "Folders";
CustomTabList.length > 0 &&
CustomTabList.reverse().forEach(
(t) => (url = url + " / " + t[props.schema.data.FolderNameField])
);
setTabURL(url);
setSelectedFolder({
Topic: resultJson[`${props.schema.data.FolderNameField}`],
description: resultJson[`${props.schema.data.FolderDescription}`]
});
setTabList(CustomTabList);
props.onChange(resultJson.objectId);
}
} catch (error) {
console.log(error);
}
};
const handleSubmit = async ({ formData }) => {
setLoader(true);
try {
let RowData = formData;
let _scanData = schemaState;
Object.keys(_scanData).forEach(function (key) {
let _dd = _scanData[key];
Object.keys(_dd).forEach(function (k) {
if (_dd[k].component === "AutoSuggest" && _dd[k].isPointer) {
let pointer = {
__type: "Pointer",
className: _dd[k].class,
objectId: RowData[k]
};
RowData[k] = pointer;
}
if (_dd[k].format === "date") {
let newdate = new Date(RowData[k]);
RowData[k] = newdate;
}
if (_dd[k].component === "DateTime") {
let newDate11;
if (!RowData[k]) {
newDate11 = new Date();
} else {
newDate11 = new Date(RowData[k]);
}
RowData[k] = newDate11;
}
if (_dd[k].component === "CurrencyInput") {
if (_dd[k].currencyColumn) {
RowData[`${_dd[k].currencyColumn}`] = _dd[k].defaultcurrency;
}
}
if (_dd[k].data !== undefined) {
if (_dd[k].data.isPointer) {
let pointer = {};
if (RowData[k] && RowData[k] !== "Select") {
if (_dd[k].data.class) {
if (RowData[k]) {
pointer = {
__type: "Pointer",
className: _dd[k].data.class,
objectId: RowData[k]
};
}
} else {
pointer = {
__type: "Pointer",
className: localStorage.getItem("extended_class"),
objectId: RowData[k]
};
}
RowData[k] = pointer;
}
}
if (_dd[k].data.FolderTypeValue) {
let obj = {
__type: "Pointer",
className: _dd[k].data.ClassName,
objectId: RowData[k]
};
RowData[k] = obj;
}
}
if (_dd[k].type === "string") {
let d = RowData[k];
if (typeof d === "string") {
RowData[k] = d.trim();
}
}
});
});
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
var forms = Parse.Object.extend(className);
var folder = new forms();
folder.set(
props.schema.data.FolderNameField,
formData[props.schema.data.FolderNameField]
);
if (props.schema.data.FolderDescription) {
folder.set(
props.schema.data.FolderDescription,
formData[props.schema.data.FolderDescription]
);
}
folder.set("AppId", appId);
folder.set(
props.schema.data.FolderTypeField,
props.schema.data.FolderTypeValue
);
const currentUser = Parse.User.current();
folder.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
if (tabList.length > 0) {
let len = tabList.length - 1;
folder.set(props.schema.data.ParentFolderField, {
__type: "Pointer",
className: props.schema.data.ClassName,
objectId: tabList[len]["objectId"]
});
}
folder.save(RowData).then(
(result) => {
RowData = {};
if (result) {
setIsAddField(false);
setLoader(false);
if (tabList.length > 0) {
let len = tabList.length - 1;
selectedItemList(tabList[len]);
} else {
selectFolderHandle();
}
}
},
(error) => {
console.log("error ", error);
setLoader(false);
}
);
} catch (error) {
setLoader(false);
}
};
useEffect(() => {
if (props.formData) {
editFolderDisplay(props.formData);
}
// eslint-disable-next-line
}, [props.formData]);
useEffect(() => {
if (defaultState) {
selectFolderHandle();
}
// eslint-disable-next-line
}, [defaultState]);
const customStyles = {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
backgroundColor: "white",
padding: 0
},
overlay: {
width: "100%",
backgroundColor: "rgba(0, 0, 0, 0.75)",
zIndex: 50
}
};
return (
<React.Fragment>
<LabelField
Title={props.schema.title}
Name={props.name}
Required={props.required}
HelpBody={props.schema.helpbody}
HelpLink={props.schema.helplink}
/>
<br />
<div className="block max-w-sm bg-white border border-gray-200 shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
<div
style={{
display: "flex",
color: "black",
fontWeight: "bold",
fontSize: "13px",
padding: "2%",
alignItems: "center"
}}
>
<div style={{ width: "20%" }}>
<div
className="pull-left"
title="Select Folder"
style={{
color: "#33bbff",
margin: "auto",
padding: "10px 10px",
textAlign: "center"
}}
>
<i
className="far fa-folder-open"
style={{ fontSize: "40px" }}
aria-hidden="true"
></i>
</div>
</div>
<div style={{ margin: "0px 10px 0 20px", width: "80%" }}>
<div>
{selectedFolder && selectedFolder.Topic
? selectedFolder.Topic
: "Root"}
<i
className="fa fa-pencil"
title="Select Folder"
style={{ fontSize: 17, cursor: "pointer", marginLeft: 12 }}
onClick={selectFolderHandle}
></i>
{selectedFolder && (
<a
className="pull-right"
onClick={() => {
setSelectedFolder(undefined);
setTabURL(undefined);
props.onChange(undefined);
}}
>
<i
className="fa fa-times-circle-o"
aria-hidden="true"
style={{ fontSize: 17 }}
></i>
</a>
)}
</div>
{TabURL && (
<div
style={{ fontSize: "10px", color: "gray", fontWeight: "bold" }}
>
({TabURL})
</div>
)}
</div>
</div>
</div>
{/* save document in folder and add new folder modal */}
<Modal
isOpen={isShowModal}
onRequestClose={() => setIsShowModal(false)}
shouldCloseOnOverlayClick={false}
contentLabel="Modal"
style={customStyles}
>
<div className="w-full min-w-[300px] md:min-w-[500px]">
{/* header */}
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
backgroundColor: "rgba(203, 200, 200, 0.09)"
}}
>
<div style={{ padding: "10px" }}>
<h4 className=" text-black">Select Folder</h4>
</div>
<div style={{ padding: "10px" }}>
<button
className="btn btn-sm pull-right"
data-dismiss="modal"
title="Select Folder"
style={{
width: "auto",
backgroundColor: "white",
border: "1px solid gray",
boxshadow: "rgba(100, 100, 111, 0.2) 0px 7px 29px 0px"
}}
onClick={(e) => {
e.preventDefault();
setIsAddField(false);
setFolderList([]);
setEditable(false);
if (!props.formData) {
setTabList([]);
}
setIsShowModal(false);
}}
>
<span aria-hidden="true">&times;</span>
</button>
</div>
</div>
<hr />
{/* body */}
<div className="p-3 space-y-6">
<div
style={{
color: "#ac4848",
cursor: "pointer",
fontSize: "14px",
fontWeight: "500"
}}
>
<a
onClick={() => {
setTabList([]);
setIsAddField(false);
setDefaultState(true);
setEditable(false);
}}
>
Folders
</a>
{" / "}
{tabList &&
tabList.map((tab, i) => (
<React.Fragment key={`${tab.objectId}-${i}`}>
<a
title={tab[`${props.schema.data.FolderNameField}`]}
onClick={(e) => removeTabListItem(e, i)}
>
{tab[`${props.schema.data.FolderNameField}`]}
</a>
{" / "}
</React.Fragment>
))}
<hr />
</div>
{/* {editable && (
<TreeEditForm
FormId={props.schema.data.FormId}
objectId={editId}
HideView={(cl) => {
setEditable(cl);
selectFolderHandle();
}}
/>
)} */}
{isAddField && !loader && !editable && (
<TreeFormComponent
Id={props.schema.data.FormId}
handleSubmit={handleSubmit}
ParentField={props.schema.parent && props.schema.parent}
ParentValue={props.schema.parent && props.parent}
ChildField={props.child && props.child}
ClassName={(cls) => setClassName(cls)}
SchemaState={(cls) => setSchemaState(cls)}
/>
)}
{loader && (
<div
className="loader-01"
style={{
marginTop: "50px",
marginLeft: "50%",
color: "rgb(0,28,28)",
fontSize: "35px"
}}
></div>
)}
{!isAddField && !editable && (
<div
style={{
maxHeight: "280px",
overflowY: "auto",
marginTop: "5px"
}}
>
<ul>
{folderList &&
folderList.map(
(fldr) =>
fldr[props.schema.data.FolderTypeField] ===
props.schema.data.FolderTypeValue && (
<li
key={`${fldr.objectId}`}
style={{
listStyle: "none",
padding: "10px 20px",
textTransform: "capitalize",
fontWeight: 300,
border: "1px solid gray",
borderRadius: "1px",
marginTop: "8px",
transition: "all 0.75s ease",
WebkitTransition: "all 0.5s ease",
MozTransition: "all 0.5s ease",
MsTransition: "all 0.5s ease",
OTransition: "all 0.5 ease",
display: "flex",
alignItems: "center",
justifyContent: "space-between"
}}
>
<div
style={{
display: "flex",
alignItems: "center"
}}
>
<i
className={"fa fa-folder"}
style={{
fontSize: "1.4rem",
color: "skyblue"
}}
aria-hidden="true"
></i>
<a
style={{
fontWeight: "bold",
fontSize: "12px",
marginLeft: "4px",
cursor: "pointer"
}}
onClick={() => {
setTabList((tabs) => tabs.concat(fldr));
selectedItemList(fldr);
}}
>
{fldr[`${props.schema.data.FolderNameField}`]}
</a>
</div>
{/* {fldr[props.schema.data.FolderTypeField] ===
props.schema.data.FolderTypeValue && (
<a
className="float-right"
onClick={() => {
setEditable(true);
setEditId(fldr.objectId);
}}
>
<i
className={"fa fa-pencil"}
title={"Edit Folder"}
style={{
fontSize: "0.9rem",
color: "purple",
cursor: "pointer"
}}
aria-hidden="true"
></i>
</a>
)} */}
</li>
)
)}
</ul>
</div>
)}
</div>
{/* footer */}
<div className="flex items-center p-3 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600 justify-between">
<button
className="btn btn-sm float-left createFolder"
title="Create New Folder"
onClick={(e) => {
e.preventDefault();
setIsAddField(true);
}}
>
{" "}
<i
className="fa fa-plus"
style={{ fontSize: "1.5rem" }}
aria-hidden="true"
></i>
</button>
{!isAddField && !editable && (
<div className="flex">
{tabList.length > 0 && (
<button
className="btn btn-sm float-right deleteFolder"
title="Delete Folder"
onClick={onDeleteFolder}
>
{" "}
<i
className="fa fa-trash"
style={{ fontSize: "1rem" }}
aria-hidden="true"
></i>
</button>
)}
<button
className="btn btn-sm float-right saveFolder"
title="Save Here"
onClick={onSubmitResult}
>
{" "}
{/* <i
className="fa fa-floppy-o"
style={{ fontSize: "2rem" }}
aria-hidden="true"
></i> */}
<i
className="fas fa-save"
aria-hidden="true"
style={{ fontSize: "20px" }}
></i>
</button>
</div>
)}
</div>
</div>
</Modal>
</React.Fragment>
);
};
const mapStateToProps = (state) => {
return {
parent: state.Level1_Dropdown,
child: state.Level2_Dropdown
};
};
export default connect(mapStateToProps)(TreeWidget);
@@ -1,312 +0,0 @@
import React, { useState, useEffect } from "react";
import { SaveFileSize } from "../../constant/saveFileSize";
import Parse from "parse";
import sanitizeFileName from "../../primitives/sanitizeFileName";
import DropboxChooser from "./DropboxChoose";
const FileUpload = (props) => {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [_fileupload, setFileUpload] = useState("");
const [fileload, setfileload] = useState(false);
const [Message] = useState(false);
const [percentage, setpercentage] = useState(0);
const REQUIRED_FIELD_SYMBOL = "*";
useEffect(() => {
if (!props.formData) {
setFileUpload([]);
props.onChange(undefined);
}
// eslint-disable-next-line
}, [props.formData]);
const onChange = (e) => {
try {
let files = e.target.files;
if (typeof files[0] !== "undefined") {
if (props.schema.filetypes && props.schema.filetypes.length > 0) {
var fileName = files[0].name;
var fileNameExt = fileName
.substr(fileName.lastIndexOf(".") + 1)
.toLowerCase();
let Extensions = props.schema.filetypes.map((x) => x.toLowerCase());
let arr = Extensions.filter((x) => x === fileNameExt);
if (arr.length > 0) {
console.log("multiple type");
} else {
alert(
"Only these file types are accepted : " + Extensions.join(", ")
);
}
}
if (props.schema.maxfilesizeKB && props.schema.maxfilesizeKB !== "") {
// console.log(Math.round(files[0].size / 1024));
if (
Math.round(Number(files[0].size) / 1024) >=
props.schema.maxfilesizeKB
) {
alert(
`The selected file size is too large. Please select a file less than ${Math.round(
props.schema.maxfilesizeKB / 1024
)} MB`
);
return;
}
}
handleFileUpload(files[0]);
} else {
alert("Please select file.");
return false;
}
} catch (error) {
alert(error.message);
return false;
}
};
const handleFileUpload = async (file) => {
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
setfileload(true);
const size = file.size;
const fileName = file.name;
const name = sanitizeFileName(fileName);
const pdfFile = file;
const parseFile = new Parse.File(name, pdfFile);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
// console.log("percentCompleted ", percentCompleted);
setpercentage(percentCompleted);
}
}
});
setFileUpload(response.url());
props.onChange(response.url());
setfileload(false);
// The response object will contain information about the uploaded file
// console.log("File uploaded:", response);
// You can access the URL of the uploaded file using response.url()
// console.log("File URL:", response.url());
if (response.url()) {
SaveFileSize(size, response.url());
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
};
const dropboxSuccess = async (files) => {
// console.log("file ", files);
setfileload(true);
const file = files[0];
const url = file.link;
const size = file.bytes;
const mb = Math.round(file.bytes / Math.pow(1024, 2));
if (mb > 10) {
setTimeout(() => {
alert(
`The selected file size is too large. Please select a file less than 10 MB`
);
}, 500);
return;
} else {
const name = sanitizeFileName(file.name);
const parseFile = new Parse.File(name, { uri: url });
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
// console.log("percentCompleted ", percentCompleted);
setpercentage(percentCompleted);
}
}
});
// console.log("response.url() ", response.url());
setFileUpload(response.url());
props.onChange(response.url());
setfileload(false);
if (response.url()) {
SaveFileSize(size, response.url());
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
}
};
const dropboxCancel = async () => {
console.log("cancel clicked ");
};
let fileView =
props.formData &&
props.schema.uploadtype === "s3viajw" ? null : props.formData &&
props.formData ? (
<React.Fragment>
<a
href={props.formData}
title={props.formData}
style={{ paddingBottom: "10px", color: "blue" }}
>
Download
</a>
</React.Fragment>
) : fileload === false ? null : (
<React.Fragment>
<a
href={_fileupload}
title={_fileupload}
style={{ paddingBottom: "10px" }}
>
Download
</a>
<br />
</React.Fragment>
);
return (
<React.Fragment>
<div style={{ display: "inline-block" }}>
<label htmlFor={props.name}>
{props.schema.title}
{props.required && (
<span className="required">{REQUIRED_FIELD_SYMBOL}</span>
)}
{fileload ? (
<div className="flex items-center gap-x-2">
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
<div
className="h-2 rounded-full bg-blue-500"
style={{ width: `${percentage}%` }}
></div>
</div>
<span className="text-black text-sm">{percentage}%</span>
</div>
) : (
Message && (
<span style={{ fontSize: "9px", marginLeft: "4px" }}>
Processing will take 5-10 mins....
</span>
)
)}
{props.schema.helpbody ? (
<div className="dropdown pull-right">
<i
className="far fa-question-circle dropdown-toggle hovereffect"
aria-hidden="true"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
style={{
fontSize: "12px",
color: "purple",
cursor: "pointer !important",
position: "relative",
bottom: "0px",
left: "0px",
paddingBottom: "4px",
paddingLeft: "4px"
}}
></i>
<div
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
style={{
marginleft: "-121px",
margintop: "-14px",
position: "absolute",
padding: "10px",
top: "102px!important"
}}
>
<br />
{props.schema.helplink ? (
<a
href={props.schema.helplink}
target="_blank"
rel="noreferrer"
className="btn btn-xs btn-primary"
>
Read more..
</a>
) : null}
</div>
</div>
) : null}
<span style={{ marginLeft: "10px" }}>{fileView}</span>
</label>
</div>
<>
{props.formData ? (
<div className="flex gap-2 justify-center items-center">
<div className="flex justify-between items-center px-2 py-[3px] w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
<div className="break-all">
file selected : {props.formData?.split("/")[3]?.split("_")[1]}
</div>
<div
onClick={() => {
console.log("clicked");
setFileUpload([]);
props.onChange(undefined);
}}
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
>
<i className="fa-solid fa-xmark"></i>
</div>
</div>
{process.env.REACT_APP_DROPBOX_API_KEY && (
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
)}
</div>
) : (
<div className="flex gap-2 justify-center items-center">
<input
type="file"
id="hashfile"
style={{
border: "1px solid #ccc",
color: "gray",
backgroundColor: "white",
padding: "5px 10px",
borderRadius: "4px",
fontSize: "13px",
width: "100%",
fontWeight: "bold"
}}
accept="application/pdf,application/vnd.ms-excel"
onChange={onChange}
/>
{process.env.REACT_APP_DROPBOX_API_KEY && (
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
)}
</div>
)}
</>
</React.Fragment>
);
};
export default FileUpload;
@@ -1,98 +0,0 @@
import React, { Component } from "react";
import Parse from "parse";
// import axios from "axios";
export class HiddenField extends Component {
state = {
baseUrl: localStorage.getItem("baseUrl"),
parseAppId: localStorage.getItem("parseAppId"),
_fields: ""
};
loadData = async () => {
if (this.props.schema.default) {
this.setState({ _fields: this.props.schema.default });
} else {
try {
Parse.serverURL = this.state.baseUrl;
Parse.initialize(this.state.parseAppId);
let currentUser = await Parse.User.current();
if (localStorage.getItem("Extand_Class")) {
let _rdata = JSON.parse(localStorage.getItem("Extand_Class"));
let custome_Filed = "";
let splitArray = this.props.schema.data.valueKey.split(".");
if (splitArray.length > 1) {
custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
} else {
let _Filed = _rdata[0][splitArray[0]];
if (typeof _Filed === "object") {
custome_Filed = {
__type: "Pointer",
className: _Filed["className"],
objectId: _Filed["objectId"]
};
} else {
custome_Filed = _rdata[0][splitArray[0]];
}
}
this.setState({ _fields: custome_Filed });
} else {
if (currentUser) {
// let url = `${this.state.baseUrl}classes/${localStorage.getItem(
// "extended_class"
// )}?where={"UserId":{"__type":"Pointer","className":"_User","objectId":"${
// currentUser.id
// }"}}&keys=${this.props.schema.data.valueKey}`;
// const headers = {
// "Content-Type": "application/json",
// "X-Parse-Application-Id": this.state.parseAppId
// };
// await axios.get(url, { headers: headers }).then((res) => {
// let custome_Filed = "";
// let _rdata = res.data.results;
// let splitArray = this.props.schema.data.valueKey.split(".");
// if (splitArray.length > 1) {
// custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
// } else {
// console.log(_rdata[0][splitArray[0]]);
// custome_Filed = _rdata[0][splitArray[0]];
// }
// this.setState({ _fields: custome_Filed });
// });
const res = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
let custome_Filed = "";
const result = res.toJSON();
let _rdata = [result];
let splitArray = this.props.schema.data.valueKey.split(".");
if (splitArray.length > 1) {
custome_Filed = _rdata[0][splitArray[0]][splitArray[1]];
} else {
console.log(_rdata[0][splitArray[0]]);
custome_Filed = _rdata[0][splitArray[0]];
}
this.setState({ _fields: custome_Filed });
}
}
} catch (error) {
console.log("err", error);
}
}
};
componentDidMount() {
this.loadData();
}
render() {
return (
<input
type="hidden"
value={this.state._fields}
onChange={this.props.onChange(this.state._fields)}
/>
);
}
}
export default HiddenField;
@@ -1,70 +0,0 @@
import React from "react";
import parse from "html-react-parser";
import PropTypes from "prop-types";
const LabelField = (props) => {
const { Title, Name, Required, HelpBody, HelpLink } = props;
const REQUIRED_FIELD_SYMBOL = "*";
return (
<div style={{ display: "inline-block" }}>
<label htmlFor={Name}>
{Title}
{Required && <span className="required">{REQUIRED_FIELD_SYMBOL}</span>}
{HelpBody ? (
<div className="dropdown pull-right">
<i
className="far fa-question-circle dropdown-toggle hovereffect pull-right"
aria-hidden="true"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
style={{
fontSize: "12px",
color: "purple",
cursor: "pointer !important",
position: "relative",
bottom: "0px",
left: "0px",
paddingBottom: "4px",
paddingLeft: "4px"
}}
></i>
<div
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
style={{
marginleft: "-121px",
margintop: "-14px",
position: "absolute",
padding: "10px",
top: "102px!important"
}}
>
{parse(`
${HelpBody}
`)}
<br />
{HelpLink ? (
<a
href={HelpLink}
target="_blank"
rel="noreferrer"
className="btn btn-xs btn-primary"
>
Read more..
</a>
) : null}
</div>
</div>
) : null}
</label>
</div>
);
};
LabelField.propTypes = {
Name: PropTypes.string.isRequired,
Title: PropTypes.string.isRequired
};
export default LabelField;
@@ -1,482 +0,0 @@
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import { onChangeLevel1Dropdown } from "../../redux/actions/index";
import axios from "axios";
import Parse from "parse";
import parse from "html-react-parser";
const Level1Dropdown = (props) => {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [Level1_DD, setLevel1_DD] = useState([]);
const [toastColor] = useState("#d9534f");
const [toastDescription, setToastDescription] = useState("");
const [active, setActive] = useState(false);
const getParams = (offset) => {
const params = {
skip: offset
};
return params;
};
const Level1DropdownData = async () => {
try {
if (
props.formData &&
props.uiSchema["ui:disabled"] &&
props.formData !== "Select"
) {
let url = `${parseBaseUrl}classes/${props.schema.data.class}/${props.formData}`;
if (typeof props.formData === "object") {
url = `${parseBaseUrl}classes/${props.schema.data.class}/${props.formData.objectId}`;
}
let headers1 = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
let data = [];
let response = [];
await axios.get(url, { headers: headers1 }).then((res) => {
if (res) {
data.push(res.data);
}
});
if (data.length > 0) {
let temp = [];
data.forEach((x) => {
let opt = "";
if (Array.isArray(x[props.schema.data.valueKey])) {
x[props.schema.data.valueKey].forEach((tt) => {
if (typeof tt === "object") {
opt = {
objectId: tt[props.schema.data.displayKey],
option: tt[props.schema.data.displayKey]
};
} else {
opt = {
objectId: tt,
option: tt
};
}
temp.push(opt);
});
} else if (props.schema.data.isPointer) {
if (props.schema.data.valueKey.includes(".")) {
let newArr = props.schema.data.valueKey.split(".");
if (newArr.length === 2) {
opt = {
objectId: x[newArr[0]][newArr[1]],
option: x[props.schema.data.displayKey]
};
} else {
opt = {
objectId: x[newArr[0]],
option: x[props.schema.data.displayKey]
};
}
} else {
opt = {
objectId: x[props.schema.data.valueKey],
option: x[props.schema.data.displayKey]
};
}
temp.push(opt);
} else {
opt = {
objectId: x[props.schema.data.valueKey],
option: x[props.schema.data.displayKey]
};
temp.push(opt);
}
});
response = temp;
setLevel1_DD(response);
setActive(false);
if (response.length === 1) {
props.onChangeLevel1Dropdown(response[0].objectId, props.name);
props.onChange(response[0].objectId);
} else if (response.length === 0) {
props.onChangeLevel1Dropdown("", props.name);
props.onChange("");
}
}
} else {
let response = [];
setActive(true);
Parse.serverURL = parseBaseUrl;
Parse.initialize(parseAppId);
const currentUser = Parse.User.current();
// eslint-disable-next-line
var reg = /(\#.*?\#)/gi;
let str = props.schema.data.query;
var test;
if (str.includes("#")) {
let res;
if (localStorage.getItem("Extand_Class")) {
let data = JSON.parse(localStorage.getItem("Extand_Class"));
res = data[0];
} else {
const currentUser = Parse.User.current();
res = await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
});
if (res) res = res.toJSON();
}
if (res) {
let json = res;
let output = str.match(reg);
if (output.length === 1) {
output = output.join();
output = output.substring(1, output.length - 1);
output = output.split(".");
if (output.length === 1) {
let out = output[0];
if (json[out]) {
if (typeof json[out] === "object") {
test = str.replace(reg, JSON.stringify(json[out]));
} else {
test = str.replace(reg, json[out]);
}
} else {
test = str.replace(reg, currentUser.id);
}
} else if (output.length === 2) {
let out1 = json[output[0]][output[1]];
if (out1) {
test = str.replace(reg, out1);
}
}
} else if (output.length === 2) {
let output1 = output[0];
output1 = output1.substring(1, output1.length - 1);
output1 = output1.split(".");
if (output1.length === 1) {
let out = output1[0];
if (json[out]) {
if (typeof json[out] === "object") {
str = str.replace(output[0], JSON.stringify(json[out]));
} else {
str = str.replace(output[0], json[out]);
}
} else {
str = str.replace(output[0], currentUser.id);
}
} else if (output1.length === 2) {
let out1 = json[output1[0]][output1[1]];
if (out1) {
str = str.replace(output[0], out1);
}
}
let output2 = output[1];
output2 = output2.substring(1, output2.length - 1);
output2 = output2.split(".");
if (output2.length === 1) {
let out = output2[0];
if (json[out]) {
if (typeof json[out] === "object") {
str = str.replace(output[1], JSON.stringify(json[out]));
} else {
str = str.replace(output[1], json[out]);
}
} else {
str = str.replace(output[1], currentUser.id);
}
} else if (output2.length === 2) {
let out1 = json[output2[0]][output2[1]];
if (out1) {
str = str.replace(output[1], out1);
}
}
test = str;
}
} else {
setToastDescription(
`User not found in ${localStorage.getItem("extended_class")}`
);
setActive(false);
var x = document.getElementById("Level_1");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 5000);
return;
}
} else {
test = str;
}
let url = `${parseBaseUrl}classes/${props.schema.data.class}?${test}`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
let fetchCount = 0;
let offset = 0;
let data = [];
/* eslint-disable no-constant-condition */
while (true) {
const params = getParams(offset);
await axios
.get(url, { params: params, headers: headers })
.then((response) => {
fetchCount = response.data.results.length;
offset += response.data.results.length;
if (fetchCount > 0) {
response.data.results.forEach((x) => {
data.push(x);
});
}
})
.catch((err) => {
console.error("Problem", err.response);
setToastDescription(err.response.data.message);
setActive(false);
var x = document.getElementById("Level_1");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 5000);
});
if (fetchCount === 0 || fetchCount < 100) {
break;
}
}
if (data.length > 0) {
let temp = [];
data.forEach((x) => {
let opt = "";
if (Array.isArray(x[props.schema.data.valueKey])) {
x[props.schema.data.valueKey].forEach((tt) => {
if (typeof tt === "object") {
if (props.schema.data.isPointer) {
opt = {
objectId: tt["objectId"],
option: tt[props.schema.data.displayKey]
};
} else {
opt = {
objectId: tt[props.schema.data.displayKey],
option: tt[props.schema.data.displayKey]
};
}
} else {
opt = {
objectId: tt,
option: tt
};
}
temp.push(opt);
});
} else if (props.schema.data.isPointer) {
if (props.schema.data.valueKey.includes(".")) {
let newArr = props.schema.data.valueKey.split(".");
if (newArr.length === 2) {
let _dis = props.schema.data.displayKey.split(".");
if (_dis.length === 2) {
opt = {
objectId: x[newArr[0]][newArr[1]],
option: x[_dis[0]][_dis[1]]
};
} else {
opt = {
objectId: x[newArr[0]][newArr[1]],
option: x[props.schema.data.displayKey]
};
}
} else {
opt = {
objectId: x[newArr[0]],
option: x[props.schema.data.displayKey]
};
}
} else {
opt = {
objectId: x[props.schema.data.valueKey],
option: x[props.schema.data.displayKey]
};
}
temp.push(opt);
} else {
opt = {
objectId: x[props.schema.data.valueKey],
option: x[props.schema.data.displayKey]
};
temp.push(opt);
}
});
response = temp;
setLevel1_DD(response);
setActive(false);
if (!props.formData) {
props.onChangeLevel1Dropdown("", props.name);
}
if (response.length === 1) {
props.onChangeLevel1Dropdown(response[0].objectId, props.name);
props.onChange(response[0].objectId);
} else if (response.length === 0) {
props.onChangeLevel1Dropdown("", props.name);
props.onChange("");
}
} else {
setActive(false);
}
}
} catch (e) {
try {
setToastDescription(e.message);
setActive(false);
x = document.getElementById("Level_1");
x.className = "show";
setTimeout(function () {
x.className = x.className.replace("show", "");
}, 5000);
} catch (error) {
console.log("Err", error);
}
}
};
useEffect(() => {
Level1DropdownData();
// eslint-disable-next-line
}, []);
useEffect(() => {
if (props.formData && props.formData[`objectId`]) {
props.onChangeLevel1Dropdown(props.formData.objectId, props.name);
} else if (props.formData) {
props.onChangeLevel1Dropdown(props.formData, props.name);
}
// eslint-disable-next-line
}, [props.formData]);
const selectDefault = () => {
props.onChange(Level1_DD[0].objectId);
props.onChangeLevel1Dropdown(Level1_DD[0].objectId, props.name);
};
const REQUIRED_FIELD_SYMBOL = "*";
return (
<React.Fragment>
<div style={{ display: "inline-block" }}>
<label htmlFor={props.name}>
{props.schema.title}
{props.required && (
<span className="required">{REQUIRED_FIELD_SYMBOL}</span>
)}
{props.schema.data.helpbody ? (
<div className="dropdown pull-right">
<i
className="far fa-question-circle dropdown-toggle hovereffect pull-right"
aria-hidden="true"
id="dropdownMenuButton"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
style={{
fontSize: "12px",
color: "purple",
cursor: "pointer !important",
position: "relative",
bottom: "0px",
left: "0px",
paddingBottom: "4px",
paddingLeft: "4px"
}}
></i>
<div
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
style={{
marginleft: "-121px",
margintop: "-14px",
position: "absolute",
padding: "10px",
top: "102px!important"
}}
>
{parse(`
${props.schema.data.helpbody}
`)}
<br />
{props.schema.data.helplink ? (
<a
href={props.schema.data.helplink}
target="_blank"
rel="noreferrer"
className="btn btn-xs btn-primary"
>
Read more..
</a>
) : null}
</div>
</div>
) : null}
</label>
</div>
{Level1_DD && (
<select
className="form-control"
name={props.name}
value={props.value}
required={props.required}
disabled={props.uiSchema["ui:disabled"]}
onChange={(e) => {
if (!e.target.value) {
if (props.schema.data.isPointer) {
localStorage.setItem("Level1", undefined);
props.onChangeLevel1Dropdown(undefined, props.name);
return props.onChange(undefined);
} else {
localStorage.setItem("Level1", e.target.value);
props.onChangeLevel1Dropdown(e.target.value, props.name);
return props.onChange(e.target.value);
}
} else {
localStorage.setItem("Level1", e.target.value);
props.onChangeLevel1Dropdown(e.target.value, props.name);
return props.onChange(e.target.value);
}
}}
readOnly={props.uiSchema["ui:disabled"] ? true : false}
defaultValue={Level1_DD.length === 1 && selectDefault}
>
<option value="">{!active ? "Select" : "loading..."}</option>
{Level1_DD &&
Level1_DD.map((x, i) => (
<React.Fragment key={i}>
{props.formData && props.formData.objectId ? (
props.formData.objectId === x.objectId ? (
<option value={x.objectId} selected>
{x.option}
</option>
) : (
<option value={x.objectId}>{x.option}</option>
)
) : props.formData && props.formData === x.objectId ? (
<option value={x.objectId} selected>
{x.option}
</option>
) : (
<option value={x.objectId}>{x.option}</option>
)}
</React.Fragment>
))}
</select>
)}
<div id="Level_1" style={{ backgroundColor: toastColor }}>
{toastDescription}
</div>
</React.Fragment>
);
};
export default connect(null, { onChangeLevel1Dropdown })(Level1Dropdown);
@@ -1,709 +0,0 @@
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import Select from "react-select";
import axios from "axios";
import LabelField from "./LabelField";
import ReactDragListView from "react-drag-listview";
import AppendFormInForm from "../AppendFormInForm";
import Modal from "react-modal";
function arrayMove(array, from, to) {
array = array.slice();
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
return array;
}
/**
* react-sortable-hoc is depcreated not usable from react 18.x.x
* need to replace it with @dnd-kit
* code changes required
*/
const MultiSelectField = (props) => {
Modal.setAppElement("body");
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [state, setState] = useState(undefined);
// const [editFormData, setEditFormData] = useState([]);
const [selected, setSelected] = React.useState([]);
const [isModal, setIsModel] = useState(false);
const onChange = (selectedOptions) => setSelected(selectedOptions);
const [modalIsOpen, setModalIsOpen] = useState(false);
const onSortEnd = ({ oldIndex, newIndex }) => {
const newValue = arrayMove(selected, oldIndex, newIndex);
setSelected(newValue);
};
// console.log("props",props)
const GetSelectListData = async () => {
try {
let response = [];
let str = props.schema.data.query;
let _query;
/* eslint-disable no-useless-escape */
let reg = /(\#.*?\#)/gi;
let extclass =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"));
let extRow = extclass.length && extclass[0];
if (str.includes("#")) {
let output = str.match(reg);
output = output.join();
output = output.substring(1, output.length - 1);
output = output.split(".");
if (output.length === 2) {
let out1 = extRow[output[0]][output[1]];
_query = str.replace(reg, out1 && out1);
} else {
let out1 = extRow[output[0]];
_query = str.replace(reg, out1 && out1);
}
} else {
_query = str;
}
let url = `${parseBaseUrl}classes/${props.schema.data.class}?${_query}`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
};
await axios.get(url, { headers: headers }).then((res) => {
let temp = [];
let formArray = [];
let _selected = [];
if (props.schema.uiLayout === "MultiDropdownList") {
if (props.formData) {
props.formData.forEach((x) => {
if (typeof x === "object") {
formArray.push(x.objectId);
} else {
formArray.push(x);
}
});
}
res.data.results.forEach((x) => {
let obj = {};
if (formArray.includes(x[props.schema.data.valueKey])) {
obj = {
name: x[props.schema.data.displayKey],
value: x[props.schema.data.valueKey],
isChecked: true
};
} else {
if (props.schema.selectAll) {
obj = {
name: x[props.schema.data.displayKey],
value: x[props.schema.data.valueKey],
isChecked: true
};
} else {
obj = {
name: x[props.schema.data.displayKey],
value: x[props.schema.data.valueKey],
isChecked: false
};
}
}
temp.push(obj);
});
response = temp;
if (props.formData) {
let checkData = [];
response.forEach((x) => {
if (x.isChecked) {
checkData.push(x.value);
}
});
setSelected(checkData);
}
setState({ [`${props.name}_DD`]: response });
} else {
res.data.results.forEach((x) => {
let obj = {};
if (props.formData) {
props.formData.forEach((x) => {
if (typeof x === "object") {
formArray.push(x.objectId);
} else {
formArray.push(x);
}
});
}
obj = {
label: x[props.schema.data.displayKey],
value: x[props.schema.data.valueKey]
};
if (formArray.includes(x[props.schema.data.valueKey])) {
_selected.push(obj);
}
if (props.schema.selectAll) {
_selected.push(obj);
}
temp.push(obj);
});
response = temp;
setState({ [`${props.name}_DD`]: response });
setSelected(_selected);
}
});
} catch (error) {
console.log("err", error);
}
};
const handleCheckChieldElement = (event) => {
let SelectLists = state[`${props.name}_DD`];
let List = [];
SelectLists.forEach((select) => {
if (select.value === event.target.value) {
select.isChecked = event.target.checked;
}
if (select.isChecked) {
List.push(select.value);
}
});
setSelected(List);
setState({ [`${props.name}_DD`]: SelectLists });
};
// const Level1CheckList = async (id) => {
// try {
// let response = [];
// // eslint-disable-next-line
// let reg = /(\#.*?\#)/gi;
// let _query = props.schema.data.query;
// let output = _query.match(reg);
// if (output.length === 2) {
// let res;
// if (localStorage.getItem("Extand_Class")) {
// let data = JSON.parse(localStorage.getItem("Extand_Class"));
// res = data[0];
// }
// output = output.filter((x) => x !== "#queryString#");
// if (output.length === 1) {
// _query = _query.replace("#queryString#", id);
// output = output.join();
// output = output.substring(1, output.length - 1);
// output = output.split(".");
// if (output.length > 0) {
// _query = _query.replace(reg, res[output[0]][output[1]]);
// } else {
// _query = _query.replace(reg, res[output[0]]);
// }
// }
// } else {
// _query = props.schema.data.query.replace(reg, id);
// }
// let url = `${parseBaseUrl}classes/${props.schema.data.class}?${_query}`;
// const headers = {
// "Content-Type": "application/json",
// "X-Parse-Application-Id": parseAppId,
// "X-Parse-Session-Token": localStorage.getItem("accesstoken")
// };
// await axios.get(url, { headers: headers }).then(async (res) => {
// let temp = [];
// let formArray = [];
// let _selected = [];
// if (editFormData.length > 0) {
// editFormData.forEach((x) => {
// if (typeof x === "object") {
// formArray.push(x.objectId);
// } else {
// formArray.push(x);
// }
// });
// } else if (
// props.schema.selectedData &&
// Object.keys(props.schema.selectedData).length !== 0 &&
// props.schema.selectedData.constructor === Object
// ) {
// try {
// let selectedDataQuery = props.schema.selectedData.query.replace(
// reg,
// id
// );
// let selectedDataUrl = `${parseBaseUrl}classes/${props.schema.selectedData.class}?${selectedDataQuery}`;
// await axios.get(selectedDataUrl, { headers }).then((sltres) => {
// let sltData = sltres.data.results;
// sltData.forEach((x) => {
// if (props.schema.selectedData.valueKey.includes(".")) {
// let sltArr = props.schema.selectedData.valueKey.split(".");
// if (Array.isArray(x[sltArr[0]])) {
// x[sltArr[0]].forEach((l) => {
// formArray.push(l[sltArr[1]]);
// });
// } else {
// formArray.push(x[sltArr[0][sltArr[1]]]);
// }
// } else {
// formArray.push(x[props.schema.selectedData.valueKey]);
// }
// });
// });
// } catch (error) {}
// }
// res.data.results.forEach((x) => {
// let obj = {};
// if (props.schema.data.valueKey.includes(".")) {
// let newArr = props.schema.data.valueKey.split(".");
// if (Array.isArray(x[newArr[0]])) {
// if (props.schema.data.displayKey.includes(".")) {
// let _dis = props.schema.data.displayKey.split(".");
// x[newArr[0]].forEach((l) => {
// if (formArray.includes(l[newArr[1]])) {
// obj = {
// label: l[_dis[1]],
// value: l[newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: l[_dis[1]],
// value: l[newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: l[_dis[1]],
// value: l[newArr[1]],
// isChecked: false
// };
// }
// }
// temp.push(obj);
// });
// } else {
// x[newArr[0]].forEach((l) => {
// if (formArray.includes(l[newArr[1]])) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: l[newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: l[newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: x[props.schema.data.displayKey],
// value: l[newArr[1]],
// isChecked: false
// };
// }
// }
// temp.push(obj);
// });
// }
// } else {
// if (props.schema.data.displayKey.includes(".")) {
// let disArr = props.schema.data.displayKey.split(".");
// if (formArray.includes(x[newArr[0]][newArr[1]])) {
// obj = {
// label: x[disArr[0]][disArr[1]],
// value: x[newArr[0]][newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: x[disArr[0]][disArr[1]],
// value: x[newArr[0]][newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: x[disArr[0]][disArr[1]],
// value: x[newArr[0]][newArr[1]],
// isChecked: false
// };
// }
// }
// } else {
// if (formArray.includes(x[newArr[0]][newArr[1]])) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[newArr[0]][newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[newArr[0]][newArr[1]],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[newArr[0]][newArr[1]],
// isChecked: false
// };
// }
// }
// }
// }
// } else {
// if (Array.isArray(x[props.schema.data.valueKey])) {
// x[props.schema.data.valueKey].forEach((t) => {
// if (formArray.includes(t)) {
// obj = {
// label: t,
// value: t,
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: t,
// value: t,
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: t,
// value: t,
// isChecked: false
// };
// }
// }
// temp.push(obj);
// });
// } else if (formArray.includes(x[props.schema.data.valueKey])) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[props.schema.data.valueKey],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// if (props.schema.selectAll) {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[props.schema.data.valueKey],
// isChecked: true
// };
// _selected.push(obj);
// } else {
// obj = {
// label: x[props.schema.data.displayKey],
// value: x[props.schema.data.valueKey],
// isChecked: false
// };
// }
// }
// temp.push(obj);
// }
// });
// response = temp;
// if (props.schema.uiLayout === "MultiDropdownList") {
// if (editFormData) {
// let checkData = [];
// response.forEach((x) => {
// if (x.isChecked) {
// checkData.push(x.value);
// }
// });
// setSelected(checkData);
// }
// setState({ [`${props.name}_DD`]: response });
// } else {
// setState({ [`${props.name}_DD`]: response });
// setSelected(_selected);
// }
// });
// } catch (error) {}
// };
const dragProps = {
onDragEnd(fromIndex, toIndex) {
const data = [...state[`${props.name}_DD`]];
const item = data.splice(fromIndex, 1)[0];
data.splice(toIndex, 0, item);
let NewList = [];
data.forEach((x) => {
if (x.isChecked) {
NewList.push(x.value);
}
});
setState({ [`${props.name}_DD`]: data });
setSelected(NewList);
},
nodeSelector: "li",
handleSelector: "a"
};
useEffect(() => {
if (!props.schema.parent) GetSelectListData();
// eslint-disable-next-line
}, []);
useEffect(() => {
if (props.schema.uiLayout === "MultiDropdownList") {
props.onChange(selected);
} else {
if (selected && selected.length) {
let newData = [];
selected.forEach((x) => {
newData.push(x.value);
});
props.onChange(newData);
}
}
// eslint-disable-next-line
}, [selected]);
// useState(() => {
// if (props.formData) {
// if (props.formData === "Select") {
// } else {
// setEditFormData(props.formData);
// }
// }
// // eslint-disable-next-line
// }, [props.formData]);
const handleModalCloseClick = () => {
setIsModel(false);
setModalIsOpen(false);
};
const openModal = () => {
setModalIsOpen(true);
};
// `handleNewDetails` is used to set just save from quick form to selected option in dropdown
const handleNewDetails = (data) => {
setState({ [`${props.name}_DD`]: [...state[`${props.name}_DD`], data] });
if (selected.length > 0) {
setSelected([...selected, data]);
} else {
setSelected([data]);
}
};
if (props.schema.uiLayout === "MultiDropdownList") {
return (
<React.Fragment>
<LabelField
Title={props.schema.title}
Name={props.name}
Required={props.required}
HelpBody={props.schema.helpbody}
HelpLink={props.schema.helplink}
/>
<ReactDragListView {...dragProps}>
<ul
style={{
listStyleType: "square",
listStylePosition: "outside",
maxHeight: "250px",
overflow: "auto",
paddingTop: "10px",
paddingBottom: "10px",
border: "0px solid #b3b3b3",
borderRadius: "5px"
}}
>
{state && state[`${props.name}_DD`].length > 0 ? (
state[`${props.name}_DD`].map((x, i) => {
return (
<li key={i} className="multiSelectList">
<input
key={x.value}
onClick={handleCheckChieldElement}
type="checkbox"
checked={x.isChecked}
style={{ marginTop: "7px" }}
value={x.value}
disabled={props.disabled}
/>{" "}
<span style={{ marginTop: "7px" }}>{x.name}</span>
{props.schema.isSortable && !props.disabled && (
<a
onClick={(e) => e.preventDefault()}
className="pull-right"
title="Sort List"
style={{ textDecoration: "none" }}
>
<i
className="fa fa-bars"
style={{
fontSize: "30px",
color: "skyblue",
paddingTop: "11px"
}}
></i>
</a>
)}
</li>
);
})
) : (
<li className="multiSelectList">No Record Found</li>
)}
</ul>
</ReactDragListView>
</React.Fragment>
);
}
return (
<React.Fragment>
<LabelField
Title={props.schema.title}
Name={props.name}
Required={props.required}
HelpBody={props.schema.helpbody}
HelpLink={props.schema.helplink}
/>
<div style={{ display: "flex", gap: 5 }}>
<div style={{ flexWrap: "wrap", width: "100%" }}>
<Select
onSortEnd={onSortEnd}
distance={4}
isMulti
options={(state && state[`${props.name}_DD`]) || []}
value={selected}
onChange={onChange}
closeMenuOnSelect={false}
/>
</div>
{props.schema.data.quickAddFormId && (
<div
onClick={() => {
setIsModel(true);
openModal();
}}
style={{
cursor: "pointer",
borderRadius: 4,
border: "1px solid #ccc",
minHeight: 38,
minWidth: 48,
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<i className="fas fa-plus"></i>
</div>
)}
{/* {props.schema.data.quickAddFormId && (
<div
className="modal fade"
id={"multiSelectModal" + props.schema.data.quickAddFormId}
tabIndex="-1"
role="dialog"
aria-labelledby="exampleModalLabel"
aria-hidden="true"
>
<div className="modal-dialog modal-lg" role="document">
<div className="modal-content">
<div className="modal-header">
<button
type="button"
className="close"
data-dismiss="modal"
aria-label="Close"
onClick={handleModalCloseClick}
>
<i className="fa fa-times-circle" aria-hidden="true"></i>
</button>
</div>
<div className="modal-body">
{isModal && (
<AppendFormInForm
id={props.schema.data.quickAddFormId}
valueKey={props.schema.data.valueKey}
displayKey={props.schema.data.displayKey}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</div>
</div>
</div>
</div>
)} */}
<Modal
isOpen={modalIsOpen}
onRequestClose={handleModalCloseClick}
shouldCloseOnOverlayClick={false}
contentLabel="Modal"
style={{
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
transform: "translate(-50%, -50%)",
padding: 0
},
overlay: {
width: "100%",
backgroundColor: "rgba(0, 0, 0, 0.75)",
zIndex: 50
}
}}
>
<div className="min-w-full md:min-w-[500px]">
<div
type="button"
className="flex justify-between items-center p-3 border-b-[1px] border-gray-300"
>
<div className=" text-black text-xl font-semibold pl-3">
Add Signer
</div>
<button onClick={handleModalCloseClick}>
<i
style={{ fontSize: 25 }}
className="fa fa-times-circle"
aria-hidden="true"
></i>
</button>
</div>
{isModal && (
<AppendFormInForm
id={props.schema.data.quickAddFormId}
valueKey={props.schema.data.valueKey}
displayKey={props.schema.data.displayKey}
details={handleNewDetails}
closePopup={handleModalCloseClick}
/>
)}
</div>
</Modal>
</div>
</React.Fragment>
);
};
const mapStateToProps = (state) => {
return {
Level1_Dropdown: state.Level1_Dropdown,
Level2_Dropdown: state.Level2_Dropdown,
Level3_Dropdown: state.Level3_Dropdown
};
};
export default connect(mapStateToProps, null)(MultiSelectField);
@@ -1,92 +0,0 @@
import React from "react";
import ObjectField from "@rjsf/core/lib/components/fields/ObjectField";
import validator from "@rjsf/validator-ajv8";
import { Col } from "react-bootstrap";
import { retrieveSchema } from "@rjsf/utils";
class GridField extends ObjectField {
state = {};
render() {
const {
uiSchema,
errorSchema,
idSchema,
disabled,
readonly,
onBlur,
formData
} = this.props;
const { fields, rootSchema } = this.props.registry;
const { SchemaField } = fields;
const schema = retrieveSchema(validator, this.props.schema, rootSchema);
const title = schema.title === undefined ? "" : schema.title;
const description =
schema && schema.description === undefined ? "" : schema.description;
// console.log('this.props.schema ', this.props.schema)
// console.log("schema ", schema);
const layout = uiSchema["ui:layout"];
return (
<fieldset>
{title ? <h4>{title}</h4> : null}
{description ? <h6>{description}</h6> : null}
{layout.map((row, index) => {
return (
<div className="row" key={index}>
{Object.keys(row).map((name, index) => {
const { doShow, ...rowProps } = row[name];
let style = {};
if (doShow && !doShow({ formData })) {
style = { display: "none" };
}
if (schema.properties[name]) {
return (
<Col {...rowProps} key={index} style={style}>
<SchemaField
name={name}
required={this.isRequired(name)}
schema={schema.properties[name]}
uiSchema={uiSchema[name]}
errorSchema={errorSchema[name]}
idSchema={idSchema[name]}
formData={formData[name]}
onChange={this.onPropertyChange(name)}
onBlur={onBlur}
registry={this.props.registry}
disabled={disabled}
readonly={readonly}
/>
</Col>
);
} else {
const { render, ...rowProps } = row[name];
let UIComponent = () => null;
if (render) {
UIComponent = render;
}
return (
<Col {...rowProps} key={index} style={style}>
<UIComponent
name={name}
formData={formData}
errorSchema={errorSchema}
uiSchema={uiSchema}
schema={schema}
registry={this.props.registry}
/>
</Col>
);
}
})}
</div>
);
})}
</fieldset>
);
}
}
export default GridField;
@@ -113,6 +113,7 @@ const SignersInput = (props) => {
value={selected}
onChange={onChange}
closeMenuOnSelect={false}
required={props.required}
/>
</div>
<div
@@ -146,6 +147,7 @@ const SignersInput = (props) => {
right: "auto",
bottom: "auto",
transform: "translate(-50%, -50%)",
border: "1px transparent",
padding: 0
},
overlay: {
@@ -157,18 +159,15 @@ const SignersInput = (props) => {
>
<div className="min-w-full md:min-w-[500px]">
<div
type="button"
className="flex justify-between items-center p-3 border-b-[1px] border-gray-300"
className="flex justify-between rounded-t items-center py-[10px] px-[20px] text-white"
style={{ background: "#32a3ac" }}
>
<div className=" text-black text-xl font-semibold pl-3">
Add Signer
</div>
<button onClick={handleModalCloseClick}>
<i
style={{ fontSize: 25 }}
className="fa fa-times-circle"
aria-hidden="true"
></i>
<div className="text-[1.2rem] font-normal">Add Signer</div>
<button
onClick={handleModalCloseClick}
className="text-[1.5rem] cursor-pointer focus:outline-none"
>
&times;
</button>
</div>
@@ -1,26 +0,0 @@
import React from "react";
import PropTypes from "prop-types";
function TimeWidget(props) {
const {
onChange,
registry: {
widgets: { BaseInput }
}
} = props;
return (
<BaseInput
type="time"
{...props}
onChange={(value) => onChange(value || undefined)}
/>
);
}
if (process.env.NODE_ENV !== "production") {
TimeWidget.propTypes = {
value: PropTypes.string
};
}
export default TimeWidget;
+3
View File
@@ -0,0 +1,3 @@
export const openInNewTab = (url) => {
window.open(url, "_blank", "noopener,noreferrer");
};
+7
View File
@@ -1,2 +1,9 @@
export const contactCls = "contracts_Contactbook";
export const templateCls = "contracts_Template";
export const documentCls = "contracts_Document";
export const rejectBtn =
"bg-[#ffffff] rounded-sm shadow-md text-[12px] font-semibold uppercase text-black py-1.5 px-4 focus:outline-none text-center border-[1px] border-[#b4b4b4]";
export const submitBtn =
"bg-[#32a3ac] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-4 focus:outline-none text-center";
export const modalSubmitBtnColor = "#17a2b8";
export const modalCancelBtnColor = "white";
+22 -580
View File
@@ -1,584 +1,26 @@
import Parse from "parse";
export const formJson = (id) => {
let formData;
import { documentCls, templateCls } from "../constant/const";
export const formJson = {
//json form for signYourself
if (id === "sHAnZphf69") {
formData = {
jsonSchema: {
title: "Sign Yourself",
description: "",
type: "object",
required: ["URL", "Name"],
properties: {
URL: {
type: "string",
title: "Select Document",
filetypes: [],
maxfilesizeKB: "10000",
uploadtype: "regular",
helpbody: "",
helplink: ""
},
Name: {
type: "string",
title: "Title",
maxLength: 50
},
Description: {
type: "string",
title: "Description",
maxLength: 500
},
Folder: {
title: "Folder",
parent: null,
data: {
ClassName: "contracts_Document",
FolderNameField: "Name",
FolderTypeField: "Type",
FolderTypeValue: "Folder",
FolderDescription: "Description",
ParentFolderField: "Folder",
FormId: "YjIB7W7Xs6",
Query: 'where={"Folder":{"$exists":false},"Type":"Folder"}'
}
},
ExtUserPtr: {
data: {
valueKey: "objectId",
isPointer: true
}
}
}
},
uiSchema: {
URL: {
"ui:field": "FileUpload"
},
Description: {
"ui:widget": "textarea",
"ui:options": {
rows: 2
}
},
Note: {
"ui:widget": "textarea",
"ui:options": {
rows: 2
}
},
Folder: {
"ui:field": "FolderComponent"
},
ExtUserPtr: {
"ui:field": "HiddenField"
}
},
userSchema: {},
rules: null,
noValidate: false,
liveValidate: false,
isRegisterForm: false,
help: { htmlbody: "" },
description: "",
class: "contracts_Document",
buttons: {
add: {
resetText: "Reset",
submitText: "Submit"
},
edit: { submitText: "Update", cancelText: "Cancel" }
},
formACL: {
"#currentUser#": { read: true, write: true },
"*": { read: true, write: true }
},
redirect_id:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/signaturePdf",
success_message: "Success!",
success_redirect: "Microapp",
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
sHAnZphf69: {
title: "Sign Yourself",
redirectRoute: "signaturePdf",
msgVar: "Document",
Cls: documentCls
},
return formData;
}
//json form for request signature
else if (id === "8mZzFxbG1z") {
const user = Parse.User.current();
const userPtr = JSON.stringify({
__type: "Pointer",
className: "_User",
objectId: user.id
});
formData = {
jsonSchema: {
title: "Request signatures",
description: "",
type: "object",
required: ["URL", "Name", "Note", "TimeToCompleteDays", "Signers"],
properties: {
URL: {
type: "string",
title: "Select Document",
filetypes: [],
maxfilesizeKB: "5000",
uploadtype: "regular",
helpbody: "",
helplink: ""
},
Name: {
type: "string",
title: "Title",
maxLength: 100
},
Description: {
type: "string",
title: "Description",
maxLength: 500
},
Signers: {
type: "array",
title: "Signers",
isSortable: false,
selectAll: false,
selectedData: null,
data: {
class: "contracts_Contactbook",
displayKey: "Name",
valueKey: "objectId",
query: `where={"CreatedBy":${userPtr},"IsDeleted":{"$ne":true}}&keys=Name`,
isPointer: true,
helpbody: "",
helplink: "",
quickAddFormId: "qUGywSWd8e"
}
},
Note: {
type: "string",
title: "Note",
default: "Please review and sign this document",
maxLength: 500
},
Folder: {
title: "Folder",
data: {
ClassName: "contracts_Document",
FolderNameField: "Name",
FolderTypeField: "Type",
FolderTypeValue: "Folder",
FolderDescription: "Description",
ParentFolderField: "Folder",
FormId: "YjIB7W7Xs6",
Query: 'where={"Folder":{"$exists":false},"Type":"Folder"}'
}
},
TimeToCompleteDays: {
type: "number",
title: "Time To Complete (Days)",
default: 15,
maxLength: 5000
},
ExtUserPtr: {
data: {
valueKey: "objectId",
isPointer: true
}
}
}
},
uiSchema: {
URL: {
"ui:field": "FileUpload"
},
Description: {
"ui:widget": "textarea",
"ui:options": {
rows: 2
}
},
Note: {
"ui:widget": "textarea",
"ui:options": {
rows: 2
}
},
Signers: {
"ui:field": "MultiSelectField"
},
Folder: {
"ui:field": "FolderComponent"
},
TimeToCompleteDays: {
"ui:placeholder": "No. of days",
"ui:widget": "updown"
},
ExtUserPtr: {
"ui:field": "HiddenField"
}
},
userSchema: {},
rules: null,
noValidate: false,
liveValidate: false,
isRegisterForm: false,
help: { htmlbody: "" },
description: "",
class: "contracts_Document",
buttons: {
add: {
resetText: "Reset",
submitText: "Submit"
},
edit: { submitText: "Update", cancelText: "Cancel" }
},
formACL: {
"#currentUser#": { read: true, write: true },
"*": { read: true, write: true }
},
redirect_id:
"remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/placeHolderSign",
success_message: "Success!",
success_redirect: "Microapp",
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
return formData;
}
//json form for create new folder
else if (id === "YjIB7W7Xs6") {
formData = {
jsonSchema: {
title: "New Folder",
description: "",
type: "object",
required: ["Name"],
properties: {
Name: {
type: "string",
title: "Name",
maxLength: 50
},
Type: {
type: "string",
data: {
valueKey: "Type",
isPointer: false
},
default: "Folder"
},
Folder: {
title: "Parent Folder",
data: {
class: "contracts_Document",
displayKey: "Name",
valueKey: "objectId",
query: 'where={"Type":"Folder"}&keys=Name',
isPointer: true,
savePointerClass: "",
helpbody: "",
helplink: ""
}
}
}
},
uiSchema: {
Type: {
"ui:field": "HiddenField"
},
Folder: {
"ui:field": "Level1Dropdown"
}
},
userSchema: {},
rules: null,
noValidate: false,
liveValidate: false,
isRegisterForm: false,
help: { htmlbody: "" },
description: "",
class: "contracts_Document",
buttons: {
add: {
resetText: "Reset",
submitText: "Submit"
},
edit: { submitText: "Update", cancelText: "Cancel" }
},
formACL: {
"#currentUser#": { read: true, write: true },
"*": { read: true, write: true }
},
redirect_id: "",
success_message: "Success!",
success_redirect: "",
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
return formData;
}
//json form for add signers
else if (id === "fICciRuUcB") {
formData = {
jsonSchema: {
title: "Add Signer",
description: "",
type: "object",
required: ["Name", "Email", "Phone", "Company"],
properties: {
Name: {
type: "string",
title: "Name",
maxLength: 50
},
Email: {
type: "string",
title: "Email",
maxLength: 50
},
Phone: {
type: "string",
title: "Phone",
maxLength: 50
},
Company: {
type: "string",
title: "Company",
maxLength: 50
},
JobTitle: {
type: "string",
title: "JobTitle",
maxLength: 50
},
IsContactEntry: {
type: "boolean",
data: {
valueKey: "IsContactEntry",
isPointer: false
},
default: true
}
}
},
uiSchema: {
Email: {
"ui:widget": "email"
},
IsContactEntry: {
"ui:field": "HiddenField"
}
},
userSchema: {
email: "$Email",
name: "$Name",
password: "$Phone",
phone: "$Phone",
role: "contracts_User",
username: "$Email"
},
rules: null,
noValidate: false,
liveValidate: false,
isRegisterForm: true,
help: {},
description: "",
class: "contracts_Users",
buttons: {
add: {
resetText: "Reset",
submitText: "Submit"
},
edit: { submitText: "Update", cancelText: "Cancel" }
},
formACL: {
"#currentUser#": { read: true, write: true },
"*": { read: true, write: true }
},
redirect_id: "",
success_message: "Success!",
success_redirect: "",
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
return formData;
}
//json form for add users
else if (id === "lM0xRnM3iE") {
formData = {
jsonSchema: {
title: "Add User",
description: "",
type: "object",
required: ["Name", "Email", "Phone", "JobTitle"],
properties: {
Name: {
type: "string",
title: "Name",
maxLength: 50
},
Email: {
type: "string",
title: "Email",
maxLength: 50
},
Phone: {
type: "string",
title: "Phone",
maxLength: 50
},
JobTitle: {
type: "string",
title: "Job Title",
maxLength: 50
},
Company: {
type: "string",
title: "Company",
maxLength: 50
}
}
},
uiSchema: {
Email: {
"ui:widget": "email"
},
Phone: {
"ui:options": {
inputType: "tel"
}
}
},
userSchema: {
email: "$Email",
name: "$Name",
password: "$Phone",
phone: "$Phone",
role: "contracts_User",
username: "$Email"
},
rules: null,
noValidate: false,
liveValidate: false,
isRegisterForm: true,
help: {},
description: "",
class: "contracts_Users",
buttons: {
add: {
resetText: "Reset",
submitText: "Submit"
},
edit: { submitText: "Update", cancelText: "Cancel" }
},
formACL: {
"#currentUser#": { read: true, write: true },
"*": { read: true, write: true }
},
redirect_id: "",
success_message: "Success!",
success_redirect: "",
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
return formData;
} else if (id === "qUGywSWd8e") {
formData = {
jsonSchema: {
title: "Add Contact",
description: "",
type: "object",
required: ["Name", "Email", "Phone"],
properties: {
Name: {
type: "string",
title: "Name",
maxLength: 50
},
Email: {
type: "string",
title: "Email",
maxLength: 50
},
Phone: {
type: "string",
title: "Phone",
maxLength: 50
}
}
},
uiSchema: {
Email: {
"ui:widget": "email"
},
Phone: {
"ui:options": {
inputType: "tel"
}
}
},
userSchema: {
name: "$Name",
username: "$Email",
email: "$Email",
password: "$Phone",
phone: "$Phone",
role: "contracts_Guest"
},
restrict_form_entry: {
enable_captcha: false,
allow_one_entry_per_ip_address: false,
allow_one_entry_per_user: false,
enable_geo_fence: false
},
restrict_form_access: {
entryCount: ""
},
help: {},
email_template: {
from: "",
to: "",
subject: "",
message: ""
},
buttons: {
add: {
submitText: "Submit",
resetText: "Reset"
},
edit: {
submitText: "Update",
cancelText: "Cancel"
}
},
appId: {
__type: "Pointer",
className: "w_appinfo",
objectId: "aIPmIvMzGM"
},
formACL: {
"*": {
read: true,
write: true
},
"#currentUser#": {
read: true,
write: true
}
},
class: "contracts_Contactbook",
description: "",
success_message: "Success!",
form_permission: "private",
success_redirect: "",
redirect_id: "",
isRegisterForm: true,
liveValidate: false,
noValidate: false,
validFunction:
"ZnVuY3Rpb24gdmFsaWRhdGUoZm9ybURhdGEsIGVycm9ycykgeyBpZiAoZm9ybURhdGEucGFzczEgIT09IGZvcm1EYXRhLnBhc3MyKSB7IGVycm9ycy5wYXNzMi5hZGRFcnJvcignUGFzc3dvcmRzIGRvbid0IG1hdGNoJyk7IH0gcmV0dXJuIGVycm9yczsgfQ=="
};
return formData;
//json form for request signatures
"8mZzFxbG1z": {
title: "Request Signatures",
msgVar: "Document",
redirectRoute: "placeHolderSign",
Cls: documentCls,
signers: true
},
//json form for template
template: {
title: "New Template",
redirectRoute: "template",
msgVar: "Template",
Cls: templateCls
}
};
+2 -2
View File
@@ -27,13 +27,13 @@
"planName": "THANKS PLAN",
"currency": "$",
"price": "29.99",
"subtitle": "Use code 'FREEBETA' to get this for free(First 1000 users)",
"subtitle": "Use code <span style='background-color: yellow;'>'FREEBETA'</span> to get this for free(First 1000 users)",
"btnText": "Subscribe",
"url": "https://subscriptions.zoho.in/subscribe/ef798486e6a0a11ea65f2bae8f2af901dbadf3175d495584fd25b9af5d2f2b9e/thanks",
"target": "_self",
"benefits": [
"Sign Unlimited Documents",
"Unlimited Secure Doc Storage with OpenSignDrive",
"Unlimited Secure Doc Storage with OpenSignDrive",
"Unlimited Guest Signers",
"Unlimited completion certificates",
"Unique Code(OTP) verification for guest signers",
+47 -24
View File
@@ -8,15 +8,16 @@ import axios from "axios";
import { useSelector } from "react-redux";
import Parse from "parse";
import ModalUi from "../primitives/ModalUi";
import { useNavigate } from "react-router-dom";
import { useNavigate, useLocation, Outlet } from "react-router-dom";
const HomeLayout = ({ children }) => {
const HomeLayout = () => {
const navigate = useNavigate();
const location = useLocation();
const { width } = useWindowSize();
const [isOpen, setIsOpen] = useState(true);
const arr = useSelector((state) => state.TourSteps);
const [isUserValid, setIsUserValid] = useState(true);
const [isLoader, setIsLoader] = useState(true);
useEffect(() => {
(async () => {
try {
@@ -27,6 +28,7 @@ const HomeLayout = ({ children }) => {
});
if (user) {
setIsUserValid(true);
setIsLoader(false);
} else {
setIsUserValid(false);
}
@@ -184,7 +186,7 @@ const HomeLayout = ({ children }) => {
console.log("err ", err);
} finally {
localStorage.removeItem("accesstoken");
navigate("/", { replace: true });
navigate("/", { replace: true, state: { from: location } });
}
};
return (
@@ -194,27 +196,48 @@ const HomeLayout = ({ children }) => {
</div>
{isUserValid ? (
<>
<div className="flex md:flex-row flex-col z-50">
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
<div className="relative h-screen flex flex-col justify-between w-full overflow-y-auto">
<div className="bg-[#eef1f5] p-3">{children}</div>
<div className="z-30">
<Footer />
</div>
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
</div>
<Tour
onRequestClose={closeTour}
steps={tourConfigs}
isOpen={isTour}
closeWithMask={false}
disableKeyboardNavigation={["esc"]}
// disableInteraction={true}
scrollOffset={-100}
rounded={5}
showCloseButton={isCloseBtn}
/>
) : (
<>
<div className="flex md:flex-row flex-col z-50">
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
<div className="relative h-screen flex flex-col justify-between w-full overflow-y-auto">
<div className="bg-[#eef1f5] p-3">{<Outlet />}</div>
<div className="z-30">
<Footer />
</div>
</div>
</div>
<Tour
onRequestClose={closeTour}
steps={tourConfigs}
isOpen={isTour}
closeWithMask={false}
disableKeyboardNavigation={["esc"]}
// disableInteraction={true}
scrollOffset={-100}
rounded={5}
showCloseButton={isCloseBtn}
/>
</>
)}
</>
) : (
<ModalUi title={"Session Expired"} isOpen={true} showClose={false}>
@@ -5,6 +5,7 @@ import axios from "axios";
import "../styles/report.css";
import ModalUi from "./ModalUi";
import AppendFormInForm from "../components/AppendFormInForm";
import { modalSubmitBtnColor, modalCancelBtnColor } from "../constant/const";
const ReportTable = ({
ReportName,
List,
@@ -342,13 +343,15 @@ const ReportTable = ({
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={() => handleDelete(item)}
className="bg-[#1ab6ce] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-3 focus:outline-none"
className="px-4 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
style={{ backgroundColor: modalSubmitBtnColor }}
>
Yes
</button>
<button
onClick={handleCloseDeleteModal}
className="bg-[#188ae2] rounded-sm shadow-md text-[12px] font-semibold uppercase text-white py-1.5 px-3 text-center ml-[2px] focus:outline-none"
className="px-4 py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
style={{ backgroundColor: modalCancelBtnColor }}
>
No
</button>
@@ -371,7 +374,7 @@ const ReportTable = ({
)}
{heading.includes("Folder") && (
<td className="px-4 py-2">
{item?.Folder?.Name || "OpenSignDrive"}
{item?.Folder?.Name || "OpenSignDrive"}
</td>
)}
<td className="px-4 py-2">
@@ -1,336 +0,0 @@
import React, { useState } from "react";
import sanitizeFileName from "./sanitizeFileName";
import Parse from "parse";
import DropboxChooser from "../components/fields/DropboxChoose";
import Alert from "./Alert";
import SelectFolder from "../components/fields/SelectFolder";
// import SignersInput from "../components/fields/SignersInput";
import Title from "../components/Title";
import { useNavigate } from "react-router-dom";
import { templateCls } from "../constant/const";
const TemplateForm = () => {
const navigate = useNavigate();
const [signers, setSigners] = useState([]);
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
const [formData, setFormData] = useState({
Name: "",
Description: "",
Note: "Please review and sign this document"
});
const [fileupload, setFileUpload] = useState([]);
const [fileload, setfileload] = useState(false);
const [percentage, setpercentage] = useState(0);
const [isAlert, setIsAlert] = useState(false);
const [isSubmit, setIsSubmit] = useState(false);
const [isErr, setIsErr] = useState("");
const handleStrInput = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const handleFileInput = (e) => {
setpercentage(0);
try {
let files = e.target.files;
if (typeof files[0] !== "undefined") {
const mb = Math.round(files[0].bytes / Math.pow(1024, 2));
if (mb > 10) {
alert(
`The selected file size is too large. Please select a file less than ${Math.round(
10
)} MB`
);
return;
}
handleFileUpload(files[0]);
} else {
alert("Please select file.");
return false;
}
} catch (error) {
alert(error.message);
return false;
}
};
const handleFileUpload = async (file) => {
Parse.serverURL = process.env.REACT_APP_SERVERURL;
Parse.initialize(process.env.REACT_APP_APPID);
setfileload(true);
const fileName = file.name;
const name = sanitizeFileName(fileName);
const pdfFile = file;
const parseFile = new Parse.File(name, pdfFile);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
};
const dropboxSuccess = async (files) => {
setfileload(true);
const file = files[0];
const url = file.link;
const mb = Math.round(file.bytes / Math.pow(1024, 2));
if (mb > 10) {
setTimeout(() => {
alert(
`The selected file size is too large. Please select a file less than 10 MB`
);
}, 500);
return;
} else {
const name = sanitizeFileName(file.name);
const parseFile = new Parse.File(name, { uri: url });
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
}
};
const dropboxCancel = async () => {};
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmit(true);
try {
const currentUser = Parse.User.current();
const template = new Parse.Object(templateCls);
Object.entries(formData).forEach((item) =>
template.set(item[0], item[1])
);
template.set("URL", fileupload);
template.set("CreatedBy", Parse.User.createWithoutData(currentUser.id));
if (folder && folder.ObjectId) {
template.set("Folder", {
__type: "Pointer",
className: templateCls,
objectId: folder.ObjectId
});
}
if (signers && signers.length > 0) {
template.set("Signers", signers);
}
const ExtCls = JSON.parse(localStorage.getItem("Extand_Class"));
template.set("ExtUserPtr", {
__type: "Pointer",
className: "contracts_Users",
objectId: ExtCls[0].objectId
});
const res = await template.save();
if (res) {
setSigners([]);
setFolder({ ObjectId: "", Name: "" });
setFormData({
Name: "",
Description: "",
Note: ""
});
setFileUpload([]);
setpercentage(0);
navigate(
"/asmf/remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/template/" +
res.id
);
}
} catch (err) {
console.log("err ", err);
setIsErr(true);
} finally {
setIsAlert(true);
setTimeout(() => {
setIsAlert(false);
}, 1000);
setIsSubmit(false);
}
};
const handleFolder = (data) => {
setFolder(data);
};
// const handleSigners = (data) => {
// if (data && data.length > 0) {
// const updateSigners = data.map((x) => ({
// __type: "Pointer",
// className: "contracts_Contactbook",
// objectId: x
// }));
// setSigners(updateSigners);
// }
// };
const handleReset = () => {
setSigners([]);
setFolder({ ObjectId: "", Name: "" });
setFormData({
Name: "",
Description: "",
Note: ""
});
setFileUpload([]);
setpercentage(0);
};
return (
<div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50">
<Title title="New Template" />
{isAlert && (
<Alert type={isErr ? "danger" : "success"}>
{isErr
? "Something went wrong please try again!"
: "Template created successfully!"}
</Alert>
)}
<form onSubmit={handleSubmit}>
<h1 className="text-[20px] font-semibold mb-4">New Template</h1>
{fileload && (
<div className="flex items-center gap-x-2">
<div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200">
<div
className="h-2 rounded-full bg-blue-500"
style={{ width: `${percentage}%` }}
></div>
</div>
<span className="text-black text-sm">{percentage}%</span>
</div>
)}
<div className="text-xs">
<label className="block">
File<span className="text-red-500 text-[13px]">*</span>
</label>
{fileupload.length > 0 ? (
<div className="flex gap-2 justify-center items-center">
<div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]">
<div className="break-all">
file selected : {fileupload?.split("/")[3]?.split("_")[1]}
</div>
<div
onClick={() => {
setFileUpload([]);
}}
className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500"
>
<i className="fa-solid fa-xmark"></i>
</div>
</div>
{process.env.REACT_APP_DROPBOX_API_KEY && (
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
)}
</div>
) : (
<div className="flex gap-2 justify-center items-center">
<input
type="file"
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
onChange={(e) => handleFileInput(e)}
accept="application/pdf,application/vnd.ms-excel"
required
/>
{process.env.REACT_APP_DROPBOX_API_KEY && (
<DropboxChooser
onSuccess={dropboxSuccess}
onCancel={dropboxCancel}
/>
)}
</div>
)}
</div>
<div className="text-xs mt-2">
<label className="block">
Title<span className="text-red-500 text-[13px]">*</span>
</label>
<input
name="Name"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Name}
onChange={(e) => handleStrInput(e)}
required
/>
</div>
<div className="text-xs mt-2">
<label className="block">
Note<span className="text-red-500 text-[13px]">*</span>
</label>
<input
name="Note"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Note}
onChange={(e) => handleStrInput(e)}
required
/>
</div>
<div className="text-xs mt-2">
<label className="block">Description</label>
<input
name="Description"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
value={formData.Description}
onChange={(e) => handleStrInput(e)}
/>
</div>
{/* <SignersInput onChange={handleSigners} /> */}
<SelectFolder onSuccess={handleFolder} folderCls={templateCls} />
<div className="flex items-center mt-3 gap-2 text-white">
<button
className={`${
isSubmit && "cursor-progress"
} bg-[#1ab6ce] rounded-sm shadow-md text-[14px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none`}
type="submit"
disabled={isSubmit}
>
Submit
</button>
<div
className="bg-[#188ae2] rounded-sm shadow-md text-[14px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none"
onClick={() => handleReset()}
>
Reset
</div>
</div>
</form>
</div>
);
};
export default TemplateForm;
+57
View File
@@ -0,0 +1,57 @@
import React, { useState, useEffect } from "react";
import Parse from "parse";
import { Outlet, useNavigate, useLocation } from "react-router-dom";
import ModalUi from "./ModalUi";
const Validate = () => {
const navigate = useNavigate();
const location = useLocation();
const [isUserValid, setIsUserValid] = useState(true);
useEffect(() => {
(async () => {
try {
// Use the session token to validate the user
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(Parse.User.current().id, {
sessionToken: localStorage.getItem("accesstoken")
});
if (user) {
setIsUserValid(true);
} else {
setIsUserValid(false);
}
} catch (error) {
// Session token is invalid or there was an error
setIsUserValid(false);
}
})();
}, []);
const handleLoginBtn = () => {
try {
Parse.User.logOut();
} catch (err) {
console.log("err ", err);
} finally {
localStorage.removeItem("accesstoken");
navigate("/", { replace: true, state: { from: location } });
}
};
return isUserValid ? (
<div>
<Outlet />
</div>
) : (
<ModalUi title={"Session Expired"} isOpen={true} showClose={false}>
<div className="flex flex-col justify-center items-center py-4 md:py-5 gap-5">
<p className="text-xl font-normal">Your session has expired.</p>
<button
onClick={handleLoginBtn}
className="text-base px-3 py-1.5 rounded shadow-md text-white bg-[#1ab6ce]"
>
Login
</button>
</div>
</ModalUi>
);
};
export default Validate;
@@ -1,7 +1,7 @@
import React, { useEffect } from "react";
import Parse from "parse";
const ValidateRoute = ({ children }) => {
import { Outlet } from "react-router-dom";
const ValidateRoute = () => {
useEffect(() => {
(async () => {
try {
@@ -28,7 +28,7 @@ const ValidateRoute = ({ children }) => {
localStorage.removeItem("accesstoken");
}
};
return <div>{children}</div>;
return <div>{<Outlet />}</div>;
};
export default ValidateRoute;
+1 -87
View File
@@ -124,8 +124,7 @@ export const login = (username, password) => async (dispatch) => {
const currentUser = Parse.User.current();
await Parse.Cloud.run("getUserDetails", {
email: currentUser.get("email")
})
.then(
}).then(
(results) => {
let userinfo = results.toJSON();
if (userinfo.TenantId) {
@@ -215,95 +214,10 @@ export const fetchRoleEnum = (name) => async (dispatch) => {
dispatch({ type: "FETCH_ROLE", payload: response });
};
export const setEnableCart = (val) => async (dispatch) => {
dispatch({
type: "ENABLE_CART",
payload: val
});
};
export const setCartUpdateData = (val) => async (dispatch) => {
dispatch({
type: "UPDATE_CART",
payload: val
});
};
export const addItemsToCart = (val) => async (dispatch) => {
dispatch({
type: "ADD_CART",
payload: val
});
};
export const clearCartData = () => async (dispatch) => {
dispatch({
type: "CLEAR_CART",
payload: []
});
};
export const SaveMultipleCart = (val) => async (dispatch) => {
dispatch({
type: "MULTI_CART",
payload: val
});
};
export const removeFromCart = (val) => async (dispatch) => {
dispatch({
type: "REMOVE_CART",
payload: val
});
};
export const onChangeLevel1Dropdown = (id, name) => async (dispatch) => {
localStorage.setItem(`_dd${name}`, id);
let _data = { [name]: `${id}` };
dispatch({ type: "Level1_Dropdown", payload: _data });
};
export const onChangeLevel2Dropdown = (id, name) => async (dispatch) => {
localStorage.setItem(`_dd${name}`, id);
let _data = { [name]: `${id}` };
dispatch({ type: "Level2_Dropdown", payload: _data });
};
export const onChangeLevel3Dropdown = (id, name) => async (dispatch) => {
localStorage.setItem(`_dd${name}`, id);
let _data = { [name]: `${id}` };
dispatch({ type: "Level3_Dropdown", payload: _data });
};
export const removeState = () => async (dispatch) => {
dispatch({ type: "REMOVE_STATE", payload: {} });
};
export const removeLevel2State = () => async (dispatch) => {
dispatch({ type: "removeLevel2", payload: {} });
};
export const removeLevel3State = () => async (dispatch) => {
dispatch({ type: "removeLevel3", payload: {} });
};
export const showTenantName = (name) => async (dispatch) => {
dispatch({ type: "SHOW_TENANT", payload: name || null });
};
export const saveDependantDDValue = (id, value) => async (dispatch) => {
let _data = { [`${id}_dd`]: value };
dispatch({ type: "SAVE_DEPENDANTDD", payload: _data });
};
export const removeDependantDDValue = (id, value) => async (dispatch) => {
let _data = { [`${id}_dd`]: value };
dispatch({ type: "REMOVE_DEPENDANTDD", payload: _data });
};
export const remove_AlldependantDD = () => async (dispatch) => {
dispatch({ type: "REMOVE_ALLDEPENDANTDD", payload: {} });
};
export const save_tourSteps = (steps) => async (dispatch) => {
dispatch({ type: "SAVE_TOURSTEPS", payload: steps });
};
+1 -1
View File
@@ -24,7 +24,7 @@ const Dashboard = (props) => {
getDashboard(localStorage.getItem("PageLanding"));
}
} else {
navigate("/", { replace: true });
navigate("/", { replace: true, state: { from: location } });
}
// eslint-disable-next-line
}, [id]);
File diff suppressed because it is too large Load Diff
+184
View File
@@ -0,0 +1,184 @@
import React, { useEffect, useState } from "react";
import Title from "../components/Title";
import axios from "axios";
import Alert from "../primitives/Alert";
import ModalUi from "../primitives/ModalUi";
import { rejectBtn, submitBtn } from "../constant/const";
import { openInNewTab } from "../constant/Utils";
function GenerateToken() {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [apiToken, SetApiToken] = useState("");
const [isLoader, setIsLoader] = useState(true);
const [copied, setCopied] = useState(false);
const [isGenerate, setIsGenerate] = useState(false);
const [isErr, setIsErr] = useState(false);
const [isModal, setIsModal] = useState(false);
useEffect(() => {
fetchToken();
// eslint-disable-next-line
}, []);
const fetchToken = async () => {
try {
const url = parseBaseUrl + "functions/getapitoken";
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
};
const res = await axios.post(url, {}, { headers: headers });
if (res) {
SetApiToken(res.data.result.result);
}
setIsLoader(false);
} catch (err) {
SetApiToken();
setIsLoader(false);
console.log("Err", err);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoader(true);
setIsModal(false);
try {
const url = parseBaseUrl + "functions/generateapitoken";
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
};
await axios.post(url, {}, { headers: headers }).then((res) => {
if (res) {
SetApiToken(res.data.result.token);
// localStorage.setItem("apiToken", res.data.result.token);
setIsGenerate(true);
setTimeout(() => {
setIsGenerate(false);
}, 1500);
setIsLoader(false);
} else {
console.error("Error while generating Token");
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
}
});
} catch (error) {
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
console.log("err", error);
}
};
const copytoclipboard = (text) => {
navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => {
setCopied(false);
}, 1500); // Reset copied state after 1.5 seconds
};
const handleModal = () => setIsModal(!isModal);
return (
<React.Fragment>
<Title title={"API Token"} />
{isGenerate && (
<Alert type="success">Token generated successfully!</Alert>
)}
{copied && <Alert type="success">Copied</Alert>}
{isErr && <Alert type="danger">Something went wrong!</Alert>}
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<div className="bg-white flex flex-col justify-center shadow rounded">
<h1 className="ml-4 mt-3 mb-2 font-semibold">API Token</h1>
<ul className="w-full flex flex-col p-2 text-sm">
<li
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<span className="w-[40%]">Api Token:</span>{" "}
<span
id="token"
className="w-[60%] md:text-end cursor-pointer"
onClick={() => copytoclipboard(apiToken)}
>
{apiToken && apiToken}
</span>
</li>
</ul>
<div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4">
<button
type="button"
onClick={apiToken ? handleModal : handleSubmit}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
>
{apiToken ? "Regenerate Token" : "Generate Token"}
</button>
<button
type="button"
onClick={() =>
openInNewTab(
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
)
}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
>
View Docs
</button>
</div>
<ModalUi
isOpen={isModal}
title={"Regenerate Token"}
handleClose={handleModal}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
Are you sure you want to regenerate token it will expire old
token?
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={handleSubmit}
className={submitBtn + "ml-[2px]"}
>
Yes
</button>
<button onClick={handleModal} className={rejectBtn}>
No
</button>
</div>
</div>
</ModalUi>
</div>
)}
</React.Fragment>
);
}
export default GenerateToken;
+2 -2
View File
@@ -30,9 +30,9 @@ function RemoteApp({ app }) {
boxShadow: "0 1px 3px 0 rgba(0, 0, 0, 0.33)",
backgroundColor: "#ffffff",
width: "100%",
overflow: "hidden",
borderRadius: 3,
minHeight: "70vh"
height: "100%",
minHeight: "100vh"
}}
>
{RemoteComponent && <RemoteComponent />}
+90 -115
View File
@@ -8,12 +8,15 @@ import axios from "axios";
import Title from "../components/Title";
import GoogleSignInBtn from "../components/LoginGoogle";
// import LoginFacebook from "../components/LoginFacebook";
import { NavLink, useNavigate } from "react-router-dom";
import { NavLink, useNavigate, useLocation } from "react-router-dom";
import login_img from "../assets/images/login_img.svg";
import { useWindowSize } from "../hook/useWindowSize";
import ModalUi from "../primitives/ModalUi";
import { modalCancelBtnColor, modalSubmitBtnColor } from "../constant/const";
function Login(props) {
const navigate = useNavigate();
const location = useLocation();
const { width } = useWindowSize();
const [state, setState] = useState({
email: "",
@@ -132,6 +135,9 @@ function Login(props) {
`${localStorage.getItem("_appName")}_appeditor`
) {
userSettings.forEach(async (element) => {
const redirectUrl =
location?.state?.from ||
`/${element.pageType}/${element.pageId}`;
if (element.role === _currentRole) {
let _role = _currentRole.replace(
`${localStorage.getItem("_appName")}_`,
@@ -260,9 +266,8 @@ function Login(props) {
localStorage.removeItem(
"userDetails"
);
navigate(
`/${element.pageType}/${element.pageId}`
);
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
} else {
navigate(`/subscription`, {
replace: true
@@ -274,9 +279,8 @@ function Login(props) {
});
}
} else {
navigate(
`/${element.pageType}/${element.pageId}`
);
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
}
}
} else {
@@ -313,9 +317,8 @@ function Login(props) {
});
}
} else {
navigate(
`/${element.pageType}/${element.pageId}`
);
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
}
}
},
@@ -462,6 +465,9 @@ function Login(props) {
`${localStorage.getItem("_appName")}_appeditor`
) {
userSettings.forEach(async (element) => {
const redirectUrl =
location?.state?.from ||
`/${element.pageType}/${element.pageId}`;
if (element.role === _currentRole) {
let _role = _currentRole.replace(
`${localStorage.getItem("_appName")}_`,
@@ -569,9 +575,7 @@ function Login(props) {
if (billingDate) {
if (billingDate > new Date()) {
localStorage.removeItem("userDetails");
navigate(
`/${element.pageType}/${element.pageId}`
);
navigate(redirectUrl);
} else {
navigate(`/subscription`, {
replace: true
@@ -581,9 +585,7 @@ function Login(props) {
navigate(`/subscription`, { replace: true });
}
} else {
navigate(
`/${element.pageType}/${element.pageId}`
);
navigate(redirectUrl);
}
}
} else {
@@ -599,9 +601,8 @@ function Login(props) {
if (billingDate) {
if (billingDate > new Date()) {
localStorage.removeItem("userDetails");
navigate(
`/${element.pageType}/${element.pageId}`
);
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
} else {
navigate(`/subscription`, { replace: true });
}
@@ -609,9 +610,7 @@ function Login(props) {
navigate(`/subscription`, { replace: true });
}
} else {
navigate(
`/${element.pageType}/${element.pageId}`
);
navigate(redirectUrl);
}
}
},
@@ -1134,100 +1133,76 @@ function Login(props) {
{state.toastDescription}
</div>
</div>
{isModal && (
<div
className="modal fade show"
id="exampleModal"
tabIndex="-1"
role="dialog"
style={{
display: "block",
zIndex: 1,
backgroundColor: "rgba(0,0,0,0.5)"
}}
>
<div className="modal-dialog" role="document">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title font-semibold">
Additional Info
</h5>
<span>
<span></span>
</span>
</div>
<div className="modal-body">
<form className="text-sm">
<div className="form-group">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="col-form-label"
>
Company{" "}
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="form-control"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
required
/>
</div>
<div className="form-group">
<label
htmlFor="JobTitle"
style={{ display: "flex" }}
className="col-form-label"
>
Job Title
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="form-control"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
required
/>
</div>
<div className="mt-4">
<button
type="button"
className="bg-[#17a2b8] text-sm p-2 text-white rounded uppercase"
onClick={(e) => handleSubmitbtn(e)}
style={{ marginRight: 10 }}
>
Login
</button>
<button
type="button"
className="bg-[#6c757d] text-sm p-2 text-white rounded uppercase"
onClick={handleCloseModal}
style={{ width: 90 }}
>
Cancel
</button>
</div>
</form>
</div>
</div>
<ModalUi isOpen={isModal} title="Additional Info" showClose={false}>
<form className="px-4 py-3">
<div className="mb-3">
<label
htmlFor="Company"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Company <span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="Company"
value={userDetails.Company}
onChange={(e) =>
setUserDetails({
...userDetails,
Company: e.target.value
})
}
required
/>
</div>
</div>
)}
<div className="mb-3">
<label
htmlFor="JobTitle"
style={{ display: "flex" }}
className="block text-xs text-gray-700 font-semibold"
>
Job Title
<span style={{ fontSize: 13, color: "red" }}>*</span>
</label>
<input
type="text"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
id="JobTitle"
value={userDetails.Destination}
onChange={(e) =>
setUserDetails({
...userDetails,
Destination: e.target.value
})
}
required
/>
</div>
<div className="mt-4">
<button
type="button"
className="px-3 py-1.5 text-white rounded shadow-md text-center focus:outline-none "
onClick={(e) => handleSubmitbtn(e)}
style={{
marginRight: 10,
backgroundColor: modalSubmitBtnColor
}}
>
Login
</button>
<button
type="button"
className="py-1.5 text-black border-[1px] border-[#ccc] shadow-md rounded focus:outline-none"
onClick={handleCloseModal}
style={{ width: 75, backgroundColor: modalCancelBtnColor }}
>
Cancel
</button>
</div>
</form>
</ModalUi>
</>
) : (
<div
+4 -2
View File
@@ -2,7 +2,7 @@
import React from "react";
import Title from "../components/Title";
const PageNotFound = () => {
const PageNotFound = ({ prefix }) => {
return (
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
<Title title={"Page Not Found"} />
@@ -10,7 +10,9 @@ const PageNotFound = () => {
<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>
<p className="text-[30px] lg:text-[50px] text-black">
{prefix ? prefix : "Page"} Not Found
</p>
</div>
</div>
);
+16 -7
View File
@@ -5,7 +5,7 @@ import axios from "axios";
import { fetchAppInfo, showTenantName } from "../redux/actions/index";
import { connect } from "react-redux";
import Title from "../components/Title";
import { useNavigate } from "react-router-dom";
import { useNavigate, useLocation } from "react-router-dom";
const appId = localStorage.getItem("AppID12");
const server = localStorage.getItem("BaseUrl12");
@@ -13,6 +13,7 @@ Parse.serverURL = server;
Parse.initialize(appId);
const PgSignUp = (props) => {
const navigate = useNavigate();
const location = useLocation();
const [parseBaseUrl] = useState(localStorage.getItem("BaseUrl12"));
const [parseAppId] = useState(localStorage.getItem("AppID12"));
const [formData, setFormData] = useState({
@@ -187,7 +188,7 @@ const PgSignUp = (props) => {
console.log("err ", error);
if (error.message === "Account already exists for this username.") {
alert("Account already exists!");
navigate("/");
navigate("/", { replace: true });
} else {
setIsLoader(false);
alert("Something went wrong, please try again later!");
@@ -351,7 +352,7 @@ const PgSignUp = (props) => {
element.pageType
);
setIsLoader(false);
navigate("/");
navigate("/", { replace: true });
} else {
extendedInfo.forEach((x) => {
if (x.TenantId) {
@@ -389,9 +390,12 @@ const PgSignUp = (props) => {
);
setIsLoader(false);
alert("Registered user successfully");
navigate(
`/${element.pageType}/${element.pageId}`
);
const redirectUrl =
location?.state?.from ||
`/${element.pageType}/${element.pageId}`;
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
}
} else {
alert("Registered user successfully");
@@ -402,7 +406,12 @@ const PgSignUp = (props) => {
);
localStorage.setItem("pageType", element.pageType);
setIsLoader(false);
navigate(`/${element.pageType}/${element.pageId}`);
const redirectUrl =
location?.state?.from ||
`/${element.pageType}/${element.pageId}`;
// Redirect to the appropriate URL after successful login
navigate(redirectUrl);
}
},
(error) => {
+19 -28
View File
@@ -39,7 +39,7 @@ const PlanSubscriptions = () => {
setIsLoader(false);
setYearlyVisible(false);
} else {
navigate("/");
navigate("/", { replace: true });
}
// eslint-disable-next-line
}, []);
@@ -78,31 +78,6 @@ const PlanSubscriptions = () => {
id="monthlyPlans"
className={`${yearlyVisible ? "none" : "block my-2"}`}
>
{/* <div className=" my-2 w-full flex justify-center">
<ul
className="navs"
style={{
listStyle: "none",
display: "flex",
alignItems: "baseline"
}}
>
<li className="nav-item">
<button className="nav-link frequency active" name="1_months">
Monthly
</button>
</li>
<li className="nav-item">
<button
className="nav-link frequency "
onClick={toggleFrequency}
name="1_years"
>
Yearly
</button>
</li>
</ul>
</div> */}
<div className="flex justify-center w-full my-2">
<ul className=" flex flex-col md:flex-row h-full bg-white justify-center border-collapse border-[1px] border-gray-300">
{plansArr.map((item) => (
@@ -129,11 +104,27 @@ const PlanSubscriptions = () => {
<div
className={`${
item.subtitle.length <= 32
? "w-[150px] text-center"
? "w-[150px] text-center"
: ""
} text-sm text-center my-2`}
>
<p>{item.subtitle}</p>
<p
style={{
backgroundColor: item.subtitlecolor
? item.subtitlecolor
: "white"
}}
>
{item.subtitle.includes("<") ? (
<div
dangerouslySetInnerHTML={{
__html: item.subtitle
}}
/>
) : (
<span>{item.subtitle}</span>
)}
</p>
</div>
</div>
+4 -9
View File
@@ -5,6 +5,7 @@ import axios from "axios";
import reportJson from "../json/ReportJson";
import { useParams } from "react-router-dom";
import Title from "../components/Title";
import PageNotFound from "./PageNotFound";
const Report = () => {
const { id } = useParams();
@@ -15,7 +16,7 @@ const Report = () => {
const [heading, setHeading] = useState([]);
const [isNextRecord, setIsNextRecord] = useState(false);
const [isMoreDocs, setIsMoreDocs] = useState(true);
const [form, setForm]= useState("")
const [form, setForm] = useState("");
const abortController = new AbortController();
const docPerPage = 10;
@@ -51,7 +52,7 @@ const Report = () => {
setActions(json.actions);
setHeading(json.heading);
setReportName(json.reportName);
setForm(json.form)
setForm(json.form);
Parse.serverURL = localStorage.getItem("BaseUrl12");
Parse.initialize(localStorage.getItem("AppID12"));
const currentUser = Parse.User.current().id;
@@ -159,13 +160,7 @@ const Report = () => {
form={form}
/>
) : (
<div className="flex items-center justify-center h-screen w-full bg-white rounded">
<div className="text-center">
<p className="text-[30px] lg:text-[50px] text-black">
Report Not Found
</p>
</div>
</div>
<PageNotFound prefix={"Report"} />
)}
</>
)}
+171
View File
@@ -0,0 +1,171 @@
import React, { useEffect, useState } from "react";
import Title from "../components/Title";
import axios from "axios";
import Alert from "../primitives/Alert";
import ModalUi from "../primitives/ModalUi";
import { rejectBtn, submitBtn } from "../constant/const";
import { openInNewTab } from "../constant/Utils";
import Parse from "parse";
function Webhook() {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
const [parseAppId] = useState(localStorage.getItem("parseAppId"));
const [webhook, setWebhook] = useState();
const [isLoader, setIsLoader] = useState(true);
const [isGenerate, setIsGenerate] = useState(false);
const [isErr, setIsErr] = useState(false);
const [isModal, setIsModal] = useState(false);
useEffect(() => {
fetchWebhook();
// eslint-disable-next-line
}, []);
const fetchWebhook = async () => {
const email = Parse.User.current().getEmail();
const params = { email: email };
try {
const extRes = await Parse.Cloud.run("getUserDetails", params);
if (extRes) {
setWebhook(extRes.get("Webhook"));
}
setIsLoader(false);
} catch (err) {
setWebhook();
setIsLoader(false);
console.log("Err", err);
}
};
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoader(true);
setIsModal(false);
try {
const params = { url: webhook };
const url = parseBaseUrl + "functions/savewebhook";
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId,
sessiontoken: localStorage.getItem("accesstoken")
};
await axios.post(url, params, { headers: headers }).then((res) => {
if (res.data && res.data.result && res.data.result.Webhook) {
setWebhook(res.data.result.Webhook);
setIsGenerate(true);
setTimeout(() => {
setIsGenerate(false);
}, 1500);
setIsLoader(false);
} else {
console.error("Error while generating webhook");
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
}
});
} catch (error) {
setIsLoader(false);
setIsErr(true);
setTimeout(() => {
setIsErr(false);
}, 1500);
console.log("err", error);
}
};
const handleModal = () => setIsModal(!isModal);
return (
<React.Fragment>
<Title title={"Webhook"} />
{isGenerate && <Alert type="success">Webhook added successfully!</Alert>}
{isErr && <Alert type="danger">Something went wrong!</Alert>}
{isLoader ? (
<div
style={{
height: "100vh",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<div className="bg-white flex flex-col justify-center shadow rounded">
<h1 className="ml-4 mt-3 mb-2 font-semibold">Webhook</h1>
<ul className="w-full flex flex-col p-2 text-sm">
<li
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
>
<span className="w-[40%]">Webhook:</span>{" "}
<span id="token" className="w-[60%] md:text-end cursor-pointer">
{webhook && webhook}
</span>
</li>
</ul>
<div className="flex flex-col md:flex-row items-center justify-center gap-2 pb-4">
<button
type="button"
onClick={handleModal}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-4 py-2 text-xs md:text-base focus:outline-none"
>
{webhook ? "Update Webhook" : "Add Webhook"}
</button>
<button
type="button"
onClick={() =>
openInNewTab(
"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"
)
}
className="rounded hover:bg-[#15b4e9] border-[1px] border-[#15b4e9] text-[#15b4e9] hover:text-white px-11 py-2 text-xs md:text-base focus:outline-none"
>
View Docs
</button>
</div>
<ModalUi
isOpen={isModal}
title={"Regenerate Token"}
handleClose={handleModal}
>
<div className="m-[20px]">
<div className="text-lg font-normal text-black">
<label className="text-sm ml-2">Webhook</label>
<input
value={webhook}
onChange={(e) => setWebhook(e.target.value)}
placeholder="Enter webhook url"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
/>
</div>
<hr className="bg-[#ccc] mt-4 " />
<div className="flex items-center mt-3 gap-2 text-white">
<button
onClick={handleSubmit}
className={submitBtn + "ml-[2px]"}
>
Yes
</button>
<button onClick={handleModal} className={rejectBtn}>
No
</button>
</div>
</div>
</ModalUi>
</div>
)}
</React.Fragment>
);
}
export default Webhook;
File diff suppressed because it is too large Load Diff
+53 -198
View File
@@ -1,209 +1,64 @@
#snackbar {
visibility: hidden;
min-width: 250px;
margin-left: -125px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 99999;
left: 50%;
visibility: hidden;
min-width: 250px;
margin-left: -125px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 99999;
left: 50%;
top: 30px;
font-size: 14px;
}
#snackbar.show {
visibility: visible;
-webkit-animation: fadein 0.5s;
}
@-webkit-keyframes fadein {
from {
top: 0;
opacity: 0;
}
to {
top: 30px;
font-size: 14px;
opacity: 1;
}
#_editsnackbar {
visibility: hidden;
min-width: 250px;
margin-left: -125px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 99999;
left: 50%;
}
@keyframes fadein {
from {
top: 0;
opacity: 0;
}
to {
top: 30px;
font-size: 14px;
opacity: 1;
}
#_editsnackbar.show {
visibility: visible;
-webkit-animation: fadein 0.5s;
}
#UpdateColumnValue.show {
visibility: visible;
-webkit-animation: fadein 0.5s;
}
#DeleteUser.show {
visibility: visible;
-webkit-animation: fadein 0.5s;
}
#DeleteUser {
visibility: hidden;
min-width: 250px;
margin-left: -125px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 99999;
left: 50%;
}
@-webkit-keyframes fadeout {
from {
top: 30px;
font-size: 14px;
opacity: 1;
}
#deleteObj.show {
visibility: visible;
-webkit-animation: fadein 0.5s;
to {
top: 0;
opacity: 0;
}
#deleteObj {
visibility: hidden;
min-width: 250px;
margin-left: -125px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 99999;
left: 50%;
}
@keyframes fadeout {
from {
top: 30px;
font-size: 14px;
opacity: 1;
}
#UpdateColumnValue {
visibility: hidden;
min-width: 250px;
margin-left: -125px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 99999;
left: 50%;
top: 30px;
font-size: 14px;
to {
top: 0;
opacity: 0;
}
#snackbar.show {
visibility: visible;
-webkit-animation: fadein 0.5s;
}
#snackbar1 {
visibility: hidden;
min-width: 250px;
margin-left: -125px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 99999;
left: 50%;
top: 30px;
font-size: 14px;
}
#snackbar1.show {
visibility: visible;
-webkit-animation: fadein 0.5s;
}
#snackbar10 {
visibility: hidden;
min-width: 250px;
margin-left: -125px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 99999;
left: 50%;
top: 30px;
font-size: 14px;
}
#snackbar10.show {
visibility: visible;
-webkit-animation: fadein 0.5s;
}
#snackbarCol {
visibility: hidden;
min-width: 250px;
margin-left: -125px;
background-color: #333;
color: #fff;
text-align: center;
border-radius: 2px;
padding: 16px;
position: fixed;
z-index: 99999;
left: 50%;
top: 30px;
font-size: 14px;
}
#snackbarCol.show {
visibility: visible;
-webkit-animation: fadein 0.5s;
}
@-webkit-keyframes fadein {
from {
top: 0;
opacity: 0;
}
to {
top: 30px;
opacity: 1;
}
}
@keyframes fadein {
from {
top: 0;
opacity: 0;
}
to {
top: 30px;
opacity: 1;
}
}
@-webkit-keyframes fadeout {
from {
top: 30px;
opacity: 1;
}
to {
top: 0;
opacity: 0;
}
}
@keyframes fadeout {
from {
top: 30px;
opacity: 1;
}
to {
top: 0;
opacity: 0;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
export function customAPIurl() {
const url = new URL(process.env.SERVER_URL);
return url.pathname === '/api/app' ? url.origin + '/api' : url.origin;
}
export const color = [
'#93a3db',
'#e6c3db',
'#c0e3bc',
'#bce3db',
'#b8ccdb',
'#ceb8db',
'#ffccff',
'#99ffcc',
'#cc99ff',
'#ffcc99',
'#66ccff',
'#ffffcc',
];
export function replaceMailVaribles(subject, body, variables) {
let replacedSubject = subject;
let replacedBody = body;
for (const variable in variables) {
const regex = new RegExp(`{{${variable}}}`, 'g');
if (subject) {
replacedSubject = replacedSubject.replace(regex, variables[variable]);
}
if (body) {
replacedBody = replacedBody.replace(regex, variables[variable]);
}
}
const result = {
subject: replacedSubject,
body: replacedBody,
};
return result;
}
@@ -0,0 +1,104 @@
//--npm modules
import express from 'express';
import cors from 'cors';
export const app = express();
import dotenv from 'dotenv';
import getUser from './routes/getUser.js';
import getDocumentList from './routes/getDocumentList.js';
import getDocument from './routes/getDocument.js';
import getContact from './routes/getContact.js';
import deleteContact from './routes/deleteContact.js';
import getContactList from './routes/getContactList.js';
import draftDocument from './routes/draftDocument.js';
import createTemplate from './routes/createTemplate.js';
import getTemplate from './routes/getTemplate.js';
import deletedTemplate from './routes/deleteTemplate.js';
import getTemplatetList from './routes/getTemplateList.js';
import updateTemplate from './routes/updateTemplate.js';
import createContact from './routes/createContact.js';
import multer from 'multer';
// import fs from 'node:fs';
import updateDocument from './routes/updateDocument.js';
import deleteDocument from './routes/deleteDocument.js';
import createDocumentWithTemplate from './routes/CreateDocumentWithTemplate.js';
import saveWebhook from './routes/saveWebhook.js';
import deleteWebhook from './routes/deleteWebhook.js';
import getWebhook from './routes/getWebhook.js';
import createDocumentwithCoordinate from './routes/createDocumentwithCoordinate.js';
import createTemplatewithCoordinate from './routes/createTemplatewithCoordinate.js';
dotenv.config();
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
app.use(cors());
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true }));
// get user details whose api token used
app.get('/getuser', getUser);
// get contact on the basis of id
app.post('/createcontact', createContact);
// get contact on the basis of id
app.get('/contact/:contact_id', getContact);
// soft delete contact
app.delete('/contact/:contact_id', deleteContact);
// get list of contacts
app.get('/contactlist', getContactList);
// create Document
app.post('/createdocumentwithbinary', upload.array('file', 1), createDocumentwithCoordinate);
// create Document with co-ordinate
app.post('/createdocument', createDocumentwithCoordinate);
// create Document with base64 without placeholder
app.post('/draftdocument', draftDocument);
// create Document with templateId
app.post('/createdocument/:template_id', createDocumentWithTemplate);
// get Document on the basis of id
app.get('/document/:document_id', getDocument);
// get document on the basis of id
app.put('/document/:document_id', updateDocument);
// get document on the basis of id
app.delete('/document/:document_id', deleteDocument);
// get all types of documents on the basis of doctype
app.get('/documentlist/:doctype', getDocumentList);
// create Template with co-ordinate
app.post('/createtemplate', createTemplatewithCoordinate);
// create Template
app.post('/drafttemplate', createTemplate);
// create Template with binary
app.post('/createtemplatewithbinary', upload.array('file', 1), createTemplatewithCoordinate);
// get template on the basis of id
app.get('/template/:template_id', getTemplate);
// get template on the basis of id
app.put('/template/:template_id', updateTemplate);
// get template on the basis of id
app.delete('/template/:template_id', deletedTemplate);
// get all types of documents on the basis of doctype
app.get('/templatelist', getTemplatetList);
// set and update webhook
app.get('/webhook', getWebhook);
// set and update webhook
app.post('/webhook', saveWebhook);
// set and update webhook
app.delete('/webhook', deleteWebhook);
@@ -0,0 +1,367 @@
import axios from 'axios';
import { customAPIurl, replaceMailVaribles } from '../../../../Utils.js';
// `sendDoctoWebhook` is used to send res data of document on webhook
async function sendDoctoWebhook(doc, WebhookUrl, userId) {
if (WebhookUrl) {
const params = {
event: 'created',
...doc,
};
await axios
.post(WebhookUrl, params, {
headers: { 'Content-Type': 'application/json' },
})
.then(res => {
try {
// console.log('res ', res);
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', res?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
})
.catch(err => {
console.log('Err send data to webhook', err);
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', err?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
});
// console.log('res ', res.data);
}
}
export default async function createDocumentWithTemplate(request, response) {
const signers = request.body.signers;
const folderId = request.body.folderId;
const templateId = request.params.template_id;
const protocol = customAPIurl();
const baseUrl = new URL(process.env.SERVER_URL);
const send_email = request.body.send_email;
const email_subject = request.body.email_subject;
const email_body = request.body.email_body;
const sendInOrder = request.body.sendInOrder || false;
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const templateQuery = new Parse.Query('contracts_Template');
templateQuery.include('ExtUserPtr');
const templateRes = await templateQuery.get(templateId, { useMasterKey: true });
if (templateRes) {
const template = JSON.parse(JSON.stringify(templateRes));
if (template?.Placeholders?.length > 0) {
const emptyplaceholder = template?.Placeholders.filter(x => !x.signerObjId);
const isValid =
signers.length >= emptyplaceholder.length &&
signers.length <= template?.Placeholders?.length;
const placeholder =
signers.length > emptyplaceholder.length ? template.Placeholders : emptyplaceholder;
const updateSigners = placeholder.every(y => signers?.some(x => x.role === y.Role));
// console.log('isValid ', isValid);
if (isValid && updateSigners) {
const folderPtr = {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
};
const template = JSON.parse(JSON.stringify(templateRes));
const object = new Parse.Object('contracts_Document');
object.set('Name', template.Name);
if (template?.Note) {
object.set('Note', template.Note);
}
if (template?.Description) {
object.set('Description', template.Description);
}
object.set('IsSendMail', send_email);
if (sendInOrder) {
object.set('SendinOrder', sendInOrder);
} else if (template?.SendinOrder && template?.SendinOrder) {
object.set('SendinOrder', template?.SendinOrder);
}
let templateSigner = template?.Signers ? template?.Signers : [];
let contact = [];
if (signers && signers.length > 0) {
let parseSigners = [...signers];
let createContactUrl = protocol + '/v1/createcontact';
for (const obj of parseSigners) {
const body = {
name: obj?.name || '',
email: obj?.email || '',
phone: obj?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
};
const newObj = { ...obj, contactPtr: contactPtr };
contact.push(newObj);
} catch (err) {
// console.log('err ', err);
if (err?.response?.data?.objectId) {
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
};
const newObj = { ...obj, contactPtr: contactPtr };
contact.push(newObj);
} else {
console.log('err ', err);
}
}
}
const contactPtrs = contact.map(x => x.contactPtr);
object.set('Signers', [...templateSigner, ...contactPtrs]);
let updatedPlaceholder = template?.Placeholders?.map(x => {
let matchingSigner = contact.find(y => x.Role && x.Role === y.role);
if (matchingSigner) {
return {
...x,
signerObjId: matchingSigner?.contactPtr?.objectId,
signerPtr: matchingSigner?.contactPtr,
};
} else {
return {
...x,
};
}
});
object.set('Placeholders', updatedPlaceholder);
} else {
object.set('Signers', templateSigner);
}
object.set('URL', template.URL);
object.set('CreatedBy', template.CreatedBy);
object.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: template.ExtUserPtr.objectId,
});
if (folderId) {
object.set('Folder', folderPtr);
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
const newDate = new Date();
newDate.setDate(newDate.getDate() + 15);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
let sender = template.ExtUserPtr.Email;
let sendMail;
const serverUrl = process.env.SERVER_URL;
const newServer = serverUrl.replaceAll('/', '%2F');
const serverParams = `${newServer}%2F&${process.env.APP_ID}&contracts`;
if (send_email === false) {
console.log("don't send mail");
} else {
let contactMail = contact;
if (sendInOrder) {
contactMail = contact.slice();
contactMail.splice(1);
}
for (let i = 0; i < contactMail.length; i++) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${process.env.SERVER_URL}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const objectId = contactMail[i].contactPtr.objectId;
const hostUrl = baseUrl.origin + '/loadmf/signmicroapp';
let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`;
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
const orgName = template.ExtUserPtr.Company ? template.ExtUserPtr.Company : '';
const themeBGcolor = '#47a3ad';
const email_html =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src='" +
imgPng +
"' height='50' style='padding:20px; width:170px; height:40px;' /></div><div style='padding: 2px;font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
template.ExtUserPtr.Name +
' has requested you to review and sign <strong> ' +
template.Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px;'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>';
let replaceVar;
const variables = {
document_title: template.Name,
sender_name: template.ExtUserPtr.Name,
sender_mail: template.ExtUserPtr.Email,
sender_phone: template.ExtUserPtr.Phone,
receiver_name: contactMail[i].name,
receiver_email: contactMail[i].email,
receiver_phone: contactMail[i].phone,
expiry_date: localExpireDate,
company_name: orgName,
signing_url: signPdf,
};
if (email_subject && email_body) {
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
} else if (email_subject) {
replaceVar = replaceMailVaribles(email_subject, '', variables);
replaceVar = { subject: replaceVar.subject, body: email_html };
} else if (email_body) {
replaceVar = replaceMailVaribles(
`${template.ExtUserPtr.Name} has requested you to sign ${template.Name}`,
email_body,
variables
);
} else {
replaceVar = {
subject: `${template.ExtUserPtr.Name} has requested you to sign ${template.Name}`,
body: email_html,
};
}
const subject = replaceVar.subject;
const html = replaceVar.body;
let params = {
recipient: contactMail[i].email,
subject: subject,
from: sender,
html: html,
};
sendMail = await axios.post(url, params, { headers: headers });
} catch (error) {
console.log('error', error);
}
}
}
// if (sendMail.data.result.status === 'success') {
try {
const doc = {
objectId: res.id,
file: template?.URL,
name: template?.Name,
note: template?.Note || '',
description: template?.Description || '',
signers: contact?.map(x => ({ name: x.name, email: x.email, phone: x.phone })),
createdAt: res.createdAt,
};
if (template.ExtUserPtr && template.ExtUserPtr?.Webhook) {
sendDoctoWebhook(doc, template.ExtUserPtr?.Webhook, userPtr?.objectId);
}
} catch (err) {
console.log('Err', err);
}
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 200 },
});
}
return response.json({
objectId: res.id,
signurl: contact.map(x => ({
email: x.email,
url: `${baseUrl.origin}/loadmf/signmicroapp/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`,
})),
message: 'Document sent successfully!',
});
// }
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers properly!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please setup template properly!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Invalid template id!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
if (err.code === 101) {
return response.status(404).json({ error: 'Invalid template id!' });
}
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,216 @@
import axios from 'axios';
export default async function createContact(request, response) {
const serverUrl = process.env.SERVER_URL;
const appId = process.env.APP_ID;
const name = request.body.name;
const phone = request.body.phone;
const email = request.body.email;
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
try {
const contactbook = new Parse.Query('contracts_Contactbook');
contactbook.equalTo('Email', email);
contactbook.equalTo('CreatedBy', userPtr);
contactbook.notEqualTo('IsDeleted', true);
const userExists = await contactbook.first({ useMasterKey: true });
if (userExists) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 401 },
});
}
return response
.status(401)
.json({ error: 'Contact already exists!', objectId: userExists.id });
} else {
try {
const Tenant = new Parse.Query('partners_Tenant');
Tenant.equalTo('UserId', userPtr);
const tenantRes = await Tenant.first({ useMasterKey: true });
const contactQuery = new Parse.Object('contracts_Contactbook');
contactQuery.set('Name', name);
contactQuery.set('Phone', phone);
contactQuery.set('Email', email);
contactQuery.set('UserRole', 'contracts_Guest');
if (tenantRes && tenantRes.id) {
contactQuery.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantRes.id,
});
}
try {
const _users = Parse.Object.extend('User');
const _user = new _users();
_user.set('name', name);
_user.set('username', email);
_user.set('email', email);
_user.set('phone', phone);
_user.set('password', phone);
const user = await _user.save();
if (user) {
const roleurl = `${serverUrl}/functions/AddUserToRole`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
// sessionToken: localStorage.getItem('accesstoken'),
};
const body = {
appName: 'contracts',
roleName: 'contracts_Guest',
userId: user.id,
};
await axios.post(roleurl, body, { headers: headers });
const currentUser = userPtr;
contactQuery.set('CreatedBy', currentUser);
contactQuery.set('UserId', user);
const acl = new Parse.ACL();
acl.setReadAccess(userPtr.objectId, true);
acl.setWriteAccess(userPtr.objectId, true);
acl.setReadAccess(user.id, true);
acl.setWriteAccess(user.id, true);
contactQuery.setACL(acl);
const contactRes = await contactQuery.save();
const parseRes = JSON.parse(JSON.stringify(contactRes));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 200 },
});
}
return response.json({
objectId: parseRes.objectId,
name: parseRes.Name,
email: parseRes.Email,
phone: parseRes.Phone,
createdAt: parseRes.createdAt,
updatedAt: parseRes.updatedAt,
});
}
} catch (err) {
console.log('err in', err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run('getUserId', params);
const roleurl = `${serverUrl}/functions/AddUserToRole`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
// sessionToken: localStorage.getItem('accesstoken'),
};
const body = {
appName: 'contracts',
roleName: 'contracts_Guest',
userId: userRes.objectId,
};
await axios.post(roleurl, body, { headers: headers });
contactQuery.set('CreatedBy', userPtr.objectId);
contactQuery.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userRes.id,
});
const acl = new Parse.ACL();
acl.setReadAccess(userPtr.objectId, true);
acl.setWriteAccess(userPtr.objectId, true);
acl.setReadAccess(userRes.id, true);
acl.setWriteAccess(userRes.id, true);
contactQuery.setACL(acl);
const contactRes = await contactQuery.save();
if (contactRes) {
const parseRes = JSON.parse(JSON.stringify(contactRes));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 200 },
});
}
return response.json({
objectId: parseRes.objectId,
name: parseRes.Name,
email: parseRes.Email,
phone: parseRes.Phone,
createdAt: parseRes.createdAt,
updatedAt: parseRes.updatedAt,
});
}
} else {
if (err.code === 137) {
return response.status(401).json({ error: 'Contact already exists!' });
}
return response
.status(400)
.json({ error: 'Something went wrong, please try again later!' });
}
}
} catch (err) {
console.log('err ', err);
if (err.code === 137) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 401 },
});
}
return response.status(401).json({ error: 'Contact already exists!' });
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 400 },
});
}
return response
.status(400)
.json({ error: 'Something went wrong, please try again later!' });
}
}
}
} catch (err) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 400 },
});
}
return response
.status(400)
.json({ error: 'Something went wrong, please try again later!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('Err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,379 @@
import axios from 'axios';
import { color, customAPIurl, replaceMailVaribles } from '../../../../Utils.js';
// `sendDoctoWebhook` is used to send res data of document on webhook
async function sendDoctoWebhook(doc, WebhookUrl, userId) {
if (WebhookUrl) {
const params = {
event: 'created',
...doc,
};
await axios
.post(WebhookUrl, params, {
headers: { 'Content-Type': 'application/json' },
})
.then(res => {
try {
// console.log('res ', res);
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', res?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
})
.catch(err => {
console.log('Err send data to webhook', err);
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', err?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
});
// console.log('res ', res.data);
}
}
const randomId = () => Math.floor(1000 + Math.random() * 9000);
export default async function createDocumentwithCoordinate(request, response) {
const name = request.body.title;
const note = request.body.note;
const description = request.body.description;
const send_email = request.body.send_email;
const signers = request.body.signers;
const folderId = request.body.folderId;
const base64File = request.body.file;
const fileData = request.files?.[0] ? request.files[0].buffer : null;
const email_subject = request.body.email_subject;
const email_body = request.body.email_body;
const sendInOrder = request.body.sendInOrder || false;
// console.log('fileData ', fileData);
const protocol = customAPIurl();
const baseUrl = new URL(process.env.SERVER_URL);
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
if (signers && signers.length > 0) {
let fileUrl;
if (request.files?.[0]) {
const file = new Parse.File(request.files?.[0]?.originalname, {
base64: fileData?.toString('base64'),
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
} else {
const file = new Parse.File(`${name}.pdf`, {
base64: base64File,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
}
const contractsUser = new Parse.Query('contracts_Users');
contractsUser.equalTo('UserId', userPtr);
const extUser = await contractsUser.first({ useMasterKey: true });
const parseExtUser = JSON.parse(JSON.stringify(extUser));
const extUserPtr = {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
};
const object = new Parse.Object('contracts_Document');
object.set('Name', name);
if (note) {
object.set('Note', note);
}
if (description) {
object.set('Description', description);
}
if (sendInOrder) {
object.set('SendinOrder', sendInOrder);
}
object.set('URL', fileUrl);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
object.set('IsSendMail', send_email);
let contact = [];
if (signers && signers.length > 0) {
let parseSigners;
if (base64File) {
parseSigners = signers;
} else {
parseSigners = JSON.parse(signers);
}
let createContactUrl = protocol + '/v1/createcontact';
for (const [index, element] of parseSigners.entries()) {
const body = {
name: element?.name || '',
email: element?.email || '',
phone: element?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
// console.log('res ', res.data);
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
} catch (err) {
// console.log('err ', err.response);
if (err?.response?.data?.objectId) {
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
}
}
}
object.set(
'Signers',
contact?.map(x => x.contactPtr)
);
let updatePlaceholders = contact.map((signer, index) => {
const placeHolder = [];
for (const widget of signer.widgets) {
const pageNumber = widget.page;
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const widgetData = {
xPosition: widget.x,
yPosition: widget.y,
isStamp: widget.type === 'stamp',
key: randomId(),
isDrag: false,
firstXPos: widget.x,
firstYPos: widget.y,
yBottom: 0,
scale: 1,
isMobile: false,
zIndex: 1,
type: widget.type,
widgetValue: '',
Width: widget.w,
Height: widget.h,
};
if (page) {
page.pos.push(widgetData);
} else {
placeHolder.push({
pageNumber,
pos: [widgetData],
});
}
}
return {
signerObjId: signer?.contactPtr?.objectId,
signerPtr: signer?.contactPtr,
Role: signer.role,
Id: randomId(),
blockColor: color[signer?.index],
placeHolder,
};
});
object.set('Placeholders', updatePlaceholders);
}
if (folderId) {
object.set('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
});
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
const doc = {
objectId: res.id,
file: fileUrl,
name: name,
note: note || '',
description: description || '',
signers: contact?.map(x => ({ name: x.name, email: x.email, phone: x.phone })),
createdAt: res.createdAt,
};
if (parseExtUser && parseExtUser.Webhook) {
sendDoctoWebhook(doc, parseExtUser?.Webhook, userPtr?.objectId);
}
const newDate = new Date();
newDate.setDate(newDate.getDate() + 15);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
let sender = parseExtUser.Email;
let sendMail;
const serverUrl = process.env.SERVER_URL;
const newServer = serverUrl.replaceAll('/', '%2F');
const serverParams = `${newServer}%2F&${process.env.APP_ID}&contracts`;
if (send_email === false) {
console.log("don't send mail");
} else {
let contactMail = contact;
if (sendInOrder) {
contactMail = contact.slice();
contactMail.splice(1);
}
for (let i = 0; i < contactMail.length; i++) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${process.env.SERVER_URL}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const objectId = contactMail[i].contactPtr.objectId;
const hostUrl = baseUrl.origin + '/loadmf/signmicroapp';
let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`;
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
const themeBGcolor = '#47a3ad';
const email_html =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding:20px; width:170px; height:40px;' /></div> <div style='padding:2px; font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
parseExtUser.Name +
' has requested you to review and sign <strong> ' +
name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>';
let replaceVar;
const variables = {
document_title: name,
sender_name: parseExtUser.Name,
sender_mail: parseExtUser.Email,
sender_phone: parseExtUser.Phone,
receiver_name: contactMail[i].name,
receiver_email: contactMail[i].email,
receiver_phone: contactMail[i].phone,
expiry_date: localExpireDate,
company_name: orgName,
signing_url: signPdf,
};
if (email_subject && email_body) {
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
} else if (email_subject) {
replaceVar = replaceMailVaribles(email_subject, '', variables);
replaceVar = { subject: replaceVar.subject, body: email_html };
} else if (email_body) {
replaceVar = replaceMailVaribles(
`${parseExtUser.Name} has requested you to sign ${name}`,
email_body,
variables
);
} else {
replaceVar = {
subject: `${parseExtUser.Name} has requested you to sign ${parseExtUser.Name}`,
body: email_html,
};
}
const subject = replaceVar.subject;
const html = replaceVar.body;
let params = {
recipient: contactMail[i].email,
subject: subject,
from: sender,
html: html,
};
sendMail = await axios.post(url, params, { headers: headers });
} catch (error) {
console.log('error', error);
}
}
}
// if (sendMail.data.result.status === 'success') {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document',
properties: { response_code: 200 },
});
}
return response.json({
objectId: res.id,
signurl: contact.map(x => ({
email: x.email,
url: `${baseUrl.origin}/loadmf/signmicroapp/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`,
})),
message: 'Document sent successfully!',
});
// }
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,135 @@
import { customAPIurl } from '../../../../Utils.js';
const randomId = () => Math.floor(1000 + Math.random() * 9000);
export default async function createTemplate(request, response) {
const name = request.body?.title;
const note = request.body?.note;
const description = request.body?.description;
const signers = request.body?.signers;
const folderId = request.body?.folderId;
const base64File = request.body.file;
const SendinOrder = request.body.sendInOrder || false;
const fileData = request.files?.[0] ? request.files[0].buffer : null;
const baseUrl = new URL(process.env.SERVER_URL);
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
let fileUrl;
if (base64File) {
const file = new Parse.File(`${name}.pdf`, {
base64: base64File,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
} else {
const file = new Parse.File(request.files?.[0]?.originalname, {
base64: fileData?.toString('base64'),
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
}
const contractsUser = new Parse.Query('contracts_Users');
contractsUser.equalTo('UserId', userPtr);
const extUser = await contractsUser.first({ useMasterKey: true });
const extUserPtr = { __type: 'Pointer', className: 'contracts_Users', objectId: extUser.id };
const folderPtr = { __type: 'Pointer', className: 'contracts_Template', objectId: folderId };
const object = new Parse.Object('contracts_Template');
object.set('Name', name);
if (note) {
object.set('Note', note);
}
if (description) {
object.set('Description', description);
}
object.set('URL', fileUrl);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
if (SendinOrder) {
object.set('SendinOrder', SendinOrder);
}
if (signers && signers.length > 0) {
let parseSigners;
if (base64File) {
parseSigners = signers;
} else {
parseSigners = JSON.parse(signers);
}
let createContactUrl = protocol + '/v1/createcontact';
let contact = [];
for (const obj of parseSigners) {
const body = {
name: obj?.name || '',
email: obj?.email || '',
phone: obj?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
// console.log('res ', res.data);
contact.push({
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
});
} catch (err) {
// console.log('err ', err.response);
if (err?.response?.data?.objectId) {
contact.push({
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
});
}
}
}
object.set('Signers', contact);
}
if (folderId) {
object.set('Folder', folderPtr);
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_draft_template',
properties: { response_code: 200 },
});
}
return response.json({
objectId: res.id,
url: baseUrl.origin + '/load/signmicroapp/template/' + res.id,
});
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,214 @@
import axios from 'axios';
import { color, customAPIurl } from '../../../../Utils.js';
const randomId = () => Math.floor(1000 + Math.random() * 9000);
export default async function createTemplatewithCoordinate(request, response) {
const name = request.body.title;
const note = request.body.note;
const description = request.body.description;
const signers = request.body.signers;
const folderId = request.body.folderId;
const base64File = request.body.file;
const fileData = request.files?.[0] ? request.files[0].buffer : null;
const SendinOrder = request.body.sendInOrder || false;
// console.log('fileData ', fileData);
const protocol = customAPIurl();
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
if (signers && signers.length > 0) {
let fileUrl;
if (request.files?.[0]) {
const file = new Parse.File(request.files?.[0]?.originalname, {
base64: fileData?.toString('base64'),
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
} else {
const file = new Parse.File(`${name}.pdf`, {
base64: base64File,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
}
const contractsUser = new Parse.Query('contracts_Users');
contractsUser.equalTo('UserId', userPtr);
const extUser = await contractsUser.first({ useMasterKey: true });
const extUserPtr = {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
};
const object = new Parse.Object('contracts_Template');
object.set('Name', name);
if (note) {
object.set('Note', note);
}
if (description) {
object.set('Description', description);
}
if (SendinOrder) {
object.set('SendinOrder', SendinOrder);
}
object.set('URL', fileUrl);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
let contact = [];
if (signers && signers.length > 0) {
let parseSigners;
if (base64File) {
parseSigners = signers;
} else {
parseSigners = JSON.parse(signers);
}
let createContactUrl = protocol + '/v1/createcontact';
for (const [index, element] of parseSigners.entries()) {
if (element?.name && element.phone && element?.email) {
const body = {
name: element?.name || '',
email: element?.email || '',
phone: element?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
// console.log('res ', res.data);
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
} catch (err) {
// console.log('err ', err.response);
if (err?.response?.data?.objectId) {
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
}
}
} else {
const newObj = { ...element, contactPtr: {}, index: index };
contact.push(newObj);
}
}
const updatedSigners = contact?.filter(x => x.contactPtr && x.contactPtr.objectId);
if (updatedSigners && updatedSigners.length > 0) {
object.set(
'Signers',
updatedSigners?.map(x => x.contactPtr)
);
}
let updatePlaceholders = contact.map((signer, index) => {
const placeHolder = [];
for (const widget of signer.widgets) {
const pageNumber = widget.page;
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const widgetData = {
xPosition: widget.x,
yPosition: widget.y,
isStamp: widget.type === 'stamp',
key: randomId(),
isDrag: false,
firstXPos: widget.x,
firstYPos: widget.y,
yBottom: 0,
scale: 1,
isMobile: false,
zIndex: 1,
type: widget.type,
widgetValue: '',
Width: widget.w,
Height: widget.h,
};
if (page) {
page.pos.push(widgetData);
} else {
placeHolder.push({
pageNumber,
pos: [widgetData],
});
}
}
return {
signerObjId: signer?.contactPtr?.objectId,
signerPtr: signer?.contactPtr,
Role: signer.role,
Id: randomId(),
blockColor: color[signer?.index],
placeHolder,
};
});
object.set('Placeholders', updatePlaceholders);
}
if (folderId) {
object.set('Folder', {
__type: 'Pointer',
className: 'contracts_Template',
objectId: folderId,
});
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_template',
properties: { response_code: 200 },
});
}
return response.json({
objectId: res.id,
message: 'Template created successfully!',
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_template',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,65 @@
export default async function deleteContact(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const Contactbook = new Parse.Query('contracts_Contactbook');
Contactbook.equalTo('objectId', request.params.contact_id);
Contactbook.equalTo('CreatedBy', userPtr);
const res = await Contactbook.first({ useMasterKey: true });
if (res) {
const isDeleted = res.get('IsDeleted');
if (isDeleted && isDeleted) {
return response.status(404).json({ error: 'Contact not found!' });
} else {
const Contactbook = Parse.Object.extend('contracts_Contactbook');
const deleteQuery = new Contactbook();
deleteQuery.id = request.params.contact_id;
deleteQuery.set('IsDeleted', true);
const deleteRes = await deleteQuery.save(null, { useMasterKey: true });
if (deleteRes) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_contact',
properties: { response_code: 200 },
});
}
return response.json({
objectId: request.params.contact_id,
deletedAt: deleteRes.get('updatedAt'),
});
}
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_contact',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Contact not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,65 @@
export default async function deleteDocument(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const Document = new Parse.Query('contracts_Document');
Document.equalTo('objectId', request.params.document_id);
Document.equalTo('CreatedBy', userPtr);
const res = await Document.first({ useMasterKey: true });
if (res) {
const isArchive = res.get('IsArchive');
if (isArchive && isArchive) {
return response.status(404).json({ error: 'Document not found!' });
} else {
const Document = Parse.Object.extend('contracts_Document');
const deleteQuery = new Document();
deleteQuery.id = request.params.document_id;
deleteQuery.set('IsArchive', true);
const deleteRes = await deleteQuery.save(null, { useMasterKey: true });
if (deleteRes) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_document',
properties: { response_code: 200 },
});
}
return response.json({
objectId: request.params.document_id,
deletedAt: deleteRes.get('updatedAt'),
});
}
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_document',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Document not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,71 @@
export default async function deletedTemplate(request, response) {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const template = new Parse.Query('contracts_Template');
template.equalTo('objectId', request.params.template_id);
template.equalTo('CreatedBy', userPtr);
const res = await template.first({ useMasterKey: true });
if (res) {
const isArchive = res.get('IsArchive');
if (isArchive && isArchive) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_template',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Template not found!' });
} else {
const template = Parse.Object.extend('contracts_Template');
const deleteQuery = new template();
deleteQuery.id = request.params.template_id;
deleteQuery.set('IsArchive', true);
const deleteRes = await deleteQuery.save(null, { useMasterKey: true });
if (deleteRes) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_template',
properties: { response_code: 200 },
});
}
return response.json({
objectId: request.params.template_id,
deletedAt: deleteRes.get('updatedAt'),
});
}
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_template',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Template not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,57 @@
export default async function deleteWebhook(request, response) {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const query = new Parse.Query('contracts_Users');
query.equalTo('UserId', userPtr);
const user = await query.first({ useMasterKey: true });
if (user) {
const updateQuery = new Parse.Object('contracts_Users');
updateQuery.id = user.id;
updateQuery.unset('Webhook');
const res = await updateQuery.save(null, { useMasterKey: true });
if (res) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_webhook',
properties: { response_code: 200 },
});
}
return response.json({
result: 'Webhook deleted successfully!',
});
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_webhook',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'User not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,157 @@
import axios from 'axios';
import { customAPIurl } from '../../../../Utils.js';
// const randomId = () => Math.floor(1000 + Math.random() * 9000);
export default async function draftDocument(request, response) {
const name = request.body.title;
const note = request.body.note;
const description = request.body.description;
const signers = request.body.signers;
const folderId = request.body.folderId;
const base64File = request.body.file;
const send_email = request.body.send_email || true;
const fileData = request.files?.[0] ? request.files[0].buffer : null;
const SendinOrder = request.body.sendInOrder || false;
// console.log('fileData ', fileData);
const protocol = customAPIurl();
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
// Valid Token then proceed request
if (signers && signers.length > 0) {
let fileUrl;
if (request.files?.[0]) {
const file = new Parse.File(request.files?.[0]?.originalname, {
base64: fileData?.toString('base64'),
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
} else {
const file = new Parse.File(`${name}.pdf`, {
base64: base64File,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
}
const contractsUser = new Parse.Query('contracts_Users');
contractsUser.equalTo('UserId', userPtr);
const extUser = await contractsUser.first({ useMasterKey: true });
const extUserPtr = {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
};
const folderPtr = {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
};
const object = new Parse.Object('contracts_Document');
object.set('Name', name);
if (note) {
object.set('Note', note);
}
if (description) {
object.set('Description', description);
}
if (SendinOrder) {
object.set('SendinOrder', SendinOrder);
}
object.set('URL', fileUrl);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
object.set('IsSendMail', send_email);
if (signers && signers.length > 0) {
let parseSigners;
if (base64File) {
parseSigners = signers;
} else {
parseSigners = JSON.parse(signers);
}
let createContactUrl = protocol + '/v1/createcontact';
let contact = [];
for (const obj of parseSigners) {
const body = {
name: obj?.name || '',
email: obj?.email || '',
phone: obj?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
// console.log('res ', res.data);
contact.push({
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
});
} catch (err) {
// console.log('err ', err.response);
if (err?.response?.data?.objectId) {
contact.push({
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
});
}
}
}
object.set('Signers', contact);
}
if (folderId) {
object.set('Folder', folderPtr);
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_draft_document',
properties: { response_code: 200 },
});
}
return response.json({
objectId: res.id,
url: protocol + '/load/signmicroapp/placeholdersign/' + res.id,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_draft_document',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,61 @@
export default async function getContact(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const Contactbook = new Parse.Query('contracts_Contactbook');
Contactbook.equalTo('objectId', request.params.contact_id);
Contactbook.equalTo('CreatedBy', userPtr);
Contactbook.notEqualTo('IsDeleted', true);
Contactbook.select('Name,Email,Phone');
const res = await Contactbook.first({ useMasterKey: true });
if (res) {
const parseRes = JSON.parse(JSON.stringify(res));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_contact',
properties: { response_code: 200 },
});
}
return response.json({
objectId: parseRes.objectId,
name: parseRes.Name,
email: parseRes.Email,
phone: parseRes.Phone,
createdAt: parseRes.createdAt,
updatedAt: parseRes.updatedAt,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_contact',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Contact not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,63 @@
export default async function getContactList(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const limit = request?.query?.limit ? parseInt(request.query.limit) : 100;
const skip = request?.query?.skip ? parseInt(request.query.skip) : 0;
const Contactbook = new Parse.Query('contracts_Contactbook');
Contactbook.equalTo('CreatedBy', userPtr);
Contactbook.notEqualTo('IsDeleted', true);
Contactbook.limit(limit);
Contactbook.skip(skip);
Contactbook.descending('createdAt');
const res = await Contactbook.find({ useMasterKey: true });
if (res && res.length > 0) {
const parseRes = JSON.parse(JSON.stringify(res));
const contactlist = parseRes.map(x => ({
objectId: x.objectId,
name: x.Name,
email: x.Email,
phone: x.Phone,
createdAt: x.createdAt,
updatedAt: x.updatedAt,
}));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_contact_list',
properties: { response_code: 200 },
});
}
return response.json({ result: contactlist });
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_contact_list',
properties: { response_code: 200 },
});
}
return response.json({ result: [] });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,84 @@
export default async function getDocument(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const Document = new Parse.Query('contracts_Document');
Document.equalTo('objectId', request.params.document_id);
Document.equalTo('CreatedBy', userPtr);
Document.notEqualTo('IsArchive', true);
Document.include('Signers');
Document.include('Folder');
Document.include('ExtUserPtr');
Document.include('Placeholders.signerPtr');
const res = await Document.first({ useMasterKey: true });
if (res) {
const document = JSON.parse(JSON.stringify(res));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_document',
properties: { response_code: 200 },
});
}
return response.json({
objectId: document.objectId,
title: document.Name,
note: document.Note || '',
folder: { objectId: document?.Folder?.objectId, name: document?.Folder?.Name } || '',
file: document?.SignedUrl || document.URL,
owner: document?.ExtUserPtr?.Name,
signers:
document?.Placeholders?.map(y => ({
role: y.Role,
name: y?.signerPtr?.Name || '',
email: y?.signerPtr?.Email || '',
phone: y?.signerPtr?.Phone || '',
widgets: y.placeHolder?.flatMap(x =>
x?.pos.map(w => ({
type: w?.type ? w.type : w.isStamp ? 'stamp' : 'signature',
x: w.xPosition,
y: w.yPosition,
w: w?.Width || 150,
h: w?.Height || 60,
page: x?.pageNumber,
}))
),
})) ||
document?.Signers?.map(y => ({ name: y?.Name, email: y?.Email, phone: y?.Phone })) ||
[],
sendInOrder: document?.SendinOrder || false,
createdAt: document.createdAt,
updatedAt: document.updatedAt,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_document',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Document not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,138 @@
import axios from 'axios';
import reportJson from '../../../parsefunction/reportsJson.js';
import dotenv from 'dotenv';
dotenv.config();
export default async function getDocumentList(request, response) {
const reqToken = request.headers['x-api-token'];
const appId = process.env.APP_ID;
const serverUrl = process.env.SERVER_URL;
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const docType = request.params.doctype;
const limit = request?.query?.limit ? request.query.limit : 100;
const skip = request?.query?.skip ? request.query.skip : 0;
let reportId;
switch (docType) {
case 'draft':
reportId = 'ByHuevtCFY';
break;
case 'signaturerequest':
reportId = '4Hhwbp482K';
break;
case 'inprogress':
reportId = '1MwEuxLEkF';
break;
case 'completed':
reportId = 'kQUoW4hUXz';
break;
case 'expired':
reportId = 'zNqBHXHsYH';
break;
case 'declined':
reportId = 'UPr2Fm5WY3';
break;
default:
reportId = '';
}
const json = reportId && reportJson(reportId, userPtr.objectId);
const clsName = 'contracts_Document';
if (reportId && json) {
const { params, keys } = json;
const orderBy = '-updatedAt';
const strParams = JSON.stringify(params);
const strKeys = keys.join();
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys},Placeholders&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr`;
try {
const res = await axios.get(url, { headers: headers });
if (res.data && res.data.results.length > 0) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_document_list_by_status',
properties: { response_code: 200, doc_type: docType },
});
}
const updateRes = res.data.results.map(x => ({
objectId: x.objectId,
title: x.Name,
note: x.Note || '',
folder: { objectId: x?.Folder?.objectId, name: x?.Folder?.Name } || '',
file: x?.SignedUrl || x.URL,
owner: x?.ExtUserPtr?.Name,
signers:
x?.Placeholders?.map(y => ({
role: y.Role,
name: y?.signerPtr?.Name || '',
email: y?.signerPtr?.Email || '',
phone: y?.signerPtr?.Phone || '',
widgets: y.placeHolder?.flatMap(x =>
x?.pos.map(w => ({
type: w?.type ? w.type : w.isStamp ? 'stamp' : 'signature',
x: w.xPosition,
y: w.yPosition,
w: w?.Width || 150,
h: w?.Height || 60,
page: x?.pageNumber,
}))
),
})) ||
x?.Signers?.map(y => ({ name: y?.Name, email: y?.Email, phone: y?.Phone })) ||
[],
sendInOrder: x?.SendinOrder || false,
createdAt: x.createdAt,
updatedAt: x.updatedAt,
}));
return response.json({ result: updateRes });
} else {
return response.json({ result: [] });
}
} catch (err) {
console.log('err in getdocument list', err);
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_document_list_by_status',
properties: { response_code: 400, doc_type: docType },
});
}
return response
.status(400)
.json({ error: 'Something went wrong, please try again later!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_document_list_by_status',
properties: { response_code: 404, doc_type: docType },
});
}
return response.status(404).json({ error: 'Report not available!' });
}
}
return response.status(405).json({ error: 'Invalid API Token!' });
} catch (err) {
console.log('Err', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,84 @@
export default async function getTemplate(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const Template = new Parse.Query('contracts_Template');
Template.equalTo('objectId', request.params.template_id);
Template.equalTo('CreatedBy', userPtr);
Template.notEqualTo('IsArchive', true);
Template.include('Signers');
Template.include('Folder');
Template.include('ExtUserPtr');
Template.include('Placeholders.signerPtr');
const res = await Template.first({ useMasterKey: true });
if (res) {
const template = JSON.parse(JSON.stringify(res));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_template',
properties: { response_code: 200 },
});
}
return response.json({
objectId: template.objectId,
title: template.Name,
note: template.Note || '',
folder: { objectId: template?.Folder?.objectId, name: template?.Folder?.Name } || '',
file: template?.SignedUrl || template?.URL,
owner: template?.ExtUserPtr?.Name,
signers:
template?.Placeholders?.map(y => ({
role: y.Role,
name: y?.signerPtr?.Name || '',
email: y?.signerPtr?.Email || '',
phone: y?.signerPtr?.Phone || '',
widgets: y.placeHolder?.flatMap(x =>
x?.pos.map(w => ({
type: w?.type ? w.type : w.isStamp ? 'stamp' : 'signature',
x: w.xPosition,
y: w.yPosition,
w: w?.Width || 150,
h: w?.Height || 60,
page: x?.pageNumber,
}))
),
})) || [],
sendInOrder: template?.SendinOrder || false,
createdAt: template.createdAt,
updatedAt: template.updatedAt,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_template',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Template not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,111 @@
import axios from 'axios';
import dotenv from 'dotenv';
dotenv.config();
export default async function getTemplatetList(request, response) {
const reqToken = request.headers['x-api-token'];
const appId = process.env.APP_ID;
const serverUrl = process.env.SERVER_URL;
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const limit = request?.query?.limit ? request.query.limit : 100;
const skip = request?.query?.skip ? request.query.skip : 0;
const clsName = 'contracts_Template';
const params = {
Type: { $ne: 'Folder' },
CreatedBy: userPtr,
IsArchive: { $ne: true },
};
const keys = [
'Name',
'Note',
'Description',
'Folder.Name',
'URL',
'SignedUrl',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
];
const orderBy = '-updatedAt';
const strParams = JSON.stringify(params);
const strKeys = keys.join();
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr`;
const res = await axios.get(url, { headers: headers });
if (res.data && res.data.results.length > 0) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_template_list',
properties: { response_code: 200 },
});
}
const updateRes = res.data.results.map(template => ({
objectId: template.objectId,
title: template.Name,
note: template.Note || '',
folder: { objectId: template?.Folder?.objectId, name: template?.Folder?.Name } || '',
file: template?.SignedUrl || template.URL,
owner: template?.ExtUserPtr?.Name,
signers:
template?.Placeholders?.map(y => ({
role: y.Role,
name: y?.signerPtr?.Name || '',
email: y?.signerPtr?.Email || '',
phone: y?.signerPtr?.Phone || '',
widgets: y.placeHolder?.flatMap(x =>
x?.pos.map(w => ({
type: w?.type ? w.type : w.isStamp ? 'stamp' : 'signature',
x: w.xPosition,
y: w.yPosition,
w: w?.Width || 150,
h: w?.Height || 60,
page: x?.pageNumber,
}))
),
})) || [],
sendInOrder: template?.SendinOrder || false,
createdAt: template.createdAt,
updatedAt: template.updatedAt,
}));
return response.json({ result: updateRes });
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_template_list',
properties: { response_code: 200 },
});
}
return response.json({ result: [] });
}
}
return response.status(405).json({ error: 'Invalid API Token!' });
} catch (err) {
console.log('err', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,62 @@
import dotenv from 'dotenv';
dotenv.config();
export default async function getUser(request, response) {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const query = new Parse.Query('contracts_Users');
query.equalTo('UserId', userPtr);
query.exclude('IsContactEntry,TourStatus,UserRole,TenantId,UserId,CreatedBy,Plan');
const user = await query.first({ useMasterKey: true });
if (user) {
const parseRes = JSON.parse(JSON.stringify(user));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_your_account_details',
properties: { response_code: 200 },
});
}
return response.json({
objectId: parseRes.objectId,
name: parseRes.Name,
email: parseRes.Email,
phone: parseRes.Phone,
jobTitle: parseRes.JobTitle,
company: parseRes.Company,
createdAt: parseRes.createdAt,
updatedAt: parseRes.updatedAt,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_your_account_details',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'User not found!' });
}
}
return response.status(405).json({ error: 'Invalid API Token!' });
} catch (err) {
console.log('Err', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,55 @@
export default async function getWebhook(request, response) {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const query = new Parse.Query('contracts_Users');
query.equalTo('UserId', userPtr);
const user = await query.first({ useMasterKey: true });
if (user) {
const extUser = JSON.parse(JSON.stringify(user));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_webhook',
properties: { response_code: 200 },
});
}
if (extUser && extUser.Webhook) {
return response.json({
webhook: extUser.Webhook,
});
} else {
return response.json({});
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_webhook',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'User not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('Err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,85 @@
export default async function saveWebhook(request, response) {
const Url = request.body.url;
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const query = new Parse.Query('contracts_Users');
query.equalTo('UserId', userPtr);
const user = await query.first({ useMasterKey: true });
if (user) {
const extUser = JSON.parse(JSON.stringify(user));
const isUrlExist = extUser?.Webhook && extUser?.Webhook === Url;
if (!isUrlExist) {
try {
const updateQuery = new Parse.Object('contracts_Users');
updateQuery.id = user.id;
updateQuery.set('Webhook', Url);
const res = await updateQuery.save(null, { useMasterKey: true });
if (res) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_save_webhook',
properties: { response_code: 200 },
});
}
return response.json({
result: 'Webhook updated successfully!',
});
}
} catch (err) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_save_webhook',
properties: { response_code: 400 },
});
}
console.log('Err ', err);
return response
.status(400)
.json({ error: 'Something went wrong, please try again later!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_save_webhook',
properties: { response_code: 401 },
});
}
return response.status(401).json({ error: 'Webhook url already exists!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_save_webhook',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'User not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('Err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,93 @@
export default async function updateDocument(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const allowedKeys = ['name', 'note', 'description', 'folderId'];
const objectKeys = Object.keys(request.body);
const isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0;
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
if (isValid) {
const document = new Parse.Query('contracts_Document');
document.equalTo('objectId', request.params.document_id);
document.equalTo('CreatedBy', userPtr);
const res = await document.first({ useMasterKey: true });
if (res) {
const isArchive = res.get('IsArchive');
if (isArchive && isArchive) {
return response.status(404).json({ message: 'Document not found!' });
} else {
const document = Parse.Object.extend('contracts_Document');
const updateQuery = new document();
updateQuery.id = request.params.document_id;
if (request?.body?.name) {
updateQuery.set('Name', request?.body?.name);
}
if (request?.body?.note) {
updateQuery.set('Note', request?.body?.note);
}
if (request?.body?.description) {
updateQuery.set('Description', request?.body?.description);
}
if (request?.body?.folderId) {
updateQuery.set('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: request?.body?.folderId,
});
}
const updatedRes = await updateQuery.save(null, { useMasterKey: true });
if (updatedRes) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_document',
properties: { response_code: 200 },
});
}
return response.json({
objectId: updatedRes.id,
updatedAt: updatedRes.get('updatedAt'),
});
}
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_document',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Document not found!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_document',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide valid field names!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -0,0 +1,93 @@
export default async function updateTemplate(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const allowedKeys = ['name', 'note', 'description', 'folderId'];
const objectKeys = Object.keys(request.body);
const isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0;
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
if (isValid) {
const template = new Parse.Query('contracts_Template');
template.equalTo('objectId', request.params.template_id);
template.equalTo('CreatedBy', userPtr);
const res = await template.first({ useMasterKey: true });
if (res) {
const isArchive = res.get('IsArchive');
if (isArchive && isArchive) {
return response.status(404).json({ message: 'Template not found!' });
} else {
const template = Parse.Object.extend('contracts_Template');
const updateQuery = new template();
updateQuery.id = request.params.template_id;
if (request?.body?.name) {
updateQuery.set('Name', request?.body?.name);
}
if (request?.body?.note) {
updateQuery.set('Note', request?.body?.note);
}
if (request?.body?.description) {
updateQuery.set('Description', request?.body?.description);
}
if (request?.body?.folderId) {
updateQuery.set('Folder', {
__type: 'Pointer',
className: 'contracts_Template',
objectId: request?.body?.folderId,
});
}
const updatedRes = await updateQuery.save(null, { useMasterKey: true });
if (updatedRes) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_template',
properties: { response_code: 200 },
});
}
return response.json({
objectId: updatedRes.id,
updatedAt: updatedRes.get('updatedAt'),
});
}
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_template',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Template not found!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_template',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide valid field names!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
+12 -4
View File
@@ -17,8 +17,12 @@ import getUserDetails from './parsefunction/getUserDetails.js';
import getDocument from './parsefunction/getDocument.js';
import getDrive from './parsefunction/getDrive.js';
import getReport from './parsefunction/getReport.js';
import generateApiToken from './parsefunction/generateApiToken.js';
import getapitoken from './parsefunction/getapitoken.js';
import TemplateAfterSave from './parsefunction/TemplateAfterSave.js';
import GetTemplate from './parsefunction/GetTemplate.js';
import savewebhook from './parsefunction/saveWebhook.js';
import callWebhook from './parsefunction/callWebhook.js';
Parse.Cloud.define('AddUserToRole', addUserToGroups);
Parse.Cloud.define('UserGroups', getUserGroups);
@@ -34,10 +38,14 @@ Parse.Cloud.define('AuthLoginAsMail', AuthLoginAsMail);
Parse.Cloud.define('getUserId', getUserId);
Parse.Cloud.define('getUserDetails', getUserDetails);
Parse.Cloud.define('getDocument', getDocument);
Parse.Cloud.define('getDrive', getDrive)
Parse.Cloud.define('getReport', getReport)
Parse.Cloud.define("getTemplate", GetTemplate)
Parse.Cloud.define('getDrive', getDrive);
Parse.Cloud.define('getReport', getReport);
Parse.Cloud.define('generateapitoken', generateApiToken);
Parse.Cloud.define('getapitoken', getapitoken);
Parse.Cloud.define('getTemplate', GetTemplate);
Parse.Cloud.define('savewebhook', savewebhook);
Parse.Cloud.define('callwebhook', callWebhook);
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
Parse.Cloud.afterSave('contracts_Users', ContractUsersAftersave);
Parse.Cloud.afterSave("contracts_Template", TemplateAfterSave)
Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave);
@@ -3,13 +3,13 @@ async function ContactbookAftersave(request) {
you can check as follows */
if (!request.original) {
const user = request.user;
const object = request.object;
// Retrieve the current ACL
const acl = new Parse.ACL();
// Ensure the current user has read access
if (acl) {
if (acl && request?.user) {
const object = request.object;
acl.setReadAccess(user, true);
acl.setWriteAccess(user, true);
acl.setReadAccess(object.get('UserId'), true);
@@ -4,20 +4,11 @@ async function DocumentAftersave(request) {
console.log('new entry is insert in contracts_Document');
const createdAt = request.object.get('createdAt');
const Folder = request.object.get('Type');
// console.log("createdAt")
// console.log(createdAt)
// console.log("Folder")
// console.log(Folder)
// console.log("before If condition")
if (createdAt && Folder === undefined) {
// console.log("IN If condition")
const TimeToCompleteDays = request.object.get('TimeToCompleteDays');
const ExpiryDate = new Date(createdAt);
// console.log("ExpiryDate")
// console.log(ExpiryDate)
ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays);
// console.log("ExpiryDate date after update")
// console.log(ExpiryDate)
const documentQuery = new Parse.Query('contracts_Document');
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
updateQuery.set('ExpiryDate', ExpiryDate);
@@ -37,21 +28,23 @@ async function DocumentAftersave(request) {
}
const signers = request.object.get('Signers');
// console.log("Signers")
// console.log(signers.length)
// update acl of New Document If There are signers present in array
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
} else {
await updateSelfDoc(request.object.id);
if (request?.object?.id && request.user) {
await updateSelfDoc(request.object.id);
}
}
} else {
if (request.user) {
if (request?.user) {
const signers = request.object.get('Signers');
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
} else {
await updateSelfDoc(request.object.id);
if (request?.object?.id) {
await updateSelfDoc(request.object.id);
}
}
}
}
@@ -65,6 +58,7 @@ async function DocumentAftersave(request) {
// console.log(objId)
const Query = new Parse.Query('contracts_Document');
Query.include('Signers');
Query.include('CreatedBy');
const updateACL = await Query.get(objId, { useMasterKey: true });
const res = JSON.parse(JSON.stringify(updateACL));
// console.log("res");
@@ -87,9 +81,10 @@ async function DocumentAftersave(request) {
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(request.user, true);
newACL.setWriteAccess(request.user, true);
if (res?.CreatedBy) {
newACL.setReadAccess(res?.CreatedBy?.objectId, true);
newACL.setWriteAccess(res?.CreatedBy?.objectId, true);
}
UsersPtr.forEach(x => {
newACL.setReadAccess(x.objectId, true);
newACL.setWriteAccess(x.objectId, true);
@@ -100,9 +95,9 @@ async function DocumentAftersave(request) {
}
async function updateSelfDoc(objId) {
// console.log("In side updateSelfDoc func")
// console.log(objId)
const Query = new Parse.Query('contracts_Document');
Query.include('CreatedBy');
const updateACL = await Query.get(objId, { useMasterKey: true });
const res = JSON.parse(JSON.stringify(updateACL));
// console.log("res");
@@ -110,8 +105,10 @@ async function DocumentAftersave(request) {
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(request.user, true);
newACL.setWriteAccess(request.user, true);
if (res?.CreatedBy) {
newACL.setReadAccess(res?.CreatedBy?.objectId, true);
newACL.setWriteAccess(res?.CreatedBy?.objectId, true);
}
updateACL.setACL(newACL);
updateACL.save(null, { useMasterKey: true });
}
@@ -8,15 +8,19 @@ export default async function TemplateAfterSave(request) {
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
} else {
await updateSelfDoc(request.object.id);
if (request?.object?.id && request?.user) {
await updateSelfDoc(request.object.id);
}
}
} else {
if (request.user) {
if (request?.user) {
const signers = request.object.get('Signers');
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
} else {
await updateSelfDoc(request.object.id);
if (request?.object?.id) {
await updateSelfDoc(request.object.id);
}
}
}
}
@@ -30,6 +34,7 @@ export default async function TemplateAfterSave(request) {
// console.log(objId)
const Query = new Parse.Query('contracts_Template');
Query.include('Signers');
Query.include('CreatedBy');
const updateACL = await Query.get(objId, { useMasterKey: true });
const res = JSON.parse(JSON.stringify(updateACL));
// console.log("res");
@@ -52,8 +57,10 @@ export default async function TemplateAfterSave(request) {
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(request.user, true);
newACL.setWriteAccess(request.user, true);
if (res?.CreatedBy) {
newACL.setReadAccess(res?.CreatedBy?.objectId, true);
newACL.setWriteAccess(res?.CreatedBy?.objectId, true);
}
UsersPtr.forEach(x => {
newACL.setReadAccess(x.objectId, true);
@@ -66,17 +73,20 @@ export default async function TemplateAfterSave(request) {
async function updateSelfDoc(objId) {
// console.log("Inside updateSelfDoc func")
const Query = new Parse.Query('contracts_Template');
Query.include('CreatedBy');
const updateACL = await Query.get(objId, { useMasterKey: true });
// const res = JSON.parse(JSON.stringify(updateACL));
const res = JSON.parse(JSON.stringify(updateACL));
// console.log("res");
// console.log(JSON.stringify(res));
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(request.user, true);
newACL.setWriteAccess(request.user, true);
if (res?.CreatedBy) {
newACL.setReadAccess(res?.CreatedBy?.objectId, true);
newACL.setWriteAccess(res?.CreatedBy?.objectId, true);
}
updateACL.setACL(newACL);
updateACL.save(null, { useMasterKey: true });
}
@@ -0,0 +1,66 @@
import axios from 'axios';
export default async function callWebhook(request) {
const event = request.params.event;
const body = request.params.body;
const serverUrl = process.env.SERVER_URL;
const appId = process.env.APP_ID;
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
const extendcls = new Parse.Query('contracts_Users');
extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
const res = await extendcls.first({ useMasterKey: true });
if (res) {
const extUser = JSON.parse(JSON.stringify(res));
if (extUser?.Webhook) {
const params = {
event: event,
...body,
};
await axios
.post(extUser?.Webhook, params, {
headers: { 'Content-Type': 'application/json' },
})
.then(res => {
try {
// console.log('res ', res);
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', res?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
})
.catch(err => {
console.log('Err send data to webhook', err);
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', err?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
});
}
return { message: 'webhook called!' };
}
} else {
return { message: 'User not found!' };
}
}
@@ -0,0 +1,44 @@
import { generateApiKey } from 'generate-api-key';
import axios from 'axios';
export default async function generateApiToken(request) {
const serverUrl = process.env.SERVER_URL;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', { __type: 'Pointer', className: '_User', objectId: userId });
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// return exsiting Token
console.log('Regenerate API Token');
const AppToken = Parse.Object.extend('appToken');
const updateToken = new AppToken();
updateToken.id = token.id;
const newToken = generateApiKey({ method: 'base62', prefix: 'opensign' });
updateToken.set('token', newToken);
const updatedRes = await updateToken.save(null, { useMasterKey: true });
return updatedRes;
} else {
// Create New Token
console.log('New API Token Generation');
const appToken = Parse.Object.extend('appToken');
const appTokenQuery = new appToken();
const token = generateApiKey({ method: 'base62', prefix: 'opensign' });
appTokenQuery.set('token', token);
appTokenQuery.set('userId', { __type: 'Pointer', className: '_User', objectId: userId });
const newRes = await appTokenQuery.save(null, { useMasterKey: true });
return newRes;
}
} else {
return 'User not found!';
}
} catch (err) {
console.log('err ', err);
}
}
@@ -1,9 +1,13 @@
async function getUserDetails(request) {
try {
const userId = request.params.userId;
const userQuery = new Parse.Query('contracts_Users');
userQuery.equalTo('Email', request.params.email);
userQuery.include('TenantId');
userQuery.include('UserId')
userQuery.include('UserId');
if (userId) {
userQuery.equalTo('CreatedBy', { __type: 'Pointer', className: '_User', objectId: userId });
}
const res = await userQuery.first({ useMasterKey: true });
return res;
} catch (err) {
@@ -0,0 +1,29 @@
import axios from 'axios';
export default async function getapitoken(request) {
const serverUrl = process.env.SERVER_URL;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', { __type: 'Pointer', className: '_User', objectId: userId });
const res = await tokenQuery.first({ useMasterKey: true });
if (res) {
return { status: 'success', result: res.get('token') };
}
}
} catch (err) {
console.log('Err', err);
console.log('err', err);
if (err.code == 209) {
return { error: 'Invalid session token' };
} else {
return { error: "You don't have access!" };
}
}
}
+105 -41
View File
@@ -1,22 +1,18 @@
import SignPDF from './SignPDF.min.cjs';
import fs from 'node:fs';
import axios from 'axios';
import FormData from 'form-data';
// import plainplaceholder from './customSignPdf/plainplaceholder.min.js';
import { plainAddPlaceholder } from 'node-signpdf/dist/helpers/index.js';
const serverUrl = process.env.SERVER_URL,
APPID = process.env.APP_ID,
masterKEY = process.env.MASTER_KEY;
async function uploadFile(a) {
async function uploadFile(e, a) {
try {
var e = new FormData(),
t =
(e.append('file', fs.createReadStream(a)),
{ 'content-type': 'multipart/form-data', 'X-Parse-Application-Id': process.env.APP_ID }),
s = process.env.SERVER_URL.slice(0, -4) + '/file_upload';
return (await axios.post(s, e, { headers: t })).data;
var t = fs.readFileSync(a),
s = new Parse.File(e, [...t]),
r = (await s.save({ useMasterKey: !0 }), s.url());
return { imageUrl: r };
} catch (e) {
console.log('err ', e), fs.unlinkSync(a);
console.log('Err ', e), fs.unlinkSync(a);
}
}
async function updateDoc(t, s, r, i, n, o) {
@@ -33,8 +29,8 @@ async function updateDoc(t, s, r, i, n, o) {
);
let a = !1;
!((n.Signers && 0 < n.Signers.length && l.length !== n.Signers.length) || !(a = !0));
var p = { SignedUrl: s, AuditTrail: e, IsCompleted: a };
await axios.put(serverUrl + '/classes/contracts_Document/' + t, p, {
var c = { SignedUrl: s, AuditTrail: e, IsCompleted: a };
await axios.put(serverUrl + '/classes/contracts_Document/' + t, c, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
@@ -59,9 +55,9 @@ async function sendMail(e) {
html:
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=https://qikinnovation.ams3.digitaloceanspaces.com/logo.png height='50' style='padding:20px'/> </div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document Copy</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document " +
s +
' Standard is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from Open Sign. For any queries regarding this email, please contact the sender ' +
' Standard is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign. For any queries regarding this email, please contact the sender ' +
t.Mail +
' directly. If you think this email is inappropriate or spam, you may file a complaint with Open Sign <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>'
' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>',
};
await axios.post(serverUrl + '/functions/sendmailv3', a, {
headers: {
@@ -84,9 +80,9 @@ async function sendCompletedMail(e) {
html:
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=https://qikinnovation.ams3.digitaloceanspaces.com/logo.png height='50' style='padding:20px'/> </div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document sign successfully</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document" +
s +
'. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from Open Sign. For any queries regarding this email, please contact the sender ' +
'. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign. For any queries regarding this email, please contact the sender ' +
t.Mail +
' directly. If you think this email is inappropriate or spam, you may file a complaint with Open Sign <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>',
' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>',
};
await axios.post(serverUrl + '/functions/sendmailv3', a, {
headers: {
@@ -96,6 +92,64 @@ async function sendCompletedMail(e) {
},
});
}
async function sendDoctoWebhook(t, e, a, s) {
let r = [];
(r = s
? { name: s?.Name, email: s?.Email, phone: s?.Phone }
: t?.data?.Signers?.map(e => ({ name: e.Name, email: e.Email, phone: e.Phone })) || [
{
name: t?.data?.ExtUserPtr?.Name,
email: t?.data?.ExtUserPtr?.Email,
phone: t?.data?.ExtUserPtr?.Phone,
},
]),
t.data.ExtUserPtr?.Webhook &&
((s =
'signed' === a
? { signer: r, signedAt: new Date() }
: { signers: r, completedAt: new Date() }),
(a = {
event: a,
objectId: t?.data?.objectId,
file: e || '',
name: t?.data?.Name,
note: t?.data?.Note || '',
description: t?.data?.Description || '',
...s,
createdAt: t?.data?.createdAt,
}),
await axios
.post(t?.data?.ExtUserPtr?.Webhook, a, { headers: { 'Content-Type': 'application/json' } })
.then(e => {
try {
var a = new Parse.Object('contracts_Webhook');
a.set('Log', e?.status),
a.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: t.data.ExtUserPtr.UserId.objectId,
}),
a.save(null, { useMasterKey: !0 });
} catch (e) {
console.log('err save in contracts_Webhook', e);
}
})
.catch(e => {
console.log('Err send data to webhook', e);
try {
var a = new Parse.Object('contracts_Webhook');
a.set('Log', e?.status),
a.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: t.data.ExtUserPtr.UserId.objectId,
}),
a.save(null, { useMasterKey: !0 });
} catch (e) {
console.log('err save in contracts_Webhook', e);
}
}));
}
async function PDF(i, n) {
try {
i.params.sign;
@@ -121,28 +175,28 @@ async function PDF(i, n) {
{
var d,
l,
p,
c = JSON.stringify({ objectId: a });
c,
p = JSON.stringify({ objectId: a });
let s, r;
r = a
? (d = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + c, {
? (d = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + p, {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': i.headers.sessiontoken,
},
})).data && 0 < d.data.results.length
? ((s = d), 'contracts_Contactbook')
: ((s = await axios.get(serverUrl + '/classes/contracts_Users?where=' + c, {
: ((s = await axios.get(serverUrl + '/classes/contracts_Users?where=' + p, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
})),
'contracts_Users')
: ((l = JSON.stringify({
UserId: { __type: 'Pointer', className: '_User', objectId: t.data.objectId },
})),
(p = await axios.get(serverUrl + '/classes/contracts_Users?where=' + l, {
(c = await axios.get(serverUrl + '/classes/contracts_Users?where=' + l, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
})).data && 0 < p.data.results.length
? ((s = p), 'contracts_Users')
})).data && 0 < c.data.results.length
? ((s = c), 'contracts_Users')
: ((s = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + l, {
headers: {
'X-Parse-Application-Id': APPID,
@@ -155,8 +209,8 @@ async function PDF(i, n) {
if (!i.params.pdfFile) return { status: 'error', message: 'pdf file not present!' };
{
let e = Buffer.from(i.params.pdfFile, 'base64');
var u = process.env.PFX_BASE64,
h = Buffer.from(u, 'base64'),
var h = process.env.PFX_BASE64,
u = Buffer.from(h, 'base64'),
f = {
UserPtr: { __type: 'Pointer', className: r, objectId: s.data.results[0].objectId },
SignedUrl: '',
@@ -175,56 +229,66 @@ async function PDF(i, n) {
);
var v,
P,
x = `./exports/exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
A =
b = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
x = './exports/' + b,
U =
(t
? ((v = o.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')),
(e =
v && 0 < v.length
? plainAddPlaceholder({
pdfBuffer: e,
reason: 'Digitally signed by Open sign for ' + v?.join(', '),
reason: 'Digitally signed by OpenSign for ' + v?.join(', '),
location: 'location',
signatureLength: 1e4,
})
: plainAddPlaceholder({
pdfBuffer: e,
reason: 'Digitally signed by Open sign for ' + m + ' <' + g + '>',
reason: 'Digitally signed by OpenSign for ' + m + ' <' + g + '>',
location: 'location',
signatureLength: 1e4,
})),
(P = await new SignPDF(e, h).signPDF()),
(P = await new SignPDF(e, u).signPDF()),
fs.writeFileSync(x, P))
: fs.writeFileSync(x, e),
await uploadFile(x));
if (A && A.imageUrl) {
await uploadFile(b, x));
if (U && U.imageUrl) {
const n = await updateDoc(
i.params.docId,
A.imageUrl,
U.imageUrl,
s.data.results[0].objectId,
i.headers['x-real-ip'],
o.data,
r
);
return (
sendMail({
url: A.imageUrl,
var I,
w = {
url: U.imageUrl,
sender: { Mail: o.data.ExtUserPtr.Email, Name: o.data.ExtUserPtr.Name },
pdfName: o.data.Name,
receiver: g,
}),
};
return (
o.data.IsSendMail && !1 === o.data.IsSendMail
? console.log("don't send mail")
: sendMail(w),
sendDoctoWebhook(o, U.imageUrl, 'signed', s?.data.results?.[0]),
n &&
n.isCompleted &&
sendCompletedMail({
url: A.imageUrl,
sender: { Mail: o.data.ExtUserPtr.Email, Name: 'Open sign' },
((I = {
url: U.imageUrl,
sender: { Mail: o.data.ExtUserPtr.Email, Name: 'OpenSign' },
pdfName: o.data.Name,
receiver: o.data.ExtUserPtr.Email,
}),
o.data.IsSendMail && !1 === o.data.IsSendMail
? console.log("don't send mail")
: sendCompletedMail(I),
sendDoctoWebhook(o, U.imageUrl, 'completed')),
fs.unlinkSync(x),
console.log('New Signed PDF created called: ' + x),
'success' === n.message
? { status: 'success', data: A.imageUrl }
? { status: 'success', data: U.imageUrl }
: { status: 'error', message: 'please provide required parameters!' }
);
}
@@ -11,14 +11,27 @@ export default function reportJson(id, userId) {
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
$or: [{Signers:{$eq:[]}}, { Signers: null }, { Signers: { $exists: true }, Placeholders: null }],
$or: [
{ Signers: { $eq: [] } },
{ Signers: null },
{ Signers: { $exists: true }, Placeholders: null },
],
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
},
keys: ['Name', 'Note', 'Folder.Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name'],
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
],
};
// Need your sign report
case '4Hhwbp482K':
@@ -53,6 +66,8 @@ export default function reportJson(id, userId) {
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Signers.UserId',
'AuditTrail',
],
@@ -77,7 +92,16 @@ export default function reportJson(id, userId) {
$gt: { __type: 'Date', iso: new Date().toISOString() },
},
},
keys: ['Name', 'Note', 'Folder.Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name'],
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
],
};
// completed documents report
case 'kQUoW4hUXz':
@@ -99,8 +123,11 @@ export default function reportJson(id, userId) {
'Note',
'Folder.Name',
'URL',
'SignedUrl',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'TimeToCompleteDays',
],
};
@@ -120,7 +147,16 @@ export default function reportJson(id, userId) {
},
},
keys: ['Name', 'Note', 'Folder.Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name'],
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
],
};
// Expired Documents report
case 'zNqBHXHsYH':
@@ -150,7 +186,16 @@ export default function reportJson(id, userId) {
objectId: currentUserId,
},
},
keys: ['Name', 'Note', 'Folder.Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name'],
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
],
};
// Recently sent for signatures report show on dashboard
case 'd9k3UfYHBc':
@@ -172,7 +217,15 @@ export default function reportJson(id, userId) {
$gt: { __type: 'Date', iso: new Date().toISOString() },
},
},
keys: ['Name', 'Folder.Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name'],
keys: [
'Name',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
],
};
// Recent signature requests report show on dashboard
case '5Go51Q7T8r':
@@ -200,7 +253,16 @@ export default function reportJson(id, userId) {
},
},
},
keys: ['Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name', 'Signers.UserId', 'AuditTrail'],
keys: [
'Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.UserId',
'AuditTrail',
'Signers.Email',
'Signers.Phone',
],
};
// Drafts report show on dashboard
case 'kC5mfynCi4':
@@ -211,14 +273,27 @@ export default function reportJson(id, userId) {
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
$or: [{Signers:{$eq:[]}}, { Signers: null }, { Signers: { $exists: true }, Placeholders: null }],
$or: [
{ Signers: { $eq: [] } },
{ Signers: null },
{ Signers: { $exists: true }, Placeholders: null },
],
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
},
keys: ['Name', 'Note', 'Folder.Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name'],
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
],
};
// contact book report
case '5KhaPr482K':
@@ -249,7 +324,16 @@ export default function reportJson(id, userId) {
objectId: currentUserId,
},
},
keys: ['Name', 'Note', 'Folder.Name', 'URL', 'ExtUserPtr.Name', 'Signers.Name'],
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
],
};
default:
return null;
@@ -0,0 +1,29 @@
import axios from 'axios';
export default async function savewebhook(request) {
const serverUrl = process.env.SERVER_URL;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
const contractuser = new Parse.Query('contracts_Users');
contractuser.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
const user = await contractuser.first({ useMasterKey: true });
if (user) {
const updateUser = new Parse.Object('contracts_Users');
updateUser.id = user.id;
updateUser.set('Webhook', request.params.url);
const updatedRes = await updateUser.save(null, { useMasterKey: true });
if (updatedRes) {
return { code: 200, Webhook: updatedRes.get('Webhook') };
}
}
} catch (err) {
console.log('update user', err);
return err;
}
}
@@ -0,0 +1,652 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
const className = 'w_menu';
const userMenu = new Parse.Query(className);
const updateUserMenu = await userMenu.get('H9vRfEYKhT');
updateUserMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: 'far fa-newspaper',
title: 'New Document',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-pen-nib',
title: 'Sign yourself',
target: '_self',
pageType: 'form',
description: '',
objectId: 'sHAnZphf69',
},
{
icon: 'fa-solid fa-paper-plane',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
{
icon: 'fas fa-file-signature',
title: 'New template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
],
},
{
icon: 'fa-solid fa-file-contract',
title: 'Templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/managesign',
},
{
icon: 'fa-solid fa-key',
title: 'API Token',
target: '_self',
pageType: 'generatetoken',
description: '',
objectId: '',
},
],
},
]);
const AdminMenu = new Parse.Query(className);
const updateAdminMenu = await AdminMenu.get('VPh91h0ZHk');
updateAdminMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: 'far fa-newspaper',
title: 'New Document',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-pen-nib',
title: 'Sign yourself',
target: '_self',
pageType: 'form',
description: '',
objectId: 'sHAnZphf69',
},
{
icon: 'fa-solid fa-paper-plane',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
{
icon: 'fas fa-file-signature',
title: 'New template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
],
},
{
icon: 'fa-solid fa-file-contract',
title: 'Templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/managesign',
},
{
icon: 'far fa-user',
title: 'Add User',
target: '_self',
pageType: 'form',
description: '',
objectId: 'lM0xRnM3iE',
},
{
icon: 'fa-solid fa-key',
title: 'API Token',
target: '_self',
pageType: 'generatetoken',
description: '',
objectId: '',
},
],
},
]);
// TODO: Set the schema here
// Example:
// schema.addString('name').addNumber('cash');
const batch = [updateUserMenu, updateAdminMenu];
return Parse.Object.saveAll(batch, { useMasterKey: true });
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
// TODO: set className here
const className = 'w_menu';
const userMenu = new Parse.Query(className);
const revertUserMenu = await userMenu.get('H9vRfEYKhT');
revertUserMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: 'far fa-newspaper',
title: 'New Document',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-pen-nib',
title: 'Sign yourself',
target: '_self',
pageType: 'form',
description: '',
objectId: 'sHAnZphf69',
},
{
icon: 'fa-solid fa-paper-plane',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
{
icon: 'fas fa-file-signature',
title: 'New template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
],
},
{
icon: 'fa-solid fa-file-contract',
title: 'Templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/managesign',
},
],
},
]);
const adminMenu = new Parse.Query(className);
const revertAdminMenu = await adminMenu.get('VPh91h0ZHk');
revertAdminMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: 'far fa-newspaper',
title: 'New Document',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-pen-nib',
title: 'Sign yourself',
target: '_self',
pageType: 'form',
description: '',
objectId: 'sHAnZphf69',
},
{
icon: 'fa-solid fa-paper-plane',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
{
icon: 'fas fa-file-signature',
title: 'New template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
],
},
{
icon: 'fa-solid fa-file-contract',
title: 'Templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/managesign',
},
{
icon: 'far fa-user',
title: 'Add User',
target: '_self',
pageType: 'form',
description: '',
objectId: 'lM0xRnM3iE',
},
],
},
]);
// TODO: Set the schema here
// Example:
// schema.addString('name').addNumber('cash');
const batch = [revertUserMenu, revertAdminMenu];
return Parse.Object.saveAll(batch, { useMasterKey: true });
};
@@ -0,0 +1,684 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
const className = 'w_menu';
const userMenu = new Parse.Query(className);
const updateUserMenu = await userMenu.get('H9vRfEYKhT');
updateUserMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: 'far fa-newspaper',
title: 'New Document',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-pen-nib',
title: 'Sign yourself',
target: '_self',
pageType: 'form',
description: '',
objectId: 'sHAnZphf69',
},
{
icon: 'fa-solid fa-paper-plane',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
{
icon: 'fas fa-file-signature',
title: 'New template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
],
},
{
icon: 'fa-solid fa-file-contract',
title: 'Templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/managesign',
},
{
icon: 'fa-solid fa-key',
title: 'API Token',
target: '_self',
pageType: 'generatetoken',
description: '',
objectId: '',
},
{
icon: 'fa-solid fa-globe',
title: 'Webhook',
target: '_self',
pageType: 'webhook',
description: '',
objectId: '',
},
],
},
]);
const AdminMenu = new Parse.Query(className);
const updateAdminMenu = await AdminMenu.get('VPh91h0ZHk');
updateAdminMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: 'far fa-newspaper',
title: 'New Document',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-pen-nib',
title: 'Sign yourself',
target: '_self',
pageType: 'form',
description: '',
objectId: 'sHAnZphf69',
},
{
icon: 'fa-solid fa-paper-plane',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
{
icon: 'fas fa-file-signature',
title: 'New template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
],
},
{
icon: 'fa-solid fa-file-contract',
title: 'Templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/managesign',
},
{
icon: 'far fa-user',
title: 'Add User',
target: '_self',
pageType: 'form',
description: '',
objectId: 'lM0xRnM3iE',
},
{
icon: 'fa-solid fa-key',
title: 'API Token',
target: '_self',
pageType: 'generatetoken',
description: '',
objectId: '',
},
{
icon: 'fa-solid fa-globe',
title: 'Webhook',
target: '_self',
pageType: 'webhook',
description: '',
objectId: '',
},
],
},
]);
// TODO: Set the schema here
// Example:
// schema.addString('name').addNumber('cash');
const batch = [updateUserMenu, updateAdminMenu];
return Parse.Object.saveAll(batch, { useMasterKey: true });
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
// TODO: set className here
const className = 'w_menu';
const userMenu = new Parse.Query(className);
const revertUserMenu = await userMenu.get('H9vRfEYKhT');
revertUserMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: 'far fa-newspaper',
title: 'New Document',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-pen-nib',
title: 'Sign yourself',
target: '_self',
pageType: 'form',
description: '',
objectId: 'sHAnZphf69',
},
{
icon: 'fa-solid fa-paper-plane',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
{
icon: 'fas fa-file-signature',
title: 'New template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
],
},
{
icon: 'fa-solid fa-file-contract',
title: 'Templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/managesign',
},
{
icon: 'fa-solid fa-key',
title: 'API Token',
target: '_self',
pageType: 'generatetoken',
description: '',
objectId: '',
},
],
},
]);
const adminMenu = new Parse.Query(className);
const revertAdminMenu = await adminMenu.get('VPh91h0ZHk');
revertAdminMenu.set('menuItems', [
{
icon: 'fas fa-tachometer-alt',
title: 'Dashboard',
target: '',
pageType: 'dashboard',
description: '',
objectId: '35KBoSgoAK',
},
{
icon: 'far fa-newspaper',
title: 'New Document',
target: '_self',
pageType: null,
description: null,
objectId: null,
children: [
{
icon: 'fas fa-pen-nib',
title: 'Sign yourself',
target: '_self',
pageType: 'form',
description: '',
objectId: 'sHAnZphf69',
},
{
icon: 'fa-solid fa-paper-plane',
title: 'Request signatures',
target: '_self',
pageType: 'form',
description: '',
objectId: '8mZzFxbG1z',
},
{
icon: 'fas fa-file-signature',
title: 'New template',
target: '_self',
pageType: 'form',
description: '',
objectId: 'template',
},
],
},
{
icon: 'fa-solid fa-file-contract',
title: 'Templates',
target: '_self',
pageType: 'report',
description: '',
objectId: '6TeaPr321t',
},
{
icon: 'fas fa-folder',
title: 'OpenSign™ Drive',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/legadrive',
},
{
icon: 'fas fa-address-card',
title: 'Reports',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-signature',
title: 'Need your sign',
target: '_self',
pageType: 'report',
description: '',
objectId: '4Hhwbp482K',
},
{
icon: 'fas fa-tasks',
title: 'In Progress',
target: '_self',
pageType: 'report',
description: '',
objectId: '1MwEuxLEkF',
},
{
icon: 'fas fa-check-circle',
title: 'Completed',
target: '_self',
pageType: 'report',
description: '',
objectId: 'kQUoW4hUXz',
},
{
icon: 'fas fa-edit',
title: 'Drafts',
target: '_self',
pageType: 'report',
description: '',
objectId: 'ByHuevtCFY',
},
{
icon: 'fas fa-times-circle',
title: 'Declined',
target: '_self',
pageType: 'report',
description: '',
objectId: 'UPr2Fm5WY3',
},
{
icon: 'fas fa-hourglass-end',
title: 'Expired',
target: '_self',
pageType: 'report',
description: '',
objectId: 'zNqBHXHsYH',
},
{
icon: 'fa-solid fa-address-book',
title: 'Contactbook',
target: '_self',
pageType: 'report',
description: '',
objectId: '5KhaPr482K',
},
],
},
{
icon: 'fas fa-cog',
title: 'Settings',
target: '_self',
pageType: null,
description: '',
objectId: null,
children: [
{
icon: 'fas fa-pen-fancy',
title: 'My Signature',
target: '_self',
pageType: 'mf',
description: '',
objectId:
'remoteUrl=aHR0cHM6Ly9xaWstYWktb3JnLmdpdGh1Yi5pby9TaWduLU1pY3JvYXBwVjIvcmVtb3RlRW50cnkuanM=&moduleToLoad=AppRoutes&remoteName=signmicroapp/managesign',
},
{
icon: 'far fa-user',
title: 'Add User',
target: '_self',
pageType: 'form',
description: '',
objectId: 'lM0xRnM3iE',
},
{
icon: 'fa-solid fa-key',
title: 'API Token',
target: '_self',
pageType: 'generatetoken',
description: '',
objectId: '',
},
],
},
]);
// TODO: Set the schema here
// Example:
// schema.addString('name').addNumber('cash');
const batch = [revertUserMenu, revertAdminMenu];
return Parse.Object.saveAll(batch, { useMasterKey: true });
};
@@ -1 +1 @@
Email Address Verification
OpenSign™ Email Address Verification
+18 -2
View File
@@ -19,7 +19,8 @@ import AWS from 'aws-sdk';
import { app as customRoute } from './cloud/customRoute/customApp.js';
import { exec } from 'child_process';
import { createTransport } from 'nodemailer';
import { app as v1 } from './cloud/customRoute/v1/apiV1.js';
import { PostHog } from 'posthog-node';
const spacesEndpoint = new AWS.Endpoint(process.env.DO_ENDPOINT);
// console.log("configuration ", configuration);
if (process.env.USE_LOCAL !== 'TRUE') {
@@ -73,6 +74,7 @@ export const config = {
import('./cloud/main.js');
},
appId: process.env.APP_ID || 'myAppId',
maxLimit: 500,
masterKey: process.env.MASTER_KEY || '', //Add your master key here. Keep it secret!
masterKeyIps: ['0.0.0.0/0', '::1'], // '::1'
serverURL: process.env.SERVER_URL || 'http://localhost:8080/app', // Don't forget to change to https if needed
@@ -129,7 +131,8 @@ export const config = {
export const app = express();
app.use(cors());
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true }));
app.use(function (req, res, next) {
// console.log("req ", req.headers);
// console.log("x-forwarded-for", req.headers["x-forwarded-for"]);
@@ -151,6 +154,16 @@ function getUserIP(request) {
return request.socket.remoteAddress;
}
}
app.use(function (req, res, next) {
const ph_project_api_key = process.env.PH_PROJECT_API_KEY;
try {
req.posthog = new PostHog(ph_project_api_key);
} catch (err) {
// console.log('Err', err);
req.posthog = '';
}
next();
});
// Serve static assets from the /public folder
app.use('/public', express.static(path.join(__dirname, '/public')));
@@ -164,6 +177,9 @@ if (!process.env.TESTING) {
// Mount your custom express app
app.use('/', customRoute);
// Mount v1
app.use('/v1', v1);
// Parse Server plays nicely with the rest of your web routes
app.get('/', function (req, res) {
// res.statusCode = 200;
+77 -1
View File
@@ -17,6 +17,7 @@
"express": "4.18.2",
"express-sse": "^0.5.3",
"form-data": "^4.0.0",
"generate-api-key": "^1.0.2",
"jsonschema": "^1.4.1",
"mailgun.js": "^9.3.0",
"mongoose": "^7.2.1",
@@ -34,6 +35,7 @@
"parse-server-s3-adapter": "^1.2.0",
"pdf-lib": "^1.16.0",
"pdfkit": "^0.13.0",
"posthog-node": "^3.6.2",
"razorpay": "^2.8.6",
"request": "^2.88.2"
},
@@ -2037,6 +2039,11 @@
"resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
"integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="
},
"node_modules/base-x": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz",
"integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -2401,6 +2408,11 @@
"node": ">=4"
}
},
"node_modules/chance": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/chance/-/chance-1.1.11.tgz",
"integrity": "sha512-kqTg3WWywappJPqtgrdvbA380VoXO2eu9VCV895JgbyHsaErXdyHK9LOZ911OvAk6L0obK7kDk9CGs8+oBawVA=="
},
"node_modules/charenc": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
@@ -4373,6 +4385,20 @@
"lodash.padstart": "^4.1.0"
}
},
"node_modules/generate-api-key": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/generate-api-key/-/generate-api-key-1.0.2.tgz",
"integrity": "sha512-4rPSpXyboIXfugOTN3/0Qaoqpzbk0sepzPS0XyxPh3UMuu+Trk+0JMyJ6mB/7FEgp7oZ1juqsRW+8wSYeKDbfA==",
"dependencies": {
"base-x": "^4.0.0",
"chance": "^1.1.8",
"rfc4648": "^1.5.2",
"uuid": "^8.3.2"
},
"engines": {
"node": ">=14"
}
},
"node_modules/generic-pool": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
@@ -8348,6 +8374,47 @@
"node": ">=0.10.0"
}
},
"node_modules/posthog-node": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-3.6.2.tgz",
"integrity": "sha512-tVIaShR3SxBx17AlAUS86jQTweKuJIFRedBB504fCz7YPnXJTYSrVcUHn5IINE2wu4jUQimQK6ihQr90Djrdrg==",
"dependencies": {
"axios": "^1.6.2",
"rusha": "^0.8.14"
},
"engines": {
"node": ">=15.0.0"
}
},
"node_modules/posthog-node/node_modules/axios": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
"dependencies": {
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/posthog-node/node_modules/follow-redirects": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/precond": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz",
@@ -8804,6 +8871,11 @@
"node": ">=0.10.0"
}
},
"node_modules/rfc4648": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/rfc4648/-/rfc4648-1.5.3.tgz",
"integrity": "sha512-MjOWxM065+WswwnmNONOT+bD1nXzY9Km6u3kzvnx8F8/HXGZdz3T6e6vZJ8Q/RIMUSp/nxqjH3GwvJDy8ijeQQ=="
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -8840,6 +8912,11 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/rusha": {
"version": "0.8.14",
"resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.14.tgz",
"integrity": "sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA=="
},
"node_modules/safari-14-idb-fix": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/safari-14-idb-fix/-/safari-14-idb-fix-3.0.0.tgz",
@@ -9897,7 +9974,6 @@
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
+2
View File
@@ -26,6 +26,7 @@
"express": "4.18.2",
"express-sse": "^0.5.3",
"form-data": "^4.0.0",
"generate-api-key": "^1.0.2",
"jsonschema": "^1.4.1",
"mailgun.js": "^9.3.0",
"mongoose": "^7.2.1",
@@ -43,6 +44,7 @@
"parse-server-s3-adapter": "^1.2.0",
"pdf-lib": "^1.16.0",
"pdfkit": "^0.13.0",
"posthog-node": "^3.6.2",
"razorpay": "^2.8.6",
"request": "^2.88.2"
},
+934
View File
@@ -0,0 +1,934 @@
{
"openapi": "3.0.3",
"info": {
"title": "OpenSign API v1",
"description": "This is API documentation for OpenSign API v1 based on the OpenAPI 3.1 specification. \n\nSome useful links:\n- [Official Website](https://www.opensignlabs.com)\n- [Github repo](https://github.com/opensignlabs/opensign)",
"termsOfService": "http://www.opensignlabs.com/terms/",
"contact": {
"email": "contact@opensignlabs.com"
},
"license": {
"name": "AGPL 3.0",
"url": "http://github.com/opensignlabs/opensign/LICENSE"
},
"version": "1.0.0"
},
"externalDocs": {
"description": "Find out more about OpenSign",
"url": "http://docs.opensignlabs.com"
},
"servers": [
{
"url": "https://app.opensignlabs.com/api/v1"
}
],
"tags": [
{
"name": "OpenSign",
"description": "OpenSource DocuSign alternative",
"externalDocs": {
"description": "Find out more",
"url": "http://www.opensignlabs.com"
}
},
{
"name": "Github repo",
"description": "Access the source code",
"externalDocs": {
"description": "Visit github",
"url": "http://github.com/opensignlabs/opensign"
}
},
{
"name": "templates",
"description": "Operations about templates"
},
{
"name": "users",
"description": "Operations about users"
},
{
"name": "documents",
"description": "Operations about documents"
}
],
"paths": {
"/getuser": {
"put": {
"tags": [
"users"
],
"summary": "Update an existing pet",
"description": "Update an existing pet by Id",
"operationId": "updatePet",
"requestBody": {
"description": "Update an existent pet in the store",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Pet not found"
},
"405": {
"description": "Validation exception"
}
},
"security": [
{
"api_key": []
}
]
},
"post": {
"tags": [
"users"
],
"summary": "Add a new pet to the store",
"description": "Add a new pet to the store",
"operationId": "addPet",
"requestBody": {
"description": "Create a new pet in the store",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"405": {
"description": "Invalid input"
}
},
"security": [
{
"api_key": []
}
]
}
},
"/documentlist": {
"get": {
"tags": [
"documents"
],
"summary": "Finds Pets by status",
"description": "Multiple status values can be provided with comma separated strings",
"operationId": "findPetsByStatus",
"parameters": [
{
"name": "status",
"in": "query",
"description": "Status values that need to be considered for filter",
"required": false,
"explode": true,
"schema": {
"type": "string",
"default": "available",
"enum": [
"available",
"pending",
"sold"
]
}
}
],
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
}
},
"application/xml": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
}
}
}
},
"400": {
"description": "Invalid status value"
}
},
"security": [
{
"api_key": []
}
]
}
},
"/document/": {
"get": {
"tags": [
"documents"
],
"summary": "Finds Pets by tags",
"description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
"operationId": "findPetsByTags",
"parameters": [
{
"name": "tags",
"in": "query",
"description": "Tags to filter by",
"required": false,
"explode": true,
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
],
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
}
},
"application/xml": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
}
}
}
},
"400": {
"description": "Invalid tag value"
}
},
"security": [
{
"api_key": []
}
]
}
},
"/contact/{contact_id}": {
"get": {
"tags": [
"contacts"
],
"summary": "Find pet by ID",
"description": "Returns a single pet",
"operationId": "getPetById",
"parameters": [
{
"name": "contact_id",
"in": "path",
"description": "ID of pet to return",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Pet not found"
}
},
"security": [
{
"api_key": []
}
]
},
"post": {
"tags": [
"contacts"
],
"summary": "Updates a pet in the store with form data",
"description": "",
"operationId": "updatePetWithForm",
"parameters": [
{
"name": "contact_id",
"in": "path",
"description": "ID of pet that needs to be updated",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"name": "name",
"in": "query",
"description": "Name of pet that needs to be updated",
"schema": {
"type": "string"
}
},
{
"name": "status",
"in": "query",
"description": "Status of pet that needs to be updated",
"schema": {
"type": "string"
}
}
],
"responses": {
"405": {
"description": "Invalid input"
}
},
"security": [
{
"api_key": []
}
]
},
"delete": {
"tags": [
"contacts"
],
"summary": "Deletes a pet",
"description": "delete a pet",
"operationId": "deletePet",
"parameters": [
{
"name": "api_key",
"in": "header",
"description": "",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "contact_id",
"in": "path",
"description": "Pet id to delete",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"400": {
"description": "Invalid pet value"
}
},
"security": [
{
"api_key": []
}
]
}
},
"/contactlist": {
"post": {
"tags": [
"contacts"
],
"summary": "uploads an image",
"description": "",
"operationId": "uploadFile",
"requestBody": {
"content": {
"application/octet-stream": {
"schema": {
"type": "string",
"format": "binary"
}
}
}
},
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ApiResponse"
}
}
}
}
},
"security": [
{
"api_key": []
}
]
}
},
"/createdocument": {
"get": {
"tags": [
"documents"
],
"summary": "Returns pet inventories by status",
"description": "Returns a map of status codes to quantities",
"operationId": "getInventory",
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": {
"type": "integer",
"format": "int32"
}
}
}
}
}
},
"security": [
{
"api_key": []
}
]
}
},
"/createtemplate": {
"post": {
"tags": [
"templates"
],
"summary": "Place an order for a pet",
"description": "Place a new order in the store",
"operationId": "placeOrder",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Order"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Order"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/Order"
}
}
}
},
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Order"
}
}
}
},
"405": {
"description": "Invalid input"
}
}
}
},
"/template/{template_id}": {
"get": {
"tags": [
"templates"
],
"summary": "Find purchase order by ID",
"description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.",
"operationId": "getOrderById",
"parameters": [
{
"name": "template_id",
"in": "path",
"description": "ID of order that needs to be fetched",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"200": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Order"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Order"
}
}
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Order not found"
}
}
},
"delete": {
"tags": [
"templates"
],
"summary": "Delete purchase order by ID",
"description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",
"operationId": "deleteOrder",
"parameters": [
{
"name": "template_id",
"in": "path",
"description": "ID of the order that needs to be deleted",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"responses": {
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Order not found"
}
}
}
},
"/templatelist": {
"post": {
"tags": [
"templates"
],
"summary": "Create user",
"description": "This can only be done by the logged in user.",
"operationId": "createUser",
"requestBody": {
"description": "Created user object",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/User"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
},
"responses": {
"default": {
"description": "successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/User"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Order": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 10
},
"petId": {
"type": "integer",
"format": "int64",
"example": 198772
},
"quantity": {
"type": "integer",
"format": "int32",
"example": 7
},
"shipDate": {
"type": "string",
"format": "date-time"
},
"status": {
"type": "string",
"description": "Order Status",
"example": "approved",
"enum": [
"placed",
"approved",
"delivered"
]
},
"complete": {
"type": "boolean"
}
},
"xml": {
"name": "order"
}
},
"Customer": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 100000
},
"username": {
"type": "string",
"example": "fehguy"
},
"address": {
"type": "array",
"xml": {
"name": "addresses",
"wrapped": true
},
"items": {
"$ref": "#/components/schemas/Address"
}
}
},
"xml": {
"name": "customer"
}
},
"Address": {
"type": "object",
"properties": {
"street": {
"type": "string",
"example": "437 Lytton"
},
"city": {
"type": "string",
"example": "Palo Alto"
},
"state": {
"type": "string",
"example": "CA"
},
"zip": {
"type": "string",
"example": "94301"
}
},
"xml": {
"name": "address"
}
},
"Category": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 1
},
"name": {
"type": "string",
"example": "Dogs"
}
},
"xml": {
"name": "category"
}
},
"User": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 10
},
"username": {
"type": "string",
"example": "theUser"
},
"firstName": {
"type": "string",
"example": "John"
},
"lastName": {
"type": "string",
"example": "James"
},
"email": {
"type": "string",
"example": "john@email.com"
},
"password": {
"type": "string",
"example": "12345"
},
"phone": {
"type": "string",
"example": "12345"
},
"userStatus": {
"type": "integer",
"description": "User Status",
"format": "int32",
"example": 1
}
},
"xml": {
"name": "user"
}
},
"Tag": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
}
},
"xml": {
"name": "tag"
}
},
"Pet": {
"required": [
"name",
"photoUrls"
],
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"example": 10
},
"name": {
"type": "string",
"example": "doggie"
},
"category": {
"$ref": "#/components/schemas/Category"
},
"photoUrls": {
"type": "array",
"xml": {
"wrapped": true
},
"items": {
"type": "string",
"xml": {
"name": "photoUrl"
}
}
},
"tags": {
"type": "array",
"xml": {
"wrapped": true
},
"items": {
"$ref": "#/components/schemas/Tag"
}
},
"status": {
"type": "string",
"description": "pet status in the store",
"enum": [
"available",
"pending",
"sold"
]
}
},
"xml": {
"name": "pet"
}
},
"ApiResponse": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"type": {
"type": "string"
},
"message": {
"type": "string"
}
},
"xml": {
"name": "##default"
}
}
},
"requestBodies": {
"Pet": {
"description": "Pet object that needs to be added to the store",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
},
"application/xml": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"UserArray": {
"description": "List of user object",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
},
"securitySchemes": {
"api_key": {
"type": "apiKey",
"name": "X-Parse-ApiKey",
"in": "header"
}
}
}
}
+85
View File
@@ -30,6 +30,7 @@
"react-dom": "^18.2.0",
"react-draggable": "^4.4.6",
"react-helmet": "^6.1.0",
"react-konva": "^18.2.10",
"react-pdf": "^7.4.0",
"react-rnd": "^10.4.1",
"react-router-dom": "^6.16.0",
@@ -6442,6 +6443,14 @@
"@types/react": "*"
}
},
"node_modules/@types/react-reconciler": {
"version": "0.28.8",
"resolved": "https://registry.npmjs.org/@types/react-reconciler/-/react-reconciler-0.28.8.tgz",
"integrity": "sha512-SN9c4kxXZonFhbX4hJrZy37yw9e7EIxcpHCxQv5JUS18wDE5ovkQKlqQEkufdJCCMfuI9BnjUJvhYeJ9x5Ra7g==",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.7",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.7.tgz",
@@ -12523,6 +12532,17 @@
"set-function-name": "^2.0.1"
}
},
"node_modules/its-fine": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/its-fine/-/its-fine-1.1.1.tgz",
"integrity": "sha512-v1Ia1xl20KbuSGlwoaGsW0oxsw8Be+TrXweidxD9oT/1lAh6O3K3/GIM95Tt6WCiv6W+h2M7RB1TwdoAjQyyKw==",
"dependencies": {
"@types/react-reconciler": "^0.28.0"
},
"peerDependencies": {
"react": ">=18.0"
}
},
"node_modules/jake": {
"version": "10.8.7",
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz",
@@ -14685,6 +14705,26 @@
"node": ">= 8"
}
},
"node_modules/konva": {
"version": "9.3.2",
"resolved": "https://registry.npmjs.org/konva/-/konva-9.3.2.tgz",
"integrity": "sha512-I4CFGVbWKpzgYi1I0pmoHz8iYfmGc/A61yfltzcuBHMFuBwDsR2VnQo4+eFQ1dGyEAmHcdvTumJ871KqkhuGng==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/lavrton"
},
{
"type": "opencollective",
"url": "https://opencollective.com/konva"
},
{
"type": "github",
"url": "https://github.com/sponsors/lavrton"
}
],
"peer": true
},
"node_modules/language-subtag-registry": {
"version": "0.3.22",
"resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz",
@@ -17904,6 +17944,36 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"node_modules/react-konva": {
"version": "18.2.10",
"resolved": "https://registry.npmjs.org/react-konva/-/react-konva-18.2.10.tgz",
"integrity": "sha512-ohcX1BJINL43m4ynjZ24MxFI1syjBdrXhqVxYVDw2rKgr3yuS0x/6m1Y2Z4sl4T/gKhfreBx8KHisd0XC6OT1g==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/lavrton"
},
{
"type": "opencollective",
"url": "https://opencollective.com/konva"
},
{
"type": "github",
"url": "https://github.com/sponsors/lavrton"
}
],
"dependencies": {
"@types/react-reconciler": "^0.28.2",
"its-fine": "^1.1.1",
"react-reconciler": "~0.29.0",
"scheduler": "^0.23.0"
},
"peerDependencies": {
"konva": "^8.0.1 || ^7.2.5 || ^9.0.0",
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
},
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
@@ -17950,6 +18020,21 @@
"node": ">=6"
}
},
"node_modules/react-reconciler": {
"version": "0.29.0",
"resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.0.tgz",
"integrity": "sha512-wa0fGj7Zht1EYMRhKWwoo1H9GApxYLBuhoAuXN0TlltESAjDssB+Apf0T/DngVqaMyPypDmabL37vw/2aRM98Q==",
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
},
"engines": {
"node": ">=0.10.0"
},
"peerDependencies": {
"react": "^18.2.0"
}
},
"node_modules/react-refresh": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
@@ -26,6 +26,7 @@
"react-dom": "^18.2.0",
"react-draggable": "^4.4.6",
"react-helmet": "^6.1.0",
"react-konva": "^18.2.10",
"react-pdf": "^7.4.0",
"react-rnd": "^10.4.1",
"react-router-dom": "^6.16.0",
@@ -0,0 +1,429 @@
import React, { useEffect, useState } from "react";
import RenderAllPdfPage from "./component/renderAllPdfPage";
import "../css/./signature.css";
import "../css/AddUser.css";
import Title from "./component/Title";
import ModalUi from "../premitives/ModalUi";
import RenderDebugPdf from "./component/RenderDebugPdf";
import { pdfjs } from "react-pdf";
import Alert from "../premitives/Alert";
import HandleError from "./component/HandleError";
import { useWindowSize } from "../hook/useWindowSize";
function processDimensions(x, y, width, height) {
if (width < 0) {
x -= Math.abs(width);
width = Math.abs(width);
}
if (height < 0) {
y -= Math.abs(height);
height = Math.abs(height);
}
return {
x: Math.floor(x),
y: Math.floor(y),
width: Math.floor(width),
height: Math.floor(height)
};
}
const DebugUi = () => {
const { width } = useWindowSize();
const [pdf, setPdf] = useState("");
const [isModal, setIsModal] = useState(true);
const [allPages, setAllPages] = useState(null);
const [pageNumber, setPageNumber] = useState(1);
const [pdfLoadFail, setPdfLoadFail] = useState({
status: false,
type: "load"
});
const [pdfDetails, setPdfDetails] = useState({
name: "",
pdftype: "",
totalPages: "",
currentPage: 1,
x: 0,
y: 0,
base64: ""
});
const [hoverCoordinates, setHoverCoordinates] = useState({ x: 0, y: 0 });
const [pdfDimension, setPdfDimension] = useState({ width: 0, height: 0 });
const [annotations, setAnnotations] = useState([]);
const [newAnnotation, setNewAnnotation] = useState([]);
const [copied, setCopied] = useState(false);
useEffect(() => {
if (pdf && pdf.name) {
const fetchPdfMetadata = async () => {
try {
const pdfDataURL = URL.createObjectURL(pdf); // Convert File to data URL
console.log("pdfDataURL ", pdfDataURL);
const pdfInfo = await pdfjs.getDocument({ url: pdfDataURL }).promise;
const pdfType = await inferPdfType(pdfInfo);
setPdfDetails((prevDetails) => ({
...prevDetails,
pdftype: pdfType
}));
} catch (error) {
console.error("Error fetching PDF metadata:", error);
}
};
fetchPdfMetadata();
}
}, [pdf]);
const inferPdfType = async (pdf) => {
try {
const firstPage = await pdf.getPage(1);
const scale = 1;
const { width, height } = firstPage.getViewport({ scale });
setPdfDimension({ width: width, height: height });
// Assuming a standard DPI of 72, you can adjust this value if needed
const dpi = 72;
const widthInInches = width / dpi;
const heightInInches = height / dpi;
const isA1 = widthInInches > 23.39 && heightInInches > 16.54;
const isA2 = widthInInches > 16.54 && heightInInches > 11.69;
const isA3 = widthInInches > 11.69 && heightInInches > 8.27;
const isA4 = widthInInches > 8.27 && heightInInches > 5.83;
const isLegal = widthInInches > 8.5 && heightInInches > 14;
const isLetter = widthInInches > 8.5 && heightInInches > 11;
const isLedger = widthInInches > 11 && heightInInches > 17;
if (isA1) return "A1";
if (isA2) return "A2";
if (isA3) return "A3";
if (isA4) return "A4";
if (isLegal) return "Legal";
if (isLetter) return "Letter";
if (isLedger) return "Ledger";
return "Unknown";
} catch (error) {
console.error("Error getting page dimensions:", error);
return "Unknown";
}
};
const handleSubmit = (e) => {
e.preventDefault();
setIsModal(false);
setPdfDetails((prevData) => ({ ...prevData, name: pdf?.name }));
};
//function for get pdf page details
const pageDetails = async ({ numPages }) => {
const load = {
status: true
};
setPdfDetails((prevDetails) => ({ ...prevDetails, totalPages: numPages }));
setPdfLoadFail(load);
};
const handleMouseMoveDiv = (event) => {
setHoverCoordinates({
x: event.nativeEvent.offsetX,
y: event.nativeEvent.offsetY
});
};
const handleMouseDown = (event) => {
if (newAnnotation.length === 0) {
const { x, y } = event.target.getStage().getPointerPosition();
setNewAnnotation([
{ x, y, width: 0, height: 0, key: "0", page: pageNumber }
]);
}
};
const handleMouseUp = (event) => {
if (newAnnotation.length === 1) {
const sx = newAnnotation[0].x;
const sy = newAnnotation[0].y;
const { x, y } = event.target.getStage().getPointerPosition();
const result = processDimensions(sx, sy, x - sx, y - sy);
const annotationToAdd = {
...result,
key: annotations.length + 1,
page: pageNumber
};
annotations.push(annotationToAdd);
setNewAnnotation([]);
setAnnotations(annotations);
setPdfDetails((prevDetails) => ({
...prevDetails,
x: result.x,
y: result.y
}));
}
};
const handleMouseMove = (event) => {
if (newAnnotation.length === 1) {
const sx = newAnnotation[0].x;
const sy = newAnnotation[0].y;
const { x, y } = event.target.getStage().getPointerPosition();
setNewAnnotation([
{
x: sx,
y: sy,
width: x - sx,
height: y - sy,
page: pageNumber,
key: "0"
}
]);
}
};
const annotationsToDraw = [...annotations, ...newAnnotation];
const handlePageLoadSuccess = (page) => {
setPdfDetails((prevDetails) => ({
...prevDetails,
currentPage: page.pageNumber
}));
};
const copytoclipboard = (text) => {
navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => {
setCopied(false);
}, 1500); // Reset copied state after 1.5 seconds
};
const handleFileChange = (files) => {
const file = files[0];
setPdf(file);
if (file && file.type === "application/pdf") {
const reader = new FileReader();
reader.onloadend = () => {
const base64String = reader.result.split(",")[1];
setPdfDetails((prevdata) => ({ ...prevdata, base64: base64String }));
};
reader.readAsDataURL(file);
}
};
const handleDelete = (key) => {
const updateAnnotations = annotations.filter((x) => x.key !== key);
setAnnotations(updateAnnotations);
};
return (
<div>
{copied && <Alert type="success">Copied</Alert>}
<Title title={"Debug Pdf"} />
{width < 800 ? (
<HandleError handleError={"Debug PDF only availble for PC"} />
) : (
<>
{!isModal && (
<div className="signatureContainer">
{/* this component used to render all pdf pages in left side */}
<RenderAllPdfPage
signPdfUrl={pdf}
allPages={allPages}
setAllPages={setAllPages}
setPageNumber={setPageNumber}
pageNumber={pageNumber}
/>
{/* pdf render view */}
<div>
<div data-tut="reactourThird">
<RenderDebugPdf
pdfUrl={pdf}
pageDetails={pageDetails}
pageNumber={pageNumber}
setPdfLoadFail={setPdfLoadFail}
pdfLoadFail={pdfLoadFail}
handlePageLoadSuccess={handlePageLoadSuccess}
handleMouseMove={handleMouseMove}
handleMouseUp={handleMouseUp}
handleMouseDown={handleMouseDown}
hoverCoordinates={hoverCoordinates}
annotations={annotationsToDraw}
pdfDimension={pdfDimension}
handleMouseMoveDiv={handleMouseMoveDiv}
/>
</div>
</div>
<div style={{ backgroundColor: "white", width: 220 }}>
<div
style={{
fontSize: 18,
fontWeight: 500,
padding: "10px 12px",
borderBottom: "1px solid grey"
}}
>
PDF details
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Name: {pdfDetails?.name}
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Pdf type: {pdfDetails?.pdftype}
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Total Pages: {pdfDetails?.totalPages}
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Current Page: {pdfDetails?.currentPage}
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
Base64 : {pdfDetails?.base64.slice(0, 10)}...
<span
style={{
borderRadius: 4,
padding: "3px 5px",
border: "1px solid gray",
fontSize: 12,
margin: 2,
cursor: "pointer"
}}
onClick={() => copytoclipboard(pdfDetails?.base64)}
>
<i className="fa-solid fa-copy"></i>
</span>
</div>
<div
style={{
fontSize: 18,
fontWeight: 500,
padding: "10px 12px",
borderBottom: "1px solid grey"
}}
>
Last click
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
x co-ordinate: {pdfDetails?.x}
</div>
<div style={{ fontSize: 14, padding: "5px 12px" }}>
y co-ordinate: {pdfDetails?.y}
</div>
<div
style={{
fontSize: 18,
fontWeight: 500,
padding: "10px 12px",
borderBottom: "1px solid grey",
borderTop: "1px solid grey"
}}
>
Annotation
</div>
<ul
style={{
listStyle: "none",
padding: 10,
height: 500,
overflowY: "auto",
scrollbarWidth: 5
}}
>
{annotations.map((coord, index) => (
<li key={index}>
<span style={{ fontSize: 13, fontWeight: 500 }}>{`Box ${
index + 1
}:`}</span>
<code
style={{
fontSize: 12,
color: "black",
cursor: "pointer"
}}
>
{` ["page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}]`}
</code>
<div>
<span
style={{
borderRadius: 4,
padding: "3px 5px",
border: "1px solid gray",
fontSize: 12,
margin: 2,
cursor: "pointer"
}}
onClick={() =>
copytoclipboard(
`"page":${coord?.page}, "x": ${coord.x}, "y": ${coord.y}, "w": ${coord.width}, "h": ${coord.height}`
)
}
>
<i className="fa-solid fa-copy"></i>
</span>
<span
style={{
borderRadius: 4,
padding: "3px 5px",
border: "1px solid gray",
fontSize: 12,
margin: 2,
cursor: "pointer"
}}
onClick={() => handleDelete(coord.key)}
>
<i className="fa-solid fa-trash-can"></i>
</span>
</div>
</li>
))}
</ul>
</div>
</div>
)}
<ModalUi title={"Select PDF"} isOpen={isModal}>
<form onSubmit={handleSubmit} style={{ margin: "10px" }}>
<input
type="file"
onChange={(e) => handleFileChange(e.target.files)}
style={{
width: "100%",
border: "1px solid grey",
borderRadius: "4px",
padding: "5px",
fontSize: 12
}}
accept=".pdf"
required
/>
<div
style={{ borderTop: "1px solid grey", margin: "10px 0" }}
></div>
<button
style={{
background: "#32a3ac",
borderRadius: "2px",
boxShadow:
"0 2px 4px rgba(0, 0, 0, 0.1), 0 2px 4px rgba(0, 0, 0, 0.18)",
border: "none",
textTransform: "uppercase",
fontSize: "13px",
fontWeight: "600",
padding: "0.375rem 0.75rem",
textAlign: "center",
color: "#ffffff",
outline: "none"
}}
type="submit"
>
Submit
</button>
</form>
</ModalUi>
</>
)}
</div>
);
};
export default DebugUi;
@@ -79,6 +79,7 @@ function PdfRequestFiles() {
const [isExpired, setIsExpired] = useState(false);
const [alreadySign, setAlreadySign] = useState(false);
const [containerWH, setContainerWH] = useState({});
const [sendInOrder, setSendInOrder] = useState(false);
const divRef = useRef(null);
const isMobile = window.innerWidth < 767;
const rowLevel =
@@ -182,6 +183,52 @@ function PdfRequestFiles() {
checkAlreadySign.length > 0
) {
setAlreadySign(true);
} else {
const obj = documentData?.[0];
setSendInOrder(obj?.SendinOrder || false);
if (
obj &&
obj.Signers &&
obj.Signers.length > 0 &&
obj.Placeholders &&
obj.Placeholders.length > 0
) {
const params = {
event: "viewed",
body: {
objectId: documentData?.[0].objectId,
file: documentData?.[0]?.SignedUrl || documentData?.[0]?.URL,
name: documentData?.[0].Name,
note: documentData?.[0].Note || "",
description: documentData?.[0].Description || "",
signers: documentData?.[0].Signers?.map((x) => ({
name: x?.Name,
email: x?.Email,
phone: x?.Phone
})),
viewedBy: jsonSender.email,
viewedAt: new Date(),
createdAt: documentData?.[0].createdAt
}
};
try {
await axios.post(
`${localStorage.getItem("baseUrl")}functions/callwebhook`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id":
localStorage.getItem("parseAppId"),
sessiontoken: localStorage.getItem("accesstoken")
}
}
);
} catch (err) {
console.log("Err ", err);
}
}
}
let signers = [];
@@ -283,175 +330,292 @@ function PdfRequestFiles() {
});
};
const checkSendInOrder = () => {
if (sendInOrder) {
const index = pdfDetails?.[0].Signers.findIndex(
(x) => x.Email === jsonSender.email
);
const newIndex = index - 1;
if (newIndex !== -1) {
const user = pdfDetails?.[0].Signers[newIndex];
const isPrevUserSigned =
pdfDetails?.[0].AuditTrail &&
pdfDetails?.[0].AuditTrail.some(
(x) =>
x.UserPtr.objectId === user.objectId && x.Activity === "Signed"
);
if (isPrevUserSigned) {
return true;
} else {
return false;
}
} else {
return true;
}
} else {
return true;
}
};
//function for embed signature or image url in pdf
async function embedImages() {
const checkUser = signerPos.filter(
(data) => data.signerObjId === signerObjectId
);
if (checkUser && checkUser.length > 0) {
let checkSignUrl = [];
const checkSign = checkUser[0].placeHolder.filter(
(data, ind) => data.pos
const validateSigning = checkSendInOrder();
if (validateSigning) {
const checkUser = signerPos.filter(
(data) => data.signerObjId === signerObjectId
);
for (let i = 0; i < checkSign.length; i++) {
const posData = checkSign[i].pos.filter((pos) => !pos.SignUrl);
if (posData && posData.length > 0) {
checkSignUrl.push(posData);
}
}
if (checkSignUrl && checkSignUrl.length > 0) {
setIsAlert({
isShow: true,
alertMessage: "Please complete your signature!"
});
} else {
setIsUiLoading(true);
const pngUrl = checkUser[0].placeHolder;
// Load a PDFDocument from the existing PDF bytes
const existingPdfBytes = await fetch(pdfUrl).then((res) =>
res.arrayBuffer()
if (checkUser && checkUser.length > 0) {
let checkSignUrl = [];
const checkSign = checkUser[0].placeHolder.filter(
(data, ind) => data.pos
);
const pdfDoc = await PDFDocument.load(existingPdfBytes, {
ignoreEncryption: true
});
// let pdfBase64;
//checking if signature is only one then send image url in jpeg formate to server
// if (pngUrl.length === 1 && pngUrl[0].pos.length === 1) {
// if (isDocId) {
// try {
// pdfBase64 = await getBase64FromUrl(pdfUrl);
// } catch (err) {
// console.log(err);
// }
// } else {
// //embed document's object id to all pages in pdf document
// try {
// await embedDocId(pdfDoc, documentId, allPages);
// } catch (err) {
// console.log(err);
// }
// try {
// pdfBase64 = await pdfDoc.saveAsBase64({
// useObjectStreams: false
// });
// } catch (err) {
// console.log(err);
// }
// }
// for (let pngData of pngUrl) {
// const imgUrlList = pngData.pos;
// const pageNo = pngData.pageNumber;
// imgUrlList.map(async (data) => {
// //cheking signUrl is defau;t signature url of custom url
// let ImgUrl = data.SignUrl;
// const checkUrl = urlValidator(ImgUrl);
// //if default signature url then convert it in base 64
// if (checkUrl) {
// ImgUrl = await getBase64FromIMG(ImgUrl + "?get");
// }
// //function for called convert png signatre to jpeg in base 64
// convertPNGtoJPEG(ImgUrl)
// .then((jpegBase64Data) => {
// const removeBase64Fromjpeg = "data:image/jpeg;base64,";
// const newImgUrl = jpegBase64Data.replace(
// removeBase64Fromjpeg,
// ""
// );
// //function for call to embed signature in pdf and get digital signature pdf
// signPdfFun(
// newImgUrl,
// documentId,
// signerObjectId,
// pdfOriginalWidth,
// pngUrl,
// containerWH,
// setIsAlert,
// data,
// pdfBase64,
// pageNo
// )
// .then((res) => {
// if (res && res.status === "success") {
// setPdfUrl(res.data);
// setIsSigned(true);
// setSignedSigners([]);
// setUnSignedSigners([]);
// getDocumentDetails();
// } else {
// setIsAlert({
// isShow: true,
// alertMessage: "something went wrong"
// });
// }
// })
// .catch((err) => {
// setIsAlert({
// isShow: true,
// alertMessage: "something went wrong"
// });
// });
// })
// .catch((error) => {
// console.error("Error:", error);
// });
// });
// }
// }
// //else if signature is more than one then embed all sign with the use of pdf-lib
// else if (pngUrl.length > 0 && pngUrl[0].pos.length > 0) {
const flag = false;
//embed document's object id to all pages in pdf document
if (!isDocId) {
await embedDocId(pdfDoc, documentId, allPages);
for (let i = 0; i < checkSign.length; i++) {
const posData = checkSign[i].pos.filter((pos) => !pos.SignUrl);
if (posData && posData.length > 0) {
checkSignUrl.push(posData);
}
}
//embed multi signature in pdf
const pdfBytes = await multiSignEmbed(
pngUrl,
pdfDoc,
pdfOriginalWidth,
flag,
containerWH
);
//function for call to embed signature in pdf and get digital signature pdf
try {
const res = await signPdfFun(
pdfBytes,
documentId,
signerObjectId,
pdfOriginalWidth,
pngUrl,
containerWH,
setIsAlert
if (checkSignUrl && checkSignUrl.length > 0) {
setIsAlert({
isShow: true,
alertMessage: "Please complete your signature!"
});
} else {
setIsUiLoading(true);
const pngUrl = checkUser[0].placeHolder;
// Load a PDFDocument from the existing PDF bytes
const existingPdfBytes = await fetch(pdfUrl).then((res) =>
res.arrayBuffer()
);
if (res && res.status === "success") {
setPdfUrl(res.data);
setIsSigned(true);
setSignedSigners([]);
setUnSignedSigners([]);
getDocumentDetails();
} else {
const pdfDoc = await PDFDocument.load(existingPdfBytes, {
ignoreEncryption: true
});
// let pdfBase64;
//checking if signature is only one then send image url in jpeg formate to server
// if (pngUrl.length === 1 && pngUrl[0].pos.length === 1) {
// if (isDocId) {
// try {
// pdfBase64 = await getBase64FromUrl(pdfUrl);
// } catch (err) {
// console.log(err);
// }
// } else {
// //embed document's object id to all pages in pdf document
// try {
// await embedDocId(pdfDoc, documentId, allPages);
// } catch (err) {
// console.log(err);
// }
// try {
// pdfBase64 = await pdfDoc.saveAsBase64({
// useObjectStreams: false
// });
// } catch (err) {
// console.log(err);
// }
// }
// for (let pngData of pngUrl) {
// const imgUrlList = pngData.pos;
// const pageNo = pngData.pageNumber;
// imgUrlList.map(async (data) => {
// //cheking signUrl is defau;t signature url of custom url
// let ImgUrl = data.SignUrl;
// const checkUrl = urlValidator(ImgUrl);
// //if default signature url then convert it in base 64
// if (checkUrl) {
// ImgUrl = await getBase64FromIMG(ImgUrl + "?get");
// }
// //function for called convert png signatre to jpeg in base 64
// convertPNGtoJPEG(ImgUrl)
// .then((jpegBase64Data) => {
// const removeBase64Fromjpeg = "data:image/jpeg;base64,";
// const newImgUrl = jpegBase64Data.replace(
// removeBase64Fromjpeg,
// ""
// );
// //function for call to embed signature in pdf and get digital signature pdf
// signPdfFun(
// newImgUrl,
// documentId,
// signerObjectId,
// pdfOriginalWidth,
// pngUrl,
// containerWH,
// setIsAlert,
// data,
// pdfBase64,
// pageNo
// )
// .then((res) => {
// if (res && res.status === "success") {
// setPdfUrl(res.data);
// setIsSigned(true);
// setSignedSigners([]);
// setUnSignedSigners([]);
// getDocumentDetails();
// } else {
// setIsAlert({
// isShow: true,
// alertMessage: "something went wrong"
// });
// }
// })
// .catch((err) => {
// setIsAlert({
// isShow: true,
// alertMessage: "something went wrong"
// });
// });
// })
// .catch((error) => {
// console.error("Error:", error);
// });
// });
// }
// }
// //else if signature is more than one then embed all sign with the use of pdf-lib
// else if (pngUrl.length > 0 && pngUrl[0].pos.length > 0) {
const flag = false;
//embed document's object id to all pages in pdf document
if (!isDocId) {
await embedDocId(pdfDoc, documentId, allPages);
}
//embed multi signature in pdf
const pdfBytes = await multiSignEmbed(
pngUrl,
pdfDoc,
pdfOriginalWidth,
flag,
containerWH
);
//function for call to embed signature in pdf and get digital signature pdf
try {
const res = await signPdfFun(
pdfBytes,
documentId,
signerObjectId,
pdfOriginalWidth,
pngUrl,
containerWH,
setIsAlert
);
if (res && res.status === "success") {
setPdfUrl(res.data);
setIsSigned(true);
setSignedSigners([]);
setUnSignedSigners([]);
getDocumentDetails();
if (sendInOrder) {
const index = pdfDetails?.[0].Signers.findIndex(
(x) => x.Email === jsonSender.email
);
const newIndex = index + 1;
const user = pdfDetails?.[0].Signers[newIndex];
if (user) {
let sendMail;
// console.log("pdfDetails", pdfDetails);
const expireDate = pdfDetails?.[0].ExpiryDate.iso;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString("en-US", {
day: "numeric",
month: "long",
year: "numeric"
});
let sender = pdfDetails?.[0].ExtUserPtr.Email;
try {
const imgPng =
"https://qikinnovation.ams3.digitaloceanspaces.com/logo.png";
let url = `${localStorage.getItem(
"baseUrl"
)}functions/sendmailv3/`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id":
localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
};
const serverUrl = localStorage.getItem("baseUrl");
const newServer = serverUrl.replaceAll("/", "%2F");
const objectId = user.objectId;
const serverParams = `${newServer}&${localStorage.getItem(
"parseAppId"
)}&${localStorage.getItem("_appName")}`;
const hostUrl =
window.location.origin + "/loadmf/signmicroapp";
let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`;
const openSignUrl =
"https://www.opensignlabs.com/contact-us";
const orgName = pdfDetails[0]?.ExtUserPtr.Company
? pdfDetails[0].ExtUserPtr.Company
: "";
const themeBGcolor = themeColor();
let params = {
recipient: user.Email,
subject: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`,
from: sender,
html:
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
pdfDetails?.[0].ExtUserPtr.Name +
" has requested you to review and sign <strong> " +
pdfDetails?.[0].Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
" directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " +
openSignUrl +
" target=_blank>here</a>.</p> </div></div></body> </html>"
};
sendMail = await axios.post(url, params, {
headers: headers
});
} catch (error) {
console.log("error", error);
}
}
}
} else {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
} catch (err) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
} catch (err) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
}
setIsSignPad(false);
// }
setIsSignPad(false);
// }
} else {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
} else {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
alertMessage:
"Please wait for your turn to sign this document, as it has been set up by the creator to be signed in a specific order; you'll be notified when it's your turn."
});
}
}
@@ -609,7 +773,7 @@ function PdfRequestFiles() {
}
}
)
.then((result) => {
.then(async (result) => {
const res = result.data;
if (res) {
const currentDecline = {
@@ -618,6 +782,40 @@ function PdfRequestFiles() {
};
setIsDecline(currentDecline);
setIsUiLoading(false);
const params = {
event: "declined",
body: {
objectId: pdfDetails?.[0].objectId,
file: pdfDetails?.[0]?.SignedUrl || pdfDetails?.[0]?.URL,
name: pdfDetails?.[0].Name,
note: pdfDetails?.[0].Note || "",
description: pdfDetails?.[0].Description || "",
signers: pdfDetails?.[0].Signers?.map((x) => ({
name: x?.Name,
email: x?.Email,
phone: x?.Phone
})),
declinedBy: jsonSender.email,
declinedAt: new Date(),
createdAt: pdfDetails?.[0].createdAt
}
};
try {
await axios.post(
`${localStorage.getItem("baseUrl")}functions/callwebhook`,
params,
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessiontoken: localStorage.getItem("accesstoken")
}
}
);
} catch (err) {
console.log("Err ", err);
}
}
})
.catch((err) => {
@@ -668,7 +668,8 @@ const TemplatePlaceholder = () => {
Signers: signers,
Name: pdfDetails[0]?.Name || "",
Note: pdfDetails[0]?.Note || "",
Description: pdfDetails[0]?.Description || ""
Description: pdfDetails[0]?.Description || "",
SendinOrder: pdfDetails[0]?.SendinOrder || false
};
await axios
@@ -726,7 +727,7 @@ const TemplatePlaceholder = () => {
selector: '[data-tut="reactourFirst"]',
content: () => (
<TourContentWithBtn
message={`Select a recipient from this list to add a place-holder where he is supposed to sign.The placeholder will appear in the same colour as the recipient name once you drop it on the document.`}
message={`Select a role from this list to add a place-holder where he is supposed to sign.The placeholder will appear in the same colour as the recipient name once you drop it on the document.`}
isChecked={handleDontShow}
/>
),
@@ -749,7 +750,7 @@ const TemplatePlaceholder = () => {
selector: '[data-tut="reactourThird"]',
content: () => (
<TourContentWithBtn
message={`Drag the placeholder for a recipient anywhere on the document.Remember, it will appear in the same colour as the name of the recipient for easy reference.`}
message={`Drag the placeholder for a role anywhere on the document.Remember, it will appear in the same colour as the name of the recipient for easy reference.`}
isChecked={handleDontShow}
/>
),
@@ -1020,11 +1021,11 @@ const TemplatePlaceholder = () => {
<ModalUi
headerColor={"#dc3545"}
isOpen={!IsReceipent}
title={"Receipent required"}
title={"Roles"}
handleClose={() => setIsReceipent(true)}
>
<div style={{ height: "100%", padding: 20 }}>
<p>Please add receipent.</p>
<p>Please add a role</p>
</div>
</ModalUi>
{/* this modal is used show send mail message and after send mail success message */}
@@ -7,7 +7,8 @@ const EditTemplate = ({ template, onSuccess }) => {
const [formData, setFormData] = useState({
Name: template?.Name || "",
Note: template?.Note || "",
Description: template?.Description || ""
Description: template?.Description || "",
SendinOrder: template?.SendinOrder ? `${template?.SendinOrder}` : "false"
});
const handleStrInput = (e) => {
@@ -22,7 +23,8 @@ const EditTemplate = ({ template, onSuccess }) => {
const handleSubmit = async (e) => {
e.preventDefault();
e.stopPropagation();
const data = {...formData }
const isChecked = formData.SendinOrder === "true" ? true : false;
const data = { ...formData, SendinOrder: isChecked };
onSuccess(data);
};
@@ -86,6 +88,45 @@ const EditTemplate = ({ template, onSuccess }) => {
className="addUserInput"
/>
</div>
<div className="form-section">
<label style={{ fontSize: 13 }}>Send In Order</label>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginLeft: 8,
marginBottom: 5
}}
>
<input
type="radio"
value={"true"}
name="SendinOrder"
checked={formData.SendinOrder === "true"}
onChange={handleStrInput}
/>
<div style={{ fontSize: 12 }}>Yes</div>
</div>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginLeft: 8,
marginBottom: 5
}}
>
<input
type="radio"
value={"false"}
name="SendinOrder"
checked={formData.SendinOrder === "false"}
onChange={handleStrInput}
/>
<div style={{ fontSize: 12 }}>No</div>
</div>
</div>
{/* <SelectFolder onSuccess={handleFolder} folderCls={"contracts_Template"} /> */}
<div className="buttoncontainer">
<button type="submit" className="submitbutton">
@@ -0,0 +1,85 @@
import React from "react";
import { Document, Page, pdfjs } from "react-pdf";
import { Stage, Layer, Rect } from "react-konva";
const RenderDebugPdf = (props) => {
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
return (
<div>
<div
style={{
position: "sticky",
top: 0,
padding: 10,
zIndex: 1,
backgroundColor: "white",
border: "1px solid grey",
margin: "5px 0"
}}
>
{`Co-ordinates: X - ${props.hoverCoordinates.x}, Y - ${props.hoverCoordinates.y}`}
</div>
<div
style={{
flex: 1,
position: "relative",
border: "1px solid grey",
overflow: "auto"
}}
onMouseMove={props.handleMouseMoveDiv}
>
<Document
onLoadError={(e) => {
const load = {
status: false,
type: "failed"
};
props.setPdfLoadFail(load);
}}
loading={"Loading Document.."}
onLoadSuccess={props.pageDetails}
ref={props.pdfRef}
file={props.pdfUrl}
>
<Page
onLoadSuccess={props.handlePageLoadSuccess}
pageNumber={props.pageNumber}
renderAnnotationLayer={false}
renderTextLayer={false}
onGetAnnotationsError={(error) => {
console.log("annotation error", error);
}}
/>
</Document>
<Stage
onMouseMove={props.handleMouseMove}
onMouseDown={props.handleMouseDown}
onMouseUp={props.handleMouseUp}
width={props.pdfDimension.width}
height={props.pdfDimension.height}
style={{ position: "absolute", top: 0, left: 0 }}
>
<Layer>
{props.annotations
.filter((value) => value.page === props.pageNumber)
.map((value) => {
return (
<Rect
x={value.x}
y={value.y}
width={value.width}
height={value.height}
fill="transparent"
stroke="black"
/>
);
})}
</Layer>
</Stage>
</div>
</div>
);
};
export default RenderDebugPdf;
@@ -370,7 +370,7 @@ function FieldsComponent({
)}
{isSignersModal && (
<ModalUi
title={"Recipients"}
title={"Roles"}
isOpen={isSignersModal}
handleClose={handleModal}
>
@@ -398,7 +398,7 @@ function FieldsComponent({
textAlign: "center"
}}
>
Please add Recipient
Please add a role
</div>
)}
</ModalUi>
@@ -661,8 +661,11 @@ function PlaceHolderSign() {
year: "numeric"
});
let sender = pdfDetails?.[0].ExtUserPtr.Email;
const signerMail = signersdata;
let signerMail = signersdata.slice();
if (pdfDetails?.[0]?.SendinOrder && pdfDetails?.[0]?.SendinOrder === true) {
signerMail.splice(1);
}
for (let i = 0; i < signerMail.length; i++) {
try {
const imgPng =
@@ -691,7 +694,6 @@ function PlaceHolderSign() {
recipient: signerMail[i].Email,
subject: `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`,
from: sender,
html:
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
@@ -10,6 +10,7 @@ import LegaDrive from "./Component/LegaDrive/LegaDrive";
import PageNotFound from "./Component/PageNotFound";
import TemplatePlaceHolder from "./Component/TemplatePlaceholder";
import Parse from "parse";
import DebugUi from "./Component/DebugUi";
Parse.serverURL = localStorage.getItem("baseUrl");
Parse.initialize(localStorage.getItem("parseAppId"));
// `AppRoutes` is used to define route path of app and
@@ -50,6 +51,7 @@ function AppRoutes() {
<Route path="/legadrive" element={<LegaDrive />} />
{/* Page Not Found */}
<Route path="/template/:templateId" element={<TemplatePlaceHolder />} />
<Route path="/debugpdf" element={<DebugUi />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
</div>
@@ -0,0 +1,25 @@
import { useEffect, useState } from "react";
export function useWindowSize() {
const [size, setSize] = useState({
width: 0,
height: 0
});
const onResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
useEffect(() => {
onResize();
window.addEventListener("resize", onResize);
return () => {
window.removeEventListener("resize", onResize);
};
}, []);
return size;
}
@@ -5,20 +5,47 @@ const Alert = ({ children, type }) => {
function theme(color) {
switch (color) {
case "success":
return "border-[#c3e6cb] bg-[#d4edda] text-[#155724]";
return {
border: `1px solid #c3e6cb`, // Border color
backgroundColor: "#d4edda", // Background color
color: "#155724" // Text color
};
case "info":
return "border-[#adcdeb] bg-[#c1daf0] text-[#153756]";
return {
border: `1px solid #adcdeb`, // Border color
backgroundColor: "#c1daf0", // Background color
color: "#153756" // Text color
};
case "danger":
return "border-[#f0a8a8] bg-[#f4bebe] text-[#c42121]";
return {
border: `1px solid #f0a8a8`, // Border color
backgroundColor: "#f4bebe", // Background color
color: "#c42121" // Text color
};
default:
return "border-[#d6d6d6] bg-[#d9d9d9] text-[#575757]";
return {
border: `1px solid #d6d6d6`, // Border color
backgroundColor: "#d9d9d9", // Background color
color: "#575757" // Text color
};
}
}
return (
<>
{children && (
<div
className={`z-40 fixed top-20 left-1/2 transform -translate-x-1/2 border-[1px] text-sm ${textcolor} rounded py-[.75rem] px-[1.25rem] `}
style={{
...textcolor,
position: "fixed",
top: "20px",
left: "50%",
transform: "translateX(-50%)",
fontSize: "0.875rem", // Corresponds to text-sm in Tailwind CSS
borderRadius: "5px", // Rounded corners
padding: "10px 15px", // Padding
zIndex: 40
}}
>
{children}
</div>
File diff suppressed because it is too large Load Diff
@@ -1002,7 +1002,8 @@ export const createDocument = async (template, placeholders, signerData) => {
className: "_User",
objectId: Doc.CreatedBy.objectId
},
Signers: signers
Signers: signers,
SendinOrder: Doc?.SendinOrder || false
};
try {