Compare commits
9 Commits
Weidget_Da
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 96bcdffdca | |||
| f8963ef476 | |||
| 9ad3b8726c | |||
| d4582c101a | |||
| c6af020c85 | |||
| 822ff1a7e4 | |||
| 92d17167b1 | |||
| 8ec62827d5 | |||
| 48f314eac4 |
6
public/assets/vendor/css/core.css
vendored
6
public/assets/vendor/css/core.css
vendored
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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}
|
||||
>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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"]);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user