Skip to content

Commit dbd70cb

Browse files
authored
Merge pull request #27 from docusign/task/MYM-72
Task/mym 72
2 parents 1484f07 + f3323b3 commit dbd70cb

35 files changed

+6771
-6754
lines changed

client/src/api/index.js

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,22 @@ import { clearAllState } from '../store/actions';
55
const isDev = process.env.NODE_ENV === 'development';
66
const apiUrl = isDev ? process.env.BACKEND_DEV_HOST : process.env.BACKEND_PROD_HOST;
77

8+
const clearState = async () => {
9+
store.dispatch(clearAllState());
10+
await persistor.purge();
11+
localStorage.clear();
12+
};
13+
814
const instance = axios.create({
915
baseURL: apiUrl,
1016
withCredentials: true,
1117
});
1218

1319
instance.interceptors.response.use(
1420
response => response,
15-
err => {
21+
async err => {
1622
if (err?.response?.status === 401) {
17-
store.dispatch(clearAllState());
23+
await clearState();
1824
}
1925
throw err;
2026
}
@@ -35,8 +41,7 @@ export const api = Object.freeze({
3541
},
3642
logout: async () => {
3743
await instance.get('/auth/jwt/logout');
38-
await persistor.purge();
39-
localStorage.clear();
44+
await clearState();
4045
},
4146
loginStatus: async () => {
4247
const response = await instance.get('/auth/jwt/login-status');
@@ -51,8 +56,7 @@ export const api = Object.freeze({
5156
},
5257
logout: async () => {
5358
await instance.get('/auth/passport/logout');
54-
await persistor.purge();
55-
localStorage.clear();
59+
await clearState();
5660
},
5761
callbackExecute: async code => {
5862
const response = await instance.get(`/auth/passport/callback?code=${code}`);
@@ -80,7 +84,7 @@ export const api = Object.freeze({
8084
const response = await instance.put(`/workflows/${workflow.id}/instances/${workflow.instanceId}/cancel`);
8185
return response;
8286
} catch (error) {
83-
console.log(error);
87+
return error.response;
8488
}
8589
},
8690
publishWorkflow: async workflowId => {
@@ -96,6 +100,7 @@ export const api = Object.freeze({
96100
return published;
97101
} catch (error) {
98102
console.log(error);
103+
return error.response;
99104
}
100105
}
101106

@@ -107,9 +112,9 @@ export const api = Object.freeze({
107112
throw error;
108113
}
109114
},
110-
triggerWorkflow: async (workflowId, body) => {
115+
triggerWorkflow: async (workflowId, templateType, body) => {
111116
try {
112-
const response = await instance.put(`/workflows/${workflowId}/trigger`, body);
117+
const response = await instance.put(`/workflows/${workflowId}/trigger?type=${templateType}`, body);
113118
return response;
114119
} catch (error) {
115120
console.log(error);
@@ -129,7 +134,7 @@ export const api = Object.freeze({
129134
},
130135
downloadWorkflowTemplate: async templateName => {
131136
try {
132-
const response = await fetch(`/workflows/download/${templateName}`);
137+
const response = await fetch(`${apiUrl}/workflows/download/${templateName}`);
133138
const blob = await response.blob();
134139
const url = window.URL.createObjectURL(blob);
135140
const a = document.createElement('a');

client/src/assets/text.json

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,27 @@
2121
"triggerForm": {
2222
"formTitle": "Fill in details",
2323
"formName": "Participant Information",
24-
"fields": {
25-
"instanceName": "Instance Name",
26-
"signerName": "Signer Name",
27-
"signerEmail": "Signer Email",
28-
"ccName": "CC Name",
29-
"ccEmail": "CC Email"
24+
"fieldsI9": {
25+
"field1": "Preparer Name",
26+
"field2": "Preparer Email",
27+
"field3": "Employee Name",
28+
"field4": "Employee Email",
29+
"field5": "HR Approver Name",
30+
"field6": "HR Approver Email"
31+
},
32+
"fieldsOffer": {
33+
"field1": "HR Manager Name",
34+
"field2": "HR Manager Email",
35+
"field3": "Company"
36+
},
37+
"fieldsNda": {
38+
"field1": "HR Manager Name",
39+
"field2": "HR Manager Email"
3040
}
3141
},
3242
"home": {
3343
"card1": {
34-
"title": "'Create a workflow'",
44+
"title": "Create a workflow",
3545
"description": "Create workflow definitions from the dropdown list below"
3646
},
3747
"card2": {
@@ -119,7 +129,8 @@
119129
"updateWorkflow": "Update workflow status",
120130
"cancelWorkflow": "Cancel workflow",
121131
"getStarted": "Get Started",
122-
"moreInfo": "More Info"
132+
"moreInfo": "More Info",
133+
"backHome": "← Back to home"
123134
},
124135
"links": {
125136
"github": "https://github.com/docusign/sample-app-mymaestro-node",

client/src/components/Popups/CreateWorkflowMoreInfo/CreateWorkflowMoreInfo.jsx

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,32 @@ import withPopup from '../../../hocs/withPopup/withPopup.jsx';
33
import textContent from '../../../assets/text.json';
44

55
const CreateWorkflowMoreinfo = () => {
6-
return (
7-
<div className={styles.popupContainer}>
8-
<h2>{textContent.buttons.behindTheScenes}</h2>
9-
<div className={styles.behindTheScenes}>
10-
<h4>{textContent.behindTheScenes.titles.main}</h4>
11-
<p>{textContent.behindTheScenes.createWorkflow.mainDescription}</p>
12-
<h4>{textContent.behindTheScenes.titles.code}</h4>
13-
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: '-0.5rem' }}>
14-
<p>{textContent.behindTheScenes.descriptions.codeDescription}</p> <a target="_blank" rel="noreferrer"
15-
href={'https://github.com/docusign/sample-app-mymaestro-node/blob/main/server/controllers/workflowsController.js'}>Controller</a>
16-
</div>
17-
<h4>{textContent.behindTheScenes.titles.step1}</h4>
18-
<p>{textContent.behindTheScenes.createWorkflow.step1Description}</p>
19-
<h4>{textContent.behindTheScenes.titles.step2}</h4>
20-
<p>{textContent.behindTheScenes.createWorkflow.step2Description}</p>
21-
<h4>{textContent.behindTheScenes.titles.step3}</h4>
22-
<p>{textContent.behindTheScenes.createWorkflow.step3Description}</p>
6+
return (
7+
<div className={styles.popupContainer}>
8+
<h2>{textContent.buttons.behindTheScenes}</h2>
9+
<div className={styles.behindTheScenes}>
10+
<h4>{textContent.behindTheScenes.titles.main}</h4>
11+
<p>{textContent.behindTheScenes.createWorkflow.mainDescription}</p>
12+
<h4>{textContent.behindTheScenes.titles.code}</h4>
13+
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: '-0.5rem' }}>
14+
<p>{textContent.behindTheScenes.descriptions.codeDescription}</p>{' '}
15+
<a
16+
target="_blank"
17+
rel="noreferrer"
18+
href={`${textContent.links.github}/blob/main/server/controllers/workflowsController.js`}
19+
>
20+
Controller
21+
</a>
2322
</div>
23+
<h4>{textContent.behindTheScenes.titles.step1}</h4>
24+
<p>{textContent.behindTheScenes.createWorkflow.step1Description}</p>
25+
<h4>{textContent.behindTheScenes.titles.step2}</h4>
26+
<p>{textContent.behindTheScenes.createWorkflow.step2Description}</p>
27+
<h4>{textContent.behindTheScenes.titles.step3}</h4>
28+
<p>{textContent.behindTheScenes.createWorkflow.step3Description}</p>
2429
</div>
25-
);
26-
}
27-
;
28-
30+
</div>
31+
);
32+
};
2933
const CreateWorkflowMoreinfoPopup = withPopup(CreateWorkflowMoreinfo);
3034
export default CreateWorkflowMoreinfoPopup;

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,25 @@ const WorkflowDefinitionCreation = ({ message }) => {
3535
return;
3636
}
3737

38+
if (workflow?.status === 400 && workflow?.data?.errorMessage.includes('limit (5)')) {
39+
dispatch(
40+
showErrorTextInPopup(
41+
'Publish workflow was unsuccessful',
42+
"You've used all your 5 available workflows on the account. Delete active workflows to make space.",
43+
null
44+
)
45+
);
46+
dispatch(closeLoadingCircleInPopup());
47+
return;
48+
}
49+
3850
dispatch(
3951
showErrorTextInPopup(
4052
'Publish workflow was unsuccessful',
4153
'The Docusign server returned an error during the workflow publishing. Try again later.',
4254
null
4355
)
4456
);
45-
4657
dispatch(closeLoadingCircleInPopup());
4758
};
4859

client/src/components/Popups/WorkflowTriggerResult/WorkflowTriggerResult.module.css

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@
3939

4040
.popupContainer a:hover {
4141
color: var(--white-main);
42-
background-color: var(--primary-dark);
43-
box-shadow: 0px 5px 14px rgba(0, 0, 0, 0.5);
42+
background-color: var(--secondary-main);
4443
}
4544

4645
.popupContainer div {

client/src/components/TriggerForm/TriggerForm.jsx

Lines changed: 72 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,58 @@ import { useNavigate } from 'react-router-dom';
33
import { useDispatch, useSelector } from 'react-redux';
44
import styles from './TriggerForm.module.css';
55
import WorkflowTriggerResultPopup from '../Popups/WorkflowTriggerResult/WorkflowTriggerResult.jsx';
6-
import textContent from '../../assets/text.json';
7-
import { ROUTE } from '../../constants.js';
6+
import { triggerForm, buttons } from '../../assets/text.json';
7+
import { ROUTE, TemplateType } from '../../constants.js';
88
import { api } from '../../api';
99
import { openPopupWindow, closePopupWindow, updateWorkflowDefinitions } from '../../store/actions';
1010

11-
const TriggerForm = ({ workflowId }) => {
11+
const TriggerForm = ({ workflowId, templateType }) => {
1212
const dispatch = useDispatch();
1313
const navigate = useNavigate();
1414
const isPopupOpened = useSelector(state => state.popup.isOpened);
1515
const workflows = useSelector(state => state.workflows.workflows);
16-
const [instanceName, setInstanceName] = useState('');
17-
const [signerName, setSignerName] = useState('');
18-
const [signerEmail, setSignerEmail] = useState('');
19-
const [ccName, setCcName] = useState('');
20-
const [ccEmail, setCcEmail] = useState('');
2116
const [isDataSending, setDataSending] = useState(false);
2217
const [workflowInstanceUrl, setWorkflowInstanceUrl] = useState('');
18+
const [i9Form, setI9Form] = useState([
19+
{ fieldHeader: triggerForm.fieldsI9.field1, fieldName: 'preparerName', value: '' },
20+
{ fieldHeader: triggerForm.fieldsI9.field2, fieldName: 'preparerEmail', value: '' },
21+
{ fieldHeader: triggerForm.fieldsI9.field3, fieldName: 'employeeName', value: '' },
22+
{ fieldHeader: triggerForm.fieldsI9.field4, fieldName: 'employeeEmail', value: '' },
23+
{ fieldHeader: triggerForm.fieldsI9.field5, fieldName: 'hrApproverName', value: '' },
24+
{ fieldHeader: triggerForm.fieldsI9.field6, fieldName: 'hrApproverEmail', value: '' },
25+
]);
26+
const [offerLetterForm, setOfferLetterForm] = useState([
27+
{ fieldHeader: triggerForm.fieldsOffer.field1, fieldName: 'hrManagerName', value: '' },
28+
{ fieldHeader: triggerForm.fieldsOffer.field2, fieldName: 'hrManagerEmail', value: '' },
29+
{ fieldHeader: triggerForm.fieldsOffer.field3, fieldName: 'Company', value: '' },
30+
]);
31+
const [ndaForm, setNdaForm] = useState([
32+
{ fieldHeader: triggerForm.fieldsNda.field1, fieldName: 'hrManagerName', value: '' },
33+
{ fieldHeader: triggerForm.fieldsNda.field2, fieldName: 'hrManagerEmail', value: '' },
34+
]);
35+
36+
let relevantFormFields = [];
37+
let relevantSetter = null;
38+
switch (templateType) {
39+
case TemplateType.I9.type:
40+
relevantFormFields = i9Form;
41+
relevantSetter = setI9Form;
42+
break;
43+
case TemplateType.OFFER.type:
44+
relevantFormFields = offerLetterForm;
45+
relevantSetter = setOfferLetterForm;
46+
break;
47+
case TemplateType.NDA.type:
48+
relevantFormFields = ndaForm;
49+
relevantSetter = setNdaForm;
50+
break;
51+
}
52+
53+
const handleChange = (idx, event) => {
54+
const newRelevantForm = [...relevantFormFields];
55+
newRelevantForm[idx].value = event.target.value;
56+
relevantSetter(newRelevantForm);
57+
};
2358

2459
const handleCloseTriggerPopup = () => {
2560
dispatch(closePopupWindow());
@@ -29,76 +64,54 @@ const TriggerForm = ({ workflowId }) => {
2964
const handleSubmit = async event => {
3065
event.preventDefault();
3166

32-
const body = {
33-
instanceName,
34-
signerEmail,
35-
signerName,
36-
ccEmail,
37-
ccName,
38-
};
67+
const body = relevantFormFields.reduce((acc, current) => {
68+
acc[current.fieldName] = current.value;
69+
return acc;
70+
}, {});
71+
72+
if (!Object.keys(body).length) {
73+
navigate(ROUTE.TRIGGER);
74+
return;
75+
}
3976

4077
setDataSending(true);
41-
const { data: triggeredWorkflow } = await api.workflows.triggerWorkflow(workflowId, body);
78+
const { data: triggeredWorkflow } = await api.workflows.triggerWorkflow(workflowId, templateType, body);
4279
setWorkflowInstanceUrl(triggeredWorkflow.workflowInstanceUrl);
4380

4481
// Update workflowDefinitions. "...workflow" creates new workflow-object to avoid mutation in redux
45-
const updatedWorkflowDefinitions = workflows.map(workflow => {
46-
if (workflow.id === workflowId) {
47-
return {
48-
...workflow,
49-
instanceId: triggeredWorkflow.instanceId,
50-
isTriggered: true,
51-
};
52-
}
53-
54-
return { ...workflow };
82+
const updatedWorkflowDefinitions = workflows.map(w => {
83+
if (w.id !== workflowId) return { ...w };
84+
85+
return {
86+
...w,
87+
instanceId: triggeredWorkflow.instanceId,
88+
isTriggered: true,
89+
};
5590
});
5691

5792
dispatch(updateWorkflowDefinitions(updatedWorkflowDefinitions));
5893
setDataSending(false);
59-
60-
setInstanceName('');
61-
setSignerName('');
62-
setSignerEmail('');
63-
setCcName('');
64-
setCcEmail('');
6594
dispatch(openPopupWindow());
6695
};
6796

6897
return (
6998
<div className={styles.formContainer}>
70-
<h2>{textContent.triggerForm.formTitle}</h2>
99+
<h2>{triggerForm.formTitle}</h2>
71100
<div className={styles.divider} />
72101
<form className={styles.triggerForm} onSubmit={handleSubmit}>
73-
<h3>{textContent.triggerForm.formName}</h3>
74-
<div>
75-
<label>{textContent.triggerForm.fields.instanceName}</label>
76-
<input type="text" value={instanceName} onChange={e => setInstanceName(e.target.value)} required={true} />
77-
</div>
78-
79-
<div>
80-
<label>{textContent.triggerForm.fields.signerName}</label>
81-
<input type="text" value={signerName} onChange={e => setSignerName(e.target.value)} required={true} />
82-
</div>
83-
84-
<div>
85-
<label>{textContent.triggerForm.fields.signerEmail}</label>
86-
<input type="text" value={signerEmail} onChange={e => setSignerEmail(e.target.value)} required={true} />
87-
</div>
88-
89-
<div>
90-
<label>{textContent.triggerForm.fields.ccName}</label>
91-
<input type="text" value={ccName} onChange={e => setCcName(e.target.value)} required={true} />
92-
</div>
102+
<h3>{triggerForm.formName}</h3>
93103

94-
<div>
95-
<label>{textContent.triggerForm.fields.ccEmail}</label>
96-
<input type="text" value={ccEmail} onChange={e => setCcEmail(e.target.value)} required={true} />
97-
</div>
104+
{relevantFormFields.map((formItem, idx) => (
105+
<div key={formItem.fieldHeader}>
106+
<label>{formItem.fieldHeader}</label>
107+
<input type="text" value={formItem.value} onChange={e => handleChange(idx, e)} required={true} />
108+
</div>
109+
))}
98110

99111
<div className={styles.divider} />
100-
<button type="submit" disabled={isDataSending}>
101-
{textContent.buttons.continue}
112+
<button className="btn btn-primary" type="submit" disabled={isDataSending}>
113+
<span className="sr-only">{buttons.continue}</span>
114+
{isDataSending ? <span className="spinner-border spinner-border-sm" /> : null}
102115
</button>
103116
</form>
104117
{isPopupOpened && (

0 commit comments

Comments
 (0)