- Add date format hook
- add Ui for Activities
This commit is contained in:
parent
b85b812981
commit
6883d87bdc
10
package-lock.json
generated
10
package-lock.json
generated
@ -18,6 +18,7 @@
|
|||||||
"apexcharts": "^4.5.0",
|
"apexcharts": "^4.5.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"axios-retry": "^4.5.0",
|
"axios-retry": "^4.5.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"dotenv-webpack": "^8.1.0",
|
"dotenv-webpack": "^8.1.0",
|
||||||
"eventemitter3": "^5.0.1",
|
"eventemitter3": "^5.0.1",
|
||||||
@ -2455,6 +2456,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/date-fns": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/kossnocorp"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"apexcharts": "^4.5.0",
|
"apexcharts": "^4.5.0",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"axios-retry": "^4.5.0",
|
"axios-retry": "^4.5.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"dotenv-webpack": "^8.1.0",
|
"dotenv-webpack": "^8.1.0",
|
||||||
"eventemitter3": "^5.0.1",
|
"eventemitter3": "^5.0.1",
|
||||||
|
155
src/components/Employee/EmpActivities.jsx
Normal file
155
src/components/Employee/EmpActivities.jsx
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import moment from "moment";
|
||||||
|
import DateRangePicker from "../common/DateRangePicker";
|
||||||
|
import useFormattedDate from "../../hooks/useFormattedDate";
|
||||||
|
import { useProjectTasksByEmployee } from "../../hooks/useProjects";
|
||||||
|
|
||||||
|
const EmpActivities = ({ employee }) => {
|
||||||
|
const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" });
|
||||||
|
const myDate = new Date("2025-08-06T10:30:00Z");
|
||||||
|
const formattedToday = useFormattedDate(myDate, "dd-MMM-yyyy");
|
||||||
|
console.log(employee);
|
||||||
|
|
||||||
|
const {
|
||||||
|
ProjectTaskList,
|
||||||
|
loading: selectedProjectLoding,
|
||||||
|
refetch,
|
||||||
|
} = useProjectTasksByEmployee(employee?.id);
|
||||||
|
|
||||||
|
console.log(ProjectTaskList);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="card h-100 mt-4">
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="my-0 text-start">
|
||||||
|
<DateRangePicker
|
||||||
|
DateDifference="30"
|
||||||
|
onRangeChange={setDateRange}
|
||||||
|
endDateMode="today"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ul className="timeline mb-0 mt-5 text-start">
|
||||||
|
<li className="timeline-item timeline-item-transparent">
|
||||||
|
<span className="timeline-point timeline-point-primary"></span>
|
||||||
|
<div className="timeline-event">
|
||||||
|
<div className="timeline-header mb-3">
|
||||||
|
<h6 className="mb-0">Matrix Properties</h6>
|
||||||
|
<small className="text-body-secondary">
|
||||||
|
{formattedToday}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<p className="mb-2">Branch Fitting</p>
|
||||||
|
<p className="mb-2">
|
||||||
|
Building 1 > First Floor > Zone One
|
||||||
|
</p>
|
||||||
|
<p className="mb-2">
|
||||||
|
<span>Planned: 22 Meter</span>
|
||||||
|
<span className="ms-2">Completed: 22 Meter</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li className="timeline-item timeline-item-transparent">
|
||||||
|
<span className="timeline-point timeline-point-success"></span>
|
||||||
|
<div className="timeline-event">
|
||||||
|
<div className="timeline-header mb-3">
|
||||||
|
<h6 className="mb-0">Client Meeting</h6>
|
||||||
|
<small className="text-body-secondary">45 min ago</small>
|
||||||
|
</div>
|
||||||
|
<p className="mb-2">Project meeting with john @10:15am</p>
|
||||||
|
<div className="d-flex justify-content-between flex-wrap gap-2 mb-2">
|
||||||
|
<div className="d-flex flex-wrap align-items-center mb-50">
|
||||||
|
<div className="avatar avatar-sm me-2">
|
||||||
|
<img
|
||||||
|
src="../../assets/img/avatars/1.png"
|
||||||
|
alt="Avatar"
|
||||||
|
className="rounded-circle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="mb-0 small fw-medium">
|
||||||
|
Lester McCarthy (Client)
|
||||||
|
</p>
|
||||||
|
<small>CEO of ThemeSelection</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li className="timeline-item timeline-item-transparent">
|
||||||
|
<span className="timeline-point timeline-point-info"></span>
|
||||||
|
<div className="timeline-event">
|
||||||
|
<div className="timeline-header mb-3">
|
||||||
|
<h6 className="mb-0">Create a new project for client</h6>
|
||||||
|
<small className="text-body-secondary">2 Day Ago</small>
|
||||||
|
</div>
|
||||||
|
<p className="mb-2">6 team members in a project</p>
|
||||||
|
<ul className="list-group list-group-flush">
|
||||||
|
<li className="list-group-item d-flex justify-content-between align-items-center flex-wrap border-top-0 p-0">
|
||||||
|
<div className="d-flex flex-wrap align-items-center">
|
||||||
|
<ul className="list-unstyled users-list d-flex align-items-center avatar-group m-0 me-2">
|
||||||
|
<li
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-popup="tooltip-custom"
|
||||||
|
data-bs-placement="top"
|
||||||
|
className="avatar pull-up"
|
||||||
|
aria-label="Vinnie Mostowy"
|
||||||
|
data-bs-original-title="Vinnie Mostowy"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="rounded-circle"
|
||||||
|
src="../../assets/img/avatars/5.png"
|
||||||
|
alt="Avatar"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-popup="tooltip-custom"
|
||||||
|
data-bs-placement="top"
|
||||||
|
className="avatar pull-up"
|
||||||
|
aria-label="Allen Rieske"
|
||||||
|
data-bs-original-title="Allen Rieske"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="rounded-circle"
|
||||||
|
src="../../assets/img/avatars/12.png"
|
||||||
|
alt="Avatar"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-popup="tooltip-custom"
|
||||||
|
data-bs-placement="top"
|
||||||
|
className="avatar pull-up"
|
||||||
|
aria-label="Julee Rossignol"
|
||||||
|
data-bs-original-title="Julee Rossignol"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="rounded-circle"
|
||||||
|
src="../../assets/img/avatars/6.png"
|
||||||
|
alt="Avatar"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li className="avatar">
|
||||||
|
<span
|
||||||
|
className="avatar-initial rounded-circle pull-up"
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-placement="bottom"
|
||||||
|
data-bs-original-title="3 more"
|
||||||
|
>
|
||||||
|
+3
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EmpActivities;
|
@ -120,7 +120,7 @@ const EmpAttendance = ({ employee }) => {
|
|||||||
<AttendLogs Id={attendanceId} />
|
<AttendLogs Id={attendanceId} />
|
||||||
</GlobalModel>
|
</GlobalModel>
|
||||||
)}
|
)}
|
||||||
<div className="card px-4 mt-0 py-2 " style={{ minHeight: "500px" }}>
|
<div className="card px-4 mt-5 py-2 " style={{ minHeight: "500px" }}>
|
||||||
<div
|
<div
|
||||||
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"
|
||||||
|
@ -24,7 +24,7 @@ const EmpBanner = ({ profile, loggedInUser }) => {
|
|||||||
)}
|
)}
|
||||||
<div className="user-profile-header d-flex flex-column flex-lg-row text-sm-start text-center mb-8">
|
<div className="user-profile-header d-flex flex-column flex-lg-row text-sm-start text-center mb-8">
|
||||||
<div className="flex-shrink-0 mt-1 mx-sm-0 mx-auto">
|
<div className="flex-shrink-0 mt-1 mx-sm-0 mx-auto">
|
||||||
{profile.gender.toLowerCase() == "male" && (
|
{profile?.gender?.toLowerCase() == "male" && (
|
||||||
<img
|
<img
|
||||||
width={125}
|
width={125}
|
||||||
src="../../assets/img/avatars/avatar_m_01.png"
|
src="../../assets/img/avatars/avatar_m_01.png"
|
||||||
@ -32,7 +32,7 @@ const EmpBanner = ({ profile, loggedInUser }) => {
|
|||||||
className="d-block h-auto ms-0 ms-sm-6 rounded-3 user-profile-img"
|
className="d-block h-auto ms-0 ms-sm-6 rounded-3 user-profile-img"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{profile.gender.toLowerCase() == "female" && (
|
{profile?.gender?.toLowerCase() == "female" && (
|
||||||
<img
|
<img
|
||||||
width={125}
|
width={125}
|
||||||
src="../../assets/img/avatars/avatar_f_02.png"
|
src="../../assets/img/avatars/avatar_f_02.png"
|
||||||
|
@ -5,7 +5,7 @@ const EmpDocuments = ({ profile, loggedInUser }) => {
|
|||||||
<>
|
<>
|
||||||
<div
|
<div
|
||||||
id="DataTables_Table_0_wrapper"
|
id="DataTables_Table_0_wrapper"
|
||||||
class="dt-container dt-bootstrap5 dt-empty-footer"
|
class=" card mt-5 dt-container dt-bootstrap5 dt-empty-footer"
|
||||||
>
|
>
|
||||||
<div class="row card-header border-bottom mx-0 px-3 py-2">
|
<div class="row card-header border-bottom mx-0 px-3 py-2">
|
||||||
<div class="d-md-flex justify-content-between align-items-center dt-layout-start col-md-auto me-auto">
|
<div class="d-md-flex justify-content-between align-items-center dt-layout-start col-md-auto me-auto">
|
||||||
|
@ -2,7 +2,7 @@ import React, { useEffect, useRef } from "react";
|
|||||||
|
|
||||||
const DateRangePicker = ({
|
const DateRangePicker = ({
|
||||||
onRangeChange,
|
onRangeChange,
|
||||||
DateDifference = 7,
|
DateDifference = 7,
|
||||||
endDateMode = "yesterday",
|
endDateMode = "yesterday",
|
||||||
}) => {
|
}) => {
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
@ -10,22 +10,22 @@ const DateRangePicker = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const endDate = new Date();
|
const endDate = new Date();
|
||||||
if (endDateMode === "yesterday") {
|
if (endDateMode === "yesterday") {
|
||||||
endDate.setDate(endDate.getDate() - 1);
|
endDate.setDate(endDate.getDate() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
endDate.setHours(0, 0, 0, 0);
|
endDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
const startDate = new Date(endDate);
|
const startDate = new Date(endDate);
|
||||||
startDate.setDate(endDate.getDate() - (DateDifference - 1));
|
startDate.setDate(endDate.getDate() - (DateDifference - 1));
|
||||||
startDate.setHours(0, 0, 0, 0);
|
startDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
const fp = flatpickr(inputRef.current, {
|
const fp = flatpickr(inputRef.current, {
|
||||||
mode: "range",
|
mode: "range",
|
||||||
dateFormat: "Y-m-d",
|
dateFormat: "Y-m-d",
|
||||||
altInput: true,
|
altInput: true,
|
||||||
altFormat: "d-m-Y",
|
altFormat: "d-m-Y",
|
||||||
defaultDate: [startDate, endDate],
|
defaultDate: [startDate, endDate],
|
||||||
static: true,
|
static: true,
|
||||||
clickOpens: true,
|
clickOpens: true,
|
||||||
maxDate: endDate, // ✅ Disable future dates
|
maxDate: endDate, // ✅ Disable future dates
|
||||||
onChange: (selectedDates, dateStr) => {
|
onChange: (selectedDates, dateStr) => {
|
||||||
@ -35,8 +35,8 @@ const DateRangePicker = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
onRangeChange?.({
|
onRangeChange?.({
|
||||||
startDate: startDate.toLocaleDateString("en-CA"),
|
startDate: startDate.toLocaleDateString("en-CA"),
|
||||||
endDate: endDate.toLocaleDateString("en-CA"),
|
endDate: endDate.toLocaleDateString("en-CA"),
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
26
src/hooks/useFormattedDate.js
Normal file
26
src/hooks/useFormattedDate.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { format } from "date-fns";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom hook to format a date string, object, or number.
|
||||||
|
* @param {string | Date | number} date - The date to be formatted.
|
||||||
|
* @param {string} formatString - The format string (e.g., 'MMMM do, yyyy').
|
||||||
|
* @returns {string} The formatted date string.
|
||||||
|
*/
|
||||||
|
const useFormattedDate = (date, formatString) => {
|
||||||
|
// Return early if no date is provided
|
||||||
|
if (!date) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const dateObj = new Date(date);
|
||||||
|
// You could also add error handling for invalid dates
|
||||||
|
// if (!isValid(dateObj)) return 'Invalid Date';
|
||||||
|
return format(dateObj, formatString);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error formatting date:", error);
|
||||||
|
return ""; // Return an empty string or a default value on error
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useFormattedDate;
|
@ -395,6 +395,34 @@ export const useProjectTasks = (workAreaId, IsExpandedArea = false) => {
|
|||||||
return { ProjectTaskList, isLoading, error };
|
return { ProjectTaskList, isLoading, error };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useProjectTasksByEmployee = (
|
||||||
|
employeeId,
|
||||||
|
fromDate,
|
||||||
|
toDate,
|
||||||
|
IsExpandedArea = false
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
|
data: ProjectTaskList,
|
||||||
|
isLoading,
|
||||||
|
error,
|
||||||
|
} = useQuery({
|
||||||
|
queryKey: ["TasksByEmployee", employeeId],
|
||||||
|
queryFn: async () => {
|
||||||
|
const res = await ProjectRepository.getProjectTasksByEmployee(
|
||||||
|
employeeId,
|
||||||
|
fromDate,
|
||||||
|
toDate
|
||||||
|
);
|
||||||
|
return res.data;
|
||||||
|
},
|
||||||
|
enabled: !!employeeId && !!IsExpandedArea,
|
||||||
|
onError: (error) => {
|
||||||
|
showToast(error.message || "Error while Fetching project Tasks", "error");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return { ProjectTaskList, isLoading, error };
|
||||||
|
};
|
||||||
|
|
||||||
// -- -------------Mutation-------------------------------
|
// -- -------------Mutation-------------------------------
|
||||||
|
|
||||||
export const useCreateProject = ({ onSuccessCallback }) => {
|
export const useCreateProject = ({ onSuccessCallback }) => {
|
||||||
|
@ -21,6 +21,7 @@ import ManageEmployee from "../../components/Employee/ManageEmployee";
|
|||||||
import EmpBanner from "../../components/Employee/EmpBanner";
|
import EmpBanner from "../../components/Employee/EmpBanner";
|
||||||
import EmpDashboard from "../../components/Employee/EmpDashboard";
|
import EmpDashboard from "../../components/Employee/EmpDashboard";
|
||||||
import EmpDocuments from "../../components/Employee/EmpDocuments";
|
import EmpDocuments from "../../components/Employee/EmpDocuments";
|
||||||
|
import EmpActivities from "../../components/Employee/EmpActivities";
|
||||||
|
|
||||||
const EmployeeProfile = () => {
|
const EmployeeProfile = () => {
|
||||||
const { profile } = useProfile();
|
const { profile } = useProfile();
|
||||||
@ -85,7 +86,7 @@ const EmployeeProfile = () => {
|
|||||||
case "activities": {
|
case "activities": {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ComingSoonPage />
|
<EmpActivities employee={currentEmployee} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
import { api } from "../utils/axiosClient";
|
import { api } from "../utils/axiosClient";
|
||||||
|
|
||||||
const EmployeeRepository = {
|
const EmployeeRepository = {
|
||||||
getAllEmployeeList:(showInactive)=>api.get(`api/employee/list?showInactive=${showInactive}`),
|
getAllEmployeeList: (showInactive) =>
|
||||||
|
api.get(`api/employee/list?showInactive=${showInactive}`),
|
||||||
getEmployeeListByproject: (projectid) =>
|
getEmployeeListByproject: (projectid) =>
|
||||||
api.get(`/api/employee/list/${projectid}`),
|
api.get(`/api/employee/list/${projectid}`),
|
||||||
searchEmployees: (query) =>
|
searchEmployees: (query) => api.get(`/api/employee/search/${query}`),
|
||||||
api.get(`/api/employee/search/${query}`),
|
manageEmployee: (data) => api.post("/api/employee/manage", data),
|
||||||
manageEmployee: (data) =>
|
|
||||||
api.post("/api/employee/manage", data),
|
|
||||||
updateEmployee: (id, data) => api.put(`/users/${id}`, data),
|
updateEmployee: (id, data) => api.put(`/users/${id}`, data),
|
||||||
// deleteEmployee: ( id ) => api.delete( `/users/${ id }` ),
|
// deleteEmployee: ( id ) => api.delete( `/users/${ id }` ),
|
||||||
getEmployeeProfile:(id)=>api.get(`/api/employee/profile/get/${id}`),
|
getEmployeeProfile: (id) => api.get(`/api/employee/profile/get/${id}`),
|
||||||
deleteEmployee:(id)=>api.delete(`/api/employee/${id}`)
|
deleteEmployee: (id) => api.delete(`/api/employee/${id}`),
|
||||||
};
|
};
|
||||||
|
|
||||||
export default EmployeeRepository;
|
export default EmployeeRepository;
|
||||||
|
@ -33,6 +33,10 @@ const ProjectRepository = {
|
|||||||
getProjectDetails: (id) => api.get(`/api/project/details/${id}`),
|
getProjectDetails: (id) => api.get(`/api/project/details/${id}`),
|
||||||
getProjectInfraByproject: (id) => api.get(`/api/project/infra-details/${id}`),
|
getProjectInfraByproject: (id) => api.get(`/api/project/infra-details/${id}`),
|
||||||
getProjectTasksByWorkArea: (id) => api.get(`/api/project/tasks/${id}`),
|
getProjectTasksByWorkArea: (id) => api.get(`/api/project/tasks/${id}`),
|
||||||
|
getProjectTasksByEmployee: (id, fromDate, toDate) =>
|
||||||
|
api.get(
|
||||||
|
`/api/project/tasks-employee/${id}?fromDate=${fromDate}&toDate:${toDate}`
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TasksRepository = {
|
export const TasksRepository = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user