Skip to content

Commit 3f8c86f

Browse files
authored
Merge pull request #22 from docusign/task/MYM-47
Add trigger functionality
2 parents beb592b + dadefea commit 3f8c86f

File tree

32 files changed

+3628
-472
lines changed

32 files changed

+3628
-472
lines changed

client/src/App.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import Hero from './pages/Hero/Hero.jsx';
55
import HomeAuthenticated from './pages/Home/Home.jsx';
66
import ManageWorkflowAuthenticated from './pages/ManageWorkflow/ManageWorkflow.jsx';
77
import TriggerWorkflowAuthenticated from './pages/TriggerWorkflow/TriggerWorkflow.jsx';
8+
import TriggerWorkflowFormAuthenticated from './pages/TriggerWorkflowForm/TriggerWorkflowForm.jsx';
89
import { LoginStatus, ROUTE } from './constants.js';
910
import { api } from './api';
10-
import TriggerWorkflowFormAuthenticated from './pages/TriggerWorkflowForm/TriggerWorkflowForm.jsx';
1111

1212
function App() {
1313
const location = useLocation();
@@ -19,10 +19,10 @@ function App() {
1919
useEffect(() => {
2020
const fetchCallback = async () => {
2121
if (code && code.length > 0) {
22-
const res = await api.acg.callbackExecute(code);
22+
const { data: userInfo } = await api.acg.callbackExecute(code);
2323
dispatch({
2424
type: 'LOGIN',
25-
payload: { authType: LoginStatus.ACG, userName: res.data.name, userEmail: res.data.email },
25+
payload: { authType: LoginStatus.ACG, userName: userInfo.name, userEmail: userInfo.email },
2626
});
2727
navigate(ROUTE.HOME);
2828
dispatch({ type: 'CLOSE_POPUP' });
@@ -39,7 +39,7 @@ function App() {
3939
<Route path={ROUTE.HOME} element={<HomeAuthenticated />} />
4040
<Route path={ROUTE.TRIGGER} element={<TriggerWorkflowAuthenticated />} />
4141
<Route path={ROUTE.MANAGE} element={<ManageWorkflowAuthenticated />} />
42-
<Route path={ROUTE.TRIGGERFORM} element={<TriggerWorkflowFormAuthenticated />} />
42+
<Route path={`${ROUTE.TRIGGERFORM}/:workflowId`} element={<TriggerWorkflowFormAuthenticated />} />
4343
</Routes>
4444
);
4545
}

client/src/api/index.js

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

44
const apiUrl = process.env.BACKEND_API;
55

@@ -22,21 +22,23 @@ export const api = Object.freeze({
2222
jwt: {
2323
// Login by JSON Web Token
2424
login: async () => {
25-
const res = await instance.get('/auth/jwt/login');
25+
const response = await instance.get('/auth/jwt/login');
2626
// If user has never logged in before, redirect to consent screen
27-
if (res.status === 210) {
28-
window.location = res.data;
27+
if (response.status === 210) {
28+
window.location = response.data;
2929
return;
3030
}
3131

32-
return res;
32+
return response;
3333
},
3434
logout: async () => {
3535
await instance.get('/auth/jwt/logout');
36+
await persistor.purge();
37+
localStorage.clear();
3638
},
3739
loginStatus: async () => {
38-
const res = await instance.get('/auth/jwt/login-status');
39-
return JSON.parse(res.data); // response boolean
40+
const response = await instance.get('/auth/jwt/login-status');
41+
return JSON.parse(response.data); // response boolean
4042
},
4143
},
4244
acg: {
@@ -47,21 +49,23 @@ export const api = Object.freeze({
4749
},
4850
logout: async () => {
4951
await instance.get('/auth/passport/logout');
52+
await persistor.purge();
53+
localStorage.clear();
5054
},
5155
callbackExecute: async code => {
52-
const res = await instance.get(`/auth/passport/callback?code=${code}`);
53-
return res;
56+
const response = await instance.get(`/auth/passport/callback?code=${code}`);
57+
return response;
5458
},
5559
loginStatus: async () => {
56-
const res = await instance.get('/auth/passport/login-status');
57-
return JSON.parse(res.data); // response boolean
60+
const response = await instance.get('/auth/passport/login-status');
61+
return JSON.parse(response.data); // response boolean
5862
},
5963
},
6064
workflows: {
6165
createWorkflowDefinition: async templateType => {
6266
try {
63-
const res = await instance.post('/workflows/create', { templateType: templateType });
64-
return res;
67+
const response = await instance.post('/workflows/create', { templateType: templateType });
68+
return response;
6569
} catch (error) {
6670
if (error.response && error.response.status === 400) {
6771
return error.response;
@@ -71,21 +75,18 @@ export const api = Object.freeze({
7175
},
7276
cancelWorkflowInstance: async workflow => {
7377
try {
74-
const res = await instance.put(`/workflows/${workflow.definitionId}/instances/${workflow.dacId}/cancel`);
75-
return res;
78+
const response = await instance.put(`/workflows/${workflow.id}/instances/${workflow.instanceId}/cancel`);
79+
return response;
7680
} catch (error) {
77-
if (error.response && error.response.status === 400) {
78-
return error.response;
79-
}
80-
throw error;
81+
console.log(error);
8182
}
8283
},
8384
publishWorkflow: async workflowId => {
84-
const res = await instance.post('/workflows/publish', { workflowId });
85+
const response = await instance.post('/workflows/publish', { workflowId });
8586

86-
if (res.status === 210) {
87+
if (response.status === 210) {
8788
try {
88-
window.open(res.data, 'newTab', 'width=800,height=600');
89+
window.open(response.data, 'newTab', 'width=800,height=600');
8990
await new Promise(r => setTimeout(r, 3000));
9091

9192
const published = await instance.post('/workflows/publish', { workflowId });
@@ -95,19 +96,27 @@ export const api = Object.freeze({
9596
}
9697
}
9798

98-
return res;
99+
return response;
100+
},
101+
triggerWorkflow: async (workflowId, body) => {
102+
try {
103+
const response = await instance.put(`/workflows/${workflowId}/trigger`, body);
104+
return response;
105+
} catch (error) {
106+
console.log(error);
107+
}
99108
},
100109
getWorkflowDefinitions: async () => {
101-
const res = await instance.get(`/workflows/definitions`);
102-
return res;
110+
const response = await instance.get(`/workflows/definitions`);
111+
return response;
103112
},
104113
getWorkflowInstance: async workflow => {
105-
const res = await instance.get(`/workflows/${workflow.definitionId}/instances/${workflow.dacId}`);
106-
return res;
114+
const response = await instance.get(`/workflows/${workflow.id}/instances/${workflow.instanceId}`);
115+
return response;
107116
},
108-
getWorkflowInstances: async definitionId => {
109-
const res = await instance.get(`/workflows/${definitionId}/instances`);
110-
return res;
117+
getWorkflowInstances: async workflowId => {
118+
const response = await instance.get(`/workflows/${workflowId}/instances`);
119+
return response;
111120
},
112121
downloadWorkflowTemplate: async templateName => {
113122
try {

client/src/components/Card/Card.jsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
1+
import { useState } from 'react';
12
import { Link } from 'react-router-dom';
3+
import { useDispatch, useSelector } from 'react-redux';
24
import styles from './Card.module.css';
35
import Dropdown from '../Dropdown/Dropdown.jsx';
46
import { WorkflowOptions } from '../../constants.js';
7+
import CreateWorkflowMoreinfoPopup from '../Popups/CreateWorkflowMoreInfo/CreateWorkflowMoreInfo.jsx';
58

69
const Card = props => {
10+
const [isBtsOpened, setBtsOpened] = useState(false);
11+
const dispatch = useDispatch();
12+
const isOpened = useSelector(state => state.popup.isOpened);
13+
14+
const togglePopup = async () => {
15+
setBtsOpened(!isBtsOpened);
16+
dispatch({ type: isOpened ? 'CLOSE_POPUP' : 'OPEN_POPUP' });
17+
};
18+
719
return (
820
<div className={styles.cardBody}>
921
<div className={styles.cardContainer}>
@@ -18,7 +30,15 @@ const Card = props => {
1830
</Link>
1931
) : (
2032
<div>
21-
<Dropdown options={WorkflowOptions} />
33+
<div className={styles.buttonGroup}>
34+
{props.moreInfo && (
35+
<button className={styles.moreInfo} onClick={togglePopup}>
36+
More Info
37+
</button>
38+
)}
39+
<Dropdown options={WorkflowOptions} />
40+
</div>
41+
{isBtsOpened && <CreateWorkflowMoreinfoPopup togglePopup={togglePopup} />}
2242
</div>
2343
)}
2444
</div>

client/src/components/Card/Card.module.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,19 @@
5656
flex-direction: column;
5757
align-items: center;
5858
overflow: visible;
59+
}
60+
61+
.buttonGroup {
62+
display: flex;
63+
flex-direction: row;
64+
}
65+
66+
.moreInfo {
67+
line-height: 10px !important;
68+
background-color: var(--grey-light) !important;
69+
color: var(--black-light) !important;
70+
}
71+
72+
.moreInfo:hover, .moreInfo:focus, .moreInfo:active {
73+
border: none;
5974
}

client/src/components/Dropdown/Dropdown.jsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import WorkflowCreationPopup from '../Popups/WorkflowDefinitionCreation/Workflow
55
import { api } from '../../api';
66

77
const Dropdown = ({ options }) => {
8-
const [selectedDocument, setSelectedDocument] = useState(options[0].value);
98
const dispatch = useDispatch();
109
const isOpened = useSelector(state => state.popup.isOpened);
10+
const [selectedDocument, setSelectedDocument] = useState(options[0].value);
1111

1212
const togglePopup = () => {
1313
dispatch({ type: isOpened ? 'CLOSE_POPUP' : 'OPEN_POPUP' });
1414
dispatch({ type: 'CLEAR_ERROR_POPUP' });
15-
dispatch({ type: 'CLEAR_WORKFLOW' });
15+
dispatch({ type: 'CLEAR_CREATED_WORKFLOW' });
1616
};
1717

1818
const handleCreateWorkflow = async ({ value, type }) => {
@@ -27,7 +27,7 @@ const Dropdown = ({ options }) => {
2727
return;
2828
}
2929

30-
dispatch({ type: 'ADD_WORKFLOW', payload: data });
30+
dispatch({ type: 'CREATED_WORKFLOW', payload: { workflowId: data.workflowDefinitionId } });
3131
dispatch({ type: 'LOADED_POPUP' });
3232
};
3333

@@ -54,12 +54,13 @@ const Dropdown = ({ options }) => {
5454
</a>
5555
))}
5656
</div>
57-
{isOpened ? (
57+
58+
{isOpened && (
5859
<WorkflowCreationPopup
5960
togglePopup={togglePopup}
6061
message={options.find(option => option.value === selectedDocument).message}
6162
/>
62-
) : null}
63+
)}
6364
</div>
6465
);
6566
};

client/src/components/LoginForm/LoginForm.jsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ const LoginForm = ({ togglePopup, setLoading }) => {
1717

1818
try {
1919
if (authType === LoginStatus.JWT) {
20-
const res = await api.jwt.login();
21-
dispatch({ type: 'LOGIN', payload: { authType, userName: res.data.name, userEmail: res.data.email } });
20+
const { data: userInfo } = await api.jwt.login();
21+
dispatch({ type: 'LOGIN', payload: { authType, userName: userInfo.name, userEmail: userInfo.email } });
2222
navigate(ROUTE.HOME);
2323
dispatch({ type: 'CLOSE_POPUP' });
2424
dispatch({ type: 'LOADED_POPUP' });
@@ -54,8 +54,9 @@ const LoginForm = ({ togglePopup, setLoading }) => {
5454
/>
5555
{LoginStatus.ACG}
5656
</label>
57-
<label className={styles.subLabel}>This authentication type provides functionality of creating, triggering
58-
and managing workflows.</label>
57+
<label className={styles.subLabel}>
58+
This authentication type provides functionality of creating, triggering and managing workflows.
59+
</label>
5960
</div>
6061
<div className={styles.radioButtonWrapper}>
6162
<label className={styles.label}>
@@ -68,7 +69,9 @@ const LoginForm = ({ togglePopup, setLoading }) => {
6869
/>
6970
{LoginStatus.JWT}
7071
</label>
71-
<label className={styles.subLabel}>This authentication type provides functionality of only triggering and managing workflows.</label>
72+
<label className={styles.subLabel}>
73+
This authentication type provides functionality of only triggering and managing workflows.
74+
</label>
7275
</div>
7376
</div>
7477
<div className={styles.formButtons}>

client/src/components/LoginForm/LoginForm.module.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ input[type=radio] {
3535
flex-direction: row;
3636
justify-content: start;
3737
padding-top: 12px;
38-
padding-left: 16px;
38+
margin-left: 2rem;
3939
line-height: 20px;
4040
font-size: 14px;
4141
font-weight: 500;
@@ -51,7 +51,7 @@ input[type=radio] {
5151
width: 100%;
5252
margin-left: 6.25rem;
5353
padding-right: 2rem;
54-
margin-top: -0.75rem;
54+
margin-top: -0.25rem;
5555

5656
display: inline-block;
5757
color: var(--black-extralight);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import styles from './CreateWorkflowMoreInfo.module.css';
2+
import withPopup from '../../../hocs/withPopup/withPopup.jsx';
3+
4+
const CreateWorkflowMoreinfo = () => {
5+
return (
6+
<div className={styles.popupContainer}>
7+
<h2>Behind the scenes</h2>
8+
<div className={styles.behindTheScenes}>
9+
<h4>This sample features</h4>
10+
<p>
11+
Employee completes ID verification
12+
Employee fills out web form with their information
13+
Data from the web form is used to populate the I-9 document
14+
Employee completes the I-9 document with embedded signing
15+
HR approver completes the document
16+
</p>
17+
<h4>Step 1 Creating workflow</h4>
18+
<p>After the form is submitted, call the
19+
AccountBrands:list
20+
method on the account to check
21+
if the brand you want to create already exists.
22+
If it does, find the corresponding brand ID. If not,
23+
call the
24+
AccountBrands:create
25+
method to create a
26+
new brand.
27+
The brand ID is then stored for the next step.
28+
</p>
29+
<h4>Step 2</h4>
30+
<p>Create an envelope definition with all of the envelope data, including the form information, brand data,
31+
tabs for form fields, documents to sign, and recipients.
32+
Place different types of tabs on the documents as part of the Signers. The tab elements are positioned using
33+
x/y coordinates on the Documents.
34+
Since the first signer is using embedded signing, you must also set the clientUserId property for that
35+
signer recipient.
36+
</p>
37+
<h4>Step 3</h4>
38+
<p>Using the
39+
Envelopes:create
40+
API method, create
41+
and immediately send the envelope with the
42+
envelope definition made from the previous step. The returned envelopeId is then stored for use in the
43+
next step.
44+
</p>
45+
</div>
46+
</div>
47+
);
48+
}
49+
;
50+
51+
const CreateWorkflowMoreinfoPopup = withPopup(CreateWorkflowMoreinfo);
52+
export default CreateWorkflowMoreinfoPopup;

0 commit comments

Comments
 (0)