Skip to content

Commit 86a2ef6

Browse files
Merge pull request #4835 from OneCommunityGlobal/Sayali_HGN_Feedback_Modal_Fix
Sayali - Fix: HGN Feedback Modal Service Worker Registration Failure
2 parents 74c7355 + b35ed82 commit 86a2ef6

5 files changed

Lines changed: 788 additions & 77 deletions

File tree

src/components/FeedbackModal/FeedbackModal.jsx

Lines changed: 66 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
1-
import { useState, useEffect, useRef } from 'react';
1+
import axios from 'axios';
2+
import { useEffect, useRef, useState } from 'react';
3+
import { useSelector } from 'react-redux';
4+
import { toast } from 'react-toastify';
25
import {
3-
Modal,
4-
ModalHeader,
5-
ModalBody,
6-
ModalFooter,
76
Button,
87
FormGroup,
9-
Label,
108
Input,
9+
Label,
10+
Modal,
11+
ModalBody,
12+
ModalFooter,
13+
ModalHeader,
1114
} from 'reactstrap';
12-
import { useSelector } from 'react-redux';
13-
import axios from 'axios';
1415
import { ENDPOINTS } from '../../utils/URL';
1516
import styles from './FeedbackModal.module.css';
16-
import StarRating from './StarRating';
1717
import MemberSearchBar from './MemberSearchBar';
18+
import StarRating from './StarRating';
1819

1920
function FeedbackModal() {
2021
const darkMode = useSelector(state => state.theme.darkMode);
2122
const userProfile = useSelector(state => state.userProfile);
22-
const [isOpen, setIsOpen] = useState(true);
23+
const [isOpen, setIsOpen] = useState(false); // ← start as false, let API decide
2324
const [receivedHelp, setReceivedHelp] = useState('');
2425
const [ratedMembers, setRatedMembers] = useState([{ id: 'active-1', name: '', rating: 0 }]);
2526
const [inactiveRatedMembers, setInactiveRatedMembers] = useState([
@@ -30,24 +31,40 @@ function FeedbackModal() {
3031
const [activeUsers, setActiveUsers] = useState([]);
3132
const [inactiveUsers, setInactiveUsers] = useState([]);
3233

33-
// Use refs to maintain counters for unique IDs
3434
const nextActiveIdRef = useRef(2);
3535
const nextInactiveIdRef = useRef(2);
3636

37-
// Fetch user names from API
37+
// Check backend if modal should show
38+
useEffect(() => {
39+
const checkHelpRequest = async () => {
40+
try {
41+
const response = await axios.get(ENDPOINTS.QUESTIONNAIRE_CHECK_MODAL(userProfile._id));
42+
if (response.data.showModal) {
43+
setIsOpen(true);
44+
} else {
45+
setIsOpen(false);
46+
}
47+
} catch (error) {
48+
// eslint-disable-next-line no-console
49+
console.error('Error checking help request:', error);
50+
setIsOpen(false);
51+
}
52+
};
53+
54+
if (userProfile?._id) {
55+
checkHelpRequest();
56+
}
57+
}, [userProfile]);
58+
59+
// Fetch active and inactive user names
3860
useEffect(() => {
3961
const fetchUserNames = async () => {
4062
try {
4163
const response = await axios.get(ENDPOINTS.QUESTIONNAIRE_USER_NAMES_LIST());
4264
if (response.data && response.data.users) {
4365
const { users } = response.data;
44-
45-
// Separate active and inactive users
46-
const active = users.filter(user => user.isActive);
47-
const inactive = users.filter(user => !user.isActive);
48-
49-
setActiveUsers(active);
50-
setInactiveUsers(inactive);
66+
setActiveUsers(users.filter(user => user.isActive));
67+
setInactiveUsers(users.filter(user => !user.isActive));
5168
}
5269
} catch (error) {
5370
// eslint-disable-next-line no-console
@@ -58,41 +75,44 @@ function FeedbackModal() {
5875
fetchUserNames();
5976
}, []);
6077

61-
// Placeholder for getting help request status
62-
useEffect(() => {
63-
// In a real implementation, this would check if a help request was made a week ago
64-
// For now, we're just showing the modal when navigating to the dashboard
65-
const checkHelpRequest = () => {
66-
// This would be replaced with actual API call to check if user made a help request a week ago
67-
const hasUncompletedFeedback = localStorage.getItem('feedbackNeeded') === 'true';
68-
const feedbackCompleted = localStorage.getItem('feedbackCompleted') === 'true';
69-
70-
if (hasUncompletedFeedback && !feedbackCompleted) {
71-
setIsOpen(true);
72-
} else {
73-
setIsOpen(false);
74-
}
75-
};
76-
77-
// Set feedback needed to true for demo purposes
78-
if (localStorage.getItem('feedbackNeeded') === null) {
79-
localStorage.setItem('feedbackNeeded', 'true');
80-
}
81-
82-
checkHelpRequest();
83-
}, []);
84-
8578
const handleSubmit = async () => {
8679
if (!userProfile || !userProfile._id) {
8780
// eslint-disable-next-line no-console
8881
console.error('User ID not available');
8982
return;
9083
}
9184

85+
// Validate that all filled member names are valid users from the list
86+
const allUsers = [...activeUsers, ...inactiveUsers];
87+
88+
const invalidActive = ratedMembers.find(
89+
m =>
90+
m.name.trim() !== '' &&
91+
!allUsers.some(
92+
u => `${u.firstName} ${u.lastName}`.toLowerCase() === m.name.trim().toLowerCase(),
93+
),
94+
);
95+
96+
const invalidInactive = inactiveRatedMembers.find(
97+
m =>
98+
m.name.trim() !== '' &&
99+
!allUsers.some(
100+
u => `${u.firstName} ${u.lastName}`.toLowerCase() === m.name.trim().toLowerCase(),
101+
),
102+
);
103+
104+
if (invalidActive || invalidInactive) {
105+
toast.warn('Please select valid members from the dropdown only.');
106+
return;
107+
}
108+
console.log('allUsers:', allUsers);
109+
console.log('ratedMembers:', ratedMembers);
110+
console.log('invalidActive:', invalidActive);
111+
console.log('invalidInactive:', invalidInactive);
112+
92113
setIsSubmitting(true);
93114

94115
try {
95-
// Format the rated members data for the API
96116
const peopleYouContacted = [...ratedMembers, ...inactiveRatedMembers]
97117
.filter(member => member.name.trim() !== '')
98118
.map(member => ({
@@ -101,7 +121,6 @@ function FeedbackModal() {
101121
isActive: !member.id.includes('inactive'),
102122
}));
103123

104-
// Prepare the request payload
105124
const payload = {
106125
userId: userProfile._id,
107126
haveYouRecievedHelpLastWeek: receivedHelp,
@@ -111,11 +130,7 @@ function FeedbackModal() {
111130
daterequestedFeedback: new Date().toISOString(),
112131
};
113132

114-
// Make the API call
115133
await axios.post(ENDPOINTS.QUESTIONNAIRE_FEEDBACK_REQUEST(), payload);
116-
117-
// Mark feedback as completed
118-
localStorage.setItem('feedbackCompleted', 'true');
119134
setIsOpen(false);
120135
} catch (error) {
121136
// eslint-disable-next-line no-console
@@ -135,17 +150,12 @@ function FeedbackModal() {
135150
setIsSubmitting(true);
136151

137152
try {
138-
// Prepare the request payload
139153
const payload = {
140154
userId: userProfile._id,
141155
foundHelpSomeWhereClosePermanently: true,
142156
};
143157

144-
// Make the API call
145158
await axios.post(ENDPOINTS.QUESTIONNAIRE_CLOSE_PERMANENTLY(), payload);
146-
147-
// Mark feedback as completed and prevent it from showing again
148-
localStorage.setItem('feedbackCompleted', 'true');
149159
setIsOpen(false);
150160
} catch (error) {
151161
// eslint-disable-next-line no-console
@@ -169,14 +179,10 @@ function FeedbackModal() {
169179

170180
const removeMember = (id, isInactive = false) => {
171181
if (isInactive) {
172-
// Ensure we don't remove the last entry
173182
if (inactiveRatedMembers.length <= 1) return;
174-
175183
setInactiveRatedMembers(inactiveRatedMembers.filter(member => member.id !== id));
176184
} else {
177-
// Ensure we don't remove the last entry
178185
if (ratedMembers.length <= 1) return;
179-
180186
setRatedMembers(ratedMembers.filter(member => member.id !== id));
181187
}
182188
};
@@ -208,19 +214,12 @@ function FeedbackModal() {
208214
};
209215

210216
const openFeedbackSuggestions = () => {
211-
// Set a flag in localStorage with a timestamp to ensure it's recognized as a new request
212-
// This ensures repeated clicks will work and helps prevent unexpected popups
213217
localStorage.setItem('openSuggestionsModal', Date.now().toString());
214-
215-
// Close the current feedback modal
216218
setIsOpen(false);
217219

218-
// If we're not on the dashboard, redirect there
219220
if (!window.location.hash.includes('/dashboard')) {
220221
window.location.href = '/#/dashboard';
221222
} else {
222-
// If we're already on the dashboard, force a refresh of the SummaryBar
223-
// by dispatching a custom event that it can listen for
224223
window.dispatchEvent(new CustomEvent('openSuggestionModal'));
225224
}
226225
};
@@ -366,7 +365,9 @@ function FeedbackModal() {
366365
placeholder="Want to send a special shout out to someone who helped you? Let us know here!"
367366
value={comments}
368367
onChange={e => setComments(e.target.value)}
368+
maxLength={1000}
369369
/>
370+
<small className="text-muted">{comments.length}/1000 characters</small>
370371
</div>
371372

372373
<div className={`${styles.suggestionsLink} mt-3 ${styles.textCenter}`}>

src/components/FeedbackModal/MemberSearchBar.jsx

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { useState, useEffect } from 'react';
21
import PropTypes from 'prop-types';
2+
import { useEffect, useState } from 'react';
33
import { Input, ListGroup, ListGroupItem } from 'reactstrap';
44
import styles from './MemberSearchBar.module.css';
55

@@ -10,11 +10,9 @@ function MemberSearchBar({ id, value, onChange, inactive, usersList = [] }) {
1010
const [loading, setLoading] = useState(false);
1111

1212
useEffect(() => {
13-
// Set the value of the input when the value prop changes
1413
setSearchTerm(value);
1514
}, [value]);
1615

17-
// Filter users based on search term
1816
const filterUsers = searchText => {
1917
if (!searchText.trim() || !usersList || usersList.length === 0) {
2018
setSuggestions([]);
@@ -25,18 +23,13 @@ function MemberSearchBar({ id, value, onChange, inactive, usersList = [] }) {
2523

2624
try {
2725
const lowerSearchText = searchText.toLowerCase();
28-
29-
// Filter users based on first name or last name containing the search text
3026
const filteredUsers = usersList.filter(user =>
3127
`${user.firstName} ${user.lastName}`.toLowerCase().includes(lowerSearchText),
3228
);
33-
34-
// Format users for display
3529
const formattedUsers = filteredUsers.map(user => ({
3630
...user,
3731
fullName: `${user.firstName} ${user.lastName}`,
3832
}));
39-
4033
setSuggestions(formattedUsers);
4134
} catch (error) {
4235
// eslint-disable-next-line no-console
@@ -52,7 +45,6 @@ function MemberSearchBar({ id, value, onChange, inactive, usersList = [] }) {
5245
setSearchTerm(inputValue);
5346
onChange(inputValue);
5447

55-
// Debounce the filtering
5648
const timeoutId = setTimeout(() => {
5749
filterUsers(inputValue);
5850
setShowSuggestions(true);
@@ -68,6 +60,25 @@ function MemberSearchBar({ id, value, onChange, inactive, usersList = [] }) {
6860
setShowSuggestions(false);
6961
};
7062

63+
// Validate on blur — clear if typed text doesn't match any user in the list
64+
const handleBlur = () => {
65+
setTimeout(() => {
66+
setShowSuggestions(false);
67+
68+
if (searchTerm.trim() !== '') {
69+
const isValidUser = usersList.some(
70+
user =>
71+
`${user.firstName} ${user.lastName}`.toLowerCase() === searchTerm.trim().toLowerCase(),
72+
);
73+
74+
if (!isValidUser) {
75+
setSearchTerm('');
76+
onChange('');
77+
}
78+
}
79+
}, 200);
80+
};
81+
7182
return (
7283
<div className={`${styles.memberSearchContainer}`}>
7384
<Input

0 commit comments

Comments
 (0)