Compare commits

..

No commits in common. "287d11660965e49672874928b9be8308f0d1af78" and "c88af2441fa1c4ff0bead4afb68201ea4d85108e" have entirely different histories.

22 changed files with 505 additions and 525 deletions

View File

@ -1,6 +1,11 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" lang="en" class="light-style layout-navbar-fixed layout-menu-fixed layout-compact" dir="ltr" <html lang="en" lang="en"
data-theme="theme-default" data-assets-path="/assets/" data-template="vertical-menu-template" data-style="light"> class="light-style layout-navbar-fixed layout-menu-fixed layout-compact"
dir="ltr"
data-theme="theme-default"
data-assets-path="/assets/"
data-template="vertical-menu-template"
data-style="light">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
@ -25,7 +30,6 @@
<!-- Core CSS --> <!-- Core CSS -->
<link rel="stylesheet" href="/assets/vendor/css/core.css" class="template-customizer-core-css" /> <link rel="stylesheet" href="/assets/vendor/css/core.css" class="template-customizer-core-css" />
<link rel="stylesheet" href="/assets/vendor/css/theme-default.css" class="template-customizer-theme-css" /> <link rel="stylesheet" href="/assets/vendor/css/theme-default.css" class="template-customizer-theme-css" />
<link rel="stylesheet" href="/assets/css/core-extend.css" />
<link rel="stylesheet" href="/assets/css/default.css" /> <link rel="stylesheet" href="/assets/css/default.css" />
<link rel="stylesheet" href="/assets/vendor/libs/perfect-scrollbar/perfect-scrollbar.css" /> <link rel="stylesheet" href="/assets/vendor/libs/perfect-scrollbar/perfect-scrollbar.css" />

View File

@ -1,12 +0,0 @@
:root,
[data-bs-theme="light"] {
--bs-nav-link-font-size: 0.7375rem;
}
.nav {
--bs-nav-link-font-size: 0.7375rem;
}
.card-header {
padding: 0.5rem var(--bs-card-cap-padding-x);
}

View File

@ -1,3 +1,7 @@
/*
* demo.css
* File include item demo only specific css only
******************************************************************************/
#root { #root {
width: 100%; width: 100%;
@ -16,28 +20,21 @@
padding: 0 !important; padding: 0 !important;
} }
.menu .app-brand { .menu .app-brand.demo {
height: 64px; height: 64px;
margin-top: 12px; margin-top: 12px;
} }
.app-brand-logo svg { .app-brand-logo.demo svg {
width: 22px; width: 22px;
height: 38px; height: 38px;
} }
.app-brand-logo-sidebar { .app-brand-logo-sidebar {
width: 45px; width: 70px;
} }
.app-brand-logo-login { .app-brand-text.demo {
width: 100px;
}
.app-brand-logo-border {
border: 1px solid #d5d5d5;
}
.app-brand-text {
font-size: 1.75rem; font-size: 1.75rem;
letter-spacing: -0.5px; letter-spacing: -0.5px;
/* text-transform: lowercase; */ /* text-transform: lowercase; */
@ -64,6 +61,39 @@
* Content * Content
******************************************************************************/ ******************************************************************************/
.demo-blocks > * {
display: block !important;
}
.demo-inline-spacing > * {
margin: 1rem 0.375rem 0 0 !important;
}
/* ? .demo-vertical-spacing class is used to have vertical margins between elements. To remove margin-top from the first-child, use .demo-only-element class with .demo-vertical-spacing class. For example, we have used this class in forms-input-groups.html file. */
.demo-vertical-spacing > * {
margin-top: 1rem !important;
margin-bottom: 0 !important;
}
.demo-vertical-spacing.demo-only-element > :first-child {
margin-top: 0 !important;
}
.demo-vertical-spacing-lg > * {
margin-top: 1.875rem !important;
margin-bottom: 0 !important;
}
.demo-vertical-spacing-lg.demo-only-element > :first-child {
margin-top: 0 !important;
}
.demo-vertical-spacing-xl > * {
margin-top: 5rem !important;
margin-bottom: 0 !important;
}
.demo-vertical-spacing-xl.demo-only-element > :first-child {
margin-top: 0 !important;
}
.rtl-only { .rtl-only {
display: none !important; display: none !important;
text-align: left !important; text-align: left !important;
@ -74,6 +104,30 @@
display: block !important; display: block !important;
} }
/*
* Layout demo
******************************************************************************/
.layout-demo-wrapper {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
margin-top: 1rem;
}
.layout-demo-placeholder img {
width: 900px;
}
.layout-demo-info {
text-align: center;
margin-top: 1rem;
}
.infra-activity-table-header { .infra-activity-table-header {
border-top: 0; border-top: 0;
text-transform: capitalize !important; text-transform: capitalize !important;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,3 +1,23 @@
/*
* demo.css
* File include item demo only specific css only
******************************************************************************/
.menu .app-brand.demo {
height: 64px;
margin-top: 12px;
}
.app-brand-logo.demo svg {
width: 22px;
height: 38px;
}
.app-brand-text.demo {
font-size: 1.75rem;
letter-spacing: -0.5px;
}
/* ! For .layout-navbar-fixed added fix padding top to .layout-page */ /* ! For .layout-navbar-fixed added fix padding top to .layout-page */
/* Detached navbar */ /* Detached navbar */
.layout-navbar-fixed .layout-navbar-fixed
@ -23,6 +43,43 @@
z-index: auto; z-index: auto;
} }
/*
* Content
******************************************************************************/
.demo-blocks > * {
display: block !important;
}
.demo-inline-spacing > * {
margin: 1rem 0.375rem 0 0 !important;
}
/* ? .demo-vertical-spacing class is used to have vertical margins between elements. To remove margin-top from the first-child, use .demo-only-element class with .demo-vertical-spacing class. For example, we have used this class in forms-input-groups.html file. */
.demo-vertical-spacing > * {
margin-top: 1rem !important;
margin-bottom: 0 !important;
}
.demo-vertical-spacing.demo-only-element > :first-child {
margin-top: 0 !important;
}
.demo-vertical-spacing-lg > * {
margin-top: 1.875rem !important;
margin-bottom: 0 !important;
}
.demo-vertical-spacing-lg.demo-only-element > :first-child {
margin-top: 0 !important;
}
.demo-vertical-spacing-xl > * {
margin-top: 5rem !important;
margin-bottom: 0 !important;
}
.demo-vertical-spacing-xl.demo-only-element > :first-child {
margin-top: 0 !important;
}
.rtl-only { .rtl-only {
display: none !important; display: none !important;
text-align: left !important; text-align: left !important;
@ -32,3 +89,41 @@
[dir="rtl"] .rtl-only { [dir="rtl"] .rtl-only {
display: block !important; display: block !important;
} }
/* Dropdown buttons going out of small screens */
@media (max-width: 576px) {
#dropdown-variation-demo .btn-group .text-truncate {
width: 231px;
position: relative;
}
#dropdown-variation-demo .btn-group .text-truncate::after {
position: absolute;
top: 45%;
right: 0.65rem;
}
}
/*
* Layout demo
******************************************************************************/
.layout-demo-wrapper {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
margin-top: 1rem;
}
.layout-demo-placeholder img {
width: 900px;
}
.layout-demo-info {
text-align: center;
margin-top: 1rem;
}

View File

@ -4,15 +4,12 @@ import { useProjects } from "../../hooks/useProjects";
import { useDashboard_Data } from "../../hooks/useDashboard_Data"; import { useDashboard_Data } from "../../hooks/useDashboard_Data";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
const ProjectProgressChart = ({ const ProjectProgressChart = () => {
ShowAllProject = true,
DefaultRange = "15D",
}) => {
const selectedProject = useSelector( const selectedProject = useSelector(
(store) => store.localVariables.projectId (store) => store.localVariables.projectId
); );
const { projects } = useProjects(); const { projects } = useProjects();
const [range, setRange] = useState(DefaultRange); const [range, setRange] = useState("15D");
const [showAllEmployees, setShowAllEmployees] = useState(false); const [showAllEmployees, setShowAllEmployees] = useState(false);
const getDaysFromRange = (range) => { const getDaysFromRange = (range) => {
@ -72,7 +69,7 @@ const ProjectProgressChart = ({
); );
const lineChartCategoriesDates = sortedDashboardData.map((d) => const lineChartCategoriesDates = sortedDashboardData.map((d) =>
new Date(d.date).toLocaleDateString("en-US", { new Date(d.date).toLocaleDateString("en-US", {
weekday: "short", weekday:"short",
month: "short", month: "short",
day: "numeric", day: "numeric",
year: "numeric", year: "numeric",
@ -85,7 +82,7 @@ const ProjectProgressChart = ({
: selectedProjectData?.name; : selectedProjectData?.name;
return ( return (
<div className="card"> <div className="card h-100">
<div className="card-header"> <div className="card-header">
<div className="d-flex flex-wrap justify-content-between align-items-start mb-2"> <div className="d-flex flex-wrap justify-content-between align-items-start mb-2">
{/* Left: Title */} {/* Left: Title */}
@ -96,24 +93,22 @@ const ProjectProgressChart = ({
{/* Right: Checkbox and Project Name */} {/* Right: Checkbox and Project Name */}
<div className="d-flex flex-column align-items-start align-items-md-end text-start text-md-end mt-1 mt-md-0"> <div className="d-flex flex-column align-items-start align-items-md-end text-start text-md-end mt-1 mt-md-0">
{ShowAllProject == true && ( <div className="form-check form-switch mb-1 d-flex align-items-center">
<div className="form-check form-switch mb-1 d-flex align-items-center"> <input
<input className="form-check-input"
className="form-check-input" type="checkbox"
type="checkbox" role="switch"
role="switch" id="showAllEmployees"
id="showAllEmployees" checked={showAllEmployees}
checked={showAllEmployees} onChange={(e) => setShowAllEmployees(e.target.checked)}
onChange={(e) => setShowAllEmployees(e.target.checked)} />
/> <label
<label className="form-check-label ms-2"
className="form-check-label ms-2" htmlFor="showAllEmployees"
htmlFor="showAllEmployees" >
> All Projects
All Projects </label>
</label> </div>
</div>
)}
{!showAllEmployees && selectedProjectName && ( {!showAllEmployees && selectedProjectName && (
<p className="text-muted mb-0 small"> <p className="text-muted mb-0 small">
<span className="card-subtitle">{selectedProjectName}</span> <span className="card-subtitle">{selectedProjectName}</span>

View File

@ -19,10 +19,6 @@ const NoteCardDirectory = ({
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isDeleting, setIsDeleting] = useState(false); const [isDeleting, setIsDeleting] = useState(false);
const [isActivProcess, setActiveProcessing] = useState(false); const [isActivProcess, setActiveProcessing] = useState(false);
// State to manage hover status
const [isHovered, setIsHovered] = useState(false);
const handleUpdateNote = async () => { const handleUpdateNote = async () => {
try { try {
setIsLoading(true); setIsLoading(true);
@ -131,8 +127,6 @@ const NoteCardDirectory = ({
background: `${noteItem.isActive ? "" : "#f8f6f6"}`, background: `${noteItem.isActive ? "" : "#f8f6f6"}`,
}} }}
key={noteItem.id} key={noteItem.id}
onMouseEnter={() => setIsHovered(true)} // Set hover state to true on mouse enter
onMouseLeave={() => setIsHovered(false)} // Set hover state to false on mouse leave
> >
<div className="d-flex justify-content-between align-items-center mb-1"> <div className="d-flex justify-content-between align-items-center mb-1">
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
@ -155,15 +149,11 @@ const NoteCardDirectory = ({
</span> </span>
</div> </div>
</div> </div>
<div <div>
className={`absolute top-4 right-4 flex space-x-2 transition-opacity duration-300 ${
isHovered ? "opacity-100" : "opacity-0"
}`}
>
{noteItem.isActive ? ( {noteItem.isActive ? (
<> <>
<i <i
className="p-2 bg-blue-500 text-white rounded-full shadow-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-75" className="bx bxs-edit bx-sm me-1 text-primary cursor-pointer"
onClick={() => setEditing(true)} onClick={() => setEditing(true)}
></i> ></i>

View File

@ -1,9 +1,9 @@
const LayoutMenu = () => { const LayoutMenu = () => {
return ( return (
<aside id="layout-menu" className="layout-menu menu-vertical menu bg-menu-theme"> <aside id="layout-menu" className="layout-menu menu-vertical menu bg-menu-theme">
<div className="app-brand"> <div className="app-brand demo">
<a href="index.html" className="app-brand-link"> <a href="index.html" className="app-brand-link">
<span className="app-brand-logo"> <span className="app-brand-logo demo">
<svg <svg
width="25" width="25"
viewBox="0 0 25 42" viewBox="0 0 25 42"
@ -58,7 +58,7 @@ const LayoutMenu = () => {
</g> </g>
</svg> </svg>
</span> </span>
<span className="app-brand-text menu-text fw-bolder ms-2">Marco</span> <span className="app-brand-text demo menu-text fw-bolder ms-2">Marco</span>
</a> </a>
<a href="javascript:void(0);" className="layout-menu-toggle menu-link text-large ms-auto d-block d-xl-none"> <a href="javascript:void(0);" className="layout-menu-toggle menu-link text-large ms-auto d-block d-xl-none">

View File

@ -1,31 +1,43 @@
import React from "react"; import React from "react";
import { Link, NavLink, useLocation, useNavigate } from "react-router-dom"; import { Link, NavLink, useLocation, useNavigate } from "react-router-dom";
import menuData from "../../data/menuData.json"; import menuData from "../../data/menuData.json";
import { getCachedProfileData } from "../../slices/apiDataManager"; import {getCachedProfileData} from "../../slices/apiDataManager";
const Sidebar = () => { const Sidebar = () => {
const navigate = useNavigate(); // const logineUser = getCachedProfileData()
const navigate = useNavigate()
// const handleLogout = (e) => {
// e.preventDefault();
// // logout();
// };
// const handleProfilePage = ()=>{
// navigate(`/employee/${profile?.employeeInfo?.id}?for=account`)
// }
return ( return (
<aside <aside
id="layout-menu" id="layout-menu"
className="layout-menu menu-vertical menu bg-menu-theme " className="layout-menu menu-vertical menu bg-menu-theme "
> >
<div className="app-brand" style={{ paddingLeft: "30px" }}> <div className="app-brand demo">
<Link to="/dashboard" className="app-brand-link"> <Link to="/dashboard" className="app-brand-link">
<span className="app-brand-logo rounded-circle app-brand-logo-border"> <span className="app-brand-logo demo">
<img <img
className="app-brand-logo-sidebar" className="app-brand-logo-sidebar"
src="/img/brand/marco.png" src="/img/brand/marco.png"
alt="logo" alt="logo"
aria-label="logo image" aria-label="logo image"
style={{ margin: "5px", paddingRight: "5px" }}
/> />
</span> </span>
<span className="app-brand-text menu-text fw-bold ms-2">PMS</span> <span className="app-brand-text demo menu-text fw-bold ms-2">
PMS
</span>
</Link> </Link>
<a className="layout-menu-toggle menu-link text-large ms-auto"> <a
className="layout-menu-toggle menu-link text-large ms-auto"
>
<i className="bx bx-chevron-left bx-sm d-flex align-items-center justify-content-center"></i> <i className="bx bx-chevron-left bx-sm d-flex align-items-center justify-content-center"></i>
</a> </a>
</div> </div>
@ -44,6 +56,87 @@ const Sidebar = () => {
</React.Fragment> </React.Fragment>
))} ))}
</ul> </ul>
{/* <div className="dropdown py-sm-4 mt-sm-auto ms-auto ms-sm-0 flex-shrink-1 ps-5">
<a
href="#"
className="d-flex align-items-center text-decoration-none dropdown-toggle"
id="dropdownUser1"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<img
src="https://github.com/mdo.png"
alt="hugenerd"
width="28"
height="28"
className="rounded-circle"
/>
<span className="d-none d-sm-inline mx-1">{ logineUser?.employeeInfo?.firstName}</span>
</a>
<ul className="dropdown-menu dropdown-menu-end">
<li onClick={handleProfilePage}>
<a aria-label="go to profile" className="dropdown-item">
<div className="d-flex">
<div className="flex-shrink-0 me-3">
<div className="avatar avatar-online">
<img
src="../assets/img/avatars/00.jpg"
className="w-px-40 h-auto rounded-circle"
alt="avatar-image"
aria-label="Avatar Image"
/>
</div>
</div>
<div className="flex-grow-1">
<span className="fw-medium d-block">{ logineUser?.employeeInfo?.firstName}</span>
<small className="text-muted">Admin</small>
</div>
</div>
</a>
</li>
<li>
<div className="dropdown-divider"></div>
</li>
<li onClick={handleProfilePage}>
<a aria-label="go to profile" className="dropdown-item" >
<i className="bx bx-user me-2"></i>
<span className="align-middle">My Profile</span>
</a>
</li>
<li>
<a aria-label="go to setting" className="dropdown-item" href="#">
<i className="bx bx-cog me-2"></i>
<span className="align-middle">Settings</span>
</a>
</li>
<li>
<a aria-label="go to billing" className="dropdown-item" href="#">
<span className="d-flex align-items-center align-middle">
<i className="flex-shrink-0 bx bx-credit-card me-2"></i>
<span className="flex-grow-1 align-middle ms-1">Billing</span>
<span className="flex-shrink-0 badge badge-center rounded-pill bg-danger w-px-20 h-px-20">
4
</span>
</span>
</a>
</li>
<li>
<div className="dropdown-divider"></div>
</li>
<li>
<a
aria-label="click to log out"
className="dropdown-item"
href="/logout" // Optional: Add this for accessibility, but it won't actually redirect
onClick={handleLogout}
>
<i className="bx bx-power-off me-2"></i>
<span className="align-middle">Log Out</span>
</a>
</li>
</ul>
</div> */}
</aside> </aside>
); );
}; };

View File

@ -1,96 +1,23 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import moment from "moment"; import moment from "moment";
import { getProjectStatusName } from "../../utils/projectStatus"; import { getProjectStatusName } from "../../utils/projectStatus";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { MANAGE_PROJECT } from "../../utils/constants";
import { cacheData, getCachedData } from "../../slices/apiDataManager";
import ManageProjectInfo from "./ManageProjectInfo";
const AboutProject = ({ data }) => { const AboutProject = ({ data }) => {
const [CurrentProject, setCurrentProject] = useState(data); const [CurrentProject, setCurrentProject] = useState(data);
const [showModal, setShowModal] = useState(false);
useEffect(() => { useEffect(() => {
setCurrentProject(data); setCurrentProject(data);
}, [data]); }, [data]);
const manageProject = useHasUserPermission(MANAGE_PROJECT);
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");
});
}
};
return ( return (
<> <>
<div
className={`modal fade ${showModal ? "show" : ""}`}
tabIndex="-1"
role="dialog"
style={{ display: showModal ? "block" : "none" }}
aria-hidden={!showModal}
>
<ManageProjectInfo
project={CurrentProject}
handleSubmitForm={handleFormSubmit}
onClose={handleClose}
></ManageProjectInfo>
</div>
{data && ( {data && (
<div className="card mb-6"> <div className="card mb-6">
<div className="card-header text-start">
<h6 className="card-action-title mb-0">
{" "}
<i className="fa fa-building rounded-circle text-primary"></i>
<span className="ms-2">Project Profile</span>
</h6>
</div>
<div className="card-body"> <div className="card-body">
<ul className="list-unstyled my-3 ps-2"> <small className="card-text text-uppercase text-muted small">
<li className="d-flex align-items-center mb-3"> Profile
<i className="bx bx-cog"></i> </small>
<span className="fw-medium mx-2">Name:</span>{" "} <ul className="list-unstyled my-3">
<span>{data.name}</span> <li className="d-flex align-items-center mb-4">
</li>
<li className="d-flex align-items-center mb-3">
<i className="bx bx-fingerprint"></i>
<span className="fw-medium mx-2">Nick Name:</span>{" "}
<span> {data.shortName} </span>
</li>
<li className="d-flex align-items-center mb-3">
<i className="bx bx-check"></i> <i className="bx bx-check"></i>
<span className="fw-medium mx-2">Start Date:</span>{" "} <span className="fw-medium mx-2">Start Date:</span>{" "}
<span> <span>
@ -99,7 +26,7 @@ const AboutProject = ({ data }) => {
: "N/A"} : "N/A"}
</span> </span>
</li> </li>
<li className="d-flex align-items-center mb-3"> <li className="d-flex align-items-center mb-4">
<i className="bx bx-stop-circle"></i>{" "} <i className="bx bx-stop-circle"></i>{" "}
<span className="fw-medium mx-2">End Date:</span>{" "} <span className="fw-medium mx-2">End Date:</span>{" "}
<span> <span>
@ -108,17 +35,17 @@ const AboutProject = ({ data }) => {
: "N/A"} : "N/A"}
</span> </span>
</li> </li>
<li className="d-flex align-items-center mb-3"> <li className="d-flex align-items-center mb-2">
<i className="bx bx-trophy"></i> <i className="bx bx-trophy"></i>
<span className="fw-medium mx-2">Status:</span>{" "} <span className="fw-medium mx-2">Status:</span>{" "}
<span>{getProjectStatusName(data.projectStatusId)}</span> <span>{getProjectStatusName(data.projectStatusId)}</span>
</li> </li>
<li className="d-flex align-items-center mb-3"> <li className="d-flex align-items-center mb-4">
<i className="bx bx-user"></i> <i className="bx bx-user"></i>
<span className="fw-medium mx-2">Contact:</span>{" "} <span className="fw-medium mx-2">Contact:</span>{" "}
<span>{data.contactPerson}</span> <span>{data.contactPerson}</span>
</li> </li>
<li className="d-flex flex-column align-items-start mb-3"> <li className="d-flex flex-column align-items-start mb-4">
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
<i className="bx bx-flag"></i> <i className="bx bx-flag"></i>
<span className="fw-medium mx-2">Address:</span> <span className="fw-medium mx-2">Address:</span>
@ -130,22 +57,6 @@ const AboutProject = ({ data }) => {
<div className="ms-4 text-start">{data.projectAddress}</div> <div className="ms-4 text-start">{data.projectAddress}</div>
)} )}
</li> </li>
<li className="d-flex justify-content-center mb-3">
{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}
>
Modify Details
</button>
)}
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -4,18 +4,6 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod"; import { z } from "zod";
// const currentDate = new Date().toISOString().split("T")[0]; // const currentDate = new Date().toISOString().split("T")[0];
const ACTIVE_STATUS_ID = "b74da4c2-d07e-46f2-9919-e75e49b12731";
const DEFAULT_EMPTY_STATUS_ID = "00000000-0000-0000-0000-000000000000";
/**
* Formats a given date string into 'YYYY-MM-DD' format.
* If the date is invalid or not provided, it defaults to the current date.
* @param {string} date - The date string to format.
* @returns {string} The formatted date string.
*/
const currentDate = new Date().toLocaleDateString('en-CA'); const currentDate = new Date().toLocaleDateString('en-CA');
const formatDate = (date) => { const formatDate = (date) => {
if (!date) { if (!date) {
@ -41,7 +29,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
shortName: z.string().optional(), 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"} )
.regex(/^[A-Za-z\s]+$/, { .regex(/^[A-Za-z\s]+$/, {
message: "Contact Person must contain only letters", message: "Contact Person must contain only letters",
}), }),
@ -65,7 +53,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
(data) => { (data) => {
const start = new Date(data.startDate); const start = new Date(data.startDate);
const end = new Date(data.endDate); const end = new Date(data.endDate);
return end >= start; return end > start;
}, },
{ {
path: ["endDate"], // attaches the error to the endDate field path: ["endDate"], // attaches the error to the endDate field
@ -90,12 +78,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
projectAddress: project?.projectAddress || "", projectAddress: project?.projectAddress || "",
startDate: formatDate(project?.startDate) || currentDate, startDate: formatDate(project?.startDate) || currentDate,
endDate: formatDate(project?.endDate) || currentDate, endDate: formatDate(project?.endDate) || currentDate,
// projectStatusId: String(project?.projectStatusId || "00000000-0000-0000-0000-000000000000"), projectStatusId: String(project?.projectStatusId || "00000000-0000-0000-0000-000000000000"),
projectStatusId: project?.projectStatusId && project.projectStatusId !== DEFAULT_EMPTY_STATUS_ID
? String(project.projectStatusId)
: ACTIVE_STATUS_ID,
}, },
mode: "onChange", mode: "onChange",
}); });
@ -105,35 +88,24 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
reset( reset(
project project
? { ? {
id: project?.id || "", id: project?.id || "",
name: project?.name || "", name: project?.name || "",
shortName: project?.shortName || "", shortName: project?.shortName || "",
contactPerson: project?.contactPerson || "", contactPerson: project?.contactPerson || "",
projectAddress: project?.projectAddress || "", projectAddress: project?.projectAddress || "",
startDate: formatDate(project?.startDate) || "", startDate: formatDate(project?.startDate) || "",
endDate: formatDate(project?.endDate) || "", endDate: formatDate(project?.endDate) || "",
// projectStatusId: String(project.projectStatusId) || "00000000-0000-0000-0000-000000000000", projectStatusId: String(project.projectStatusId) || "00000000-0000-0000-0000-000000000000",
projectStatusId: project?.projectStatusId && project.projectStatusId !== DEFAULT_EMPTY_STATUS_ID }
? String(project.projectStatusId)
: ACTIVE_STATUS_ID,
}
: {} : {}
); );
setAddressLength(project?.projectAddress?.length || 0); setAddressLength(project?.projectAddress?.length || 0);
}, [project, reset]); }, [project, reset]);
/**
* Handles the form submission.
* @param {object} updatedProject - The project data from the form.
*/
const onSubmitForm = (updatedProject) => { const onSubmitForm = (updatedProject) => {
setLoading(true); setLoading(true);
handleSubmitForm(updatedProject, setLoading, reset); handleSubmitForm( updatedProject, setLoading,reset );
}; };
const handleCancel = () => { const handleCancel = () => {
reset({ reset({
@ -144,10 +116,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
projectAddress: project?.projectAddress || "", projectAddress: project?.projectAddress || "",
startDate: formatDate(project?.startDate) || currentDate, startDate: formatDate(project?.startDate) || currentDate,
endDate: formatDate(project?.endDate) || currentDate, endDate: formatDate(project?.endDate) || currentDate,
// projectStatusId: String(project?.projectStatusId || "00000000-0000-0000-0000-000000000000"), projectStatusId: String(project?.projectStatusId || "00000000-0000-0000-0000-000000000000"),
projectStatusId: project?.projectStatusId && project.projectStatusId !== DEFAULT_EMPTY_STATUS_ID
? String(project.projectStatusId)
: ACTIVE_STATUS_ID,
}); });
onClose(); onClose();
}; };
@ -290,9 +259,8 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
valueAsNumber: false, valueAsNumber: false,
})} })}
> >
{/* <option disabled>Status</option> <option disabled>Status</option>
<option value="b74da4c2-d07e-46f2-9919-e75e49b12731">Active</option> */} <option value="b74da4c2-d07e-46f2-9919-e75e49b12731">Active</option>
<option value={ACTIVE_STATUS_ID}>Active</option>
<option value="603e994b-a27f-4e5d-a251-f3d69b0498ba">On Hold</option> <option value="603e994b-a27f-4e5d-a251-f3d69b0498ba">On Hold</option>
<option value="cdad86aa-8a56-4ff4-b633-9c629057dfef">In Progress</option> <option value="cdad86aa-8a56-4ff4-b633-9c629057dfef">In Progress</option>
@ -358,4 +326,4 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
); );
}; };
export default ManageProjectInfo; export default ManageProjectInfo;

View File

@ -1,124 +1,33 @@
import React from "react"; import React from "react";
import { import {useEmployeesByProjectAllocated, useProjects} from "../../hooks/useProjects";
useEmployeesByProjectAllocated,
useProjects,
} from "../../hooks/useProjects";
const ProjectOverview = ({ project }) => {
const { projects } = useProjects();
const getProgress = (planned, completed) => {
return (completed * 100) / planned + "%";
};
const getProgressInNumber = (planned, completed) => {
var number = (completed * 100) / planned;
return FormattedNumber(number);
};
const project_detail = projects.find((pro) => pro.id == project);
// Utility function to check if a number has a decimal part
const hasDecimal = (num) => {
// Convert to string and check for a decimal point
// Or, check if the number is not equal to its integer part
return num % 1 !== 0;
};
// FormattedNumber component to display numbers with conditional decimal places
function FormattedNumber(value, locale = "en-US") {
// Ensure the value is a number
const numericValue = parseFloat(value);
// Handle non-numeric values gracefully
if (isNaN(numericValue)) {
return <span>Invalid Number</span>;
}
let options = {};
// Determine formatting options based on whether the number has a decimal part
if (hasDecimal(numericValue)) {
// If it has a decimal, format to exactly two decimal places
options = {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
};
} else {
// If it's a whole number, format to zero decimal places
options = {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
};
}
// Use Intl.NumberFormat for robust and locale-aware formatting
const formattedString = new Intl.NumberFormat(locale, options).format(
numericValue
);
return formattedString;
}
const ProjectOverview = ({project}) =>
{
const {projects} = useProjects()
const project_detail = projects.find( ( pro ) => pro.id == project )
return ( return (
<div className="card mb-6"> <div className="card mb-6">
<div className="card-header text-start">
<h6 className="card-action-title mb-0">
{" "}
<i className="fa fa-line-chart rounded-circle text-primary"></i>
<span className="ms-2">Project Statistics</span>
</h6>
</div>
<div className="card-body"> <div className="card-body">
<small className="card-text text-uppercase text-muted small">
Overview
</small>
<ul className="list-unstyled mb-0 mt-3 pt-1"> <ul className="list-unstyled mb-0 mt-3 pt-1">
<li className="d-flex align-items-center mb-3"> <li className="d-flex align-items-center mb-4">
<i className="bx bx-check"></i> <i className="bx bx-check"></i>
<span className="fw-medium mx-2">Task Planned:</span>{" "} <span className="fw-medium mx-2">Task Planned:</span>{" "}
<span>{project_detail?.plannedWork}</span> <span>{project_detail?.plannedWork
}</span>
</li> </li>
<li className="d-flex align-items-center mb-3"> <li className="d-flex align-items-center mb-4">
<i className="bx bx-star"></i> <i className="bx bx-star"></i>
<span className="fw-medium mx-2">Task Completed:</span>{" "} <span className="fw-medium mx-2">Task Completed:</span>{" "}
<span>{project_detail?.completedWork}</span> <span>{project_detail?.completedWork }</span>
</li> </li>
<li className="d-flex align-items-center mb-3"> <li className="d-flex align-items-center">
<i className="bx bx-user"></i> <i className="bx bx-user"></i>
<span className="fw-medium mx-2">Current team Size:</span>{" "} <span className="fw-medium mx-2">Current team Size:</span>{" "}
<span>{project_detail?.teamSize}</span> <span>{project_detail?.teamSize}</span>
</li> </li>
<li className=" mb-3">
{project_detail && (
<>
<div className="d-flex text-end mb-2 mt-5">
<small className="text-body text-muted ">
{Math.floor(
getProgressInNumber(
project_detail.plannedWork,
project_detail.completedWork
)
) || 0}{" "}
% Completed
</small>
</div>
<div
className="progress mb-4 rounded"
style={{ height: "8px" }}
>
<div
className="progress-bar rounded"
role="progressbar"
style={{
width: getProgress(
project_detail.plannedWork,
project_detail.completedWork
),
}}
aria-valuenow={project_detail.completedWork}
aria-valuemin="0"
aria-valuemax={project_detail.plannedWork}
></div>
</div>
</>
)}
</li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -2,40 +2,37 @@ import React, { useEffect, useRef } from "react";
const DateRangePicker = ({ const DateRangePicker = ({
onRangeChange, onRangeChange,
DateDifference = 7, DateDifference = 7,
endDateMode = "yesterday", endDateMode = "yesterday", // "today" or "yesterday"
}) => { }) => {
const inputRef = useRef(null); const inputRef = useRef(null);
useEffect(() => { useEffect(() => {
const endDate = new Date(); const endDate = new Date();
if (endDateMode === "yesterday") { if (endDateMode === "yesterday") {
endDate.setDate(endDate.getDate() - 1); endDate.setDate(endDate.getDate() - 1);
} }
endDate.setHours(0, 0, 0, 0); const startDate = new Date();
startDate.setDate(endDate.getDate() - DateDifference);
const startDate = new Date(endDate);
startDate.setDate(endDate.getDate() - (DateDifference - 1));
startDate.setHours(0, 0, 0, 0);
const fp = flatpickr(inputRef.current, { const fp = flatpickr(inputRef.current, {
mode: "range", mode: "range",
dateFormat: "Y-m-d", dateFormat: "Y-m-d",
altInput: true, altInput: true,
altFormat: "d-m-Y", altFormat: "d-m-Y",
defaultDate: [startDate, endDate], defaultDate: [startDate, endDate],
static: true, static: true,
clickOpens: true, clickOpens: true,
onChange: (selectedDates, dateStr) => { onChange: (selectedDates, dateStr) => {
const [startDateString, endDateString] = dateStr.split(" to "); const [startDate, endDate] = dateStr.split(" to ");
onRangeChange?.({ startDate: startDateString, endDate: endDateString }); onRangeChange?.({ startDate, endDate });
}, },
}); });
onRangeChange?.({ onRangeChange?.({
startDate: startDate.toLocaleDateString("en-CA"), startDate: startDate.toLocaleDateString("en-CA"),
endDate: endDate.toLocaleDateString("en-CA"), endDate: endDate.toLocaleDateString("en-CA"),
}); });
return () => { return () => {

View File

@ -50,7 +50,7 @@
{ {
"text": "Daily Expenses", "text": "Daily Expenses",
"available": true, "available": true,
"link": "/activities/reports" "link": "/activities/gallary"
} }
] ]
}, },

View File

@ -306,7 +306,7 @@ const DirectoryPageHeader = ({
)} )}
</a> </a>
<div className="dropdown-menu p-0" style={{ minWidth: "550px" }}> <div className="dropdown-menu p-0" style={{ minWidth: "700px" }}>
{/* Scrollable Filter Content */} {/* Scrollable Filter Content */}
<div <div
className="p-3" className="p-3"
@ -319,19 +319,18 @@ const DirectoryPageHeader = ({
> >
<div className="d-flex gap-3"> <div className="d-flex gap-3">
{/* Created By */} {/* Created By */}
<div style={{ flexBasis: "30%", maxHeight: "260px", overflowY: "auto" }}> <div style={{ flex: 0.50, maxHeight: "260px", overflowY: "auto" }}>
<div style={{ position: "sticky", top: 0, background: "#fff", zIndex: 1 }}> <div style={{ position: "sticky", top: 0, background: "#fff", zIndex: 1 }}>
<p className="text-muted mb-2 pt-2">Created By</p> <p className="text-muted mb-2 pt-2">Created By</p>
</div> </div>
{allCreators.map((name, idx) => ( {allCreators.map((name, idx) => (
<div className="form-check mb-1" key={`creator-${idx}`}> <div className="form-check mb-1" key={`creator-${idx}`}>
<input <input
className="form-check-input form-check-input-sm" className="form-check-input"
type="checkbox" type="checkbox"
id={`creator-${idx}`} id={`creator-${idx}`}
checked={selectedCreators.includes(name)} checked={selectedCreators.includes(name)}
onChange={() => handleToggleCreator(name)} onChange={() => handleToggleCreator(name)}
style={{ width: "1rem", height: "1rem" }}
/> />
<label className="form-check-label text-nowrap" htmlFor={`creator-${idx}`}> <label className="form-check-label text-nowrap" htmlFor={`creator-${idx}`}>
{name} {name}
@ -341,19 +340,18 @@ const DirectoryPageHeader = ({
</div> </div>
{/* Organization */} {/* Organization */}
<div style={{ maxHeight: "260px", overflowY: "auto",overflowX: "hidden", }}> <div style={{ flex: 1, maxHeight: "260px", overflowY: "auto",overflowX: "hidden", }}>
<div style={{ position: "sticky", top: 0, background: "#fff", zIndex: 1 }}> <div style={{ position: "sticky", top: 0, background: "#fff", zIndex: 1 }}>
<p className="text-muted mb-2 pt-2">Organization</p> <p className="text-muted mb-2 pt-2">Organization</p>
</div> </div>
{filteredOrganizations.map((org, idx) => ( {filteredOrganizations.map((org, idx) => (
<div className="form-check mb-1" key={`org-${idx}`}> <div className="form-check mb-1" key={`org-${idx}`}>
<input <input
className="form-check-input form-check-input-sm" className="form-check-input"
type="checkbox" type="checkbox"
id={`org-${idx}`} id={`org-${idx}`}
checked={selectedOrgs.includes(org)} checked={selectedOrgs.includes(org)}
onChange={() => handleToggleOrg(org)} onChange={() => handleToggleOrg(org)}
style={{ width: "1rem", height: "1rem" }}
/> />
<label className="form-check-label text-nowrap" htmlFor={`org-${idx}`}> <label className="form-check-label text-nowrap" htmlFor={`org-${idx}`}>
{org} {org}
@ -446,12 +444,11 @@ const DirectoryPageHeader = ({
{filteredBuckets.map(({ id, name }) => ( {filteredBuckets.map(({ id, name }) => (
<div className="form-check me-3 mb-1" style={{ minWidth: "calc(50% - 15px)" }} key={id}> <div className="form-check me-3 mb-1" style={{ minWidth: "calc(50% - 15px)" }} key={id}>
<input <input
className="form-check-input form-check-input-sm" className="form-check-input"
type="checkbox" type="checkbox"
id={`bucket-${id}`} id={`bucket-${id}`}
checked={tempSelectedBucketIds.includes(id)} checked={tempSelectedBucketIds.includes(id)}
onChange={() => handleTempBucketChange(id)} onChange={() => handleTempBucketChange(id)}
style={{ width: "1rem", height: "1rem" }}
/> />
<label className="form-check-label text-nowrap text-small" htmlFor={`bucket-${id}`}> <label className="form-check-label text-nowrap text-small" htmlFor={`bucket-${id}`}>
{name} {name}
@ -466,12 +463,11 @@ const DirectoryPageHeader = ({
{filteredCategories.map(({ id, name }) => ( {filteredCategories.map(({ id, name }) => (
<div className="form-check me-3 mb-1" style={{ minWidth: "calc(50% - 15px)" }} key={id}> <div className="form-check me-3 mb-1" style={{ minWidth: "calc(50% - 15px)" }} key={id}>
<input <input
className="form-check-input form-check-input-sm" className="form-check-input"
type="checkbox" type="checkbox"
id={`cat-${id}`} id={`cat-${id}`}
checked={tempSelectedCategoryIds.includes(id)} checked={tempSelectedCategoryIds.includes(id)}
onChange={() => handleTempCategoryChange(id)} onChange={() => handleTempCategoryChange(id)}
style={{ width: "1rem", height: "1rem" }}
/> />
<label className="form-check-label text-nowrap text-small" htmlFor={`cat-${id}`}> <label className="form-check-label text-nowrap text-small" htmlFor={`cat-${id}`}>
{name} {name}

View File

@ -212,7 +212,7 @@ const ImageGallery = () => {
map.set(id, name); map.set(id, name);
} }
}); });
return Array.from(map.entries()).sort((a, b) => a[1].localeCompare(b[1])); return Array.from(map.entries());
}, [allImagesData]); }, [allImagesData]);
const getUniqueUploadedByUsers = useCallback(() => { const getUniqueUploadedByUsers = useCallback(() => {
@ -227,7 +227,7 @@ const ImageGallery = () => {
} }
}); });
}); });
return Array.from(uniqueUsersMap.entries()).sort((a, b) => a[1].localeCompare(b[1])); return Array.from(uniqueUsersMap.entries());
}, [allImagesData]); }, [allImagesData]);
const buildings = getUniqueValuesWithIds("buildingId", "buildingName"); const buildings = getUniqueValuesWithIds("buildingId", "buildingName");
@ -365,8 +365,8 @@ const ImageGallery = () => {
}, []); }, []);
const renderFilterCategory = (label, items, type) => ( const renderFilterCategory = (label, items, type) => (
<div className={`dropdown my-2 ${collapsedFilters[type] ? "collapsed" : ""}`}> <div className={`dropdown ${collapsedFilters[type] ? "collapsed" : ""}`}>
<div className="dropdown-header bg-label-primary" onClick={() => toggleCollapse(type)}> <div className="dropdown-header" onClick={() => toggleCollapse(type)}>
<strong>{label}</strong> <strong>{label}</strong>
<div className="header-controls"> <div className="header-controls">
{type === "dateRange" && (selectedFilters.startDate || selectedFilters.endDate) && ( {type === "dateRange" && (selectedFilters.startDate || selectedFilters.endDate) && (
@ -595,14 +595,6 @@ const ImageGallery = () => {
aria-label="Close" aria-label="Close"
></button> ></button>
</div> </div>
<div className="filter-actions mt-auto mx-2">
<button className="btn btn-secondary btn-xs" onClick={handleClearAllFilters}>
Clear All
</button>
<button className="btn btn-primary btn-xs" onClick={handleApplyFilters}>
Apply Filters
</button>
</div>
<div className="offcanvas-body d-flex flex-column"> <div className="offcanvas-body d-flex flex-column">
{renderFilterCategory("Date Range", [], "dateRange")} {renderFilterCategory("Date Range", [], "dateRange")}
{renderFilterCategory("Building", buildings, "building")} {renderFilterCategory("Building", buildings, "building")}
@ -612,7 +604,14 @@ const ImageGallery = () => {
{renderFilterCategory("Uploaded By (User)", uploadedByUsers, "uploadedBy")} {renderFilterCategory("Uploaded By (User)", uploadedByUsers, "uploadedBy")}
{renderFilterCategory("Work Category", workCategories, "workCategory")} {renderFilterCategory("Work Category", workCategories, "workCategory")}
<div className="filter-actions mt-auto">
<button className="btn btn-secondary btn-xs" onClick={handleClearAllFilters}>
Clear All
</button>
<button className="btn btn-primary btn-xs" onClick={handleApplyFilters}>
Apply Filters
</button>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -3,7 +3,9 @@
gap: 4px; gap: 4px;
padding: 25px; padding: 25px;
font-family: sans-serif; font-family: sans-serif;
height: calc(100vh - 20px);
box-sizing: border-box; box-sizing: border-box;
background-color: #f7f9fc;
transition: grid-template-columns 0.3s ease-in-out; transition: grid-template-columns 0.3s ease-in-out;
} }
@ -65,9 +67,8 @@
transition: background-color 0.2s ease, box-shadow 0.2s ease, width 0.3s ease-in-out, transition: background-color 0.2s ease, box-shadow 0.2s ease, width 0.3s ease-in-out,
height 0.3s ease-in-out, border-radius 0.3s ease-in-out, padding 0.3s ease-in-out; height 0.3s ease-in-out, border-radius 0.3s ease-in-out, padding 0.3s ease-in-out;
position: absolute; position: absolute;
top: 150px; top: 250px;
right: 0; right: 0;
position: fixed;
height: 40px; height: 40px;
width: 40px; width: 40px;
z-index: 100; z-index: 100;
@ -137,7 +138,7 @@
border-radius: 0 0 4px 4px; border-radius: 0 0 4px 4px;
max-height: 150px; max-height: 150px;
/* Default max-height for scrollable dropdowns */ /* Default max-height for scrollable dropdowns */
overflow-y: auto;
/* Default overflow for scrollable dropdowns */ /* Default overflow for scrollable dropdowns */
transition: max-height 0.3s ease-in-out, padding 0.3s ease-in-out; transition: max-height 0.3s ease-in-out, padding 0.3s ease-in-out;
} }
@ -194,6 +195,9 @@
transition: background 0.2s; transition: background 0.2s;
} }
.dropdown-content label:hover {
background-color: #eef2ff;
}
.dropdown-content input[type="checkbox"] { .dropdown-content input[type="checkbox"] {
-webkit-appearance: none; -webkit-appearance: none;

View File

@ -14,12 +14,8 @@ export const AuthWrapper = ({ children }) => {
to="/" to="/"
className="app-brand-link gap-2" className="app-brand-link gap-2"
> >
<span className="app-brand-logo rounded-circle "> <span className="app-brand-logo demo">
<img <img src="/img/brand/marco.png" alt="marco-logo" />
src="/img/brand/marco.png"
alt="marco-logo"
className="app-brand-logo-login"
/>
</span> </span>
</Link> </Link>
</div> </div>

View File

@ -52,6 +52,7 @@ const ChangePasswordPage = ({ onClose }) => {
oldPassword: data.oldPassword, oldPassword: data.oldPassword,
newPassword: data.newPassword, newPassword: data.newPassword,
}; };
console.log(formData);
setLoading(true); setLoading(true);
await AuthRepository.changepassword(formData); await AuthRepository.changepassword(formData);
showToast("Your Password changed Successfully", "success"); showToast("Your Password changed Successfully", "success");
@ -71,20 +72,23 @@ const ChangePasswordPage = ({ onClose }) => {
role="dialog" role="dialog"
style={{ display: "flex", backgroundColor: "rgba(0,0,0,0.5)" }} style={{ display: "flex", backgroundColor: "rgba(0,0,0,0.5)" }}
> >
<div className="modal-dialog" role="document"> <div
<div className="modal-header"> className="modal-dialog"
{" "} role="document"
style={{
maxWidth: "600px",
width: "100%",
}}
>
<div className="modal-content p-4 rounded shadow bg-white position-relative">
{/* Close Button */}
<button <button
type="button" type="button"
class="btn-close" className="btn-close position-absolute"
data-bs-dismiss="modal" style={{ top: "30px", right: "25px" }}
style={{ top: "40px", right: "15px" }}
aria-label="Close" aria-label="Close"
onClick={onClose}
></button> ></button>
</div>
<div className="modal-content p-10 rounded shadow bg-white position-relative">
{/* Close Button */}
<h5 className="mb-2">Change Password</h5> <h5 className="mb-2">Change Password</h5>
<p className="mb-4" style={{ fontSize: "14px" }}> <p className="mb-4" style={{ fontSize: "14px" }}>
@ -93,7 +97,7 @@ const ChangePasswordPage = ({ onClose }) => {
<form onSubmit={handleSubmit(onChangePassword)}> <form onSubmit={handleSubmit(onChangePassword)}>
{/* Old Password */} {/* Old Password */}
<div className="mb-3 text-start"> <div className="mb-3">
<label className="form-label">Old Password</label> <label className="form-label">Old Password</label>
<div className="input-group input-group-merge d-flex align-items-center border rounded px-2"> <div className="input-group input-group-merge d-flex align-items-center border rounded px-2">
<input <input
@ -119,75 +123,75 @@ const ChangePasswordPage = ({ onClose }) => {
)} )}
</div> </div>
{/* <div className="row"> */} <div className="row">
<div className="mb-3 text-start"> <div className="mb-3 col-md-6">
<label className="form-label">New Password</label> <label className="form-label">New Password</label>
<div className="input-group input-group-merge d-flex align-items-center border rounded px-2"> <div className="input-group input-group-merge d-flex align-items-center border rounded px-2">
<input <input
type={hideNewPass ? "password" : "text"} type={hideNewPass ? "password" : "text"}
className="form-control form-control-sm border-0 shadow-none" className="form-control form-control-sm border-0 shadow-none"
{...register("newPassword")} {...register("newPassword")}
placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;" placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;"
style={{ flex: 1 }} style={{ flex: 1 }}
/> />
<button <button
type="button" type="button"
className="btn btn-link p-0 ms-2" className="btn btn-link p-0 ms-2"
style={{ fontSize: "18px", color: "#6c757d" }} style={{ fontSize: "18px", color: "#6c757d" }}
onClick={() => setHideNewPass(!hideNewPass)} onClick={() => setHideNewPass(!hideNewPass)}
> >
<i className={`bx ${hideNewPass ? "bx-hide" : "bx-show"}`} /> <i
</button> className={`bx ${hideNewPass ? "bx-hide" : "bx-show"}`}
</div> />
{errors.newPassword && ( </button>
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.newPassword.message}
</div> </div>
)} {errors.newPassword && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.newPassword.message}
</div>
)}
</div>
{/* Confirm Password */}
<div className="mb-3 col-md-6">
<label className="form-label">Confirm New Password</label>
<div className="input-group input-group-merge d-flex align-items-center border rounded px-2">
<input
type={hideConfirmPass ? "password" : "text"}
className="form-control form-control-sm border-0 shadow-none"
{...register("confirmPassword")}
placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;"
style={{ flex: 1 }}
/>
<button
type="button"
className="btn btn-link p-0 ms-2"
style={{ fontSize: "18px", color: "#6c757d" }}
onClick={() => setHideConfirmPass(!hideConfirmPass)}
>
<i
className={`bx ${
hideConfirmPass ? "bx-hide" : "bx-show"
}`}
/>
</button>
</div>
{errors.confirmPassword && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.confirmPassword.message}
</div>
)}
</div>
</div> </div>
{/* Confirm Password */}
<div className="mb-3 text-start">
<label className="form-label">Confirm New Password</label>
<div className="input-group input-group-merge d-flex align-items-center border rounded px-2">
<input
type={hideConfirmPass ? "password" : "text"}
className="form-control form-control-sm border-0 shadow-none"
{...register("confirmPassword")}
placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;"
style={{ flex: 1 }}
/>
<button
type="button"
className="btn btn-link p-0 ms-2"
style={{ fontSize: "18px", color: "#6c757d" }}
onClick={() => setHideConfirmPass(!hideConfirmPass)}
>
<i
className={`bx ${hideConfirmPass ? "bx-hide" : "bx-show"}`}
/>
</button>
</div>
{errors.confirmPassword && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.confirmPassword.message}
</div>
)}
</div>
{/* </div> */}
<div className="d-flex justify-content-center pt-2">
Your password must have at least 8 characters and include a lower
case latter, an uppercase letter, a number, and a special
character.
</div>
{/* Action Buttons */} {/* Action Buttons */}
<div className="d-flex justify-content-center pt-2"> <div className="d-flex justify-content-end pt-2">
<button <button
type="submit" type="submit"
className="btn btn-primary btn-sm me-2" className="btn btn-primary btn-sm me-2"
@ -204,6 +208,21 @@ const ChangePasswordPage = ({ onClose }) => {
Cancel Cancel
</button> </button>
</div> </div>
<div className="mb-3 text-start ">
<p className="p-0 m-0">Password must be:</p>
<p className="p-0 m-0">- at least 8 characters long</p>
<p className="p-0 m-0">
- must contain one uppercase, one lowercase letter, at least one
number, at least one special character
</p>
{/* <p className="p-0 m-0">
- must contain at least one lowercase letter
</p>
<p className="p-0 m-0">- must contain at least one number</p>
<p className="p-0 m-0">
- must contain at least one special character
</p> */}
</div>
</form> </form>
</div> </div>
</div> </div>

View File

@ -11,11 +11,7 @@ import ProjectInfra from "../../components/Project/ProjectInfra";
import Loader from "../../components/common/Loader"; import Loader from "../../components/common/Loader";
import WorkPlan from "../../components/Project/WorkPlan"; import WorkPlan from "../../components/Project/WorkPlan";
import Breadcrumb from "../../components/common/Breadcrumb"; import Breadcrumb from "../../components/common/Breadcrumb";
import { import { cacheData, clearCacheKey, getCachedData } from "../../slices/apiDataManager";
cacheData,
clearCacheKey,
getCachedData,
} from "../../slices/apiDataManager";
import ProjectRepository from "../../repositories/ProjectRepository"; import ProjectRepository from "../../repositories/ProjectRepository";
import { ActivityeRepository } from "../../repositories/MastersRepository"; import { ActivityeRepository } from "../../repositories/MastersRepository";
import "./ProjectDetails.css"; import "./ProjectDetails.css";
@ -28,7 +24,6 @@ import { setProjectId } from "../../slices/localVariablesSlice";
import { ComingSoonPage } from "../Misc/ComingSoonPage"; import { ComingSoonPage } from "../Misc/ComingSoonPage";
import Directory from "../Directory/Directory"; import Directory from "../Directory/Directory";
import eventBus from "../../services/eventBus"; import eventBus from "../../services/eventBus";
import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChart";
const ProjectDetails = () => { const ProjectDetails = () => {
let { projectId } = useParams(); let { projectId } = useParams();
@ -80,31 +75,18 @@ const ProjectDetails = () => {
switch (activePill) { switch (activePill) {
case "profile": { case "profile": {
return ( return (
<> <div className="row">
<div className="row"> <div className="col-xl-4 col-lg-5 col-md-5 mt-5">
<div className="col-lg-4 col-md-5 mt-5"> {/* About User */}
{/* About User */} <AboutProject data={projectDetails}></AboutProject>
<AboutProject data={projectDetails}></AboutProject> {/* About User */}
<ProjectOverview project={projectId} />
{/* About User */}
</div>
<div className="col-lg-8 col-md-5 mt-5">
{/* Profile Overview */}
<ProjectProgressChart
ShowAllProject="false"
DefaultRange="1M"
/>
{/* Profile Overview */}
</div>
</div> </div>
<div className="row"> <div className="col-xl-4 col-lg-5 col-md-5 mt-5">
<div className="col-xl-4 col-lg-5 col-md-5 "> {/* Profile Overview */}
{/* Profile Overview */} <ProjectOverview project={projectId} />
{/* <ProjectOverview project={projectId} /> */} {/* Profile Overview */}
{/* Profile Overview */}
</div>
</div> </div>
</> </div>
); );
} }
case "teams": { case "teams": {
@ -159,22 +141,22 @@ const ProjectDetails = () => {
const handler = useCallback( const handler = useCallback(
(msg) => { (msg) => {
if (msg.keyword === "Update_Project" && project.id === msg.response.id) { if (msg.keyword === "Update_Project" && project.id === msg.response.id) {
clearCacheKey("projectInfo"); clearCacheKey("projectInfo")
ProjectRepository.getProjectByprojectId(projectId) ProjectRepository.getProjectByprojectId(projectId)
.then((response) => { .then((response) => {
setProjectDetails(response.data); setProjectDetails(response.data);
setProject(response.data); setProject(response.data);
cacheData("projectInfo", { projectId, data: response.data }); cacheData("projectInfo", { projectId, data: response.data });
setLoading(false); setLoading(false);
}) })
.catch((error) => { .catch((error) => {
console.error(error); console.error(error);
setError("Failed to fetch data."); setError("Failed to fetch data.");
setLoading(false); setLoading(false);
}); });
} }
}, },
[project, handleDataChange] [project,handleDataChange]
); );
useEffect(() => { useEffect(() => {
eventBus.on("project", handler); eventBus.on("project", handler);
@ -189,15 +171,15 @@ const ProjectDetails = () => {
data={[ data={[
{ label: "Home", link: "/dashboard" }, { label: "Home", link: "/dashboard" },
{ label: "Projects", link: "/projects" }, { label: "Projects", link: "/projects" },
{ label: `${project?.name ? project?.name : ""}`, link: null }, { label: "Details", link: null },
]} ]}
></Breadcrumb> ></Breadcrumb>
<div className="row"> <div className="row">
{projectLoading && <p>Loading....</p>} {projectLoading && <p>Loading....</p>}
{/* {!projectLoading && project && ( {!projectLoading && project && (
<ProjectBanner project_data={project}></ProjectBanner> <ProjectBanner project_data={project}></ProjectBanner>
)} */} )}
<ProjectNav <ProjectNav
onPillClick={handlePillClick} onPillClick={handlePillClick}

View File

@ -20,12 +20,12 @@ export const getDateDifferenceInDays = (startDate, endDate) => {
return differenceInDays; return differenceInDays;
}; };
// export const formatDate = (date) => { export const formatDate = (date) => {
// if (!date) return ""; // Return an empty string if no date if (!date) return ""; // Return an empty string if no date
// const dateObj = new Date(date); const dateObj = new Date(date);
// // return dateObj.toISOString().split("T")[0]; // return dateObj.toISOString().split("T")[0];
// return dateObj.toLocaleDateString('en-CA'); // Get the date in YYYY-MM-DD format return dateObj.toLocaleDateString('en-CA'); // Get the date in YYYY-MM-DD format
// }; };
export const convertShortTime = (dateString) => { export const convertShortTime = (dateString) => {
const date = new Date(dateString); const date = new Date(dateString);
@ -61,23 +61,3 @@ export const checkIfCurrentDate = (dateString) => {
return currentDate.getTime() === inputDate.getTime(); return currentDate.getTime() === inputDate.getTime();
}; };
function utcToLocalTime(utcTime) {
// Convert string to Date if needed
const utcDate = typeof utcTime === 'string' ? new Date(utcTime) : utcTime;
return new Date(utcDate.getTime());
}
export const formatDate =(utcTime, options = {}) =>{
const localDate = utcToLocalTime(utcTime);
const defaultOptions = {
day: '2-digit',
month: '2-digit',
year: 'numeric',
...options
};
return localDate.toLocaleDateString('en-GB', defaultOptions);
}