diff --git a/src/components/Activities/InfraPlanning.jsx b/src/components/Activities/InfraPlanning.jsx index 084fd562..ef417ded 100644 --- a/src/components/Activities/InfraPlanning.jsx +++ b/src/components/Activities/InfraPlanning.jsx @@ -52,49 +52,7 @@ const InfraPlanning = () => ))} - {/*
- - - - -
*/}
{project_deatilsLoader && (

Loading...

)} diff --git a/src/components/Project/Infrastructure/EditActivityModal.jsx b/src/components/Project/Infrastructure/EditActivityModal.jsx index 44d7b87e..a0077e26 100644 --- a/src/components/Project/Infrastructure/EditActivityModal.jsx +++ b/src/components/Project/Infrastructure/EditActivityModal.jsx @@ -47,7 +47,8 @@ const EditActivityModal = ({ completedWork: 0, }; - const { projects_Details, refetch } = useProjectDetails(selectedProject); + const {projects_Details, refetch} = useProjectDetails( selectedProject ); + const [ActivityUnit,setActivityUnit]= useState("") const { activities, loading, error } = useActivitiesMaster(); const [formData, setFormData] = useState(defaultModel); const [selectedActivity, setSelectedActivity] = useState(null); @@ -170,9 +171,14 @@ const EditActivityModal = ({ const ISselectedActivity = watch("activityID"); useEffect(() => { - const selected = activities.find((a) => a.id === ISselectedActivity); - setSelectedActivity(selected || null); - }, [ISselectedActivity]); + if( ISselectedActivity ){ + const selected = activities.find((a) => a.id === ISselectedActivity); + setSelectedActivity( selected || null ); + setActivityUnit(selected?.unitOfMeasurement) + } + }, [ ISselectedActivity,activities] ); + + return (
@@ -257,7 +263,7 @@ const EditActivityModal = ({ {activity.activityName} ))} - {!loading && activityData.length === 0 && ( + {!loading && activities.length === 0 && ( )} @@ -320,7 +326,7 @@ const EditActivityModal = ({ {/* )} */}
-
@@ -37,7 +37,7 @@ const WorkArea = ({ workArea, floor, forBuilding }) => { Work Area:  {" "} - + {workArea.areaName}
diff --git a/src/components/Project/Infrastructure/WorkItem.jsx b/src/components/Project/Infrastructure/WorkItem.jsx index f7bc65db..140eb671 100644 --- a/src/components/Project/Infrastructure/WorkItem.jsx +++ b/src/components/Project/Infrastructure/WorkItem.jsx @@ -5,14 +5,28 @@ import EditActivityModal from "./EditActivityModal"; import { useHasUserPermission } from "../../../hooks/useHasUserPermission"; import { MANAGE_PROJECT_INFRA, MANAGE_TASK } from "../../../utils/constants"; +import ConfirmModal from "../../common/ConfirmModal"; +import ProjectRepository from '../../../repositories/ProjectRepository' +import {useProjectDetails} from '../../../hooks/useProjects' +import showToast from "../../../services/toastService"; +import {cacheData, getCachedData} from "../../../slices/apiDataManager"; +import {useDispatch} from "react-redux"; +import {refreshData} from "../../../slices/localVariablesSlice"; + const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => { const { projectId } = useParams(); const [itemName, setItemName] = useState(""); const [NewWorkItem, setNewWorkItem] = useState(); const [isModalOpen, setIsModalOpen] = useState(false); - const [showModal, setShowModal] = useState(false); + const [ showModal, setShowModal ] = useState( false ); + const [showModal2, setShowModal2] = useState(false); const ManageTasks = useHasUserPermission(MANAGE_TASK); - const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA); + const ManageInfra = useHasUserPermission( MANAGE_PROJECT_INFRA ); + const [ loadingDelete, setLoadingDelete ] = useState( false ) + const project = getCachedData("projectInfo"); + const dispatch = useDispatch() + + const {projects_Details} = useProjectDetails(projectId || project?.projectId) const openModal = () => setIsModalOpen(true); const closeModal = () => setIsModalOpen(false); @@ -41,10 +55,75 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => { document.querySelectorAll('[data-bs-toggle="tooltip"]') ); tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el)); - }, []); + }, [] ); + + const showModal1 = () => setShowModal(true); - const closeModal1 = () => setShowModal(false); + const closeModal1 = () => setShowModal( false ); + const showModalDelete = () => setShowModal2(true); + const closeModalDelete = () => setShowModal2( false ); + + + const handleSubmit = async() => { + setLoadingDelete(true); + try + { + + const updatedProject = { ...projects_Details }; + const response = await ProjectRepository.deleteProjectTask( workItem.workItemId ); + debugger + const newProject = { + ...updatedProject, + buildings: updatedProject.buildings.map((building) => + building.id === forBuilding?.id + ? { + ...building, + floors: building.floors.map((floor) => + floor.id === forFloor?.id + ? { + ...floor, + workAreas: floor.workAreas.map((workArea) => + workArea.id === forWorkArea?.id + ? { + ...workArea, + workItems: workArea.workItems.filter( + (item) => + String(item?.workItem?.id ?? item?.id) !== + String(workItem.workItemId) + ), + } + : workArea + ), + } + : floor + ), + } + : building + ), + }; + + cacheData("projectInfo", { + projectId: newProject.id, + data: newProject, + }); + + dispatch( refreshData( true ) ); + closeModalDelete() + setLoadingDelete(false) + showToast("Activity Deleted Successfully","success") + } catch ( error ) + { + setLoadingDelete( false ) + closeModalDelete() + const message = + error.response?.data?.message || + error.message || + "An unexpected error occurred"; + showToast( message, "error" ); + + } + }; return ( <> {isModalOpen && ( @@ -73,6 +152,17 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => { floor={forFloor} onClose={closeModal1} /> +
} + + + {showModal2 &&
+
} @@ -164,7 +254,7 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => { aria-label="Delete" type="button" className="btn p-0 dropdown-toggle hide-arrow" - onClick={showModal1} + onClick={showModalDelete} > { + const selectRef = useSelect({ minimumResultsForSearch: 5 }); + + return ( +
+ + + {error &&

{error.message}

} +
+ ); +}; + +export default ActivityDropdown; diff --git a/src/components/common/ConfirmModal.jsx b/src/components/common/ConfirmModal.jsx new file mode 100644 index 00000000..a92cf3af --- /dev/null +++ b/src/components/common/ConfirmModal.jsx @@ -0,0 +1,77 @@ +import React, { useState } from 'react'; + +const ConfirmModal = ({ type, onSubmit, onClose, message, loading ,header}) => { + + const TypeofIcon = (type) => { + switch (type) { + case "delete": + return ; + default: + return null; + } + }; + + const TypeofModal = (type) => { + switch (type) { + case "delete": + return "sm"; + case "other": + return "md"; + default: + return "sm"; // Return a default modal size + } + }; + + return ( +
+
+
+
+
+ +
+ +
+ {header && < strong className='mb-0 font-weight-bold'>{header }} +
+ +
+
{TypeofIcon(type)}
+
+ {message} +
+ + +
+
+
+ + +
+
+
+
+
+
+ ); +}; + +export default ConfirmModal; diff --git a/src/components/common/GlobalModal/CommentEditor.css b/src/components/common/GlobalModal/CommentEditor.css new file mode 100644 index 00000000..9bc05902 --- /dev/null +++ b/src/components/common/GlobalModal/CommentEditor.css @@ -0,0 +1,42 @@ +.marco-quill-wrapper { + border: 1px solid #dcdfe4; + border-radius: 6px; + background: #fff; + padding: 4px; + font-family: "Segoe UI", sans-serif; + position: absolute; + bottom: 0; + } + + /* Style the toolbar */ + .marco-quill-wrapper .ql-toolbar { + border: none; + padding: 4px 8px; + background: #f4f5f7; + border-bottom: 1px solid #dcdfe4; + border-radius: 6px 6px 0 0; + } + + /* Style the editor */ + .marco-quill-wrapper .ql-container { + border: none; + } + + .jira-quill-wrapper .ql-editor { + min-height: 100px; + padding: 8px; + font-size: 14px; + line-height: 1.6; + } + + /* Optional: Customize hover/focus */ + .marco-quill-wrapper .ql-toolbar button:hover { + background-color: #ebecf0; + } + + .marco-quill-wrapper .ql-toolbar button.ql-active { + background-color: #d2e0f7; + } + .marco-quill-wrapper .quill{ + padding: 0px; + } \ No newline at end of file diff --git a/src/components/common/GlobalModal/CommentEditor.jsx b/src/components/common/GlobalModal/CommentEditor.jsx new file mode 100644 index 00000000..68d4e353 --- /dev/null +++ b/src/components/common/GlobalModal/CommentEditor.jsx @@ -0,0 +1,54 @@ +// src/components/CommentEditor.jsx +import React, { useState } from "react"; +import ReactQuill from "react-quill"; +import "react-quill/dist/quill.snow.css"; // Core styling +import "./CommentEditor.css"; // Custom styles + +const modules = { + toolbar: [ + [{ header: [1, 2, false] }], + ["bold", "italic", "underline"], + [{ list: "ordered" }, { list: "bullet" }], + ["link"], + ["clean"], + ], +}; + +const formats = [ + "header", + "bold", + "italic", + "underline", + "list", + "bullet", + "link", +]; + +const CommentEditor = () => { + const [value, setValue] = useState(""); + + const handleSubmit = () => { + console.log("Comment:", value); + // Submit or handle content + }; + + return ( +
+ +
+ +
+
+ ); +}; + +export default CommentEditor; diff --git a/src/components/common/GlobalModal/CreateIssue.jsx b/src/components/common/GlobalModal/CreateIssue.jsx new file mode 100644 index 00000000..9d090df8 --- /dev/null +++ b/src/components/common/GlobalModal/CreateIssue.jsx @@ -0,0 +1,174 @@ +import React,{useState} from "react"; +import CommentEditor from "./CommentEditor"; +import ReactQuill from "react-quill"; +import "react-quill/dist/quill.snow.css"; +import "./CommentEditor.css"; + +const jiraModules = { + toolbar: [ + ["bold", "italic", "underline", "strike"], + ["link", "code"], + [{ list: "bullet" }], + ], + }; + + const jiraFormats = [ + "bold", + "italic", + "underline", + "strike", + "link", + "code", + "list", + ]; + +const CreateIssue = () => +{ + const [value, setValue] = useState(""); + return ( + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+ +
+
+ + +
+ ); +}; + +export default CreateIssue; diff --git a/src/components/common/useSelect.js b/src/components/common/useSelect.js new file mode 100644 index 00000000..6b40b2e4 --- /dev/null +++ b/src/components/common/useSelect.js @@ -0,0 +1,32 @@ +import { useEffect, useRef } from "react"; + +const useSelect= (selector, options = {}) => { + const selectRef = useRef(null); + + useEffect(() => { + const $ = window.$; + const node = selectRef.current; + + if (!node || !$.fn.select2) return; + + const $select = $(node); + + if (!$select.hasClass("select2-hidden-accessible")) { + $select.select2({ + width: "100%", + dropdownAutoWidth: true, + ...options, + }); + } + + return () => { + if ($select.hasClass("select2-hidden-accessible")) { + $select.select2("destroy"); + } + }; + }, [options]); + + return selectRef; +}; + +export default useSelect; diff --git a/src/components/master/CreateActivity.jsx b/src/components/master/CreateActivity.jsx index 841ef3a6..b9958280 100644 --- a/src/components/master/CreateActivity.jsx +++ b/src/components/master/CreateActivity.jsx @@ -204,7 +204,7 @@ const CreateActivity = ({ onClose }) => { onClick={() => removeChecklistItem(index)} className="btn btn-xs btn-icon btn-text-secondary" > - @@ -219,7 +219,7 @@ const CreateActivity = ({ onClose }) => { className="btn btn-xs btn-primary mt-2" onClick={addChecklistItem} > - diff --git a/src/pages/Activities/TaskPlannng.jsx b/src/pages/Activities/TaskPlannng.jsx index e8238914..da81d487 100644 --- a/src/pages/Activities/TaskPlannng.jsx +++ b/src/pages/Activities/TaskPlannng.jsx @@ -9,6 +9,7 @@ import { useProfile } from "../../hooks/useProfile"; import { useDispatch, useSelector } from "react-redux"; import { useProjectDetails, useProjects } from "../../hooks/useProjects"; import { setProjectId } from "../../slices/localVariablesSlice"; +import showToast from "../../services/toastService"; const TaskPlannng = () => { const { profile } = useProfile(); @@ -22,6 +23,7 @@ const TaskPlannng = () => { (store) => store.localVariables.projectId ); + const [project, setProject] = useState(null); const [projectDetails, setProjectDetails] = useState(null); const [activities, setActivities] = useState(null); @@ -66,14 +68,22 @@ const TaskPlannng = () => { }); }) .catch((error) => { - console.error(error); - setError("Failed to fetch data."); + const message = + error.response?.data?.message || + error.message || + "An unexpected error occurred"; + showToast( message, "error" ); }); } else { setProjectDetails(project_cache); } } catch (err) { - setError("Failed to fetch data."); + setError( "Failed to fetch data." ); + const message = + error.response?.data?.message || + error.message || + "An unexpected error occurred"; + showToast( message, "error" ); } finally { setLoading(false); } diff --git a/src/pages/master/MasterTable.jsx b/src/pages/master/MasterTable.jsx index b39d0472..efb72057 100644 --- a/src/pages/master/MasterTable.jsx +++ b/src/pages/master/MasterTable.jsx @@ -83,7 +83,7 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => { currentItems.map((item, index) => ( - + {updatedColumns.map((col) => ( diff --git a/src/repositories/ProjectRepository.jsx b/src/repositories/ProjectRepository.jsx index 90ee336e..3425d2b0 100644 --- a/src/repositories/ProjectRepository.jsx +++ b/src/repositories/ProjectRepository.jsx @@ -16,7 +16,8 @@ const ProjectRepository = { manageProjectAllocation: ( data ) => api.post( "/api/project/allocation", data ), manageProjectInfra: (data) => api.post("/api/project/manage-infra", data), - manageProjectTasks: (data) => api.post("/api/project/task", data), + manageProjectTasks: ( data ) => api.post( "/api/project/task", data ), + deleteProjectTask:(id)=> api.delete(`/api/project/task/${id}`), updateProject: (id, data) => api.put(`/api/project/update/${id}`, data), deleteProject: (id) => api.delete(`/projects/${id}`),