Compare commits

...

9 Commits

9 changed files with 210 additions and 160 deletions

View File

@ -89,7 +89,7 @@
);
--bs-root-font-size: 16px;
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 0.875rem;
--bs-body-font-size: 0.85rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.375;
--bs-body-color: #646e78;
@ -9060,7 +9060,7 @@ img[data-app-light-img][data-app-dark-img] {
}
.table th {
color: var(--bs-heading-color);
font-size: 0.8125rem;
font-size: 0.8025rem;
letter-spacing: 0.2px;
text-transform: uppercase;
}
@ -20345,7 +20345,7 @@ li:not(:first-child) .dropdown-item,
}
.fs-6 {
font-size: 0.9375rem !important;
font-size: 0.8375rem !important;
}
.fs-tiny {

View File

@ -1,24 +1,48 @@
import { useState } from "react";
const PreviewDocument = ({ imageUrl }) => {
const [loading, setLoading] = useState(true);
const [rotation, setRotation] = useState(0);
const [scale, setScale] = useState(1);
const zoomIn = () => setScale((prev) => Math.min(prev + 0.2, 3));
const zoomOut = () => setScale((prev) => Math.max(prev - 0.2, 0.4));
const resetAll = () => {
setRotation(0);
setScale(1);
};
return (
<>
<div className="d-flex justify-content-start">
<div className="d-flex justify-content-start gap-3 mb-2">
<i
className="bx bx-rotate-right cursor-pointer"
className="bx bx-rotate-right cursor-pointer fs-4"
title="Rotate"
onClick={() => setRotation((prev) => prev + 90)}
></i>
<i
className="bx bx-zoom-in cursor-pointer fs-4"
title="Zoom In"
onClick={zoomIn}
></i>
<i
className="bx bx-zoom-out cursor-pointer fs-4"
title="Zoom Out"
onClick={zoomOut}
></i>
</div>
<div
className="position-relative d-flex flex-column justify-content-center align-items-center"
className="position-relative d-flex flex-column justify-content-center align-items-center overflow-hidden"
style={{ minHeight: "80vh" }}
>
{loading && (
<div className="text-secondary text-center mb-2">Loading...</div>
<div className="text-secondary text-center mb-2">
Loading...
</div>
)}
<div className="mb-3 d-flex justify-content-center align-items-center">
@ -30,18 +54,18 @@ const PreviewDocument = ({ imageUrl }) => {
maxHeight: "80vh",
objectFit: "contain",
display: loading ? "none" : "block",
transform: `rotate(${rotation}deg)`,
transform: `rotate(${rotation}deg) scale(${scale})`,
transition: "transform 0.3s ease",
cursor: "grab",
}}
onLoad={() => setLoading(false)}
/>
</div>
<div className="position-absolute bottom-0 start-0 justify-content-center gap-2">
<div className="position-absolute bottom-0 start-0 m-2">
<button
className="btn btn-outline-secondary"
onClick={() => setRotation(0)}
title="Reset Rotation"
onClick={resetAll}
>
<i className="bx bx-reset"></i> Reset
</button>
@ -51,4 +75,6 @@ const PreviewDocument = ({ imageUrl }) => {
);
};
export default PreviewDocument;

View File

@ -25,9 +25,7 @@ const Sidebar = () => {
/>
</span> */}
<small
className="app-brand-link fw-bold navbar-brand text-green fs-6"
>
<small className="app-brand-link fw-bold navbar-brand text-green fs-6">
<span className="app-brand-logo demo">
<img src="/img/brand/marco.png" width="50" />
</span>
@ -38,7 +36,6 @@ const Sidebar = () => {
</Link>
<small 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>
</small>
</div>
@ -61,7 +58,7 @@ const Sidebar = () => {
</>
)}
{data &&
data?.data.map((section) => (
data?.data?.map((section) => (
<React.Fragment
key={section.id || section.header || section.items[0]?.id}
>

View File

@ -10,11 +10,13 @@ import {
import useMaster, { useServices } from "../../../hooks/masterHook/useMaster";
import showToast from "../../../services/toastService";
import { useOrganizationEmployees } from "../../../hooks/useOrganization";
import { useDispatch } from "react-redux";
import { changeMaster } from "../../../slices/localVariablesSlice";
const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
const selectedProject = useSelectedProject();
const debounceSearchTerm = useDebounce(searchTerm, 500);
const dispatch = useDispatch();
const {
data: employeesData = [],
isLoading,
@ -45,6 +47,7 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
});
useEffect(() => {
dispatch(changeMaster("Job Role"));
if (employeesData?.data?.length > 0) {
const available = employeesData.data.filter((emp) => {
const projEmp = projectEmployees.find((pe) => pe.employeeId === emp.id);
@ -183,8 +186,7 @@ if (employees.length === 0) {
onChange={(e) =>
handleSelectChange(index, "serviceId", e.target.value)
}
className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${
emp.errors.serviceId ? "is-invalid" : ""
className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${emp.errors.serviceId ? "is-invalid" : ""
}`}
>
<option value="">Select Service</option>
@ -205,8 +207,7 @@ if (employees.length === 0) {
onChange={(e) =>
handleSelectChange(index, "jobRole", e.target.value)
}
className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${
emp.errors.jobRole ? "is-invalid" : ""
className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${emp.errors.jobRole ? "is-invalid" : ""
}`}
>
<option value="">Select Job Role</option>

View File

@ -66,19 +66,10 @@ const Jobs = () => {
<div className="col-12 col-md-6 text-start">
<button
type="button"
className={`btn btn-sm ${showArchive ? "btn-primary" : "btn-outline-secondary"}`}
className={`btn btn-sm ${showArchive ? "btn-secondary" : "btn-outline-secondary"}`}
onClick={() => setShowArchive(!showArchive)}
style={{ fontSize: "13px" }}
>
{showArchive ? (
<>
<i className="bx bx-list-ul me-1 mt-1"></i> Show Active
</>
) : (
<>
<i className="bx bx-archive me-1 mt-1"></i> Show Archived
</>
)}
<i className="bx bx-archive bx-sm me-1 mt-1"></i> Archived
</button>
</div>

View File

@ -89,7 +89,7 @@ const ServiceProjectCard = ({ project, isCore = true }) => {
>
{project?.shortName ? project?.shortName : project?.name}
</h5>
<div className="client-info text-body">
<div className="client-info text-body text-start">
<span>{project?.shortName ? project?.name : ""}</span>
</div>
</div>

View File

@ -149,11 +149,6 @@ export const useAllocationServiceProjectTeam = (onSuccessCallback) => {
//#endregion
//#region Service Jobs
export const useServiceProjectJobs = (
@ -164,7 +159,14 @@ export const useServiceProjectJobs = (
isArchive
) => {
return useQuery({
queryKey: ["serviceProjectJobs", pageSize, pageNumber, isActive, project, isArchive],
queryKey: [
"serviceProjectJobs",
pageSize,
pageNumber,
isActive,
project,
isArchive,
],
queryFn: async () => {
const resp = await ServiceProjectRepository.GetJobList(
pageSize,
@ -285,7 +287,6 @@ export const useUpdateServiceProjectJob = (onSuccessCallback) => {
}
},
onError: (error) => {
showToast(
error?.response?.data?.message ||
@ -297,15 +298,8 @@ export const useUpdateServiceProjectJob = (onSuccessCallback) => {
});
};
//#endregion
//#region Branch
export const useBranches = (
projectId,
@ -344,8 +338,8 @@ export const useBranchTypes = () => {
const resp = await ServiceProjectRepository.GetBranchTypeList();
return resp.data;
},
})
}
});
};
export const useBranchDetails = (id) => {
return useQuery({
@ -354,9 +348,9 @@ export const useBranchDetails = (id) => {
const resp = await ServiceProjectRepository.GetBranchDetail(id);
return resp.data;
},
enabled: !!id
})
}
enabled: !!id,
});
};
export const useCreateBranch = (onSuccessCallBack) => {
const queryClient = useQueryClient();
@ -402,7 +396,6 @@ export const useUpdateBranch = (onSuccessCallBack) => {
});
};
export const useDeleteBranch = () => {
const queryClient = useQueryClient();
@ -412,7 +405,10 @@ export const useDeleteBranch = () => {
onSuccess: (_, variable) => {
queryClient.invalidateQueries({ queryKey: ["branches"] });
showToast(`Branch ${variable.isActive ? "restored" : "deleted"} successfully`, "success");
showToast(
`Branch ${variable.isActive ? "restored" : "deleted"} successfully`,
"success"
);
},
onError: (error) => {

View File

@ -1,4 +1,4 @@
import React, { createContext, useContext, useEffect, useState } from "react";
import React, { createContext, useContext, useEffect, useRef, useState } from "react";
import Breadcrumb from "../../components/common/Breadcrumb";
import {
ITEMS_PER_PAGE,
@ -36,7 +36,7 @@ const ProjectPage = () => {
isOpen: false,
project: null,
});
const dropdownRef = useRef(null);
const [projectList, setProjectList] = useState([]);
const [listView, setListView] = useState(false);
const [searchTerm, setSearchTerm] = useState("");
@ -46,6 +46,7 @@ const ProjectPage = () => {
});
const HasManageProject = useHasUserPermission(MANAGE_PROJECT);
const [currentPage, setCurrentPage] = useState(1);
const [open, setOpen] = useState(false);
const [selectedStatuses, setSelectedStatuses] = useState(
PROJECT_STATUS.map((s) => s.id)
@ -70,6 +71,16 @@ const ProjectPage = () => {
sessionStorage.setItem("whichProjectDisplay", String(value));
};
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
return (
<ProjectContext.Provider value={contextDispatcher}>
<div className="container-fluid">
@ -89,8 +100,7 @@ const ProjectPage = () => {
{/* Service Project Button */}
<button
type="button"
className={`btn px-2 py-1 rounded-0 text-tiny ${
!coreProjects ? "btn-primary text-white" : ""
className={`btn px-2 py-1 rounded-0 text-tiny ${!coreProjects ? "btn-primary text-white" : ""
}`}
onClick={() => handleToggleProject(false)}
>
@ -99,8 +109,7 @@ const ProjectPage = () => {
{/* Organization Project Button */}
<button
type="button"
className={`btn px-2 py-1 rounded-0 text-tiny ${
coreProjects ? "btn-primary text-white" : ""
className={`btn px-2 py-1 rounded-0 text-tiny ${coreProjects ? "btn-primary text-white" : ""
}`}
onClick={() => handleToggleProject(true)}
>
@ -129,8 +138,7 @@ const ProjectPage = () => {
<div className="d-flex gap-2">
<button
type="button"
className={`btn btn-sm p-1 ${
!listView ? "btn-primary" : "btn-outline-primary"
className={`btn btn-sm p-1 ${!listView ? "btn-primary" : "btn-outline-primary"
}`}
onClick={() => setListView(false)}
title="Card View"
@ -140,8 +148,7 @@ const ProjectPage = () => {
<button
type="button"
className={`btn btn-sm p-1 ${
listView ? "btn-primary" : "btn-outline-primary"
className={`btn btn-sm p-1 ${listView ? "btn-primary" : "btn-outline-primary"
}`}
onClick={() => setListView(true)}
title="List View"
@ -151,17 +158,36 @@ const ProjectPage = () => {
</div>
{/* Dropdown Filter */}
<div className="dropdown me-2">
<a
className="dropdown-toggle hide-arrow cursor-pointer p-1"
data-bs-toggle="dropdown"
aria-expanded="false"
title="Filter"
<div className="dropdown me-2 position-relative">
<div
className="cursor-pointer p-1"
onClick={() => setOpen(!open)}
>
<i className="bx bx-slider-alt fs-5"></i>
</a>
<i
className={`bx bx-slider-alt fs-5 ${selectedStatuses.length !== PROJECT_STATUS.length ? "text-primary" : ""
}`}
></i>
<ul className="dropdown-menu p-2 text-capitalize">
{selectedStatuses.length !== PROJECT_STATUS.length && (
<span className="badge bg-warning text-white rounded-pill position-absolute"
style={{
top: "-4px",
right: "-4px",
fontSize: "0.6rem",
padding: "2px 5px",
}}
>
{PROJECT_STATUS.length - selectedStatuses.length}
</span>
)}
</div>
{open && (
<ul
ref={dropdownRef}
className="dropdown-menu show p-2 text-capitalize"
onMouseDown={(e) => e.stopPropagation()} // IMPORTANT
>
{PROJECT_STATUS.map(({ id, label }) => (
<li key={id}>
<div className="form-check">
@ -169,15 +195,25 @@ const ProjectPage = () => {
className="form-check-input"
type="checkbox"
checked={selectedStatuses.includes(id)}
onClick={(e) => e.stopPropagation()} // IMPORTANT
onChange={() => handleStatusChange(id)}
/>
<label className="form-check-label">{label}</label>
<label
className="form-check-label"
onClick={(e) => e.stopPropagation()} // OPTIONAL
>
{label}
</label>
</div>
</li>
))}
</ul>
)}
</div>
{HasManageProject && (
<button
type="button"

View File

@ -150,6 +150,9 @@ export function startSignalR(loggedUser) {
queryClient.invalidateQueries(["serviceProjects"]);
queryClient.invalidateQueries(["serviceProject"]);
}
if (keyword === "Project_Branch") {
queryClient.invalidateQueries(["branches"]);
}
if (keyword === "Service_Project_Allocation") {
queryClient.invalidateQueries(["serviceProjectTeam"]);