Adds the shortName field to be displayed in: #200

Merged
vikas.nale merged 1 commits from Ashutosh_Enhancement#496_ShortName into main 2025-06-10 14:19:15 +00:00
5 changed files with 145 additions and 99 deletions

View File

@ -26,6 +26,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
.object({ .object({
...(project?.id ? { id: z.string().optional() } : {}), ...(project?.id ? { id: z.string().optional() } : {}),
name: z.string().min(1, { message: "Project Name is required" }), name: z.string().min(1, { message: "Project Name is required" }),
shortName: z.string().optional(),
contactPerson: z contactPerson: z
.string() .string()
.min( 1, {message: "Contact Person Name is required"} ) .min( 1, {message: "Contact Person Name is required"} )
@ -72,6 +73,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
defaultValues: { defaultValues: {
id: project?.id || "", id: project?.id || "",
name: project?.name || "", name: project?.name || "",
shortName: project?.shortName || "",
contactPerson: project?.contactPerson || "", contactPerson: project?.contactPerson || "",
projectAddress: project?.projectAddress || "", projectAddress: project?.projectAddress || "",
startDate: formatDate(project?.startDate) || currentDate, startDate: formatDate(project?.startDate) || currentDate,
@ -88,6 +90,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
? { ? {
id: project?.id || "", id: project?.id || "",
name: project?.name || "", name: project?.name || "",
shortName: project?.shortName || "",
contactPerson: project?.contactPerson || "", contactPerson: project?.contactPerson || "",
projectAddress: project?.projectAddress || "", projectAddress: project?.projectAddress || "",
startDate: formatDate(project?.startDate) || "", startDate: formatDate(project?.startDate) || "",
@ -108,6 +111,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
reset({ reset({
id: project?.id || "", id: project?.id || "",
name: project?.name || "", name: project?.name || "",
shortName: project?.shortName || "",
contactPerson: project?.contactPerson || "", contactPerson: project?.contactPerson || "",
projectAddress: project?.projectAddress || "", projectAddress: project?.projectAddress || "",
startDate: formatDate(project?.startDate) || currentDate, startDate: formatDate(project?.startDate) || currentDate,
@ -157,6 +161,27 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
</div> </div>
)} )}
</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"> <div className="col-12 col-md-12">
<label className="form-label" htmlFor="contactPerson"> <label className="form-label" htmlFor="contactPerson">
Contact Person 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 ManageProjectInfo from "./ManageProjectInfo";
import showToast from "../../services/toastService"; import showToast from "../../services/toastService";
import ProjectRepository from "../../repositories/ProjectRepository"; import ProjectRepository from "../../repositories/ProjectRepository";
import { cacheData,getCachedData } from "../../slices/apiDataManager"; import { cacheData, getCachedData } from "../../slices/apiDataManager";
import {hasUserPermission} from "../../utils/authUtils"; import { hasUserPermission } from "../../utils/authUtils";
import moment from "moment"; import moment from "moment";
import {useHasUserPermission} from "../../hooks/useHasUserPermission"; import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import {MANAGE_PROJECT} from "../../utils/constants"; import { MANAGE_PROJECT } from "../../utils/constants";
const ProjectBanner = ({ project_data }) => {
const ProjectBanner = ( {project_data} ) =>
{
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
const manageProject = useHasUserPermission(MANAGE_PROJECT) const manageProject = useHasUserPermission(MANAGE_PROJECT);
const [ CurrentProject, setCurrentProject ] = useState( project_data ) const [CurrentProject, setCurrentProject] = useState(project_data);
if (project_data == null) { if (project_data == null) {
return <span>incomplete project information</span>; return <span>incomplete project information</span>;
} }
const handleShow = () => setShowModal(true); const handleShow = () => setShowModal(true);
const handleClose = () => setShowModal(false); 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 ( return (
<> <>
<div <div
className={`modal fade ${showModal ? 'show' : ''}`} className={`modal fade ${showModal ? "show" : ""}`}
tabIndex="-1" tabIndex="-1"
role="dialog" role="dialog"
style={{ display: showModal ? 'block' : 'none' }} style={{ display: showModal ? "block" : "none" }}
aria-hidden={!showModal} aria-hidden={!showModal}
> >
<ManageProjectInfo <ManageProjectInfo
project={CurrentProject} project={CurrentProject}
handleSubmitForm={handleFormSubmit} handleSubmitForm={handleFormSubmit}
onClose={handleClose} onClose={handleClose}
></ManageProjectInfo> ></ManageProjectInfo>
</div> </div>
{/* Project Banner */} {/* Project Banner */}
<div className="col-12"> <div className="col-12">
@ -89,17 +84,23 @@ const ProjectBanner = ( {project_data} ) =>
style={{ width: "40px", height: "40px" }} style={{ width: "40px", height: "40px" }}
/> />
<h5 className="mb-0"> <h5 className="mb-0">
{CurrentProject.name ? CurrentProject.name : "N/A"} {CurrentProject.name
? CurrentProject.shortName
? `${CurrentProject.name} (${CurrentProject.shortName})`
: CurrentProject.name
: "N/A"}
</h5> </h5>
</div> </div>
{manageProject && ( {manageProject && (
<button <button
type="button" type="button"
className={`btn btn-sm btn-primary ${!manageProject && 'd-none'}`} className={`btn btn-sm btn-primary ${
data-bs-toggle="modal" !manageProject && "d-none"
data-bs-target="#edit-project-modal" }`}
onClick={handleShow} data-bs-toggle="modal"
> data-bs-target="#edit-project-modal"
onClick={handleShow}
>
Modify Modify
</button> </button>
)} )}

View File

@ -14,7 +14,7 @@ import {
getProjectStatusName, getProjectStatusName,
} from "../../utils/projectStatus"; } from "../../utils/projectStatus";
const ProjectCard = ({ projectData }) => { const ProjectCard = ({ projectData, recall }) => {
const [projectInfo, setProjectInfo] = useState(projectData); const [projectInfo, setProjectInfo] = useState(projectData);
const [projectDetails, setProjectDetails] = useState(null); const [projectDetails, setProjectDetails] = useState(null);
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
@ -78,7 +78,7 @@ const ProjectCard = ({ projectData }) => {
); );
cacheData("projectslist", updatedProjectsList); cacheData("projectslist", updatedProjectsList);
} }
recall(getCachedData("projectslist"));
showToast("Project updated successfully.", "success"); showToast("Project updated successfully.", "success");
setShowModal(false); setShowModal(false);
}) })
@ -88,7 +88,6 @@ const ProjectCard = ({ projectData }) => {
} }
}; };
return ( return (
<> <>
{showModal && projectDetails && ( {showModal && projectDetails && (
@ -119,17 +118,16 @@ const ProjectCard = ({ projectData }) => {
></i> ></i>
</div> </div>
<div className="me-2"> <div className="me-2">
<h5 className="mb-0"> <h5
<a className="mb-0 stretched-link text-heading text-start"
className="stretched-link text-heading" onClick={handleViewProject}
onClick={handleViewProject} >
> {projectInfo.shortName
{projectInfo.name} ? projectInfo.shortName
</a> : projectInfo.name}
</h5> </h5>
<div className="client-info text-body"> <div className="client-info text-body">
<span className="fw-medium">Client: </span> <span>{projectInfo.shortName ? projectInfo.name : ""}</span>
<span>{projectInfo.contactPerson}</span>
</div> </div>
</div> </div>
</div> </div>
@ -141,9 +139,14 @@ const ProjectCard = ({ projectData }) => {
data-bs-toggle="dropdown" data-bs-toggle="dropdown"
aria-expanded="false" aria-expanded="false"
> >
{modifyProjectLoading? <div class="spinner-border spinner-border-sm text-secondary" role="status"> {modifyProjectLoading ? (
<span class="visually-hidden">Loading...</span> <div
</div> : className="spinner-border spinner-border-sm text-secondary"
role="status"
>
<span className="visually-hidden">Loading...</span>
</div>
) : (
<i <i
className="bx bx-dots-vertical-rounded bx-sm text-muted" className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip" data-bs-toggle="tooltip"
@ -151,7 +154,8 @@ const ProjectCard = ({ projectData }) => {
data-bs-placement="top" data-bs-placement="top"
data-bs-custom-class="tooltip-dark" data-bs-custom-class="tooltip-dark"
title="More Action" title="More Action"
></i>} ></i>
)}
</button> </button>
<ul className="dropdown-menu dropdown-menu-end"> <ul className="dropdown-menu dropdown-menu-end">
<li> <li>
@ -191,6 +195,12 @@ const ProjectCard = ({ projectData }) => {
<div className="card-body pb-1"> <div className="card-body pb-1">
<div className="d-flex align-items-center flex-wrap"> <div className="d-flex align-items-center flex-wrap">
<div className="text-start mb-4"> <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"> <p className="mb-1">
<span className="text-heading fw-medium">Start Date: </span> <span className="text-heading fw-medium">Start Date: </span>
{projectInfo.startDate {projectInfo.startDate

View File

@ -37,7 +37,7 @@ const ProjectList = () => {
const handleShow = () => setShowModal(true); const handleShow = () => setShowModal(true);
const handleClose = () => setShowModal(false); const handleClose = () => setShowModal(false);
useEffect(() => { const sortingProject = (projects) =>{
if (!loading && Array.isArray(projects)) { if (!loading && Array.isArray(projects)) {
const grouped = {}; const grouped = {};
projects.forEach((project) => { projects.forEach((project) => {
@ -56,6 +56,10 @@ const ProjectList = () => {
setProjectList(sortedGrouped); setProjectList(sortedGrouped);
} }
}
useEffect(() => {
sortingProject(projects)
}, [projects, loginUser?.projects, loading]); }, [projects, loginUser?.projects, loading]);
useEffect(() => { useEffect(() => {
@ -75,6 +79,7 @@ const ProjectList = () => {
setProjectList( ( prev ) => [ ...prev, response.data ] ); setProjectList( ( prev ) => [ ...prev, response.data ] );
setloading( false ) setloading( false )
reset() reset()
sortingProject(getCachedData("projectslist"))
showToast("Project Created successfully.", "success"); showToast("Project Created successfully.", "success");
setShowModal(false); setShowModal(false);
}) })
@ -118,7 +123,7 @@ const ProjectList = () => {
indexOfLastItem indexOfLastItem
); );
const totalPages = Math.ceil(filteredProjects.length / itemsPerPage); const totalPages = Math.ceil(filteredProjects.length / itemsPerPage);
useEffect(() => { useEffect(() => {
const tooltipTriggerList = Array.from( const tooltipTriggerList = Array.from(
document.querySelectorAll('[data-bs-toggle="tooltip"]') document.querySelectorAll('[data-bs-toggle="tooltip"]')
@ -268,7 +273,7 @@ const ProjectList = () => {
<th className="text-start" colSpan={5}> <th className="text-start" colSpan={5}>
Project Name Project Name
</th> </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">START DATE</th>
<th className="mx-2">DEADLINE</th> <th className="mx-2">DEADLINE</th>
<th className="mx-2">Task</th> <th className="mx-2">Task</th>
@ -336,7 +341,7 @@ const ProjectList = () => {
</tr> </tr>
) : ( ) : (
currentItems.map((project) => ( currentItems.map((project) => (
<ProjectListView key={project.id} projectData={project} /> <ProjectListView key={project.id} projectData={project} recall={sortingProject} />
)) ))
)} )}
</tbody> </tbody>
@ -344,7 +349,7 @@ const ProjectList = () => {
</div> </div>
) : ( ) : (
currentItems.map((project) => ( currentItems.map((project) => (
<ProjectCard key={project.id} projectData={project} /> <ProjectCard key={project.id} projectData={project} recall={sortingProject} />
)) ))
)} )}
</div> </div>

View File

@ -15,7 +15,7 @@ import ManageProjectInfo from "../../components/Project/ManageProjectInfo";
import showToast from "../../services/toastService"; import showToast from "../../services/toastService";
import { getCachedData, cacheData } from "../../slices/apiDataManager"; import { getCachedData, cacheData } from "../../slices/apiDataManager";
const ProjectListView = ({ projectData }) => { const ProjectListView = ({ projectData, recall }) => {
const [projectInfo, setProjectInfo] = useState(projectData); const [projectInfo, setProjectInfo] = useState(projectData);
const [projectDetails, setProjectDetails] = useState(null); const [projectDetails, setProjectDetails] = useState(null);
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
@ -76,6 +76,7 @@ const ProjectListView = ({ projectData }) => {
); );
cacheData("projectslist", updatedProjectsList); cacheData("projectslist", updatedProjectsList);
} }
recall(getCachedData("projectslist"));
showToast("Project updated successfully.", "success"); showToast("Project updated successfully.", "success");
setShowModal(false); setShowModal(false);
}) })
@ -87,19 +88,21 @@ const ProjectListView = ({ projectData }) => {
return ( return (
<> <>
{showModal && projectDetails && ( {showModal && projectDetails && (
<div <tr>
className="modal fade show" <td
tabIndex="-1" className="modal fade show"
role="dialog" tabIndex="-1"
style={{ display: "block" }} role="dialog"
aria-hidden="false" style={{ display: "block" }}
> aria-hidden="false"
<ManageProjectInfo >
project={projectDetails} <ManageProjectInfo
handleSubmitForm={handleFormSubmit} project={projectDetails}
onClose={handleClose} handleSubmitForm={handleFormSubmit}
/> onClose={handleClose}
</div> />
</td>
</tr>
)} )}
<tr className="py-8"> <tr className="py-8">
@ -108,7 +111,9 @@ const ProjectListView = ({ projectData }) => {
className="text-primary cursor-pointer" className="text-primary cursor-pointer"
onClick={() => navigate(`/projects/${projectInfo.id}`)} onClick={() => navigate(`/projects/${projectInfo.id}`)}
> >
{projectInfo.name} {projectInfo.shortName
? `${projectInfo.name} (${projectInfo.shortName})`
: projectInfo.name}
</strong> </strong>
</td> </td>
<td className="text-start small">{projectInfo.contactPerson}</td> <td className="text-start small">{projectInfo.contactPerson}</td>