Skip to content

Commit a695791

Browse files
committed
Change local docker configurations
Add UI fixes
1 parent c54e345 commit a695791

File tree

25 files changed

+294
-207
lines changed

25 files changed

+294
-207
lines changed

.env.example

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,14 @@ NODE_ENV=development # Set to "production" so server serves static assets
55
### Client Variables
66
FRONTEND_DEV_HOST=http://localhost:3000
77
FRONTEND_PROD_HOST=https://mymaestro.azurewebsites.net
8-
BACKEND_API=http://localhost:4000 # On production change port to http(80) or https with domain name. On dev - http:4000
9-
108

119
### Server Variables
12-
BACKEND_HOST=http://localhost
13-
BACKEND_PORT=4000
14-
REDIRECT_URI=https://developers.docusign.com/platform/auth/consent # Where the user will be redirected after providing consent for JWT.
10+
BACKEND_DEV_HOST=http://localhost:4000/api
11+
BACKEND_PROD_HOST=https://mymaestro.azurewebsites.net/api
12+
JWT_REDIRECT_URI=https://developers.docusign.com/platform/auth/consent # Where the user will be redirected after providing consent for JWT.
1513
DS_OAUTH_SERVER=https://account-d.docusign.com # The Docusign authentication server, used for JWT.
1614
USER_ID={YOUR_USER_ID} # A GUID unique to each user's Docusign Account, located on the Apps and Keys page.
17-
DS_CLIENT_ID={YOUR_DS_JWT_CLIENT_ID} # A GUID unique to each application making Docusign API calls, located on the Apps and Keys page.
15+
DS_CLIENT_ID={YOUR_DS_CLIENT_ID} # An Integration key of your app. Located on the Apps and Keys page.
1816
DS_CLIENT_SECRET={YOUR_DS_CLIENT_SECRET}
1917
TARGET_ACCOUNT_ID=false # If you have your accound id set here, if not it's false by default.
2018
SESSION_SECRET={YOUR_SESSION_SECRET} # A unique string of your choice that is used to encrypt the session cookie.

README.md

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,31 @@
11
# Node.js and React: MyMaestro Sample Application
22

3+
![React + Vite](./client/src/assets/img/favicon.png) ![React + Vite](./client/src/assets/img/logo.svg)
4+
35
## Introduction
46

5-
Welcome to the MyMaestro sample app! MyMaestro is written using Node.js (server) and React (client), and showcase the new Maestro API and how developers can use the API to create solutions for HR use cases, specifically use cases related to hiring
7+
Welcome to the MyMaestro sample app! MyMaestro is written using Node.js (server) and React (client), and showcase the new Maestro API. Docusign Maestro is a core platform service powering the Docusign. Easily build and deploy customized workflows that automate and accelerate your agreement processes without writing any code. Maestro connects all the tools and activities in your workflow so agreement processes are more efficient, more uniform, and have better visibility.
8+
9+
With Docusign Maestro, you can combine workflow steps for Docusign apps like ID Verification, Web Forms, and eSignature, and third-party extensions to automate your agreement process end-to-end.
610

711
## Configuring your integration
812

913
Before you can run this sample app on your local machine, you must first create a new integration with a Docusign developer account.
1014

1115
### Create a new integration
1216

13-
1. If you don't already have one, create a [free developer account](https://go.docusign.com/sandbox/productshot/?elqCampaignId=16535).
14-
2. Log into your developer account, and navigate to [My Apps & Keys](https://admindemo.docusign.com/authenticate?goTo=apiIntegratorKey).
17+
1. If you don't already have one, create a [free developer account](https://go.docusign.com/sandbox/productshot/).
18+
2. Log into your developer account, and navigate to [My Apps & Keys](https://admindemo.docusign.com/apps-and-keys).
1519
3. Select **Add App and Integration Key**.
16-
4. Create a new integration that is configured to use **JSON Web Token (JWT) Grant**.
20+
4. Create a new integration that is configured to use **JSON Web Token (JWT) Grant** and **Authorization Code Grant (ACG)**.
1721
You will need the **integration key** itself and its **RSA key pair**. To use this application, you must add your application's **Redirect URI** to your integration key. See our video, [**Creating an Integration Key for JWT Authentication**](https://www.youtube.com/watch?v=GgDqa7-L0yo) for a demonstration of how to create an integration key (client ID) for a user application like this example.
1822
- Save the **integration key** and **private RSA key pair** somewhere safe as you will need these later.
19-
5. Add the following as redirect URIs for your app:
20-
- http://localhost:3000
21-
- http://localhost:3000/index
23+
5. Add redirect URIs for your app. There are several variables from the **.env** file that are used in the code and configured for redirect urls. Find variables below in the **.env** file and add the values of these variables in the Docusign account settings in the appropriate **Redirect URIs** section:
24+
- FRONTEND_DEV_HOST (http://localhost:3000)
25+
- FRONTEND_PROD_HOST (https://mymaestro.azurewebsites.net)
26+
- JWT_REDIRECT_URI (https://developers.docusign.com/platform/auth/consent)
2227

23-
## Installation guide
28+
Please pay attention that if you run the project in docker using the **docker-compose.local.yml** file, the variable FRONTEND_DEV_HOST there changes to the value **http://localhost:80**. Keep in mind that in this case this value will also have to be added to **Redirect URIs** section.
2429

2530
### Prerequisites
2631

@@ -30,16 +35,6 @@ Before you can run this sample app on your local machine, you must first create
3035
- [VS Code](https://code.visualstudio.com/)
3136
- [Docker](https://docs.docker.com/get-docker/)
3237

33-
### Docusign account settings
34-
35-
The following must be enabled on your developer account in order to run all of the examples:
36-
37-
- **SMS delivery**: Follow the instructions in the [Docusign eSignature Admin Guide](https://support.docusign.com/guides/ndse-admin-guide-sending-settings) under the **Fields and Properties** section. Make sure "Allow SMS delivery to recipients" is checked.
38-
- **Conditional routing**: Follow the instructions in the [Introduction to Conditional Routing](https://support.docusign.com/en/guides/ndse-user-guide-intro-to-conditional-routing) under the **Getting started with conditional routing** section. Make sure "Enable conditional routing" is checked.
39-
- **CertifiedDelivery recipients**: Follow the instructions on this [Docusign eSignature Admin Guide](https://support.docusign.com/guides/ndse-admin-guide-sending-settings) under the **Recipient Roles** section. Make sure "Enable needs to view role" is checked.
40-
- **Document visibility**: Follow the instructions on this [Docusign eSignature Admin Guide](https://support.docusign.com/guides/ndse-admin-guide-sending-settings) under the **Fields and Properties** section. Set your settings to "Must sign to view, unless sender," and make sure "Allow sender to specify document visibility" is checked.
41-
- **IDV**: Follow the instructions in the [Docusign Identify - ID Verification Q&A](https://support.docusign.com/en/articles/Tech-Readiness-DocuSign-Identify-ID-Verification#How_to_add_ID_Verification_on_an_account) to enable IDV on your account.
42-
4338
### Install dependencies locally
4439

4540
1. Download or clone this repository to your workstation in a new folder named **sample-app-mymaestro-node**.
@@ -50,18 +45,23 @@ The following must be enabled on your developer account in order to run all of t
5045
6. Install dependencies: **`npm install`**
5146
7. Rename the **.env.example** file in the root directory to **.env**, and update the file with the integration key and other settings.
5247
> **Note:** Protect your integration key and client secret. You should make sure that the **.env** file will not be stored in your source code repository.
53-
8. Rename the **example_private.key** file to **private.key**, and paste your complete private RSA key into this file (including the header and footer of the key).
48+
8. Rename the **example_private.key** file to **private.key**, and paste your complete private RSA key into this file (including the header and footer of the key). Private RSA you should get when created Docusign account.
5449

55-
## Running MyMaestro
50+
## Running MyMaestro in development mode
5651

5752
1. Navigate to the application folder: **`cd sample-app-mymaestro-node`**
58-
2. Navigate to the server folder: **`cd server`**
59-
3. To start the server and client at the same time: **`npm run dev`**
60-
4. **Or,** to run the server and client separately:
61-
- In one terminal, navigate to the server folder (**`cd server`**) and run **`npm run server`**
62-
- In a separate terminal, navigate to the client folder (**`cd client`**) and run **`npm start`**
63-
5. Open a browser to **http://localhost:3000**
53+
2. To start the server and client at the same time: **`npm run concurrently:dev`**
54+
3. **Or,** to run the server and client separately:
55+
- In one terminal, run **`npm run client:dev`**
56+
- In a separate terminal, run **`npm run server:dev`**
57+
4. Open a browser to **http://localhost:3000**
58+
59+
## Running MyMaestro in docker
6460

65-
## License information
61+
You can run application in docker locally like in production mode
6662

67-
This repository uses the MIT License. See the [LICENSE](./LICENSE) file for more information.
63+
1. Navigate to the application folder: **`cd sample-app-mymaestro-node`**
64+
2. Make sure that you configured **.env** file and saved **private.key** in the root of the folder. Make sure that you have docker installed.
65+
3. Run **`docker-compose -f docker-compose.local.yaml up -d`**
66+
4. In order to stop containers run **`docker compose -f docker-compose.local.yaml down`**
67+
5. Open a browser to **http://localhost:80**

client/Dockerfile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
FROM node:20.14 AS base
22

3+
# Declare build arguments from docker-compose.local.yaml
4+
ARG NODE_ENV
5+
ARG BACKEND_DEV_HOST
6+
37
RUN mkdir -p /home/vite/app
48
RUN npm config set cache /tmp --global
59
WORKDIR /home/vite
@@ -11,6 +15,10 @@ RUN npm ci
1115

1216
COPY client ./
1317

18+
# Set environment variables from build arguments
19+
ENV NODE_ENV=${NODE_ENV}
20+
ENV BACKEND_DEV_HOST=${BACKEND_DEV_HOST}
21+
1422
RUN npm run build
1523

1624
# Run stage

client/src/api/index.js

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import axios from 'axios';
22
import { persistor, store } from '../store/store';
33

4-
const apiUrl = process.env.BACKEND_API;
4+
const isDev = process.env.NODE_ENV === 'development';
5+
const apiUrl = isDev ? process.env.BACKEND_DEV_HOST : process.env.BACKEND_PROD_HOST;
56

67
const instance = axios.create({
7-
baseURL: `${apiUrl}/api`,
8+
baseURL: apiUrl,
89
withCredentials: true,
910
});
1011

@@ -45,7 +46,7 @@ export const api = Object.freeze({
4546
// Login by Authorization Code Grant
4647
login: () => {
4748
// Avoid here XHR requests because we encounter problems with CORS for ACG Authorization
48-
window.location.href = `${apiUrl}/api/auth/passport/login`;
49+
window.location.href = `${apiUrl}/auth/passport/login`;
4950
},
5051
logout: async () => {
5152
await instance.get('/auth/passport/logout');
@@ -82,21 +83,28 @@ export const api = Object.freeze({
8283
}
8384
},
8485
publishWorkflow: async workflowId => {
85-
const response = await instance.post('/workflows/publish', { workflowId });
86+
try {
87+
const response = await instance.post('/workflows/publish', { workflowId });
8688

87-
if (response.status === 210) {
88-
try {
89-
window.open(response.data, 'newTab', 'width=800,height=600');
90-
await new Promise(r => setTimeout(r, 3000));
89+
if (response.status === 210) {
90+
try {
91+
window.open(response.data, 'newTab', 'width=600,height=400');
92+
await new Promise(r => setTimeout(r, 3000));
9193

92-
const published = await instance.post('/workflows/publish', { workflowId });
93-
return published;
94-
} catch (error) {
95-
console.log(error);
94+
const published = await instance.post('/workflows/publish', { workflowId });
95+
return published;
96+
} catch (error) {
97+
console.log(error);
98+
}
9699
}
97-
}
98100

99-
return response;
101+
return response;
102+
} catch (error) {
103+
if (error.response && error.response.status >= 400) {
104+
return error.response;
105+
}
106+
throw error;
107+
}
100108
},
101109
triggerWorkflow: async (workflowId, body) => {
102110
try {

client/src/assets/text.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@
4444
}
4545
},
4646
"loader": {
47-
"title": "Waiting for log in",
48-
"paragraph": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
47+
"title": "Waiting for log in"
4948
},
5049
"behindTheScenes": {
5150
"titles": {
@@ -127,4 +126,4 @@
127126
"createsandbox": "https://go.docusign.com/o/sandbox/",
128127
"learnmore": "https://developers.docusign.com/"
129128
}
130-
}
129+
}

client/src/components/Dropdown/Dropdown.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ const Dropdown = ({ options }) => {
2323
const { status, data } = await api.workflows.createWorkflowDefinition(type);
2424

2525
if (status === 400) {
26-
dispatch({ type: 'SET_ERROR_POPUP', payload: { errorMessage: data.message, templateName: data.templateName } });
26+
dispatch({
27+
type: 'SET_ERROR_POPUP',
28+
payload: { errorMessage: data.message, errorHeader: null, templateName: data.templateName },
29+
});
2730
dispatch({ type: 'LOADED_POPUP' });
2831
return;
2932
}

client/src/components/Popups/WorkflowDefinitionCreation/WorkflowDefinitionCreation.jsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import textContent from '../../../assets/text.json';
99
const WorkflowDefinitionCreation = ({ message }) => {
1010
const dispatch = useDispatch();
1111
const errorMessage = useSelector(state => state.popup.errorMessage);
12+
const errorHeader = useSelector(state => state.popup.errorHeader);
1213
const templateName = useSelector(state => state.popup.templateName);
1314
const lastCreatedWorkflow = useSelector(state => state.workflows.lastCreatedWorkflow);
1415

@@ -24,8 +25,18 @@ const WorkflowDefinitionCreation = ({ message }) => {
2425

2526
if (workflow?.status === 200) {
2627
dispatch({ type: 'PUBLISHED_LAST_WORKFLOW' });
28+
dispatch({ type: 'LOADED_POPUP' });
29+
return;
2730
}
2831

32+
dispatch({
33+
type: 'SET_ERROR_POPUP',
34+
payload: {
35+
errorHeader: 'Publish workflow was unsuccessful',
36+
errorMessage: 'The Docusign server returned an error during the workflow publishing. Try again later.',
37+
templateName: null,
38+
},
39+
});
2940
dispatch({ type: 'LOADED_POPUP' });
3041
};
3142

@@ -41,13 +52,15 @@ const WorkflowDefinitionCreation = ({ message }) => {
4152
);
4253
}
4354

44-
if (errorMessage && templateName) {
55+
if (errorMessage) {
4556
return (
4657
<div className={styles.popupContainer}>
4758
<img src={imgError} alt="" />
48-
<h2>{textContent.popups.workflowDefinitionCreated.error.title}</h2>
59+
<h2>{errorHeader ?? textContent.popups.workflowDefinitionCreated.error.title}</h2>
4960
<p>{errorMessage}</p>
50-
<button onClick={handleDownloadTemplate}>{textContent.popups.workflowDefinitionCreated.error.button}</button>
61+
{templateName && (
62+
<button onClick={handleDownloadTemplate}>{textContent.popups.workflowDefinitionCreated.error.button}</button>
63+
)}
5164
</div>
5265
);
5366
}
Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,32 @@
11
.popupContainer {
2-
padding-top: 5rem;
3-
display: flex;
4-
flex-direction: column;
5-
align-items: center;
2+
display: flex;
3+
flex-direction: column;
4+
align-items: center;
65
}
76

87
.popupContainer h2 {
9-
height: 54px;
10-
width: 450px;
11-
line-height: 28px !important;
12-
margin: 12px 0 -12px 0;
13-
text-align: center;
14-
font-size: 24px !important;
15-
font-weight: 700 !important;
8+
height: 54px;
9+
width: 450px;
10+
line-height: 28px !important;
11+
margin: 12px 0 12px 0;
12+
text-align: center;
13+
font-size: 24px !important;
14+
font-weight: 700 !important;
1615
}
1716

1817
.popupContainer p {
19-
text-align: center;
20-
width: 80%;
21-
font-size: 16px;
22-
font-weight: 400;
18+
text-align: center;
19+
width: 80%;
20+
font-size: 16px;
21+
font-weight: 400;
2322
}
2423

2524
.popupContainer button {
26-
height: 40px;
27-
width: 250px;
28-
max-width: 100%;
29-
font-size: 16px;
30-
font-weight: 500;
31-
padding: 8px 16px;
32-
margin: 2rem 0 0 0;
33-
}
25+
height: 40px;
26+
width: 250px;
27+
max-width: 100%;
28+
font-size: 16px;
29+
font-weight: 500;
30+
padding: 8px 16px;
31+
margin: 2rem 0 0 0;
32+
}

client/src/components/TriggerForm/TriggerForm.jsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import { useState } from 'react';
2+
import { useNavigate } from 'react-router-dom';
23
import { useDispatch, useSelector } from 'react-redux';
34
import styles from './TriggerForm.module.css';
45
import WorkflowTriggerResultPopup from '../Popups/WorkflowTriggerResult/WorkflowTriggerResult.jsx';
56
import { api } from '../../api';
67
import textContent from '../../assets/text.json';
8+
import { ROUTE } from '../../constants.js';
79

810
const TriggerForm = ({ workflowId }) => {
911
const dispatch = useDispatch();
12+
const navigate = useNavigate();
1013
const isOpened = useSelector(state => state.popup.isOpened);
1114
const workflows = useSelector(state => state.workflows.workflows);
1215
const [instanceName, setInstanceName] = useState('');
@@ -17,6 +20,11 @@ const TriggerForm = ({ workflowId }) => {
1720
const [isDataSending, setDataSending] = useState(false);
1821
const [workflowInstanceUrl, setWorkflowInstanceUrl] = useState('');
1922

23+
const handleCloseTriggerPopup = () => {
24+
dispatch({ type: isOpened ? 'CLOSE_POPUP' : 'OPEN_POPUP' });
25+
navigate(ROUTE.HOME);
26+
};
27+
2028
const handleSubmit = async event => {
2129
event.preventDefault();
2230

@@ -92,7 +100,9 @@ const TriggerForm = ({ workflowId }) => {
92100
{textContent.buttons.continue}
93101
</button>
94102
</form>
95-
{isOpened && <WorkflowTriggerResultPopup workflowInstanceUrl={workflowInstanceUrl} />}
103+
{isOpened && (
104+
<WorkflowTriggerResultPopup workflowInstanceUrl={workflowInstanceUrl} togglePopup={handleCloseTriggerPopup} />
105+
)}
96106
</div>
97107
);
98108
};

client/src/components/WorkflowDescription/WorkflowDescription.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const WorkflowDescription = ({ title, behindTheScenesComponent, backRoute }) =>
66
return (
77
<div className={styles.descriptionContainer}>
88
<Link to={backRoute}>
9-
<button className={styles.backButton}>&#129028; Back</button>
9+
<button className={styles.backButton}> Back</button>
1010
</Link>
1111
<h2>{title}</h2>
1212
<div className={`dropdown ${styles.dropDown}`}>

0 commit comments

Comments
 (0)