Skip to content

Commit 730f1f2

Browse files
committed
Merge branch 'develop' of github.com:objectcomputing/check-ins into develop
2 parents 6a1ce4e + 89d5538 commit 730f1f2

File tree

4 files changed

+85
-50
lines changed

4 files changed

+85
-50
lines changed

web-ui/src/components/view_feedback_responses/ViewFeedbackResponses.css

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@
2424
text-align: center;
2525
border-radius: 4px;
2626
padding: 10px;
27+
display: flex;
28+
align-items: center;
29+
}
30+
31+
.caution-icon {
32+
display: inline-block;
33+
width: 16px;
34+
height: 16px;
35+
background-size: cover;
36+
margin-right: 8px;
2737
}
2838

2939
.question-responses-container {
@@ -48,4 +58,4 @@
4858
height: 200px;
4959
margin-bottom: 2em;
5060
}
51-
}
61+
}

web-ui/src/components/view_feedback_responses/ViewFeedbackResponses.jsx

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useContext, useEffect, useState } from 'react';
22
import { styled } from '@mui/material/styles';
3-
import { Autocomplete, Avatar, Button, Checkbox, Chip, TextField, Typography } from '@mui/material';
3+
import { Autocomplete, Avatar, Button, Checkbox, Chip, TextField, Typography, Box } from '@mui/material';
44
import FeedbackResponseCard from './feedback_response_card/FeedbackResponseCard';
55
import { getQuestionsAndAnswers } from '../../api/feedbackanswer';
66
import { getFeedbackRequestById } from '../../api/feedback';
@@ -20,7 +20,6 @@ import { getAvatarURL } from '../../api/api';
2020
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
2121
import CheckBoxIcon from '@mui/icons-material/CheckBox';
2222
import SkeletonLoader from '../skeleton_loader/SkeletonLoader';
23-
2423
import './ViewFeedbackResponses.css';
2524

2625
const PREFIX = 'MuiCardContent';
@@ -50,15 +49,13 @@ const Root = styled('div')({
5049
marginRight: '3em',
5150
width: '350px',
5251
['@media (max-width: 800px)']: {
53-
// eslint-disable-line no-useless-computed-key
5452
marginRight: 0,
5553
width: '100%'
5654
}
5755
},
5856
[`& .${classes.responderField}`]: {
5957
minWidth: '500px',
6058
['@media (max-width: 800px)']: {
61-
// eslint-disable-line no-useless-computed-key
6259
minWidth: 0,
6360
width: '100%'
6461
}
@@ -75,8 +72,7 @@ const ViewFeedbackResponses = () => {
7572
const [searchText, setSearchText] = useState('');
7673
const [responderOptions, setResponderOptions] = useState([]);
7774
const [selectedResponders, setSelectedResponders] = useState([]);
78-
const [filteredQuestionsAndAnswers, setFilteredQuestionsAndAnswers] =
79-
useState([]);
75+
const [filteredQuestionsAndAnswers, setFilteredQuestionsAndAnswers] = useState([]);
8076
const [isLoading, setIsLoading] = useState(true);
8177

8278
useEffect(() => {
@@ -90,7 +86,29 @@ const ViewFeedbackResponses = () => {
9086
? requests
9187
: [requests]
9288
: [];
93-
return await getQuestionsAndAnswers(requests, cookie);
89+
const res = await getQuestionsAndAnswers(requests, cookie);
90+
91+
if (res) {
92+
const sanitizedResponses = res.map(question => ({
93+
...question,
94+
answers: question.answers.map(answer => ({
95+
...answer,
96+
answer: isEmptyOrWhitespace(answer.answer) ? ' ⚠️ No response submitted' : String(answer.answer),
97+
}))
98+
}));
99+
100+
sanitizedResponses.sort((a, b) => a.questionNumber - b.questionNumber);
101+
setQuestionsAndAnswers(sanitizedResponses);
102+
103+
} else {
104+
window.snackDispatch({
105+
type: UPDATE_TOAST,
106+
payload: {
107+
severity: 'error',
108+
toast: 'Failed to retrieve questions and answers'
109+
}
110+
});
111+
}
94112
}
95113

96114
if (!csrf || !query.request) {
@@ -120,59 +138,47 @@ const ViewFeedbackResponses = () => {
120138
});
121139
}
122140
});
123-
retrieveQuestionsAndAnswers(query.request, csrf).then(res => {
124-
if (res) {
125-
res.sort((a, b) => a.questionNumber - b.questionNumber);
126-
setQuestionsAndAnswers(res);
127-
} else {
128-
window.snackDispatch({
129-
type: UPDATE_TOAST,
130-
payload: {
131-
severity: 'error',
132-
toast: 'Failed to retrieve questions and answers'
133-
}
134-
});
135-
}
136-
});
141+
retrieveQuestionsAndAnswers(query.request, csrf);
137142
}, [csrf, query.request]);
138143

139-
// Sets the options for filtering by responders
140144
useEffect(() => {
141145
let allResponders = [];
142146
questionsAndAnswers.forEach(({ answers }) => {
143147
const responders = answers.map(answer => answer.responder);
144148
allResponders.push(...responders);
145149
});
146-
allResponders = [...new Set(allResponders)]; // Remove duplicate responders
150+
allResponders = [...new Set(allResponders)];
147151
setResponderOptions(allResponders);
148152
}, [state, questionsAndAnswers]);
149153

150-
// Populate all responders as selected by default
151154
useEffect(() => {
152155
setSelectedResponders(responderOptions);
153156
}, [responderOptions]);
154157

155158
useEffect(() => {
156159
let responsesToDisplay = [...questionsAndAnswers];
157-
158160
responsesToDisplay = responsesToDisplay.map(response => {
159-
// Filter based on selected responders
160161
let filteredAnswers = response.answers.filter(answer =>
161162
selectedResponders.includes(answer.responder)
162163
);
164+
165+
if (filteredAnswers.length === 0) {
166+
filteredAnswers = [{ answer: 'No input due to recipient filter', responder: null }];
167+
}
168+
163169
if (searchText.trim()) {
164-
// Filter based on search text
165170
filteredAnswers = filteredAnswers.filter(
166171
({ answer }) =>
167172
answer &&
168173
answer.toLowerCase().includes(searchText.trim().toLowerCase())
169174
);
170175
}
176+
171177
return { ...response, answers: filteredAnswers };
172178
});
173179

174180
setFilteredQuestionsAndAnswers(responsesToDisplay);
175-
}, [searchText, selectedResponders]); // eslint-disable-line react-hooks/exhaustive-deps
181+
}, [searchText, selectedResponders]);
176182

177183
useEffect(() => {
178184
if (isLoading && filteredQuestionsAndAnswers.length > 0) {
@@ -184,7 +190,14 @@ const ViewFeedbackResponses = () => {
184190
setSelectedResponders(responderOptions);
185191
};
186192

193+
194+
const isEmptyOrWhitespace = (text) => {
195+
return typeof text !== 'string' || !text.trim();
196+
};
197+
198+
187199
return selectCanViewFeedbackAnswerPermission(state) ? (
200+
188201
<Root className="view-feedback-responses-page">
189202
<Typography
190203
variant="h4"
@@ -283,6 +296,9 @@ const ViewFeedbackResponses = () => {
283296
))}
284297
{!isLoading &&
285298
filteredQuestionsAndAnswers?.map(question => {
299+
const questionText = question.question;
300+
const hasResponses = question.answers.length > 0;
301+
286302
return (
287303
<div
288304
className="question-responses-container"
@@ -292,26 +308,21 @@ const ViewFeedbackResponses = () => {
292308
className="question-text"
293309
style={{ marginBottom: '0.5em', fontWeight: 'bold' }}
294310
>
295-
Q{question.questionNumber}: {question.question}
311+
{questionText}
296312
</Typography>
297-
{question.answers.length === 0 && (
298-
<div className="no-responses-found">
299-
<Typography variant="body1" style={{ color: 'gray' }}>
300-
No matching responses found
301-
</Typography>
302-
</div>
303-
)}
304-
{question.inputType !== 'NONE' &&
305-
question.answers.length > 0 &&
313+
314+
{/* If the question has no answers or inputType is "NONE" */}
315+
{!hasResponses || question.inputType === 'NONE' ? null : (
306316
question.answers.map(answer => (
307317
<FeedbackResponseCard
308318
key={answer.id || answer.responder}
309319
responderId={answer.responder}
310-
answer={answer.answer || ''}
320+
answer={isEmptyOrWhitespace(answer.answer) ? ' ⚠️ No response submitted' : String(answer.answer)}
311321
inputType={question.inputType}
312322
sentiment={answer.sentiment}
313323
/>
314-
))}
324+
))
325+
)}
315326
</div>
316327
);
317328
})}
@@ -321,4 +332,4 @@ const ViewFeedbackResponses = () => {
321332
);
322333
};
323334

324-
export default ViewFeedbackResponses;
335+
export default ViewFeedbackResponses;

web-ui/src/components/view_feedback_responses/feedback_response_card/FeedbackResponseCard.jsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import FeedbackAnswerInput from '../../feedback_answer_input/FeedbackAnswerInput
1212

1313
const propTypes = {
1414
responderId: PropTypes.string.isRequired,
15-
answer: PropTypes.string.isRequired,
15+
answer: PropTypes.string, // Allow answer to be null or undefined
1616
inputType: PropTypes.string.isRequired,
1717
sentiment: PropTypes.number
1818
};
@@ -21,6 +21,19 @@ const FeedbackResponseCard = props => {
2121
const { state } = useContext(AppContext);
2222
const userInfo = selectProfile(state, props.responderId);
2323

24+
const getFormattedAnswer = () => {
25+
if (props.inputType === 'NONE') {
26+
return null; // Return null to display nothing
27+
}
28+
29+
// Return fallback if the answer is null, undefined, or empty
30+
if (props.answer === null || props.answer === undefined || !props.answer.trim()) {
31+
return '⚠️ No response submitted';
32+
}
33+
34+
return props.answer;
35+
};
36+
2437
return (
2538
<Card className="response-card">
2639
<CardContent className="response-card-content">
@@ -31,16 +44,18 @@ const FeedbackResponseCard = props => {
3144
/>
3245
<Typography className="responder-name">{userInfo?.name}</Typography>
3346
</div>
34-
<FeedbackAnswerInput
35-
inputType={props.inputType}
36-
readOnly
37-
answer={props.answer}
38-
/>
47+
{props.inputType !== 'NONE' && (
48+
<FeedbackAnswerInput
49+
inputType={props.inputType}
50+
readOnly
51+
answer={getFormattedAnswer()} // Ensure the proper message is displayed
52+
/>
53+
)}
3954
</CardContent>
4055
</Card>
4156
);
4257
};
4358

4459
FeedbackResponseCard.propTypes = propTypes;
4560

46-
export default FeedbackResponseCard;
61+
export default FeedbackResponseCard;

web-ui/src/components/volunteer/Organizations.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ const Organizations = ({ onlyMe = false }) => {
203203
aria-label="Add Organization"
204204
onClick={addOrganization} // Open the dialog to add an organization
205205
>
206-
{console.log("Add Organization button rendered")}
207206
<AddCircleOutline />
208207
</IconButton>
209208
</div>

0 commit comments

Comments
 (0)