Compare commits
11 Commits
main
...
Service_Or
Author | SHA1 | Date | |
---|---|---|---|
764d508974 | |||
3b032b7b07 | |||
b8891d403f | |||
01568db61c | |||
80a974e3be | |||
f3e05a11d6 | |||
222e6495a8 | |||
18a3b8a85b | |||
d75296ffe8 | |||
6649cab6a2 | |||
eab23389ed |
@ -280,3 +280,7 @@
|
|||||||
.w-8-xl{ width: 2rem; }
|
.w-8-xl{ width: 2rem; }
|
||||||
.w-10-xl{ width: 2.5rem; }
|
.w-10-xl{ width: 2.5rem; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursor-not-allowed{
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
@ -126,7 +126,7 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat
|
|||||||
checked={ShowPending}
|
checked={ShowPending}
|
||||||
onChange={(e) => setShowPending(e.target.checked)}
|
onChange={(e) => setShowPending(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
<label className="form-check-label ms-0">Show Pending</label>
|
<label className="form-check-label ms-0">Pending Attendance</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{attLoading ? (
|
{attLoading ? (
|
||||||
|
@ -190,7 +190,7 @@ useEffect(() => {
|
|||||||
checked={showPending}
|
checked={showPending}
|
||||||
onChange={(e) => setShowPending(e.target.checked)}
|
onChange={(e) => setShowPending(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
<label className="form-check-label ms-0">Show Pending</label>
|
<label className="form-check-label ms-0">Pending Attendance</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -23,7 +23,7 @@ const HorizontalBarChart = ({
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-[380px] flex items-center justify-center bg-gray-100 rounded-xl">
|
<div className="w-full h-[380px] flex items-center justify-center bg-gray-100 rounded-xl">
|
||||||
<span className="text-gray-500 text-sm">Loading chart...</span>
|
<span className="text-gray-500">Loading chart...</span>
|
||||||
{/* Replace this with a skeleton or spinner if you prefer */}
|
{/* Replace this with a skeleton or spinner if you prefer */}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -3,7 +3,8 @@ import HorizontalBarChart from "../Charts/HorizontalBarChart";
|
|||||||
import { useProjects } from "../../hooks/useProjects";
|
import { useProjects } from "../../hooks/useProjects";
|
||||||
|
|
||||||
const ProjectCompletionChart = () => {
|
const ProjectCompletionChart = () => {
|
||||||
const { projects, loading } = useProjects();
|
const { data: projects = [], isLoading: loading, isError, error } = useProjects();
|
||||||
|
|
||||||
|
|
||||||
// Bar chart logic
|
// Bar chart logic
|
||||||
const projectNames = projects?.map((p) => p.name) || [];
|
const projectNames = projects?.map((p) => p.name) || [];
|
||||||
@ -11,7 +12,7 @@ const ProjectCompletionChart = () => {
|
|||||||
projects?.map((p) => {
|
projects?.map((p) => {
|
||||||
const completed = p.completedWork || 0;
|
const completed = p.completedWork || 0;
|
||||||
const planned = p.plannedWork || 1;
|
const planned = p.plannedWork || 1;
|
||||||
const percent = (completed / planned) * 100;
|
const percent = planned ? (completed / planned) * 100 : 0;
|
||||||
return Math.min(Math.round(percent), 100);
|
return Math.min(Math.round(percent), 100);
|
||||||
}) || [];
|
}) || [];
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { useDocumentFilterEntities } from "../../hooks/useDocument";
|
import { useDocumentFilterEntities } from "../../hooks/useDocument";
|
||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
@ -9,9 +9,11 @@ import {
|
|||||||
import { DateRangePicker1 } from "../common/DateRangePicker";
|
import { DateRangePicker1 } from "../common/DateRangePicker";
|
||||||
import SelectMultiple from "../common/SelectMultiple";
|
import SelectMultiple from "../common/SelectMultiple";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
const DocumentFilterPanel = ({ entityTypeId, onApply }) => {
|
const DocumentFilterPanel = ({ entityTypeId, onApply }) => {
|
||||||
const [resetKey, setResetKey] = useState(0);
|
const [resetKey, setResetKey] = useState(0);
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
const { data, isError, isLoading, error } =
|
const { data, isError, isLoading, error } =
|
||||||
useDocumentFilterEntities(entityTypeId);
|
useDocumentFilterEntities(entityTypeId);
|
||||||
@ -52,6 +54,13 @@ const DocumentFilterPanel = ({ entityTypeId, onApply }) => {
|
|||||||
closePanel();
|
closePanel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Close popup when navigating to another component
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
closePanel();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (isLoading) return <div>Loading...</div>;
|
if (isLoading) return <div>Loading...</div>;
|
||||||
if (isError)
|
if (isError)
|
||||||
return <div>Error: {error?.message || "Something went wrong!"}</div>;
|
return <div>Error: {error?.message || "Something went wrong!"}</div>;
|
||||||
@ -63,6 +72,8 @@ const DocumentFilterPanel = ({ entityTypeId, onApply }) => {
|
|||||||
documentTag = [],
|
documentTag = [],
|
||||||
} = data?.data || {};
|
} = data?.data || {};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
@ -73,18 +84,16 @@ const DocumentFilterPanel = ({ entityTypeId, onApply }) => {
|
|||||||
<div className="d-inline-flex border rounded-pill overflow-hidden shadow-none">
|
<div className="d-inline-flex border rounded-pill overflow-hidden shadow-none">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`btn px-2 py-1 rounded-0 text-tiny ${
|
className={`btn px-2 py-1 rounded-0 text-tiny ${isUploadedAt ? "active btn-secondary text-white" : ""
|
||||||
isUploadedAt ? "active btn-secondary text-white" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => setValue("isUploadedAt", true)}
|
onClick={() => setValue("isUploadedAt", true)}
|
||||||
>
|
>
|
||||||
Uploaded On
|
Uploaded On
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`btn px-2 py-1 rounded-0 text-tiny ${
|
className={`btn px-2 py-1 rounded-0 text-tiny ${!isUploadedAt ? "active btn-secondary text-white" : ""
|
||||||
!isUploadedAt ? "active btn-secondary text-white" : ""
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => setValue("isUploadedAt", false)}
|
onClick={() => setValue("isUploadedAt", false)}
|
||||||
>
|
>
|
||||||
Updated On
|
Updated On
|
||||||
|
@ -144,12 +144,12 @@ const Documents = ({ Document_Entity, Entity }) => {
|
|||||||
<span className="switch-off"></span>
|
<span className="switch-off"></span>
|
||||||
</span>
|
</span>
|
||||||
<span className="switch-label">
|
<span className="switch-label">
|
||||||
{isActive ? "Active" : "In-Active"}
|
{isActive ? "Active Document" : "In-Active Document"}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-6 col-md-6 col-lg-8 text-end">
|
<div className="col-12 col-md-6 col-lg-8 text-end">
|
||||||
{(isSelf || canUploadDocument) && (
|
{(isSelf || canUploadDocument) && (
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-primary me-3"
|
className="btn btn-sm btn-primary me-3"
|
||||||
|
@ -51,7 +51,6 @@ const EmpAttendance = () => {
|
|||||||
new Date(b?.checkInTime).getTime() - new Date(a?.checkInTime).getTime()
|
new Date(b?.checkInTime).getTime() - new Date(a?.checkInTime).getTime()
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(sorted);
|
|
||||||
|
|
||||||
const { currentPage, totalPages, currentItems, paginate } = usePagination(
|
const { currentPage, totalPages, currentItems, paginate } = usePagination(
|
||||||
sorted,
|
sorted,
|
||||||
|
@ -131,7 +131,7 @@ const OrganizationsList = ({searchText}) => {
|
|||||||
<div className="d-flex justify-content-center gap-2">
|
<div className="d-flex justify-content-center gap-2">
|
||||||
<i className="bx bx-show text-primary cursor-pointer" onClick={()=>onOpen({startStep:5,orgData:org.id,flowType:"view"})}></i>
|
<i className="bx bx-show text-primary cursor-pointer" onClick={()=>onOpen({startStep:5,orgData:org.id,flowType:"view"})}></i>
|
||||||
<i className="bx bx-edit text-secondary cursor-pointer" onClick={()=>onOpen({startStep:4,orgData:org,flowType:"edit"})}></i>
|
<i className="bx bx-edit text-secondary cursor-pointer" onClick={()=>onOpen({startStep:4,orgData:org,flowType:"edit"})}></i>
|
||||||
<i className="bx bx-trash text-danger cursor-pointer"></i>
|
<i className="bx bx-trash text-danger cursor-not-allowed"></i>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -22,9 +22,8 @@ const VieworgDataanization = ({ orgId }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="text-end">
|
<div className="text-end">
|
||||||
<span
|
<span
|
||||||
className={`badge bg-label-${
|
className={`badge bg-label-${data?.isActive ? "primary" : "secondary"
|
||||||
data?.isActive ? "primary" : "secondary"
|
} `}
|
||||||
} `}
|
|
||||||
>
|
>
|
||||||
{data?.isActive ? "Active" : "In-Active"}{" "}
|
{data?.isActive ? "Active" : "In-Active"}{" "}
|
||||||
</span>
|
</span>
|
||||||
@ -105,9 +104,101 @@ const VieworgDataanization = ({ orgId }) => {
|
|||||||
<div className="text-muted text-start">{data?.address}</div>
|
<div className="text-muted text-start">{data?.address}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex text-secondary mb-2">
|
<div className="col-12 mb-3">
|
||||||
{" "}
|
<div
|
||||||
<i className="bx bx-sm bx-briefcase me-1" /> Projects And Services
|
className="d-flex justify-content-between align-items-center text-secondary mb-2 cursor-pointer"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#collapse-projects-services"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<i className="bx bx-sm bx-briefcase me-1" /> Projects
|
||||||
|
</div>
|
||||||
|
<i className="bx bx-chevron-down me-2"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* remove "show" from className */}
|
||||||
|
<div id="collapse-projects-services" className="collapse">
|
||||||
|
{data?.projects && data.projects.length > 0 ? (
|
||||||
|
data.projects
|
||||||
|
.reduce((acc, curr) => {
|
||||||
|
const projectId = curr.project.id;
|
||||||
|
if (!acc.find((p) => p.id === projectId)) {
|
||||||
|
acc.push(curr.project);
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, [])
|
||||||
|
.map((project) => (
|
||||||
|
<div key={project.id} className="mb-2 rounded p-2">
|
||||||
|
<div
|
||||||
|
className="d-flex justify-content-between align-items-center cursor-pointer"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target={`#collapse-${project.id}`}
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<label className="form-label fw-semibold">
|
||||||
|
<i className="bx bx-buildings me-2"></i>
|
||||||
|
{project.name}
|
||||||
|
</label>
|
||||||
|
<i className="bx bx-chevron-down"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id={`collapse-${project.id}`} className="collapse mt-2 ps-5">
|
||||||
|
{data.projects
|
||||||
|
.filter((p) => p.project.id === project.id)
|
||||||
|
.map((p) => (
|
||||||
|
<div key={p.service.id} className="mb-1 text-muted">
|
||||||
|
<i className="bx bx-wrench me-2"></i>
|
||||||
|
{p.service.name}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="text-muted fst-italic ps-2">No projects available</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Services Section */}
|
||||||
|
<div className="col-12 mb-3">
|
||||||
|
<div
|
||||||
|
className="d-flex justify-content-between align-items-center text-secondary mb-2 cursor-pointer"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#collapse-services"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<i className="bx bx-sm bx-cog me-1" /> Services
|
||||||
|
</div>
|
||||||
|
<i className="bx bx-chevron-down me-2"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* collapse is closed initially */}
|
||||||
|
<div id="collapse-services" className="collapse">
|
||||||
|
{data?.services && data.services.length > 0 ? (
|
||||||
|
<div className="row">
|
||||||
|
{data.services.map((service) => (
|
||||||
|
<div key={service.id} className="col-md-12 mb-3">
|
||||||
|
<div className="card h-100 shadow-sm border-0">
|
||||||
|
<div className="card-body">
|
||||||
|
<h6 className="fw-semibold mb-1">
|
||||||
|
<i className="bx bx-wrench me-1"></i>
|
||||||
|
{service.name}
|
||||||
|
</h6>
|
||||||
|
<p className="text-muted small mb-0">
|
||||||
|
{service.description || "No description available."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-muted fst-italic ps-2">No services available</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { ComingSoonPage } from "../../pages/Misc/ComingSoonPage";
|
import { ComingSoonPage } from "../../pages/Misc/ComingSoonPage";
|
||||||
import ProjectPermission from "./ProjectPermission";
|
import ProjectPermission from "./ProjectPermission";
|
||||||
|
import ListViewContact_1 from "./services/Services";
|
||||||
|
import ServiceListView from "./services/Services";
|
||||||
|
|
||||||
const ProjectSetting = () => {
|
const ProjectSetting = () => {
|
||||||
const [activePill, setActivePill] = useState("Permissions")
|
const [activePill, setActivePill] = useState("Permissions")
|
||||||
@ -9,7 +11,7 @@ const ProjectSetting = () => {
|
|||||||
// });
|
// });
|
||||||
const projectSettingTab = [
|
const projectSettingTab = [
|
||||||
{ key: "Permissions", label: "Permissions" },
|
{ key: "Permissions", label: "Permissions" },
|
||||||
{ key: "Notification", label: "Notification" },
|
{ key: "services", label: "Services" },
|
||||||
{ key: "SeparatedLink", label: "Separated link", isButton: true },
|
{ key: "SeparatedLink", label: "Separated link", isButton: true },
|
||||||
];
|
];
|
||||||
const handlePillClick = (pillKey) => {
|
const handlePillClick = (pillKey) => {
|
||||||
@ -22,8 +24,8 @@ const ProjectSetting = () => {
|
|||||||
case "Permissions":
|
case "Permissions":
|
||||||
return <ProjectPermission />;
|
return <ProjectPermission />;
|
||||||
|
|
||||||
case "Notification":
|
case "services":
|
||||||
return <ComingSoonPage />;
|
return <ServiceListView />;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return <ComingSoonPage />;
|
return <ComingSoonPage />;
|
||||||
@ -33,7 +35,7 @@ const ProjectSetting = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="w-100">
|
<div className="w-100">
|
||||||
<div className="card py-2 px-5">
|
<div className="card py-2 px-5">
|
||||||
{/* <div className="col-12">
|
<div className="col-12">
|
||||||
<div className="dropdown text-end">
|
<div className="dropdown text-end">
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-outline-primary dropdown-toggle"
|
className="btn btn-sm btn-outline-primary dropdown-toggle"
|
||||||
@ -64,7 +66,7 @@ const ProjectSetting = () => {
|
|||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div> */}
|
</div>
|
||||||
|
|
||||||
<div className="mt-3">{renderContent()}</div>
|
<div className="mt-3">{renderContent()}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -201,7 +201,7 @@ const Teams = () => {
|
|||||||
className="form-check-label ms-2"
|
className="form-check-label ms-2"
|
||||||
htmlFor="activeEmployeeSwitch"
|
htmlFor="activeEmployeeSwitch"
|
||||||
>
|
>
|
||||||
{activeEmployee ? "Active Employees" : "Include Inactive Employees"}
|
{activeEmployee ? "Active Employees" : "In-active Employees"}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
105
src/components/Project/services/Services.jsx
Normal file
105
src/components/Project/services/Services.jsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { useProjectAssignedServices } from "../../../hooks/useProjects";
|
||||||
|
import { useSelectedProject } from "../../../slices/apiDataManager";
|
||||||
|
|
||||||
|
const ServiceListView = () => {
|
||||||
|
const projectId = useSelectedProject();
|
||||||
|
const { data, isLoading, error } = useProjectAssignedServices(projectId);
|
||||||
|
|
||||||
|
const serviceColumns = [
|
||||||
|
{
|
||||||
|
key: "name",
|
||||||
|
label: "Service Name",
|
||||||
|
getValue: (service) => (
|
||||||
|
<span
|
||||||
|
className="text-truncate d-inline-block"
|
||||||
|
style={{ maxWidth: "200px" }}
|
||||||
|
>
|
||||||
|
{service?.name || "N/A"}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
align: "text-start",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "description",
|
||||||
|
label: "Description",
|
||||||
|
getValue: (service) => (
|
||||||
|
<span
|
||||||
|
className="text-truncate d-inline-block"
|
||||||
|
style={{ maxWidth: "400px" }}
|
||||||
|
title={service?.description}
|
||||||
|
>
|
||||||
|
{service?.description || "N/A"}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
align: "text-start",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "isActive",
|
||||||
|
label: "Status",
|
||||||
|
getValue: (service) => (
|
||||||
|
<span
|
||||||
|
className={`badge ${service?.isActive ? "bg-success" : "bg-secondary"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{service?.isActive ? "Active" : "Inactive"}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
align: "text-center",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (isLoading)
|
||||||
|
return <div className="p-4 text-center">Loading services...</div>;
|
||||||
|
if (error)
|
||||||
|
return (
|
||||||
|
<div className="p-4 text-center text-danger">
|
||||||
|
Failed to load services: {error.message}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const services = data || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="card-datatable table-responsive">
|
||||||
|
<div className="dataTables_wrapper no-footer mx-5 pb-2">
|
||||||
|
|
||||||
|
<table className="table dataTable text-nowrap">
|
||||||
|
<thead>
|
||||||
|
<tr className="table_header_border">
|
||||||
|
{serviceColumns.map((col) => (
|
||||||
|
<th key={col.key} className={col.align}>
|
||||||
|
{col.label}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{Array.isArray(services) && services.length > 0 ? (
|
||||||
|
services.map((row, i) => (
|
||||||
|
<tr key={i}>
|
||||||
|
{serviceColumns.map((col) => (
|
||||||
|
<td key={col.key} className={col.align}>
|
||||||
|
{col.getValue(row)}
|
||||||
|
</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<tr style={{ height: "200px" }}>
|
||||||
|
<td
|
||||||
|
colSpan={serviceColumns.length}
|
||||||
|
className="text-center border-0 align-middle"
|
||||||
|
>
|
||||||
|
No services found
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServiceListView;
|
@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
contactsFilter,
|
contactsFilter,
|
||||||
@ -8,11 +8,14 @@ import {
|
|||||||
import { useContactFilter } from "../../hooks/useDirectory";
|
import { useContactFilter } from "../../hooks/useDirectory";
|
||||||
import { ExpenseFilterSkeleton } from "../../components/Expenses/ExpenseSkeleton";
|
import { ExpenseFilterSkeleton } from "../../components/Expenses/ExpenseSkeleton";
|
||||||
import SelectMultiple from "../../components/common/SelectMultiple";
|
import SelectMultiple from "../../components/common/SelectMultiple";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
const ContactFilterPanel = ({ onApply, clearFilter }) => {
|
const ContactFilterPanel = ({ onApply, clearFilter }) => {
|
||||||
const { data, isError, isLoading, error, isFetched, isFetching } =
|
const { data, isError, isLoading, error, isFetched, isFetching } =
|
||||||
useContactFilter();
|
useContactFilter();
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
const methods = useForm({
|
const methods = useForm({
|
||||||
resolver: zodResolver(contactsFilter),
|
resolver: zodResolver(contactsFilter),
|
||||||
defaultValues: defaultContactFilter,
|
defaultValues: defaultContactFilter,
|
||||||
@ -30,14 +33,24 @@ const ContactFilterPanel = ({ onApply, clearFilter }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
reset(defaultContactFilter);
|
reset(defaultContactFilter);
|
||||||
onApply(defaultContactFilter);
|
onApply(defaultContactFilter);
|
||||||
closePanel();
|
closePanel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
closePanel();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
if (isLoading || isFetching) return <ExpenseFilterSkeleton />;
|
if (isLoading || isFetching) return <ExpenseFilterSkeleton />;
|
||||||
if (isError && isFetched)
|
if (isError && isFetched)
|
||||||
return <div>Something went wrong Here- {error.message} </div>;
|
return <div>Something went wrong Here- {error.message} </div>;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="p-2 text-start">
|
<form onSubmit={handleSubmit(onSubmit)} className="p-2 text-start">
|
||||||
|
@ -139,9 +139,8 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
|
|||||||
<ul className="nav nav-tabs">
|
<ul className="nav nav-tabs">
|
||||||
<li className="nav-item cursor-pointer">
|
<li className="nav-item cursor-pointer">
|
||||||
<a
|
<a
|
||||||
className={`nav-link ${
|
className={`nav-link ${activeTab === "notes" ? "active" : ""
|
||||||
activeTab === "notes" ? "active" : ""
|
} fs-6`}
|
||||||
} fs-6`}
|
|
||||||
onClick={(e) => handleTabClick("notes", e)}
|
onClick={(e) => handleTabClick("notes", e)}
|
||||||
>
|
>
|
||||||
<i className="bx bx-notepad bx-sm me-1_5"></i>
|
<i className="bx bx-notepad bx-sm me-1_5"></i>
|
||||||
@ -150,9 +149,8 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
|
|||||||
</li>
|
</li>
|
||||||
<li className="nav-item cursor-pointer">
|
<li className="nav-item cursor-pointer">
|
||||||
<a
|
<a
|
||||||
className={`nav-link ${
|
className={`nav-link ${activeTab === "contacts" ? "active" : ""
|
||||||
activeTab === "contacts" ? "active" : ""
|
} fs-6`}
|
||||||
} fs-6`}
|
|
||||||
onClick={(e) => handleTabClick("contacts", e)}
|
onClick={(e) => handleTabClick("contacts", e)}
|
||||||
>
|
>
|
||||||
<i className="bx bxs-contact bx-sm me-1_5"></i>
|
<i className="bx bxs-contact bx-sm me-1_5"></i>
|
||||||
@ -168,105 +166,84 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
|
|||||||
{activeTab === "notes" && (
|
{activeTab === "notes" && (
|
||||||
<div className="col-8 col-md-3">
|
<div className="col-8 col-md-3">
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
placeholder="Search notes..."
|
placeholder="Search notes..."
|
||||||
value={searchNote}
|
value={searchNote}
|
||||||
onChange={(e) => setSearchNote(e.target.value)}
|
onChange={(e) => setSearchNote(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{activeTab === "contacts" && (
|
{activeTab === "contacts" && (
|
||||||
<div className="d-flex align-items-center gap-3">
|
<div className="d-flex align-items-center gap-3">
|
||||||
<div className="col-12 col-md-8 d-flex flex-row gap-2">
|
<div className="col-12 col-md-8 d-flex flex-row gap-2">
|
||||||
<div className="col-7 col-md-4">
|
<div className="col-7 col-md-4">
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
placeholder="Search contacts..."
|
placeholder="Search contacts..."
|
||||||
value={searchContact}
|
value={searchContact}
|
||||||
onChange={(e) => setsearchContact(e.target.value)}
|
onChange={(e) => setsearchContact(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className={`btn btn-sm p-1 ${
|
className={`btn btn-sm p-1 ${gridView ? " btn-primary" : " btn-outline-primary"
|
||||||
!gridView ? "btn-primary" : "btn-outline-primary"
|
}`}
|
||||||
}`}
|
|
||||||
onClick={() => setGridView(false)}
|
|
||||||
>
|
|
||||||
<i className="bx bx-list-ul"></i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
className={`btn btn-sm p-1 ${
|
|
||||||
gridView ? " btn-primary" : " btn-outline-primary"
|
|
||||||
}`}
|
|
||||||
onClick={() => setGridView(true)}
|
onClick={() => setGridView(true)}
|
||||||
>
|
>
|
||||||
<i className="bx bx-grid-alt"></i>
|
<i className="bx bx-grid-alt"></i>
|
||||||
</button>
|
</button>
|
||||||
<div className="form-check form-switch d-flex align-items-center d-none d-md-flex">
|
<button
|
||||||
<input
|
className={`btn btn-sm p-1 ${!gridView ? "btn-primary" : "btn-outline-primary"
|
||||||
type="checkbox"
|
}`}
|
||||||
className="form-check-input"
|
onClick={() => setGridView(false)}
|
||||||
role="switch"
|
|
||||||
id="inactiveEmployeesCheckbox"
|
|
||||||
checked={showActive}
|
|
||||||
onChange={(e) => setShowActive(e.target.checked)}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
className="form-check-label ms-2"
|
|
||||||
htmlFor="inactiveEmployeesCheckbox"
|
|
||||||
>
|
>
|
||||||
{showActive ? "Active" : "Inactive"} Contacts
|
<i className="bx bx-list-ul"></i>
|
||||||
</label>
|
</button>
|
||||||
|
|
||||||
|
<div className="form-check form-switch d-flex align-items-end d-none d-md-flex">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
role="switch"
|
||||||
|
id="inactiveEmployeesCheckbox"
|
||||||
|
checked={showActive}
|
||||||
|
onChange={(e) => setShowActive(e.target.checked)}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className="form-check-label ms-2"
|
||||||
|
htmlFor="inactiveEmployeesCheckbox"
|
||||||
|
>
|
||||||
|
{showActive ? "Active" : "In-active"} Contacts
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="col-12 col-md-2 d-flex justify-content-end align-items-center gap-2">
|
<div className="col-12 col-md-2 d-flex justify-content-end align-items-center gap-2">
|
||||||
<div className={`form-check form-switch d-flex align-items-center ${activeTab === "contacts" ? " d-flex d-md-none m-0":"d-none" }`}>
|
<div className=" btn-group">
|
||||||
<input
|
<button
|
||||||
type="checkbox"
|
className="btn btn-sm btn-label-secondary dropdown-toggle"
|
||||||
className="form-check-input"
|
type="button"
|
||||||
role="switch"
|
data-bs-toggle="dropdown"
|
||||||
id="inactiveEmployeesCheckbox"
|
aria-expanded="false"
|
||||||
checked={showActive}
|
>
|
||||||
onChange={(e) => setShowActive(e.target.checked)}
|
<i className="bx bx-export me-2 bx-sm"></i>Export
|
||||||
/>
|
</button>
|
||||||
<label
|
<ul className="dropdown-menu">
|
||||||
className="form-check-label ms-2"
|
<li>
|
||||||
htmlFor="inactiveEmployeesCheckbox"
|
<a
|
||||||
|
className="dropdown-item cursor-pointer"
|
||||||
|
onClick={() => handleExport("csv")}
|
||||||
>
|
>
|
||||||
{showActive ? "Active" : "Inactive"} Contacts
|
<i className="bx bx-file me-1"></i> CSV
|
||||||
</label>
|
</a>
|
||||||
</div>
|
</li>
|
||||||
<div className=" btn-group">
|
</ul>
|
||||||
<button
|
</div>
|
||||||
className="btn btn-sm btn-label-secondary dropdown-toggle"
|
|
||||||
type="button"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<i className="bx bx-export me-2 bx-sm"></i>Export
|
|
||||||
</button>
|
|
||||||
<ul className="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="dropdown-item cursor-pointer"
|
|
||||||
onClick={() => handleExport("csv")}
|
|
||||||
>
|
|
||||||
<i className="bx bx-file me-1"></i> CSV
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
import {
|
import {
|
||||||
defaultNotesFilter,
|
defaultNotesFilter,
|
||||||
@ -8,11 +8,18 @@ import {
|
|||||||
import { useContactFilter, useNoteFilter } from "../../hooks/useDirectory";
|
import { useContactFilter, useNoteFilter } from "../../hooks/useDirectory";
|
||||||
import { ExpenseFilterSkeleton } from "../../components/Expenses/ExpenseSkeleton";
|
import { ExpenseFilterSkeleton } from "../../components/Expenses/ExpenseSkeleton";
|
||||||
import SelectMultiple from "../../components/common/SelectMultiple";
|
import SelectMultiple from "../../components/common/SelectMultiple";
|
||||||
|
import { useLocation } from "react-router-dom";
|
||||||
|
|
||||||
const NoteFilterPanel = ({ onApply, clearFilter }) => {
|
const NoteFilterPanel = ({ onApply, clearFilter }) => {
|
||||||
const { data, isError, isLoading, error, isFetched, isFetching } =
|
const { data, isError, isLoading, error, isFetched, isFetching } =
|
||||||
useNoteFilter();
|
useNoteFilter();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
closePanel();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const methods = useForm({
|
const methods = useForm({
|
||||||
resolver: zodResolver(notesFilter),
|
resolver: zodResolver(notesFilter),
|
||||||
defaultValues: defaultNotesFilter,
|
defaultValues: defaultNotesFilter,
|
||||||
@ -31,7 +38,7 @@ const NoteFilterPanel = ({ onApply, clearFilter }) => {
|
|||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
reset(defaultNotesFilter);
|
reset(defaultNotesFilter);
|
||||||
onApply(defaultNotesFilter);
|
onApply(defaultNotesFilter);
|
||||||
closePanel();
|
closePanel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -176,12 +176,10 @@ const EmployeeList = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loading && Array.isArray(employees)) {
|
if (!loading && Array.isArray(employees)) {
|
||||||
const sorted = [...employees].sort((a, b) => {
|
const sorted = [...employees].sort((a, b) => {
|
||||||
const nameA = `${a.firstName || ""}${a.middleName || ""}${
|
const nameA = `${a.firstName || ""}${a.middleName || ""}${a.lastName || ""
|
||||||
a.lastName || ""
|
}`.toLowerCase();
|
||||||
}`.toLowerCase();
|
const nameB = `${b.firstName || ""}${b.middleName || ""}${b.lastName || ""
|
||||||
const nameB = `${b.firstName || ""}${b.middleName || ""}${
|
}`.toLowerCase();
|
||||||
b.lastName || ""
|
|
||||||
}`.toLowerCase();
|
|
||||||
return nameA?.localeCompare(nameB);
|
return nameA?.localeCompare(nameB);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -258,9 +256,8 @@ const EmployeeList = () => {
|
|||||||
? "Suspend Employee"
|
? "Suspend Employee"
|
||||||
: "Reactivate Employee"
|
: "Reactivate Employee"
|
||||||
}
|
}
|
||||||
message={`Are you sure you want to ${
|
message={`Are you sure you want to ${selectedEmpFordelete?.isActive ? "suspend" : "reactivate"
|
||||||
selectedEmpFordelete?.isActive ? "suspend" : "reactivate"
|
} this employee?`}
|
||||||
} this employee?`}
|
|
||||||
onSubmit={(id) =>
|
onSubmit={(id) =>
|
||||||
suspendEmployee({
|
suspendEmployee({
|
||||||
employeeId: id,
|
employeeId: id,
|
||||||
@ -309,7 +306,7 @@ const EmployeeList = () => {
|
|||||||
className="form-check-label ms-0"
|
className="form-check-label ms-0"
|
||||||
htmlFor="inactiveEmployeesCheckbox"
|
htmlFor="inactiveEmployeesCheckbox"
|
||||||
>
|
>
|
||||||
Show Inactive Employees
|
In-active Employees
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -471,9 +468,8 @@ const EmployeeList = () => {
|
|||||||
Status
|
Status
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
className={`sorting_disabled ${
|
className={`sorting_disabled ${!Manage_Employee && "d-none"
|
||||||
!Manage_Employee && "d-none"
|
}`}
|
||||||
}`}
|
|
||||||
rowSpan="1"
|
rowSpan="1"
|
||||||
colSpan="1"
|
colSpan="1"
|
||||||
style={{ width: "50px" }}
|
style={{ width: "50px" }}
|
||||||
@ -493,20 +489,20 @@ const EmployeeList = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading &&
|
{!loading &&
|
||||||
displayData?.length === 0 &&
|
displayData?.length === 0 &&
|
||||||
(!searchText ) ? (
|
(!searchText) ? (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={8} className="border-0 py-3">
|
<td colSpan={8} className="border-0 py-3">
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
No Data Found
|
No Data Found
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{!loading &&
|
{!loading &&
|
||||||
displayData?.length === 0 &&
|
displayData?.length === 0 &&
|
||||||
(searchText ) ? (
|
(searchText) ? (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={8} className="border-0 py-3">
|
<td colSpan={8} className="border-0 py-3">
|
||||||
<div className="py-4">
|
<div className="py-4">
|
||||||
@ -542,18 +538,17 @@ const EmployeeList = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="text-start d-none d-sm-table-cell">
|
<td className="text-start d-none d-sm-table-cell">
|
||||||
{item.email ? (
|
{item.email ? (
|
||||||
<span className="text-truncate">
|
<span className="text-truncate">
|
||||||
<i className="bx bxs-envelope text-primary me-2"></i>
|
<i className="bx bxs-envelope text-primary me-2"></i>
|
||||||
{item.email}
|
{item.email}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-truncate text-italic">
|
<span className="d-block text-start text-muted fst-italic">NA</span>
|
||||||
-
|
|
||||||
</span>
|
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td className="text-start d-none d-sm-table-cell">
|
<td className="text-start d-none d-sm-table-cell">
|
||||||
<span className="text-truncate">
|
<span className="text-truncate">
|
||||||
<i className="bx bxs-phone-call text-primary me-2"></i>
|
<i className="bx bxs-phone-call text-primary me-2"></i>
|
||||||
@ -567,9 +562,14 @@ const EmployeeList = () => {
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td className=" d-none d-md-table-cell">
|
<td className="d-none d-md-table-cell">
|
||||||
{moment(item.joiningDate)?.format("DD-MMM-YYYY")}
|
{item.joiningDate ? (
|
||||||
|
moment(item.joiningDate).format("DD-MMM-YYYY")
|
||||||
|
) : (
|
||||||
|
<span className="d-block text-center text-muted fst-italic">NA</span>
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
{showInactive ? (
|
{showInactive ? (
|
||||||
<span
|
<span
|
||||||
|
Loading…
x
Reference in New Issue
Block a user