Adds the shortName
field to be displayed in:
#200
@ -26,6 +26,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
.object({
|
||||
...(project?.id ? { id: z.string().optional() } : {}),
|
||||
name: z.string().min(1, { message: "Project Name is required" }),
|
||||
shortName: z.string().optional(),
|
||||
contactPerson: z
|
||||
.string()
|
||||
.min( 1, {message: "Contact Person Name is required"} )
|
||||
@ -72,6 +73,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
defaultValues: {
|
||||
id: project?.id || "",
|
||||
name: project?.name || "",
|
||||
shortName: project?.shortName || "",
|
||||
contactPerson: project?.contactPerson || "",
|
||||
projectAddress: project?.projectAddress || "",
|
||||
startDate: formatDate(project?.startDate) || currentDate,
|
||||
@ -88,6 +90,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
? {
|
||||
id: project?.id || "",
|
||||
name: project?.name || "",
|
||||
shortName: project?.shortName || "",
|
||||
contactPerson: project?.contactPerson || "",
|
||||
projectAddress: project?.projectAddress || "",
|
||||
startDate: formatDate(project?.startDate) || "",
|
||||
@ -108,6 +111,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
reset({
|
||||
id: project?.id || "",
|
||||
name: project?.name || "",
|
||||
shortName: project?.shortName || "",
|
||||
contactPerson: project?.contactPerson || "",
|
||||
projectAddress: project?.projectAddress || "",
|
||||
startDate: formatDate(project?.startDate) || currentDate,
|
||||
@ -157,6 +161,27 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="shortName">
|
||||
Short Name
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="shortName"
|
||||
name="shortName"
|
||||
className="form-control"
|
||||
placeholder="Short Name"
|
||||
{...register("shortName")}
|
||||
/>
|
||||
{errors.shortName && (
|
||||
<div
|
||||
className="danger-text text-start"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{errors.shortName.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="col-12 col-md-12">
|
||||
<label className="form-label" htmlFor="contactPerson">
|
||||
Contact Person
|
||||
|
@ -1,80 +1,75 @@
|
||||
import React, { useState,useEffect } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import ManageProjectInfo from "./ManageProjectInfo";
|
||||
import showToast from "../../services/toastService";
|
||||
import ProjectRepository from "../../repositories/ProjectRepository";
|
||||
import { cacheData,getCachedData } from "../../slices/apiDataManager";
|
||||
import {hasUserPermission} from "../../utils/authUtils";
|
||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||
import { hasUserPermission } from "../../utils/authUtils";
|
||||
import moment from "moment";
|
||||
import {useHasUserPermission} from "../../hooks/useHasUserPermission";
|
||||
import {MANAGE_PROJECT} from "../../utils/constants";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import { MANAGE_PROJECT } from "../../utils/constants";
|
||||
|
||||
|
||||
const ProjectBanner = ( {project_data} ) =>
|
||||
{
|
||||
const ProjectBanner = ({ project_data }) => {
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
const manageProject = useHasUserPermission(MANAGE_PROJECT)
|
||||
const [ CurrentProject, setCurrentProject ] = useState( project_data )
|
||||
const manageProject = useHasUserPermission(MANAGE_PROJECT);
|
||||
const [CurrentProject, setCurrentProject] = useState(project_data);
|
||||
if (project_data == null) {
|
||||
return <span>incomplete project information</span>;
|
||||
}
|
||||
|
||||
|
||||
const handleShow = () => setShowModal(true);
|
||||
const handleClose = () => setShowModal(false);
|
||||
|
||||
const handleFormSubmit = ( updatedProject,setLoading ) =>
|
||||
{
|
||||
|
||||
if ( CurrentProject?.id )
|
||||
{
|
||||
ProjectRepository.updateProject(CurrentProject.id,updatedProject).then( ( response ) =>
|
||||
{
|
||||
const updatedProjectData = {
|
||||
...CurrentProject,
|
||||
...response.data,
|
||||
building: CurrentProject.building,
|
||||
};
|
||||
setCurrentProject( updatedProject )
|
||||
|
||||
cacheData( `projectinfo-${ CurrentProject.id }`, updatedProjectData );
|
||||
const projects_list = getCachedData("projectslist");
|
||||
if ( projects_list )
|
||||
{
|
||||
const updatedProjectsList = projects_list.map(project =>
|
||||
project.id === CurrentProject.id ? {
|
||||
...project,
|
||||
...response.data,
|
||||
// tenant:project.tenant
|
||||
} : project
|
||||
);
|
||||
|
||||
cacheData("projectslist",updatedProjectsList)
|
||||
}
|
||||
|
||||
showToast( "Project updated successfully.", "success" );
|
||||
setLoading(false)
|
||||
setShowModal(false)
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast( error.message, "error" );
|
||||
|
||||
});
|
||||
}
|
||||
const handleFormSubmit = (updatedProject, setLoading) => {
|
||||
if (CurrentProject?.id) {
|
||||
ProjectRepository.updateProject(CurrentProject.id, updatedProject)
|
||||
.then((response) => {
|
||||
const updatedProjectData = {
|
||||
...CurrentProject,
|
||||
...response.data,
|
||||
building: CurrentProject.building,
|
||||
};
|
||||
setCurrentProject(updatedProject);
|
||||
|
||||
cacheData(`projectinfo-${CurrentProject.id}`, updatedProjectData);
|
||||
const projects_list = getCachedData("projectslist");
|
||||
if (projects_list) {
|
||||
const updatedProjectsList = projects_list.map((project) =>
|
||||
project.id === CurrentProject.id
|
||||
? {
|
||||
...project,
|
||||
...response.data,
|
||||
// tenant:project.tenant
|
||||
}
|
||||
: project
|
||||
);
|
||||
|
||||
cacheData("projectslist", updatedProjectsList);
|
||||
}
|
||||
|
||||
showToast("Project updated successfully.", "success");
|
||||
setLoading(false);
|
||||
setShowModal(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
showToast(error.message, "error");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={`modal fade ${showModal ? 'show' : ''}`}
|
||||
className={`modal fade ${showModal ? "show" : ""}`}
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: showModal ? 'block' : 'none' }}
|
||||
style={{ display: showModal ? "block" : "none" }}
|
||||
aria-hidden={!showModal}
|
||||
>
|
||||
<ManageProjectInfo
|
||||
project={CurrentProject}
|
||||
handleSubmitForm={handleFormSubmit}
|
||||
onClose={handleClose}
|
||||
></ManageProjectInfo>
|
||||
<ManageProjectInfo
|
||||
project={CurrentProject}
|
||||
handleSubmitForm={handleFormSubmit}
|
||||
onClose={handleClose}
|
||||
></ManageProjectInfo>
|
||||
</div>
|
||||
{/* Project Banner */}
|
||||
<div className="col-12">
|
||||
@ -89,17 +84,23 @@ const ProjectBanner = ( {project_data} ) =>
|
||||
style={{ width: "40px", height: "40px" }}
|
||||
/>
|
||||
<h5 className="mb-0">
|
||||
{CurrentProject.name ? CurrentProject.name : "N/A"}
|
||||
{CurrentProject.name
|
||||
? CurrentProject.shortName
|
||||
? `${CurrentProject.name} (${CurrentProject.shortName})`
|
||||
: CurrentProject.name
|
||||
: "N/A"}
|
||||
</h5>
|
||||
</div>
|
||||
{manageProject && (
|
||||
<button
|
||||
type="button"
|
||||
className={`btn btn-sm btn-primary ${!manageProject && 'd-none'}`}
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#edit-project-modal"
|
||||
onClick={handleShow}
|
||||
>
|
||||
type="button"
|
||||
className={`btn btn-sm btn-primary ${
|
||||
!manageProject && "d-none"
|
||||
}`}
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#edit-project-modal"
|
||||
onClick={handleShow}
|
||||
>
|
||||
Modify
|
||||
</button>
|
||||
)}
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
getProjectStatusName,
|
||||
} from "../../utils/projectStatus";
|
||||
|
||||
const ProjectCard = ({ projectData }) => {
|
||||
const ProjectCard = ({ projectData, recall }) => {
|
||||
const [projectInfo, setProjectInfo] = useState(projectData);
|
||||
const [projectDetails, setProjectDetails] = useState(null);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
@ -78,7 +78,7 @@ const ProjectCard = ({ projectData }) => {
|
||||
);
|
||||
cacheData("projectslist", updatedProjectsList);
|
||||
}
|
||||
|
||||
recall(getCachedData("projectslist"));
|
||||
showToast("Project updated successfully.", "success");
|
||||
setShowModal(false);
|
||||
})
|
||||
@ -88,7 +88,6 @@ const ProjectCard = ({ projectData }) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
{showModal && projectDetails && (
|
||||
@ -119,17 +118,16 @@ const ProjectCard = ({ projectData }) => {
|
||||
></i>
|
||||
</div>
|
||||
<div className="me-2">
|
||||
<h5 className="mb-0">
|
||||
<a
|
||||
className="stretched-link text-heading"
|
||||
onClick={handleViewProject}
|
||||
>
|
||||
{projectInfo.name}
|
||||
</a>
|
||||
<h5
|
||||
className="mb-0 stretched-link text-heading text-start"
|
||||
onClick={handleViewProject}
|
||||
>
|
||||
{projectInfo.shortName
|
||||
? projectInfo.shortName
|
||||
: projectInfo.name}
|
||||
</h5>
|
||||
<div className="client-info text-body">
|
||||
<span className="fw-medium">Client: </span>
|
||||
<span>{projectInfo.contactPerson}</span>
|
||||
<span>{projectInfo.shortName ? projectInfo.name : ""}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -141,9 +139,14 @@ const ProjectCard = ({ projectData }) => {
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
{modifyProjectLoading? <div class="spinner-border spinner-border-sm text-secondary" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div> :
|
||||
{modifyProjectLoading ? (
|
||||
<div
|
||||
className="spinner-border spinner-border-sm text-secondary"
|
||||
role="status"
|
||||
>
|
||||
<span className="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
) : (
|
||||
<i
|
||||
className="bx bx-dots-vertical-rounded bx-sm text-muted"
|
||||
data-bs-toggle="tooltip"
|
||||
@ -151,7 +154,8 @@ const ProjectCard = ({ projectData }) => {
|
||||
data-bs-placement="top"
|
||||
data-bs-custom-class="tooltip-dark"
|
||||
title="More Action"
|
||||
></i>}
|
||||
></i>
|
||||
)}
|
||||
</button>
|
||||
<ul className="dropdown-menu dropdown-menu-end">
|
||||
<li>
|
||||
@ -191,6 +195,12 @@ const ProjectCard = ({ projectData }) => {
|
||||
<div className="card-body pb-1">
|
||||
<div className="d-flex align-items-center flex-wrap">
|
||||
<div className="text-start mb-4">
|
||||
<p className="mb-1">
|
||||
<span className="text-heading fw-medium">
|
||||
Contact Person:{" "}
|
||||
</span>
|
||||
{projectInfo.contactPerson ? projectInfo.contactPerson : "NA"}
|
||||
</p>
|
||||
<p className="mb-1">
|
||||
<span className="text-heading fw-medium">Start Date: </span>
|
||||
{projectInfo.startDate
|
||||
|
@ -37,7 +37,7 @@ const ProjectList = () => {
|
||||
const handleShow = () => setShowModal(true);
|
||||
const handleClose = () => setShowModal(false);
|
||||
|
||||
useEffect(() => {
|
||||
const sortingProject = (projects) =>{
|
||||
if (!loading && Array.isArray(projects)) {
|
||||
const grouped = {};
|
||||
projects.forEach((project) => {
|
||||
@ -56,6 +56,10 @@ const ProjectList = () => {
|
||||
|
||||
setProjectList(sortedGrouped);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
sortingProject(projects)
|
||||
}, [projects, loginUser?.projects, loading]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -75,6 +79,7 @@ const ProjectList = () => {
|
||||
setProjectList( ( prev ) => [ ...prev, response.data ] );
|
||||
setloading( false )
|
||||
reset()
|
||||
sortingProject(getCachedData("projectslist"))
|
||||
showToast("Project Created successfully.", "success");
|
||||
setShowModal(false);
|
||||
})
|
||||
@ -118,7 +123,7 @@ const ProjectList = () => {
|
||||
indexOfLastItem
|
||||
);
|
||||
const totalPages = Math.ceil(filteredProjects.length / itemsPerPage);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const tooltipTriggerList = Array.from(
|
||||
document.querySelectorAll('[data-bs-toggle="tooltip"]')
|
||||
@ -268,7 +273,7 @@ const ProjectList = () => {
|
||||
<th className="text-start" colSpan={5}>
|
||||
Project Name
|
||||
</th>
|
||||
<th className="mx-2 text-start">Project Manger</th>
|
||||
<th className="mx-2 text-start">Contact Person</th>
|
||||
<th className="mx-2">START DATE</th>
|
||||
<th className="mx-2">DEADLINE</th>
|
||||
<th className="mx-2">Task</th>
|
||||
@ -336,7 +341,7 @@ const ProjectList = () => {
|
||||
</tr>
|
||||
) : (
|
||||
currentItems.map((project) => (
|
||||
<ProjectListView key={project.id} projectData={project} />
|
||||
<ProjectListView key={project.id} projectData={project} recall={sortingProject} />
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
@ -344,7 +349,7 @@ const ProjectList = () => {
|
||||
</div>
|
||||
) : (
|
||||
currentItems.map((project) => (
|
||||
<ProjectCard key={project.id} projectData={project} />
|
||||
<ProjectCard key={project.id} projectData={project} recall={sortingProject} />
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@ import ManageProjectInfo from "../../components/Project/ManageProjectInfo";
|
||||
import showToast from "../../services/toastService";
|
||||
import { getCachedData, cacheData } from "../../slices/apiDataManager";
|
||||
|
||||
const ProjectListView = ({ projectData }) => {
|
||||
const ProjectListView = ({ projectData, recall }) => {
|
||||
const [projectInfo, setProjectInfo] = useState(projectData);
|
||||
const [projectDetails, setProjectDetails] = useState(null);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
@ -76,6 +76,7 @@ const ProjectListView = ({ projectData }) => {
|
||||
);
|
||||
cacheData("projectslist", updatedProjectsList);
|
||||
}
|
||||
recall(getCachedData("projectslist"));
|
||||
showToast("Project updated successfully.", "success");
|
||||
setShowModal(false);
|
||||
})
|
||||
@ -87,19 +88,21 @@ const ProjectListView = ({ projectData }) => {
|
||||
return (
|
||||
<>
|
||||
{showModal && projectDetails && (
|
||||
<div
|
||||
className="modal fade show"
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: "block" }}
|
||||
aria-hidden="false"
|
||||
>
|
||||
<ManageProjectInfo
|
||||
project={projectDetails}
|
||||
handleSubmitForm={handleFormSubmit}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</div>
|
||||
<tr>
|
||||
<td
|
||||
className="modal fade show"
|
||||
tabIndex="-1"
|
||||
role="dialog"
|
||||
style={{ display: "block" }}
|
||||
aria-hidden="false"
|
||||
>
|
||||
<ManageProjectInfo
|
||||
project={projectDetails}
|
||||
handleSubmitForm={handleFormSubmit}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
|
||||
<tr className="py-8">
|
||||
@ -108,7 +111,9 @@ const ProjectListView = ({ projectData }) => {
|
||||
className="text-primary cursor-pointer"
|
||||
onClick={() => navigate(`/projects/${projectInfo.id}`)}
|
||||
>
|
||||
{projectInfo.name}
|
||||
{projectInfo.shortName
|
||||
? `${projectInfo.name} (${projectInfo.shortName})`
|
||||
: projectInfo.name}
|
||||
</strong>
|
||||
</td>
|
||||
<td className="text-start small">{projectInfo.contactPerson}</td>
|
||||
|
Loading…
x
Reference in New Issue
Block a user