diff --git a/src/components/UserProfile/TeamsAndProjects/AddProjectPopup.jsx b/src/components/UserProfile/TeamsAndProjects/AddProjectPopup.jsx
index 31901680bb..3ef5430073 100644
--- a/src/components/UserProfile/TeamsAndProjects/AddProjectPopup.jsx
+++ b/src/components/UserProfile/TeamsAndProjects/AddProjectPopup.jsx
@@ -113,7 +113,6 @@ const AddProjectPopup = React.memo(props => {
await dispatch(assignProject(selectedProject._id, props.userId, 'Assign'));
// ✅ Make sure we're passing the complete project object
props.onSelectAssignProject?.(selectedProject);
- toast.success(`Assigned to "${selectedProject.projectName}".`);
props.onClose?.();
} catch (e) {
// eslint-disable-next-line no-console
diff --git a/src/components/UserProfile/TeamsAndProjects/UserProjectsTable.jsx b/src/components/UserProfile/TeamsAndProjects/UserProjectsTable.jsx
index 14eb0a4c5c..4efcb5ef65 100644
--- a/src/components/UserProfile/TeamsAndProjects/UserProjectsTable.jsx
+++ b/src/components/UserProfile/TeamsAndProjects/UserProjectsTable.jsx
@@ -1,5 +1,6 @@
import React, { useState, useEffect, useMemo } from 'react';
-import { Button, Col, Tooltip , NavItem, UncontrolledTooltip } from 'reactstrap';
+import PropTypes from "prop-types";
+import { Button, Col, UncontrolledTooltip } from 'reactstrap';
import './TeamsAndProjects.css';
import hasPermission from '../../../utils/permissions';
// import styles from './UserProjectsTable.css';
@@ -33,7 +34,7 @@ const UserProjectsTable = React.memo(props => {
const currentRoute = location.pathname;
const isUserProfilePage = currentRoute === '/usermanagement';
- const toggleTooltip = () => setTooltip(!tooltipOpen);
+ // const toggleTooltip = () => setTooltip(!tooltipOpen);
//Situation can be all, active, or complete
const filterTasksAndUpdateFilter = situation => {
@@ -42,7 +43,7 @@ const UserProjectsTable = React.memo(props => {
const sortedTasksByNumber = useMemo(() => {
return userTasks?.sort((task1, task2) => task1.num - task2.num);
- }, [userTasks]);
+}, [userTasks]);
const tasksByProject = userProjects?.map(project => {
const tasks = sortedTasksByNumber?.filter(task => task.projectId?.includes(project.projectId));
@@ -81,26 +82,35 @@ const UserProjectsTable = React.memo(props => {
setFilteredTasks(() => filterTasksByUserTaskSituation(actualType));
}, [sortedTasksByNumber, actualType]);
- const removeOrAddTaskFromUser = (task, method) => {
- const newResources = task.resources?.map(resource => {
- if (resource.userID === props.userId) {
- return {
- ...resource,
- completedTask: method === 'remove'
- };
+const removeOrAddTaskFromUser = (task, method) => {
+
+ let newResources = task.resources;
+
+ if (method === "remove") {
+ // REMOVE the user completely
+ newResources = task.resources.filter(r => r.userID !== props.userId);
+ } else if (method === "add") {
+ // RE-ADD user as uncompleted resource
+ newResources = [
+ ...task.resources,
+ {
+ userID: props.userId,
+ completedTask: method === 'remove',
+ reviewStatus: "Unsubmitted",
+ startedDatetime: new Date().toISOString()
}
- return resource;
- });
-
- const updatedTask = {
- ...task,
+ ];
+ }
+
+ const updatedTask = {
+ ...task,
resources: newResources,
status: method === 'remove' ? 'Complete' : 'Started'
- };
-
- props.updateTask(task._id, updatedTask, method);
};
+ props.updateTask(task._id, updatedTask, method);
+};
+
//For updating tasks visually but not saving until user clicks save changes
const deleteTasksTemporarily = (project_id) => {
setFilteredTasks(filteredTasks?.filter(project => project.projectId !== project_id ));
@@ -108,7 +118,7 @@ const UserProjectsTable = React.memo(props => {
useEffect(()=>{
setFilteredTasks(() => filterTasksByUserTaskSituation('active'));
- }, [props.userProjectsById])
+ }, [props.userProjectsById], filteredTasks);
return (
@@ -126,9 +136,9 @@ const UserProjectsTable = React.memo(props => {
{props.disabled ? (
<>
-
+ {/*
Please save changes before assign project
-
+ */}
@@ -380,7 +390,7 @@ const UserProjectsTable = React.memo(props => {
{props.userProjectsById.length > 0 ? (
- tasksByProject?.map((project, index) => (
+ filteredTasks?.map((project, index) => (
| {index + 1} |
{`${project.projectName}`} |
@@ -391,7 +401,6 @@ const UserProjectsTable = React.memo(props => {
disabled={!canUpdateTask}
onClick={e => {
props.onDeleteClick(project.projectId);
- deleteTasksTemporarily(project.projectId);
}}
style={darkMode ? boxStyleDark : boxStyle}
>
@@ -513,4 +522,15 @@ const UserProjectsTable = React.memo(props => {
);
});
-export default connect(null, { hasPermission })(UserProjectsTable);
\ No newline at end of file
+export default connect(null, { hasPermission })(UserProjectsTable);
+UserProjectsTable.propTypes = {
+ userId: PropTypes.string,
+ userProjectsById: PropTypes.array,
+ userTasks: PropTypes.array,
+ role: PropTypes.string,
+ edit: PropTypes.bool,
+ hasPermission: PropTypes.func,
+ onDeleteClick: PropTypes.func,
+ updateTask: PropTypes.func,
+ darkMode: PropTypes.bool,
+};
diff --git a/src/components/UserProfile/UserProfile.jsx b/src/components/UserProfile/UserProfile.jsx
index 30ba398c3b..82424a49fb 100644
--- a/src/components/UserProfile/UserProfile.jsx
+++ b/src/components/UserProfile/UserProfile.jsx
@@ -555,62 +555,111 @@ function UserProfile(props) {
setTeams(prevTeams => prevTeams.filter(team => team._id !== deletedTeamId));
};
- const onDeleteProject = deletedProjectId => {
- setProjects(prevProject => prevProject.filter(project => project._id !== deletedProjectId));
+ const onDeleteProject = async (deletedProjectId) => {
+
+ const removedProject = projects.find(p => (p._id || p.projectId) === deletedProjectId);
+
+ const updatedProjects = projects.filter(p => {
+ return (p._id || p.projectId) !== deletedProjectId;
+ });
+
+ setProjects(updatedProjects);
+
+ // Prepare backend payload
+ const updatedUserProfile = {
+ ...userProfileRef.current,
+ projects: updatedProjects.map(p => String(p._id || p.projectId)),
};
+ try {
+ await handleSubmit(updatedUserProfile); // this already toasts success
+ toast.success(`User removed from Project "${removedProject?.projectName || 'Unknown'}"`);
+ } catch (e) {
+ toast.error('Failed to remove project, please try again.');
+ console.error(e);
+ }
+ return updatedProjects;
+};
+
+
const onAssignTeam = assignedTeam => {
setTeams(prevState => [...prevState, assignedTeam]);
};
-const onAssignProject = assignedProject => {
- // eslint-disable-next-line no-console
- console.log("Adding project to state:", assignedProject);
+const onAssignProject = async (assignedProject) => {
+ const projectId = assignedProject._id || assignedProject.projectId;
+
+ // Avoid duplicates
+ const currentProjects = Array.isArray(projects) ? projects : [];
+ if (currentProjects.some(p => (p._id || p.projectId) === projectId)) {
+ toast.info(`Project "${assignedProject.projectName || 'Unknown'}" already assigned to this user`);
+ return;
+ }
+
+ const updatedProjects = [...currentProjects, assignedProject];
+ setProjects(updatedProjects);
+
+ const updatedUserProfile = {
+ ...userProfileRef.current,
+ projects: updatedProjects.map(p => String(p._id || p.projectId)),
+ };
+
+ try {
+ await handleSubmit(updatedUserProfile); // reuses same pipeline
+ toast.success(`User assigned to Project "${assignedProject.projectName || 'Unknown'}"`);
+ } catch (e) {
+ toast.error('Failed to assign project, please try again.');
+ console.error(e);
+ }
+ return updatedProjects;
+};
+
+const onUpdateTask = async (taskId, updatedTask, method) => {
- // Always create a new array to trigger React re-render
- setProjects(prevProjects =>
- {
- // Ensure prevProjects is an array
- const currentProjects = Array.isArray(prevProjects) ? prevProjects : [];
-
- if (currentProjects.some(proj => proj._id === assignedProject._id)) {
- // eslint-disable-next-line no-console
- console.log("Project already exists, not adding duplicate");
- return currentProjects;
+ let newTasks;
+
+ if (method === 'remove') {
+ try{
+ newTasks = tasks.filter(t => t._id !== taskId);
+ const res = await axios.delete(ENDPOINTS.TASK_DELETE_BY_ID(taskId, userProfile._id));
+ if (res.status === 200) {
+ setTasks(newTasks);
+ toast.success('Task removed successfully');
+ } else {
+ toast.error('Failed to remove task');
}
-
- // Add project and log the new state
- // eslint-disable-next-line no-console
- console.log("Adding new project:", assignedProject.projectName);
- const newProjects = [...currentProjects, assignedProject];
- // eslint-disable-next-line no-console
- console.log("Updated projects state:", newProjects);
- return newProjects; // Return the new array with the project added
- });
+ return newTasks;
+ } catch (e) {
+ toast.error('Failed to remove task, please try again.');
+ console.error(e);
+ return tasks;
+ }
+ } else {
+ // UPDATE the task normally
+ newTasks = tasks.map(t =>
+ t._id === taskId ? updatedTask : t
+ );
+ }
+ setTasks(newTasks);
+
+ const updatedUserProfile = {
+ ...userProfileRef.current,
+ tasks: newTasks
};
- const onUpdateTask = (taskId, updatedTask) => {
- const newTask = {
- updatedTask,
- taskId,
- };
- setTasks(tasks => {
- const tasksWithoutTheUpdated = [...tasks];
- const taskIndex = tasks.findIndex(task => task._id === taskId);
- tasksWithoutTheUpdated[taskIndex] = updatedTask;
- return tasksWithoutTheUpdated;
- });
+setUpdatedTasks(prev => {
+ const others = prev.filter(t => t.taskId !== taskId);
+ return [...others, { taskId, updatedTask }];
+});
- if (updatedTasks.findIndex(task => task.taskId === taskId) !== -1) {
- const taskIndex = updatedTasks.findIndex(task => task.taskId === taskId);
- const tasksToUpdate = updatedTasks;
- tasksToUpdate.splice(taskIndex, 1);
- tasksToUpdate.splice(taskIndex, 0, newTask);
- setUpdatedTasks(tasksToUpdate);
- } else {
- setUpdatedTasks(tasks => [...tasks, newTask]);
- }
- };
+ try {
+ await handleSubmit(updatedUserProfile);
+ toast.success("Task updated");
+ } catch (e) {
+ toast.error("Failed to update task");
+ console.error(e);
+ }
+};
const handleImageUpload = async evt => {
if (evt) evt.preventDefault();
@@ -1382,15 +1431,6 @@ const onAssignProject = assignedProject => {
- {/* }
- {!isProfileEqual ||
- !isTasksEqual ||
- !isProjectsEqual ? (
-
- Please click on "Save changes" to save the changes you have made.{' '}
-
- ) : null}
- */}
{!codeValid ? (
NOT SAVED! The code must be between 5 and 7 characters long
@@ -1741,12 +1781,13 @@ const onAssignProject = assignedProject => {
userId={props.match.params.userId}
updateTask={onUpdateTask}
handleSubmit={handleSubmit}
- disabled={
- !formValid.firstName ||
- !formValid.lastName ||
- !formValid.email ||
- !(isProfileEqual && isTasksEqual && isProjectsEqual)
- }
+ // disabled={
+ // !formValid.firstName ||
+ // !formValid.lastName ||
+ // !formValid.email ||
+ // !(isProfileEqual && isTasksEqual && isProjectsEqual)
+ // }
+ disabled={false}
darkMode={darkMode}
/>
)
@@ -1797,7 +1838,7 @@ const onAssignProject = assignedProject => {
)}
- {((canEdit && activeTab) || canEditTeamCode) && (
+ {((canEdit && activeTab) || canEditTeamCode) && activeTab !== '4' && (
<>
{
!formValid.email ||
!codeValid ||
(userStartDate > userEndDate && userEndDate !== '') ||
- // titleOnSet ||
(isProfileEqual && isTasksEqual && isProjectsEqual)
}
userProfile={userProfile}
@@ -2184,12 +2224,13 @@ const onAssignProject = assignedProject => {
userId={props.match.params.userId}
updateTask={onUpdateTask}
handleSubmit={handleSubmit}
- disabled={
- !formValid.firstName ||
- !formValid.lastName ||
- !formValid.email ||
- !(isProfileEqual && isTasksEqual && isProjectsEqual)
- }
+ // disabled={
+ // !formValid.firstName ||
+ // !formValid.lastName ||
+ // !formValid.email ||
+ // !(isProfileEqual && isTasksEqual && isProjectsEqual)
+ // }
+ disabled={false}
darkMode={darkMode}
/>
diff --git a/src/utils/URL.js b/src/utils/URL.js
index 0b6952e83d..45ea4834a1 100644
--- a/src/utils/URL.js
+++ b/src/utils/URL.js
@@ -101,6 +101,7 @@ export const ENDPOINTS = {
TASK_DEL: (taskId, motherId) => `${APIEndpoint}/task/del/${taskId}/${motherId}`,
GET_TASK: taskId => `${APIEndpoint}/task/${taskId}`,
TASK_UPDATE: taskId => `${APIEndpoint}/task/update/${taskId}`,
+ TASK_DELETE_BY_ID: (taskId, userId) => `${APIEndpoint}/task/deleteTask/${taskId}/${userId}`,
TASK_UPDATE_STATUS: taskId => `${APIEndpoint}/task/updateStatus/${taskId}`,
TASK_CHANGE_LOGS: taskId => `${APIEndpoint}/task/${taskId}/changeLogs`,
DELETE_CHILDREN: taskId => `${APIEndpoint}/task/delete/children/${taskId}`,