Merge branch 'Issue_May_2W' of https://git.marcoaiot.com/admin/marco.pms.web into pramod_Task#186DeleteActivity

This commit is contained in:
Pramod Mahajan 2025-05-07 13:00:09 +05:30
commit 9e3101ebf4
10 changed files with 621 additions and 13 deletions

View File

@ -0,0 +1,104 @@
import React, {useState} from 'react'
import ProjectRepository from '../../../repositories/ProjectRepository'
import {useProjectDetails} from '../../../hooks/useProjects'
const DleleteActivity = ( {workItem, workArea, building, floor, onClose} ) =>
{
const {projects_Details, refetch} = useProjectDetails()
const [loading,setLoading] = useState(false)
const handleDeleteActivity =async () =>
{
try
{
setLoading(false)
const updatedProject = { ...projects_Details };
const response = await ProjectRepository.deleteProjectTask( workItem.workItemId );
const newProject = {
...updatedProject,
buildings: updatedProject.buildings.map((building) =>
building.id === building.buildingID
? {
...building,
floors: building.floors.map((floor) =>
floor.id === building.floorId
? {
...floor,
workAreas: floor.workAreas.map((workArea) =>
workArea.id === workItem?.workAreaId
? {
...workArea,
workItems: (() => {
const exists = workArea.workItems.some(
(item) =>
String(
item?.workItem?.id ?? item?.id
) === String(finalData.id)
);
finalUpdatedWorkItem = workItem;
return exists
? workArea.workItems.map((item) =>
String(
item?.workItem?.id ?? item?.id
) === String(finalData.id)
? workItem
: item
)
: [...workArea.workItems, workItem];
})(),
}
: workArea
),
}
: floor
),
}
: building
),
};
cacheData("projectInfo", {
projectId: newProject.id,
data: newProject,
});
resetForm();
dispatch( refreshData( true ) );
setLoading(false)
showToast("Activity Updated Successfully","success")
onClose();
} catch ( error )
{
console.log(error)
}
}
return (
<div className="modal-dialog modal-md modal-simple modal-edit-user">
<div className='modal-dialog modal-dialog-centered'>
<div className="modal-content">
<div className="modal-body">
<div className="row">
<button
type="button"
className="btn-close"
aria-label="Close"
onClick={()=>onClose}
/>
<div className="text-center mb-1">
<h5>Are you sure you want delete this Activity { workItem.id}</h5>
<div className='d-flex justify-content-evenly'>
<button className='btn btn-primary btn-xs' onClick={handleDeleteActivity}>{ loading ? "Please Wait ":"Yes"}</button>
<button className='btn btn-secondary btn-xs' onClick={()=>onClose}>Cancel</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export default DleleteActivity

View File

@ -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);
@ -84,7 +85,6 @@ const EditActivityModal = ({
floorId: floor?.id,
workAreaId: workArea?.id,
};
console.log(finalData);
ProjectRepository.manageProjectTasks([finalData])
.then((response) => {
@ -171,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 (
<div className="modal-dialog modal-lg modal-simple modal-edit-user">
<div className="modal-content">
@ -258,7 +263,7 @@ const EditActivityModal = ({
{activity.activityName}
</option>
))}
{!loading && activityData.length === 0 && (
{!loading && activities.length === 0 && (
<option disabled>No activities available</option>
)}
</select>
@ -321,7 +326,7 @@ const EditActivityModal = ({
{/* )} */}
<div className="col-12 text-center">
<button type="submit" className="btn btn-sm btn-primary me-3">
<button type="submit" className="btn btn-sm btn-primary me-3" disabled={activities.length === 0}>
{isSubmitting ? "Please Wait.." : "Edit Task"}
</button>
<button

View File

@ -4,15 +4,28 @@ import { useParams } from "react-router-dom";
import EditActivityModal from "./EditActivityModal";
import { useHasUserPermission } from "../../../hooks/useHasUserPermission";
import { MANAGE_PROJECT_INFRA, MANAGE_TASK } from "../../../utils/constants";
import DleleteActivity from "./DleleteActivity";
import ConfirmModal from "../../common/ConfirmModal";
import ProjectRepository from '../../../repositories/ProjectRepository'
import {useProjectDetails} from '../../../hooks/useProjects'
import showToast from "../../../services/toastService";
import {cacheData} 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 dispatch = useDispatch()
const {projects_Details} = useProjectDetails(projectId)
const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);
@ -41,10 +54,78 @@ 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 );
console.log(workItem)
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
),
};
console.log(newProject)
cacheData("projectInfo", {
projectId: newProject.id,
data: newProject,
});
dispatch( refreshData( true ) );
setLoadingDelete(false)
showToast("Activity Updated Successfully","success")
} catch ( error )
{
setLoadingDelete(false)
const message =
error.response?.data?.message ||
error.message ||
"An unexpected error occurred";
showToast( message, "error" );
console.log(error)
}
};
return (
<>
{isModalOpen && (
@ -73,6 +154,23 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
floor={forFloor}
onClose={closeModal1}
/>
</div>}
{showModal2 && <div
className={`modal fade ${showModal2 ? "show" : ""}`}
tabIndex="-1"
role="dialog"
style={{ display: showModal2 ? "block" : "none" }}
aria-hidden='false'
>
{/* <DleleteActivity
workItem={workItem}
workArea={forWorkArea}
building={forBuilding}
floor={forFloor}
onClose={closeModalDelete}
/> */}
<ConfirmModal type={"delete"} message={"Are you sure you want delete"} onSubmit={ handleSubmit} onClose={closeModalDelete} loading={loadingDelete}/>
</div> }
<tr>
@ -164,7 +262,7 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
aria-label="Delete"
type="button"
className="btn p-0 dropdown-toggle hide-arrow"
onClick={showModal1}
onClick={showModalDelete}
>
<i
className="bx bx-trash me-1 text-danger"

View File

@ -0,0 +1,34 @@
import React from "react";
import useSelect from "../common/useSelect"; // your custom hook
const ActivityDropdown = ({ options, value, onChange, register, error }) => {
const selectRef = useSelect({ minimumResultsForSearch: 5 });
return (
<div className="col-12 col-md-12">
<label className="form-label" htmlFor="activityID">
Select Activity
</label>
<select
id="activityID"
name="activityID"
ref={selectRef}
className="select2 form-select form-select-sm"
aria-label="Default select example"
{...register("activityID", { valueAsNumber: true })}
value={value}
onChange={onChange}
>
<option value="0">Select Activity</option>
{options.map((activity) => (
<option key={activity.id} value={activity.id}>
{activity.activityName}
</option>
))}
</select>
{error && <p className="danger-text">{error.message}</p>}
</div>
);
};
export default ActivityDropdown;

View File

@ -0,0 +1,64 @@
import React, { useState } from 'react';
const ConfirmModal = ({ type, onSubmit, onClose, message, loading }) => {
const TypeofIcon = (type) => {
switch (type) {
case "delete":
return <i className='bx bx-x-circle text-danger bx-lg'></i>;
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 (
<div className={`modal-dialog modal-${TypeofModal(type)} modal-simple modal-confirm`}>
<div className='modal-dialog modal-dialog-centered'>
<div className="modal-content">
<div className="modal-body">
<div className="row">
<button
type="button"
className="btn-close"
aria-label="Close"
onClick={onClose}
/>
<div className="text-center mb-1">
<p className='fs-6'>{TypeofIcon(type)} <span className='fs-6'>{message}</span></p>
<div className='d-flex justify-content-evenly'>
<button
className='btn btn-primary btn-sm'
onClick={onSubmit}
disabled={loading}
>
{loading ? "Please Wait..." : "Yes"}
</button>
<button
className='btn btn-secondary btn-sm'
onClick={onClose}
disabled={loading}
>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
};
export default ConfirmModal;

View File

@ -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;
}

View File

@ -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 (
<div className="comment-editor">
<ReactQuill
theme="snow"
value={value}
onChange={setValue}
modules={modules}
formats={formats}
placeholder="Add a comment..."
/>
<div className="editor-footer">
<button className="btn btn-sm btn-primary" onClick={handleSubmit}>
Add Comment
</button>
</div>
</div>
);
};
export default CommentEditor;

View File

@ -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 (
<from >
<div className="row">
<div className="col-12 col-md-8">
<div className="row">
<label for="defaultFormControlInput" class="form-label ">
Name
</label>
<input
type="text"
class="form-control form-control-sm"
id="defaultFormControlInput"
placeholder="John Doe"
aria-describedby="defaultFormControlHelp"
/>
</div>
<div className="row my-md-6 my-4 px-0">
<div className="col-6 ps-0">
<label for="defaultFormControlInput" class="form-label ">
Projec
</label>
<select
name="DataTables_Table_0_length"
aria-controls="DataTables_Table_0"
className="form-select form-select-sm"
aria-label=""
>
<option>Project-2</option>
<option>Project-2</option>
<option>Project-3</option>
</select>
</div>
<div className="col-6 pe-0">
<label for="defaultFormControlInput" class="form-label ">
Type
</label>
<select
name="DataTables_Table_0_length"
aria-controls="DataTables_Table_0"
className="form-select form-select-sm"
aria-label=""
>
<option>Project-2</option>
<option>Project-2</option>
<option>Project-3</option>
</select>
</div>
</div>
<div className="row my-md-6 my-4 px-0">
<div className="col-6 ps-0">
<label for="defaultFormControlInput" class="form-label ">
Status
</label>
<select
name="DataTables_Table_0_length"
aria-controls="DataTables_Table_0"
className="form-select form-select-sm"
aria-label=""
>
<option>Project-2</option>
<option>Project-2</option>
<option>Project-3</option>
</select>
</div>
<div className="col-6 pe-0">
<label for="defaultFormControlInput" class="form-label ">
Activity
</label>
<select
name="DataTables_Table_0_length"
aria-controls="DataTables_Table_0"
className="form-select form-select-sm"
aria-label=""
>
<option>Project-2</option>
<option>Project-2</option>
<option>Project-3</option>
</select>
</div>
</div>
</div>
<div className="col-12 col-md-4">
<div class="row g-2">
<label for="defaultFormControlInput" class="form-label ">
Priority
</label>
<div class="col-md-4 mt-0">
<div class="form-check custom-option custom-option-icon p-2">
<label class="form-check-label custom-option-content" for="customRadioIcon1">
<input
name="customRadioIcon"
class="form-check-input form-check-input-sm"
type="radio"
id="customRadioIcon1"
checked
/> Low
</label>
</div>
</div>
<div class="col-md-4 mt-0">
<div class="form-check custom-option custom-option-icon p-2">
<label class="form-check-label custom-option-content" for="customRadioIcon2">
<input
name="customRadioIcon"
class="form-check-input form-check-input-sm"
type="radio"
id="customRadioIcon2"
/> Medium
</label>
</div>
</div>
<div class="col-md-4 mt-0">
<div class="form-check custom-option custom-option-icon p-2">
<label class="form-check-label custom-option-content" for="customRadioIcon3">
<input
name="customRadioIcon"
class="form-check-input form-check-input-sm"
type="radio"
id="customRadioIcon3"
/> High
</label>
</div>
</div>
</div>
</div>
<div className="marco-quill-wrapper">
<ReactQuill
theme="snow"
value={value}
onChange={setValue}
placeholder="Add a comment..."
modules={jiraModules}
formats={jiraFormats}
/>
</div>
</div>
</from>
);
};
export default CreateIssue;

View File

@ -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;

View File

@ -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}`),