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

This commit is contained in:
Kartik Sharma 2025-09-05 11:44:27 +05:30
commit 8f8cbf27ef
48 changed files with 945 additions and 830 deletions

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useEmployeeAttendacesLog } from "../../hooks/useAttendance"; import { useEmployeeAttendacesLog } from "../../hooks/useAttendance";
import { convertShortTime } from "../../utils/dateUtils"; import { convertShortTime, formatUTCToLocalTime } from "../../utils/dateUtils";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { THRESH_HOLD } from "../../utils/constants"; import { THRESH_HOLD } from "../../utils/constants";
@ -128,7 +128,7 @@ const AttendLogs = ({ Id }) => {
<p> <p>
Attendance logs for{" "} Attendance logs for{" "}
{logs[0]?.employee?.firstName + " " + logs[0]?.employee?.lastName}{" "} {logs[0]?.employee?.firstName + " " + logs[0]?.employee?.lastName}{" "}
on {logs[0]?.activityTime.slice(0, 10)}{" "} on {formatUTCToLocalTime(logs[0]?.activityTime)}
</p> </p>
)} )}
</div> </div>
@ -156,7 +156,7 @@ const AttendLogs = ({ Id }) => {
.sort((a, b) => b.id - a.id) .sort((a, b) => b.id - a.id)
.map((log, index) => ( .map((log, index) => (
<tr key={index}> <tr key={index}>
<td>{log.activityTime.slice(0, 10)}</td> <td>{formatUTCToLocalTime(log.activityTime)}</td>
<td>{convertShortTime(log.activityTime)}</td> <td>{convertShortTime(log.activityTime)}</td>
<td> <td>
{whichActivityPerform(log.activity, log.activityTime)} {whichActivityPerform(log.activity, log.activityTime)}

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect, useCallback, useMemo } from "react"; import React, { useState, useEffect, useCallback, useMemo } from "react";
import moment from "moment"; import moment from "moment";
import Avatar from "../common/Avatar"; import Avatar from "../common/Avatar";
import { convertShortTime } from "../../utils/dateUtils"; import { convertShortTime, formatUTCToLocalTime } from "../../utils/dateUtils";
import RenderAttendanceStatus from "./RenderAttendanceStatus"; import RenderAttendanceStatus from "./RenderAttendanceStatus";
import usePagination from "../../hooks/usePagination"; import usePagination from "../../hooks/usePagination";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -10,7 +10,7 @@ import { useAttendance } from "../../hooks/useAttendance";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import eventBus from "../../services/eventBus"; import eventBus from "../../services/eventBus";
import { useSelectedproject } from "../../slices/apiDataManager"; import { useSelectedProject } from "../../slices/apiDataManager";
const Attendance = ({ getRole, handleModalData, searchTerm }) => { const Attendance = ({ getRole, handleModalData, searchTerm }) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -21,7 +21,7 @@ const Attendance = ({ getRole, handleModalData, searchTerm }) => {
// const selectedProject = useSelector( // const selectedProject = useSelector(
// (store) => store.localVariables.projectId // (store) => store.localVariables.projectId
// ); // );
const selectedProject = useSelectedproject(); const selectedProject = useSelectedProject();
const { const {
attendance, attendance,
loading: attLoading, loading: attLoading,
@ -116,7 +116,7 @@ const Attendance = ({ getRole, handleModalData, searchTerm }) => {
<> <>
<div className="table-responsive text-nowrap h-100" > <div className="table-responsive text-nowrap h-100" >
<div className="d-flex text-start align-items-center py-2"> <div className="d-flex text-start align-items-center py-2">
<strong>Date : {todayDate.toLocaleDateString("en-GB")}</strong> <strong>Date : {formatUTCToLocalTime(todayDate)}</strong>
<div className="form-check form-switch text-start m-0 ms-5"> <div className="form-check form-switch text-start m-0 ms-5">
<input <input
type="checkbox" type="checkbox"

View File

@ -6,11 +6,12 @@ import RenderAttendanceStatus from "./RenderAttendanceStatus";
import { useSelector, useDispatch } from "react-redux"; import { useSelector, useDispatch } from "react-redux";
import { fetchAttendanceData } from "../../slices/apiSlice/attedanceLogsSlice"; import { fetchAttendanceData } from "../../slices/apiSlice/attedanceLogsSlice";
import DateRangePicker from "../common/DateRangePicker"; import DateRangePicker from "../common/DateRangePicker";
import { clearCacheKey, getCachedData, useSelectedproject } from "../../slices/apiDataManager"; import { clearCacheKey, getCachedData, useSelectedProject } from "../../slices/apiDataManager";
import eventBus from "../../services/eventBus"; import eventBus from "../../services/eventBus";
import AttendanceRepository from "../../repositories/AttendanceRepository"; import AttendanceRepository from "../../repositories/AttendanceRepository";
import { useAttendancesLogs } from "../../hooks/useAttendance"; import { useAttendancesLogs } from "../../hooks/useAttendance";
import { queryClient } from "../../layouts/AuthLayout"; import { queryClient } from "../../layouts/AuthLayout";
import { ITEMS_PER_PAGE } from "../../utils/constants";
const usePagination = (data, itemsPerPage) => { const usePagination = (data, itemsPerPage) => {
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
@ -37,7 +38,7 @@ const AttendanceLog = ({ handleModalData, searchTerm }) => {
// const selectedProject = useSelector( // const selectedProject = useSelector(
// (store) => store.localVariables.projectId // (store) => store.localVariables.projectId
// ); // );
const selectedProject = useSelectedproject(); const selectedProject = useSelectedProject();
const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" });
const dispatch = useDispatch(); const dispatch = useDispatch();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -353,7 +354,7 @@ const AttendanceLog = ({ handleModalData, searchTerm }) => {
<span className="text-secondary">No Pending Record Available !</span> <span className="text-secondary">No Pending Record Available !</span>
</div> </div>
)} )}
{filteredSearchData.length > 10 && ( {filteredSearchData.length > ITEMS_PER_PAGE && (
<nav aria-label="Page "> <nav aria-label="Page ">
<ul className="pagination pagination-sm justify-content-end py-1"> <ul className="pagination pagination-sm justify-content-end py-1">
<li className={`page-item ${currentPage === 1 ? "disabled" : ""}`}> <li className={`page-item ${currentPage === 1 ? "disabled" : ""}`}>

View File

@ -9,7 +9,7 @@ import { markAttendance } from "../../slices/apiSlice/attedanceLogsSlice";
import showToast from "../../services/toastService"; import showToast from "../../services/toastService";
import { checkIfCurrentDate } from "../../utils/dateUtils"; import { checkIfCurrentDate } from "../../utils/dateUtils";
import { useMarkAttendance } from "../../hooks/useAttendance"; import { useMarkAttendance } from "../../hooks/useAttendance";
import { useSelectedproject } from "../../slices/apiDataManager"; import { useSelectedProject } from "../../slices/apiDataManager";
const createSchema = (modeldata) => { const createSchema = (modeldata) => {
return z return z
@ -43,9 +43,8 @@ const createSchema = (modeldata) => {
}); });
}; };
const CheckCheckOutmodel = ({ modeldata, closeModal, handleSubmitForm }) => { const CheckInCheckOut = ({ modeldata, closeModal, handleSubmitForm }) => {
// const projectId = useSelector((store) => store.localVariables.projectId); const projectId = useSelectedProject();
const projectId = useSelectedproject();
const { mutate: MarkAttendance } = useMarkAttendance(); const { mutate: MarkAttendance } = useMarkAttendance();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const coords = usePositionTracker(); const coords = usePositionTracker();
@ -174,7 +173,7 @@ const CheckCheckOutmodel = ({ modeldata, closeModal, handleSubmitForm }) => {
); );
}; };
export default CheckCheckOutmodel; export default CheckInCheckOut;
const schemaReg = z.object({ const schemaReg = z.object({
description: z.string().min(1, { message: "please give reason!" }), description: z.string().min(1, { message: "please give reason!" }),

View File

@ -15,7 +15,7 @@ import {useDispatch, useSelector} from "react-redux";
import {useProfile} from "../../hooks/useProfile"; import {useProfile} from "../../hooks/useProfile";
import {refreshData, setProjectId} from "../../slices/localVariablesSlice"; import {refreshData, setProjectId} from "../../slices/localVariablesSlice";
import InfraTable from "../Project/Infrastructure/InfraTable"; import InfraTable from "../Project/Infrastructure/InfraTable";
import { useSelectedproject } from "../../slices/apiDataManager"; import { useSelectedProject } from "../../slices/apiDataManager";
import Loader from "../common/Loader"; import Loader from "../common/Loader";
@ -24,7 +24,7 @@ const InfraPlanning = () =>
const {profile: LoggedUser, refetch : fetchData} = useProfile() const {profile: LoggedUser, refetch : fetchData} = useProfile()
const dispatch = useDispatch() const dispatch = useDispatch()
// const selectedProject = useSelector((store)=>store.localVariables.projectId) // const selectedProject = useSelector((store)=>store.localVariables.projectId)
const selectedProject = useSelectedproject(); const selectedProject = useSelectedProject();
const {projectInfra, isLoading, error} = useProjectInfra( selectedProject ) const {projectInfra, isLoading, error} = useProjectInfra( selectedProject )
@ -35,15 +35,15 @@ const InfraPlanning = () =>
const reloadedData = useSelector( ( store ) => store.localVariables.reload ) const reloadedData = useSelector( ( store ) => store.localVariables.reload )
useEffect( () => // useEffect( () =>
{ // {
if (reloadedData) // if (reloadedData)
{ // {
refetch() // refetch()
dispatch( refreshData( false ) ) // dispatch( refreshData( false ) )
} // }
},[reloadedData]) // },[reloadedData])
return ( return (
<div className="col-md-12 col-lg-12 col-xl-12 order-0 mb-4"> <div className="col-md-12 col-lg-12 col-xl-12 order-0 mb-4">

View File

@ -7,13 +7,13 @@ import { useRegularizationRequests } from "../../hooks/useAttendance";
import moment from "moment"; import moment from "moment";
import usePagination from "../../hooks/usePagination"; import usePagination from "../../hooks/usePagination";
import eventBus from "../../services/eventBus"; import eventBus from "../../services/eventBus";
import { cacheData, clearCacheKey, useSelectedproject } from "../../slices/apiDataManager"; import { cacheData, clearCacheKey, useSelectedProject } from "../../slices/apiDataManager";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
const Regularization = ({ handleRequest, searchTerm }) => { const Regularization = ({ handleRequest, searchTerm }) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
// var selectedProject = useSelector((store) => store.localVariables.projectId); // var selectedProject = useSelector((store) => store.localVariables.projectId);
const selectedProject = useSelectedproject(); const selectedProject = useSelectedProject();
const [regularizesList, setregularizedList] = useState([]); const [regularizesList, setregularizedList] = useState([]);
const { regularizes, loading, error, refetch } = const { regularizes, loading, error, refetch } =
useRegularizationRequests(selectedProject); useRegularizationRequests(selectedProject);

View File

@ -4,7 +4,7 @@ import useAttendanceStatus, { ACTIONS } from '../../hooks/useAttendanceStatus';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { usePositionTracker } from '../../hooks/usePositionTracker'; import { usePositionTracker } from '../../hooks/usePositionTracker';
import {markCurrentAttendance} from '../../slices/apiSlice/attendanceAllSlice'; import {markCurrentAttendance} from '../../slices/apiSlice/attendanceAllSlice';
import {cacheData, getCachedData, useSelectedproject} from '../../slices/apiDataManager'; import {cacheData, getCachedData, useSelectedProject} from '../../slices/apiDataManager';
import showToast from '../../services/toastService'; import showToast from '../../services/toastService';
import { useMarkAttendance } from '../../hooks/useAttendance'; import { useMarkAttendance } from '../../hooks/useAttendance';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
@ -18,7 +18,7 @@ const {mutate:MarkAttendance,isPending} = useMarkAttendance()
const queryClient = useQueryClient() const queryClient = useQueryClient()
// const projectId = useSelector((store)=>store.localVariables.projectId) // const projectId = useSelector((store)=>store.localVariables.projectId)
const projectId = useSelectedproject(); const projectId = useSelectedProject();
const {latitude,longitude} = usePositionTracker(); const {latitude,longitude} = usePositionTracker();
const dispatch = useDispatch() const dispatch = useDispatch()

View File

@ -1,8 +1,16 @@
import React, { useEffect, useState, useMemo } from "react"; import React, { useEffect, useState, useMemo } from "react";
import { DirectoryRepository } from "../../repositories/DirectoryRepository"; import { DirectoryRepository } from "../../repositories/DirectoryRepository";
import NoteCardDirectoryEditable from "./NoteCardDirectoryEditable"; import NoteCardDirectoryEditable from "./NoteCardDirectoryEditable";
import { useSelectedProject } from "../../slices/apiDataManager";
const NotesCardViewDirectory = ({
notes,
setNotesForFilter,
searchText,
filterAppliedNotes,
}) => {
const projectId = useSelectedProject();
const NotesCardViewDirectory = ({ notes, setNotesForFilter, searchText, filterAppliedNotes }) => {
const [allNotes, setAllNotes] = useState([]); const [allNotes, setAllNotes] = useState([]);
const [filteredNotes, setFilteredNotes] = useState([]); const [filteredNotes, setFilteredNotes] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@ -13,13 +21,15 @@ const NotesCardViewDirectory = ({ notes, setNotesForFilter, searchText, filterAp
const pageSize = 20; const pageSize = 20;
useEffect(() => { useEffect(() => {
fetchNotes(); if (projectId) {
}, []); fetchNotes(projectId);
}
}, [projectId]);
const fetchNotes = async () => { const fetchNotes = async (projId) => {
setLoading(true); setLoading(true);
try { try {
const response = await DirectoryRepository.GetNotes(1000, 1); const response = await DirectoryRepository.GetNotes(1000, 1, projId); // pass projectId
const fetchedNotes = response.data?.data || []; const fetchedNotes = response.data?.data || [];
setAllNotes(fetchedNotes); setAllNotes(fetchedNotes);
setNotesForFilter(fetchedNotes) setNotesForFilter(fetchedNotes)
@ -122,7 +132,7 @@ const NotesCardViewDirectory = ({ notes, setNotesForFilter, searchText, filterAp
prevNotes.map((n) => (n.id === updatedNote.id ? updatedNote : n)) prevNotes.map((n) => (n.id === updatedNote.id ? updatedNote : n))
); );
}} }}
onNoteDelete={() => fetchNotes()} onNoteDelete={() => fetchNotes(projectId)} // reload with projectId
/> />
))} ))}
</div> </div>

View File

@ -126,7 +126,7 @@ const EmpAttendance = ({ employee }) => {
className="dataTables_length text-start py-2 d-flex justify-content-between " className="dataTables_length text-start py-2 d-flex justify-content-between "
id="DataTables_Table_0_length" id="DataTables_Table_0_length"
> >
<div className="col-md-3 my-0 "> <div className="col-md-4 my-0 ">
<DateRangePicker <DateRangePicker
DateDifference="30" DateDifference="30"
onRangeChange={setDateRange} onRangeChange={setDateRange}

View File

@ -59,20 +59,20 @@ const EmpBanner = ({ profile, loggedInUser }) => {
</h4> </h4>
<ul className="list-inline mb-0 d-flex align-items-center flex-wrap justify-content-sm-start justify-content-center gap-4 mt-4"> <ul className="list-inline mb-0 d-flex align-items-center flex-wrap justify-content-sm-start justify-content-center gap-4 mt-4">
<li className="list-inline-item"> <li className="list-inline-item">
<i className="icon-base bx bx-crown me-2 align-top"></i> <i className="icon-base bx bx-crown me-1 align-top"></i>
<span className="fw-medium"> <span className="fw-medium">
{profile?.jobRole || <em>NA</em>} {profile?.jobRole || <em>NA</em>}
</span> </span>
</li> </li>
<li className="list-inline-item"> <li className="list-inline-item">
<i className="icon-base bx bx-phone me-2 align-top"></i> <i className="icon-base bx bx-phone me-0 align-top"></i>
<span className="fw-medium"> <span className="fw-medium">
{" "} {" "}
{profile?.phoneNumber || <em>NA</em>} {profile?.phoneNumber || <em>NA</em>}
</span> </span>
</li> </li>
<li className="list-inline-item"> <li className="list-inline-item">
<i className="icon-base bx bx-calendar me-2 align-top"></i> <i className="icon-base bx bx-calendar me-0 align-top"></i>
<span className="fw-medium"> <span className="fw-medium">
{" "} {" "}
Joined on{" "} Joined on{" "}
@ -85,18 +85,21 @@ const EmpBanner = ({ profile, loggedInUser }) => {
</li> </li>
</ul> </ul>
<ul className="list-inline mb-0 d-flex align-items-center flex-wrap justify-content-sm-start justify-content-center mt-4"> <ul className="list-inline mb-0 d-flex align-items-center flex-wrap justify-content-sm-start justify-content-center mt-4">
<li className="list-inline-item"> {profile?.isActive && ( // show only if active
<button <li className="list-inline-item">
className="btn btn-sm btn-primary btn-block"
onClick={() => setShowModal(true)}
>
Edit Profile
</button>
</li>
<li className="list-inline-item">
{profile?.id == loggedInUser?.employeeInfo?.id && (
<button <button
className="btn btn-sm btn-outline-primary btn-block" className="btn btn-sm btn-primary btn-block"
onClick={() => setShowModal(true)}
>
Edit Profile
</button>
</li>
)}
<li className="list-inline-item">
{profile?.id === loggedInUser?.employeeInfo?.id && (
<button
className="btn btn-sm btn-outline-primary btn-block"
onClick={() => openChangePassword()} onClick={() => openChangePassword()}
> >
Change Password Change Password

View File

@ -105,6 +105,7 @@ const ExpenseFilterPanel = ({ onApply, handleGroupBy }) => {
startField="startDate" startField="startDate"
endField="endDate" endField="endDate"
resetSignal={resetKey} resetSignal={resetKey}
defaultRange={false}
/> />
</div> </div>

View File

@ -3,7 +3,7 @@ import {
cacheData, cacheData,
clearAllCache, clearAllCache,
getCachedData, getCachedData,
useSelectedproject, useSelectedProject,
} from "../../slices/apiDataManager"; } from "../../slices/apiDataManager";
import AuthRepository from "../../repositories/AuthRepository"; import AuthRepository from "../../repositories/AuthRepository";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
@ -101,7 +101,7 @@ const Header = () => {
const { projectNames, loading: projectLoading, fetchData } = useProjectName(); const { projectNames, loading: projectLoading, fetchData } = useProjectName();
const selectedProject = useSelectedproject(); const selectedProject = useSelectedProject();
const projectsForDropdown = isDashboardPath const projectsForDropdown = isDashboardPath
? projectNames ? projectNames

View File

@ -8,7 +8,7 @@ import { MANAGE_PROJECT } from "../../utils/constants";
import GlobalModel from "../common/GlobalModel"; import GlobalModel from "../common/GlobalModel";
import ManageProjectInfo from "./ManageProjectInfo"; import ManageProjectInfo from "./ManageProjectInfo";
import { useQueryClient } from "@tanstack/react-query"; import { useQueryClient } from "@tanstack/react-query";
import { useSelectedproject } from "../../slices/apiDataManager"; import { useSelectedProject } from "../../slices/apiDataManager";
const AboutProject = () => { const AboutProject = () => {
const [IsOpenModal, setIsOpenModal] = useState(false); const [IsOpenModal, setIsOpenModal] = useState(false);
@ -21,7 +21,7 @@ const AboutProject = () => {
// *** MODIFIED LINE: Get projectId from Redux store using useSelector *** // *** MODIFIED LINE: Get projectId from Redux store using useSelector ***
// const projectId = useSelector((store) => store.localVariables.projectId); // const projectId = useSelector((store) => store.localVariables.projectId);
const projectId = useSelectedproject(); const projectId = useSelectedProject();
const manageProject = useHasUserPermission(MANAGE_PROJECT); const manageProject = useHasUserPermission(MANAGE_PROJECT);
const { projects_Details, isLoading, error, refetch } = useProjectDetails(projectId); // Pass projectId from useSelector const { projects_Details, isLoading, error, refetch } = useProjectDetails(projectId); // Pass projectId from useSelector

View File

@ -15,7 +15,7 @@ import {
cacheData, cacheData,
clearCacheKey, clearCacheKey,
getCachedData, getCachedData,
useSelectedproject, useSelectedProject,
} from "../../slices/apiDataManager"; } from "../../slices/apiDataManager";
import { useProjectDetails, useProjectInfra } from "../../hooks/useProjects"; import { useProjectDetails, useProjectInfra } from "../../hooks/useProjects";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
@ -27,7 +27,7 @@ import GlobalModel from "../common/GlobalModel";
const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) => const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) =>
{ {
// const projectId = useSelector((store)=>store.localVariables.projectId) // const projectId = useSelector((store)=>store.localVariables.projectId)
const projectId = useSelectedproject(); const projectId = useSelectedProject();
const reloadedData = useSelector((store) => store.localVariables.reload); const reloadedData = useSelector((store) => store.localVariables.reload);
const [ expandedBuildings, setExpandedBuildings ] = useState( [] ); const [ expandedBuildings, setExpandedBuildings ] = useState( [] );
const {projectInfra,isLoading,error} = useProjectInfra(projectId) const {projectInfra,isLoading,error} = useProjectInfra(projectId)

View File

@ -18,12 +18,12 @@ import {
useEmployeesByProjectAllocated, useEmployeesByProjectAllocated,
useManageProjectAllocation, useManageProjectAllocation,
} from "../../hooks/useProjects"; } from "../../hooks/useProjects";
import { useSelectedproject } from "../../slices/apiDataManager"; import { useSelectedProject } from "../../slices/apiDataManager";
const Teams = () => { const Teams = () => {
// const {projectId} = useParams() // const {projectId} = useParams()
// const projectId = useSelector((store)=>store.localVariables.projectId) // const projectId = useSelector((store)=>store.localVariables.projectId)
const projectId = useSelectedproject(); const projectId = useSelectedProject();
const dispatch = useDispatch(); const dispatch = useDispatch();
const { data, loading } = useMaster(); const { data, loading } = useMaster();

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import Label from '../common/Label'; import Label from '../common/Label';
import { useFormContext,useForm,FormProvider } from 'react-hook-form'; import { useFormContext, useForm, FormProvider } from 'react-hook-form';
import { useIndustries, useTenantDetails, useUpdateTenantDetails } from '../../hooks/useTenant'; import { useIndustries, useTenantDetails, useUpdateTenantDetails } from '../../hooks/useTenant';
import { orgSize, reference } from '../../utils/constants'; import { orgSize, reference } from '../../utils/constants';
import { LogoUpload } from './LogoUpload'; import { LogoUpload } from './LogoUpload';
@ -8,18 +8,18 @@ import showToast from '../../services/toastService';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { EditTenant } from './TenantSchema'; import { EditTenant } from './TenantSchema';
const EditProfile = ({ TenantId,onClose }) => { const EditProfile = ({ TenantId, onClose }) => {
const { data, isLoading, isError, error } = useTenantDetails(TenantId); const { data, isLoading, isError, error } = useTenantDetails(TenantId);
const [logoPreview, setLogoPreview] = useState(null); const [logoPreview, setLogoPreview] = useState(null);
const [logoName, setLogoName] = useState(""); const [logoName, setLogoName] = useState("");
const { data: Industries, isLoading: industryLoading, isError: industryError } = useIndustries(); const { data: Industries, isLoading: industryLoading, isError: industryError } = useIndustries();
const {mutate:UpdateTenant,isPending,} = useUpdateTenantDetails(()=>{ const { mutate: UpdateTenant, isPending, } = useUpdateTenantDetails(() => {
showToast("Tenant Details Updated Successfully","success") showToast("Tenant Details Updated Successfully", "success")
onClose() onClose()
}) })
const methods = useForm({ const methods = useForm({
resolver:zodResolver(EditTenant), resolver: zodResolver(EditTenant),
defaultValues: { defaultValues: {
firstName: "", firstName: "",
lastName: "", lastName: "",
@ -40,8 +40,8 @@ const EditProfile = ({ TenantId,onClose }) => {
const { register, reset, handleSubmit, formState: { errors } } = methods; const { register, reset, handleSubmit, formState: { errors } } = methods;
const onSubmit = (formData) => { const onSubmit = (formData) => {
const tenantPayload = {...formData,contactName:`${formData.firstName} ${formData.lastName}`,id:data.id,} const tenantPayload = { ...formData, contactName: `${formData.firstName} ${formData.lastName}`, id: data.id, }
UpdateTenant({id:data.id,tenantPayload}) UpdateTenant({ id: data.id, tenantPayload })
}; };
useEffect(() => { useEffect(() => {
@ -70,117 +70,117 @@ const EditProfile = ({ TenantId,onClose }) => {
return ( return (
<FormProvider {...methods}> <FormProvider {...methods}>
<form className="row g-6" onSubmit={handleSubmit(onSubmit)}> <form className="row g-6" onSubmit={handleSubmit(onSubmit)}>
<h6>Edit Tenant</h6> <h6>Edit Tenant</h6>
<div className="col-sm-6 mt-1"> <div className="col-sm-6 mt-1">
<Label htmlFor="firstName" required>First Name</Label> <Label htmlFor="firstName" required>First Name</Label>
<input id="firstName" type="text" className="form-control form-control-sm" {...register("firstName")} inputMode='text' /> <input id="firstName" type="text" className="form-control form-control-sm" {...register("firstName")} inputMode='text' />
{errors.firstName && <div className="danger-text">{errors.firstName.message}</div>} {errors.firstName && <div className="danger-text">{errors.firstName.message}</div>}
</div> </div>
<div className="col-sm-6 mt-1"> <div className="col-sm-6 mt-1">
<Label htmlFor="lastName" required>Last Name</Label> <Label htmlFor="lastName" required>Last Name</Label>
<input id="lastName" type="text" className="form-control form-control-sm" {...register("lastName")} /> <input id="lastName" type="text" className="form-control form-control-sm" {...register("lastName")} />
{errors.lastName && <div className="danger-text">{errors.lastName.message}</div>} {errors.lastName && <div className="danger-text">{errors.lastName.message}</div>}
</div> </div>
<div className="col-sm-6 mt-1"> <div className="col-sm-6 mt-1">
<Label htmlFor="contactNumber" required>Contact Number</Label> <Label htmlFor="contactNumber" required>Contact Number</Label>
<input id="contactNumber" type="text" className="form-control form-control-sm" {...register("contactNumber")} inputMode="tel" <input id="contactNumber" type="text" className="form-control form-control-sm" {...register("contactNumber")} inputMode="tel"
placeholder="+91 9876543210" /> placeholder="+91 9876543210" />
{errors.contactNumber && <div className="danger-text">{errors.contactNumber.message}</div>} {errors.contactNumber && <div className="danger-text">{errors.contactNumber.message}</div>}
</div> </div>
<div className="col-sm-6 mt-1"> <div className="col-sm-6 mt-1">
<Label htmlFor="domainName" required>Domain Name</Label> <Label htmlFor="domainName" >Domain Name</Label>
<input id="domainName" type="text" className="form-control form-control-sm" {...register("domainName")} /> <input id="domainName" type="text" className="form-control form-control-sm" {...register("domainName")} />
{errors.domainName && <div className="danger-text">{errors.domainName.message}</div>} {errors.domainName && <div className="danger-text">{errors.domainName.message}</div>}
</div> </div>
<div className="col-sm-6 mt-1"> <div className="col-sm-6 mt-1">
<Label htmlFor="taxId" required>Tax ID</Label> <Label htmlFor="taxId" >Tax ID</Label>
<input id="taxId" type="text" className="form-control form-control-sm" {...register("taxId")} /> <input id="taxId" type="text" className="form-control form-control-sm" {...register("taxId")} />
{errors.taxId && <div className="danger-text">{errors.taxId.message}</div>} {errors.taxId && <div className="danger-text">{errors.taxId.message}</div>}
</div> </div>
<div className="col-sm-6 mt-1"> <div className="col-sm-6 mt-1">
<Label htmlFor="officeNumber" required>Office Number</Label> <Label htmlFor="officeNumber" >Office Number</Label>
<input id="officeNumber" type="text" className="form-control form-control-sm" {...register("officeNumber")} /> <input id="officeNumber" type="text" className="form-control form-control-sm" {...register("officeNumber")} />
{errors.officeNumber && <div className="danger-text">{errors.officeNumber.message}</div>} {errors.officeNumber && <div className="danger-text">{errors.officeNumber.message}</div>}
</div> </div>
<div className="col-sm-6 mt-1"> <div className="col-sm-6 mt-1">
<Label htmlFor="industryId" required>Industry</Label> <Label htmlFor="industryId" required>Industry</Label>
<select className="form-select form-select-sm" {...register("industryId")}> <select className="form-select form-select-sm" {...register("industryId")}>
{industryLoading ? <option value="">Loading...</option> : {industryLoading ? <option value="">Loading...</option> :
Industries?.map((indu) => ( Industries?.map((indu) => (
<option key={indu.id} value={indu.id}>{indu.name}</option> <option key={indu.id} value={indu.id}>{indu.name}</option>
)) ))
} }
</select> </select>
{errors.industryId && <div className="danger-text">{errors.industryId.message}</div>} {errors.industryId && <div className="danger-text">{errors.industryId.message}</div>}
</div> </div>
<div className="col-sm-6 mt-1"> <div className="col-sm-6 mt-1">
<Label htmlFor="reference">Reference</Label> <Label htmlFor="reference">Reference</Label>
<select className="form-select form-select-sm" {...register("reference")}> <select className="form-select form-select-sm" {...register("reference")}>
{reference.map((org) => ( {reference.map((org) => (
<option key={org.val} value={org.val}>{org.name}</option> <option key={org.val} value={org.val}>{org.name}</option>
))} ))}
</select> </select>
{errors.reference && <div className="danger-text">{errors.reference.message}</div>} {errors.reference && <div className="danger-text">{errors.reference.message}</div>}
</div> </div>
<div className="col-sm-6"> <div className="col-sm-6">
<Label htmlFor="organizationSize" required> <Label htmlFor="organizationSize" required>
Organization Size Organization Size
</Label> </Label>
<select <select
className="form-select form-select-sm" className="form-select form-select-sm"
{...register("organizationSize")} {...register("organizationSize")}
> >
{orgSize.map((org) => ( {orgSize.map((org) => (
<option key={org.val} value={org.val}> <option key={org.val} value={org.val}>
{org.name} {org.name}
</option> </option>
))} ))}
</select> </select>
{errors.organizationSize && ( {errors.organizationSize && (
<div className="danger-text">{errors.organizationSize.message}</div> <div className="danger-text">{errors.organizationSize.message}</div>
)} )}
</div> </div>
<div className="col-12 mt-1"> <div className="col-12 mt-1">
<Label htmlFor="billingAddress" required>Billing Address</Label> <Label htmlFor="billingAddress" required>Billing Address</Label>
<textarea id="billingAddress" className="form-control" {...register("billingAddress")} rows={2} /> <textarea id="billingAddress" className="form-control" {...register("billingAddress")} rows={2} />
{errors.billingAddress && <div className="danger-text">{errors.billingAddress.message}</div>} {errors.billingAddress && <div className="danger-text">{errors.billingAddress.message}</div>}
</div> </div>
<div className="col-12 mt-1"> <div className="col-12 mt-1">
<Label htmlFor="description">Description</Label> <Label htmlFor="description">Description</Label>
<textarea id="description" className="form-control" {...register("description")} rows={2} /> <textarea id="description" className="form-control" {...register("description")} rows={2} />
{errors.description && <div className="danger-text">{errors.description.message}</div>} {errors.description && <div className="danger-text">{errors.description.message}</div>}
</div> </div>
<div className="col-sm-12"> <div className="col-sm-12">
<Label htmlFor="logImage">Logo Image</Label> <Label htmlFor="logImage">Logo Image</Label>
<LogoUpload <LogoUpload
preview={logoPreview} preview={logoPreview}
setPreview={setLogoPreview} setPreview={setLogoPreview}
fileName={logoName} fileName={logoName}
setFileName={setLogoName} setFileName={setLogoName}
/> />
</div> </div>
<div className="d-flex justify-content-center gap-2 mt-3"> <div className="d-flex justify-content-center gap-2 mt-3">
<button type="submit" disabled={isPending} className="btn btn-sm btn-primary">{isPending ? "Please Wait..." : "Submit"}</button> <button type="submit" disabled={isPending} className="btn btn-sm btn-primary">{isPending ? "Please Wait..." : "Submit"}</button>
<button type="button" disabled={isPending} className="btn btn-sm btn-secondary" onClick={onClose}>Cancel</button> <button type="button" disabled={isPending} className="btn btn-sm btn-secondary" onClick={onClose}>Cancel</button>
</div> </div>
</form> </form>
</FormProvider> </FormProvider>
); );
}; };

View File

@ -48,7 +48,7 @@ const OrganizationInfo = ({ onNext, onPrev, onSubmitTenant }) => {
const data = getValues(); const data = getValues();
// onSubmitTenant(data); // onSubmitTenant(data);
// onNext(); // onNext();
const tenantPayload = {...data,onBoardingDate: moment.utc(data.onBoardingDate, "DD-MM-YYYY").toISOString() } const tenantPayload = { ...data, onBoardingDate: moment.utc(data.onBoardingDate, "DD-MM-YYYY").toISOString() }
CreateTenant(tenantPayload); CreateTenant(tenantPayload);
} }
}; };
@ -73,7 +73,7 @@ const OrganizationInfo = ({ onNext, onPrev, onSubmitTenant }) => {
</div> </div>
<div className="col-sm-6"> <div className="col-sm-6">
<Label htmlFor="officeNumber" required> <Label htmlFor="officeNumber" >
Office Number Office Number
</Label> </Label>
<input <input
@ -87,7 +87,7 @@ const OrganizationInfo = ({ onNext, onPrev, onSubmitTenant }) => {
</div> </div>
<div className="col-sm-6"> <div className="col-sm-6">
<Label htmlFor="domainName" required> <Label htmlFor="domainName" >
Domain Name Domain Name
</Label> </Label>
<input <input
@ -101,7 +101,7 @@ const OrganizationInfo = ({ onNext, onPrev, onSubmitTenant }) => {
</div> </div>
<div className="col-sm-6"> <div className="col-sm-6">
<Label htmlFor="taxId" required> <Label htmlFor="taxId" >
Tax ID Tax ID
</Label> </Label>
<input <input
@ -138,8 +138,10 @@ const OrganizationInfo = ({ onNext, onPrev, onSubmitTenant }) => {
</Label> </Label>
<select <select
className="form-select form-select-sm" id="organizationSize"
{...register("organizationSize")} className="form-select shadow-none border py-1 px-2"
style={{ fontSize: "0.875rem" }} // Bootstrap's small text size
{...register("organizationSize", { required: "Organization size is required" })}
> >
{orgSize.map((org) => ( {orgSize.map((org) => (
<option key={org.val} value={org.val}> <option key={org.val} value={org.val}>
@ -147,17 +149,20 @@ const OrganizationInfo = ({ onNext, onPrev, onSubmitTenant }) => {
</option> </option>
))} ))}
</select> </select>
{errors.organizationSize && ( {errors.organizationSize && (
<div className="danger-text">{errors.organizationSize.message}</div> <div className="danger-text">{errors.organizationSize.message}</div>
)} )}
</div> </div>
<div className="col-sm-6"> <div className="col-sm-6">
<Label htmlFor="industryId" required> <Label htmlFor="industryId" required>
Industry Industry
</Label> </Label>
<select <select
className="form-select form-select-sm" id="industryId"
className="form-select shadow-none border py-1 px-2 small"
{...register("industryId")} {...register("industryId")}
> >
{industryLoading ? ( {industryLoading ? (
@ -177,9 +182,9 @@ const OrganizationInfo = ({ onNext, onPrev, onSubmitTenant }) => {
<div className="col-sm-6"> <div className="col-sm-6">
<Label htmlFor="reference">Reference</Label> <Label htmlFor="reference">Reference</Label>
<select <select
className="form-select form-select-sm" id="reference"
className="form-select shadow-none border py-1 px-2 small"
{...register("reference")} {...register("reference")}
> >
{reference.map((org) => ( {reference.map((org) => (
@ -193,6 +198,7 @@ const OrganizationInfo = ({ onNext, onPrev, onSubmitTenant }) => {
)} )}
</div> </div>
<div className="col-sm-12"> <div className="col-sm-12">
<Label htmlFor="description">Description</Label> <Label htmlFor="description">Description</Label>
<textarea <textarea

View File

@ -1,5 +1,5 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import React, { useState,useCallback } from "react"; import React, { useState, useCallback, useEffect } from "react";
import { FormProvider, useForm, useFormContext } from "react-hook-form"; import { FormProvider, useForm, useFormContext } from "react-hook-form";
import { defaultFilterValues, filterSchema } from "./TenantSchema"; import { defaultFilterValues, filterSchema } from "./TenantSchema";
import Label from "../common/Label"; import Label from "../common/Label";
@ -8,15 +8,16 @@ import { useIndustries } from "../../hooks/useTenant";
import { reference, TENANT_STATUS } from "../../utils/constants"; import { reference, TENANT_STATUS } from "../../utils/constants";
import { DateRangePicker1 } from "../common/DateRangePicker"; import { DateRangePicker1 } from "../common/DateRangePicker";
import moment from "moment"; import moment from "moment";
import { useLocation } from "react-router-dom";
const TenantFilterPanel = ({ onApply }) => {
const [resetKey, setResetKey] = useState(0);
const TenantFilterPanel = ({onApply}) => {
const [resetKey, setResetKey] = useState(0);
const methods = useForm({ const methods = useForm({
resolver: zodResolver(filterSchema), resolver: zodResolver(filterSchema),
defaultValues: defaultFilterValues, defaultValues: defaultFilterValues,
}); });
const { handleSubmit, reset } = methods; const { handleSubmit, reset } = methods;
const { data: industries = [], isLoading } = useIndustries(); const { data: industries = [], isLoading } = useIndustries();
@ -36,6 +37,13 @@ const [resetKey, setResetKey] = useState(0);
[onApply, handleClosePanel] [onApply, handleClosePanel]
); );
// Close popup when navigating to another component
const location = useLocation();
useEffect(() => {
handleClosePanel();
}, [location]);
const onClear = useCallback(() => { const onClear = useCallback(() => {
reset(defaultFilterValues); reset(defaultFilterValues);
setResetKey((prev) => prev + 1); // triggers DateRangePicker reset setResetKey((prev) => prev + 1); // triggers DateRangePicker reset
@ -48,44 +56,44 @@ const [resetKey, setResetKey] = useState(0);
return ( return (
<FormProvider {...methods}> <FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<div className="text-start mb-1"> <div className="text-start mb-1">
<div className="text-start my-2"> <div className="text-start my-2">
<DateRangePicker1 <DateRangePicker1
placeholder="DD-MM-YYYY To DD-MM-YYYY" placeholder="DD-MM-YYYY To DD-MM-YYYY"
startField="startDate" startField="startDate"
endField="endDate" endField="endDate"
resetSignal={resetKey} resetSignal={resetKey}
defaultRange={false} defaultRange={false}
/> />
</div> </div>
<div className="text-strat mb-2"> <div className="text-strat mb-2">
<SelectMultiple <SelectMultiple
name="industryIds" name="industryIds"
label="Industries" label="Industries"
options={industries} options={industries}
labelKey="name" labelKey="name"
valueKey="id" valueKey="id"
/> />
</div> </div>
<div className="text-start mb-2"> <div className="text-start mb-2">
<SelectMultiple <SelectMultiple
name="references" name="references"
label="References" label="References"
options={reference} options={reference}
labelKey="name" labelKey="name"
valueKey="val" valueKey="val"
/> />
</div> </div>
<div className="text-start"> <div className="text-start">
<SelectMultiple <SelectMultiple
name="tenantStatusIds" name="tenantStatusIds"
label="Tenant Status" label="Tenant Status"
options={TENANT_STATUS} options={TENANT_STATUS}
labelKey="name" labelKey="name"
valueKey="id" valueKey="id"
/> />
</div> </div>
{/* <SelectMultiple {/* <SelectMultiple
name="references" name="references"
@ -100,7 +108,7 @@ const [resetKey, setResetKey] = useState(0);
type="button" type="button"
className="btn btn-secondary btn-xs" className="btn btn-secondary btn-xs"
onClick={onClear} onClick={onClear}
> >
Clear Clear
</button> </button>

View File

@ -2,34 +2,34 @@ import { z } from "zod";
export const newTenantSchema = z.object({ export const newTenantSchema = z.object({
firstName: z firstName: z
.string().trim() .string().trim()
.min(1, { message: "First Name is required!" }) .min(1, { message: "First Name is required!" })
.regex(/^[A-Za-z]+$/, { message: "First Name should contain only letters!" }), .regex(/^[A-Za-z]+$/, { message: "First Name should contain only letters!" }),
lastName: z lastName: z
.string().trim() .string().trim()
.min(1, { message: "Last Name is required!" }) .min(1, { message: "Last Name is required!" })
.regex(/^[A-Za-z]+$/, { message: "Last Name should contain only letters!" }), .regex(/^[A-Za-z]+$/, { message: "Last Name should contain only letters!" }),
email: z.string().trim().email("Invalid email address"), email: z.string().trim().email("Invalid email address"),
description: z.string().trim().optional(), description: z.string().trim().optional(),
domainName: z.string().trim().nonempty("Domain name is required"), domainName: z.string().trim().optional(),
billingAddress: z.string().trim().nonempty("Billing address is required"), billingAddress: z.string().trim().nonempty("Billing address is required"),
taxId: z.string().trim().nonempty("Tax ID is required"), taxId: z.string().trim().optional(),
logoImage: z.string().trim().optional(), logoImage: z.string().trim().optional(),
organizationName: z.string().trim().nonempty("Organization name is required"), organizationName: z.string().trim().nonempty("Organization name is required"),
officeNumber: z.string().trim().nonempty("Office number is required"), officeNumber: z.string().trim().optional(),
contactNumber: z.string().trim() contactNumber: z.string().trim()
.nonempty("Contact number is required") .nonempty("Contact number is required")
.regex(/^\+?[1-9]\d{7,14}$/, "Enter a valid contact number"), .regex(/^\+?[1-9]\d{7,14}$/, "Enter a valid contact number"),
onBoardingDate: z.preprocess((val) => { onBoardingDate: z.preprocess((val) => {
if (typeof val === "string" && val.includes("-")) { if (typeof val === "string" && val.includes("-")) {
const [day, month, year] = val.split("-"); const [day, month, year] = val.split("-");
return new Date(`${year}-${month}-${day}`); return new Date(`${year}-${month}-${day}`);
} }
return val; return val;
}, z.date({ }, z.date({
required_error: "Onboarding date is required", required_error: "Onboarding date is required",
invalid_type_error: "Invalid date format", invalid_type_error: "Invalid date format",
})), })),
organizationSize: z.string().nonempty("Organization size is required"), organizationSize: z.string().nonempty("Organization size is required"),
industryId: z.string().uuid("Invalid industry ID"), industryId: z.string().uuid("Invalid industry ID"),
reference: z.string().nonempty("Reference is required"), reference: z.string().nonempty("Reference is required"),
@ -71,8 +71,8 @@ export const getSubscriptionSchema = (minUsers) =>
export const subscriptionDefaultValues = { export const subscriptionDefaultValues = {
// tenantId: "", // tenantId: "",
planId: "", planId: "",
currencyId: "", currencyId: "",
maxUsers: 1, maxUsers: 1,
frequency: 1, frequency: 1,
isTrial: false, isTrial: false,
@ -84,8 +84,8 @@ export const filterSchema = z.object({
// createdByIds: z.array(z.string()).optional(), // createdByIds: z.array(z.string()).optional(),
tenantStatusIds: z.array(z.string()).optional(), tenantStatusIds: z.array(z.string()).optional(),
references: z.array(z.string()).optional(), references: z.array(z.string()).optional(),
startDate: z.string().optional(), startDate: z.string().optional(),
endDate: z.string().optional(), endDate: z.string().optional(),
}); });
export const defaultFilterValues = { export const defaultFilterValues = {
industryIds: [], industryIds: [],
@ -133,23 +133,23 @@ export const getStepFields = (stepIndex) => {
export const EditTenant = z.object({ export const EditTenant = z.object({
firstName: z firstName: z
.string().trim() .string().trim()
.min(1, { message: "First Name is required!" }) .min(1, { message: "First Name is required!" })
.regex(/^[A-Za-z]+$/, { message: "First Name should contain only letters!" }), .regex(/^[A-Za-z]+$/, { message: "First Name should contain only letters!" }),
lastName: z lastName: z
.string().trim() .string().trim()
.min(1, { message: "Last Name is required!" }) .min(1, { message: "Last Name is required!" })
.regex(/^[A-Za-z]+$/, { message: "Last Name should contain only letters!" }), .regex(/^[A-Za-z]+$/, { message: "Last Name should contain only letters!" }),
description: z.string().trim().optional(), description: z.string().trim().optional(),
domainName: z.string().trim().min(1, { message: "Domain Name is required!" }), domainName: z.string().trim().optional(),
billingAddress: z.string().trim().min(1, { message: "Billing Address is required!" }), billingAddress: z.string().trim().min(1, { message: "Billing Address is required!" }),
taxId: z.string().trim().min(1, { message: "Tax ID is required!" }), taxId: z.string().trim().optional(),
logoImage: z.string().optional(), logoImage: z.string().optional(),
officeNumber: z.string().trim().min(1, { message: "Office Number is required!" }), officeNumber: z.string().trim().optional(),
contactNumber: z.string().trim() contactNumber: z.string().trim()
.nonempty("Contact number is required") .nonempty("Contact number is required")
.regex(/^\+?[1-9]\d{7,14}$/, "Enter a valid contact number"), .regex(/^\+?[1-9]\d{7,14}$/, "Enter a valid contact number"),
organizationSize: z.string().min(1, { message: "Organization Size is required!" }), organizationSize: z.string().min(1, { message: "Organization Size is required!" }),
industryId: z.string().min(1,{ message: "Invalid Industry ID!" }), industryId: z.string().min(1, { message: "Invalid Industry ID!" }),
reference: z.string().optional(), reference: z.string().optional(),
}); });

View File

@ -46,6 +46,12 @@ const DateRangePicker = ({
}; };
}, [onRangeChange, DateDifference, endDateMode]); }, [onRangeChange, DateDifference, endDateMode]);
const handleIconClick = () => {
if (inputRef.current) {
inputRef.current._flatpickr.open(); // directly opens flatpickr
}
};
return ( return (
<div className={`col-${sm} col-sm-${md} px-1`}> <div className={`col-${sm} col-sm-${md} px-1`}>
<input <input
@ -57,7 +63,7 @@ const DateRangePicker = ({
/> />
<i <i
className="bx bx-calendar calendar-icon cursor-pointer position-relative top-50 translate-middle-y " className="bx bx-calendar calendar-icon cursor-pointer position-relative top-50 translate-middle-y " onClick={handleIconClick}
style={{ right: "22px", bottom: "-8px" }} style={{ right: "22px", bottom: "-8px" }}
></i> ></i>
</div> </div>
@ -77,7 +83,7 @@ export const DateRangePicker1 = ({
className = "", className = "",
allowText = false, allowText = false,
resetSignal, resetSignal,
defaultRange = true, defaultRange = true,
...rest ...rest
}) => { }) => {
const inputRef = useRef(null); const inputRef = useRef(null);

View File

@ -1,4 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useSelector } from "react-redux";
const FilterIcon = ({ const FilterIcon = ({
taskListData, taskListData,
@ -7,15 +8,26 @@ const FilterIcon = ({
currentSelectedFloors, currentSelectedFloors,
currentSelectedActivities, currentSelectedActivities,
}) => { }) => {
const selectedProject = useSelector((store) => store.localVariables.projectId);
const [selectedBuilding, setSelectedBuilding] = useState(currentSelectedBuilding || ""); const [selectedBuilding, setSelectedBuilding] = useState(currentSelectedBuilding || "");
const [selectedFloors, setSelectedFloors] = useState(currentSelectedFloors || []); const [selectedFloors, setSelectedFloors] = useState(currentSelectedFloors || []);
const [selectedActivities, setSelectedActivities] = useState(currentSelectedActivities || []); const [selectedActivities, setSelectedActivities] = useState(currentSelectedActivities || []);
const [appliedBuilding, setAppliedBuilding] = useState(currentSelectedBuilding || "");
const [appliedFloors, setAppliedFloors] = useState(currentSelectedFloors || []);
const [appliedActivities, setAppliedActivities] = useState(currentSelectedActivities || []);
// Reset filters whenever inputs OR projectId changes
useEffect(() => { useEffect(() => {
setSelectedBuilding(currentSelectedBuilding || ""); setSelectedBuilding(currentSelectedBuilding || "");
setSelectedFloors(currentSelectedFloors || []); setSelectedFloors(currentSelectedFloors || []);
setSelectedActivities(currentSelectedActivities || []); setSelectedActivities(currentSelectedActivities || []);
}, [currentSelectedBuilding, currentSelectedFloors, currentSelectedActivities]);
setAppliedBuilding(currentSelectedBuilding || "");
setAppliedFloors(currentSelectedFloors || []);
setAppliedActivities(currentSelectedActivities || []);
}, [currentSelectedBuilding, currentSelectedFloors, currentSelectedActivities, selectedProject]);
const getUniqueFilterValues = (key, overrideBuilding, overrideFloors) => { const getUniqueFilterValues = (key, overrideBuilding, overrideFloors) => {
if (!taskListData) return []; if (!taskListData) return [];
@ -61,12 +73,11 @@ const FilterIcon = ({
} else if (filterType === "floor") { } else if (filterType === "floor") {
if (updatedFloors.includes(value)) { if (updatedFloors.includes(value)) {
updatedFloors = updatedFloors.filter((floor) => floor !== value); updatedFloors = updatedFloors.filter((floor) => floor !== value);
const validActivities = getUniqueFilterValues("activity", updatedBuilding, updatedFloors);
updatedActivities = updatedActivities.filter((act) => validActivities.includes(act));
} else { } else {
updatedFloors.push(value); updatedFloors.push(value);
} }
const validActivities = getUniqueFilterValues("activity", updatedBuilding, updatedFloors);
updatedActivities = updatedActivities.filter((act) => validActivities.includes(act));
} else if (filterType === "activity") { } else if (filterType === "activity") {
if (updatedActivities.includes(value)) { if (updatedActivities.includes(value)) {
updatedActivities = updatedActivities.filter((act) => act !== value); updatedActivities = updatedActivities.filter((act) => act !== value);
@ -78,12 +89,20 @@ const FilterIcon = ({
setSelectedBuilding(updatedBuilding); setSelectedBuilding(updatedBuilding);
setSelectedFloors(updatedFloors); setSelectedFloors(updatedFloors);
setSelectedActivities(updatedActivities); setSelectedActivities(updatedActivities);
};
const applyFilters = () => {
setAppliedBuilding(selectedBuilding);
setAppliedFloors(selectedFloors);
setAppliedActivities(selectedActivities);
onApplyFilters({ onApplyFilters({
selectedBuilding: updatedBuilding, selectedBuilding,
selectedFloors: updatedFloors, selectedFloors,
selectedActivities: updatedActivities, selectedActivities,
}); });
document.getElementById("filterDropdown").click();
}; };
const clearAllFilters = () => { const clearAllFilters = () => {
@ -91,6 +110,10 @@ const FilterIcon = ({
setSelectedFloors([]); setSelectedFloors([]);
setSelectedActivities([]); setSelectedActivities([]);
setAppliedBuilding("");
setAppliedFloors([]);
setAppliedActivities([]);
onApplyFilters({ onApplyFilters({
selectedBuilding: "", selectedBuilding: "",
selectedFloors: [], selectedFloors: [],
@ -98,21 +121,51 @@ const FilterIcon = ({
}); });
}; };
// Count applied filters
const appliedFilterCount =
(appliedBuilding ? 1 : 0) + appliedFloors.length + appliedActivities.length;
return ( return (
<div className="dropdown" style={{marginLeft:"-14px"}}> <div className="dropdown" style={{ marginLeft: "-14px", position: "relative" }}>
<a <a
className="dropdown-toggle hide-arrow cursor-pointer" className="dropdown-toggle hide-arrow cursor-pointer"
id="filterDropdown" id="filterDropdown"
data-bs-toggle="dropdown" data-bs-toggle="dropdown"
aria-expanded="false" aria-expanded="false"
> >
{/* <i className="bx bx-slider-alt ms-1" /> */} <div style={{ position: "relative", display: "inline-block" }}>
<i <i
className="bx bx-slider-alt" className="bx bx-slider-alt"
style={{ color: selectedBuilding || selectedFloors.length > 0 || selectedActivities.length > 0 ? "#7161EF" : "gray" }} style={{
></i> color: appliedFilterCount > 0 ? "#7161EF" : "gray",
fontSize: "20px",
}}
></i>
{appliedFilterCount > 0 && (
<span
style={{
position: "absolute",
top: "-11px",
right: "-6px",
backgroundColor: "#FFC107", // yellow
color: "white",
fontSize: "10px",
fontWeight: "bold",
borderRadius: "50%",
width: "18px",
height: "18px",
display: "flex",
alignItems: "center",
justifyContent: "center",
border: "1px solid white",
}}
>
{appliedFilterCount}
</span>
)}
</div>
</a> </a>
<ul <ul
className="dropdown-menu p-2 mt-2" className="dropdown-menu p-2 mt-2"
aria-labelledby="filterDropdown" aria-labelledby="filterDropdown"
@ -205,45 +258,30 @@ const FilterIcon = ({
)} )}
{/* Action Buttons */} {/* Action Buttons */}
<li><hr className="my-1" /></li> <li>
{(selectedBuilding || selectedFloors.length > 0 || selectedActivities.length > 0) && ( <hr className="my-1" />
<li className="d-flex justify-content-end gap-2 px-2"> </li>
{(appliedFilterCount > 0 ||
selectedBuilding ||
selectedFloors.length > 0 ||
selectedActivities.length > 0) && (
<li className="d-flex justify-content-end gap-2 px-2 mt-2 mb-2">
<button <button
type="button" type="button"
className="btn btn-sm" className="btn btn-secondary btn-sm py-0 px-2"
style={{
backgroundColor: "#7161EF",
color: "white",
fontSize: "13px",
padding: "4px 16px",
borderRadius: "8px",
boxShadow: "0 1px 4px rgba(0,0,0,0.1)"
}}
onClick={clearAllFilters} onClick={clearAllFilters}
> >
Clear Clear
</button> </button>
<button <button
type="button" type="button"
className="btn btn-sm" className="btn btn-primary btn-sm py-0 px-2"
style={{ onClick={applyFilters}
backgroundColor: "#7161EF",
color: "white",
fontSize: "13px",
padding: "4px 16px",
borderRadius: "8px",
boxShadow: "0 1px 4px rgba(0,0,0,0.1)"
}}
onClick={() => {
document.getElementById("filterDropdown").click();
}}
> >
Apply Apply
</button> </button>
</li> </li>
)} )}
</ul> </ul>
</div> </div>
); );

View File

@ -222,15 +222,12 @@ const CreateActivity = ({ onClose }) => {
<div className="col-12 text-end mt-3"> <div className="col-12 text-end mt-3">
<button <button
type="reset" type="button" // change to button
className="btn btn-sm btn-label-secondary me-3" className="btn btn-sm btn-label-secondary"
onClick={handleClose} onClick={handleClose}
> >
Cancel Cancel
</button> </button>
<button type="submit" className="btn btn-sm btn-primary">
{isLoading ? "Please Wait" : "Submit"}
</button>
</div> </div>
</div> </div>

View File

@ -1,10 +1,10 @@
import React, { useEffect,useState } from 'react' import React, { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { MasterRespository } from '../../repositories/MastersRepository'; import { MasterRespository } from '../../repositories/MastersRepository';
import { clearApiCacheKey } from '../../slices/apiCacheSlice'; import { clearApiCacheKey } from '../../slices/apiCacheSlice';
import { getCachedData,cacheData } from '../../slices/apiDataManager'; import { getCachedData, cacheData } from '../../slices/apiDataManager';
import showToast from '../../services/toastService'; import showToast from '../../services/toastService';
import {useCreateContactCategory} from '../../hooks/masterHook/useMaster'; import {useCreateContactCategory} from '../../hooks/masterHook/useMaster';
import Label from '../common/Label'; import Label from '../common/Label';
@ -13,32 +13,32 @@ import Label from '../common/Label';
const schema = z.object({ const schema = z.object({
name: z.string().min(1, { message: "Category name is required" }), name: z.string().min(1, { message: "Category name is required" }),
description: z.string().min(1, { message: "Description is required" }) description: z.string().min(1, { message: "Description is required" })
.max(255, { message: "Description cannot exceed 255 characters" }), .max(255, { message: "Description cannot exceed 255 characters" }),
}); });
const CreateContactCategory = ({onClose}) => { const CreateContactCategory = ({ onClose }) => {
const { const {
register, register,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
reset, reset,
} = useForm({ } = useForm({
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: { defaultValues: {
name: "", name: "",
description: "", description: "",
}, },
}); });
const [descriptionLength, setDescriptionLength] = useState(0); const [descriptionLength, setDescriptionLength] = useState(0);
const maxDescriptionLength = 255; const maxDescriptionLength = 255;
const { mutate: createContactCategory, isPending: isLoading } = useCreateContactCategory(() => onClose?.()); const { mutate: createContactCategory, isPending: isLoading } = useCreateContactCategory(() => onClose?.());
const onSubmit = (payload) => { const onSubmit = (payload) => {
createContactCategory(payload); createContactCategory(payload);
}; };
// const onSubmit = (data) => { // const onSubmit = (data) => {
// setIsLoading(true) // setIsLoading(true)
// MasterRespository.createContactCategory(data).then((resp)=>{ // MasterRespository.createContactCategory(data).then((resp)=>{
@ -55,27 +55,27 @@ const onSubmit = (payload) => {
// setIsLoading(false) // setIsLoading(false)
// }) // })
// }; // };
const resetForm = () => { const resetForm = () => {
reset({ name: "", description: "" }); reset({ name: "", description: "" });
setDescriptionLength(0); setDescriptionLength(0);
}; };
useEffect(() => {
return () => resetForm();
}, []);
useEffect(() => {
return () => resetForm();
}, []);
return (<> return (<>
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}> <form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 col-md-12 text-start"> <div className="col-12 col-md-12">
<Label className="form-label" required>Category Name</Label> <label className="form-label">Category Name</label>
<input type="text" <input type="text"
{...register("name")} {...register("name")}
className={`form-control ${errors.name ? 'is-invalids' : ''}`} className={`form-control ${errors.name ? 'is-invalids' : ''}`}
/> />
{errors.name && <p className="text-danger">{errors.name.message}</p>} {errors.name && <p className="text-danger">{errors.name.message}</p>}
</div> </div>
<div className="col-12 col-md-12 text-start"> <div className="col-12 col-md-12">
<Label className="form-label" htmlFor="description" required>Description</Label> <label className="form-label" htmlFor="description">Description</label>
<textarea <textarea
rows="3" rows="3"
{...register("description")} {...register("description")}
@ -88,29 +88,31 @@ useEffect(() => {
<div className="text-end small text-muted"> <div className="text-end small text-muted">
{maxDescriptionLength - descriptionLength} characters left {maxDescriptionLength - descriptionLength} characters left
</div> </div>
{errors.description && ( {errors.description && (
<p className="text-danger">{errors.description.message}</p> <p className="text-danger">{errors.description.message}</p>
)} )}
</div> </div>
<div className="col-12 text-end"> <div className="col-12 text-center">
<button <button type="submit" className="btn btn-sm btn-primary me-3">
type="reset" {isLoading ? "Please Wait..." : "Submit"}
className="btn btn-sm btn-label-secondary me-3" </button>
data-bs-dismiss="modal" <button
aria-label="Close" type="button" // not reset
className="btn btn-sm btn-label-secondary"
onClick={() => {
resetForm(); // clear inputs
onClose?.(); // close modal from parent
}}
> >
Cancel Cancel
</button> </button>
<button type="submit" className="btn btn-sm btn-primary">
{isLoading? "Please Wait...":"Submit"}
</button>
</div> </div>
</form> </form>
</> </>
) )
} }

View File

@ -1,10 +1,10 @@
import React, { useEffect,useState } from 'react' import React, { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { MasterRespository } from '../../repositories/MastersRepository'; import { MasterRespository } from '../../repositories/MastersRepository';
import { clearApiCacheKey } from '../../slices/apiCacheSlice'; import { clearApiCacheKey } from '../../slices/apiCacheSlice';
import { getCachedData,cacheData } from '../../slices/apiDataManager'; import { getCachedData, cacheData } from '../../slices/apiDataManager';
import showToast from '../../services/toastService'; import showToast from '../../services/toastService';
import {useCreateContactTag} from '../../hooks/masterHook/useMaster'; import {useCreateContactTag} from '../../hooks/masterHook/useMaster';
import Label from '../common/Label'; import Label from '../common/Label';
@ -13,31 +13,31 @@ import Label from '../common/Label';
const schema = z.object({ const schema = z.object({
name: z.string().min(1, { message: "Tag name is required" }), name: z.string().min(1, { message: "Tag name is required" }),
description: z.string().min(1, { message: "Description is required" }) description: z.string().min(1, { message: "Description is required" })
.max(255, { message: "Description cannot exceed 255 characters" }), .max(255, { message: "Description cannot exceed 255 characters" }),
}); });
const CreateContactTag = ({onClose}) => { const CreateContactTag = ({ onClose }) => {
const { const {
register, register,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
reset, reset,
} = useForm({ } = useForm({
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: { defaultValues: {
name: "", name: "",
description: "", description: "",
}, },
}); });
const [descriptionLength, setDescriptionLength] = useState(0); const [descriptionLength, setDescriptionLength] = useState(0);
const maxDescriptionLength = 255; const maxDescriptionLength = 255;
const { mutate: createContactTag, isPending: isLoading } = useCreateContactTag(() => onClose?.()); const { mutate: createContactTag, isPending: isLoading } = useCreateContactTag(() => onClose?.());
const onSubmit = (payload) => { const onSubmit = (payload) => {
createContactTag(payload); createContactTag(payload);
}; };
// const onSubmit = (data) => { // const onSubmit = (data) => {
// setIsLoading(true) // setIsLoading(true)
// MasterRespository.createContactTag(data).then((resp)=>{ // MasterRespository.createContactTag(data).then((resp)=>{
@ -55,15 +55,15 @@ const onSubmit = (payload) => {
// setIsLoading(false) // setIsLoading(false)
// }) // })
// }; // };
const resetForm = () => { const resetForm = () => {
reset({ name: "", description: "" }); reset({ name: "", description: "" });
setDescriptionLength(0); setDescriptionLength(0);
}; };
useEffect(() => {
return () => resetForm();
}, []);
useEffect(() => {
return () => resetForm();
}, []);
return (<> return (<>
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}> <form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 col-md-12 text-start"> <div className="col-12 col-md-12 text-start">
@ -88,29 +88,31 @@ useEffect(() => {
<div className="text-end small text-muted"> <div className="text-end small text-muted">
{maxDescriptionLength - descriptionLength} characters left {maxDescriptionLength - descriptionLength} characters left
</div> </div>
{errors.description && ( {errors.description && (
<p className="text-danger">{errors.description.message}</p> <p className="text-danger">{errors.description.message}</p>
)} )}
</div> </div>
<div className="col-12 text-end"> <div className="col-12 text-center">
<button <button type="submit" className="btn btn-sm btn-primary me-3">
type="reset" {isLoading ? "Please Wait..." : "Submit"}
className="btn btn-sm btn-label-secondary me-3" </button>
data-bs-dismiss="modal" <button
aria-label="Close" type="button" // not reset
className="btn btn-sm btn-label-secondary"
onClick={() => {
resetForm(); // clear inputs
onClose?.(); // close modal from parent
}}
> >
Cancel Cancel
</button> </button>
<button type="submit" className="btn btn-sm btn-primary">
{isLoading? "Please Wait...":"Submit"}
</button>
</div> </div>
</form> </form>
</> </>
) )
} }

View File

@ -1,10 +1,10 @@
import React, { useEffect,useState } from 'react' import React, { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { MasterRespository } from '../../repositories/MastersRepository'; import { MasterRespository } from '../../repositories/MastersRepository';
import { clearApiCacheKey } from '../../slices/apiCacheSlice'; import { clearApiCacheKey } from '../../slices/apiCacheSlice';
import { getCachedData,cacheData } from '../../slices/apiDataManager'; import { getCachedData, cacheData } from '../../slices/apiDataManager';
import showToast from '../../services/toastService'; import showToast from '../../services/toastService';
import {useCreateJobRole} from '../../hooks/masterHook/useMaster'; import {useCreateJobRole} from '../../hooks/masterHook/useMaster';
import Label from '../common/Label'; import Label from '../common/Label';
@ -13,10 +13,10 @@ import Label from '../common/Label';
const schema = z.object({ const schema = z.object({
role: z.string().min(1, { message: "Role is required" }), role: z.string().min(1, { message: "Role is required" }),
description: z.string().min(1, { message: "Description is required" }) description: z.string().min(1, { message: "Description is required" })
.max(255, { message: "Description cannot exceed 255 characters" }), .max(255, { message: "Description cannot exceed 255 characters" }),
}); });
const CreateJobRole = ({onClose}) => { const CreateJobRole = ({ onClose }) => {
const maxDescriptionLength = 255; const maxDescriptionLength = 255;
const [descriptionLength, setDescriptionLength] = useState(0); const [descriptionLength, setDescriptionLength] = useState(0);
@ -40,34 +40,34 @@ const CreateJobRole = ({onClose}) => {
setDescriptionLength(0); setDescriptionLength(0);
}; };
const { mutate: createJobRole, isPending:isLoading } = useCreateJobRole(() => { const { mutate: createJobRole, isPending: isLoading } = useCreateJobRole(() => {
onClose?.(); onClose?.();
} ); });
// const onSubmit = (data) => { // const onSubmit = (data) => {
// setIsLoading(true) // setIsLoading(true)
// const result = { // const result = {
// name: data.role, // name: data.role,
// description: data.description, // description: data.description,
// }; // };
// MasterRespository.createJobRole(result).then((resp)=>{
// setIsLoading(false)
// resetForm()
// const cachedData = getCachedData("Job Role");
// const updatedData = [...cachedData, resp?.data];
// cacheData("Job Role", updatedData);
// showToast("JobRole Added successfully.", "success");
// onClose() // MasterRespository.createJobRole(result).then((resp)=>{
// }).catch((error)=>{ // setIsLoading(false)
// showToast(error.message, "error"); // resetForm()
// setIsLoading(false) // const cachedData = getCachedData("Job Role");
// }) // const updatedData = [...cachedData, resp?.data];
// cacheData("Job Role", updatedData);
// showToast("JobRole Added successfully.", "success");
// };
// onClose()
// }).catch((error)=>{
// showToast(error.message, "error");
// setIsLoading(false)
// })
// };
const onSubmit = (data) => { const onSubmit = (data) => {
const payload = { const payload = {
name: data.role, name: data.role,
@ -88,7 +88,7 @@ const CreateJobRole = ({onClose}) => {
}, []); }, []);
return (<> return (<>
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}> <form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
{/* <div className="col-12 col-md-12"> {/* <div className="col-12 col-md-12">
<label className="fs-5 text-dark text-center d-flex align-items-center justify-content-center flex-wrap">Create Job Role</label> <label className="fs-5 text-dark text-center d-flex align-items-center justify-content-center flex-wrap">Create Job Role</label>
</div> */} </div> */}
@ -114,29 +114,31 @@ const CreateJobRole = ({onClose}) => {
<div className="text-end small text-muted"> <div className="text-end small text-muted">
{maxDescriptionLength - descriptionLength} characters left {maxDescriptionLength - descriptionLength} characters left
</div> </div>
{errors.description && ( {errors.description && (
<p className="text-danger">{errors.description.message}</p> <p className="text-danger">{errors.description.message}</p>
)} )}
</div> </div>
<div className="col-12 text-end"> <div className="col-12 text-center">
<button <button type="submit" className="btn btn-sm btn-primary me-3">
type="reset" {isLoading ? "Please Wait..." : "Submit"}
className="btn btn-sm btn-label-secondary me-3" </button>
data-bs-dismiss="modal" <button
aria-label="Close" type="button" // change from reset button
className="btn btn-sm btn-label-secondary"
onClick={() => {
resetForm(); // optional: clears form
onClose?.(); // close modal via parent
}}
> >
Cancel Cancel
</button> </button>
<button type="submit" className="btn btn-sm btn-primary">
{isLoading? "Please Wait...":"Submit"}
</button>
</div> </div>
</form> </form>
</> </>
) )
} }

View File

@ -229,19 +229,18 @@ const CreateRole = ({ modalType, onClose }) => {
{masterFeatures && ( {masterFeatures && (
<div className="col-12 text-end"> <div className="col-12 text-center">
<button <button type="submit" className="btn btn-sm btn-primary me-3">
type="reset" {isLoading ? "Please Wait..." : "Submit"}
className="btn btn-sm btn-label-secondary me-3" </button>
data-bs-dismiss="modal" <button
aria-label="Close" type="button"
className="btn btn-sm btn-label-secondary"
onClick={onClose}
> >
Cancel Cancel
</button> </button>
<button type="submit" className="btn btn-sm btn-primary">
{isLoading ? "Please Wait..." : "Submit"}
</button>
</div> </div>
)} )}
</form> </form>

View File

@ -1,10 +1,10 @@
import React, { useEffect,useState } from 'react' import React, { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { MasterRespository } from '../../repositories/MastersRepository'; import { MasterRespository } from '../../repositories/MastersRepository';
import { clearApiCacheKey } from '../../slices/apiCacheSlice'; import { clearApiCacheKey } from '../../slices/apiCacheSlice';
import { getCachedData,cacheData } from '../../slices/apiDataManager'; import { getCachedData, cacheData } from '../../slices/apiDataManager';
import showToast from '../../services/toastService'; import showToast from '../../services/toastService';
import {useCreateWorkCategory} from '../../hooks/masterHook/useMaster'; import {useCreateWorkCategory} from '../../hooks/masterHook/useMaster';
import Label from '../common/Label'; import Label from '../common/Label';
@ -13,69 +13,69 @@ import Label from '../common/Label';
const schema = z.object({ const schema = z.object({
name: z.string().min(1, { message: "Category name is required" }), name: z.string().min(1, { message: "Category name is required" }),
description: z.string().min(1, { message: "Description is required" }) description: z.string().min(1, { message: "Description is required" })
.max(255, { message: "Description cannot exceed 255 characters" }), .max(255, { message: "Description cannot exceed 255 characters" }),
}); });
const CreateWorkCategory = ({onClose}) => { const CreateWorkCategory = ({ onClose }) => {
const { const {
register, register,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
reset, reset,
} = useForm({ } = useForm({
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: { defaultValues: {
name: "", name: "",
description: "", description: "",
}, },
});
const [descriptionLength, setDescriptionLength] = useState(0);
const maxDescriptionLength = 255;
const { mutate: createWorkCategory, isPending: isLoading } = useCreateWorkCategory(() => {
resetForm();
onClose?.();
});
const onSubmit = (payload) => {
createWorkCategory(payload)
};
// const onSubmit = (data) => {
// setIsLoading(true)
// MasterRespository.createWorkCategory(data).then((resp)=>{
// setIsLoading(false)
// resetForm()
// const cachedData = getCachedData("Work Category");
// const updatedData = [...cachedData, resp?.data];
// cacheData("Work Category", updatedData);
// showToast("Work Category Added successfully.", "success");
// onClose()
// }).catch((error)=>{
// showToast(error?.response?.data?.message, "error");
// setIsLoading(false)
// })
// };
const resetForm = () => {
reset({
name: "",
description: "",
}); });
setDescriptionLength(0);
};
useEffect(() => { const [descriptionLength, setDescriptionLength] = useState(0);
return () => resetForm(); const maxDescriptionLength = 255;
}, []);
const { mutate: createWorkCategory, isPending: isLoading } = useCreateWorkCategory(() => {
resetForm();
onClose?.();
});
const onSubmit = (payload) => {
createWorkCategory(payload)
};
// const onSubmit = (data) => {
// setIsLoading(true)
// MasterRespository.createWorkCategory(data).then((resp)=>{
// setIsLoading(false)
// resetForm()
// const cachedData = getCachedData("Work Category");
// const updatedData = [...cachedData, resp?.data];
// cacheData("Work Category", updatedData);
// showToast("Work Category Added successfully.", "success");
// onClose()
// }).catch((error)=>{
// showToast(error?.response?.data?.message, "error");
// setIsLoading(false)
// })
// };
const resetForm = () => {
reset({
name: "",
description: "",
});
setDescriptionLength(0);
};
useEffect(() => {
return () => resetForm();
}, []);
return (<> return (<>
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}> <form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
{/* <div className="col-12 col-md-12"> {/* <div className="col-12 col-md-12">
@ -104,17 +104,22 @@ useEffect(() => {
<div className="text-end small text-muted"> <div className="text-end small text-muted">
{maxDescriptionLength - descriptionLength} characters left {maxDescriptionLength - descriptionLength} characters left
</div> </div>
{errors.description && ( {errors.description && (
<p className="text-danger">{errors.description.message}</p> <p className="text-danger">{errors.description.message}</p>
)} )}
</div> </div>
<div className="col-12 text-end"> <div className="col-12 text-center">
<button <button type="submit" className="btn btn-sm btn-primary me-3">
type="reset" {isLoading ? "Please Wait..." : "Submit"}
className="btn btn-sm btn-label-secondary me-3" </button>
data-bs-dismiss="modal" <button
aria-label="Close" type="button" // not reset
className="btn btn-sm btn-label-secondary"
onClick={() => {
resetForm(); // clear inputs
onClose?.(); // close modal from parent
}}
> >
Cancel Cancel
</button> </button>
@ -123,10 +128,10 @@ useEffect(() => {
</button> </button>
</div> </div>
</form> </form>
</> </>
) )
} }

View File

@ -34,13 +34,15 @@ const DeleteMaster = ({ master, onClose }) => {
)} )}
</button> </button>
<button <button
type="reset" type="button" // not reset
className="btn btn-label-secondary" className="btn btn-label-secondary"
data-bs-dismiss="modal" onClick={() => {
aria-label="Close" onClose?.(); // properly close modal
}}
> >
Cancel Cancel
</button> </button>
</div> </div>
</div> </div>
); );

View File

@ -239,8 +239,8 @@ const UpdateActivity = ({ activityData, onClose }) => {
{/* Submit / Cancel */} {/* Submit / Cancel */}
<div className="col-12 text-end mt-3"> <div className="col-12 text-end mt-3">
<button <button
type="button" type="button" // change to button
className="btn btn-sm btn-label-secondary me-3" className="btn btn-sm btn-label-secondary"
onClick={onClose} onClick={onClose}
> >
Cancel Cancel

View File

@ -1,10 +1,10 @@
import React, { useEffect,useState } from 'react' import React, { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { MasterRespository } from '../../repositories/MastersRepository'; import { MasterRespository } from '../../repositories/MastersRepository';
import { clearApiCacheKey } from '../../slices/apiCacheSlice'; import { clearApiCacheKey } from '../../slices/apiCacheSlice';
import { getCachedData,cacheData } from '../../slices/apiDataManager'; import { getCachedData, cacheData } from '../../slices/apiDataManager';
import showToast from '../../services/toastService'; import showToast from '../../services/toastService';
import {useUpdateContactCategory} from '../../hooks/masterHook/useMaster'; import {useUpdateContactCategory} from '../../hooks/masterHook/useMaster';
import Label from '../common/Label'; import Label from '../common/Label';
@ -13,38 +13,38 @@ import Label from '../common/Label';
const schema = z.object({ const schema = z.object({
name: z.string().min(1, { message: "Category name is required" }), name: z.string().min(1, { message: "Category name is required" }),
description: z.string().min(1, { message: "Description is required" }) description: z.string().min(1, { message: "Description is required" })
.max(255, { message: "Description cannot exceed 255 characters" }), .max(255, { message: "Description cannot exceed 255 characters" }),
}); });
const EditContactCategory= ({data,onClose}) => { const EditContactCategory = ({ data, onClose }) => {
const { const {
register, register,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
reset, reset,
} = useForm({ } = useForm({
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: { defaultValues: {
name: data?.name || "", name: data?.name || "",
description: data?.description || "", description: data?.description || "",
}, },
}); });
const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0); const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0);
const maxDescriptionLength = 255; const maxDescriptionLength = 255;
const { mutate: updateContactCategory, isPending: isLoading } = useUpdateContactCategory(() => onClose?.()); const { mutate: updateContactCategory, isPending: isLoading } = useUpdateContactCategory(() => onClose?.());
const onSubmit = (formData) => { const onSubmit = (formData) => {
const payload = { const payload = {
id: data?.id, id: data?.id,
name: formData.name, name: formData.name,
description: formData.description, description: formData.description,
};
updateContactCategory({ id: data?.id, payload });
}; };
// const onSubmit = (formdata) => {
updateContactCategory({id:data?.id,payload});
};
// const onSubmit = (formdata) => {
// setIsLoading(true) // setIsLoading(true)
// const result = { // const result = {
// id:data?.id, // id:data?.id,
@ -52,8 +52,8 @@ const onSubmit = (formData) => {
// description: formdata.description, // description: formdata.description,
// }; // };
// MasterRespository.updateContactCategory(data?.id,result).then((resp)=>{ // MasterRespository.updateContactCategory(data?.id,result).then((resp)=>{
// setIsLoading(false) // setIsLoading(false)
// showToast("Contact Category Updated successfully.", "success"); // showToast("Contact Category Updated successfully.", "success");
@ -71,18 +71,18 @@ const onSubmit = (formData) => {
// showToast(error?.response?.data?.message, "error") // showToast(error?.response?.data?.message, "error")
// setIsLoading(false) // setIsLoading(false)
// }) // })
// }; // };
const resetForm = () => { const resetForm = () => {
reset({ name: "", description: "" }); reset({ name: "", description: "" });
setDescriptionLength(0); setDescriptionLength(0);
}; };
useEffect(() => {
return () => resetForm();
}, []);
useEffect(() => {
return () => resetForm();
}, []);
return (<> return (<>
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}> <form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 col-md-12 text-start"> <div className="col-12 col-md-12 text-start">
@ -107,17 +107,22 @@ useEffect(() => {
<div className="text-end small text-muted"> <div className="text-end small text-muted">
{maxDescriptionLength - descriptionLength} characters left {maxDescriptionLength - descriptionLength} characters left
</div> </div>
{errors.description && ( {errors.description && (
<p className="text-danger">{errors.description.message}</p> <p className="text-danger">{errors.description.message}</p>
)} )}
</div> </div>
<div className="col-12 text-end"> <div className="col-12 text-center">
<button <button type="submit" className="btn btn-sm btn-primary me-3">
type="button" {isLoading ? "Please Wait..." : "Submit"}
className="btn btn-sm btn-label-secondary me-3" </button>
data-bs-dismiss="modal" <button
aria-label="Close" type="button" // not reset
className="btn btn-sm btn-label-secondary"
onClick={() => {
resetForm(); // clear inputs
onClose?.(); // close modal from parent
}}
> >
Cancel Cancel
</button> </button>
@ -126,10 +131,10 @@ useEffect(() => {
</button> </button>
</div> </div>
</form> </form>
</> </>
) )
} }

View File

@ -1,10 +1,10 @@
import React,{useState,useEffect} from 'react' import React, { useState, useEffect } from 'react'
import {useForm} from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { MasterRespository } from '../../repositories/MastersRepository'; import { MasterRespository } from '../../repositories/MastersRepository';
import { clearApiCacheKey } from '../../slices/apiCacheSlice'; import { clearApiCacheKey } from '../../slices/apiCacheSlice';
import { getCachedData,cacheData } from '../../slices/apiDataManager'; import { getCachedData, cacheData } from '../../slices/apiDataManager';
import showToast from '../../services/toastService'; import showToast from '../../services/toastService';
import {useUpdateContactTag} from '../../hooks/masterHook/useMaster'; import {useUpdateContactTag} from '../../hooks/masterHook/useMaster';
import Label from '../common/Label'; import Label from '../common/Label';
@ -13,38 +13,38 @@ import Label from '../common/Label';
const schema = z.object({ const schema = z.object({
name: z.string().min(1, { message: "Tag name is required" }), name: z.string().min(1, { message: "Tag name is required" }),
description: z.string().min(1, { message: "Description is required" }) description: z.string().min(1, { message: "Description is required" })
.max(255, { message: "Description cannot exceed 255 characters" }), .max(255, { message: "Description cannot exceed 255 characters" }),
}); });
const EditContactTag= ({data,onClose}) => { const EditContactTag = ({ data, onClose }) => {
const { const {
register, register,
handleSubmit, handleSubmit,
formState: { errors }, formState: { errors },
reset reset
} = useForm({ } = useForm({
resolver: zodResolver(schema), resolver: zodResolver(schema),
defaultValues: { defaultValues: {
name: data?.name || "", name: data?.name || "",
description: data?.description || "", description: data?.description || "",
}, },
}); });
const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0); const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0);
const maxDescriptionLength = 255; const maxDescriptionLength = 255;
const { mutate: updateContactTag, isPending: isLoading } = useUpdateContactTag(() => onClose?.()); const { mutate: updateContactTag, isPending: isLoading } = useUpdateContactTag(() => onClose?.());
const onSubmit = (formData) => { const onSubmit = (formData) => {
const payload = { const payload = {
id: data?.id, id: data?.id,
name: formData.name, name: formData.name,
description: formData.description, description: formData.description,
}; };
debugger debugger
updateContactTag({ id: data?.id, payload} ); updateContactTag({ id: data?.id, payload });
} }
// const onSubmit = (formdata) => { // const onSubmit = (formdata) => {
// setIsLoading(true) // setIsLoading(true)
// const result = { // const result = {
// id:data?.id, // id:data?.id,
@ -52,8 +52,8 @@ const onSubmit = (formData) => {
// description: formdata.description, // description: formdata.description,
// }; // };
// MasterRespository.updateContactTag(data?.id,result).then((resp)=>{ // MasterRespository.updateContactTag(data?.id,result).then((resp)=>{
// setIsLoading(false) // setIsLoading(false)
// showToast("Contact Tag Updated successfully.", "success"); // showToast("Contact Tag Updated successfully.", "success");
@ -71,18 +71,18 @@ const onSubmit = (formData) => {
// showToast(error?.response?.data?.message, "error") // showToast(error?.response?.data?.message, "error")
// setIsLoading(false) // setIsLoading(false)
// }) // })
// }; // };
const resetForm = () => { const resetForm = () => {
reset({ name: "", description: "" }); reset({ name: "", description: "" });
setDescriptionLength(0); setDescriptionLength(0);
}; };
useEffect(() => {
return () => resetForm();
}, []);
useEffect(() => {
return () => resetForm();
}, []);
return (<> return (<>
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}> <form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 col-md-12 text-start"> <div className="col-12 col-md-12 text-start">
@ -107,17 +107,22 @@ useEffect(() => {
<div className="text-end small text-muted"> <div className="text-end small text-muted">
{maxDescriptionLength - descriptionLength} characters left {maxDescriptionLength - descriptionLength} characters left
</div> </div>
{errors.description && ( {errors.description && (
<p className="text-danger">{errors.description.message}</p> <p className="text-danger">{errors.description.message}</p>
)} )}
</div> </div>
<div className="col-12 text-end"> <div className="col-12 text-center">
<button <button type="submit" className="btn btn-sm btn-primary me-3">
type="button" {isLoading ? "Please Wait..." : "Submit"}
className="btn btn-sm btn-label-secondary me-3" </button>
data-bs-dismiss="modal" <button
aria-label="Close" type="button" // not reset
className="btn btn-sm btn-label-secondary"
onClick={() => {
resetForm(); // clear inputs
onClose?.(); // close modal from parent
}}
> >
Cancel Cancel
</button> </button>
@ -126,10 +131,10 @@ useEffect(() => {
</button> </button>
</div> </div>
</form> </form>
</> </>
) )
} }

View File

@ -1,9 +1,9 @@
import React, { useEffect,useState } from 'react' import React, { useEffect, useState } from 'react'
import { useForm ,Controller} from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { set, z } from 'zod'; import { set, z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { MasterRespository } from '../../repositories/MastersRepository'; import { MasterRespository } from '../../repositories/MastersRepository';
import { cacheData,getCachedData } from '../../slices/apiDataManager'; import { cacheData, getCachedData } from '../../slices/apiDataManager';
import showToast from '../../services/toastService'; import showToast from '../../services/toastService';
import {useUpdateJobRole} from '../../hooks/masterHook/useMaster'; import {useUpdateJobRole} from '../../hooks/masterHook/useMaster';
import Label from '../common/Label'; import Label from '../common/Label';
@ -13,13 +13,13 @@ import Label from '../common/Label';
const schema = z.object({ const schema = z.object({
role: z.string().min(1, { message: "Role is required" }), role: z.string().min(1, { message: "Role is required" }),
description: z.string().min(1, { message: "Description is required" }) description: z.string().min(1, { message: "Description is required" })
.max(255, { message: "Description cannot exceed 255 characters" }), .max(255, { message: "Description cannot exceed 255 characters" }),
}); });
const EditJobRole = ({data,onClose}) => { const EditJobRole = ({ data, onClose }) => {
const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0); const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0);
const maxDescriptionLength = 255; const maxDescriptionLength = 255;
const { const {
@ -36,38 +36,38 @@ const [descriptionLength, setDescriptionLength] = useState(data?.description?.le
}, },
}); });
const { mutate: updateJobRole, isPendin:isLoading } = useUpdateJobRole(() => { const { mutate: updateJobRole, isPendin: isLoading } = useUpdateJobRole(() => {
onClose?.(); onClose?.();
}); });
// const onSubmit = (formdata) => { // const onSubmit = (formdata) => {
// setIsLoading(true) // setIsLoading(true)
// const result = { // const result = {
// id:data?.id, // id:data?.id,
// name: formdata?.role, // name: formdata?.role,
// description: formdata.description, // description: formdata.description,
// }; // };
// MasterRespository.updateJobRole(data?.id,result).then((resp)=>{
// setIsLoading(false)
// showToast("JobRole Update successfully.", "success");
// const cachedData = getCachedData("Job Role");
// if (cachedData) {
// const updatedData = cachedData.map((role) =>
// role.id === data?.id ? { ...role, ...resp.data } : role
// );
// cacheData("Job Role", updatedData);
// }
// onClose() // MasterRespository.updateJobRole(data?.id,result).then((resp)=>{
// }).catch((error)=>{ // setIsLoading(false)
// showToast(error.message, "error") // showToast("JobRole Update successfully.", "success");
// setIsLoading(false) // const cachedData = getCachedData("Job Role");
// }) // if (cachedData) {
// }; // const updatedData = cachedData.map((role) =>
// role.id === data?.id ? { ...role, ...resp.data } : role
// );
// cacheData("Job Role", updatedData);
// }
// onClose()
// }).catch((error)=>{
// showToast(error.message, "error")
// setIsLoading(false)
// })
// };
const onSubmit = (formData) => { const onSubmit = (formData) => {
updateJobRole({ updateJobRole({
@ -94,9 +94,9 @@ const [descriptionLength, setDescriptionLength] = useState(data?.description?.le
}); });
return () => sub.unsubscribe(); return () => sub.unsubscribe();
}, [watch]); }, [watch]);
return (<> return (<>
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}> <form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
{/* <div className="col-12 col-md-12"> {/* <div className="col-12 col-md-12">
<label className="fs-5 text-dark text-center d-flex align-items-center justify-content-center flex-wrap">Edit Job Role</label> <label className="fs-5 text-dark text-center d-flex align-items-center justify-content-center flex-wrap">Edit Job Role</label>
</div> */} </div> */}
@ -126,14 +126,16 @@ const [descriptionLength, setDescriptionLength] = useState(data?.description?.le
<p className="text-danger">{errors.description.message}</p> <p className="text-danger">{errors.description.message}</p>
)} )}
</div> </div>
<div className="col-12 text-end"> <div className="col-12 text-center">
<button <button type="submit" className="btn btn-sm btn-primary me-3">
{isLoading ? "Please Wait..." : "Submit"}
</button>
<button
type="button" type="button"
className="btn btn-sm btn-label-secondary me-3" className="btn btn-sm btn-label-secondary"
data-bs-dismiss="modal" onClick={onClose} // 👈 This will now close the popup
aria-label="Close"
> >
Cancel Cancel
</button> </button>
@ -142,10 +144,11 @@ const [descriptionLength, setDescriptionLength] = useState(data?.description?.le
</button> </button>
</div> </div>
</form> </form>
</> </>
) )
} }

View File

@ -279,13 +279,11 @@ const EditMaster = ({ master, onClose }) => {
<div className="col-12 text-end"> <div className="col-12 text-end">
<button <button
type="button" type="button"
className="btn btn-sm btn-label-secondary me-3" className="btn btn-sm btn-label-secondary"
data-bs-dismiss="modal" onClick={onClose}
aria-label="Close"
> >
Cancel Cancel
</button> </button>
<button type="submit" className="btn btn-sm btn-primary"> {isLoading ? "Please Wait..." : "Submit"}</button>
</div> </div>

View File

@ -1,103 +1,103 @@
import React, { useEffect,useState } from 'react' import React, { useEffect, useState } from 'react'
import { useForm ,Controller} from 'react-hook-form'; import { useForm, Controller } from 'react-hook-form';
import { set, z } from 'zod'; import { set, z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { MasterRespository } from '../../repositories/MastersRepository'; import { MasterRespository } from '../../repositories/MastersRepository';
import { cacheData,getCachedData } from '../../slices/apiDataManager'; import { cacheData, getCachedData } from '../../slices/apiDataManager';
import showToast from '../../services/toastService'; import showToast from '../../services/toastService';
import {useUpdateWorkCategory} from '../../hooks/masterHook/useMaster'; import { useUpdateWorkCategory } from '../../hooks/masterHook/useMaster';
const schema = z.object({ const schema = z.object({
name: z.string().min(1, { message: "Work Category is required" }), name: z.string().min(1, { message: "Work Category is required" }),
description: z.string().min(1, { message: "Description is required" }) description: z.string().min(1, { message: "Description is required" })
.max(255, { message: "Description cannot exceed 255 characters" }), .max(255, { message: "Description cannot exceed 255 characters" }),
}); });
const EditWorkCategory = ({data,onClose}) => { const EditWorkCategory = ({ data, onClose }) => {
const {
register,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: zodResolver(schema),
defaultValues: {
name: data?.name || "",
description: data?.description || "",
},
});
const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0); const {
const maxDescriptionLength = 255; register,
handleSubmit,
formState: { errors },
reset,
} = useForm({
resolver: zodResolver(schema),
defaultValues: {
name: data?.name || "",
description: data?.description || "",
},
});
const { mutate: updateWorkCategory, isPending: isLoading } = useUpdateWorkCategory(() => onClose?.()); const [descriptionLength, setDescriptionLength] = useState(data?.description?.length || 0);
const maxDescriptionLength = 255;
const onSubmit = (formdata) => { const { mutate: updateWorkCategory, isPending: isLoading } = useUpdateWorkCategory(() => onClose?.());
const payload = {
id: data?.id, const onSubmit = (formdata) => {
name: formdata.name, const payload = {
description: formdata.description, id: data?.id,
name: formdata.name,
description: formdata.description,
};
updateWorkCategory({ id: data?.id, payload });
}; };
updateWorkCategory({id:data?.id,payload}); // const onSubmit = (formdata) => {
}; // setIsLoading(true)
// const result = {
// const onSubmit = (formdata) => { // id:data?.id,
// setIsLoading(true) // name: formdata?.name,
// const result = { // description: formdata.description,
// id:data?.id, // };
// name: formdata?.name,
// description: formdata.description,
// };
// MasterRespository.updateWorkCategory(data?.id,result).then((resp)=>{
// setIsLoading(false)
// showToast("Work Category Update successfully.", "success");
// MasterRespository.updateWorkCategory(data?.id,result).then((resp)=>{ // const cachedData = getCachedData("Work Category");
// setIsLoading(false) // if (cachedData) {
// showToast("Work Category Update successfully.", "success");
// const cachedData = getCachedData("Work Category"); // const updatedData = cachedData.map((category) =>
// if (cachedData) { // category.id === data?.id ? { ...category, ...resp.data } : category
// );
// const updatedData = cachedData.map((category) => // cacheData("Work Category", updatedData);
// category.id === data?.id ? { ...category, ...resp.data } : category // }
// );
// cacheData("Work Category", updatedData); // onClose()
// } // }).catch((error)=>{
// showToast(error?.response?.data?.message, "error")
// onClose() // setIsLoading(false)
// }).catch((error)=>{ // })
// showToast(error?.response?.data?.message, "error")
// setIsLoading(false) // };
// }) useEffect(() => {
reset({
// }; name: data?.name || "",
useEffect(() => { description: data?.description || "",
reset({ });
name: data?.name || "", setDescriptionLength(data?.description?.length || 0);
description: data?.description || "", }, [data, reset]);
});
setDescriptionLength(data?.description?.length || 0);
}, [data, reset]);
return (<> return (<>
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}> <form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 col-md-12"> <div className="col-12 col-md-12">
<label className="form-label">Category Name</label> <label className="form-label">Category Name</label>
<input type="text" <input type="text"
{...register("name")} {...register("name")}
className={`form-control ${errors.name ? 'is-invalids' : ''}`} className={`form-control ${errors.name ? 'is-invalids' : ''}`}
/> />
{errors.name && <p className="text-danger">{errors.name.message}</p>} {errors.name && <p className="text-danger">{errors.name.message}</p>}
</div> </div>
<div className="col-12 col-md-12"> <div className="col-12 col-md-12">
<label className="form-label" htmlFor="description">Description</label> <label className="form-label" htmlFor="description">Description</label>
<textarea <textarea
rows="3" rows="3"
{...register("description")} {...register("description")}
@ -114,25 +114,25 @@ useEffect(() => {
<p className="text-danger">{errors.description.message}</p> <p className="text-danger">{errors.description.message}</p>
)} )}
</div> </div>
<div className="col-12 text-center"> <div className="col-12 text-center">
<button type="submit" className="btn btn-sm btn-primary me-3"> <button type="submit" className="btn btn-sm btn-primary me-3">
{isLoading? "Please Wait...":"Submit"} {isLoading ? "Please Wait..." : "Submit"}
</button> </button>
<button <button
type="reset" type="button"
className="btn btn-sm btn-label-secondary" className="btn btn-sm btn-label-secondary"
data-bs-dismiss="modal" onClick={onClose}
aria-label="Close"
> >
Cancel Cancel
</button> </button>
</div> </div>
</form> </form>
</> </>
) )
} }

View File

@ -18,31 +18,31 @@ const ManagePaymentMode = ({ data = null, onClose }) => {
formState: { errors }, formState: { errors },
} = useForm({ } = useForm({
resolver: zodResolver(ExpnseSchema), resolver: zodResolver(ExpnseSchema),
defaultValues: { name: "", description: "" }, defaultValues: { name: "", description: "" },
}); });
const { mutate: CreatePaymentMode, isPending } = useCreatePaymentMode(() => const { mutate: CreatePaymentMode, isPending } = useCreatePaymentMode(() =>
onClose?.() onClose?.()
); );
const {mutate:UpdatePaymentMode,isPending:Updating} = useUpdatePaymentMode(()=>onClose?.()) const { mutate: UpdatePaymentMode, isPending: Updating } = useUpdatePaymentMode(() => onClose?.())
const onSubmit = (payload) => { const onSubmit = (payload) => {
if(data){ if (data) {
UpdatePaymentMode({id:data.id,payload:{...payload,id:data.id}}) UpdatePaymentMode({ id: data.id, payload: { ...payload, id: data.id } })
}else( } else (
CreatePaymentMode(payload) CreatePaymentMode(payload)
) )
}; };
useEffect(()=>{ useEffect(() => {
if(data){ if (data) {
reset({ reset({
name:data.name ?? "", name: data.name ?? "",
description:data.description ?? "" description: data.description ?? ""
}) })
} }
},[data]) }, [data])
return ( return (
@ -71,8 +71,19 @@ const ManagePaymentMode = ({ data = null, onClose }) => {
)} )}
</div> </div>
<div className="col-12 text-center">
<button
type="submit"
className="btn btn-sm btn-primary me-3"
disabled={isPending || Updating}
>
{isPending || Updating ? "Please Wait..." : Updating ? "Update" : "Submit"}
</button>
<div className="col-12 text-end"> <div className="col-12 text-end">
<button <button
type="button"
className="btn btn-sm btn-label-secondary"
onClick={onClose} // call onClose here
type="reset" type="reset"
className="btn btn-sm btn-label-secondary me-3" className="btn btn-sm btn-label-secondary me-3"
data-bs-dismiss="modal" data-bs-dismiss="modal"

View File

@ -1,5 +1,5 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { cacheData, getCachedData, useSelectedproject } from "../slices/apiDataManager"; import { cacheData, getCachedData, useSelectedProject } from "../slices/apiDataManager";
import AttendanceRepository from "../repositories/AttendanceRepository"; import AttendanceRepository from "../repositories/AttendanceRepository";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import showToast from "../services/toastService"; import showToast from "../services/toastService";
@ -143,8 +143,7 @@ export const useRegularizationRequests = (projectId) => {
export const useMarkAttendance = () => { export const useMarkAttendance = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
// const selectedProject = useSelector((store)=>store.localVariables.projectId) const selectedProject = useSelectedProject();
const selectedProject = useSelectedproject();
const selectedDateRange = useSelector((store)=>store.localVariables.defaultDateRange) const selectedDateRange = useSelector((store)=>store.localVariables.defaultDateRange)
return useMutation({ return useMutation({

View File

@ -17,13 +17,13 @@ const cleanFilter = (filter) => {
}); });
// moment.utc() to get consistent UTC ISO strings // moment.utc() to get consistent UTC ISO strings
if (!cleaned.startDate) { // if (!cleaned.startDate) {
cleaned.startDate = moment.utc().subtract(7, "days").startOf("day").toISOString(); // cleaned.startDate = moment.utc().subtract(7, "days").startOf("day").toISOString();
} // }
if (!cleaned.endDate) { // if (!cleaned.endDate) {
cleaned.endDate = moment.utc().startOf("day").toISOString(); // cleaned.endDate = moment.utc().startOf("day").toISOString();
} // }
return cleaned; return cleaned;
}; };

View File

@ -4,7 +4,7 @@ import {
clearCacheKey, clearCacheKey,
getCachedData, getCachedData,
getCachedProfileData, getCachedProfileData,
useSelectedproject, useSelectedProject,
} from "../../slices/apiDataManager"; } from "../../slices/apiDataManager";
import Breadcrumb from "../../components/common/Breadcrumb"; import Breadcrumb from "../../components/common/Breadcrumb";
import AttendanceLog from "../../components/Activities/AttendcesLogs"; import AttendanceLog from "../../components/Activities/AttendcesLogs";
@ -26,11 +26,11 @@ import { useQueryClient } from "@tanstack/react-query";
const AttendancePage = () => { const AttendancePage = () => {
const [activeTab, setActiveTab] = useState("all"); const [activeTab, setActiveTab] = useState("all");
const [ShowPending, setShowPending] = useState(false); const [ShowPending, setShowPending] = useState(false);
const [searchTerm, setSearchTerm] = useState(""); // 🔹 New state for search const [searchTerm, setSearchTerm] = useState("");
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const loginUser = getCachedProfileData(); const loginUser = getCachedProfileData();
// const selectedProject = useSelector((store) => store.localVariables.projectId);
const selectedProject = useSelectedproject(); const selectedProject = useSelectedProject();
const dispatch = useDispatch(); const dispatch = useDispatch();
const [attendances, setAttendances] = useState(); const [attendances, setAttendances] = useState();

View File

@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useRef, useState } from "react"; import React, { useEffect, useMemo, useState } from "react";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { useTaskList } from "../../hooks/useTasks"; import { useTaskList } from "../../hooks/useTasks";
import { useProjectName } from "../../hooks/useProjects"; import { useProjectName } from "../../hooks/useProjects";
@ -13,13 +13,13 @@ import SubTask from "../../components/Activities/SubTask";
import { formatNumber, formatUTCToLocalTime } from "../../utils/dateUtils"; import { formatNumber, formatUTCToLocalTime } from "../../utils/dateUtils";
import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { APPROVE_TASK, ASSIGN_REPORT_TASK } from "../../utils/constants"; import { APPROVE_TASK, ASSIGN_REPORT_TASK } from "../../utils/constants";
import { useSelectedproject } from "../../slices/apiDataManager"; import { useSelectedProject } from "../../slices/apiDataManager";
import moment from "moment"; import moment from "moment";
import Loader from "../../components/common/Loader"; import Loader from "../../components/common/Loader";
const DailyTask = () => { const DailyTask = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const selectedProject = useSelectedproject(); const selectedProject = useSelectedProject();
const { projectNames } = useProjectName(); const { projectNames } = useProjectName();
const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK); const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK);
const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK); const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK);
@ -41,10 +41,20 @@ const DailyTask = () => {
// Ensure project is set // Ensure project is set
useEffect(() => { useEffect(() => {
if (!selectedProject && projectNames.length > 0) { if (!selectedProject && projectNames.length > 0) {
debugger
dispatch(setProjectId(projectNames[0].id)); dispatch(setProjectId(projectNames[0].id));
} }
}, [selectedProject, projectNames, dispatch]); }, [selectedProject, projectNames, dispatch]);
// 🔹 Reset filters when project changes
useEffect(() => {
setFilters({
selectedBuilding: "",
selectedFloors: [],
selectedActivities: [],
});
}, [selectedProject]);
// Memoized filtering // Memoized filtering
const filteredTasks = useMemo(() => { const filteredTasks = useMemo(() => {
if (!TaskList) return []; if (!TaskList) return [];
@ -91,8 +101,8 @@ const DailyTask = () => {
data-bs-content={` data-bs-content={`
<div class="border border-secondary rounded custom-popover p-2 px-3"> <div class="border border-secondary rounded custom-popover p-2 px-3">
${task.teamMembers ${task.teamMembers
.map( .map(
(m) => ` (m) => `
<div class="d-flex align-items-center gap-2 mb-2"> <div class="d-flex align-items-center gap-2 mb-2">
<div class="avatar avatar-xs"> <div class="avatar avatar-xs">
<span class="avatar-initial rounded-circle bg-label-primary"> <span class="avatar-initial rounded-circle bg-label-primary">
@ -101,8 +111,8 @@ const DailyTask = () => {
</div> </div>
<span>${m.firstName} ${m.lastName}</span> <span>${m.firstName} ${m.lastName}</span>
</div>` </div>`
) )
.join("")} .join("")}
</div> </div>
`} `}
> >
@ -163,6 +173,7 @@ const DailyTask = () => {
currentSelectedBuilding={filters.selectedBuilding} currentSelectedBuilding={filters.selectedBuilding}
currentSelectedFloors={filters.selectedFloors} currentSelectedFloors={filters.selectedFloors}
currentSelectedActivities={filters.selectedActivities} currentSelectedActivities={filters.selectedActivities}
selectedProject={selectedProject}
/> />
</div> </div>

View File

@ -4,21 +4,19 @@ import InfraPlanning from "../../components/Activities/InfraPlanning";
import { useProjectName } from "../../hooks/useProjects"; import { useProjectName } from "../../hooks/useProjects";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { setProjectId } from "../../slices/localVariablesSlice"; import { setProjectId } from "../../slices/localVariablesSlice";
import { useSelectedproject } from "../../slices/apiDataManager"; import { useSelectedProject } from "../../slices/apiDataManager";
const TaskPlannng = () => { const TaskPlannng = () => {
const selectedProject = useSelectedproject(); const selectedProject = useSelectedProject();
const dispatch = useDispatch(); const dispatch = useDispatch();
const { projectNames, loading: projectLoading } = useProjectName(); const { projectNames = [], loading: projectLoading } = useProjectName();
const initialized = useRef(false); useEffect(() => {
if (!selectedProject) {
dispatch(setProjectId(projectNames[0].id));
}
}, [projectNames, selectedProject?.id, dispatch]);
useEffect(() => {
if (!initialized.current && projectNames.length > 0 && !selectedProject?.id) {
dispatch(setProjectId(projectNames[0].id));
initialized.current = true;
}
}, [projectNames, selectedProject, dispatch]);
return ( return (
<div className="container-fluid"> <div className="container-fluid">

View File

@ -210,7 +210,7 @@ const LoginPage = () => {
Login With Password Login With Password
</a> </a>
) : ( ) : (
<Link to="/auth/reqest/demo" className="registration-link"> <Link to="/market/enquire" className="registration-link">
Request a Demo Request a Demo
</Link> </Link>
)} )}

View File

@ -18,7 +18,7 @@ import {
VIEW_ALL_EMPLOYEES, VIEW_ALL_EMPLOYEES,
VIEW_TEAM_MEMBERS, VIEW_TEAM_MEMBERS,
} from "../../utils/constants"; } from "../../utils/constants";
import { clearCacheKey, useSelectedproject } from "../../slices/apiDataManager"; import { clearCacheKey, useSelectedProject } from "../../slices/apiDataManager";
import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import SuspendEmp from "../../components/Employee/SuspendEmp"; // Keep if you use SuspendEmp import SuspendEmp from "../../components/Employee/SuspendEmp"; // Keep if you use SuspendEmp
import { import {
@ -41,7 +41,7 @@ const EmployeeList = () => {
// const selectedProjectId = useSelector( // const selectedProjectId = useSelector(
// (store) => store.localVariables.projectId // (store) => store.localVariables.projectId
// ); // );
const selectedProjectId = useSelectedproject(); const selectedProjectId = useSelectedProject();
const { projectNames, loading: projectLoading, fetchData } = useProjectName(); const { projectNames, loading: projectLoading, fetchData } = useProjectName();
const dispatch = useDispatch(); const dispatch = useDispatch();

View File

@ -122,14 +122,17 @@ const MasterPage = () => {
onChange={(e) => dispatch(changeMaster(e.target.value))} onChange={(e) => dispatch(changeMaster(e.target.value))}
name="DataTables_Table_0_length" name="DataTables_Table_0_length"
aria-controls="DataTables_Table_0" aria-controls="DataTables_Table_0"
className="form-select form-select-sm" className="form-select py-1 px-2"
style={{ fontSize: "0.875rem", height: "32px", width: "150px" }}
value={selectedMaster} value={selectedMaster}
> >
{isLoading && (<option value={null}>Loading...</option>)} {isLoading && <option value="">Loading...</option>}
{(!isLoading && data) && data?.map((item) => ( {!isLoading &&
data?.map((item) => (
<option key={item.id} value={item.name}>{item.name}</option> <option key={item.id} value={item.name}>
))} {item.name}
</option>
))}
</select> </select>
</label> </label>
</div> </div>

View File

@ -13,7 +13,7 @@ import {
cacheData, cacheData,
clearCacheKey, clearCacheKey,
getCachedData, getCachedData,
useSelectedproject, useSelectedProject,
} from "../../slices/apiDataManager"; } from "../../slices/apiDataManager";
import "./ProjectDetails.css"; import "./ProjectDetails.css";
import { import {
@ -29,7 +29,7 @@ import { setProjectId } from "../../slices/localVariablesSlice";
const ProjectDetails = () => { const ProjectDetails = () => {
const projectId = useSelectedproject() const projectId = useSelectedProject()
const { projectNames, fetchData } = useProjectName(); const { projectNames, fetchData } = useProjectName();
const dispatch = useDispatch() const dispatch = useDispatch()

View File

@ -34,6 +34,8 @@ export const DirectoryRepository = {
DeleteNote: (id, isActive) => DeleteNote: (id, isActive) =>
api.delete(`/api/directory/note/${id}?active=${isActive}`), api.delete(`/api/directory/note/${id}?active=${isActive}`),
GetNotes: (pageSize, pageNumber) => GetNotes: (pageSize, pageNumber, projectId) =>
api.get(`/api/directory/notes?pageSize=${pageSize}&pageNumber=${pageNumber}`), api.get(
`/api/directory/notes?pageSize=${pageSize}&pageNumber=${pageNumber}&projectId=${projectId}`
),
}; };

View File

@ -1,6 +1,6 @@
import { api } from "../utils/axiosClient"; import { api } from "../utils/axiosClient";
export const MarketRepository = { export const MarketRepository = {
requestDemo: (data) => api.post("/api/market/inquiry", data), requestDemo: (data) => api.post("/api/market/enquire", data),
getIndustries: () => api.get("api/market/industries"), getIndustries: () => api.get("api/market/industries"),
}; };

View File

@ -52,7 +52,7 @@ const router = createBrowserRouter(
children: [ children: [
{ path: "/auth/login", element: <LoginPage /> }, { path: "/auth/login", element: <LoginPage /> },
{ path: "/auth/login-otp", element: <LoginWithOtp /> }, { path: "/auth/login-otp", element: <LoginWithOtp /> },
{ path: "/auth/reqest/demo", element: <RegisterPage /> }, { path: "/market/enquire", element: <RegisterPage /> },
{ path: "/auth/forgot-password", element: <ForgotPasswordPage /> }, { path: "/auth/forgot-password", element: <ForgotPasswordPage /> },
{ path: "/reset-password", element: <ResetPasswordPage /> }, { path: "/reset-password", element: <ResetPasswordPage /> },
{ path: "/legal-info", element: <LegalInfoCard /> }, { path: "/legal-info", element: <LegalInfoCard /> },

View File

@ -33,13 +33,12 @@ export const clearAllCache = () => {
export const cacheProfileData = ( data) => { export const cacheProfileData = ( data) => {
store.dispatch(setLoginUserPermmisions(data)); store.dispatch(setLoginUserPermmisions(data));
}; };
// Get cached data // Get cached data
export const getCachedProfileData = () => { export const getCachedProfileData = () => {
return store.getState().globalVariables.loginUser; return store.getState().globalVariables.loginUser;
}; };
export const useSelectedproject = () => { export const useSelectedProject = () => {
const selectedProject = useSelector((store)=> store.localVariables.projectId); const selectedProject = useSelector((store)=> store.localVariables.projectId);
var project = localStorage.getItem("project"); var project = localStorage.getItem("project");
if(project){ if(project){
@ -47,7 +46,7 @@ export const useSelectedproject = () => {
} else{ } else{
return selectedProject return selectedProject
} }
// return project ? selectedProject
}; };