Skip to content

Commit d29e636

Browse files
committed
Allow for anonymous pulse submission and viewing in the pulse report.
1 parent 7712618 commit d29e636

File tree

9 files changed

+82
-51
lines changed

9 files changed

+82
-51
lines changed

server/src/main/java/com/objectcomputing/checkins/services/pulseresponse/PulseResponse.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class PulseResponse {
5252

5353
@Column(name="teammemberid")
5454
@TypeDef(type=DataType.STRING)
55-
@NotNull
55+
@Nullable
5656
@Schema(description = "id of the teamMember this entry is associated with")
5757
private UUID teamMemberId;
5858

@@ -77,7 +77,7 @@ public class PulseResponse {
7777
protected PulseResponse() {
7878
}
7979

80-
public PulseResponse(UUID id, Integer internalScore, Integer externalScore, LocalDate submissionDate, UUID teamMemberId, String internalFeelings, String externalFeelings) {
80+
public PulseResponse(UUID id, Integer internalScore, Integer externalScore, LocalDate submissionDate, @Nullable UUID teamMemberId, String internalFeelings, String externalFeelings) {
8181
this.id = id;
8282
this.internalScore = internalScore;
8383
this.externalScore = externalScore;
@@ -88,7 +88,7 @@ public PulseResponse(UUID id, Integer internalScore, Integer externalScore, Loca
8888
}
8989

9090
public PulseResponse(Integer internalScore, Integer externalScore, LocalDate submissionDate, UUID teamMemberId, String internalFeelings, String externalFeelings) {
91-
this(null,internalScore, externalScore, submissionDate, teamMemberId, internalFeelings, externalFeelings);
91+
this(null, internalScore, externalScore, submissionDate, teamMemberId, internalFeelings, externalFeelings);
9292
}
9393

9494
public UUID getId() {

server/src/main/java/com/objectcomputing/checkins/services/pulseresponse/PulseResponseCreateDTO.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class PulseResponseCreateDTO {
2727
@Schema(description = "date for submissionDate")
2828
private LocalDate submissionDate;
2929

30-
@NotNull
30+
@Nullable
3131
@Schema(description = "id of the associated member")
3232
private UUID teamMemberId;
3333

server/src/main/java/com/objectcomputing/checkins/services/pulseresponse/PulseResponseServicesImpl.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,14 @@ public PulseResponse save(PulseResponse pulseResponse) {
5555
LocalDate pulseSubDate = pulseResponse.getSubmissionDate();
5656
if (pulseResponse.getId() != null) {
5757
throw new BadArgException(String.format("Found unexpected id for pulseresponse %s", pulseResponse.getId()));
58-
} else if (memberRepo.findById(memberId).isEmpty()) {
58+
} else if (memberId != null &&
59+
memberRepo.findById(memberId).isEmpty()) {
5960
throw new BadArgException(String.format("Member %s doesn't exists", memberId));
6061
} else if (pulseSubDate.isBefore(LocalDate.EPOCH) || pulseSubDate.isAfter(LocalDate.MAX)) {
6162
throw new BadArgException(String.format("Invalid date for pulseresponse submission date %s", memberId));
62-
} else if (!currentUserId.equals(memberId) && !isSubordinateTo(memberId, currentUserId)) {
63+
} else if (memberId != null &&
64+
!currentUserId.equals(memberId) &&
65+
!isSubordinateTo(memberId, currentUserId)) {
6366
throw new BadArgException(String.format("User %s does not have permission to create pulse response for user %s", currentUserId, memberId));
6467
}
6568
pulseResponseRet = pulseResponseRepo.save(pulseResponse);
@@ -94,7 +97,7 @@ public PulseResponse update(PulseResponse pulseResponse) {
9497
} else if (memberRepo.findById(memberId).isEmpty()) {
9598
throw new BadArgException(String.format("Member %s doesn't exist", memberId));
9699
} else if (memberId == null) {
97-
throw new BadArgException(String.format("Invalid pulseresponse %s", pulseResponse));
100+
throw new BadArgException("Cannot update anonymous pulse response");
98101
} else if (pulseSubDate.isBefore(LocalDate.EPOCH) || pulseSubDate.isAfter(LocalDate.MAX)) {
99102
throw new BadArgException(String.format("Invalid date for pulseresponse submission date %s", memberId));
100103
} else if (!currentUserId.equals(memberId) && !isSubordinateTo(memberId, currentUserId)) {
@@ -191,4 +194,4 @@ public void sendPulseLowScoreEmail(PulseResponse pulseResponse) {
191194
emailSender.sendEmail(null, null, subject, bodyBuilder.toString(), recipients.toArray(new String[0]));
192195
}
193196
}
194-
}
197+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE pulse_response DROP CONSTRAINT pulse_response_teamMemberId_fkey;

web-ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"fuse.js": "^6.4.6",
2929
"html-react-parser": "^5.1.12",
3030
"isomorphic-fetch": "^3.0.0",
31+
"js-cookie": "^3.0.5",
3132
"js-file-download": "^0.4.12",
3233
"lodash": "^4.17.21",
3334
"markdown-builder": "^0.9.0",

web-ui/src/pages/PulsePage.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,9 @@
1212
text-align: center;
1313
}
1414
}
15+
16+
.submit-row {
17+
align-items: center;
18+
display: flex;
19+
margin-top: 2rem; /* This is the default top margin for Buttons */
20+
}

web-ui/src/pages/PulsePage.jsx

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import Cookies from 'js-cookie';
12
import { format } from 'date-fns';
23
import React, { useContext, useEffect, useState } from 'react';
34
import { useHistory } from 'react-router-dom';
4-
import { Button, Typography } from '@mui/material';
5-
import { resolve } from '../api/api.js';
5+
import { Button, Checkbox, Typography } from '@mui/material';
6+
import { downloadData, initiate } from '../api/generic.js';
67
import Pulse from '../components/pulse/Pulse.jsx';
78
import { AppContext } from '../context/AppContext';
89
import { selectCsrfToken, selectCurrentUser } from '../context/selectors';
@@ -23,9 +24,19 @@ const PulsePage = () => {
2324
const [internalScore, setInternalScore] = useState(center);
2425
const [pulse, setPulse] = useState(null);
2526
const [submittedToday, setSubmittedToday] = useState(false);
27+
const [submitAnonymously, setSubmitAnonymously] = useState(false);
28+
2629
const today = format(new Date(), 'yyyy-MM-dd');
30+
const cookieName = "pulse_submitted_anonymously";
31+
const pulseURL = '/services/pulse-responses';
2732

2833
useEffect(() => {
34+
const submitted = Cookies.get(cookieName);
35+
if (submitted) {
36+
setSubmittedToday(true);
37+
return;
38+
}
39+
2940
if (!pulse) return;
3041

3142
const now = new Date();
@@ -52,19 +63,8 @@ const PulsePage = () => {
5263
dateTo: today,
5364
teamMemberId: currentUser.id
5465
};
55-
const queryString = Object.entries(query)
56-
.map(([key, value]) => `${key}=${value}`)
57-
.join('&');
58-
59-
const res = await resolve({
60-
method: 'GET',
61-
url: `/services/pulse-responses?${queryString}`,
62-
headers: {
63-
'X-CSRF-Header': csrf,
64-
Accept: 'application/json',
65-
'Content-Type': 'application/json;charset=UTF-8'
66-
}
67-
});
66+
67+
const res = await downloadData(pulseURL, csrf, query);
6868
if (res.error) return;
6969

7070
// Sort pulse responses by date, latest to earliest
@@ -97,22 +97,18 @@ const PulsePage = () => {
9797
internalScore: internalScore + 1, // converts to 1-based
9898
submissionDate: today,
9999
updatedDate: today,
100-
teamMemberId: myId
100+
teamMemberId: submitAnonymously ? null : myId,
101101
};
102-
const res = await resolve({
103-
method: 'POST',
104-
url: '/services/pulse-responses',
105-
headers: {
106-
'X-CSRF-Header': csrf,
107-
Accept: 'application/json',
108-
'Content-Type': 'application/json;charset=UTF-8'
109-
},
110-
data
111-
});
102+
const res = await initiate(pulseURL, csrf, data);
112103
if (res.error) return;
113104

114-
// Refresh browser to show that pulses where already submitted today.
115-
history.go(0);
105+
if (submitAnonymously) {
106+
setSubmittedToday(true);
107+
Cookies.set(cookieName, 'true', { expires: 1 });
108+
} else {
109+
// Refresh browser to show that pulses where already submitted today.
110+
history.go(0);
111+
}
116112
};
117113

118114
return (
@@ -141,9 +137,25 @@ const PulsePage = () => {
141137
setScore={setExternalScore}
142138
title="How are you feeling about life outside of work?"
143139
/>
144-
<Button onClick={submit} variant="contained">
145-
Submit
146-
</Button>
140+
<div className="submit-row">
141+
<Button
142+
style={{ marginTop: 0 }}
143+
onClick={submit}
144+
variant="contained">
145+
Submit
146+
</Button>
147+
<div style={{ padding: '.3rem' }}/>
148+
<label>
149+
<Checkbox
150+
disableRipple
151+
id="submit-anonymously"
152+
type="checkbox"
153+
checked={submitAnonymously}
154+
onChange={(event) => setSubmitAnonymously(event.target.checked)}
155+
/>
156+
Submit Anonymously
157+
</label>
158+
</div>
147159
</>
148160
)}
149161
</div>

web-ui/src/pages/PulseReportPage.jsx

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ const PulseReportPage = () => {
161161

162162
for (const pulse of pulses) {
163163
const memberId = pulse.teamMemberId;
164-
if (!teamMemberIds.includes(memberId)) continue;
164+
if (memberId && !teamMemberIds.includes(memberId)) continue;
165165

166166
const { externalScore, internalScore, submissionDate } = pulse;
167167
const [year, month, day] = submissionDate;
@@ -181,18 +181,21 @@ const PulseReportPage = () => {
181181
frequencies[internalScore - 1].internal++;
182182
frequencies[externalScore - 1].external++;
183183

184-
const member = memberMap[memberId];
185-
const { supervisorid } = member;
186-
const memberIdToUse = managerMode ? supervisorid : memberId;
187-
188-
/* For debugging ...
189-
if (supervisorid) {
190-
const supervisor = memberMap[supervisorid];
191-
console.log(`The supervisor of ${member.name} is ${supervisor.name}`);
192-
} else {
193-
console.log(`${member.name} has no supervisor`);
184+
let memberIdToUse;
185+
if (memberId) {
186+
const member = memberMap[memberId];
187+
const { supervisorid } = member;
188+
memberIdToUse = managerMode ? supervisorid : memberId;
189+
190+
/* For debugging ...
191+
if (supervisorid) {
192+
const supervisor = memberMap[supervisorid];
193+
console.log(`The supervisor of ${member.name} is ${supervisor.name}`);
194+
} else {
195+
console.log(`${member.name} has no supervisor`);
196+
}
197+
*/
194198
}
195-
*/
196199

197200
// When in manager mode, if the member
198201
// doesn't have a supervisor then skip this data.

web-ui/yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4627,6 +4627,11 @@ jest-fetch-mock@^3.0.3:
46274627
cross-fetch "^3.0.4"
46284628
promise-polyfill "^8.1.3"
46294629

4630+
js-cookie@^3.0.5:
4631+
version "3.0.5"
4632+
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc"
4633+
integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==
4634+
46304635
js-file-download@^0.4.12:
46314636
version "0.4.12"
46324637
resolved "https://registry.yarnpkg.com/js-file-download/-/js-file-download-0.4.12.tgz#10c70ef362559a5b23cdbdc3bd6f399c3d91d821"

0 commit comments

Comments
 (0)