Compare commits
	
		
			No commits in common. "250c1bf2e1a8f6c502d10d686fb831442a52b164" and "164f3cc02f704452bb8dec8bfe05f63d2e3a6f8b" have entirely different histories.
		
	
	
		
			250c1bf2e1
			...
			164f3cc02f
		
	
		
@ -199,17 +199,3 @@
 | 
			
		||||
  .dropdown-menu[data-bs-popper] {
 | 
			
		||||
  inset-block-start: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ql-toolbar.ql-snow {
 | 
			
		||||
  border-top-left-radius: 5px;
 | 
			
		||||
  border-top-right-radius: 5px;
 | 
			
		||||
}
 | 
			
		||||
.ql-container.ql-snow {
 | 
			
		||||
  border-bottom-left-radius: 5px;
 | 
			
		||||
  border-bottom-right-radius: 5px;
 | 
			
		||||
  max-height: 200px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ql-editor {
 | 
			
		||||
  max-height: 200px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ const InfraPlanning = () =>
 | 
			
		||||
    <div className="card">
 | 
			
		||||
      <div className="card-body" style={{ padding: "0.5rem" }}>
 | 
			
		||||
        <div className="align-items-center">
 | 
			
		||||
            {/* <div className="row ">
 | 
			
		||||
            <div className="row ">
 | 
			
		||||
              <div className="col-sm-3 col-8 text-start mb-1">
 | 
			
		||||
                   <select name="DataTables_Table_0_length"
 | 
			
		||||
                                aria-controls="DataTables_Table_0"
 | 
			
		||||
@ -65,7 +65,7 @@ const InfraPlanning = () =>
 | 
			
		||||
                      </select>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
          </div> */}
 | 
			
		||||
          </div>
 | 
			
		||||
            <div className="row ">
 | 
			
		||||
              {project_deatilsLoader && ( <p>Loading...</p> )}
 | 
			
		||||
              {( !project_deatilsLoader && projects_Details?.buildings.length === 0 ) && ( <p>No Result Found</p> )}
 | 
			
		||||
 | 
			
		||||
@ -2,13 +2,10 @@ import React, { useState } from "react";
 | 
			
		||||
import LineChart from "../Charts/LineChart";
 | 
			
		||||
import { useProjects } from "../../hooks/useProjects";
 | 
			
		||||
import { useDashboard_Data } from "../../hooks/useDashboard_Data";
 | 
			
		||||
import {useSelector} from "react-redux";
 | 
			
		||||
 | 
			
		||||
const ProjectProgressChart = () =>
 | 
			
		||||
{
 | 
			
		||||
  const selectedProject = useSelector((store) => store.localVariables.projectId);
 | 
			
		||||
const ProjectProgressChart = () => {
 | 
			
		||||
  const { projects } = useProjects();
 | 
			
		||||
  // const [selectedProjectId, setSelectedProjectId] = useState("all");
 | 
			
		||||
  const [selectedProjectId, setSelectedProjectId] = useState("all");
 | 
			
		||||
  const [range, setRange] = useState("1W");
 | 
			
		||||
 | 
			
		||||
  const getDaysFromRange = (range) => {
 | 
			
		||||
@ -31,7 +28,7 @@ const ProjectProgressChart = () =>
 | 
			
		||||
  const { dashboard_data, loading: isLineChartLoading } = useDashboard_Data({
 | 
			
		||||
    days,
 | 
			
		||||
    FromDate,
 | 
			
		||||
     projectId:  selectedProject  === " "  ? "all" : selectedProject//  selectedProjectId === "all" ? null : selectedProjectId,
 | 
			
		||||
    projectId: selectedProjectId === "all" ? null : selectedProjectId,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const sortedDashboardData = [...dashboard_data].sort(
 | 
			
		||||
@ -66,7 +63,7 @@ const ProjectProgressChart = () =>
 | 
			
		||||
            <p className="card-subtitle">Progress Overview by Project</p>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
          {/* <div className="btn-group">
 | 
			
		||||
          <div className="btn-group">
 | 
			
		||||
            <button
 | 
			
		||||
              className="btn btn-outline-primary btn-sm dropdown-toggle"
 | 
			
		||||
              type="button"
 | 
			
		||||
@ -95,7 +92,7 @@ const ProjectProgressChart = () =>
 | 
			
		||||
                </li>
 | 
			
		||||
              ))}
 | 
			
		||||
            </ul>
 | 
			
		||||
          </div> */}
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        {/* Row 2: Time Range Buttons */}
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,6 @@ const CardViewDirectory = ({
 | 
			
		||||
}) => {
 | 
			
		||||
  const { buckets } = useBuckets();
 | 
			
		||||
  const { dirActions, setDirActions } = useDir();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      className="card text-start border-1"
 | 
			
		||||
@ -25,7 +24,8 @@ const CardViewDirectory = ({
 | 
			
		||||
      <div className="card-body  px-1 py-2 pb-0">
 | 
			
		||||
        <div className="d-flex justify-content-between">
 | 
			
		||||
          <div
 | 
			
		||||
            className={`d-flex align-items-center ${IsActive && "cursor-pointer"
 | 
			
		||||
            className={`d-flex align-items-center ${
 | 
			
		||||
              IsActive && "cursor-pointer"
 | 
			
		||||
            }`}
 | 
			
		||||
            onClick={() => {
 | 
			
		||||
              if (IsActive) {
 | 
			
		||||
@ -89,7 +89,8 @@ const CardViewDirectory = ({
 | 
			
		||||
            )}
 | 
			
		||||
            {!IsActive && (
 | 
			
		||||
              <i
 | 
			
		||||
                className={`bx  ${dirActions.action && dirActions.id === contact.id
 | 
			
		||||
                className={`bx  ${
 | 
			
		||||
                  dirActions.action && dirActions.id === contact.id
 | 
			
		||||
                    ? "bx-loader-alt bx-spin"
 | 
			
		||||
                    : "bx-recycle"
 | 
			
		||||
                } me-1 text-primary cursor-pointer`}
 | 
			
		||||
@ -113,7 +114,8 @@ const CardViewDirectory = ({
 | 
			
		||||
        </ul>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div
 | 
			
		||||
        className={`card-footer text-start px-1 py-1 ${IsActive && "cursor-pointer"
 | 
			
		||||
        className={`card-footer text-start px-1 py-1 ${
 | 
			
		||||
          IsActive && "cursor-pointer"
 | 
			
		||||
        }`}
 | 
			
		||||
        onClick={() => {
 | 
			
		||||
          if (IsActive) {
 | 
			
		||||
@ -149,16 +151,14 @@ const CardViewDirectory = ({
 | 
			
		||||
          </ul>
 | 
			
		||||
        )}
 | 
			
		||||
 | 
			
		||||
        {contact?.tags?.length > 0 ? (
 | 
			
		||||
        {contact?.contactCategory?.name ? (
 | 
			
		||||
          <ul className="list-inline m-0   ms-2">
 | 
			
		||||
            <li className="list-inline-item me-2 my-1">
 | 
			
		||||
              <i className="fa-solid fa-tag fs-6 ms-1"></i>
 | 
			
		||||
            </li>
 | 
			
		||||
            {contact.tags.map((tag, index) => (
 | 
			
		||||
              <li key={index} className="list-inline-item text-small active">
 | 
			
		||||
                {tag.name}
 | 
			
		||||
            <li className="list-inline-item text-small active">
 | 
			
		||||
              {contact?.contactCategory?.name}
 | 
			
		||||
            </li>
 | 
			
		||||
            ))}
 | 
			
		||||
          </ul>
 | 
			
		||||
        ) : (
 | 
			
		||||
          <ul className="list-inline m-0   ms-2">
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,7 @@ export const ContactSchema = z
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
      .min(1, { message: "At least one tag is required" }),
 | 
			
		||||
bucketIds: z.array(z.string()).nonempty({ message: "At least one bucket is required" })
 | 
			
		||||
bucketIds: z.array(z.string()).nonempty({ message: "At least one label is required" })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
//    .refine((data) => {
 | 
			
		||||
 | 
			
		||||
@ -66,6 +66,7 @@ const ManageBucket = () => {
 | 
			
		||||
      const cache_buckets = getCachedData("buckets") || [];
 | 
			
		||||
      let response;
 | 
			
		||||
 | 
			
		||||
      // Utility: Compare arrays regardless of order
 | 
			
		||||
      const arraysAreEqual = (a, b) => {
 | 
			
		||||
        if (a.length !== b.length) return false;
 | 
			
		||||
        const setA = new Set(a);
 | 
			
		||||
@ -73,9 +74,11 @@ const ManageBucket = () => {
 | 
			
		||||
        return [...setA].every((id) => setB.has(id));
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      // UPDATE existing bucket
 | 
			
		||||
      if (selected_bucket) {
 | 
			
		||||
        const payload = { ...data, id: selected_bucket.id };
 | 
			
		||||
 | 
			
		||||
        // 1. Update bucket details
 | 
			
		||||
        response = await DirectoryRepository.UpdateBuckets(
 | 
			
		||||
          selected_bucket.id,
 | 
			
		||||
          payload
 | 
			
		||||
@ -88,12 +91,14 @@ const ManageBucket = () => {
 | 
			
		||||
        cacheData("buckets", updatedBuckets);
 | 
			
		||||
        setBucketList(updatedBuckets);
 | 
			
		||||
 | 
			
		||||
        // 2. Update employee assignments if they changed
 | 
			
		||||
        const existingEmployeeIds = selected_bucket?.employeeIds || [];
 | 
			
		||||
        const employeesToUpdate = selectedEmployee.filter((emp) => {
 | 
			
		||||
          const isExisting = existingEmployeeIds.includes(emp.employeeId);
 | 
			
		||||
          return (!isExisting && emp.isActive) || (isExisting && !emp.isActive);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Create a filtered list of active employee IDs to compare
 | 
			
		||||
        const newActiveEmployeeIds = selectedEmployee
 | 
			
		||||
          .filter((emp) => {
 | 
			
		||||
            const isExisting = existingEmployeeIds.includes(emp.employeeId);
 | 
			
		||||
@ -105,7 +110,7 @@ const ManageBucket = () => {
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
          !arraysAreEqual(newActiveEmployeeIds, existingEmployeeIds) &&
 | 
			
		||||
          employeesToUpdate.length !== 0
 | 
			
		||||
          employeesToUpdate.length != 0
 | 
			
		||||
        ) {
 | 
			
		||||
          try {
 | 
			
		||||
            response = await DirectoryRepository.AssignedBuckets(
 | 
			
		||||
@ -128,7 +133,10 @@ const ManageBucket = () => {
 | 
			
		||||
 | 
			
		||||
        setBucketList(updatedData);
 | 
			
		||||
        showToast("Bucket Updated Successfully", "success");
 | 
			
		||||
      } else {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // CREATE new bucket
 | 
			
		||||
      else {
 | 
			
		||||
        response = await DirectoryRepository.CreateBuckets(data);
 | 
			
		||||
 | 
			
		||||
        const updatedBuckets = [...cache_buckets, response?.data];
 | 
			
		||||
@ -155,7 +163,7 @@ const ManageBucket = () => {
 | 
			
		||||
      const resp = await DirectoryRepository.DeleteBucket(deleteBucket);
 | 
			
		||||
      const cache_buckets = getCachedData("buckets") || [];
 | 
			
		||||
      const updatedBuckets = cache_buckets.filter(
 | 
			
		||||
        (bucket) => bucket.id !== deleteBucket
 | 
			
		||||
        (bucket) => bucket.id != deleteBucket
 | 
			
		||||
      );
 | 
			
		||||
      cacheData("buckets", updatedBuckets);
 | 
			
		||||
      setBucketList(updatedBuckets);
 | 
			
		||||
@ -185,8 +193,6 @@ const ManageBucket = () => {
 | 
			
		||||
    select_bucket(null);
 | 
			
		||||
    setAction_bucket(false);
 | 
			
		||||
    setSubmitting(false);
 | 
			
		||||
    reset({ name: "", description: "" });
 | 
			
		||||
    setSelectEmployee([]);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const sortedBucktesList = sortedBuckteList?.filter((bucket) => {
 | 
			
		||||
@ -194,7 +200,6 @@ const ManageBucket = () => {
 | 
			
		||||
    const name = bucket.name?.toLowerCase();
 | 
			
		||||
    return name?.includes(term);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      {deleteBucket && (
 | 
			
		||||
@ -210,14 +215,15 @@ const ManageBucket = () => {
 | 
			
		||||
          <ConfirmModal
 | 
			
		||||
            type={"delete"}
 | 
			
		||||
            header={"Delete Bucket"}
 | 
			
		||||
            message={"Are you sure you want to delete this bucket?"}
 | 
			
		||||
            message={"Are you sure you want delete?"}
 | 
			
		||||
            onSubmit={handleDeleteContact}
 | 
			
		||||
            onClose={() => setDeleteBucket(null)}
 | 
			
		||||
            // loading={IsDeleting}
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <div className="container m-0 p-0" style={{ minHeight: "00px" }}>
 | 
			
		||||
      <div className="container m-0 p-0" style={{ minHeight: "200px" }}>
 | 
			
		||||
        <div className="d-flex justify-content-center">
 | 
			
		||||
          <p className="fs-6 fw-semibold m-0">Manage Buckets</p>
 | 
			
		||||
        </div>
 | 
			
		||||
@ -251,12 +257,7 @@ const ManageBucket = () => {
 | 
			
		||||
            className={`btn  btn-sm btn-primary ms-auto ${
 | 
			
		||||
              action_bucket ? "d-none" : ""
 | 
			
		||||
            }`}
 | 
			
		||||
            onClick={() => {
 | 
			
		||||
              setAction_bucket(true);
 | 
			
		||||
              select_bucket(null);
 | 
			
		||||
              reset({ name: "", description: "" });
 | 
			
		||||
              setSelectEmployee([]);
 | 
			
		||||
            }}
 | 
			
		||||
            onClick={() => setAction_bucket(true)}
 | 
			
		||||
          >
 | 
			
		||||
            <i className="bx bx-plus-circle me-2"></i>
 | 
			
		||||
            Add Bucket
 | 
			
		||||
@ -264,88 +265,113 @@ const ManageBucket = () => {
 | 
			
		||||
        </div>
 | 
			
		||||
        <div>
 | 
			
		||||
          {!action_bucket ? (
 | 
			
		||||
            <div className="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-3 pt-3 px-2 px-sm-0">
 | 
			
		||||
            <div className="table-responsive text-nowrap pt-1 px-2 px-sm-0 mt-3">
 | 
			
		||||
              <table className="table  px-2">
 | 
			
		||||
                <thead className="p-0">
 | 
			
		||||
                  <tr className="p-0">
 | 
			
		||||
                    <th
 | 
			
		||||
                      colSpan={2}
 | 
			
		||||
                      className="cursor-pointer"
 | 
			
		||||
                      onClick={() => requestSort((e) => `${e.name} `)}
 | 
			
		||||
                    >
 | 
			
		||||
                      <div className="d-flex justify-content-start align-items-center gap-1 mx-2">
 | 
			
		||||
                        <span>Name {getSortIcon()}</span>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </th>
 | 
			
		||||
                    <th className="text-start d-none d-sm-table-cell">
 | 
			
		||||
                      <div className="d-flex align-items-center justify-content-center gap-1">
 | 
			
		||||
                        <span>Description</span>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </th>
 | 
			
		||||
                    <th>Contacts</th>
 | 
			
		||||
                    <th>
 | 
			
		||||
                      <div className="d-flex align-items-center justify-content-center gap-1">
 | 
			
		||||
                        <span>Action</span>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </th>
 | 
			
		||||
                  </tr>
 | 
			
		||||
                </thead>
 | 
			
		||||
 | 
			
		||||
                <tbody className="table-border-bottom-0 overflow-auto">
 | 
			
		||||
                  {loading && (
 | 
			
		||||
                <div className="col-12">
 | 
			
		||||
                    <tr className="mt-10">
 | 
			
		||||
                      <td colSpan={5}>
 | 
			
		||||
                        {" "}
 | 
			
		||||
                        <div className="d-flex justify-content-center align-items-center py-5">
 | 
			
		||||
                          Loading...
 | 
			
		||||
                        </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                      </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                  )}
 | 
			
		||||
              {!loading && buckets.length === 0 && (
 | 
			
		||||
                <div className="col-12">
 | 
			
		||||
                  {!loading && buckets.length == 0 && (
 | 
			
		||||
                    <tr>
 | 
			
		||||
                      <td colSpan={5}>
 | 
			
		||||
                        <div className="d-flex justify-content-center align-items-center py-5">
 | 
			
		||||
                    No Buckets Available.
 | 
			
		||||
                  </div>
 | 
			
		||||
                          Bucket Not Available.
 | 
			
		||||
                        </div>
 | 
			
		||||
                      </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                  )}
 | 
			
		||||
              {!loading && sortedBucktesList.length === 0 && (
 | 
			
		||||
                <div className="col-12">
 | 
			
		||||
                  {!loading && sortedBucktesList.length == 0 && (
 | 
			
		||||
                    <tr>
 | 
			
		||||
                      <td className="text-center py-4 h-25" colSpan={5}>
 | 
			
		||||
                        <div className="d-flex justify-content-center align-items-center py-5">
 | 
			
		||||
                          No Matching Bucket Found.
 | 
			
		||||
                        </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                      </td>
 | 
			
		||||
                    </tr>
 | 
			
		||||
                  )}
 | 
			
		||||
                  {!loading &&
 | 
			
		||||
                    sortedBucktesList.map((bucket) => (
 | 
			
		||||
                  <div className="col" key={bucket.id}>
 | 
			
		||||
                    <div className="card h-100">
 | 
			
		||||
                      <div className="card-body p-4">
 | 
			
		||||
                        <h6 className="card-title d-flex justify-content-between align-items-center">
 | 
			
		||||
                          <span>{bucket.name}</span>
 | 
			
		||||
                      <tr key={bucket.id}>
 | 
			
		||||
                        <td colSpan={2} className="text-start text-wrap">
 | 
			
		||||
                          <i className="bx bx-right-arrow-alt me-1"></i>{" "}
 | 
			
		||||
                          {bucket.name}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td
 | 
			
		||||
                          className="text-start d-none d-sm-table-cell text-truncate"
 | 
			
		||||
                          style={{
 | 
			
		||||
                            maxWidth: "300px",
 | 
			
		||||
                            whiteSpace: "wrap",
 | 
			
		||||
                            overflow: "hidden",
 | 
			
		||||
                            textOverflow: "ellipsis",
 | 
			
		||||
                          }}
 | 
			
		||||
                          title={bucket.description}
 | 
			
		||||
                        >
 | 
			
		||||
                          {bucket.description}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>{bucket.numberOfContacts}</td>
 | 
			
		||||
                        <td className="justify-content-center">
 | 
			
		||||
                          {(DirManager ||
 | 
			
		||||
                            DirAdmin ||
 | 
			
		||||
                            bucket?.createdBy?.id ===
 | 
			
		||||
                              profile?.employeeInfo?.id) && (
 | 
			
		||||
                            <div className="d-flex gap-2">
 | 
			
		||||
                            <div className="d-flex justify-content-center align-items-center gap-2">
 | 
			
		||||
                              <i
 | 
			
		||||
                                className="bx bx-edit bx-sm text-primary cursor-pointer"
 | 
			
		||||
                                className="bx bx-edit bx-sm text-primary cursor-pointer "
 | 
			
		||||
                                onClick={() => {
 | 
			
		||||
                                  select_bucket(bucket);
 | 
			
		||||
                                  setAction_bucket(true);
 | 
			
		||||
                                  const initialSelectedEmployees = employeesList
 | 
			
		||||
                                    .filter((emp) =>
 | 
			
		||||
                                      bucket.employeeIds?.includes(
 | 
			
		||||
                                        emp.employeeId
 | 
			
		||||
                                      )
 | 
			
		||||
                                    )
 | 
			
		||||
                                    .map((emp) => ({ ...emp, isActive: true }));
 | 
			
		||||
                                  setSelectEmployee(initialSelectedEmployees);
 | 
			
		||||
                                }}
 | 
			
		||||
                              ></i>
 | 
			
		||||
                              <i
 | 
			
		||||
                                className="bx bx-trash bx-sm text-danger cursor-pointer ms-0"
 | 
			
		||||
                                className="bx bx-trash bx-sm text-danger cursor-pointer"
 | 
			
		||||
                                onClick={() => setDeleteBucket(bucket?.id)}
 | 
			
		||||
                              ></i>
 | 
			
		||||
                            </div>
 | 
			
		||||
                          )}
 | 
			
		||||
                        </h6>
 | 
			
		||||
                        <h6 className="card-subtitle mb-2 text-muted  text-start">
 | 
			
		||||
                          Contacts:{" "}
 | 
			
		||||
                          {bucket.numberOfContacts
 | 
			
		||||
                            ? bucket.numberOfContacts
 | 
			
		||||
                            : 0}
 | 
			
		||||
                        </h6>
 | 
			
		||||
                        <p
 | 
			
		||||
                          className="card-text text-start"
 | 
			
		||||
                          title={bucket.description}
 | 
			
		||||
                        >
 | 
			
		||||
                          {bucket.description || "No description available."}
 | 
			
		||||
                        </p>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                        </td>
 | 
			
		||||
                      </tr>
 | 
			
		||||
                    ))}
 | 
			
		||||
                </tbody>
 | 
			
		||||
              </table>
 | 
			
		||||
            </div>
 | 
			
		||||
          ) : (
 | 
			
		||||
            <>
 | 
			
		||||
              <form onSubmit={handleSubmit(onSubmit)} className="px-2 px-sm-0">
 | 
			
		||||
                <div className="mb-3">
 | 
			
		||||
                  <label htmlFor="bucketName" className="form-label">
 | 
			
		||||
                    Bucket Name
 | 
			
		||||
                  </label>
 | 
			
		||||
                <div className="">
 | 
			
		||||
                  <label className="form-label">Bucket Name</label>
 | 
			
		||||
                  <input
 | 
			
		||||
                    id="bucketName"
 | 
			
		||||
                    className="form-control form-control-sm"
 | 
			
		||||
                    {...register("name")}
 | 
			
		||||
                  />
 | 
			
		||||
@ -353,12 +379,9 @@ const ManageBucket = () => {
 | 
			
		||||
                    <small className="danger-text">{errors.name.message}</small>
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div className="mb-3">
 | 
			
		||||
                  <label htmlFor="bucketDescription" className="form-label">
 | 
			
		||||
                    Bucket Description
 | 
			
		||||
                  </label>
 | 
			
		||||
                <div className="">
 | 
			
		||||
                  <label className="form-label">Bucket Discription</label>
 | 
			
		||||
                  <textarea
 | 
			
		||||
                    id="bucketDescription"
 | 
			
		||||
                    className="form-control form-control-sm"
 | 
			
		||||
                    rows="3"
 | 
			
		||||
                    {...register("description")}
 | 
			
		||||
@ -378,10 +401,9 @@ const ManageBucket = () => {
 | 
			
		||||
                  />
 | 
			
		||||
                )}
 | 
			
		||||
 | 
			
		||||
                <div className="mt-4 d-flex justify-content-center gap-3">
 | 
			
		||||
                <div className="mt-2 d-flex justify-content-center gap-3">
 | 
			
		||||
                  <button
 | 
			
		||||
                    type="button"
 | 
			
		||||
                    onClick={handleBack}
 | 
			
		||||
                    onClick={() => handleBack()}
 | 
			
		||||
                    className="btn btn-sm btn-secondary"
 | 
			
		||||
                    disabled={isSubmitting}
 | 
			
		||||
                  >
 | 
			
		||||
 | 
			
		||||
@ -362,7 +362,7 @@ const ManageDirectory = ({ submitContact, onCLosed }) => {
 | 
			
		||||
              {buckets?.map((item) => (
 | 
			
		||||
                <li
 | 
			
		||||
                  key={item.id}
 | 
			
		||||
                  className="list-inline-item flex-shrink-0 me-6 mb-1"
 | 
			
		||||
                  className="list-inline-item flex-shrink-0 me-6 mb-2"
 | 
			
		||||
                >
 | 
			
		||||
                  <div className="form-check ">
 | 
			
		||||
                    <input
 | 
			
		||||
@ -381,13 +381,12 @@ const ManageDirectory = ({ submitContact, onCLosed }) => {
 | 
			
		||||
                  </div>
 | 
			
		||||
                </li>
 | 
			
		||||
              ))}
 | 
			
		||||
            
 | 
			
		||||
            </ul>
 | 
			
		||||
              {errors.bucketIds && (
 | 
			
		||||
                <small className="danger-text mt-0">
 | 
			
		||||
                  {errors.bucketIds.message}
 | 
			
		||||
                </small>
 | 
			
		||||
              )}
 | 
			
		||||
            </ul>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -132,17 +132,17 @@ const NoteCardDirectory = ({
 | 
			
		||||
        <div className="d-flex align-items-center">
 | 
			
		||||
          <Avatar
 | 
			
		||||
            size="xs"
 | 
			
		||||
            firstName={noteItem?.createdBy?.firstName}
 | 
			
		||||
            lastName={noteItem?.createdBy?.lastName}
 | 
			
		||||
            firstName={noteItem.createdBy.firstName}
 | 
			
		||||
            lastName={noteItem.createdBy.lastName}
 | 
			
		||||
            className="m-0"
 | 
			
		||||
          />
 | 
			
		||||
          <div className="d-flex flex-column ms-2">
 | 
			
		||||
            <span className="fw-semibold small">
 | 
			
		||||
              {noteItem?.createdBy?.firstName} {noteItem?.createdBy?.lastName}
 | 
			
		||||
              {noteItem.createdBy.firstName} {noteItem.createdBy.lastName}
 | 
			
		||||
            </span>
 | 
			
		||||
            <span className="text-muted" style={{ fontSize: "10px" }}>
 | 
			
		||||
              {moment
 | 
			
		||||
                .utc(noteItem?.createdAt)
 | 
			
		||||
                .utc(noteItem.createdAt)
 | 
			
		||||
                .add(5, "hours")
 | 
			
		||||
                .add(30, "minutes")
 | 
			
		||||
                .format("MMMM DD, YYYY [at] hh:mm A")}
 | 
			
		||||
@ -160,7 +160,7 @@ const NoteCardDirectory = ({
 | 
			
		||||
              {!isDeleting ? (
 | 
			
		||||
                <i
 | 
			
		||||
                  className="bx bx-trash bx-sm me-1 text-secondary cursor-pointer"
 | 
			
		||||
                  onClick={() => handleDeleteNote(!noteItem?.isActive)}
 | 
			
		||||
                  onClick={() => handleDeleteNote(!noteItem.isActive)}
 | 
			
		||||
                ></i>
 | 
			
		||||
              ) : (
 | 
			
		||||
                <div
 | 
			
		||||
@ -176,7 +176,7 @@ const NoteCardDirectory = ({
 | 
			
		||||
          ) : (
 | 
			
		||||
            <i
 | 
			
		||||
              className="bx bx-recycle  me-1 text-primary cursor-pointer"
 | 
			
		||||
              onClick={() => handleDeleteNote(!noteItem?.isActive)}
 | 
			
		||||
              onClick={() => handleDeleteNote(!noteItem.isActive)}
 | 
			
		||||
              title="Restore"
 | 
			
		||||
            ></i>
 | 
			
		||||
          )}
 | 
			
		||||
 | 
			
		||||
@ -101,9 +101,6 @@ const NotesDirectory = ({
 | 
			
		||||
    <div className="text-start">
 | 
			
		||||
      <div className="d-flex align-items-center justify-content-between">
 | 
			
		||||
        <p className="fw-semibold m-0">Notes :</p>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className="d-flex align-items-center justify-content-between mb-5">
 | 
			
		||||
        {contactNotes?.length > 0 && (
 | 
			
		||||
        <div className="m-0 d-flex aligin-items-center">
 | 
			
		||||
          <label className="switch switch-primary">
 | 
			
		||||
            <input
 | 
			
		||||
@ -120,51 +117,12 @@ const NotesDirectory = ({
 | 
			
		||||
                {/* <i class="icon-base bx bx-x"></i> */}
 | 
			
		||||
              </span>
 | 
			
		||||
            </span>
 | 
			
		||||
              <span className="switch-label ">Include Deleted Notes</span>
 | 
			
		||||
            <span className="switch-label ">Show Including Inactive Notes</span>
 | 
			
		||||
          </label>
 | 
			
		||||
        </div>
 | 
			
		||||
        )}
 | 
			
		||||
 | 
			
		||||
        {!addNote && (
 | 
			
		||||
          <div
 | 
			
		||||
            className={`
 | 
			
		||||
              ${
 | 
			
		||||
                contactNotes?.length > 0
 | 
			
		||||
                  ? "d-flex justify-content-center px-2"
 | 
			
		||||
                  : "d-flex justify-content-center px-2w-100"
 | 
			
		||||
              }`}
 | 
			
		||||
          >
 | 
			
		||||
            <span
 | 
			
		||||
              className={`btn btn-sm   ${
 | 
			
		||||
                addNote ? "btn-danger" : "btn-primary"
 | 
			
		||||
              }`}
 | 
			
		||||
              onClick={() => setAddNote(!addNote)}
 | 
			
		||||
            >
 | 
			
		||||
              {addNote ? "Hide Editor" : "Add a Note"}
 | 
			
		||||
            </span>
 | 
			
		||||
          </div>
 | 
			
		||||
        )}
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      {addNote && (
 | 
			
		||||
        <div className="card m-2 mb-5">
 | 
			
		||||
          <button
 | 
			
		||||
            type="button"
 | 
			
		||||
            class="btn  btn-close btn-secondary position-absolute top-0 end-0 m-2 mt-3 rounded-circle"
 | 
			
		||||
            aria-label="Close"
 | 
			
		||||
            style={{ backgroundColor: "#eee", color: "white" }}
 | 
			
		||||
            onClick={() => setAddNote(!addNote)}
 | 
			
		||||
          ></button>
 | 
			
		||||
          {/* <div className="d-flex justify-content-end px-2">
 | 
			
		||||
            <span
 | 
			
		||||
              className={`btn btn-sm   ${
 | 
			
		||||
                addNote ? "btn-danger" : "btn-primary"
 | 
			
		||||
              }`}
 | 
			
		||||
              onClick={() => setAddNote(!addNote)}
 | 
			
		||||
            >
 | 
			
		||||
              {addNote ? "Hide Editor" : "Add Note"}
 | 
			
		||||
            </span>
 | 
			
		||||
          </div> */}
 | 
			
		||||
        <form onSubmit={handleSubmit(onSubmit)}>
 | 
			
		||||
          <Editor
 | 
			
		||||
            value={noteValue}
 | 
			
		||||
@ -177,9 +135,15 @@ const NotesDirectory = ({
 | 
			
		||||
            <p className="text-danger small mt-1">{errors.note.message}</p>
 | 
			
		||||
          )}
 | 
			
		||||
        </form>
 | 
			
		||||
        </div>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <div className="d-flex justify-content-end px-2">
 | 
			
		||||
        <span
 | 
			
		||||
          className={`btn btn-sm   ${addNote ? "btn-danger" : "btn-primary"}`}
 | 
			
		||||
          onClick={() => setAddNote(!addNote)}
 | 
			
		||||
        >
 | 
			
		||||
          {addNote ? "Hide Editor" : "Add Note"}
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div className=" justify-content-start  px-1 mt-1">
 | 
			
		||||
        {isLoading && (
 | 
			
		||||
          <div className="text-center">
 | 
			
		||||
 | 
			
		||||
@ -159,23 +159,6 @@ const ProfileContactDirectory = ({ contact, setOpen_contact, closeModal }) => {
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
             {contactProfile?.tags?.length > 0 && (
 | 
			
		||||
              <div className="d-flex mb-2">
 | 
			
		||||
                <div style={{ width: "100px", minWidth: "100px" }}>
 | 
			
		||||
                  <p className="m-0">Tags : </p>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div>
 | 
			
		||||
                  <ul className="list-inline mb-0">
 | 
			
		||||
                    {contactProfile.tags.map((tag, index) => (
 | 
			
		||||
                      <li key={index} className="list-inline-item">
 | 
			
		||||
                        <i className="fa-solid fa-tag  me-1"></i>
 | 
			
		||||
                        {tag.name}
 | 
			
		||||
                      </li>
 | 
			
		||||
                    ))}
 | 
			
		||||
                  </ul>
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
 | 
			
		||||
            {contactProfile?.buckets?.length > 0 && (
 | 
			
		||||
              <div className="d-flex ">
 | 
			
		||||
 | 
			
		||||
@ -1,24 +1,18 @@
 | 
			
		||||
import getGreetingMessage from "../../utils/greetingHandler";
 | 
			
		||||
import { clearAllCache } from "../../slices/apiDataManager";
 | 
			
		||||
import AuthRepository from "../../repositories/AuthRepository";
 | 
			
		||||
import { useDispatch, useSelector } from "react-redux";
 | 
			
		||||
import { changeMaster, setProjectId } from "../../slices/localVariablesSlice";
 | 
			
		||||
import { useDispatch } from "react-redux";
 | 
			
		||||
import { changeMaster } from "../../slices/localVariablesSlice";
 | 
			
		||||
import useMaster from "../../hooks/masterHook/useMaster";
 | 
			
		||||
import { useProfile } from "../../hooks/useProfile";
 | 
			
		||||
import { useLocation, useNavigate, useParams } from "react-router-dom";
 | 
			
		||||
import { useNavigate } from "react-router-dom";
 | 
			
		||||
import Avatar from "../../components/common/Avatar";
 | 
			
		||||
import { useChangePassword } from "../Context/ChangePasswordContext";
 | 
			
		||||
import { useProjects } from "../../hooks/useProjects";
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import { useProjectName } from "../../hooks/useProjects";
 | 
			
		||||
 | 
			
		||||
const Header = () => {
 | 
			
		||||
  const { profile } = useProfile();
 | 
			
		||||
  const location = useLocation();
 | 
			
		||||
  const dispatch = useDispatch(changeMaster("Job Role"));
 | 
			
		||||
  const { data, loading } = useMaster();
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
 | 
			
		||||
  const getRole = (roles, joRoleId) => {
 | 
			
		||||
    if (!Array.isArray(roles)) return "User";
 | 
			
		||||
    let role = roles.find((role) => role.id === joRoleId);
 | 
			
		||||
@ -55,7 +49,7 @@ const Header = () => {
 | 
			
		||||
    } catch (error) {
 | 
			
		||||
      console.error(
 | 
			
		||||
        "Error during logout:",
 | 
			
		||||
        error?.response?.data || error.message
 | 
			
		||||
        error.response?.data || error.message
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
@ -63,36 +57,11 @@ const Header = () => {
 | 
			
		||||
  const handleProfilePage = () => {
 | 
			
		||||
    navigate(`/employee/${profile?.employeeInfo?.id}?for=attendance`);
 | 
			
		||||
  };
 | 
			
		||||
  // const { projects, loading: projectLoading } = useProjects();
 | 
			
		||||
  const { projectNames, loading: projectLoading } = useProjectName();
 | 
			
		||||
 | 
			
		||||
  const selectedProject = useSelector(
 | 
			
		||||
    (store) => store.localVariables.projectId
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const selectedProjectName = projectNames?.find(
 | 
			
		||||
    (p) => p?.id === selectedProject
 | 
			
		||||
  )?.name;
 | 
			
		||||
 | 
			
		||||
  let displayText = "";
 | 
			
		||||
  if (selectedProjectName) {
 | 
			
		||||
    displayText = selectedProjectName;
 | 
			
		||||
  } else if (projectLoading && selectedProject) {
 | 
			
		||||
    displayText = selectedProject;
 | 
			
		||||
  } else if (projectLoading) {
 | 
			
		||||
    displayText = "Loading...";
 | 
			
		||||
  }
 | 
			
		||||
  // const ChangePasswordPage = () => {
 | 
			
		||||
  //   navigate(`/auth/changepassword`);
 | 
			
		||||
  // };
 | 
			
		||||
 | 
			
		||||
  const { openChangePassword } = useChangePassword();
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (projectNames && selectedProject !== " ") {
 | 
			
		||||
      dispatch(setProjectId(projectNames[0]?.id));
 | 
			
		||||
    }
 | 
			
		||||
  }, [projectNames]);
 | 
			
		||||
 | 
			
		||||
  /** Check if current page id project details page */
 | 
			
		||||
  const isProjectPath = /^\/projects\/[a-f0-9-]{36}$/.test(location.pathname);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <nav
 | 
			
		||||
      className="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
 | 
			
		||||
@ -110,46 +79,47 @@ const Header = () => {
 | 
			
		||||
        className="navbar-nav-right d-flex align-items-center justify-content-between"
 | 
			
		||||
        id="navbar-collapse"
 | 
			
		||||
      >
 | 
			
		||||
        {projectNames?.length > 0 && (
 | 
			
		||||
          <div className=" align-items-center">
 | 
			
		||||
            {!isProjectPath && (
 | 
			
		||||
              <>
 | 
			
		||||
                <i
 | 
			
		||||
                  className="rounded-circle bx bx-building-house"
 | 
			
		||||
                  style={{ fontSize: "xx-large" }}
 | 
			
		||||
                ></i>
 | 
			
		||||
                <div className="btn-group">
 | 
			
		||||
                  <button
 | 
			
		||||
                    className={`btn btn-sm-sm btn-xl ${
 | 
			
		||||
                      projectNames?.length > 1 && "dropdown-toggle"
 | 
			
		||||
                    } px-1`}
 | 
			
		||||
                    type="button"
 | 
			
		||||
                    data-bs-toggle="dropdown"
 | 
			
		||||
        {/* Search */}
 | 
			
		||||
        <div className="navbar-nav align-items-center">
 | 
			
		||||
          <div className="nav-item navbar-search-wrapper mb-0">
 | 
			
		||||
            <a className="nav-item nav-link search-toggler px-0" href="#">
 | 
			
		||||
              <span
 | 
			
		||||
                className="d-inline-block text-body-secondary fw-normal"
 | 
			
		||||
                id="autocomplete"
 | 
			
		||||
              >
 | 
			
		||||
                <div
 | 
			
		||||
                  className="aa-Autocomplete"
 | 
			
		||||
                  role="combobox"
 | 
			
		||||
                  aria-expanded="false"
 | 
			
		||||
                  aria-haspopup="listbox"
 | 
			
		||||
                  aria-labelledby="autocomplete-0-label"
 | 
			
		||||
                >
 | 
			
		||||
                    {displayText}
 | 
			
		||||
                  </button>
 | 
			
		||||
 | 
			
		||||
                  {projectNames?.length > 1 && (
 | 
			
		||||
                    <ul className="dropdown-menu">
 | 
			
		||||
                      {projectNames?.map((project) => (
 | 
			
		||||
                        <li key={project?.id}>
 | 
			
		||||
                  <button
 | 
			
		||||
                            className="dropdown-item"
 | 
			
		||||
                            onClick={() => dispatch(setProjectId(project?.id))}
 | 
			
		||||
                    type="button"
 | 
			
		||||
                    className="aa-DetachedSearchButton"
 | 
			
		||||
                    title="Search"
 | 
			
		||||
                    id="autocomplete-0-label"
 | 
			
		||||
                  >
 | 
			
		||||
                            {project?.name}
 | 
			
		||||
                    <div className="aa-DetachedSearchButtonIcon"></div>
 | 
			
		||||
                    <div className="aa-DetachedSearchButtonPlaceholder">
 | 
			
		||||
                      Search -{" "}
 | 
			
		||||
                      <span className="font-italic">Coming Soon...</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div className="aa-DetachedSearchButtonQuery"></div>
 | 
			
		||||
                  </button>
 | 
			
		||||
                        </li>
 | 
			
		||||
                      ))}
 | 
			
		||||
                    </ul>
 | 
			
		||||
                  )}
 | 
			
		||||
                </div>
 | 
			
		||||
              </>
 | 
			
		||||
            )}
 | 
			
		||||
              </span>
 | 
			
		||||
            </a>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        )}
 | 
			
		||||
 | 
			
		||||
        {/* marquee */}
 | 
			
		||||
        {/* <marquee>
 | 
			
		||||
          {" "}
 | 
			
		||||
          {getGreetingMessage(profile?.employeeInfo?.firstName)}
 | 
			
		||||
        </marquee> */}
 | 
			
		||||
 | 
			
		||||
        {/* icon list */}
 | 
			
		||||
        <ul className="navbar-nav flex-row align-items-center ms-md-auto">
 | 
			
		||||
          <li className="nav-item dropdown-shortcuts navbar-dropdown dropdown me-2 me-xl-0">
 | 
			
		||||
            <a
 | 
			
		||||
@ -193,7 +163,7 @@ const Header = () => {
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div className="dropdown-shortcuts-item col">
 | 
			
		||||
                    <a
 | 
			
		||||
                      onClick={() => navigate(`/projectNames`)}
 | 
			
		||||
                      onClick={() => navigate(`/projects`)}
 | 
			
		||||
                      className="text-heading text-truncate cursor-pointer"
 | 
			
		||||
                    >
 | 
			
		||||
                      <span className="dropdown-shortcuts-icon rounded-circle mb-3">
 | 
			
		||||
@ -632,7 +602,7 @@ const Header = () => {
 | 
			
		||||
                  <span className="align-middle">My Profile</span>
 | 
			
		||||
                </a>
 | 
			
		||||
              </li>
 | 
			
		||||
              <li>
 | 
			
		||||
              <li onClick={handleProfilePage}>
 | 
			
		||||
                <a
 | 
			
		||||
                  aria-label="go to setting "
 | 
			
		||||
                  className="dropdown-item cusor-pointer"
 | 
			
		||||
@ -641,7 +611,7 @@ const Header = () => {
 | 
			
		||||
                  <span className="align-middle">Settings</span>
 | 
			
		||||
                </a>
 | 
			
		||||
              </li>
 | 
			
		||||
              <li>
 | 
			
		||||
              {/* <li>
 | 
			
		||||
                <a
 | 
			
		||||
                  aria-label="go to billing "
 | 
			
		||||
                  className="dropdown-item cusor-pointer"
 | 
			
		||||
@ -656,7 +626,7 @@ const Header = () => {
 | 
			
		||||
                    </span>
 | 
			
		||||
                  </span>
 | 
			
		||||
                </a>
 | 
			
		||||
              </li>
 | 
			
		||||
              </li> */}
 | 
			
		||||
              <li onClick={openChangePassword}>
 | 
			
		||||
                {" "}
 | 
			
		||||
                {/* Use the function from the context */}
 | 
			
		||||
 | 
			
		||||
@ -108,10 +108,7 @@ const InfraTable = ({ buildings }) => {
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
   if (buildings && buildings.length > 0) {
 | 
			
		||||
    setProjectBuilding(buildings);
 | 
			
		||||
    setExpandedBuildings([buildings[0].id]);
 | 
			
		||||
  }
 | 
			
		||||
  }, [buildings]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ const Editor = ({
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="editor-wrapper m-2 mt-5">
 | 
			
		||||
    <div className="editor-wrapper m-5">
 | 
			
		||||
      <div id="custom-toolbar" className="ql-toolbar ql-snow custom-toolbar">
 | 
			
		||||
        <div className="d-flex justify-content-between align-items-center w-100">
 | 
			
		||||
          {/* Left: Quill Format Buttons */}
 | 
			
		||||
 | 
			
		||||
@ -10,15 +10,21 @@
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "text": "Projects",
 | 
			
		||||
                "icon": "bx bx-building-house",
 | 
			
		||||
                "icon": "bx bx-layout",
 | 
			
		||||
                "available": true,
 | 
			
		||||
                "link": "/ui",
 | 
			
		||||
                "submenu": [
 | 
			
		||||
                    {
 | 
			
		||||
                        "text": "Project List",
 | 
			
		||||
                        "available": true,
 | 
			
		||||
                        "link": "/projects"
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                        "text": "Employees",
 | 
			
		||||
                "icon": "bx bx-user",
 | 
			
		||||
                        "available": true,
 | 
			
		||||
                        "link": "/employees"
 | 
			
		||||
                    }
 | 
			
		||||
                ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "text": "Activities",
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,8 @@ import { setProjectId } from "../slices/localVariablesSlice";
 | 
			
		||||
import EmployeeList from "../components/Directory/EmployeeList";
 | 
			
		||||
 | 
			
		||||
export const useProjects = () => {
 | 
			
		||||
  const loggedUser = useSelector((store) => store.globalVariables.loginUser);
 | 
			
		||||
 | 
			
		||||
  const loggedUser = useSelector( ( store ) => store.globalVariables.loginUser )
 | 
			
		||||
  const [projects, setProjects] = useState([]);
 | 
			
		||||
  const [loading, setLoading] = useState(true);
 | 
			
		||||
  const [error, setError] = useState("");
 | 
			
		||||
@ -128,34 +129,39 @@ export const useProjectDetails = (projectId) => {
 | 
			
		||||
    }
 | 
			
		||||
  }, [projectId, profile]);
 | 
			
		||||
 | 
			
		||||
  return { projects_Details, loading, error, refetch: fetchData };
 | 
			
		||||
};
 | 
			
		||||
  return { projects_Details, loading, error, refetch: fetchData }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const useProjectsByEmployee = (employeeId) => {
 | 
			
		||||
 | 
			
		||||
export const useProjectsByEmployee = ( employeeId ) =>
 | 
			
		||||
{
 | 
			
		||||
  const [projectList, setProjectList] = useState([]);
 | 
			
		||||
  const [loading, setLoading] = useState(false);
 | 
			
		||||
  const [error, setError] = useState("");
 | 
			
		||||
  const [error, setError] = useState('');
 | 
			
		||||
 | 
			
		||||
  const fetchProjects = async (id) => {
 | 
			
		||||
    try {
 | 
			
		||||
      setLoading(true);
 | 
			
		||||
      setError(""); // clear previous error
 | 
			
		||||
      setError(''); // clear previous error
 | 
			
		||||
      const res = await ProjectRepository.getProjectsByEmployee(id);
 | 
			
		||||
      setProjectList(res.data);
 | 
			
		||||
      cacheData("ProjectsByEmployee", { data: res.data, employeeId: id });
 | 
			
		||||
      setLoading(false);
 | 
			
		||||
      cacheData( 'ProjectsByEmployee', {data: res.data, employeeId: id} );
 | 
			
		||||
      setLoading(false)
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      setError(err?.message || "Failed to fetch projects");
 | 
			
		||||
      setLoading(false);
 | 
			
		||||
      setError( err?.message || 'Failed to fetch projects' );
 | 
			
		||||
      setLoading(false)
 | 
			
		||||
    } 
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (!employeeId) return;
 | 
			
		||||
 | 
			
		||||
    const cache_project = getCachedData("ProjectsByEmployee");
 | 
			
		||||
    const cache_project = getCachedData('ProjectsByEmployee');
 | 
			
		||||
 | 
			
		||||
    if (!cache_project?.data || cache_project?.employeeId !== employeeId) {
 | 
			
		||||
    if (
 | 
			
		||||
      !cache_project?.data ||
 | 
			
		||||
      cache_project?.employeeId !== employeeId
 | 
			
		||||
    ) {
 | 
			
		||||
      fetchProjects(employeeId);
 | 
			
		||||
    } else {
 | 
			
		||||
      setProjectList(cache_project.data);
 | 
			
		||||
@ -166,29 +172,7 @@ export const useProjectsByEmployee = (employeeId) => {
 | 
			
		||||
    projectList,
 | 
			
		||||
    loading,
 | 
			
		||||
    error,
 | 
			
		||||
    refetch: fetchProjects,
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const useProjectName = () => {
 | 
			
		||||
  const [loading, setLoading] = useState(true);
 | 
			
		||||
  const [projectNames, setProjectName] = useState([]);
 | 
			
		||||
  const [Error, setError] = useState();
 | 
			
		||||
 | 
			
		||||
  const fetchData = async () => {
 | 
			
		||||
    try {
 | 
			
		||||
      let response = await ProjectRepository.projectNameList();
 | 
			
		||||
      setProjectName(response.data);
 | 
			
		||||
      cacheData("basicProjectNameList", response.data);
 | 
			
		||||
      setLoading(false);
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      setError("Failed to fetch data.");
 | 
			
		||||
      setLoading(false);
 | 
			
		||||
    refetch : fetchProjects
 | 
			
		||||
  }
 | 
			
		||||
  };
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    fetchData();
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  return { projectNames, loading, Error };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -167,7 +167,7 @@ const AttendancePage = () => {
 | 
			
		||||
          ]}
 | 
			
		||||
        ></Breadcrumb>
 | 
			
		||||
        <div className="nav-align-top nav-tabs-shadow">
 | 
			
		||||
          {/* <ul className="nav nav-tabs" role="tablist">
 | 
			
		||||
          <ul className="nav nav-tabs align-items-center" role="tablist">
 | 
			
		||||
            <div
 | 
			
		||||
              className="dataTables_length text-start py-2 px-2 d-flex "
 | 
			
		||||
              id="DataTables_Table_0_length"
 | 
			
		||||
@ -201,7 +201,26 @@ const AttendancePage = () => {
 | 
			
		||||
                </label>
 | 
			
		||||
              )}
 | 
			
		||||
            </div>
 | 
			
		||||
          </ul> */}
 | 
			
		||||
            <li
 | 
			
		||||
              className={`nav-item ms-auto ${
 | 
			
		||||
                activeTab === "regularization" ? "d-none" : ""
 | 
			
		||||
              }`}
 | 
			
		||||
            >
 | 
			
		||||
              <label className="switch switch-primary">
 | 
			
		||||
                <input
 | 
			
		||||
                  type="checkbox"
 | 
			
		||||
                  className="switch-input"
 | 
			
		||||
                  checked={showOnlyCheckout}
 | 
			
		||||
                  onChange={handleToggle}
 | 
			
		||||
                />
 | 
			
		||||
                <span className="switch-toggle-slider">
 | 
			
		||||
                  <span className="switch-on"></span>
 | 
			
		||||
                  <span className="switch-off"></span>
 | 
			
		||||
                </span>
 | 
			
		||||
                <span className="switch-label m-2">Pending Actions</span>
 | 
			
		||||
              </label>
 | 
			
		||||
            </li>
 | 
			
		||||
          </ul>
 | 
			
		||||
 | 
			
		||||
          <ul className="nav nav-tabs" role="tablist">
 | 
			
		||||
            <li className="nav-item">
 | 
			
		||||
@ -240,7 +259,7 @@ const AttendancePage = () => {
 | 
			
		||||
              </button>
 | 
			
		||||
            </li>
 | 
			
		||||
          </ul>
 | 
			
		||||
          <div className="tab-content attedanceTabs py-0 px-1 px-sm-3">
 | 
			
		||||
          <div className="tab-content attedanceTabs py-2 px-1 px-sm-3">
 | 
			
		||||
            {projectLoading && <span>Loading..</span>}
 | 
			
		||||
            {!projectLoading && !attendances && <span>Not Found</span>}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -167,7 +167,7 @@ const DailyTask = () => {
 | 
			
		||||
          closeModal={closeModal}
 | 
			
		||||
          refetch={refetch}
 | 
			
		||||
        />
 | 
			
		||||
       
 | 
			
		||||
        {isModalOpen && <div className="modal-backdrop fade show"></div>}{" "}
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div
 | 
			
		||||
@ -181,7 +181,7 @@ const DailyTask = () => {
 | 
			
		||||
          commentsData={comments}
 | 
			
		||||
          closeModal={closeCommentModal}
 | 
			
		||||
        />
 | 
			
		||||
   
 | 
			
		||||
        {isModalOpenComment && <div className="modal-backdrop fade show"></div>}{" "}
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div className="container-xxl flex-grow-1 container-p-y">
 | 
			
		||||
@ -194,7 +194,7 @@ const DailyTask = () => {
 | 
			
		||||
        <div className="card card-action mb-6 ">
 | 
			
		||||
          <div className="card-body p-1 p-sm-2">
 | 
			
		||||
            <div className="row d-flex justify-content-between align-items-center">
 | 
			
		||||
              <div className="col-md-12 d-flex gap-3 align-items-center col-12 text-start mb-2 mb-md-0">
 | 
			
		||||
              <div className="col-md-6 d-flex gap-3 align-items-center col-12 text-start mb-2 mb-md-0">
 | 
			
		||||
                <DateRangePicker
 | 
			
		||||
                  onRangeChange={setDateRange}
 | 
			
		||||
                  endDateMode="today"
 | 
			
		||||
@ -209,7 +209,7 @@ const DailyTask = () => {
 | 
			
		||||
                  currentSelectedActivities={filters.selectedActivities}
 | 
			
		||||
                />
 | 
			
		||||
              </div>
 | 
			
		||||
              {/* <div className="col-md-4 col-12 text-center mb-2 mb-md-0">
 | 
			
		||||
              <div className="col-md-4 col-12 text-center mb-2 mb-md-0">
 | 
			
		||||
                <select
 | 
			
		||||
                  name="project_select"
 | 
			
		||||
                  aria-controls="DataTables_Table_0"
 | 
			
		||||
@ -232,7 +232,7 @@ const DailyTask = () => {
 | 
			
		||||
                      </option>
 | 
			
		||||
                    ))}
 | 
			
		||||
                </select>
 | 
			
		||||
              </div> */}
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div className="table-responsive text-nowrap mt-3">
 | 
			
		||||
              <table className="table">
 | 
			
		||||
 | 
			
		||||
@ -112,7 +112,7 @@ const TaskPlannng = () => {
 | 
			
		||||
        <Breadcrumb
 | 
			
		||||
          data={[
 | 
			
		||||
            { label: "Home", link: "/dashboard" },
 | 
			
		||||
            { label: "Daily Task Planning", link: "/activities/task" },
 | 
			
		||||
            { label: "Daily Task Planning", link: "/task" },
 | 
			
		||||
          ]}
 | 
			
		||||
        ></Breadcrumb>
 | 
			
		||||
        {project_listLoader && <p>Loading..</p>}
 | 
			
		||||
 | 
			
		||||
@ -22,19 +22,18 @@ import {
 | 
			
		||||
import EmployeeRepository from "../../repositories/EmployeeRepository";
 | 
			
		||||
import ManageEmployee from "../../components/Employee/ManageEmployee";
 | 
			
		||||
import ConfirmModal from "../../components/common/ConfirmModal";
 | 
			
		||||
import { useSelector } from "react-redux";
 | 
			
		||||
import GlobalModel from "../../components/common/GlobalModel";
 | 
			
		||||
import AssignToProject from "./AssignToProject";
 | 
			
		||||
 | 
			
		||||
const EmployeeList = () => {
 | 
			
		||||
  const selectedProjectId = useSelector((store) => store.localVariables.projectId);
 | 
			
		||||
  const [selectedProject, setSelectedProject] = useState(() => selectedProjectId || "");
 | 
			
		||||
  const { profile: loginUser } = useProfile();
 | 
			
		||||
  const [selectedProject, setSelectedProject] = useState("");
 | 
			
		||||
  const { projects, loading: projectLoading } = useProjects();
 | 
			
		||||
  const [showInactive, setShowInactive] = useState(false);
 | 
			
		||||
  const [showAllEmployees, setShowAllEmployees] = useState(false); // New state for "All Employee"
 | 
			
		||||
  const Manage_Employee = useHasUserPermission(MANAGE_EMPLOYEES);
 | 
			
		||||
 | 
			
		||||
  // Modify the hook to conditionally pass selectedProject or null based on showAllEmployees
 | 
			
		||||
  const { employees, loading, setLoading, error, recallEmployeeData } =
 | 
			
		||||
    useEmployeesAllOrByProjectId(showAllEmployees ? null : selectedProject, showInactive);
 | 
			
		||||
    useEmployeesAllOrByProjectId(selectedProject, showInactive);
 | 
			
		||||
  const [projectsList, setProjectsList] = useState(projects || []);
 | 
			
		||||
 | 
			
		||||
  const [employeeList, setEmployeeList] = useState([]);
 | 
			
		||||
@ -50,7 +49,8 @@ const EmployeeList = () => {
 | 
			
		||||
  const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
 | 
			
		||||
  const [selectedEmpFordelete, setSelectedEmpFordelete] = useState(null);
 | 
			
		||||
  const [employeeLodaing, setemployeeLodaing] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const [ selectedEmployee, setSelectEmployee ] = useState( null )
 | 
			
		||||
  const [IsOpenAsssingModal,setOpenAssignModal] = useState(false)
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
 | 
			
		||||
  const handleSearch = (e) => {
 | 
			
		||||
@ -73,9 +73,11 @@ const EmployeeList = () => {
 | 
			
		||||
    if (!loading && Array.isArray(employees)) {
 | 
			
		||||
      // Sort by full name (firstName + lastName)
 | 
			
		||||
      const sorted = [...employees].sort((a, b) => {
 | 
			
		||||
        const nameA = `${a.firstName || ""}${a.middleName || ""}${b.lastName || ""
 | 
			
		||||
        const nameA = `${a.firstName || ""}${a.middleName || ""}${
 | 
			
		||||
          a.lastName || ""
 | 
			
		||||
        }`.toLowerCase();
 | 
			
		||||
        const nameB = `${b.firstName || ""}${b.middleName || ""}${b.lastName || ""
 | 
			
		||||
        const nameB = `${b.firstName || ""}${b.middleName || ""}${
 | 
			
		||||
          b.lastName || ""
 | 
			
		||||
        }`.toLowerCase();
 | 
			
		||||
        return nameA.localeCompare(nameB);
 | 
			
		||||
      });
 | 
			
		||||
@ -83,7 +85,7 @@ const EmployeeList = () => {
 | 
			
		||||
      setEmployeeList(sorted);
 | 
			
		||||
      setFilteredData(sorted);
 | 
			
		||||
    }
 | 
			
		||||
  }, [loading, employees, selectedProject, showAllEmployees]); // Add showAllEmployees to dependencies
 | 
			
		||||
  }, [loading, employees, selectedProject]);
 | 
			
		||||
 | 
			
		||||
  const displayData = searchText ? filteredData : employeeList;
 | 
			
		||||
  const indexOfLastItem = currentPage * itemsPerPage;
 | 
			
		||||
@ -165,7 +167,7 @@ const EmployeeList = () => {
 | 
			
		||||
        exportToExcel(currentItems, "employees");
 | 
			
		||||
        break;
 | 
			
		||||
      case "pdf":
 | 
			
		||||
        exportToPDF(currentItems, "employees");
 | 
			
		||||
        exportToPDF(currentItems, "employees"); // Pass the employeeList directly
 | 
			
		||||
        break;
 | 
			
		||||
      case "print":
 | 
			
		||||
        printTable(tableRef.current);
 | 
			
		||||
@ -180,15 +182,6 @@ const EmployeeList = () => {
 | 
			
		||||
    recallEmployeeData(e.target.checked);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // New handler for "All Employee" toggle
 | 
			
		||||
  const handleAllEmployeesToggle = (e) => {
 | 
			
		||||
    const isChecked = e.target.checked;
 | 
			
		||||
    setShowAllEmployees(isChecked);
 | 
			
		||||
    // If "All Employees" is checked, we don't want to filter by project,
 | 
			
		||||
    // so we pass null for selected project. Otherwise, use the currently selected project.
 | 
			
		||||
    recallEmployeeData(showInactive, isChecked ? null : selectedProject);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleEmployeeModel = (id) => {
 | 
			
		||||
    setSelecedEmployeeId(id);
 | 
			
		||||
    setShowModal(true);
 | 
			
		||||
@ -199,20 +192,11 @@ const EmployeeList = () => {
 | 
			
		||||
    setSelectedEmpFordelete(employee);
 | 
			
		||||
    setIsDeleteModalOpen(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const handleProjectSelection = (e) => {
 | 
			
		||||
    const newProjectId = e.target.value;
 | 
			
		||||
    setSelectedProject(newProjectId);
 | 
			
		||||
    // If a specific project is selected, uncheck "All Employees"
 | 
			
		||||
    if (newProjectId) {
 | 
			
		||||
      setShowAllEmployees(false);
 | 
			
		||||
  const handleCloseAssignModal = () =>
 | 
			
		||||
  {
 | 
			
		||||
    setOpenAssignModal( false )
 | 
			
		||||
    setSelectEmployee(null)
 | 
			
		||||
 }
 | 
			
		||||
  };
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setSelectedProject(selectedProjectId || "");
 | 
			
		||||
  }, [selectedProjectId]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      {isCreateModalOpen && (
 | 
			
		||||
@ -236,7 +220,8 @@ const EmployeeList = () => {
 | 
			
		||||
            />
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>)}
 | 
			
		||||
      </div> )}
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
      {IsDeleteModalOpen && (
 | 
			
		||||
        <div
 | 
			
		||||
@ -263,6 +248,11 @@ const EmployeeList = () => {
 | 
			
		||||
        </div>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      {IsOpenAsssingModal && ( <GlobalModel isOpen={IsOpenAsssingModal} closeModal={()=>setOpenAssignModal(false)}>
 | 
			
		||||
        <AssignToProject employee={selectedEmployee} onClose={() => setOpenAssignModal( false )} />
 | 
			
		||||
      </GlobalModel>)}
 | 
			
		||||
     
 | 
			
		||||
 | 
			
		||||
      <div className="container-xxl flex-grow-1 container-p-y">
 | 
			
		||||
        <Breadcrumb
 | 
			
		||||
          data={[
 | 
			
		||||
@ -273,7 +263,6 @@ const EmployeeList = () => {
 | 
			
		||||
        <div className="row">
 | 
			
		||||
          <div className="card ">
 | 
			
		||||
            <div className="card-datatable table-responsive pt-2">
 | 
			
		||||
 | 
			
		||||
              <div
 | 
			
		||||
                id="DataTables_Table_0_wrapper"
 | 
			
		||||
                className="dataTables_wrapper dt-bootstrap5 no-footer"
 | 
			
		||||
@ -281,19 +270,44 @@ const EmployeeList = () => {
 | 
			
		||||
              >
 | 
			
		||||
                <div className="row  mb-2 ">
 | 
			
		||||
                  <div className="col-md-3 col-sm-6 ">
 | 
			
		||||
                    <div className="form-check text-start">
 | 
			
		||||
                      <input
 | 
			
		||||
                        type="checkbox"
 | 
			
		||||
                        className="form-check-input"
 | 
			
		||||
                        id="allEmployeesCheckbox"
 | 
			
		||||
                        checked={showAllEmployees}
 | 
			
		||||
                        onChange={handleAllEmployeesToggle}
 | 
			
		||||
                      />
 | 
			
		||||
                      <label className="form-check-label" htmlFor="allEmployeesCheckbox">
 | 
			
		||||
                        All Employees
 | 
			
		||||
                    <div className="ms-0">
 | 
			
		||||
                      <div
 | 
			
		||||
                        className="dataTables_length text-start"
 | 
			
		||||
                        id="DataTables_Table_0_length"
 | 
			
		||||
                      >
 | 
			
		||||
                        <label>
 | 
			
		||||
                          <select
 | 
			
		||||
                            id="project-select"
 | 
			
		||||
                            onChange={(e) => setSelectedProject(e.target.value)}
 | 
			
		||||
                            name="DataTables_Table_0_length"
 | 
			
		||||
                            aria-controls="DataTables_Table_0"
 | 
			
		||||
                            className="form-select form-select-sm"
 | 
			
		||||
                            value={selectedProject || ""}
 | 
			
		||||
                          >
 | 
			
		||||
                            {projectLoading ? (
 | 
			
		||||
                              <option value="Loading">Loading...</option>
 | 
			
		||||
                            ) : (
 | 
			
		||||
                              <>
 | 
			
		||||
                                <option value="">All Employees</option>
 | 
			
		||||
                                {Array.isArray(projects) &&
 | 
			
		||||
                                  projects
 | 
			
		||||
                                    .filter((item) =>
 | 
			
		||||
                                      loginUser?.projects?.includes(
 | 
			
		||||
                                        String(item.id)
 | 
			
		||||
                                      )
 | 
			
		||||
                                    )
 | 
			
		||||
                                    .map((item) => (
 | 
			
		||||
                                      <option key={item.id} value={item.id}>
 | 
			
		||||
                                        {item.name}
 | 
			
		||||
                                      </option>
 | 
			
		||||
                                    ))}
 | 
			
		||||
                              </>
 | 
			
		||||
                            )}
 | 
			
		||||
                          </select>
 | 
			
		||||
                        </label>
 | 
			
		||||
                      </div>
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                  <div className="col-md-9 col-sm-6">
 | 
			
		||||
                    <div className="dt-action-buttons text-xl-end text-lg-start text-md-end text-start d-flex align-items-center justify-content-sm-end justify-content-between gap-2 flex-md-row flex-sm-column mb-3 mb-md-0 mt-1 mt-md-0 gap-md-4">
 | 
			
		||||
                      <div
 | 
			
		||||
@ -363,7 +377,8 @@ const EmployeeList = () => {
 | 
			
		||||
                            </li>
 | 
			
		||||
                          </ul>
 | 
			
		||||
                          <button
 | 
			
		||||
                            className={`btn btn-sm add-new btn-primary ${!Manage_Employee && "d-none"
 | 
			
		||||
                            className={`btn btn-sm add-new btn-primary ${
 | 
			
		||||
                              !Manage_Employee && "d-none"
 | 
			
		||||
                            }`}
 | 
			
		||||
                            tabIndex="0"
 | 
			
		||||
                            type="button"
 | 
			
		||||
@ -383,24 +398,23 @@ const EmployeeList = () => {
 | 
			
		||||
                    </div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div className="d-flex justify-content-end mb-2">
 | 
			
		||||
                  {/* Show Inactive Employees Checkbox */}
 | 
			
		||||
                  <div className={`${showAllEmployees ? '' : selectedProject ? 'd-none' : ''}`}>
 | 
			
		||||
                    <div className="form-check">
 | 
			
		||||
                 <div className={`text-end mb-2 ${selectedProject ? 'd-none' : ''}`}>
 | 
			
		||||
                  <label className="switch switch-primary">
 | 
			
		||||
                    <input
 | 
			
		||||
                      type="checkbox"
 | 
			
		||||
                        className="form-check-input"
 | 
			
		||||
                        id="inactiveEmployeesCheckbox"
 | 
			
		||||
                      className="switch-input"
 | 
			
		||||
                      checked={showInactive}
 | 
			
		||||
                      onChange={handleToggle}
 | 
			
		||||
                    />
 | 
			
		||||
                      <label className="form-check-label" htmlFor="inactiveEmployeesCheckbox">
 | 
			
		||||
                    <span className="switch-toggle-slider">
 | 
			
		||||
                      <span className="switch-on"></span>
 | 
			
		||||
                      <span className="switch-off"></span>
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <span className="switch-label">
 | 
			
		||||
                      Show Inactive Employees
 | 
			
		||||
                    </span>
 | 
			
		||||
                  </label>
 | 
			
		||||
                </div>
 | 
			
		||||
                  </div>
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <table
 | 
			
		||||
                  className="datatables-users table border-top dataTable no-footer dtr-column text-nowrap"
 | 
			
		||||
                  id="DataTables_Table_0"
 | 
			
		||||
@ -476,7 +490,8 @@ const EmployeeList = () => {
 | 
			
		||||
                        Status
 | 
			
		||||
                      </th>
 | 
			
		||||
                      <th
 | 
			
		||||
                        className={`sorting_disabled  ${!Manage_Employee && "d-none"
 | 
			
		||||
                        className={`sorting_disabled  ${
 | 
			
		||||
                          !Manage_Employee && "d-none"
 | 
			
		||||
                        }`}
 | 
			
		||||
                        rowSpan="1"
 | 
			
		||||
                        colSpan="1"
 | 
			
		||||
@ -640,6 +655,19 @@ const EmployeeList = () => {
 | 
			
		||||
                                        <i className="bx bx-cog bx-sm"></i>{" "}
 | 
			
		||||
                                        Manage Role
 | 
			
		||||
                                      </button>
 | 
			
		||||
                                      <button
 | 
			
		||||
                                        className="dropdown-item py-1"
 | 
			
		||||
                                     
 | 
			
		||||
                                        onClick={() =>
 | 
			
		||||
                                        {
 | 
			
		||||
                                          setSelectEmployee( item ),
 | 
			
		||||
                                          setOpenAssignModal(true)
 | 
			
		||||
                                        }
 | 
			
		||||
                                        }
 | 
			
		||||
                                      >
 | 
			
		||||
                                        <i className='bx bx-select-multiple'></i>{" "}
 | 
			
		||||
                                       Assign Project
 | 
			
		||||
                                      </button>
 | 
			
		||||
                                    </>
 | 
			
		||||
                                  )}
 | 
			
		||||
                                </div>
 | 
			
		||||
@ -654,11 +682,13 @@ const EmployeeList = () => {
 | 
			
		||||
                <div style={{ width: "1%" }}></div>
 | 
			
		||||
 | 
			
		||||
                {/* Pagination */}
 | 
			
		||||
                {!loading && displayData.length > itemsPerPage && (
 | 
			
		||||
                  <nav aria-label="Page">
 | 
			
		||||
                {!loading && (
 | 
			
		||||
                  <nav aria-label="Page ">
 | 
			
		||||
                    <ul className="pagination pagination-sm justify-content-end py-1">
 | 
			
		||||
                      <li
 | 
			
		||||
                        className={`page-item ${currentPage === 1 ? "disabled" : ""}`}
 | 
			
		||||
                        className={`page-item  ${
 | 
			
		||||
                          currentPage === 1 ? "disabled" : ""
 | 
			
		||||
                        }`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <button
 | 
			
		||||
                          className="page-link btn-xs"
 | 
			
		||||
@ -667,28 +697,28 @@ const EmployeeList = () => {
 | 
			
		||||
                          «
 | 
			
		||||
                        </button>
 | 
			
		||||
                      </li>
 | 
			
		||||
 | 
			
		||||
                      {[...Array(totalPages)]?.map((_, index) => (
 | 
			
		||||
                        <li
 | 
			
		||||
                          key={index}
 | 
			
		||||
                          className={`page-item ${currentPage === index + 1 ? "active" : ""
 | 
			
		||||
                          className={`page-item ${
 | 
			
		||||
                            currentPage === index + 1 ? "active" : ""
 | 
			
		||||
                          }`}
 | 
			
		||||
                        >
 | 
			
		||||
                          <button
 | 
			
		||||
                            className="page-link"
 | 
			
		||||
                            className="page-link "
 | 
			
		||||
                            onClick={() => paginate(index + 1)}
 | 
			
		||||
                          >
 | 
			
		||||
                            {index + 1}
 | 
			
		||||
                          </button>
 | 
			
		||||
                        </li>
 | 
			
		||||
                      ))}
 | 
			
		||||
 | 
			
		||||
                      <li
 | 
			
		||||
                        className={`page-item ${currentPage === totalPages ? "disabled" : ""
 | 
			
		||||
                        className={`page-item ${
 | 
			
		||||
                          currentPage === totalPages ? "disabled" : ""
 | 
			
		||||
                        }`}
 | 
			
		||||
                      >
 | 
			
		||||
                        <button
 | 
			
		||||
                          className="page-link"
 | 
			
		||||
                          className="page-link "
 | 
			
		||||
                          onClick={() => paginate(currentPage + 1)}
 | 
			
		||||
                        >
 | 
			
		||||
                          »
 | 
			
		||||
@ -697,7 +727,6 @@ const EmployeeList = () => {
 | 
			
		||||
                    </ul>
 | 
			
		||||
                  </nav>
 | 
			
		||||
                )}
 | 
			
		||||
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
 | 
			
		||||
@ -22,8 +22,7 @@ const ProjectRepository = {
 | 
			
		||||
  updateProject: (id, data) => api.put(`/api/project/update/${id}`, data),
 | 
			
		||||
  deleteProject: ( id ) => api.delete( `/projects/${ id }` ),
 | 
			
		||||
  getProjectsByEmployee: ( id ) => api.get( `/api/project/assigned-projects/${ id }` ),
 | 
			
		||||
  updateProjectsByEmployee:(id,data)=>api.post(`/api/project/assign-projects/${id}`,data),
 | 
			
		||||
  projectNameList:()=>api.get("/api/project/list/basic")
 | 
			
		||||
  updateProjectsByEmployee:(id,data)=>api.post(`/api/project/assign-projects/${id}`,data)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const TasksRepository = {
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,7 @@ const localVariablesSlice = createSlice({
 | 
			
		||||
  initialState: {
 | 
			
		||||
    selectedMaster:"Application Role",
 | 
			
		||||
    regularizationCount:0,
 | 
			
		||||
    projectId: "",
 | 
			
		||||
    projectId: 1,
 | 
			
		||||
    reload:false
 | 
			
		||||
  
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
@ -39,67 +39,103 @@ axiosClient.interceptors.response.use(
 | 
			
		||||
  async (error) => {
 | 
			
		||||
    const originalRequest = error.config;
 | 
			
		||||
 | 
			
		||||
    // Prevent infinite loop
 | 
			
		||||
    if (!originalRequest || originalRequest._retry) {
 | 
			
		||||
    if (!originalRequest) {
 | 
			
		||||
      return Promise.reject(error);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Only show one toast per request
 | 
			
		||||
    // Prevent multiple toasts for the same request
 | 
			
		||||
    if (!originalRequest._toastShown) {
 | 
			
		||||
      originalRequest._toastShown = true;
 | 
			
		||||
 | 
			
		||||
      if (error.code === "ERR_CONNECTION_REFUSED") {
 | 
			
		||||
        showToast("Unable to connect to the server. Please try again later.", "error");
 | 
			
		||||
        console.error(
 | 
			
		||||
          "Connection refused. Please ensure the server is running."
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        if (error.config.url.indexOf("refresh-token") != -1) {
 | 
			
		||||
          //showToast("Server is unreachable. Try again later!", "error");
 | 
			
		||||
        } else {
 | 
			
		||||
          showToast(
 | 
			
		||||
            "Unable to connect to the server. Please try again later.",
 | 
			
		||||
            "error"
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      } else if (error.code === "ERR_NETWORK") {
 | 
			
		||||
        showToast("Network error. Please check your connection.", "error");
 | 
			
		||||
        console.error("Network error: Unable to reach the server.");
 | 
			
		||||
        if (error.config.url.indexOf("refresh-token") != -1) {
 | 
			
		||||
          //showToast("Server is unreachable. Try again later!", "error");
 | 
			
		||||
        }
 | 
			
		||||
        redirectToLogin();
 | 
			
		||||
      } else if (error.code === "ECONNABORTED") {
 | 
			
		||||
        showToast("Request timed out. Please try again.", "error");
 | 
			
		||||
        console.error("Request timed out.");
 | 
			
		||||
 | 
			
		||||
        if (error.config.url.indexOf("refresh-token") != -1) {
 | 
			
		||||
          //showToast("Server is unreachable. Try again later!", "error");
 | 
			
		||||
        } else {
 | 
			
		||||
          showToast(
 | 
			
		||||
            "The request took too long. Please try again later.",
 | 
			
		||||
            "error"
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      } else if (error.response) {
 | 
			
		||||
        const status = error.response.status;
 | 
			
		||||
        const isRefreshRequest = error.config.url.includes("refresh-token");
 | 
			
		||||
 | 
			
		||||
        if (status === 401 && !isRefreshRequest) {
 | 
			
		||||
          // Mark as retried to avoid loops
 | 
			
		||||
        if (error.config.url.indexOf("refresh-token") != -1) {
 | 
			
		||||
          //showToast("Server is unreachable. Try again later!", "error");
 | 
			
		||||
        }
 | 
			
		||||
        // else {
 | 
			
		||||
        //   // showToast(error.response.data.message, "error");  // repeted toast
 | 
			
		||||
        // }
 | 
			
		||||
        if (error.response.status === 401) {
 | 
			
		||||
          originalRequest._retry = true;
 | 
			
		||||
 | 
			
		||||
          try {
 | 
			
		||||
            // Get the refresh token from secure storage
 | 
			
		||||
            const refreshToken = localStorage.getItem("refreshToken");
 | 
			
		||||
 | 
			
		||||
          if (!refreshToken || error.response.data?.errors === "Invalid or expired refresh token.") {
 | 
			
		||||
            if (!refreshToken || error.response.data.errors === "Invalid or expired refresh token.") {
 | 
			
		||||
              // Redirect to login if refresh token is not available
 | 
			
		||||
              redirectToLogin();
 | 
			
		||||
              return Promise.reject(error);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            stopSignalR();
 | 
			
		||||
 | 
			
		||||
          try {
 | 
			
		||||
            // Refresh token
 | 
			
		||||
            const res = await axiosClient.post("/api/Auth/refresh-token", {
 | 
			
		||||
            // Make a request to refresh the access token
 | 
			
		||||
            const response = await axiosClient
 | 
			
		||||
              .post("/api/Auth/refresh-token", {
 | 
			
		||||
                token: localStorage.getItem("jwtToken"),
 | 
			
		||||
                refreshToken,
 | 
			
		||||
              })
 | 
			
		||||
              .catch((error) => {
 | 
			
		||||
                // console.log("7 - error fetching refresh token :", error);
 | 
			
		||||
              });
 | 
			
		||||
 | 
			
		||||
            const { token, refreshToken: newRefreshToken } = res.data.data;
 | 
			
		||||
 | 
			
		||||
            // Save new tokens
 | 
			
		||||
            const { token, refreshToken: newRefreshToken } = response.data.data;
 | 
			
		||||
            localStorage.setItem("jwtToken", token);
 | 
			
		||||
            localStorage.setItem("refreshToken", newRefreshToken); 
 | 
			
		||||
 | 
			
		||||
            startSignalR()
 | 
			
		||||
            // Set Authorization header
 | 
			
		||||
            // Retry the original request with the new token
 | 
			
		||||
            originalRequest.headers["Authorization"] = `Bearer ${token}`;
 | 
			
		||||
 | 
			
		||||
            // Optional: Instead of retrying, you may choose to reload app or go to home
 | 
			
		||||
            return axiosClient(originalRequest); // <== only retry once
 | 
			
		||||
          } catch (refreshError) {
 | 
			
		||||
            // Retry the original request
 | 
			
		||||
            return axiosClient(originalRequest);
 | 
			
		||||
          } catch (err) {
 | 
			
		||||
            // Redirect to login if token refresh fails
 | 
			
		||||
            redirectToLogin();
 | 
			
		||||
            return Promise.reject(refreshError);
 | 
			
		||||
          }
 | 
			
		||||
            return Promise.reject(err);
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
        showToast("An unknown error occurred.", "error");
 | 
			
		||||
          if (error.config.url.indexOf("refresh-token") != -1) {
 | 
			
		||||
            //showToast("Server is unreachable. Try again later!", "error");
 | 
			
		||||
            // console.log("6 - error fetching refresh token :", error);
 | 
			
		||||
          }
 | 
			
		||||
          // showToast(
 | 
			
		||||
          //   error.response.data?.message ||
 | 
			
		||||
          //     "An error occurred. Please try again.",
 | 
			
		||||
          //   "error"
 | 
			
		||||
          // );
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        console.error("An unknown error occurred:", error.message);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return Promise.reject(error);
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user