Skip to content

Commit 565d0ae

Browse files
Merge pull request #4969 from OneCommunityGlobal/venkataramanan_fix_assign_team_and_projects_modal_formatting
Venkataramanan fix assign team and projects modal formatting
2 parents a077404 + b1bc4c0 commit 565d0ae

3 files changed

Lines changed: 419 additions & 359 deletions

File tree

Lines changed: 156 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useMemo, useState } from 'react';
1+
import React, { useEffect, useState } from 'react';
22
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Alert, Input } from 'reactstrap';
33
import AddProjectsAutoComplete from './AddProjectsAutoComplete';
44
import { boxStyle, boxStyleDark } from '~/styles';
@@ -7,50 +7,22 @@ import { toast } from 'react-toastify';
77
import axios from 'axios';
88
import { ENDPOINTS } from '~/utils/URL';
99
import { useDispatch } from 'react-redux';
10-
1110
import { assignProject } from '~/actions/projectMembers';
12-
13-
const createUserProjectMembership = async (userId, projectId) => {
14-
// If your ENDPOINTS key is named differently, use that one.
15-
// Typical: POST /userProjects body: { userId, projectId, isActive }
16-
return axios.post(ENDPOINTS.USER_PROJECTS, {
17-
userId,
18-
projectId,
19-
isActive: true,
20-
});
21-
};
22-
/*const AddProjectPopup = React.memo(function AddProjectPopup(props) {
11+
12+
// eslint-disable-next-line react/display-name
13+
const AddProjectPopup = React.memo((props) => {
2314
const {
2415
open,
2516
onClose,
26-
userId,
2717
darkMode,
28-
// projects already in DB (for autocomplete / create-new check)
2918
projects = [],
30-
// projects already assigned to this user (to block duplicates)
3119
userProjects = [],
32-
// optional: parent callback to update its local table immediately
33-
onSelectAssignProject, // (project) => void
20+
userId,
21+
onSelectAssignProject,
3422
} = props;
35-
const safeProjects = Array.isArray(projects) ? projects : [];
36-
const safeUserProjects = Array.isArray(userProjects) ? userProjects : [];
37-
38-
// set data from props
39-
useEffect(() => {
40-
setAllProjects(safeProjects);
41-
const categories = Array.from(
42-
new Set(safeProjects.map(p => p?.category).filter(Boolean))
43-
);
44-
setCategoryOptions(categories.length ? categories : ['Unspecified']);
45-
}, [projects]);*/
46-
47-
// eslint-disable-next-line react/display-name
48-
const AddProjectPopup = React.memo(props => {
49-
const { darkMode, projects = [], onClose } = props;
5023

5124
const dispatch = useDispatch();
5225

53-
// ---------- local state ----------
5426
const [selectedProject, setSelectedProject] = useState(null);
5527
const [isValidProject, setIsValidProject] = useState(true);
5628
const [showDoesNotExistAlert, setShowDoesNotExistAlert] = useState(false);
@@ -60,30 +32,27 @@ const AddProjectPopup = React.memo(props => {
6032
const [searchText, setSearchText] = useState('');
6133
const [allProjects, setAllProjects] = useState([]);
6234

63-
// set data from props
6435
useEffect(() => {
6536
setAllProjects(projects || []);
66-
const categories = Array.from(new Set((projects || []).map(p => p.category).filter(Boolean)));
37+
const categories = Array.from(new Set((projects || []).map((p) => p.category).filter(Boolean)));
6738
setCategoryOptions(categories.length ? categories : ['Unspecified']);
6839
}, [projects]);
6940

70-
// reset validation each time the modal opens
7141
useEffect(() => {
7242
if (open) {
7343
setIsValidProject(true);
7444
setShowDoesNotExistAlert(false);
7545
setCreatingNew(false);
7646
setSelectedProject(null);
7747
setSearchText('');
48+
setNewProjectCategory('Unspecified');
7849
}
7950
}, [open]);
8051

81-
52+
const format = (s) => (s || '').toLowerCase().trim();
8253

83-
84-
// ---------- helpers ----------
85-
const format = s => (s || '').toLowerCase().trim();
86-
const projectByName = name => (allProjects || []).find(p => format(p.projectName) === format(name));
54+
const projectByName = (name) =>
55+
(allProjects || []).find((p) => format(p.projectName) === format(name));
8756

8857
const handleSelectProject = (project) => {
8958
setSelectedProject(project);
@@ -95,39 +64,42 @@ const AddProjectPopup = React.memo(props => {
9564
onClose?.();
9665
setCreatingNew(false);
9766
setShowDoesNotExistAlert(false);
67+
setSelectedProject(null);
68+
setSearchText('');
69+
setIsValidProject(true);
70+
};
71+
72+
const handleConfirm = async () => {
73+
if (!selectedProject) {
74+
setIsValidProject(false);
75+
toast.error('Please select a project from the list.');
76+
return;
77+
}
78+
79+
if (userProjects.some((p) => p?._id === selectedProject._id)) {
80+
setIsValidProject(false);
81+
toast.error('Great idea, but they already have that one! Pick another!');
82+
return;
83+
}
84+
85+
try {
86+
await dispatch(assignProject(selectedProject._id, userId, 'Assign'));
87+
onSelectAssignProject?.(selectedProject);
88+
onClose?.();
89+
} catch (e) {
90+
// eslint-disable-next-line no-console
91+
console.error('Error assigning project:', e);
92+
toast.error('Failed to assign project. Please try again.');
93+
}
9894
};
9995

100-
// ---------- Confirm existing project ----------
101-
const handleConfirm = async () => {
102-
if (!selectedProject) {
103-
setIsValidProject(false);
104-
toast.error('Please select a project from the list.');
105-
return;
106-
}
107-
if ((props.userProjects || []).some(p => p?._id === selectedProject._id)) {
108-
setIsValidProject(false);
109-
toast.error('Great idea, but they already have that one! Pick another!');
110-
return;
111-
}
112-
try {
113-
await dispatch(assignProject(selectedProject._id, props.userId, 'Assign'));
114-
// ✅ Make sure we're passing the complete project object
115-
props.onSelectAssignProject?.(selectedProject);
116-
props.onClose?.();
117-
} catch (e) {
118-
// eslint-disable-next-line no-console
119-
console.error('Error Assigning Project:', e);
120-
toast.error('Failed to assign project. Please try again.');
121-
}
122-
};
123-
124-
// ---------- Create new project, then confirm ----------
12596
const handleCreateNew = async () => {
12697
if (!searchText.trim()) {
12798
setIsValidProject(false);
12899
setSelectedProject(null);
129100
return;
130101
}
102+
131103
if (projectByName(searchText)) {
132104
toast.error('This project already exists.');
133105
return;
@@ -142,9 +114,9 @@ const AddProjectPopup = React.memo(props => {
142114
try {
143115
await axios.post(ENDPOINTS.PROJECTS, newProject);
144116
const res = await axios.get(ENDPOINTS.PROJECTS);
117+
145118
const created =
146-
(res.data || []).find(p => format(p.projectName) === format(searchText)) ||
147-
null;
119+
(res.data || []).find((p) => format(p.projectName) === format(searchText)) || null;
148120

149121
if (!created) {
150122
toast.success('Project created successfully, but it was not auto-selected.');
@@ -153,80 +125,141 @@ const AddProjectPopup = React.memo(props => {
153125
return;
154126
}
155127

156-
// select the newly created project so pressing Confirm assigns it
157128
setSelectedProject(created);
158129
setAllProjects(res.data || []);
159130
setCreatingNew(false);
160131
toast.success('Project created successfully');
161-
} catch {
132+
} catch (e) {
162133
toast.error('Project creation failed');
163134
}
164135
};
165136

166137
return (
167138
<Modal
168-
isOpen={props.open}
169-
toggle={onClose}
170-
// eslint-disable-next-line jsx-a11y/no-autofocus
171-
autoFocus={false}
139+
isOpen={open}
140+
toggle={close}
141+
centered
142+
size="lg"
172143
className={darkMode ? 'text-light dark-mode' : ''}
173144
>
174-
<ModalHeader className={darkMode ? 'bg-space-cadet' : ''} toggle={onClose}>
175-
{creatingNew ? 'Create' : ' Add'} Project{' '}
145+
<ModalHeader className={darkMode ? 'bg-space-cadet text-light' : ''} toggle={close}>
146+
{creatingNew ? 'Create Project' : 'Add Project'}
176147
</ModalHeader>
177148

178-
<ModalBody className={darkMode ? 'bg-yinmn-blue' : ''} style={{ textAlign: 'center' }}>
179-
<div className="input-group-prepend" style={{ marginBottom: 10 }}>
180-
<AddProjectsAutoComplete
181-
projectsData={allProjects}
182-
onDropDownSelect={handleSelectProject}
183-
selectedProject={selectedProject}
184-
setIsOpenDropdown={setCreatingNew}
185-
searchText={searchText}
186-
onInputChange={setSearchText}
187-
isSetUserIsNotSelectedAutoComplete={setShowDoesNotExistAlert}
188-
formatText={(s) => format(s).replace(/\s+/g, '')}
189-
/>
190-
<Button
191-
color={creatingNew ? 'success' : 'primary'}
192-
style={darkMode ? {} : { ...boxStyle, marginLeft: 5 }}
193-
onClick={creatingNew ? handleCreateNew : handleConfirm}
149+
<ModalBody
150+
className={darkMode ? 'bg-yinmn-blue text-light' : ''}
151+
style={{ padding: '1.5rem' }}
152+
>
153+
<div
154+
style={{
155+
display: 'flex',
156+
flexDirection: 'column',
157+
gap: '1rem',
158+
width: '100%',
159+
}}
160+
>
161+
<div
162+
style={{
163+
display: 'flex',
164+
alignItems: 'stretch',
165+
gap: '0.75rem',
166+
width: '100%',
167+
flexWrap: 'wrap',
168+
}}
194169
>
195-
{creatingNew ? 'Create' : 'Confirm'}
196-
</Button>
197-
</div>
198-
199-
{creatingNew && (
200-
<div className="input-group-prepend" style={{ marginBottom: 10, display: 'flex', gap: 10 }}>
201-
<Input type="select" value={newProjectCategory} onChange={e => setNewProjectCategory(e.target.value)}>
202-
{categoryOptions.map(opt => (
203-
<option key={opt}>{opt}</option>
204-
))}
205-
</Input>
170+
<div
171+
style={{
172+
flex: '1 1 420px',
173+
minWidth: '260px',
174+
}}
175+
>
176+
<AddProjectsAutoComplete
177+
projectsData={allProjects}
178+
onDropDownSelect={handleSelectProject}
179+
selectedProject={selectedProject}
180+
setIsOpenDropdown={setCreatingNew}
181+
searchText={searchText}
182+
onInputChange={setSearchText}
183+
isSetUserIsNotSelectedAutoComplete={setShowDoesNotExistAlert}
184+
formatText={(s) => format(s).replace(/\s+/g, '')}
185+
/>
186+
</div>
206187

207188
<Button
208-
color="danger"
209-
onClick={() => {
210-
setCreatingNew(false);
211-
setShowDoesNotExistAlert(false);
212-
setSearchText('');
189+
color={creatingNew ? 'success' : 'primary'}
190+
style={{
191+
...(darkMode ? {} : boxStyle),
192+
minWidth: '120px',
193+
height: '38px',
194+
alignSelf: 'stretch',
213195
}}
214-
style={{ width: '100%' }}
196+
onClick={creatingNew ? handleCreateNew : handleConfirm}
215197
>
216-
Cancel project creation
198+
{creatingNew ? 'Create' : 'Confirm'}
217199
</Button>
218200
</div>
219-
)}
220-
221-
{!isValidProject && selectedProject && (
222-
<Alert color="danger">Great idea, but they already have that one! Pick another!</Alert>
223-
)}
224-
{!isValidProject && !selectedProject && (
225-
<Alert color="danger">
226-
Hey, You need to {creatingNew ? 'write the name of' : 'pick'} a project first!
227-
</Alert>
228-
)}
229-
{showDoesNotExistAlert && <Alert color="danger">This project does not exist.</Alert>}
201+
202+
{creatingNew && (
203+
<div
204+
style={{
205+
display: 'flex',
206+
gap: '0.75rem',
207+
width: '100%',
208+
flexWrap: 'wrap',
209+
alignItems: 'stretch',
210+
}}
211+
>
212+
<Input
213+
type="select"
214+
value={newProjectCategory}
215+
onChange={(e) => setNewProjectCategory(e.target.value)}
216+
className={darkMode ? 'bg-darkmode-liblack border-0 text-light' : ''}
217+
style={{
218+
flex: '1 1 240px',
219+
minWidth: '220px',
220+
}}
221+
>
222+
{categoryOptions.map((opt) => (
223+
<option key={opt}>{opt}</option>
224+
))}
225+
</Input>
226+
227+
<Button
228+
color="danger"
229+
onClick={() => {
230+
setCreatingNew(false);
231+
setShowDoesNotExistAlert(false);
232+
setSearchText('');
233+
setSelectedProject(null);
234+
setIsValidProject(true);
235+
}}
236+
style={{
237+
minWidth: '180px',
238+
}}
239+
>
240+
Cancel project creation
241+
</Button>
242+
</div>
243+
)}
244+
245+
{!isValidProject && selectedProject && (
246+
<Alert color="danger" style={{ marginBottom: 0 }}>
247+
Great idea, but they already have that one! Pick another!
248+
</Alert>
249+
)}
250+
251+
{!isValidProject && !selectedProject && (
252+
<Alert color="danger" style={{ marginBottom: 0 }}>
253+
Hey, You need to {creatingNew ? 'write the name of' : 'pick'} a project first!
254+
</Alert>
255+
)}
256+
257+
{showDoesNotExistAlert && (
258+
<Alert color="danger" style={{ marginBottom: 0 }}>
259+
This project does not exist.
260+
</Alert>
261+
)}
262+
</div>
230263
</ModalBody>
231264

232265
<ModalFooter className={darkMode ? 'bg-yinmn-blue' : ''}>
@@ -238,4 +271,4 @@ const AddProjectPopup = React.memo(props => {
238271
);
239272
});
240273

241-
export default AddProjectPopup;
274+
export default AddProjectPopup;

0 commit comments

Comments
 (0)