Merge branch 'Feature_Task_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Feature_Task_Management

This commit is contained in:
Pramod Mahajan 2025-04-08 13:03:12 +05:30
commit df989605b9
32 changed files with 1594 additions and 1126 deletions

View File

@ -46,7 +46,7 @@
<!-- Helpers -->
<script src="/assets/vendor/js/helpers.js"></script>
<script src="/assets/js/config.js"></script>
<!-- Timer Picker -->
<!-- Flatpickr CSS -->
<link rel="stylesheet" href="/assets/vendor/libs/flatpickr/flatpickr.css" />
@ -71,12 +71,12 @@
<script src="/assets/vendor/libs/select2/select2.js"></script>
<script src="/assets/vendor/js/menu.js"></script>
<!-- endbuild -->
<!-- Vendors JS -->
@ -84,8 +84,8 @@
<script src="/assets/vendor/libs/bootstrap-select/bootstrap-select.js"></script>
<script src="/assets/vendor/libs/select2/select2.js"></script>
<script src="/assets/vendor/libs/apex-charts/apexcharts.js"></script>
<script src="/assets/vendor/libs/jquery-timepicker/jquery-timepicker.js" ></script>
<script src="/assets/vendor/libs/flatpickr/flatpickr.js" ></script>
<!-- <script src="/assets/vendor/libs/jquery-timepicker/jquery-timepicker.js" ></script> -->
<script src="/assets/vendor/libs/flatpickr/flatpickr.js"></script>
<!-- Main JS -->
<script src="/assets/js/main.js"></script>
@ -94,15 +94,15 @@
<script src="/assets/js/dashboards-analytics.js"></script>
<!-- component -->
<script src="./public/js/timppick.js"></script>
<!-- <script src="./public/js/timppick.js"></script> -->
<script src="/assets/vendor/libs/sweetalert2/sweetalert2.js" ></script>
<script src="/assets/vendor/libs/sweetalert2/sweetalert2.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.min.js"></script> -->
<!-- Flatpickr JS -->
<!-- Flatpickr JS -->
</body>

View File

@ -35469,7 +35469,7 @@ html:not([dir="rtl"]) .menu-toggle::after {
}
.menu-vertical .menu-item .menu-link {
font-size: 0.8375rem;
font-size: 0.7375rem;
min-height: 1.825rem;
}

View File

@ -11,7 +11,7 @@ import {checkIfCurrentDate} from "../../utils/dateUtils";
const schema = z.object({
time: z.string().nonempty({message:"Time is required"}),
markTime: z.string().nonempty({message:"Time is required"}),
description:z.string().optional()
});
@ -71,10 +71,10 @@ const CheckCheckOutmodel = ({modeldata,closeModal,handleSubmitForm,}) => {
<TimePicker
label="Choose a time"
onChange={(e) => setValue("time", e)}
onChange={(e) => setValue("markTime", e)}
interval={10}
/>
{errors.time && <p className="text-danger">{errors.time.message}</p>}
{errors. markTime && <p className="text-danger">{errors.markTime.message}</p>}
</div>

View File

@ -6,9 +6,8 @@ import { z } from "zod";
import showToast from "../../services/toastService";
import { TasksRepository } from "../../repositories/TaskRepository";
export const ReportTask = ({ report,closeModal,refetch }) => {
const [ loading, setloading ] = useState( false );
export const ReportTask = ({ report, closeModal, refetch }) => {
const [loading, setloading] = useState(false);
const schema = z.object({
completedTask: z
@ -32,149 +31,156 @@ export const ReportTask = ({ report,closeModal,refetch }) => {
});
const onSubmit = async (data) => {
try
{
setloading(true)
const reportData = {
...data,
try {
setloading(true);
const reportData = {
...data,
id: report?.id,
reportedDate: new Date().toISOString(),
};
let response = await TasksRepository.reportTsak( reportData )
showToast( "succesfully", "success" )
refetch()
closeModal()
};
let response = await TasksRepository.reportTsak(reportData);
showToast("succesfully", "success");
refetch();
closeModal();
} catch (error) {
showToast("Somthing wrog", "error");
}
};
const handleClose = () => {
closeModal();
closeModal();
};
return (
<div
className="modal-dialog modal-md modal-simple report-task-modal"
role="document"
>
<div className="modal-content">
<div className="modal-body px-1">
<button
type="button"
className="btn-close"
onClick={handleClose}
aria-label="Close"
></button>
<div className="modal-dialog modal-md modal-simple report-task-modal" role="document">
<div className="modal-content">
<div className="modal-body px-1">
<button
type="button"
className="btn-close"
onClick={handleClose}
aria-label="Close"
></button>
<div className="container m-0">
<div className="d-flex justify-content-between">
<figure class="text-start p-0 m-0">
<blockquote class="blockquote">
<small> Assigned Date : {formatDate(report?.assignmentDate)}</small>
<div className="container m-0">
<div className="d-flex justify-content-between">
<figure className="text-start p-0 m-0">
<blockquote className="blockquote">
<small>
{" "}
Assigned Date : {formatDate(report?.assignmentDate)}
</small>
</blockquote>
</figure>
<figure class="text-end p-0 m-0">
<blockquote class="blockquote">
<small>Assigned By</small>
</blockquote>
<figcaption className="blockquote-footer mb-0">
{/* <div className="d-flex avatar avatar-xs">
</figure>
<figure className="text-end p-0 m-0">
<blockquote className="blockquote">
<small>Assigned By</small>
</blockquote>
<figcaption className="blockquote-footer mb-0">
{/* <div className="d-flex avatar avatar-xs">
<span className="avatar-initial rounded-circle bg-label-primary">
{report?.assignedBy?.firstName.slice(0, 1)}
</span> */}
<cite title="Source Title m-0">{` ${report?.assignedBy.firstName} ${report?.assignedBy.lastName}`}</cite>
{/* </div> */}
</figcaption>
</figure>
</div>
<cite title="Source Title m-0">{` ${report?.assignedBy.firstName} ${report?.assignedBy.lastName}`}</cite>
{/* </div> */}
</figcaption>
</figure>
</div>
<div className="d-flex p-0 m-0">
<div className="flex-shrink-0 mt-1 mx-sm-0 px-2 mx-auto">
<i class="bx bx-buildings"></i>
</div>
<p class="lead">{report?.workItem?.workArea?.floor?.building?.name}</p>
<p class="lead ms-12">{report?.workItem?.workArea?.floor?.floorName}</p>
</div>
<dl class="row text-start ms-3">
<dt class="col-sm-6">
Work Area : {report?.workItem?.workArea?.areaName}
</dt>
<dd class="col-sm-6">
<small> {report?.workItem?.activityMaster.activityName}</small>
</dd>
</dl>
<dl class="row text-start ms-3">
<dt class="col-sm-4">Team</dt>
<dd class="col-sm-4 d-flex align-items-center avatar-group justify-content-start">
{report?.teamMembers.map((member) => (
<>
<div
data-bs-toggle="tooltip"
data-bs-html="true"
data-popup="tooltip-custom"
data-bs-placement="top"
title={`${member.firstName} ${member.lastName}`}
className="avatar avatar-xs"
>
{/* <img src="..." alt="Avatar" class="rounded-circle pull-up" /> */}
<span className="avatar-initial rounded-circle bg-label-primary">
{member?.firstName.slice(0, 1)}
</span>
<div className="d-flex p-0 m-0">
<div className="flex-shrink-0 mt-1 mx-sm-0 px-2 mx-auto">
<i className="bx bx-buildings"></i>
</div>
</>
))}
</dd>
<dt class="col-sm-4 text-start">Planned : {report?.plannedTask}</dt>
</dl>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row p-0">
<div className="col-4">
<input
{...register("completedTask", { valueAsNumber: true })}
id="smallInput"
className="form-control form-control-sm"
type="number"
placeholder="Completed Work"
/>
{errors.completedTask && (
<div className="text-danger">{errors.completedTask.message}</div>
)}
</div>
<div className="col-8">
<textarea
{...register("comment")}
className="form-control"
id="exampleFormControlTextarea1"
rows="1"
placeholder="Enter comment"
/>
{errors.comment && (
<div className="text-danger">{errors.comment.message}</div>
)}
</div>
</div>
<p className="lead">
{report?.workItem?.workArea?.floor?.building?.name}
</p>
<p className="lead ms-12">
{report?.workItem?.workArea?.floor?.floorName}
</p>
</div>
<dl className="row text-start ms-3">
<dt className="col-sm-6">
Work Area : {report?.workItem?.workArea?.areaName}
</dt>
<dd className="col-sm-6">
<small> {report?.workItem?.activityMaster.activityName}</small>
</dd>
</dl>
<dl className="row text-start ms-3">
<dt className="col-sm-4">Team</dt>
<div className="col-12 text-center my-2">
<button type="submit" className="btn btn-sm btn-primary me-3">
{loading ? "Please wait":"Submit Report"}
</button>
<button
type="button"
className="btn btn-sm btn-label-secondary"
onClick={handleClose}
>
Cancel
</button>
</div>
</form>
<dd className="col-sm-4 d-flex align-items-center avatar-group justify-content-start">
{report?.teamMembers.map((member) => (
<>
<div
data-bs-toggle="tooltip"
data-bs-html="true"
data-popup="tooltip-custom"
data-bs-placement="top"
title={`${member.firstName} ${member.lastName}`}
className="avatar avatar-xs"
>
{/* <img src="..." alt="Avatar" className="rounded-circle pull-up" /> */}
<span className="avatar-initial rounded-circle bg-label-primary">
{member?.firstName.slice(0, 1)}
</span>
</div>
</>
))}
</dd>
<dt className="col-sm-4 text-start">
Planned : {report?.plannedTask}
</dt>
</dl>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row p-0">
<div className="col-4">
<input
{...register("completedTask", { valueAsNumber: true })}
id="smallInput"
className="form-control form-control-sm"
type="number"
placeholder="Completed Work"
/>
{errors.completedTask && (
<div className="text-danger">
{errors.completedTask.message}
</div>
)}
</div>
<div className="col-8">
<textarea
{...register("comment")}
className="form-control"
id="exampleFormControlTextarea1"
rows="1"
placeholder="Enter comment"
/>
{errors.comment && (
<div className="text-danger">{errors.comment.message}</div>
)}
</div>
</div>
<div className="col-12 text-center my-2">
<button type="submit" className="btn btn-sm btn-primary me-3">
{loading ? "Please wait" : "Submit Report"}
</button>
<button
type="button"
className="btn btn-sm btn-label-secondary"
onClick={handleClose}
>
Cancel
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
);
};

View File

@ -1,62 +1,54 @@
import React, {useEffect, useState} from "react";
import {useProfile} from "../../hooks/useProfile";
import React, { useEffect, useState } from "react";
import { useProfile } from "../../hooks/useProfile";
import moment from "moment";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import {TasksRepository} from "../../repositories/TaskRepository";
import { TasksRepository } from "../../repositories/TaskRepository";
import showToast from "../../services/toastService";
const schema = z.object({
comment: z.string().min(1, "Comment cannot be empty"),
comment: z.string().min(1, "Comment cannot be empty"),
});
const ReportTaskComments = ({ commentsData, closeModal }) => {
const [loading, setloading] = useState(false);
const { profile } = useProfile();
const [comments, setComment] = useState([]);
const {
register,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: zodResolver(schema),
});
const ReportTaskComments = ( {commentsData, closeModal} ) =>
{
const [loading,setloading]=useState(false)
const {profile} = useProfile()
const [comments,setComment] = useState([])
const {
register,
handleSubmit,
formState: { errors },reset
} = useForm({
resolver: zodResolver(schema),
});
useEffect(() => {
setComment(commentsData?.comments);
}, [commentsData]);
const isLoggedUser = (usrId) => profile?.employeeInfo.id;
useEffect( () =>
{
setComment(commentsData?.comments
)
}, [commentsData] )
const isLoggedUser = ( usrId ) => profile?.employeeInfo.id;
const onSubmit = async(data) =>
{
const onSubmit = async (data) => {
let sendComment = {
...data,
taskAllocationId: commentsData?.id,
commentDate: new Date().toISOString(),
}
};
try
{
setloading(true)
try {
setloading(true);
// const resp = await TasksRepository.taskComments( sendComment );
// console.timeLog( resp )
reset()
setloading(false)
showToast( "Successfully Sent", "success" )
closeModal()
} catch ( err )
{
setloading(false)
showToast(error.response.data?.message || "Something wrong","error")
}
}
reset();
setloading(false);
showToast("Successfully Sent", "success");
closeModal();
} catch (err) {
setloading(false);
showToast(error.response.data?.message || "Something wrong", "error");
}
};
return (
<div
className="modal-dialog modal-md modal-simple report-task-comments-modal"
@ -71,39 +63,49 @@ const ReportTaskComments = ( {commentsData, closeModal} ) =>
aria-label="Close"
></button>
<div className="container ">
{
comments && comments.map( ( data ) =>
(
<div className="text-start" key={data.id}>
<div class={`li-wrapper d-flex justify-content-${isLoggedUser(data?.employee?.id) ? "end":"start"} align-items-start`}>
<div class="avatar avatar-xs me-1">
<span class="avatar-initial rounded-circle bg-label-success">
M
</span>
</div>
<div class="text-start py-0">
<p class="mb-0">
<strong>{ `${data?.employee?.firstName} ${data?.employee?.lastName}`}</strong>
</p>
<small style={{fontSize: "10px"}}>{ moment(data?.commentDate).fromNow()}</small>
</div>
</div>
<p className={`ms-${ isLoggedUser( data?.employee?.id ) ? "0 text-end me-6" : "6 " } mt-1`}>{ data?.comment
}</p>
{comments &&
comments.map((data) => (
<div className="text-start" key={data.id}>
<div
className={`li-wrapper d-flex justify-content-${
isLoggedUser(data?.employee?.id) ? "end" : "start"
} align-items-start`}
>
<div className="avatar avatar-xs me-1">
<span className="avatar-initial rounded-circle bg-label-success">
M
</span>
</div>
<div className="text-start py-0">
<p className="mb-0">
<strong>{`${data?.employee?.firstName} ${data?.employee?.lastName}`}</strong>
</p>
<small style={{ fontSize: "10px" }}>
{moment(data?.commentDate).fromNow()}
</small>
</div>
</div>
<p
className={`ms-${
isLoggedUser(data?.employee?.id)
? "0 text-end me-6"
: "6 "
} mt-1`}
>
{data?.comment}
</p>
</div>
))
}
))}
{/* by other users */}
{/* <div className="text-start">
<div class="li-wrapper d-flex justify-content-start align-items-start">
<div class="avatar avatar-xs me-1">
<span class="avatar-initial rounded-circle bg-label-success">
<div className="li-wrapper d-flex justify-content-start align-items-start">
<div className="avatar avatar-xs me-1">
<span className="avatar-initial rounded-circle bg-label-success">
M
</span>
</div>
<div class="text-start py-0">
<p class="mb-0">
<div className="text-start py-0">
<p className="mb-0">
<strong>Mahajan</strong>
</p>
<small style={{ fontSize: "10px" }}>2 hour ago</small>
@ -114,14 +116,14 @@ const ReportTaskComments = ( {commentsData, closeModal} ) =>
{/* by login usrer */}
{/* <div className="text-start">
<div class="li-wrapper d-flex justify-content-end align-items-start">
<div class="avatar avatar-xs me-1">
<span class="avatar-initial rounded-circle bg-label-success">
<div className="li-wrapper d-flex justify-content-end align-items-start">
<div className="avatar avatar-xs me-1">
<span className="avatar-initial rounded-circle bg-label-success">
M
</span>
</div>
<div class="text-start py-0">
<p class="mb-0">
<div className"text-start py-0">
<p className="mb-0">
<strong>Pramod Mahajan</strong>
</p>
<small style={{ fontSize: "10px" }}>2 hour ago</small>
@ -129,25 +131,31 @@ const ReportTaskComments = ( {commentsData, closeModal} ) =>
</div>
<p className="ms-6 mt-1">Stylized implementation of HTMLs element for abbreviations and acronyms to show the expanded version on hover. Abbreviations have a default underline and gain a help cursor to provide additional context on hov </p>
</div> */}
<form onSubmit={handleSubmit( onSubmit )}>
<textarea
{...register("comment")}
className="form-control"
id="exampleFormControlTextarea1"
rows="1"
placeholder="Enter comment"
/>
{errors.comment && (
<div className="danger-text">{errors.comment.message}</div>
<form onSubmit={handleSubmit(onSubmit)}>
<textarea
{...register("comment")}
className="form-control"
id="exampleFormControlTextarea1"
rows="1"
placeholder="Enter comment"
/>
{errors.comment && (
<div className="danger-text">{errors.comment.message}</div>
)}
<div class="text-end my-1">
<button type="button" class="btn btn-sm btn-secondary" onClick={closeModal} data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-sm btn-primary ms-2">{ loading ? "Sending...":"Comment"}</button>
</div>
<div className="text-end my-1">
<button
type="button"
className="btn btn-sm btn-secondary"
onClick={closeModal}
data-bs-dismiss="modal"
>
Close
</button>
<button type="submit" className="btn btn-sm btn-primary ms-2">
{loading ? "Sending..." : "Comment"}
</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -1,135 +1,172 @@
import React from 'react'
import React from "react";
const DemoTable = () => {
return (
<div class="content-wrapper">
<div className="content-wrapper">
<div className="container-xxl flex-grow-1 container-p-y">
<div className="card">
<div className="card-datatable table-responsive">
<table className="datatables-basic table border-top">
<thead>
<tr>
<th></th>
<th></th>
<th>id</th>
<th>Name</th>
<th>Email</th>
<th>Date</th>
<th>Salary</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
</table>
</div>
</div>
<div className="offcanvas offcanvas-end" id="add-new-record">
<div className="offcanvas-header border-bottom">
<h5 className="offcanvas-title" id="exampleModalLabel">
New Record
</h5>
<button
type="button"
className="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Close"
></button>
</div>
<div className="offcanvas-body flex-grow-1">
<form
className="add-new-record pt-0 row g-2"
id="form-add-new-record"
onsubmit="return false"
>
<div className="col-sm-12">
<label className="form-label" for="basicFullname">
Full Name
</label>
<div className="input-group input-group-merge">
<span id="basicFullname2" className="input-group-text">
<i className="bx bx-user"></i>
</span>
<input
type="text"
id="basicFullname"
className="form-control dt-full-name"
name="basicFullname"
placeholder="John Doe"
aria-label="John Doe"
aria-describedby="basicFullname2"
/>
</div>
</div>
<div className="col-sm-12">
<label className="form-label" for="basicPost">
Post
</label>
<div className="input-group input-group-merge">
<span id="basicPost2" className="input-group-text">
<i className="bx bxs-briefcase"></i>
</span>
<input
type="text"
id="basicPost"
name="basicPost"
className="form-control dt-post"
placeholder="Web Developer"
aria-label="Web Developer"
aria-describedby="basicPost2"
/>
</div>
</div>
<div className="col-sm-12">
<label className="form-label" for="basicEmail">
Email
</label>
<div className="input-group input-group-merge">
<span className="input-group-text">
<i className="bx bx-envelope"></i>
</span>
<input
type="text"
id="basicEmail"
name="basicEmail"
className="form-control dt-email"
placeholder="john.doe@example.com"
aria-label="john.doe@example.com"
/>
</div>
<div className="form-text">
You can use letters, numbers & periods
</div>
</div>
<div className="col-sm-12">
<label className="form-label" for="basicDate">
Joining Date
</label>
<div className="input-group input-group-merge">
<span id="basicDate2" className="input-group-text">
<i className="bx bx-calendar"></i>
</span>
<input
type="text"
className="form-control dt-date"
id="basicDate"
name="basicDate"
aria-describedby="basicDate2"
placeholder="MM/DD/YYYY"
aria-label="MM/DD/YYYY"
/>
</div>
</div>
<div className="col-sm-12">
<label className="form-label" for="basicSalary">
Salary
</label>
<div className="input-group input-group-merge">
<span id="basicSalary2" className="input-group-text">
<i className="bx bx-dollar"></i>
</span>
<input
type="number"
id="basicSalary"
name="basicSalary"
className="form-control dt-salary"
placeholder="12000"
aria-label="12000"
aria-describedby="basicSalary2"
/>
</div>
</div>
<div className="col-sm-12">
<button
type="submit"
className="btn btn-primary data-submit me-sm-4 me-1"
>
Submit
</button>
<button
type="reset"
className="btn btn-outline-secondary"
data-bs-dismiss="offcanvas"
>
Cancel
</button>
</div>
</form>
</div>
</div>
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<div class="card-datatable table-responsive">
<table class="datatables-basic table border-top">
<thead>
<tr>
<th></th>
<th></th>
<th>id</th>
<th>Name</th>
<th>Email</th>
<th>Date</th>
<th>Salary</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
</table>
</div>
</div>
<div class="offcanvas offcanvas-end" id="add-new-record">
<div class="offcanvas-header border-bottom">
<h5 class="offcanvas-title" id="exampleModalLabel">New Record</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Close"></button>
</div>
<div class="offcanvas-body flex-grow-1">
<form class="add-new-record pt-0 row g-2" id="form-add-new-record" onsubmit="return false">
<div class="col-sm-12">
<label class="form-label" for="basicFullname">Full Name</label>
<div class="input-group input-group-merge">
<span id="basicFullname2" class="input-group-text"><i class="bx bx-user"></i></span>
<input
type="text"
id="basicFullname"
class="form-control dt-full-name"
name="basicFullname"
placeholder="John Doe"
aria-label="John Doe"
aria-describedby="basicFullname2" />
</div>
</div>
<div class="col-sm-12">
<label class="form-label" for="basicPost">Post</label>
<div class="input-group input-group-merge">
<span id="basicPost2" class="input-group-text"><i class="bx bxs-briefcase"></i></span>
<input
type="text"
id="basicPost"
name="basicPost"
class="form-control dt-post"
placeholder="Web Developer"
aria-label="Web Developer"
aria-describedby="basicPost2" />
</div>
</div>
<div class="col-sm-12">
<label class="form-label" for="basicEmail">Email</label>
<div class="input-group input-group-merge">
<span class="input-group-text"><i class="bx bx-envelope"></i></span>
<input
type="text"
id="basicEmail"
name="basicEmail"
class="form-control dt-email"
placeholder="john.doe@example.com"
aria-label="john.doe@example.com" />
</div>
<div class="form-text">You can use letters, numbers & periods</div>
</div>
<div class="col-sm-12">
<label class="form-label" for="basicDate">Joining Date</label>
<div class="input-group input-group-merge">
<span id="basicDate2" class="input-group-text"><i class="bx bx-calendar"></i></span>
<input
type="text"
class="form-control dt-date"
id="basicDate"
name="basicDate"
aria-describedby="basicDate2"
placeholder="MM/DD/YYYY"
aria-label="MM/DD/YYYY" />
</div>
</div>
<div class="col-sm-12">
<label class="form-label" for="basicSalary">Salary</label>
<div class="input-group input-group-merge">
<span id="basicSalary2" class="input-group-text"><i class="bx bx-dollar"></i></span>
<input
type="number"
id="basicSalary"
name="basicSalary"
class="form-control dt-salary"
placeholder="12000"
aria-label="12000"
aria-describedby="basicSalary2" />
</div>
</div>
<div class="col-sm-12">
<button type="submit" class="btn btn-primary data-submit me-sm-4 me-1">Submit</button>
<button type="reset" class="btn btn-outline-secondary" data-bs-dismiss="offcanvas">Cancel</button>
</div>
</form>
</div>
<hr className="my-12" />
<hr className="my-12" />
<hr className="my-12" />
</div>
<hr class="my-12" />
<div className="content-backdrop fade"></div>
</div>
);
};
<hr class="my-12" />
<hr class="my-12" />
</div>
<div class="content-backdrop fade"></div>
</div>
)
}
export default DemoTable
export default DemoTable;

View File

@ -11,7 +11,7 @@ import { Link, useNavigate, useParams } from "react-router-dom";
import { formatDate } from "../../utils/dateUtils";
import { useEmployeeProfile } from "../../hooks/useEmployees";
import { clearCacheKey, getCachedData } from "../../slices/apiDataManager";
import {clearApiCacheKey} from "../../slices/apiCacheSlice";
import { clearApiCacheKey } from "../../slices/apiCacheSlice";
const mobileNumberRegex = /^[7-9]\d{9}$/;
@ -24,66 +24,78 @@ const ManageEmployee = () => {
error,
loading: empLoading,
} = useEmployeeProfile(employeeId);
dispatch( changeMaster( "Job Role" ) );
const [disabledEmail,setDisabledEmail] = useState(false)
dispatch(changeMaster("Job Role"));
const [disabledEmail, setDisabledEmail] = useState(false);
const { data: job_role, loading } = useMaster();
const [isloading, setLoading] = useState(false);
const navigation = useNavigate();
const [currentEmployee, setCurrentEmployee] = useState();
const userSchema = z
.object({
...(employeeId ? { Id: z.number().optional() } : {}),
FirstName: z.string().min(1, { message: "First Name is required" }),
MiddleName: z.string().optional(),
LastName: z.string().min(1, { message: "Last Name is required" }),
Email: z.string().optional(),
CurrentAddress: z
.string()
.min(1, { message: "Current Address is required" }).max(150, { message: "Address cannot exceed 150 characters" }),
BirthDate: z.string().min(1, { message: "Birth Date is required" }).refine((date, ctx) => {
return new Date(date) <= new Date();
}, {
message: "Birth date cannot be in the future",
}),
JoiningDate: z.string().min(1, { message: "Joining Date is required" }).refine((date, ctx) => {
return new Date(date) <= new Date();
}, {
message: "Joining date cannot be in the future",
}),
EmergencyPhoneNumber: z
.string()
.min(1, { message: "Phone Number is required" })
.regex(mobileNumberRegex, { message: "Invalid phone number " }),
EmergencyContactPerson: z
.string()
.min(1, { message: "Emergency Contact Person is required" }),
AadharNumber: z.string()
.regex(/^\d{12}$/, "Aadhar card must be exactly 12 digits long")
const userSchema = z.object({
...(employeeId ? { Id: z.number().optional() } : {}),
FirstName: z.string().min(1, { message: "First Name is required" }),
MiddleName: z.string().optional(),
LastName: z.string().min(1, { message: "Last Name is required" }),
Email: z.string().optional(),
CurrentAddress: z
.string()
.min(1, { message: "Current Address is required" })
.max(150, { message: "Address cannot exceed 150 characters" }),
BirthDate: z
.string()
.min(1, { message: "Birth Date is required" })
.refine(
(date, ctx) => {
return new Date(date) <= new Date();
},
{
message: "Birth date cannot be in the future",
}
),
JoiningDate: z
.string()
.min(1, { message: "Joining Date is required" })
.refine(
(date, ctx) => {
return new Date(date) <= new Date();
},
{
message: "Joining date cannot be in the future",
}
),
EmergencyPhoneNumber: z
.string()
.min(1, { message: "Phone Number is required" })
.regex(mobileNumberRegex, { message: "Invalid phone number " }),
EmergencyContactPerson: z
.string()
.min(1, { message: "Emergency Contact Person is required" }),
AadharNumber: z
.string()
.regex(/^\d{12}$/, "Aadhar card must be exactly 12 digits long")
.nonempty("Aadhar card is required"),
Gender: z
.string()
.min(1, { message: "Gender is required" })
.refine((val) => val !== "Select Gender", {
message: "Please select a gender",
}),
PanNumber: z
Gender: z
.string()
.min(1, { message: "Gender is required" })
.refine((val) => val !== "Select Gender", {
message: "Please select a gender",
}),
PanNumber: z
.string()
.optional()
.refine((val) => !val || /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/.test(val), {
message: "Invalid PAN number",
}),
PeramnentAddress: z
.string()
.min(1, { message: "Permanent Address is required" }).max(150, { message: "Address cannot exceed 150 characters" }),
PhoneNumber: z
.string()
.min(1, { message: "Phone Number is required" })
.regex(mobileNumberRegex, { message: "Invalid phone number " }),
JobRoleId: z.string().min(1, { message: "Role is required" }),
})
PeramnentAddress: z
.string()
.min(1, { message: "Permanent Address is required" })
.max(150, { message: "Address cannot exceed 150 characters" }),
PhoneNumber: z
.string()
.min(1, { message: "Phone Number is required" })
.regex(mobileNumberRegex, { message: "Invalid phone number " }),
JobRoleId: z.string().min(1, { message: "Role is required" }),
});
const {
register,
@ -116,7 +128,7 @@ const ManageEmployee = () => {
const onSubmit = (data) => {
setLoading(true);
const formData = getValues();
const formDataToSend = new FormData();
@ -140,11 +152,10 @@ const ManageEmployee = () => {
}
EmployeeRepository.manageEmployee(formDataToSend)
.then( ( response ) =>
{
showToast("Employee details updated successfully.", "success" );
clearCacheKey("employeeListByProject")
clearCacheKey( "allEmployeeList" )
.then((response) => {
showToast("Employee details updated successfully.", "success");
clearCacheKey("employeeListByProject");
clearCacheKey("allEmployeeList");
setLoading(false);
navigation("/employees");
})
@ -197,11 +208,19 @@ const ManageEmployee = () => {
<h6 className="mb-0">
{employee ? "Update Employee" : "Create Employee"}
</h6>
<span className="cursor-pointer fs-6" data-htm="true" data-bs-toggle="tooltip"
data-bs-offset="0,6"
data-bs-placement="top"
data-bs-html="true" title="Move Back" onClick={()=>navigation("/employees")}><i class='bx bxs-chevron-left'></i> Back</span>
<span
className="cursor-pointer fs-6"
data-htm="true"
data-bs-toggle="tooltip"
data-bs-offset="0,6"
data-bs-placement="top"
data-bs-html="true"
title="Move Back"
onClick={() => navigation("/employees")}
>
<i className="bx bxs-chevron-left"></i> Back
</span>
</div>
<div className="card-body">
{!currentEmployee && empLoading && (
@ -289,8 +308,6 @@ const ManageEmployee = () => {
{errors.Email.message}
</div>
)}
</div>
<div className="col-sm-6">
<div className="form-text text-start">Phone Number</div>
@ -444,7 +461,9 @@ const ManageEmployee = () => {
Select Role
</option>
{job_role?.map((item) => (
<option value={item?.id} key={item.id}>{item?.name} </option>
<option value={item?.id} key={item.id}>
{item?.name}{" "}
</option>
))}
</select>
</div>
@ -525,7 +544,14 @@ const ManageEmployee = () => {
id="PanNumber"
placeholder="PAN Number"
/>
{errors.PanNumber && <div className="danger-text text-start" style={{fontSize:"12px"}}>{errors.PanNumber.message}</div>}
{errors.PanNumber && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.PanNumber.message}
</div>
)}
</div>
</div>

View File

@ -1,10 +1,9 @@
import React,{useState} from "react";
import React, { useState } from "react";
import moment from "moment";
import { ProjectStatus } from "../../utils/projectStatus";
const AboutProject = ( {data} ) =>
{
const [CurrentProject,setCurrentProject] = useState(data)
const AboutProject = ({ data }) => {
const [CurrentProject, setCurrentProject] = useState(data);
return (
<>
{data && (
@ -24,7 +23,7 @@ const AboutProject = ( {data} ) =>
</span>
</li>
<li className="d-flex align-items-center mb-4">
<i class="bx bx-stop-circle"></i>{" "}
<i className="bx bx-stop-circle"></i>{" "}
<span className="fw-medium mx-2">End Date:</span>{" "}
<span>
{data.endDate
@ -33,7 +32,7 @@ const AboutProject = ( {data} ) =>
</span>
</li>
<li className="d-flex align-items-center mb-2">
<i class="bx bx-trophy"></i>
<i className="bx bx-trophy"></i>
<span className="fw-medium mx-2">Status:</span>{" "}
<span>{ProjectStatus(data.projectStatusId)}</span>
</li>

View File

@ -2,136 +2,151 @@ import React from "react";
const ActivityTimeline = () => {
return (
<div className="card card-action mb-6">
<div className="card-header align-items-center">
<h5 className="card-action-title mb-0">
<i className="bx bx-bar-chart-alt-2 bx-lg text-body me-4"></i>
Activity Timeline
</h5>
</div>
<div className="card-body pt-3">
<ul className="timeline mb-0">
<li className="timeline-item timeline-item-transparent">
<span className="timeline-point timeline-point-primary"></span>
<div className="timeline-event">
<div className="timeline-header mb-3">
<h6 className="mb-0">12 Invoices have been paid</h6>
<small className="text-muted">12 min ago</small>
</div>
<p className="mb-2">Invoices have been paid to the company</p>
<div className="d-flex align-items-center mb-2">
<div className="badge bg-lighter rounded d-flex align-items-center">
<img
src="../../assets//img/icons/misc/pdf.png"
alt="img"
width="15"
className="me-2"
></img>
<span className="h6 mb-0 text-body">invoices.pdf</span>
</div>
</div>
</div>
</li>
<li className="timeline-item timeline-item-transparent">
<span className="timeline-point timeline-point-success"></span>
<div className="timeline-event">
<div className="timeline-header mb-3">
<h6 className="mb-0">Client Meeting</h6>
<small className="text-muted">45 min ago</small>
</div>
<p className="mb-2">Project meeting with john @10:15am</p>
<div className="d-flex justify-content-between flex-wrap gap-2 mb-2">
<div className="d-flex flex-wrap align-items-center mb-50">
<div className="avatar avatar-sm me-3">
<img
src="../../assets/img/avatars/1.png"
alt="Avatar"
className="rounded-circle"
></img>
</div>
<div>
<p className="mb-0 small fw-medium">
Lester McCarthy (Client)
</p>
<small>CEO of ThemeSelection</small>
</div>
</div>
</div>
</div>
</li>
<li className="timeline-item timeline-item-transparent">
<span className="timeline-point timeline-point-info"></span>
<div className="timeline-event">
<div className="timeline-header mb-3">
<h6 className="mb-0">Create a new project for client</h6>
<small className="text-muted">2 Day Ago</small>
</div>
<p className="mb-2">6 team members in a project</p>
<ul className="list-group list-group-flush">
<li className="list-group-item d-flex justify-content-between align-items-center flex-wrap border-top-0 p-0">
<div className="d-flex flex-wrap align-items-center">
<ul className="list-unstyled users-list d-flex align-items-center avatar-group m-0 me-2">
<li
data-bs-toggle="tooltip"
data-popup="tooltip-custom"
data-bs-placement="top"
className="avatar pull-up"
aria-label="Vinnie Mostowy"
data-bs-original-title="Vinnie Mostowy"
>
<img
className="rounded-circle"
src="../../assets/img/avatars/1.png"
alt="Avatar"
></img>
</li>
<li
data-bs-toggle="tooltip"
data-popup="tooltip-custom"
data-bs-placement="top"
className="avatar pull-up"
aria-label="Allen Rieske"
data-bs-original-title="Allen Rieske"
>
<img
className="rounded-circle"
src="../../assets/img/avatars/4.png"
alt="Avatar"
></img>
</li>
<li
data-bs-toggle="tooltip"
data-popup="tooltip-custom"
data-bs-placement="top"
className="avatar pull-up"
aria-label="Julee Rossignol"
data-bs-original-title="Julee Rossignol"
>
<img
className="rounded-circle"
src="../../assets/img/avatars/2.png"
alt="Avatar"
></img>
</li>
<li className="avatar">
<span
className="avatar-initial rounded-circle pull-up text-heading"
data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-original-title="3 more"
>
+3
</span>
</li>
</ul>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
// <div className="card card-action mb-6">
// <div className="card-header align-items-center">
// <h5 className="card-action-title mb-0">
// <i className="bx bx-bar-chart-alt-2 bx-lg text-body me-4"></i>
// Activity Timeline
// </h5>
// </div>
// <div className="card-body pt-3">
// <ul className="timeline mb-0">
// <li className="timeline-item timeline-item-transparent">
// <span className="timeline-point timeline-point-primary"></span>
// <div className="timeline-event">
// <div className="timeline-header mb-3">
// <h6 className="mb-0">12 Invoices have been paid</h6>
// <small className="text-muted">12 min ago</small>
// </div>
// <p className="mb-2">Invoices have been paid to the company</p>
// <div className="d-flex align-items-center mb-2">
// <div className="badge bg-lighter rounded d-flex align-items-center">
// <img
// src="../../assets//img/icons/misc/pdf.png"
// alt="img"
// width="15"
// className="me-2"
// ></img>
// <span className="h6 mb-0 text-body">invoices.pdf</span>
// </div>
// </div>
// </div>
// </li>
// <li className="timeline-item timeline-item-transparent">
// <span className="timeline-point timeline-point-success"></span>
// <div className="timeline-event">
// <div className="timeline-header mb-3">
// <h6 className="mb-0">Client Meeting</h6>
// <small className="text-muted">45 min ago</small>
// </div>
// <p className="mb-2">Project meeting with john @10:15am</p>
// <div className="d-flex justify-content-between flex-wrap gap-2 mb-2">
// <div className="d-flex flex-wrap align-items-center mb-50">
// <div className="avatar avatar-sm me-3">
// <img
// src="../../assets/img/avatars/1.png"
// alt="Avatar"
// className="rounded-circle"
// ></img>
// </div>
// <div>
// <p className="mb-0 small fw-medium">
// Lester McCarthy (Client)
// </p>
// <small>CEO of ThemeSelection</small>
// </div>
// </div>
// </div>
// </div>
// </li>
// <li className="timeline-item timeline-item-transparent">
// <span className="timeline-point timeline-point-info"></span>
// <div className="timeline-event">
// <div className="timeline-header mb-3">
// <h6 className="mb-0">Create a new project for client</h6>
// <small className="text-muted">2 Day Ago</small>
// </div>
// <p className="mb-2">6 team members in a project</p>
// <ul className="list-group list-group-flush">
// <li className="list-group-item d-flex justify-content-between align-items-center flex-wrap border-top-0 p-0">
// <div className="d-flex flex-wrap align-items-center">
// <ul className="list-unstyled users-list d-flex align-items-center avatar-group m-0 me-2">
// <li
// data-bs-toggle="tooltip"
// data-popup="tooltip-custom"
// data-bs-placement="top"
// className="avatar pull-up"
// aria-label="Vinnie Mostowy"
// data-bs-original-title="Vinnie Mostowy"
// >
// <img
// className="rounded-circle"
// src="../../assets/img/avatars/1.png"
// alt="Avatar"
// ></img>
// </li>
// <li
// data-bs-toggle="tooltip"
// data-popup="tooltip-custom"
// data-bs-placement="top"
// className="avatar pull-up"
// aria-label="Allen Rieske"
// data-bs-original-title="Allen Rieske"
// >
// <img
// className="rounded-circle"
// src="../../assets/img/avatars/4.png"
// alt="Avatar"
// ></img>
// </li>
// <li
// data-bs-toggle="tooltip"
// data-popup="tooltip-custom"
// data-bs-placement="top"
// className="avatar pull-up"
// aria-label="Julee Rossignol"
// data-bs-original-title="Julee Rossignol"
// >
// <img
// className="rounded-circle"
// src="../../assets/img/avatars/2.png"
// alt="Avatar"
// ></img>
// </li>
// <li className="avatar">
// <span
// className="avatar-initial rounded-circle pull-up text-heading"
// data-bs-toggle="tooltip"
// data-bs-placement="bottom"
// data-bs-original-title="3 more"
// >
// +3
// </span>
// </li>
// </ul>
// </div>
// </li>
// </ul>
// </div>
// </li>
// </ul>
// </div>
// </div>
<div className="misc-wrapper">
<h2 className="mb-2 mx-2">Coming Soon!</h2>
<p className="mb-4 mx-2">We're currently working on this feature and will have it ready shortly.
Thank you for your patience!</p>
<div className="mt-4">
<img
src="../assets/img/illustrations/girl-doing-yoga-light.png"
alt="girl-doing-yoga-light"
aria-label="Girl doing yoga light"
width="500"
className="img-fluid"
data-app-dark-img="illustrations/girl-doing-yoga-dark.png"
data-app-light-img="illustrations/girl-doing-yoga-light.png" />
</div>
</div>
);
};

View File

@ -1,4 +1,4 @@
import React, { useState,useEffect } from "react";
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { changeMaster } from "../../slices/localVariablesSlice";
import useMaster from "../../hooks/masterHook/useMaster";
@ -6,33 +6,33 @@ import { employee } from "../../data/masters";
import { useForm, Controller } from "react-hook-form";
import { z } from "zod";
import { getCachedData } from "../../slices/apiDataManager";
import {useModal} from "../../ModalContext";
import {useProjects} from "../../hooks/useProjects";
import {useEmployeesAllOrByProjectId} from "../../hooks/useEmployees";
import {TasksRepository} from "../../repositories/ProjectRepository";
import { useModal } from "../../ModalContext";
import { useProjects } from "../../hooks/useProjects";
import { useEmployeesAllOrByProjectId } from "../../hooks/useEmployees";
import { TasksRepository } from "../../repositories/ProjectRepository";
import showToast from "../../services/toastService";
const schema = z.object({
selectedEmployees: z.array( z.number() ).min( 1, {message: "At least one employee must be selected"} ),
description: z.string().min( 1, {message: "description required"} ),
})
selectedEmployees: z
.array(z.number())
.min(1, { message: "At least one employee must be selected" }),
description: z.string().min(1, { message: "description required" }),
});
const AssignRoleModel = ({ assignData, onClose }) => {
const [plannedTask, setPlannedTask] = useState();
const { openModal, closeModal } = useModal();
const selectedProject = useSelector(
(store) => store.localVariables.projectId
);
const { employees } = useEmployeesAllOrByProjectId(selectedProject);
const AssignRoleModel = ( {assignData,onClose}) => {
const [ plannedTask, setPlannedTask ] = useState()
const { openModal, closeModal } = useModal()
const selectedProject = useSelector((store)=>store.localVariables.projectId)
const {employees} = useEmployeesAllOrByProjectId( selectedProject )
const dispatch = useDispatch();
const { data, loading } = useMaster();
const jobRoleData = getCachedData("Job Role");
const dispatch = useDispatch()
const {data,loading} = useMaster()
const jobRoleData = getCachedData("Job Role")
const [selectedRole, setSelectedRole] = useState("all");
const [selectedEmployees, setSelectedEmployees] = useState([]);
const [selectedRole, setSelectedRole] = useState("all");
const [selectedEmployees, setSelectedEmployees] = useState([]);
const { handleSubmit, control, setValue, watch, formState: { errors },reset } = useForm({
@ -47,40 +47,36 @@ const { handleSubmit, control, setValue, watch, formState: { errors },reset } =
},
});
const handleRoleChange = (event) => {
setSelectedRole(event.plannedTask.value);
};
const handleRoleChange = (event) => {
setSelectedRole(event.plannedTask.value);
};
const filteredEmployees =
selectedRole === "all"
? employees
: employees.filter((emp) => emp.JobRoleId.toString() === selectedRole);
const filteredEmployees = selectedRole === "all"
? employees
: employees.filter((emp) => emp.JobRoleId.toString() === selectedRole);
// not need currently for this fun
const handleEmployeeSelection = (employeeId, field) => {
setSelectedEmployees((prevSelected) => {
let updatedSelection;
if (!prevSelected.includes(employeeId)) {
updatedSelection = [...prevSelected, employeeId];
} else {
updatedSelection = prevSelected.filter((id) => id !== employeeId);
}
field.onChange(updatedSelection);
return updatedSelection;
});
};
// not need currently for this fun
const handleEmployeeSelection = (employeeId,field) => {
setSelectedEmployees((prevSelected) => {
let updatedSelection;
if (!prevSelected.includes(employeeId)) {
updatedSelection = [...prevSelected, employeeId];
} else {
updatedSelection = prevSelected.filter((id) => id !== employeeId);
}
field.onChange(updatedSelection);
return updatedSelection;
});
};
const removeEmployee = (employeeId) => {
setSelectedEmployees((prevSelected) => {
const updatedSelection = prevSelected.filter((id) => id !== employeeId);
setValue("selectedEmployees", updatedSelection); // Ensure form state is updated
return updatedSelection;
});
};
const removeEmployee = (employeeId) => {
setSelectedEmployees((prevSelected) => {
const updatedSelection = prevSelected.filter((id) => id !== employeeId);
setValue("selectedEmployees", updatedSelection); // Ensure form state is updated
return updatedSelection;
});
};
const onSubmit = async(data) => {
@ -111,150 +107,190 @@ useEffect(()=>{
},[dispatch])
return (<>
<div className="container my-1">
<div className="mb-2">
<div className="bs-stepper wizard-numbered d-flex justify-content-center align-items-center flex-wrap">
{[
assignData?.building?.name,
assignData?.floor?.floorName,
assignData?.workArea?.areaName,
assignData?.workItem?.workItem?.activityMaster?.activityName
].map((item, index, array) => (
<div key={index} className="col d-flex justify-content-center align-items-center">
<div className="bs-stepper-header p-1 text-center">
<span className="fs-5">{item}</span>
{/* Arrow between items */}
{index < array.length - 1 && (
<div className="line">
<i className="icon-base bx bx-chevron-right scaleX-n1-rtl"></i>
</div>
)}
</div>
</div>
))}
</div>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row mb-1">
<div className="col-sm-4">
<div className="form-text text-start">Select Role</div>
<div className="input-group input-group-merge">
<select
className="form-select form-select-sm"
id="Role"
value={selectedRole}
onChange={handleRoleChange}
aria-label=""
return (
<>
<div className="container my-1">
<div className="mb-2">
<div className="bs-stepper wizard-numbered d-flex justify-content-center align-items-center flex-wrap">
{[
assignData?.building?.name,
assignData?.floor?.floorName,
assignData?.workArea?.areaName,
assignData?.workItem?.workItem?.activityMaster?.activityName,
].map((item, index, array) => (
<div
key={index}
className="col d-flex justify-content-center align-items-center"
>
{loading && data ? "Loading..." : (
<>
<option value="all">All</option>
{ jobRoleData?.map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</>
)}
</select>
</div>
</div>
</div>
<div class="divider text-start">
<div class="divider-text">Employee</div>
</div>
{selectedRole !== "" && (
<div className="row mb-2">
<div className="col-sm-12">
<div className="row">
{filteredEmployees.map((emp) => {
const jobRole = jobRoleData?.find((role) => role.id === emp.jobRoleId);
<div className="bs-stepper-header p-1 text-center">
<span className="fs-5">{item}</span>
return (
<div key={emp.id} className="col-6 col-sm-4 col-md-4 col-lg-3 mb-1">
<div className="form-check text-start p-0">
<div className="li-wrapper d-flex justify-content-start align-items-start">
<Controller
name="selectedEmployees"
control={control}
render={({ field }) => (
<input
{...field}
className="form-check-input mx-2"
type="checkbox"
id={`employee-${emp.id}`}
value={emp.id}
checked={field.value.includes(emp.id)} // Ensure the checkbox reflects the current form state
onChange={() => {
handleEmployeeSelection( emp.id, field )
}}
/>
)}
/>
<div className="list-content">
<h6 className="mb-0">{emp.firstName
} {emp.lastName}</h6>
<small className="text-muted">
{loading && (<p className="skeleton para" style={{height:"7px"}}></p>)}
{data && !loading && (jobRole ? jobRole.name : 'Unknown Role')}
</small>
</div>
</div>
</div>
{/* Arrow between items */}
{index < array.length - 1 && (
<div className="line">
<i className="icon-base bx bx-chevron-right scaleX-n1-rtl"></i>
</div>
);
})}
)}
</div>
</div>
))}
</div>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="row mb-1">
<div className="col-sm-4">
<div className="form-text text-start">Select Role</div>
<div className="input-group input-group-merge">
<select
className="form-select form-select-sm"
id="Role"
value={selectedRole}
onChange={handleRoleChange}
aria-label=""
>
{loading && data ? (
"Loading..."
) : (
<>
<option value="all">All</option>
{jobRoleData?.map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</>
)}
</select>
</div>
</div>
</div>
</div>
)}
{selectedEmployees.length > 0 && (
<div className="mt-1">
<div className="text-start px-2">
{selectedEmployees.map((empId) => {
const emp = employees.find((emp) => emp.id === empId);
return (
<span key={empId} className="badge bg-label-primary d-inline-flex align-items-center gap-2 me-1 p-2 mb-2">
{emp.firstName
} {emp.lastName}
<p
type="button"
className=" btn-close-white p-0 m-0"
aria-label="Close"
onClick={() => {
removeEmployee(empId);
setValue("selectedEmployees", selectedEmployees.filter(id => id !== empId));
}}
><i className="icon-base bx bx-x icon-md "></i></p>
</span>
);
})}
<div className="divider text-start">
<div className="divider-text">Employee</div>
</div>
</div>
)}
{selectedRole !== "" && (
<div className="row mb-2">
<div className="col-sm-12">
<div className="row">
{filteredEmployees.map((emp) => {
const jobRole = jobRoleData?.find(
(role) => role.id === emp.jobRoleId
);
<div class="col-md text-start mx-0 px-0">
<div class="form-check form-check-inline mt-4 px-1">
<label className="form-text fs-6" for="inlineCheckbox1">Pending Work</label>
<label className="form-check-label ms-2" for="inlineCheckbox1">{ assignData?.workItem?.workItem?.plannedWork - assignData?.workItem?.workItem?.completedWork}</label>
</div>
<div className="form-check form-check-inline col-sm-2 col">
<label for="defaultFormControlInput" className="form-label">Target</label>
<input type="text" className="form-control form-control-sm " value={plannedTask} onChange={(e)=>setPlannedTask(e.target.value)} id="defaultFormControlInput" aria-describedby="defaultFormControlHelp" />
</div>
</div>
{errors.selectedEmployees && (
<div className="danger-text mt-1">
<p>{errors.selectedEmployees[0]}</p>
</div>
)}
<label for="exampleFormControlTextarea1" className="form-label">Description</label>
return (
<div
key={emp.id}
className="col-6 col-sm-4 col-md-4 col-lg-3 mb-1"
>
<div className="form-check text-start p-0">
<div className="li-wrapper d-flex justify-content-start align-items-start">
<Controller
name="selectedEmployees"
control={control}
render={({ field }) => (
<input
{...field}
className="form-check-input mx-2"
type="checkbox"
id={`employee-${emp.id}`}
value={emp.id}
checked={field.value.includes(emp.id)} // Ensure the checkbox reflects the current form state
onChange={() => {
handleEmployeeSelection(emp.id, field);
}}
/>
)}
/>
<div className="list-content">
<h6 className="mb-0">
{emp.firstName} {emp.lastName}
</h6>
<small className="text-muted">
{loading && (
<p
className="skeleton para"
style={{ height: "7px" }}
></p>
)}
{data &&
!loading &&
(jobRole ? jobRole.name : "Unknown Role")}
</small>
</div>
</div>
</div>
</div>
);
})}
</div>
</div>
</div>
)}
{selectedEmployees.length > 0 && (
<div className="mt-1">
<div className="text-start px-2">
{selectedEmployees.map((empId) => {
const emp = employees.find((emp) => emp.id === empId);
return (
<span
key={empId}
className="badge bg-label-primary d-inline-flex align-items-center gap-2 me-1 p-2 mb-2"
>
{emp.firstName} {emp.lastName}
<p
type="button"
className=" btn-close-white p-0 m-0"
aria-label="Close"
onClick={() => {
removeEmployee(empId);
setValue(
"selectedEmployees",
selectedEmployees.filter((id) => id !== empId)
);
}}
>
<i className="icon-base bx bx-x icon-md "></i>
</p>
</span>
);
})}
</div>
</div>
)}
<div className="col-md text-start mx-0 px-0">
<div className="form-check form-check-inline mt-4 px-1">
<label className="form-text fs-6" for="inlineCheckbox1">
Pending Work
</label>
<label className="form-check-label ms-2" for="inlineCheckbox1">
{assignData?.workItem?.workItem?.plannedWork -
assignData?.workItem?.workItem?.completedWork}
</label>
</div>
<div className="form-check form-check-inline col-sm-2 col">
<label for="defaultFormControlInput" className="form-label">
Target
</label>
<input
type="text"
className="form-control form-control-sm "
value={plannedTask}
onChange={(e) => setPlannedTask(e.target.value)}
id="defaultFormControlInput"
aria-describedby="defaultFormControlHelp"
/>
</div>
</div>
{errors.selectedEmployees && (
<div className="danger-text mt-1">
<p>{errors.selectedEmployees[0]}</p>
</div>
)}
<label for="exampleFormControlTextarea1" className="form-label">
Description
</label>
<Controller
name="description"
control={control}
@ -266,22 +302,27 @@ useEffect(()=>{
rows="3"
/>
)}
/>
{errors.description && ( <div>{errors.description.message }</div>)}
<div className="col-12 d-flex justify-content-center align-items-center gap-sm-6 gap-8 text-center mt-1">
<button type="submit" className="btn btn-sm btn-primary ">Submit</button>
<button type="reset" className="btn btn-sm btn-label-secondary" data-bs-dismiss="modal" aria-label="Close" onClick={closeModal}>
Cancel
</button>
</div>
</form>
</div>
</div>
/>
{errors.description && <div>{errors.description.message}</div>}
</>
<div className="col-12 d-flex justify-content-center align-items-center gap-sm-6 gap-8 text-center mt-1">
<button type="submit" className="btn btn-sm btn-primary ">
Submit
</button>
<button
type="reset"
className="btn btn-sm btn-label-secondary"
data-bs-dismiss="modal"
aria-label="Close"
onClick={closeModal}
>
Cancel
</button>
</div>
</form>
</div>
</div>
</>
);
};
export default AssignRoleModel
export default AssignRoleModel;

View File

@ -105,12 +105,7 @@ const FloorModel = ({
<div className="modal-content">
<div className="modal-body">
<div className="row">
<button
type="button"
className="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
<button type="button" className="btn-close" aria-label="Close" onClick={onClose}/>
<div className="text-center mb-1">
<h5 className="mb-1">Manage Floors - {project.name}</h5>
</div>
@ -154,7 +149,7 @@ const FloorModel = ({
onChange={handleFloorChange}
>
<option value="0">Add New Floor</option>
{selectedBuilding.floors.map((floor) => (
{selectedBuilding?.floors?.map((floor) => (
<option key={floor.id} value={floor.id}>
{floor.floorName}
</option>

View File

@ -1,69 +1,154 @@
import {useEffect, useState} from "react";
import { useEffect, useState } from "react";
import Building from "./Building";
import Floor from "./Floor";
import FloorModel from "./FloorModel";
import showToast from "../../../services/toastService";
import ProjectRepository from "../../../repositories/ProjectRepository";
const InfraTable = ( {buildings,assign} ) =>
{
const [projectBuiling,setProjectBuilding] = useState([])
const [expandedBuildings, setExpandedBuildings] = useState([]);
const toggleBuilding = (buildingId) => {
setExpandedBuildings((prevExpanded) =>
prevExpanded.includes(buildingId)
? prevExpanded.filter((id) => id !== buildingId)
: [...prevExpanded, buildingId]
);
};
const InfraTable = ({ buildings }) => {
const [projectBuilding, setProjectBuilding] = useState([]);
const [expandedBuildings, setExpandedBuildings] = useState([]);
const [showFloorModal, setShowFloorModal] = useState(false);
const [selectedBuilding, setSelectedBuilding] = useState(null);
const [clearTrigger, setClearTrigger] = useState(false);
const getContent = (building) => {
return building.floors.length > 0 ? (
building.floors.map((floor) => <Floor forBuilding={building} key={floor.id} floor={floor} workAreas={floor.workAreas} />)
) : (
<tr>
<td colSpan="4">
<div className="alert alert-warning text-center mb-0" role="alert">
<p>No floors have been added yet. Please add floors to start managing your building.</p>
<button
type="button"
className="btn btn-xs btn-primary"
data-bs-toggle="modal"
data-bs-target="#addFloorModal"
onClick={() => openModal('addFloor')}
>
<i className="bx bx-plus-circle me-2"></i>
Add Floors
</button>
</div>
</td>
</tr>
);
};
useEffect( () =>
{
setProjectBuilding(buildings)
},[buildings])
return (
<div className="col-12 overflow-auto">
{projectBuiling && projectBuiling.length > 0 && (
<table className="table table-bordered">
<tbody>
{projectBuiling.map((building) => (
<Building
key={building.id}
building={building}
toggleBuilding={toggleBuilding}
expandedBuildings={expandedBuildings}
getContent={getContent}
/>
))}
</tbody>
</table>
)}
</div>
const toggleBuilding = (buildingId) => {
setExpandedBuildings((prevExpanded) =>
prevExpanded.includes(buildingId)
? prevExpanded.filter((id) => id !== buildingId)
: [...prevExpanded, buildingId]
);
};
export default InfraTable
const handleAddFloor = (building) => {
setSelectedBuilding(building);
setShowFloorModal(true);
};
const handleFloorSubmit = async (data) => {
try {
const payload = [
{
building: null,
floor: {
id: data.id || "0",
floorName: data.floorName,
buildingId: data.buildingId,
},
workArea: null,
},
];
const res = await ProjectRepository.manageProjectInfra(payload);
if (res?.success) {
showToast("Floor saved successfully!", "success");
// Find and update the correct building
const updatedBuildings = projectBuilding.map((b) => {
if (b.id === parseInt(data.buildingId)) {
const newFloor = {
id: res.data?.[0]?.floor?.id || Math.random(),
floorName: res.data?.[0]?.floor?.floorName || data.floorName,
workAreas: [],
};
return {
...b,
floors: [...(b.floors || []), newFloor],
};
}
return b;
});
setProjectBuilding(updatedBuildings);
setShowFloorModal(false);
setClearTrigger(true);
} else {
showToast("Failed to save floor", "error");
}
} catch (err) {
console.error("Error adding floor", err);
showToast("Error occurred while saving floor", "error");
}
};
const handleClearComplete = () => {
setClearTrigger(false);
};
const getContent = (building) => {
return building.floors?.length > 0 ? (
building.floors.map((floor) => (
<Floor
key={floor.id}
forBuilding={building}
floor={floor}
workAreas={floor.workAreas}
/>
))
) : (
<tr>
<td colSpan="4">
<div className="alert alert-warning text-center mb-0" role="alert">
<p>No floors have been added yet. Please add floors to start managing your building.</p>
<button
type="button"
className="btn btn-xs btn-primary"
onClick={() => handleAddFloor(building)}
>
<i className="bx bx-plus-circle me-2"></i>
Add Floors
</button>
</div>
</td>
</tr>
);
};
useEffect(() => {
setProjectBuilding(buildings);
}, [buildings]);
return (
<div >
{projectBuilding && projectBuilding.length > 0 && (
<table className="table table-bordered">
<tbody>
{projectBuilding.map((building) => (
<Building
key={building.id}
building={building}
toggleBuilding={toggleBuilding}
expandedBuildings={expandedBuildings}
getContent={getContent}
/>
))}
</tbody>
</table>
)}
{showFloorModal && selectedBuilding && (
<div
className="modal fade show"
id="floor-model"
tabIndex="-1"
style={{ display: "block" }}
aria-modal="true"
role="dialog"
>
<FloorModel
project={{
buildings: [selectedBuilding]
}}
onClose={() => setShowFloorModal(false)}
onSubmit={handleFloorSubmit}
onClearComplete={handleClearComplete}
/>
</div>
)}
</div>
);
};
export default InfraTable;

View File

@ -1,4 +1,4 @@
import React, { useState,useEffect } from "react";
import React, { useState, useEffect } from "react";
import { useModal } from "../../../ModalContext";
import AssignRoleModel from "../AssignRole";
import { useParams } from "react-router-dom";
@ -6,8 +6,8 @@ import GlobalModel from "../../common/GlobalModel";
const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
const { projectId } = useParams();
const [ itemName, setItemName ] = useState( "" );
const [NewWorkItem,setNewWorkItem] = useState()
const [itemName, setItemName] = useState("");
const [NewWorkItem, setNewWorkItem] = useState();
const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = () => setIsModalOpen(true);
@ -17,7 +17,6 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
};
const handleAssignTask = () => {
setItemName("");
};
@ -29,8 +28,7 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
// };
useEffect(() => {
setNewWorkItem(workItem)
setNewWorkItem(workItem);
}, [workItem]); // This hook will run whenever the workItem prop changes
let assigndata = {
@ -42,6 +40,8 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
console.log(assigndata)
const hasWorkItem = NewWorkItem && NewWorkItem
const hasWorkItem = NewWorkItem && NewWorkItem;
return (
<>
<GlobalModel
@ -57,21 +57,32 @@ console.log(assigndata)
<td className="text-start table-cell-small">
<i className="bx bx-right-arrow-alt"></i>
<span className="fw-medium">
{hasWorkItem ? ( NewWorkItem?.workItem?.activityMaster?.activityName || workItem.activityMaster?.activityName ) :"NA"
}
{hasWorkItem
? NewWorkItem?.workItem?.activityMaster?.activityName ||
workItem.activityMaster?.activityName
: "NA"}
</span>
</td>
{/* for mobile view */}
<td className="text-center d-sm-none d-sm-table-cell">
{hasWorkItem ? (NewWorkItem?.workItem?.completedWork || workItem?.completedWork) :"NA" }/{" "}
{ hasWorkItem ? (NewWorkItem?.workItem?.plannedWork || workItem?.plannedWork) : "NA"}
{hasWorkItem
? NewWorkItem?.workItem?.completedWork || workItem?.completedWork
: "NA"}
/{" "}
{hasWorkItem
? NewWorkItem?.workItem?.plannedWork || workItem?.plannedWork
: "NA"}
</td>
{/* for greather than mobile view ************* */}
{/* for greather than mobile view ************* */}
<td className="text-center d-none d-md-table-cell">
{hasWorkItem ? (NewWorkItem?.workItem?.plannedWork || workItem?.plannedWork): "NA"}
{hasWorkItem
? NewWorkItem?.workItem?.plannedWork || workItem?.plannedWork
: "NA"}
</td>
<td className="text-center d-none d-md-table-cell">
{hasWorkItem ? (NewWorkItem?.workItem?.completedWork || workItem?.completedWork) : "NA"}
{hasWorkItem
? NewWorkItem?.workItem?.completedWork || workItem?.completedWork
: "NA"}
</td>
{/* ************************************************ */}
<td className="text-center" style={{ width: "15%" }}>
@ -81,17 +92,23 @@ console.log(assigndata)
role="progressbar"
style={{
width: getProgress(
(NewWorkItem?.workItem?.plannedWork || workItem?.plannedWork),
(NewWorkItem?.workItem?.completedWork || workItem?.completedWork)
NewWorkItem?.workItem?.plannedWork || workItem?.plannedWork,
NewWorkItem?.workItem?.completedWork ||
workItem?.completedWork
),
height: "10px",
}}
aria-valuenow={
hasWorkItem ? (NewWorkItem?.workItem?.completedWork || workItem?.completedWork) : 0
hasWorkItem
? NewWorkItem?.workItem?.completedWork ||
workItem?.completedWork
: 0
}
aria-valuemin="0"
aria-valuemax={
hasWorkItem ? (NewWorkItem?.workItem?.plannedWork || workItem?.plannedWork) : 0
hasWorkItem
? NewWorkItem?.workItem?.plannedWork || workItem?.plannedWork
: 0
}
></div>
</div>

View File

@ -312,12 +312,12 @@ const ProjectInfra = ({ data, activityMaster, onDataChange,eachSiteEngineer }) =
}
const openModal = () => {
const modalElement = document.getElementById('building-model');
const modal = new Modal(modalElement, {
backdrop: false,
keyboard: true,
focus: true
});
modal.show()
const modal = new Modal(modalElement, {
backdrop: false,
keyboard: true,
focus: true
});
modal.show()
};
const closeModal = () => {
setIsModalOpen(false);
@ -364,10 +364,12 @@ const ProjectInfra = ({ data, activityMaster, onDataChange,eachSiteEngineer }) =
</div>
{isFloorModalOpen && (
<div
className={`modal fade `}
className="modal fade show"
id="floor-model"
tabIndex="-1"
aria-hidden="true"
role="dialog"
style={{ display: 'block' }}
aria-hidden="false"
>
<FloorModel
project={data}
@ -375,7 +377,7 @@ const ProjectInfra = ({ data, activityMaster, onDataChange,eachSiteEngineer }) =
onSubmit={handleFloorModelFormSubmit}
clearTrigger={clearFormTrigger}
onClearComplete={() => setClearFormTrigger(false)}
></FloorModel>
/>
</div>
)}
@ -437,15 +439,14 @@ const ProjectInfra = ({ data, activityMaster, onDataChange,eachSiteEngineer }) =
Manage Building
</button>
<button
type="button"
data-bs-toggle="modal"
className="link-button m-1"
data-bs-target="#floor-model"
onClick={() => openFloorModel()}
>
<i className="bx bx-plus-circle me-2"></i>
Manage Floors
</button>
type="button"
className="link-button m-1"
onClick={() => openFloorModel()}
>
<i className="bx bx-plus-circle me-2"></i>
Manage Floors
</button>
<button
type="button"
@ -470,7 +471,7 @@ const ProjectInfra = ({ data, activityMaster, onDataChange,eachSiteEngineer }) =
</div>
</div>
<div className="row ">
<InfraTable buildings={project.buildings}/>
<InfraTable buildings={project.buildings} project={data}/>
</div>
</div>
</div>

View File

@ -1,7 +1,25 @@
import React from "react";
const WorkPlan = () => {
return <div>Work plan calender will go here</div>;
return (
<>
<div className="misc-wrapper">
<h2 className="mb-2 mx-2">Coming Soon!</h2>
<p className="mb-4 mx-2">We're currently working on this feature and will have it ready shortly.
Thank you for your patience!</p>
<div className="mt-4">
<img
src="../assets/img/illustrations/girl-doing-yoga-light.png"
alt="girl-doing-yoga-light"
aria-label="Girl doing yoga light"
width="500"
className="img-fluid"
data-app-dark-img="illustrations/girl-doing-yoga-dark.png"
data-app-light-img="illustrations/girl-doing-yoga-light.png" />
</div>
</div>
</>
)
};
export default WorkPlan;

View File

@ -2,33 +2,33 @@ import React from "react";
const Loader = () => {
return (
<div class="demo-inline-spacing">
<div class="spinner-grow" role="status">
<span class="visually-hidden">Loading...</span>
<div className="demo-inline-spacing">
<div className="spinner-grow" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<div class="spinner-grow text-primary" role="status">
<span class="visually-hidden">Loading...</span>
<div className="spinner-grow text-primary" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<div class="spinner-grow text-secondary" role="status">
<span class="visually-hidden">Loading...</span>
<div className="spinner-grow text-secondary" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<div class="spinner-grow text-success" role="status">
<span class="visually-hidden">Loading...</span>
<div className="spinner-grow text-success" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<div class="spinner-grow text-danger" role="status">
<span class="visually-hidden">Loading...</span>
<div className="spinner-grow text-danger" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<div class="spinner-grow text-warning" role="status">
<span class="visually-hidden">Loading...</span>
<div className="spinner-grow text-warning" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<div class="spinner-grow text-info" role="status">
<span class="visually-hidden">Loading...</span>
<div className="spinner-grow text-info" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<div class="spinner-grow text-light" role="status">
<span class="visually-hidden">Loading...</span>
<div className="spinner-grow text-light" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<div class="spinner-grow text-dark" role="status">
<span class="visually-hidden">Loading...</span>
<div className="spinner-grow text-dark" role="status">
<span className="visually-hidden">Loading...</span>
</div>
</div>
);

View File

@ -3,18 +3,18 @@ import axios from "axios";
const API_URL = "http://localhost:5000/mastersdata";
const DeleteMaster = ({ master,onClose}) => {
const DeleteMaster = ({ master, onClose }) => {
const [loader, setLoader] = useState(false);
const handleDelete = () => {
const index = mastersdata[master?.masterType]?.findIndex(item => String(item?.id) === String(master?.item?.id));
console.log(index)
const handleDelete = () => {
const index = mastersdata[master?.masterType]?.findIndex(
(item) => String(item?.id) === String(master?.item?.id)
);
console.log(index);
if (index !== -1) {
mastersdata[master?.masterType].splice(index, 1);
mastersdata[master?.masterType].splice(index, 1);
}
onClose()
onClose();
};
return (
@ -27,8 +27,8 @@ const DeleteMaster = ({ master,onClose}) => {
onClick={handleDelete}
>
{loader ? (
<div class="spinner-border text-primary" role="status">
<span class="sr-only">Loading...</span>
<div className="spinner-border text-primary" role="status">
<span className="sr-only">Loading...</span>
</div>
) : (
"Delete"

View File

@ -70,7 +70,7 @@
{
"text": "Daily Expenses",
"available": true,
"link": "/dashboard"
"link": "/dashboard/"
}
]
},
@ -83,12 +83,12 @@
{
"text": "Application Users",
"available": true,
"link": "/employees"
"link": "/employees/"
},
{
"text": "Employees",
"available": true,
"link": "/employees"
"link": "/employees/list"
},
{
"text": "Masters",

View File

@ -1,5 +1,9 @@
import React, { useState, useEffect } from "react";
import { cacheData, getCachedData, getCachedProfileData } from "../../slices/apiDataManager";
import {
cacheData,
getCachedData,
getCachedProfileData,
} from "../../slices/apiDataManager";
import Breadcrumb from "../../components/common/Breadcrumb";
import AttendanceLog from "../../components/Activities/AttendcesLogs";
import Attendance from "../../components/Activities/Attendance";
@ -10,31 +14,41 @@ import Regularization from "../../components/Activities/Regularization";
import { useAttendace } from "../../hooks/useAttendance";
import { useDispatch, useSelector } from "react-redux";
import { setProjectId } from "../../slices/localVariablesSlice";
import {markCurrentAttendance} from "../../slices/apiSlice/attendanceAllSlice";
import { markCurrentAttendance } from "../../slices/apiSlice/attendanceAllSlice";
import { hasUserPermission } from "../../utils/authUtils";
import {useHasUserPermission} from "../../hooks/useHasUserPermission";
import {REGULARIZE_ATTENDANCE} from "../../utils/constants";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { REGULARIZE_ATTENDANCE } from "../../utils/constants";
const AttendancePage = () =>
{
const loginUser = getCachedProfileData()
var selectedProject = useSelector( ( store ) => store.localVariables.projectId )
const {projects,loading:projectLoading} = useProjects()
const {attendance,loading:attLoading} = useAttendace(selectedProject)
const[attendances,setAttendances] = useState()
const AttendancePage = () => {
const loginUser = getCachedProfileData();
var selectedProject = useSelector((store) => store.localVariables.projectId);
const { projects, loading: projectLoading } = useProjects();
const { attendance, loading: attLoading } = useAttendace(selectedProject);
const [attendances, setAttendances] = useState();
const [empRoles, setEmpRoles] = useState(null);
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
<<<<<<< Updated upstream
const [modelConfig, setModelConfig] = useState();
const DoRegularized = useHasUserPermission(REGULARIZE_ATTENDANCE);
const dispatch = useDispatch();
const [formData, setFormData] = useState({
time: "",
description: "",
date: new Date().toLocaleDateString(),
});
=======
const [ modelConfig, setModelConfig ] = useState();
const DoRegularized = useHasUserPermission(REGULARIZE_ATTENDANCE)
const dispatch = useDispatch()
const [formData, setFormData] = useState({
time: "",
markTime: "",
description: "",
date: new Date().toLocaleDateString(),
});
>>>>>>> Stashed changes
const getRole = (roleId) => {
if (!empRoles) return "Unassigned";
if (!roleId) return "Unassigned";
@ -46,16 +60,22 @@ const AttendancePage = () =>
setIsCreateModalOpen(true);
};
const handleModalData =(employee)=>{
const handleModalData = (employee) => {
setModelConfig(employee);
}
};
const closeModal = () => {
setModelConfig(null);
setIsCreateModalOpen(false);
const modalElement = document.getElementById("check-Out-modal");
if (modalElement) {
<<<<<<< Updated upstream
modalElement.classList.remove("show");
modalElement.style.display = "none";
document.body.classList.remove("modal-open");
document.querySelector(".modal-backdrop").remove();
}
=======
modalElement.classList.remove('show');
modalElement.style.display = 'none';
document.body.classList.remove('modal-open');
@ -64,7 +84,9 @@ const AttendancePage = () =>
};
const handleSubmit = ( formData ) =>{
const handleSubmit = ( formData ) =>
{
debugger
dispatch( markCurrentAttendance( formData ) ).then( ( action ) =>
{
const updatedAttendance = attendances.map(item =>
@ -80,42 +102,112 @@ const AttendancePage = () =>
{
showToast(error.message,"error")
});
>>>>>>> Stashed changes
};
const handleSubmit = (formData) => {
dispatch(markCurrentAttendance(formData))
.then((action) => {
const updatedAttendance = attendances.map((item) =>
item.employeeId === action.payload.employeeId
? { ...item, ...action.payload }
: item
);
cacheData("Attendance", {
data: updatedAttendance,
projectId: selectedProject,
});
setAttendances(updatedAttendance);
showToast("Attedance Marked Successfully", "success");
})
.catch((error) => {
showToast(error.message, "error");
});
};
useEffect(() => {
if (modelConfig !== null) {
openModel();
}
}, [modelConfig,isCreateModalOpen]);
useEffect(()=>{
setAttendances(attendance)
},[attendance])
useEffect( () =>
{
dispatch(setProjectId(projects[0]?.id))
},[projects])
}, [modelConfig, isCreateModalOpen]);
useEffect(() => {
setAttendances(attendance);
}, [attendance]);
useEffect(() => {
dispatch(setProjectId(projects[0]?.id));
}, [projects]);
return (
<>
{isCreateModalOpen && modelConfig && (
<div
className="modal fade show"
style={{ display: "block" }}
id="check-Out-modal"
tabindex="-1"
aria-hidden="true"
>
<AttendanceModel modelConfig={modelConfig} closeModal={closeModal} handleSubmitForm={handleSubmit}/>
{isCreateModalOpen && modelConfig && (
<div
className="modal fade show"
style={{ display: "block" }}
id="check-Out-modal"
tabindex="-1"
aria-hidden="true"
>
<AttendanceModel
modelConfig={modelConfig}
closeModal={closeModal}
handleSubmitForm={handleSubmit}
/>
</div>
)}
<div className="container-xxl flex-grow-1 container-p-y">
<Breadcrumb
)}
<div className="container-xxl flex-grow-1 container-p-y">
<Breadcrumb
data={[
{ label: "Home", link: "/dashboard" },
{ label: "Attendance", link: null },
]}
<<<<<<< Updated upstream
></Breadcrumb>
<div className="nav-align-top nav-tabs-shadow">
<ul className="nav nav-tabs" role="tablist">
<div
className="dataTables_length text-start py-2 px-2"
id="DataTables_Table_0_length"
>
{loginUser && loginUser?.projects.length > 1 && (
<label>
<select
name="DataTables_Table_0_length"
aria-controls="DataTables_Table_0"
className="form-select form-select-sm"
value={selectedProject}
onChange={(e) => dispatch(setProjectId(e.target.value))}
aria-label=""
>
{!projectLoading &&
projects
?.filter((project) =>
loginUser?.projects?.map(Number).includes(project.id)
)
.map((project) => (
<option value={project.id}>{project.name}</option>
))}
{projectLoading && (
<option value="Loading..." disabled>
Loading...
</option>
)}
</select>
</label>
)}
</div>
</ul>
<ul className="nav nav-tabs" role="tablist">
<li className="nav-item">
<button
type="button"
className="nav-link active"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-top-home"
aria-controls="navs-top-home"
aria-selected="true"
=======
></Breadcrumb>
<div class="nav-align-top nav-tabs-shadow">
<ul class="nav nav-tabs" role="tablist">
@ -124,7 +216,7 @@ const AttendancePage = () =>
id="DataTables_Table_0_length"
>
{
((loginUser && loginUser?.projects.length > 1) ) && (<label>
((loginUser && loginUser?.projects?.length > 1) ) && (<label>
<select
name="DataTables_Table_0_length"
aria-controls="DataTables_Table_0"
@ -132,44 +224,26 @@ const AttendancePage = () =>
value={selectedProject}
onChange={(e)=>dispatch(setProjectId(e.target.value))}
aria-label=""
>>>>>>> Stashed changes
>
{!projectLoading && projects?.filter(project =>
loginUser?.projects?.map(Number).includes(project.id)).map((project)=>(
<option value={project.id}>{project.name}</option>
))}
{projectLoading && <option value="Loading..." disabled>Loading...</option> }
</select>
</label>)
}
</div>
</ul>
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<button
type="button"
class="nav-link active"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-top-home"
aria-controls="navs-top-home"
aria-selected="true">
All
</button>
</li>
<li class="nav-item">
<button
type="button"
class="nav-link"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-top-profile"
aria-controls="navs-top-profile"
aria-selected="false">
Logs
</button>
</li>
<li className={`nav-item ${!DoRegularized && 'd-none'}`}>
All
</button>
</li>
<li className="nav-item">
<button
type="button"
className="nav-link"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-top-profile"
aria-controls="navs-top-profile"
aria-selected="false"
>
Logs
</button>
</li>
<li className={`nav-item ${!DoRegularized && "d-none"}`}>
<button
type="button"
className="nav-link "
@ -177,43 +251,55 @@ const AttendancePage = () =>
data-bs-toggle="tab"
data-bs-target="#navs-top-messages"
aria-controls="navs-top-messages"
aria-selected="false">
aria-selected="false"
>
Regularization
</button>
</li>
</ul>
<div class="tab-content attedanceTabs py-2">
{projectLoading && (<span>Loading..</span>)}
{(!projectLoading && !attendances) && <span>Not Found</span>}
{ (projects && projects.length > 0 ) && (
<>
<div className="tab-pane fade show active py-0" id="navs-top-home" role="tabpanel" key={projects.id}>
<Attendance attendance={attendances} handleModalData={handleModalData} getRole={getRole} />
</div>
<div class="tab-pane fade" id="navs-top-profile" role="tabpanel">
<AttendanceLog
attendance={attendances}
handleModalData={handleModalData}
projectId={selectedProject}
/>
</div>
<div className="tab-pane fade" id="navs-top-messages" role="tabpanel">
<Regularization
attendance={attendances}
handleRequest ={handleSubmit}
/>
</div>
</>
)}
</ul>
<div className="tab-content attedanceTabs py-2">
{projectLoading && <span>Loading..</span>}
{!projectLoading && !attendances && <span>Not Found</span>}
{projects && projects.length > 0 && (
<>
<div
className="tab-pane fade show active py-0"
id="navs-top-home"
role="tabpanel"
key={projects.id}
>
<Attendance
attendance={attendances}
handleModalData={handleModalData}
getRole={getRole}
/>
</div>
<div
className="tab-pane fade"
id="navs-top-profile"
role="tabpanel"
>
<AttendanceLog
attendance={attendances}
handleModalData={handleModalData}
projectId={selectedProject}
/>
</div>
<div
className="tab-pane fade"
id="navs-top-messages"
role="tabpanel"
>
<Regularization
attendance={attendances}
handleRequest={handleSubmit}
/>
</div>
</>
)}
</div>
</div>
</div>
</div>
</>
);
};

View File

@ -6,10 +6,10 @@ import { useProjects } from "../../hooks/useProjects";
import { setProjectId } from "../../slices/localVariablesSlice";
import { useProfile } from "../../hooks/useProfile";
import React, { useEffect, useState } from "react";
import {formatDate} from "../../utils/dateUtils";
import { formatDate } from "../../utils/dateUtils";
import GlobalModel from "../../components/common/GlobalModel";
import AssignRoleModel from "../../components/Project/AssignRole";
import {ReportTask} from "../../components/Activities/ReportTask";
import { ReportTask } from "../../components/Activities/ReportTask";
import ReportTaskComments from "../../components/Activities/ReportTaskComments";
const DailyTask = () => {
@ -22,59 +22,63 @@ const DailyTask = () => {
const selectedProject = useSelector(
(store) => store.localVariables.projectId
);
const dispatch = useDispatch( selectedProject );
const dispatch = useDispatch(selectedProject);
const {
TaskList,
loading: task_loading,
error: task_error,
refetch
refetch,
} = useTaskList(selectedProject);
const [TaskLists, setTaskLists] = useState([]);
useEffect(() => {
setTaskLists(TaskList);
}, [ TaskList, selectedProject ] );
const [ selectedTask, selectTask ] = useState( null )
const [comments,setComment] = useState(null)
}, [TaskList, selectedProject]);
const [selectedTask, selectTask] = useState(null);
const [comments, setComment] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isModalOpenComment, setIsModalOpenComment] = useState(false);
const [ isModalOpen, setIsModalOpen ] = useState( false );
const [ isModalOpenComment, setIsModalOpenComment ] = useState( false )
const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen( false );
const openComment = () =>setIsModalOpenComment(true)
const closeCommentModal =()=>setIsModalOpenComment(false)
const handletask = (task) =>
{
selectTask(task)
openModal()
}
const closeModal = () => setIsModalOpen(false);
const openComment = () => setIsModalOpenComment(true);
const closeCommentModal = () => setIsModalOpenComment(false);
const handletask = (task) => {
selectTask(task);
openModal();
};
return (
<>
<div
className={`modal fade ${isModalOpen ? 'show' : ''}`}
className={`modal fade ${isModalOpen ? "show" : ""}`}
tabIndex="-1"
role="dialog"
style={{ display: isModalOpen ? 'block' : 'none' }}
style={{ display: isModalOpen ? "block" : "none" }}
aria-hidden={!isModalOpen}
>
<ReportTask report={selectedTask} closeModal={ closeModal} refetch={refetch} />
<ReportTask
report={selectedTask}
closeModal={closeModal}
refetch={refetch}
/>
</div>
<div
className={`modal fade ${isModalOpenComment ? 'show' : ''}`}
className={`modal fade ${isModalOpenComment ? "show" : ""}`}
tabIndex="-1"
role="dialog"
style={{ display: isModalOpenComment ? 'block' : 'none' }}
style={{ display: isModalOpenComment ? "block" : "none" }}
aria-hidden={!isModalOpenComment}
>
<ReportTaskComments commentsData={comments} closeModal={ closeCommentModal} />
<ReportTaskComments
commentsData={comments}
closeModal={closeCommentModal}
/>
</div>
<div className="container-xxl flex-grow-1 container-p-y">
<Breadcrumb
data={[
@ -147,67 +151,75 @@ const DailyTask = () => {
return (
<React.Fragment key={index}>
{/* Main Row */}
<tr >
<tr>
<td>
<div className="d-flex justify-content-center align-items-center" data-bs-toggle="collapse"
data-bs-target={`#${accordionId}`}
aria-expanded="false"
<div
className="d-flex justify-content-center align-items-center"
data-bs-toggle="collapse"
data-bs-target={`#${accordionId}`}
aria-expanded="false"
aria-controls={accordionId}
>
<div className="d-flex flex-column">
<a
href="#"
className="text-heading text-truncate"
>
<i class='bx bx-chevron-right'></i>
<i className="bx bx-chevron-right"></i>
</a>
</div>
</div>
</td>
<td className="flex-wrap">
{task.workItem.activityMaster.activityName || "No Activity Name"}
{task.workItem.activityMaster.activityName ||
"No Activity Name"}
</td>
<td>{task.plannedTask || "NA"}</td>
<td>{task.completedTask}</td>
<td>{formatDate( task.assignmentDate )}</td>
<td>{formatDate(task.assignmentDate)}</td>
<td className="text-center">
<div class="d-flex align-items-center avatar-group justify-content-center">
{task.teamMembers.map( ( member ) => (
<div key={member.id} data-bs-toggle="tooltip" data-bs-html="true" data-popup="tooltip-custom" data-bs-placement="top" title={`${member.firstName} ${member.lastName}`} className="avatar avatar-xs">
{/* <img src="..." alt="Avatar" class="rounded-circle pull-up" /> */}
<span className="avatar-initial rounded-circle bg-label-primary">{member?.firstName.slice(0,1) }</span>
</div>
))}
<div className="d-flex align-items-center avatar-group justify-content-center">
{task.teamMembers.map((member) => (
<div
key={member.id}
data-bs-toggle="tooltip"
data-bs-html="true"
data-popup="tooltip-custom"
data-bs-placement="top"
title={`${member.firstName} ${member.lastName}`}
className="avatar avatar-xs"
>
{/* <img src="..." alt="Avatar" className="rounded-circle pull-up" /> */}
<span className="avatar-initial rounded-circle bg-label-primary">
{member?.firstName.slice(0, 1)}
</span>
</div>
))}
</div>
</td>
<td className="text-center">
<div className="d-flex justify-content-center">
<button
type="button"
className="btn btn-xs btn-primary"
onClick={() =>
{
selectTask( task )
openModal()
}}
>
Report
</button>
<button
type="button"
className="btn btn-xs btn-primary ms-2"
onClick={() =>
{
setComment( task )
openComment()
<button
type="button"
className="btn btn-xs btn-primary"
onClick={() => {
selectTask(task);
openModal();
}}
>
Comment
</button>
>
Report
</button>
<button
type="button"
className="btn btn-xs btn-primary ms-2"
onClick={() => {
setComment(task);
openComment();
}}
>
Comment
</button>
</div>
</td>
</tr>

View File

@ -1,7 +1,21 @@
import React from "react";
const ImageGallary = () => {
return <div>Image Gallery</div>;
return <div className="misc-wrapper">
<h2 className="mb-2 mx-2">Coming Soon!</h2>
<p className="mb-4 mx-2">We're currently working on this feature and will have it ready shortly.
Thank you for your patience!</p>
<div className="mt-4">
<img
src="../assets/img/illustrations/girl-doing-yoga-light.png"
alt="girl-doing-yoga-light"
aria-label="Girl doing yoga light"
width="500"
className="img-fluid"
data-app-dark-img="illustrations/girl-doing-yoga-dark.png"
data-app-light-img="illustrations/girl-doing-yoga-light.png" />
</div>
</div>;
};
export default ImageGallary;

View File

@ -107,7 +107,7 @@ const LoginPage = () => {
aria-describedby="password"
/>
<span
class="input-group-text cursor-pointer"
className="input-group-text cursor-pointer"
onClick={() => setHidepass(!hidepass)}
>
{hidepass ? (

View File

@ -118,7 +118,7 @@ const ResetPasswordPage = () => {
aria-describedby="password"
/>
<span
class="input-group-text lcursor-pointer"
className="input-group-text lcursor-pointer"
onClick={() => setHidepass(!hidepass)}
>
{hidepass ? (
@ -129,13 +129,13 @@ const ResetPasswordPage = () => {
</span>
</div>
{errors.password && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.password.message}
</div>
)}
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.password.message}
</div>
)}
<div className="mt-2">
{" "}
<label htmlFor="email" className="form-label">
@ -144,7 +144,7 @@ const ResetPasswordPage = () => {
</div>
<div className=" input-group input-group-merge">
<input
type={hidepass ? "password" : "text"}
type={hidepass ? "password" : "text"}
autoComplete="true"
id="password"
className="form-control"
@ -154,7 +154,7 @@ const ResetPasswordPage = () => {
aria-describedby="password"
/>
<span
class="input-group-text cursor-pointer"
className="input-group-text cursor-pointer"
onClick={() => setHidepass(!hidepass)}
>
{hidepass ? (
@ -165,13 +165,13 @@ const ResetPasswordPage = () => {
</span>
</div>
{errors.confirmPassword && (
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.confirmPassword.message}
</div>
)}
<div
className="danger-text text-start"
style={{ fontSize: "12px" }}
>
{errors.confirmPassword.message}
</div>
)}
</div>
<div className="mb-3 text-start ">

View File

@ -84,7 +84,21 @@ const EmployeeProfile = () => {
}
default:
return <div>Select a pill to display content here.</div>;
return <div className="misc-wrapper">
<h2 className="mb-2 mx-2">Coming Soon!</h2>
<p className="mb-4 mx-2">We're currently working on this feature and will have it ready shortly.
Thank you for your patience!</p>
<div className="mt-4">
<img
src="../assets/img/illustrations/girl-doing-yoga-light.png"
alt="girl-doing-yoga-light"
aria-label="Girl doing yoga light"
width="500"
className="img-fluid"
data-app-dark-img="illustrations/girl-doing-yoga-dark.png"
data-app-light-img="illustrations/girl-doing-yoga-light.png" />
</div>
</div>;
}
};

View File

@ -1,11 +1,21 @@
import React from "react";
const Inventory = () => {
return <div>Inventory
<div>
<p>ddj</p>
</div>
</div>;
return <div className="misc-wrapper">
<h2 className="mb-2 mx-2">Coming Soon!</h2>
<p className="mb-4 mx-2">We're currently working on this feature and will have it ready shortly.
Thank you for your patience!</p>
<div className="mt-4">
<img
src="../assets/img/illustrations/girl-doing-yoga-light.png"
alt="girl-doing-yoga-light"
aria-label="Girl doing yoga light"
width="500"
className="img-fluid"
data-app-dark-img="illustrations/girl-doing-yoga-dark.png"
data-app-light-img="illustrations/girl-doing-yoga-light.png" />
</div>
</div>;
};
export default Inventory;

View File

@ -149,7 +149,21 @@ const ProjectDetails = () => {
}
default:
return <div>Select a pill to display content here.</div>;
return <div className="misc-wrapper">
<h2 className="mb-2 mx-2">Coming Soon!</h2>
<p className="mb-4 mx-2">We're currently working on this feature and will have it ready shortly.
Thank you for your patience!</p>
<div className="mt-4">
<img
src="../assets/img/illustrations/girl-doing-yoga-light.png"
alt="girl-doing-yoga-light"
aria-label="Girl doing yoga light"
width="500"
className="img-fluid"
data-app-dark-img="illustrations/girl-doing-yoga-dark.png"
data-app-light-img="illustrations/girl-doing-yoga-light.png" />
</div>
</div>;
}
};

View File

@ -1,7 +1,21 @@
import React from "react";
const Reports = () => {
return <div>Reports</div>;
return <div className="misc-wrapper">
<h2 className="mb-2 mx-2">Coming Soon!</h2>
<p className="mb-4 mx-2">We're currently working on this feature and will have it ready shortly.
Thank you for your patience!</p>
<div className="mt-4">
<img
src="../assets/img/illustrations/girl-doing-yoga-light.png"
alt="girl-doing-yoga-light"
aria-label="Girl doing yoga light"
width="500"
className="img-fluid"
data-app-dark-img="illustrations/girl-doing-yoga-dark.png"
data-app-light-img="illustrations/girl-doing-yoga-light.png" />
</div>
</div>;
};
export default Reports;

View File

@ -1,7 +1,21 @@
import React from "react";
const Connect = () => {
return <div>Connect</div>;
return <div className="misc-wrapper">
<h2 className="mb-2 mx-2">Coming Soon!</h2>
<p className="mb-4 mx-2">We're currently working on this feature and will have it ready shortly.
Thank you for your patience!</p>
<div className="mt-4">
<img
src="../assets/img/illustrations/girl-doing-yoga-light.png"
alt="girl-doing-yoga-light"
aria-label="Girl doing yoga light"
width="500"
className="img-fluid"
data-app-dark-img="illustrations/girl-doing-yoga-dark.png"
data-app-light-img="illustrations/girl-doing-yoga-light.png" />
</div>
</div>;
};
export default Connect;

View File

@ -1,7 +1,21 @@
import React from "react";
const Documentation = () => {
return <div>Documentation</div>;
return <div className="misc-wrapper">
<h2 className="mb-2 mx-2">Coming Soon!</h2>
<p className="mb-4 mx-2">We're currently working on this feature and will have it ready shortly.
Thank you for your patience!</p>
<div className="mt-4">
<img
src="../assets/img/illustrations/girl-doing-yoga-light.png"
alt="girl-doing-yoga-light"
aria-label="Girl doing yoga light"
width="500"
className="img-fluid"
data-app-dark-img="illustrations/girl-doing-yoga-dark.png"
data-app-light-img="illustrations/girl-doing-yoga-light.png" />
</div>
</div>;
};
export default Documentation;

View File

@ -1,7 +1,21 @@
import React from "react";
const Support = () => {
return <div>Support</div>;
return <div className="misc-wrapper">
<h2 className="mb-2 mx-2">Coming Soon!</h2>
<p className="mb-4 mx-2">We're currently working on this feature and will have it ready shortly.
Thank you for your patience!</p>
<div className="mt-4">
<img
src="../assets/img/illustrations/girl-doing-yoga-light.png"
alt="girl-doing-yoga-light"
aria-label="Girl doing yoga light"
width="500"
className="img-fluid"
data-app-dark-img="illustrations/girl-doing-yoga-dark.png"
data-app-light-img="illustrations/girl-doing-yoga-light.png" />
</div>
</div>;
};
export default Support;

View File

@ -3,9 +3,13 @@ import AttendanceRepository from '../../repositories/AttendanceRepository';
export const markCurrentAttendance = createAsyncThunk(
'attendanceCurrentDate/markAttendance',
async (formData, { getState, dispatch, rejectWithValue }) => {
async ( formData, {getState, dispatch, rejectWithValue} ) =>
{
debugger
const { projectId } = getState().localVariables
try {
try
{
console.log(formData)
// Create the new attendance record
const newRecordAttendance = {
id: null,
@ -13,7 +17,7 @@ export const markCurrentAttendance = createAsyncThunk(
employeeID: formData.employeeId,
projectId: projectId,
date: new Date().toISOString(),
markTime: formData.time,
markTime: formData.markTime,
latitude: formData.latitude.toString(),
longitude: formData.longitude.toString(),
action: formData.action,

View File

@ -44,32 +44,40 @@ axiosClient.interceptors.response.use(
originalRequest._toastShown = true;
if (error.code === "ERR_CONNECTION_REFUSED") {
console.error("Connection refused. Please ensure the server is running.");
showToast("Unable to connect to the server. Please try again later.", "error");
console.error(
"Connection refused. Please ensure the server is running."
);
showToast(
"Unable to connect to the server. Please try again later.",
"error"
);
} else if (error.code === "ERR_NETWORK") {
console.error("Network error: Unable to reach the server.");
showToast("Server is unreachable. Try again later!", "error");
redirectToLogin();
} else if (error.code === "ECONNABORTED") {
console.error("Request timed out.");
showToast("The request took too long. Please try again later.", "error");
showToast(
"The request took too long. Please try again later.",
"error"
);
} else if (error.response) {
showToast(error.response.data.message,"error")
showToast(error.response.data.message, "error");
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
// debugger;
// Get the refresh token from secure storage
// debugger;
// Get the refresh token from secure storage
const refreshToken = localStorage.getItem("refreshToken");
if (!refreshToken) {
// Redirect to login if refresh token is not available
// Redirect to login if refresh token is not available
redirectToLogin();
return Promise.reject(error);
}
// Make a request to refresh the access token
// Make a request to refresh the access token
const response = await axiosClient.post("/api/Auth/refresh-token", {
token: localStorage.getItem("jwtToken"),
refreshToken,
@ -82,17 +90,19 @@ axiosClient.interceptors.response.use(
// Retry the original request with the new token
originalRequest.headers["Authorization"] = `Bearer ${token}`;
// Retry the original request
// Retry the original request
return axiosClient(originalRequest);
} catch (err) {
// Redirect to login if token refresh fails
// Redirect to login if token refresh fails
redirectToLogin();
return Promise.reject(err);
}
} else
{
showToast(error.response.data?.message || "An error occurred. Please try again.", "error");
} else {
showToast(
error.response.data?.message ||
"An error occurred. Please try again.",
"error"
);
}
} else {
console.error("An unknown error occurred:", error.message);