Adds the shortName field to be displayed in:

- Project Card Component
- Project List Component
- Project Profile Component
This commit is contained in:
ashutosh.nehete 2025-06-10 16:31:49 +05:30
parent 435fa730b6
commit ad1ad667b1
5 changed files with 145 additions and 99 deletions

View File

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

View File

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

View File

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

View File

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

View File

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