From 3eabec59d3d3830367fcc32b0c011d6916d3a5ab Mon Sep 17 00:00:00 2001
From: NanBohdanMoroz <92790530+NanSigmaBohdanMoroz@users.noreply.github.com>
Date: Tue, 7 Jan 2025 18:18:03 +0200
Subject: [PATCH 1/6] add pcke auth
---
package-lock.json | 14 +++++---
package.json | 1 +
src/api/apiFactory.js | 60 +++++++++++++++++++++++++++----
src/hooks/useImplicitGrantAuth.js | 23 +++++++-----
4 files changed, 80 insertions(+), 18 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 56c61a6..8849693 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,6 +20,7 @@
"html2canvas": "^1.4.1",
"i18next": "^23.7.18",
"i18next-http-backend": "^2.4.2",
+ "js-sha256": "^0.11.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-bootstrap": "^2.10.0",
@@ -12224,6 +12225,11 @@
"jiti": "bin/jiti.js"
}
},
+ "node_modules/js-sha256": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.0.tgz",
+ "integrity": "sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q=="
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -17595,16 +17601,16 @@
}
},
"node_modules/typescript": {
- "version": "5.4.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
- "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
+ "version": "4.9.5",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
+ "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
- "node": ">=14.17"
+ "node": ">=4.2.0"
}
},
"node_modules/unbox-primitive": {
diff --git a/package.json b/package.json
index 7eca6b0..cc24828 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"html2canvas": "^1.4.1",
"i18next": "^23.7.18",
"i18next-http-backend": "^2.4.2",
+ "js-sha256": "^0.11.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-bootstrap": "^2.10.0",
diff --git a/src/api/apiFactory.js b/src/api/apiFactory.js
index f79483b..edbe2d1 100644
--- a/src/api/apiFactory.js
+++ b/src/api/apiFactory.js
@@ -1,6 +1,8 @@
/* eslint-disable no-param-reassign */
+import { sha256 } from "js-sha256";
import { getAuthToken } from "../services/accountRepository";
+
const configureInterceptors = (api) => {
// Request interceptor for API calls
api.interceptors.request.use(
@@ -10,7 +12,10 @@ const configureInterceptors = (api) => {
"Content-Type": "application/json",
};
const accessTokenInfo = getAuthToken();
- config.headers.Authorization = `Bearer ${accessTokenInfo.accessToken}`;
+
+ if (accessTokenInfo?.accessToken) {
+ config.headers.Authorization = `Bearer ${accessTokenInfo.accessToken}`;
+ }
return config;
},
(error) => {
@@ -280,10 +285,28 @@ export const createEmbeddedSigningAPI = (
};
};
+export const generateCodeVerifier = () => {
+ const randomValues = new Uint8Array(32);
+ window.crypto.getRandomValues(randomValues);
+ return btoa(String.fromCharCode.apply(null, randomValues))
+ .replace(/\+/g, "-")
+ .replace(/\//g, "_")
+ .replace(/=+$/, "");
+};
+
+export const generateCodeChallenge = (codeVerifier) => {
+ const hash = sha256.arrayBuffer(codeVerifier);
+ const hashArray = Array.from(new Uint8Array(hash));
+ return btoa(String.fromCharCode.apply(null, hashArray))
+ .replace(/\+/g, "-")
+ .replace(/\//g, "_")
+ .replace(/=+$/, "");
+};
+
export const createAuthAPI = (
axios,
serviceProvider,
- implicitGrantPath,
+ authPath,
userInfoPath,
eSignBase,
scopes,
@@ -292,15 +315,21 @@ export const createAuthAPI = (
) => {
const api = createAPI(axios);
+ const codeVerifier = generateCodeVerifier();
+ const codeChallenge = generateCodeChallenge(codeVerifier);
+
const login = async (nonce, onPopupIsBlocked) => {
const url =
- `${serviceProvider}${implicitGrantPath}` +
- `?response_type=token` +
+ `${serviceProvider}${authPath}` +
+ `?response_type=code` +
`&scope=${scopes}` +
`&client_id=${clientId}` +
`&redirect_uri=${returnUrl}` +
- `&state=${nonce}`;
+ `&state=${nonce}` +
+ `&code_challenge_method=S256` +
+ `&code_challenge=${codeChallenge}`;
const loginWindow = window.open(url, "_blank");
+
const newTab = loginWindow;
if (!newTab || newTab.closed || typeof newTab.closed === "undefined") {
// POPUP BLOCKED
@@ -311,6 +340,25 @@ export const createAuthAPI = (
return { window: loginWindow, nonce };
};
+ const obtainAccessToken = async (code) => {
+ const requestData = {
+ code: `${code}`,
+ code_verifier: `${codeVerifier}`,
+ grant_type: "authorization_code",
+ client_id: `${clientId}`,
+ code_challenge_method: "S256",
+ redirect_uri: `${returnUrl}`
+
+ };
+
+ const response = await api.post(
+ `${serviceProvider}/oauth/token`,
+ requestData
+ );
+
+ return response.data;
+ }
+
const fetchUserInfo = async () => {
const response = await api.get(`${serviceProvider}${userInfoPath}`);
return response.data;
@@ -323,5 +371,5 @@ export const createAuthAPI = (
return response.data.externalAccountId;
};
- return { login, fetchUserInfo, fetchExternalAccountId };
+ return { login, fetchUserInfo, fetchExternalAccountId, obtainAccessToken };
};
diff --git a/src/hooks/useImplicitGrantAuth.js b/src/hooks/useImplicitGrantAuth.js
index bbde545..32db925 100644
--- a/src/hooks/useImplicitGrantAuth.js
+++ b/src/hooks/useImplicitGrantAuth.js
@@ -66,24 +66,30 @@ export const useImplicitGrantAuth = (onError, onSuccess) => {
userEmail: userInfo.email,
});
- const parseHash = (hash) => {
- const items = hash.split(/=|&/);
+ const parseSearch = (search) => {
+ const queryString = search.startsWith('?') ? search.slice(1) : search;
+ const items = queryString.split(/=|&/);
+
let i = 0;
const response = {};
+
while (i + 1 < items.length) {
- response[items[i]] = items[i + 1];
+ response[decodeURIComponent(items[i])] = decodeURIComponent(items[i + 1]);
i += 2;
}
+
return response;
};
+
- const handleMessage = async (data) => {
- // close the browser tab used for OAuth
+ const handleMessage = async () => {
if (loginResult.current.window) {
loginResult.current.window.close();
}
- const hash = data.hash.substring(1); // remove the #
- const response = parseHash(hash);
+
+ const response = parseSearch(loginResult.current.window.location.search);
+
+ const obtainAccessTokenResponse = await authenticationAPI.obtainAccessToken(response.code);
const newState = response.state;
if (newState !== loginResult.current.nonce) {
@@ -93,8 +99,9 @@ export const useImplicitGrantAuth = (onError, onSuccess) => {
});
return;
}
+
const accessTokenInfo = {
- accessToken: response.access_token,
+ accessToken: obtainAccessTokenResponse.access_token,
accessTokenExpires: new Date(Date.now() + response.expires_in * 1000),
};
accountRepository.setAuthToken(accessTokenInfo);
From d16efe9e3f1fcd813d926a63e0025ec5a1a8747b Mon Sep 17 00:00:00 2001
From: NanBohdanMoroz <92790530+NanSigmaBohdanMoroz@users.noreply.github.com>
Date: Tue, 7 Jan 2025 18:30:49 +0200
Subject: [PATCH 2/6] fix naming
---
src/components/header.js | 2 +-
src/hooks/{useImplicitGrantAuth.js => usePckeAuth.js} | 0
src/pages/home/index.js | 2 +-
3 files changed, 2 insertions(+), 2 deletions(-)
rename src/hooks/{useImplicitGrantAuth.js => usePckeAuth.js} (100%)
diff --git a/src/components/header.js b/src/components/header.js
index d275cd6..c978ef8 100644
--- a/src/components/header.js
+++ b/src/components/header.js
@@ -5,7 +5,7 @@ import { Navbar } from "react-bootstrap";
import PropTypes from "prop-types";
import logo from "../assets/img/logo.svg";
import * as accountRepository from "../services/accountRepository";
-import { logout } from "../hooks/useImplicitGrantAuth";
+import { logout } from "../hooks/usePckeAuth";
import useBreakpoint, { SIZE_MD, SIZE_SM } from "../hooks/useBreakpoint";
import whiteToggleIcon from "../assets/img/white-links.png";
import blackToggleIcon from "../assets/img/black-links.png";
diff --git a/src/hooks/useImplicitGrantAuth.js b/src/hooks/usePckeAuth.js
similarity index 100%
rename from src/hooks/useImplicitGrantAuth.js
rename to src/hooks/usePckeAuth.js
diff --git a/src/pages/home/index.js b/src/pages/home/index.js
index f88b01e..7d1c60f 100644
--- a/src/pages/home/index.js
+++ b/src/pages/home/index.js
@@ -6,7 +6,7 @@ import parse from "html-react-parser";
import {
useImplicitGrantAuth,
needToLogin
-} from "../../hooks/useImplicitGrantAuth";
+} from "../../hooks/usePckeAuth";
import { Card, Layout, WaitingModal, MessageModal } from "../../components";
import { CTASection, TitleSection, ResoursesSection } from "./components";
From 5f77d862871ce8c89d4d739694eeb8929cd3e236 Mon Sep 17 00:00:00 2001
From: NanBohdanMoroz <92790530+NanSigmaBohdanMoroz@users.noreply.github.com>
Date: Thu, 9 Jan 2025 23:35:29 +0200
Subject: [PATCH 3/6] add focused view
---
public/index.html | 1 +
src/api/apiFactory.js | 36 +++--
.../components/focusedView.js | 153 ++++++++++++++++++
.../knowYourCustomer/components/index.js | 1 +
src/pages/knowYourCustomer/index.js | 48 ++----
...dSigningTemplate.js => signingTemplate.js} | 2 +-
.../knowYourCustomer/useEmbeddedSigning.js | 4 +-
src/pages/sendInsuranceCard/insuranceSlice.js | 22 ++-
.../sendInsuranceCard/useGenerateDocument.js | 22 ++-
src/services/fileService.js | 2 +-
10 files changed, 223 insertions(+), 68 deletions(-)
create mode 100644 src/pages/knowYourCustomer/components/focusedView.js
rename src/pages/knowYourCustomer/{embeddedSigningTemplate.js => signingTemplate.js} (98%)
diff --git a/public/index.html b/public/index.html
index ebdc304..a0a90ff 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,6 +1,7 @@
+
diff --git a/src/api/apiFactory.js b/src/api/apiFactory.js
index edbe2d1..a05c5f7 100644
--- a/src/api/apiFactory.js
+++ b/src/api/apiFactory.js
@@ -121,18 +121,7 @@ export const createDocumentAPI = (
return response.status;
};
- const createEnvelop = async (templateId, signerEmail, signerName) => {
- const requestData = {
- templateId,
- templateRoles: [
- {
- email: signerEmail,
- name: signerName,
- roleName: "signer",
- },
- ],
- status: "created",
- };
+ const createEnvelope = async (requestData) => {
const response = await api.post(
`${accountBaseUrl}${eSignBase}/accounts/${accountId}/envelopes`,
requestData
@@ -194,10 +183,10 @@ export const createDocumentAPI = (
createTemplate,
addDocumentToTemplate,
addTabsToTemplate,
- createEnvelop,
+ createEnvelope,
getDocumentId,
updateFormFields,
- sendEnvelop,
+ sendEnvelop
};
};
@@ -265,6 +254,23 @@ export const createEmbeddedSigningAPI = (
return response.data.url;
};
+ const embeddedSigningCeremony1 = async (envelopeId, requestData) => {
+ // const requestData = {
+ // returnUrl: dsReturnUrl,
+ // authenticationMethod: "None",
+ // clientUserId: 1000,
+ // email: signer.email,
+ // userName: signer.name,
+ // };
+
+ const response = await api.post(
+ `${accountBaseUrl}${eSignBase}/accounts/${accountId}/envelopes/${envelopeId}/views/recipient`,
+ requestData
+ );
+
+ return response.data.url;
+ };
+
const embeddedSigning = async (signer, template, onPopupIsBlocked) => {
const envelopeId = await createEnvelope(template, signer);
const url = await embeddedSigningCeremony(envelopeId, signer);
@@ -282,6 +288,8 @@ export const createEmbeddedSigningAPI = (
return {
embeddedSigning,
+ embeddedSigningCeremony,
+ embeddedSigningCeremony1
};
};
diff --git a/src/pages/knowYourCustomer/components/focusedView.js b/src/pages/knowYourCustomer/components/focusedView.js
new file mode 100644
index 0000000..a987578
--- /dev/null
+++ b/src/pages/knowYourCustomer/components/focusedView.js
@@ -0,0 +1,153 @@
+import React, { useEffect } from "react";
+import { useTranslation } from "react-i18next";
+import { initDocumentAPI, initEmbeddedSigningAPI } from "../../../api";
+import * as accountRepository from "../../../services/accountRepository";
+import { createSigningTemplate } from "../signingTemplate";
+
+const createEnvelope = async (envelopeDefinition) => {
+ const accountInfo = accountRepository.getAccountInfo();
+ const api = initDocumentAPI(accountInfo.accountBaseUrl, accountInfo.accountId);
+ const envelopeId = await api.createEnvelope(envelopeDefinition);
+ return envelopeId;
+};
+
+const createRecipientView = async (envelopeId, requestData) => {
+ const accountInfo = accountRepository.getAccountInfo();
+ const api = initEmbeddedSigningAPI(accountInfo.accountBaseUrl, accountInfo.accountId);
+ const recipientView = await api.embeddedSigningCeremony1(envelopeId, requestData);
+ return recipientView;
+};
+
+const createEnvelopDefinition = async (signerEmail, signerName, signerClientId, userPhoto, t) => {
+
+ const template = createSigningTemplate({name: signerName, email: signerEmail, photo: userPhoto}, t);
+ const document = {
+ name: process.env.REACT_APP_EMBEDDED_DOCUMENT_NAME,
+ documentId: 1,
+ htmlDefinition: {
+ source: template,
+ }
+ }
+
+ const signHere1 = {
+ anchorString: "/sn1/",
+ anchorYOffset: "10",
+ anchorUnits: "pixels",
+ anchorXOffset: "20",
+ };
+
+ const signer1 = {
+ email: signerEmail,
+ name: signerName,
+ clientUserId: signerClientId,
+ recipientId: 1,
+ tabs: {
+ signHereTabs: [signHere1],
+ },
+ };
+
+ return {
+ emailSubject: "Please sign this document",
+ documents: [document],
+ recipients: { signers: [signer1] },
+ status: "sent",
+ };
+};
+
+const createRecipientViewDefinition = (dsReturnUrl, signerEmail, signerName, signerClientId, dsPingUrl) => ({
+ returnUrl: `${dsReturnUrl}/know-your-customer?state=123`,
+ authenticationMethod: "none",
+ email: signerEmail,
+ userName: signerName,
+ clientUserId: signerClientId,
+ pingFrequency: 600,
+ pingUrl: dsPingUrl,
+ frameAncestors: [ window.location.origin, 'https://apps-d.docusign.com', 'https://demo.docusign.net'],
+ messageOrigins: ['https://apps-d.docusign.com'],
+});
+
+
+// Main Component
+export const FocusedView = (args) => {
+
+ const { t } = useTranslation("EmbeddedSigningTemplate");
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const { photo } = args;
+
+ const accountInfo = accountRepository.getAccountInfo();
+
+ // Create the envelope definition
+ const envelope = await createEnvelopDefinition(accountInfo.userEmail, accountInfo.userName, 1000, photo, t);
+
+ // Create the envelope
+ const originEnvelopeId = await createEnvelope(envelope);
+
+ // Create the recipient view definition
+ const recipientViewDefinition = createRecipientViewDefinition(
+ window.location.origin,
+ accountInfo.userEmail,
+ accountInfo.userName,
+ 1000,
+ window.location.origin
+ );
+
+ // Create the recipient view
+ const recipientViewUrl = await createRecipientView(originEnvelopeId, recipientViewDefinition);
+
+ const wdf = process.env.REACT_APP_OAUTH_CLIENT_ID;
+ console.log(wdf);
+ // Initialize DocuSign SDK and mount the signing view
+ const docusign = await window.DocuSign.loadDocuSign(process.env.REACT_APP_OAUTH_CLIENT_ID);
+ const signing = docusign.signing({
+ url: recipientViewUrl,
+ displayFormat: "focused",
+ style: {
+ branding: {
+ primaryButton: {
+ backgroundColor: "#333",
+ color: "#fff",
+ },
+ },
+ signingNavigationButton: {
+ finishText: "You have finished the document! Hooray!",
+ position: "bottom-center",
+ },
+ },
+ });
+ console.log(navigator.geolocation);
+ signing.on("ready", () => {
+ console.log("UI is rendered");
+ });
+
+ signing.on("sessionEnd", (event) => {
+ window.location.reload();
+ console.log("Session ended:", event);
+ });
+
+ signing.mount("#agreement");
+ } catch (error) {
+ console.error("Error fetching recipient view:", error);
+ }
+ };
+
+ fetchData();
+ }, []);
+
+ return (
+
+ );
+};
diff --git a/src/pages/knowYourCustomer/components/index.js b/src/pages/knowYourCustomer/components/index.js
index b08628e..84e0287 100644
--- a/src/pages/knowYourCustomer/components/index.js
+++ b/src/pages/knowYourCustomer/components/index.js
@@ -1,3 +1,4 @@
export * from "./stepDescription";
export * from "./customerInformation";
export * from "./photoModal";
+export * from "./focusedView";
diff --git a/src/pages/knowYourCustomer/index.js b/src/pages/knowYourCustomer/index.js
index 4f3c78e..f6a3e94 100644
--- a/src/pages/knowYourCustomer/index.js
+++ b/src/pages/knowYourCustomer/index.js
@@ -4,14 +4,10 @@ import { useTranslation } from "react-i18next";
import {
SeeMore,
Layout,
- WaitingModal,
ErrorModal,
MessageModal,
} from "../../components";
-import { CustomerInformation, PhotoModal, StepDescription } from "./components";
-import { useEmbeddedSigning } from "./useEmbeddedSigning";
-import * as accountRepository from "../../services/accountRepository";
-import { fileToBase64 } from "../../services/fileService";
+import { CustomerInformation, FocusedView, PhotoModal, StepDescription } from "./components";
import success from "../../assets/img/success.svg";
import warning from "../../assets/img/warning.svg";
import templatePhoto from "../../assets/img/templatePhoto.png";
@@ -25,39 +21,15 @@ const KnowYourCustomer = () => {
const [photo, setPhoto] = useState("");
const [simpleFlow, setSimpleFlow] = useState(false);
const [lastStep, setLastStep] = useState(false);
- const [error, setError] = useState({ title: "", description: "" });
+ const [showFocusedView, setShowFocusedView] = useState(false);
+ const [error] = useState({ title: "", description: "" });
const [showModal, setShowModal] = useState(false);
- const [showWaitingModal, setWaitingModal] = useState(false);
const [showErrorModal, setShowErrorModal] = useState(false);
const [showSuccessModal, setShowSuccessModal] = useState(false);
const [showWarningModal, setShowWarningModal] = useState(false);
- const [embeddedSigning, inProgress] = useEmbeddedSigning(
- accountRepository,
- (err) => {
- setError(err);
- setShowErrorModal(true);
- },
- () => {
- setLastStep(false);
- setPhoto("");
- setShowSuccessModal(true);
- }
- );
-
- useEffect(() => {
- if (inProgress) {
- setWaitingModal(true);
- } else {
- setWaitingModal(false);
- }
- }, [inProgress]);
-
const handleSigning = async () => {
- const signerPhoto =
- photo ||
- `data:image/png;base64, ${await fileToBase64("Photo.png", defaultPhoto)}`;
- await embeddedSigning(signerPhoto);
+ setShowFocusedView(true);
};
useEffect(() => {
@@ -96,7 +68,14 @@ const KnowYourCustomer = () => {
+ {showFocusedView ? (
+
+
+ ) :
+ (
{lastStep ? (
{
/>
)}
+ )}
{
setLastStep(true);
}}
/>
- showWaitingModal(false)}
title={t("WaitingModal.Title")}
description={t("WaitingModal.Description")}
- />
+ /> */}
setShowErrorModal(false)}
diff --git a/src/pages/knowYourCustomer/embeddedSigningTemplate.js b/src/pages/knowYourCustomer/signingTemplate.js
similarity index 98%
rename from src/pages/knowYourCustomer/embeddedSigningTemplate.js
rename to src/pages/knowYourCustomer/signingTemplate.js
index c8b18b2..6fb3521 100644
--- a/src/pages/knowYourCustomer/embeddedSigningTemplate.js
+++ b/src/pages/knowYourCustomer/signingTemplate.js
@@ -52,7 +52,7 @@ const styles = {
},
};
-export const createEmbeddedSigningTemplate = (signer, t) =>
+export const createSigningTemplate = (signer, t) =>
renderToString(
diff --git a/src/pages/knowYourCustomer/useEmbeddedSigning.js b/src/pages/knowYourCustomer/useEmbeddedSigning.js
index 8917b7a..9f881a4 100644
--- a/src/pages/knowYourCustomer/useEmbeddedSigning.js
+++ b/src/pages/knowYourCustomer/useEmbeddedSigning.js
@@ -1,7 +1,7 @@
import { useEffect, useState, useRef } from "react";
import { useTranslation } from "react-i18next";
import { initEmbeddedSigningAPI } from "../../api";
-import { createEmbeddedSigningTemplate } from "./embeddedSigningTemplate";
+import { createSigningTemplate } from "./signingTemplate";
export const useEmbeddedSigning = (AccountService, onError, onSuccess) => {
const { t } = useTranslation("EmbeddedSigningTemplate");
@@ -64,7 +64,7 @@ export const useEmbeddedSigning = (AccountService, onError, onSuccess) => {
name: accountInfo.userName,
photo,
};
- const template = createEmbeddedSigningTemplate(signer, t);
+ const template = createSigningTemplate(signer, t);
try {
signingResult.current = await api.embeddedSigning(signer, template, () =>
diff --git a/src/pages/sendInsuranceCard/insuranceSlice.js b/src/pages/sendInsuranceCard/insuranceSlice.js
index 8643324..1141cef 100644
--- a/src/pages/sendInsuranceCard/insuranceSlice.js
+++ b/src/pages/sendInsuranceCard/insuranceSlice.js
@@ -22,21 +22,27 @@ export const sendInsurance = createAsyncThunk(
process.env.REACT_APP_INSURANCE_DOCUMENT_TEMPLATE_NAME,
process.env.REACT_APP_INSURANCE_DOCUMENT_TEMPLATE_EMAIL_SUBJECT
);
- const documentBase64 = await fileToBase64(
- "documentTemplate.docx",
- documentTemplate
- );
+ const documentBase64 = await fileToBase64(documentTemplate);
await api.addDocumentToTemplate(
templateId,
documentBase64,
process.env.REACT_APP_INSURANCE_DOCUMENT_NAME
);
await api.addTabsToTemplate(templateId);
- const envelopId = await api.createEnvelop(
+
+ const requestData = {
templateId,
- signerEmail,
- signerName
- );
+ templateRoles: [
+ {
+ email: signerEmail,
+ name: signerName,
+ roleName: "signer",
+ },
+ ],
+ status: "created",
+ };
+
+ const envelopId = await api.createEnvelop(requestData);
const documentId = await api.getDocumentId(envelopId);
await api.updateFormFields(documentId, envelopId, insuranceID, insured);
await api.sendEnvelop(envelopId);
diff --git a/src/pages/sendInsuranceCard/useGenerateDocument.js b/src/pages/sendInsuranceCard/useGenerateDocument.js
index fcfb0c3..d32bcb5 100644
--- a/src/pages/sendInsuranceCard/useGenerateDocument.js
+++ b/src/pages/sendInsuranceCard/useGenerateDocument.js
@@ -49,21 +49,27 @@ export const useGenerateDocument = () => {
process.env.REACT_APP_INSURANCE_DOCUMENT_TEMPLATE_NAME,
process.env.REACT_APP_INSURANCE_DOCUMENT_TEMPLATE_EMAIL_SUBJECT
);
- const documentBase64 = await fileToBase64(
- "documentTemplate.docx",
- documentTemplate
- );
+ const documentBase64 = await fileToBase64(documentTemplate);
await api.addDocumentToTemplate(
templateId,
documentBase64,
process.env.REACT_APP_INSURANCE_DOCUMENT_NAME
);
await api.addTabsToTemplate(templateId);
- const envelopId = await api.createEnvelop(
+
+ const requestData = {
templateId,
- signerEmail,
- signerName
- );
+ templateRoles: [
+ {
+ email: signerEmail,
+ name: signerName,
+ roleName: "signer",
+ },
+ ],
+ status: "created",
+ };
+
+ const envelopId = await api.createEnvelop(requestData);
const documentId = await api.getDocumentId(envelopId);
await api.updateFormFields(documentId, envelopId, insuranceID, insured);
await api.sendEnvelop(envelopId);
diff --git a/src/services/fileService.js b/src/services/fileService.js
index 0f3f2b6..79f3e90 100644
--- a/src/services/fileService.js
+++ b/src/services/fileService.js
@@ -1,5 +1,5 @@
// Convert file to base64 string
-export const fileToBase64 = async (filename, filepath) => {
+export const fileToBase64 = async (filepath) => {
const response = await fetch(filepath);
const fileBlob = await response.blob();
const reader = new FileReader();
From 9cc75363837cc5a94113c0e57eb1195d83a14b71 Mon Sep 17 00:00:00 2001
From: NanBohdanMoroz <92790530+NanSigmaBohdanMoroz@users.noreply.github.com>
Date: Fri, 10 Jan 2025 10:51:19 +0200
Subject: [PATCH 4/6] improve style cop
---
src/api/apiFactory.js | 88 ++-----------------
src/api/index.js | 8 +-
.../components/focusedView.js | 21 ++---
.../knowYourCustomer/useEmbeddedSigning.js | 86 ------------------
4 files changed, 15 insertions(+), 188 deletions(-)
delete mode 100644 src/pages/knowYourCustomer/useEmbeddedSigning.js
diff --git a/src/api/apiFactory.js b/src/api/apiFactory.js
index a05c5f7..f033fd6 100644
--- a/src/api/apiFactory.js
+++ b/src/api/apiFactory.js
@@ -121,14 +121,6 @@ export const createDocumentAPI = (
return response.status;
};
- const createEnvelope = async (requestData) => {
- const response = await api.post(
- `${accountBaseUrl}${eSignBase}/accounts/${accountId}/envelopes`,
- requestData
- );
- return response.data.envelopeId;
- };
-
const getDocumentId = async (envelopeId) => {
const response = await api.get(
`${accountBaseUrl}${eSignBase}/accounts/${accountId}/envelopes/${envelopeId}/docGenFormFields`
@@ -190,7 +182,7 @@ export const createDocumentAPI = (
};
};
-export const createEmbeddedSigningAPI = (
+export const createFocusedViewAPI = (
axios,
eSignBase,
dsReturnUrl,
@@ -199,37 +191,7 @@ export const createEmbeddedSigningAPI = (
) => {
const api = createAPI(axios);
- const createEnvelope = async (htmlDoc, signer) => {
- const requestData = {
- emailSubject:
- process.env.REACT_APP_EMBEDDED_DOCUMENT_TEMPLATE_EMAIL_SUBJECT,
- description: process.env.REACT_APP_EMBEDDED_DOCUMENT_TEMPLATE_DESCRIPTION,
- name: process.env.REACT_APP_EMBEDDED_DOCUMENT_TEMPLATE_NAME,
- shared: false,
- status: "sent",
- recipients: {
- signers: [
- {
- email: signer.email,
- name: signer.name,
- recipientId: "1",
- clientUserId: 1000,
- roleName: "signer",
- routingOrder: "1",
- },
- ],
- },
- documents: [
- {
- name: process.env.REACT_APP_EMBEDDED_DOCUMENT_NAME,
- documentId: 1,
- htmlDefinition: {
- source: htmlDoc,
- },
- },
- ],
- };
-
+ const createEnvelope = async (requestData) => {
const response = await api.post(
`${accountBaseUrl}${eSignBase}/accounts/${accountId}/envelopes`,
requestData
@@ -237,15 +199,8 @@ export const createEmbeddedSigningAPI = (
return response.data.envelopeId;
};
- const embeddedSigningCeremony = async (envelopeId, signer) => {
- const requestData = {
- returnUrl: dsReturnUrl,
- authenticationMethod: "None",
- clientUserId: 1000,
- email: signer.email,
- userName: signer.name,
- };
+ const getRecipientView = async (envelopeId, requestData) => {
const response = await api.post(
`${accountBaseUrl}${eSignBase}/accounts/${accountId}/envelopes/${envelopeId}/views/recipient`,
requestData
@@ -254,42 +209,9 @@ export const createEmbeddedSigningAPI = (
return response.data.url;
};
- const embeddedSigningCeremony1 = async (envelopeId, requestData) => {
- // const requestData = {
- // returnUrl: dsReturnUrl,
- // authenticationMethod: "None",
- // clientUserId: 1000,
- // email: signer.email,
- // userName: signer.name,
- // };
-
- const response = await api.post(
- `${accountBaseUrl}${eSignBase}/accounts/${accountId}/envelopes/${envelopeId}/views/recipient`,
- requestData
- );
-
- return response.data.url;
- };
-
- const embeddedSigning = async (signer, template, onPopupIsBlocked) => {
- const envelopeId = await createEnvelope(template, signer);
- const url = await embeddedSigningCeremony(envelopeId, signer);
-
- const signingWindow = window.open(url, "_blank");
- const newTab = signingWindow;
- if (!newTab || newTab.closed || typeof newTab.closed === "undefined") {
- // POPUP BLOCKED
- onPopupIsBlocked();
- return false;
- }
- signingWindow.focus();
- return signingWindow;
- };
-
return {
- embeddedSigning,
- embeddedSigningCeremony,
- embeddedSigningCeremony1
+ getRecipientView,
+ createEnvelope
};
};
diff --git a/src/api/index.js b/src/api/index.js
index bddde56..6ace734 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -2,7 +2,7 @@ import axios from "axios";
import {
createAuthAPI,
createDocumentAPI,
- createEmbeddedSigningAPI,
+ createFocusedViewAPI,
} from "./apiFactory";
const authenticationAPI = createAuthAPI(
@@ -24,8 +24,8 @@ const initDocumentAPI = (baseAccountUrl, accountId) =>
accountId
);
-const initEmbeddedSigningAPI = (baseAccountUrl, accountId) =>
- createEmbeddedSigningAPI(
+const initFocusedViewAPI = (baseAccountUrl, accountId) =>
+ createFocusedViewAPI(
axios,
process.env.REACT_APP_ESIGN_BASE,
process.env.REACT_APP_DS_RETURN_URL,
@@ -33,4 +33,4 @@ const initEmbeddedSigningAPI = (baseAccountUrl, accountId) =>
accountId
);
-export { authenticationAPI, initDocumentAPI, initEmbeddedSigningAPI };
+export { authenticationAPI, initDocumentAPI, initFocusedViewAPI };
diff --git a/src/pages/knowYourCustomer/components/focusedView.js b/src/pages/knowYourCustomer/components/focusedView.js
index a987578..db9a5ba 100644
--- a/src/pages/knowYourCustomer/components/focusedView.js
+++ b/src/pages/knowYourCustomer/components/focusedView.js
@@ -1,20 +1,20 @@
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
-import { initDocumentAPI, initEmbeddedSigningAPI } from "../../../api";
+import { initFocusedViewAPI } from "../../../api";
import * as accountRepository from "../../../services/accountRepository";
import { createSigningTemplate } from "../signingTemplate";
const createEnvelope = async (envelopeDefinition) => {
const accountInfo = accountRepository.getAccountInfo();
- const api = initDocumentAPI(accountInfo.accountBaseUrl, accountInfo.accountId);
+ const api = initFocusedViewAPI(accountInfo.accountBaseUrl, accountInfo.accountId);
const envelopeId = await api.createEnvelope(envelopeDefinition);
return envelopeId;
};
const createRecipientView = async (envelopeId, requestData) => {
const accountInfo = accountRepository.getAccountInfo();
- const api = initEmbeddedSigningAPI(accountInfo.accountBaseUrl, accountInfo.accountId);
- const recipientView = await api.embeddedSigningCeremony1(envelopeId, requestData);
+ const api = initFocusedViewAPI(accountInfo.accountBaseUrl, accountInfo.accountId);
+ const recipientView = await api.getRecipientView(envelopeId, requestData);
return recipientView;
};
@@ -66,8 +66,6 @@ const createRecipientViewDefinition = (dsReturnUrl, signerEmail, signerName, sig
messageOrigins: ['https://apps-d.docusign.com'],
});
-
-// Main Component
export const FocusedView = (args) => {
const { t } = useTranslation("EmbeddedSigningTemplate");
@@ -79,13 +77,10 @@ export const FocusedView = (args) => {
const accountInfo = accountRepository.getAccountInfo();
- // Create the envelope definition
const envelope = await createEnvelopDefinition(accountInfo.userEmail, accountInfo.userName, 1000, photo, t);
- // Create the envelope
const originEnvelopeId = await createEnvelope(envelope);
- // Create the recipient view definition
const recipientViewDefinition = createRecipientViewDefinition(
window.location.origin,
accountInfo.userEmail,
@@ -94,12 +89,8 @@ export const FocusedView = (args) => {
window.location.origin
);
- // Create the recipient view
const recipientViewUrl = await createRecipientView(originEnvelopeId, recipientViewDefinition);
- const wdf = process.env.REACT_APP_OAUTH_CLIENT_ID;
- console.log(wdf);
- // Initialize DocuSign SDK and mount the signing view
const docusign = await window.DocuSign.loadDocuSign(process.env.REACT_APP_OAUTH_CLIENT_ID);
const signing = docusign.signing({
url: recipientViewUrl,
@@ -112,12 +103,12 @@ export const FocusedView = (args) => {
},
},
signingNavigationButton: {
- finishText: "You have finished the document! Hooray!",
+ finishText: "You have finished the document! Continue!",
position: "bottom-center",
},
},
});
- console.log(navigator.geolocation);
+
signing.on("ready", () => {
console.log("UI is rendered");
});
diff --git a/src/pages/knowYourCustomer/useEmbeddedSigning.js b/src/pages/knowYourCustomer/useEmbeddedSigning.js
deleted file mode 100644
index 9f881a4..0000000
--- a/src/pages/knowYourCustomer/useEmbeddedSigning.js
+++ /dev/null
@@ -1,86 +0,0 @@
-import { useEffect, useState, useRef } from "react";
-import { useTranslation } from "react-i18next";
-import { initEmbeddedSigningAPI } from "../../api";
-import { createSigningTemplate } from "./signingTemplate";
-
-export const useEmbeddedSigning = (AccountService, onError, onSuccess) => {
- const { t } = useTranslation("EmbeddedSigningTemplate");
- const [inProgress, setInProgress] = useState(false);
- const signingResult = useRef(null);
-
- const handleError = (error) => {
- setInProgress(false);
- onError(error);
- };
-
- const handleSuccess = () => {
- setInProgress(false);
- onSuccess();
- };
-
- const handleMessage = async (data) => {
- if (signingResult.current) {
- signingResult.current.close();
- }
- const { href } = data;
- const sections = href.split("?");
- const hasEvents = sections.length === 2;
- if (!hasEvents) {
- handleError(`Unexpected signing ceremony response: ${data.href}.`);
- return;
- }
-
- handleSuccess();
- };
-
- const messageListener = async (event) => {
- if (!event.data) {
- return;
- }
- const { source } = event.data;
-
- if (source === "dsResponse") {
- await handleMessage(event.data);
- }
- };
-
- useEffect(() => {
- window.addEventListener("message", messageListener);
- return () => {
- window.removeEventListener("message", messageListener);
- };
- }, []);
-
- const embeddedSigning = async (photo) => {
- setInProgress(true);
- const accountInfo = AccountService.getAccountInfo();
- const api = initEmbeddedSigningAPI(
- accountInfo.accountBaseUrl,
- accountInfo.accountId
- );
-
- const signer = {
- email: accountInfo.userEmail,
- name: accountInfo.userName,
- photo,
- };
- const template = createSigningTemplate(signer, t);
-
- try {
- signingResult.current = await api.embeddedSigning(signer, template, () =>
- handleError({
- title: "Popup error",
- description:
- "Browser popup is blocked. Please allow it and try again",
- })
- );
- } catch (e) {
- handleError({
- title: "Embedded Signing Error",
- description: e.response?.data?.message ?? e.message,
- });
- }
- };
-
- return [embeddedSigning, inProgress];
-};
From 2995c4d2fe4d1f3e7d65a47e63b181d8513feab6 Mon Sep 17 00:00:00 2001
From: NanBohdanMoroz <92790530+NanSigmaBohdanMoroz@users.noreply.github.com>
Date: Fri, 10 Jan 2025 11:12:29 +0200
Subject: [PATCH 5/6] update translate
---
.../locales/en/EmbeddedSigningTemplate.json | 10 -------
public/locales/en/KnowYourCustomer.json | 10 +++++++
.../components/focusedView.js | 28 +++++++++++--------
src/pages/knowYourCustomer/index.js | 6 ----
src/pages/knowYourCustomer/signingTemplate.js | 16 +++++------
5 files changed, 35 insertions(+), 35 deletions(-)
delete mode 100644 public/locales/en/EmbeddedSigningTemplate.json
diff --git a/public/locales/en/EmbeddedSigningTemplate.json b/public/locales/en/EmbeddedSigningTemplate.json
deleted file mode 100644
index fd2cacb..0000000
--- a/public/locales/en/EmbeddedSigningTemplate.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "Title": "Enter your personal information:",
- "FullName": "Full name:",
- "StreetAddress": "Street address:",
- "City": "City:",
- "State": "State:",
- "ZipCode": "Zip code:",
- "Phone": "Phone:",
- "Email": "Email:"
-}
\ No newline at end of file
diff --git a/public/locales/en/KnowYourCustomer.json b/public/locales/en/KnowYourCustomer.json
index 352c9d7..aa577d3 100644
--- a/public/locales/en/KnowYourCustomer.json
+++ b/public/locales/en/KnowYourCustomer.json
@@ -44,5 +44,15 @@
"WarningModal": {
"Title": "Alert: Camera not found",
"Description": "It appears your device doesn’t have a camera! For demonstration purposes, we’ll provide a generic image."
+ },
+ "FocusedView": {
+ "Title": "Enter your personal information:",
+ "FullName": "Full name:",
+ "StreetAddress": "Street address:",
+ "City": "City:",
+ "State": "State:",
+ "ZipCode": "Zip code:",
+ "Phone": "Phone:",
+ "Email": "Email:"
}
}
diff --git a/src/pages/knowYourCustomer/components/focusedView.js b/src/pages/knowYourCustomer/components/focusedView.js
index db9a5ba..df53a5f 100644
--- a/src/pages/knowYourCustomer/components/focusedView.js
+++ b/src/pages/knowYourCustomer/components/focusedView.js
@@ -1,8 +1,9 @@
-import React, { useEffect } from "react";
+import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { initFocusedViewAPI } from "../../../api";
import * as accountRepository from "../../../services/accountRepository";
import { createSigningTemplate } from "../signingTemplate";
+import { WaitingModal } from "../../../components";
const createEnvelope = async (envelopeDefinition) => {
const accountInfo = accountRepository.getAccountInfo();
@@ -18,7 +19,7 @@ const createRecipientView = async (envelopeId, requestData) => {
return recipientView;
};
-const createEnvelopDefinition = async (signerEmail, signerName, signerClientId, userPhoto, t) => {
+const createEnvelopeDefinition = async (signerEmail, signerName, signerClientId, userPhoto, t) => {
const template = createSigningTemplate({name: signerName, email: signerEmail, photo: userPhoto}, t);
const document = {
@@ -55,7 +56,7 @@ const createEnvelopDefinition = async (signerEmail, signerName, signerClientId,
};
const createRecipientViewDefinition = (dsReturnUrl, signerEmail, signerName, signerClientId, dsPingUrl) => ({
- returnUrl: `${dsReturnUrl}/know-your-customer?state=123`,
+ returnUrl: `${dsReturnUrl}`,
authenticationMethod: "none",
email: signerEmail,
userName: signerName,
@@ -68,16 +69,19 @@ const createRecipientViewDefinition = (dsReturnUrl, signerEmail, signerName, sig
export const FocusedView = (args) => {
- const { t } = useTranslation("EmbeddedSigningTemplate");
+ const { t } = useTranslation("KnowYourCustomer");
+
+ const [showWaitingModal, setWaitingModal] = useState(false);
useEffect(() => {
const fetchData = async () => {
try {
const { photo } = args;
+ setWaitingModal(true);
const accountInfo = accountRepository.getAccountInfo();
- const envelope = await createEnvelopDefinition(accountInfo.userEmail, accountInfo.userName, 1000, photo, t);
+ const envelope = await createEnvelopeDefinition(accountInfo.userEmail, accountInfo.userName, 1000, photo, t);
const originEnvelopeId = await createEnvelope(envelope);
@@ -108,17 +112,13 @@ export const FocusedView = (args) => {
},
},
});
-
- signing.on("ready", () => {
- console.log("UI is rendered");
- });
- signing.on("sessionEnd", (event) => {
+ signing.on("sessionEnd", () => {
window.location.reload();
- console.log("Session ended:", event);
});
signing.mount("#agreement");
+ setWaitingModal(false);
} catch (error) {
console.error("Error fetching recipient view:", error);
}
@@ -129,6 +129,12 @@ export const FocusedView = (args) => {
return (
+
showWaitingModal(false)}
+ title={t("WaitingModal.Title")}
+ description={t("WaitingModal.Description")}
+ />
Signing
{
setLastStep(true);
}}
/>
- {/*
showWaitingModal(false)}
- title={t("WaitingModal.Title")}
- description={t("WaitingModal.Description")}
- /> */}
setShowErrorModal(false)}
diff --git a/src/pages/knowYourCustomer/signingTemplate.js b/src/pages/knowYourCustomer/signingTemplate.js
index 6fb3521..04f471b 100644
--- a/src/pages/knowYourCustomer/signingTemplate.js
+++ b/src/pages/knowYourCustomer/signingTemplate.js
@@ -65,12 +65,12 @@ export const createSigningTemplate = (signer, t) =>
-
{t("Title")}
+
{t("FocusedView.Title")}