Compare commits
4 Commits
04e25527e8
...
1ac9d22e2d
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ac9d22e2d | |||
| 7b0e6923a9 | |||
| d36d925e38 | |||
| 2d3412f79a |
@@ -36,12 +36,20 @@ router.get(
|
|||||||
e.name as employee_name, e.username as employee_username,
|
e.name as employee_name, e.username as employee_username,
|
||||||
s.name as supervisor_name,
|
s.name as supervisor_name,
|
||||||
d.name as department_name,
|
d.name as department_name,
|
||||||
c.name as contractor_name
|
COALESCE(oc.name, c.name) as contractor_name,
|
||||||
|
es.id as active_swap_id,
|
||||||
|
es.original_contractor_id,
|
||||||
|
es.target_contractor_id,
|
||||||
|
oc.name as original_contractor_name,
|
||||||
|
tc.name as target_contractor_name
|
||||||
FROM attendance a
|
FROM attendance a
|
||||||
JOIN users e ON a.employee_id = e.id
|
JOIN users e ON a.employee_id = e.id
|
||||||
JOIN users s ON a.supervisor_id = s.id
|
JOIN users s ON a.supervisor_id = s.id
|
||||||
LEFT JOIN departments d ON e.department_id = d.id
|
LEFT JOIN departments d ON e.department_id = d.id
|
||||||
LEFT JOIN users c ON e.contractor_id = c.id
|
LEFT JOIN users c ON e.contractor_id = c.id
|
||||||
|
LEFT JOIN employee_swaps es ON es.employee_id = e.id AND es.status = 'Active' AND es.swap_date <= a.work_date
|
||||||
|
LEFT JOIN users oc ON es.original_contractor_id = oc.id
|
||||||
|
LEFT JOIN users tc ON es.target_contractor_id = tc.id
|
||||||
WHERE 1=1
|
WHERE 1=1
|
||||||
`;
|
`;
|
||||||
const queryParams: unknown[] = [];
|
const queryParams: unknown[] = [];
|
||||||
@@ -97,12 +105,18 @@ router.get("/:id", authenticateToken, async (ctx) => {
|
|||||||
e.name as employee_name, e.username as employee_username,
|
e.name as employee_name, e.username as employee_username,
|
||||||
s.name as supervisor_name,
|
s.name as supervisor_name,
|
||||||
d.name as department_name,
|
d.name as department_name,
|
||||||
c.name as contractor_name
|
COALESCE(oc.name, c.name) as contractor_name,
|
||||||
|
es.id as active_swap_id,
|
||||||
|
oc.name as original_contractor_name,
|
||||||
|
tc.name as target_contractor_name
|
||||||
FROM attendance a
|
FROM attendance a
|
||||||
JOIN users e ON a.employee_id = e.id
|
JOIN users e ON a.employee_id = e.id
|
||||||
JOIN users s ON a.supervisor_id = s.id
|
JOIN users s ON a.supervisor_id = s.id
|
||||||
LEFT JOIN departments d ON e.department_id = d.id
|
LEFT JOIN departments d ON e.department_id = d.id
|
||||||
LEFT JOIN users c ON e.contractor_id = c.id
|
LEFT JOIN users c ON e.contractor_id = c.id
|
||||||
|
LEFT JOIN employee_swaps es ON es.employee_id = e.id AND es.status = 'Active' AND es.swap_date <= a.work_date
|
||||||
|
LEFT JOIN users oc ON es.original_contractor_id = oc.id
|
||||||
|
LEFT JOIN users tc ON es.target_contractor_id = tc.id
|
||||||
WHERE a.id = ?`,
|
WHERE a.id = ?`,
|
||||||
[attendanceId],
|
[attendanceId],
|
||||||
);
|
);
|
||||||
@@ -157,27 +171,43 @@ router.post(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if there's an active check-in (not yet checked out) for today
|
// Check if there's any existing attendance record for today
|
||||||
const activeCheckIn = await db.query<Attendance[]>(
|
const existingRecord = await db.query<Attendance[]>(
|
||||||
"SELECT * FROM attendance WHERE employee_id = ? AND work_date = ? AND status = 'CheckedIn'",
|
"SELECT * FROM attendance WHERE employee_id = ? AND work_date = ?",
|
||||||
[employeeId, workDate],
|
[employeeId, workDate],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (activeCheckIn.length > 0) {
|
|
||||||
ctx.response.status = 400;
|
|
||||||
ctx.response.body = { error: "User has an active check-in. Please check out first before checking in again." };
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const checkInTime = new Date().toISOString().slice(0, 19).replace(
|
const checkInTime = new Date().toISOString().slice(0, 19).replace(
|
||||||
"T",
|
"T",
|
||||||
" ",
|
" ",
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await db.execute(
|
let recordId: number;
|
||||||
"INSERT INTO attendance (employee_id, supervisor_id, check_in_time, work_date, status) VALUES (?, ?, ?, ?, ?)",
|
|
||||||
[employeeId, currentUser.id, checkInTime, workDate, "CheckedIn"],
|
if (existingRecord.length > 0) {
|
||||||
);
|
const record = existingRecord[0];
|
||||||
|
|
||||||
|
// If already checked in, don't allow another check-in
|
||||||
|
if (record.status === "CheckedIn") {
|
||||||
|
ctx.response.status = 400;
|
||||||
|
ctx.response.body = { error: "User has an active check-in. Please check out first before checking in again." };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If checked out or other status, update the existing record to check in again
|
||||||
|
await db.execute(
|
||||||
|
"UPDATE attendance SET check_in_time = ?, check_out_time = NULL, status = ?, supervisor_id = ? WHERE id = ?",
|
||||||
|
[checkInTime, "CheckedIn", currentUser.id, record.id],
|
||||||
|
);
|
||||||
|
recordId = record.id;
|
||||||
|
} else {
|
||||||
|
// No existing record, create new one
|
||||||
|
const result = await db.execute(
|
||||||
|
"INSERT INTO attendance (employee_id, supervisor_id, check_in_time, work_date, status) VALUES (?, ?, ?, ?, ?)",
|
||||||
|
[employeeId, currentUser.id, checkInTime, workDate, "CheckedIn"],
|
||||||
|
);
|
||||||
|
recordId = result.insertId as number;
|
||||||
|
}
|
||||||
|
|
||||||
const newRecord = await db.query<Attendance[]>(
|
const newRecord = await db.query<Attendance[]>(
|
||||||
`SELECT a.*,
|
`SELECT a.*,
|
||||||
@@ -191,10 +221,10 @@ router.post(
|
|||||||
LEFT JOIN departments d ON e.department_id = d.id
|
LEFT JOIN departments d ON e.department_id = d.id
|
||||||
LEFT JOIN users c ON e.contractor_id = c.id
|
LEFT JOIN users c ON e.contractor_id = c.id
|
||||||
WHERE a.id = ?`,
|
WHERE a.id = ?`,
|
||||||
[result.insertId],
|
[recordId],
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.response.status = 201;
|
ctx.response.status = existingRecord.length > 0 ? 200 : 201;
|
||||||
ctx.response.body = newRecord[0];
|
ctx.response.body = newRecord[0];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Check in error:", error);
|
console.error("Check in error:", error);
|
||||||
|
|||||||
@@ -29,11 +29,14 @@ router.get(
|
|||||||
e.name as employee_name, e.username as employee_username,
|
e.name as employee_name, e.username as employee_username,
|
||||||
e.phone_number as employee_phone,
|
e.phone_number as employee_phone,
|
||||||
s.name as supervisor_name,
|
s.name as supervisor_name,
|
||||||
c.name as contractor_name,
|
COALESCE(oc.name, c.name) as contractor_name,
|
||||||
sd.name as sub_department_name,
|
sd.name as sub_department_name,
|
||||||
d.name as department_name,
|
d.name as department_name,
|
||||||
d.id as department_id,
|
d.id as department_id,
|
||||||
sr.rate as standard_rate
|
sr.rate as standard_rate,
|
||||||
|
es.id as active_swap_id,
|
||||||
|
oc.name as original_contractor_name,
|
||||||
|
tc.name as target_contractor_name
|
||||||
FROM work_allocations wa
|
FROM work_allocations wa
|
||||||
JOIN users e ON wa.employee_id = e.id
|
JOIN users e ON wa.employee_id = e.id
|
||||||
JOIN users s ON wa.supervisor_id = s.id
|
JOIN users s ON wa.supervisor_id = s.id
|
||||||
@@ -42,6 +45,9 @@ router.get(
|
|||||||
LEFT JOIN departments d ON e.department_id = d.id
|
LEFT JOIN departments d ON e.department_id = d.id
|
||||||
LEFT JOIN standard_rates sr ON wa.sub_department_id = sr.sub_department_id
|
LEFT JOIN standard_rates sr ON wa.sub_department_id = sr.sub_department_id
|
||||||
AND wa.activity = sr.activity
|
AND wa.activity = sr.activity
|
||||||
|
LEFT JOIN employee_swaps es ON es.employee_id = e.id AND es.status = 'Active' AND es.swap_date <= wa.assigned_date
|
||||||
|
LEFT JOIN users oc ON es.original_contractor_id = oc.id
|
||||||
|
LEFT JOIN users tc ON es.target_contractor_id = tc.id
|
||||||
WHERE wa.status = 'Completed'
|
WHERE wa.status = 'Completed'
|
||||||
`;
|
`;
|
||||||
const queryParams: unknown[] = [];
|
const queryParams: unknown[] = [];
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export const AllRatesPage: React.FC = () => {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError("");
|
setError("");
|
||||||
try {
|
try {
|
||||||
const params: any = {};
|
const params: { departmentId?: number; startDate?: string; endDate?: string } = {};
|
||||||
if (filters.departmentId) {
|
if (filters.departmentId) {
|
||||||
params.departmentId = parseInt(filters.departmentId);
|
params.departmentId = parseInt(filters.departmentId);
|
||||||
}
|
}
|
||||||
@@ -55,8 +55,8 @@ export const AllRatesPage: React.FC = () => {
|
|||||||
const data = await api.getAllRates(params);
|
const data = await api.getAllRates(params);
|
||||||
setAllRates(data.allRates);
|
setAllRates(data.allRates);
|
||||||
setSummary(data.summary);
|
setSummary(data.summary);
|
||||||
} catch (err: any) {
|
} catch (err: unknown) {
|
||||||
setError(err.message || "Failed to fetch rates");
|
setError(err instanceof Error ? err.message : "Failed to fetch rates");
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,13 @@ export const DashboardPage: React.FC = () => {
|
|||||||
const [contractorRates, setContractorRates] = useState<
|
const [contractorRates, setContractorRates] = useState<
|
||||||
Record<number, number>
|
Record<number, number>
|
||||||
>({});
|
>({});
|
||||||
|
const [activeSwaps, setActiveSwaps] = useState<{
|
||||||
|
employee_id: number;
|
||||||
|
original_department_id: number;
|
||||||
|
target_department_id: number;
|
||||||
|
original_contractor_id?: number;
|
||||||
|
target_contractor_id?: number;
|
||||||
|
}[]>([]);
|
||||||
|
|
||||||
const refreshAllData = () => {
|
const refreshAllData = () => {
|
||||||
refreshEmployees();
|
refreshEmployees();
|
||||||
@@ -84,6 +91,12 @@ export const DashboardPage: React.FC = () => {
|
|||||||
api.getAttendance({ startDate: today, endDate: today })
|
api.getAttendance({ startDate: today, endDate: today })
|
||||||
.then(setAttendance)
|
.then(setAttendance)
|
||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
|
// Refresh active swaps for SuperAdmin
|
||||||
|
if (isSuperAdmin) {
|
||||||
|
api.getEmployeeSwaps({ status: "Active" })
|
||||||
|
.then(setActiveSwaps)
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isSuperAdmin = user?.role === "SuperAdmin";
|
const isSuperAdmin = user?.role === "SuperAdmin";
|
||||||
@@ -102,6 +115,15 @@ export const DashboardPage: React.FC = () => {
|
|||||||
.catch(console.error);
|
.catch(console.error);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Fetch active swaps for SuperAdmin
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSuperAdmin) {
|
||||||
|
api.getEmployeeSwaps({ status: "Active" })
|
||||||
|
.then(setActiveSwaps)
|
||||||
|
.catch(console.error);
|
||||||
|
}
|
||||||
|
}, [isSuperAdmin]);
|
||||||
|
|
||||||
// Fetch contractor rates for supervisor view
|
// Fetch contractor rates for supervisor view
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSupervisor) {
|
if (isSupervisor) {
|
||||||
@@ -213,6 +235,16 @@ export const DashboardPage: React.FC = () => {
|
|||||||
|
|
||||||
const supervisors = employees.filter((e) => e.role === "Supervisor");
|
const supervisors = employees.filter((e) => e.role === "Supervisor");
|
||||||
|
|
||||||
|
// Helper function to get the effective contractor ID for an employee
|
||||||
|
// If employee has an active swap, return the original contractor ID
|
||||||
|
const getEffectiveContractorId = (employeeId: number, currentContractorId?: number) => {
|
||||||
|
const swap = activeSwaps.find((s) => s.employee_id === employeeId);
|
||||||
|
if (swap && swap.original_contractor_id) {
|
||||||
|
return swap.original_contractor_id;
|
||||||
|
}
|
||||||
|
return currentContractorId;
|
||||||
|
};
|
||||||
|
|
||||||
return supervisors.map((supervisor) => {
|
return supervisors.map((supervisor) => {
|
||||||
const deptContractors = employees.filter(
|
const deptContractors = employees.filter(
|
||||||
(e) =>
|
(e) =>
|
||||||
@@ -220,67 +252,125 @@ export const DashboardPage: React.FC = () => {
|
|||||||
e.department_id === supervisor.department_id,
|
e.department_id === supervisor.department_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get employees without a contractor but in this department (e.g., swapped employees)
|
// Find employees swapped INTO this department (without a specific target contractor)
|
||||||
const unassignedEmployees = employees.filter(
|
const swappedIntoThisDept = activeSwaps
|
||||||
(e) =>
|
.filter((s) => s.target_department_id === supervisor.department_id && !s.target_contractor_id)
|
||||||
e.role === "Employee" &&
|
.map((swap) => {
|
||||||
e.department_id === supervisor.department_id &&
|
const emp = employees.find((e) => e.id === swap.employee_id);
|
||||||
!e.contractor_id,
|
const originalContractor = employees.find((e) => e.id === swap.original_contractor_id);
|
||||||
);
|
return emp ? { emp, originalContractor, swap } : null;
|
||||||
|
})
|
||||||
|
.filter((x): x is NonNullable<typeof x> => x !== null);
|
||||||
|
|
||||||
const contractorNodes = deptContractors.map((contractor) => {
|
const contractorNodes = deptContractors.map((contractor) => {
|
||||||
|
// Include employees whose original contractor is this one (they belong here)
|
||||||
const contractorEmployees = employees.filter(
|
const contractorEmployees = employees.filter(
|
||||||
(e) => e.role === "Employee" && e.contractor_id === contractor.id,
|
(e) => e.role === "Employee" && getEffectiveContractorId(e.id, e.contractor_id) === contractor.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Find employees swapped INTO this specific contractor
|
||||||
|
const swappedInEmployees = activeSwaps
|
||||||
|
.filter((s) => s.target_contractor_id === contractor.id)
|
||||||
|
.map((swap) => employees.find((e) => e.id === swap.employee_id))
|
||||||
|
.filter((e): e is NonNullable<typeof e> => e !== undefined);
|
||||||
|
|
||||||
|
// Build children: regular employees + swapped-in employees as nested
|
||||||
|
const employeeNodes = contractorEmployees.map((emp) => {
|
||||||
|
const empAttendance = attendance.find((a) =>
|
||||||
|
a.employee_id === emp.id
|
||||||
|
);
|
||||||
|
const empAllocation = allocations.find((a) =>
|
||||||
|
a.employee_id === emp.id && a.status !== "Completed"
|
||||||
|
);
|
||||||
|
const empSwap = activeSwaps.find((s) => s.employee_id === emp.id);
|
||||||
|
|
||||||
|
// If employee is swapped OUT, show swap info in activity
|
||||||
|
let activityText = empAllocation?.description || empAllocation?.activity || "-";
|
||||||
|
if (empSwap) {
|
||||||
|
// Find target contractor/department name
|
||||||
|
const targetContractor = employees.find((e) => e.id === empSwap.target_contractor_id);
|
||||||
|
const targetDept = departments.find((d) => d.id === empSwap.target_department_id);
|
||||||
|
activityText = `Swapped to ${targetContractor?.name || targetDept?.name || "another dept"}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swapped-in employees are added at contractor level, not as children of individual employees
|
||||||
|
const swappedInChildren: HierarchyNode[] = [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: emp.id,
|
||||||
|
name: emp.name,
|
||||||
|
role: "Employee",
|
||||||
|
department: emp.department_name || "",
|
||||||
|
subDepartment: empAllocation?.sub_department_name || "-",
|
||||||
|
activity: activityText,
|
||||||
|
status: empAttendance
|
||||||
|
? (empAttendance.status === "CheckedIn" ||
|
||||||
|
empAttendance.status === "CheckedOut"
|
||||||
|
? "Present"
|
||||||
|
: "Absent")
|
||||||
|
: undefined,
|
||||||
|
inTime: empAttendance?.check_in_time
|
||||||
|
? new Date(empAttendance.check_in_time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||||||
|
: undefined,
|
||||||
|
outTime: empAttendance?.check_out_time
|
||||||
|
? new Date(empAttendance.check_out_time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||||||
|
: undefined,
|
||||||
|
remark: empAttendance?.remark,
|
||||||
|
children: swappedInChildren,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add swapped-in employees as a separate group under this contractor
|
||||||
|
const swappedInNodes = swappedInEmployees.map((emp) => {
|
||||||
|
const empAttendance = attendance.find((a) => a.employee_id === emp.id);
|
||||||
|
const empAllocation = allocations.find((a) =>
|
||||||
|
a.employee_id === emp.id && a.status !== "Completed"
|
||||||
|
);
|
||||||
|
const empSwap = activeSwaps.find((s) => s.employee_id === emp.id);
|
||||||
|
const originalContractor = employees.find((e) => e.id === empSwap?.original_contractor_id);
|
||||||
|
const originalDept = departments.find((d) => d.id === empSwap?.original_department_id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: emp.id,
|
||||||
|
name: `${emp.name} (from ${originalContractor?.name || originalDept?.name || "other"})`,
|
||||||
|
role: "Employee",
|
||||||
|
department: emp.department_name || "",
|
||||||
|
subDepartment: empAllocation?.sub_department_name || "-",
|
||||||
|
activity: empAllocation?.description || empAllocation?.activity || "-",
|
||||||
|
status: empAttendance
|
||||||
|
? (empAttendance.status === "CheckedIn" ||
|
||||||
|
empAttendance.status === "CheckedOut"
|
||||||
|
? "Present"
|
||||||
|
: "Absent")
|
||||||
|
: undefined,
|
||||||
|
inTime: empAttendance?.check_in_time
|
||||||
|
? new Date(empAttendance.check_in_time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||||||
|
: undefined,
|
||||||
|
outTime: empAttendance?.check_out_time
|
||||||
|
? new Date(empAttendance.check_out_time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
||||||
|
: undefined,
|
||||||
|
remark: empAttendance?.remark,
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: contractor.id,
|
id: contractor.id,
|
||||||
name: contractor.name,
|
name: contractor.name,
|
||||||
role: "Contractor",
|
role: "Contractor",
|
||||||
department: contractor.department_name || "",
|
department: contractor.department_name || "",
|
||||||
children: contractorEmployees.map((emp) => {
|
children: [...employeeNodes, ...swappedInNodes],
|
||||||
const empAttendance = attendance.find((a) =>
|
|
||||||
a.employee_id === emp.id
|
|
||||||
);
|
|
||||||
const empAllocation = allocations.find((a) =>
|
|
||||||
a.employee_id === emp.id && a.status !== "Completed"
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: emp.id,
|
|
||||||
name: emp.name,
|
|
||||||
role: "Employee",
|
|
||||||
department: emp.department_name || "",
|
|
||||||
subDepartment: empAllocation?.sub_department_name || "-",
|
|
||||||
activity: empAllocation?.description || empAllocation?.activity ||
|
|
||||||
"-",
|
|
||||||
status: empAttendance
|
|
||||||
? (empAttendance.status === "CheckedIn" ||
|
|
||||||
empAttendance.status === "CheckedOut"
|
|
||||||
? "Present"
|
|
||||||
: "Absent")
|
|
||||||
: undefined,
|
|
||||||
inTime: empAttendance?.check_in_time
|
|
||||||
? new Date(empAttendance.check_in_time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
|
||||||
: undefined,
|
|
||||||
outTime: empAttendance?.check_out_time
|
|
||||||
? new Date(empAttendance.check_out_time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
|
||||||
: undefined,
|
|
||||||
remark: empAttendance?.remark,
|
|
||||||
children: [],
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add unassigned employees node if there are any
|
// Add "Swapped In" node for employees swapped into this department without a specific contractor
|
||||||
if (unassignedEmployees.length > 0) {
|
if (swappedIntoThisDept.length > 0) {
|
||||||
contractorNodes.push({
|
contractorNodes.push({
|
||||||
id: -supervisor.department_id!, // Negative ID to avoid conflicts
|
id: -supervisor.department_id!, // Negative ID to avoid conflicts
|
||||||
name: "Unassigned (Swapped)",
|
name: "Swapped In",
|
||||||
role: "Contractor",
|
role: "Contractor",
|
||||||
department: supervisor.department_name || "",
|
department: supervisor.department_name || "",
|
||||||
children: unassignedEmployees.map((emp) => {
|
children: swappedIntoThisDept.map(({ emp, originalContractor }) => {
|
||||||
const empAttendance = attendance.find((a) =>
|
const empAttendance = attendance.find((a) =>
|
||||||
a.employee_id === emp.id
|
a.employee_id === emp.id
|
||||||
);
|
);
|
||||||
@@ -290,12 +380,11 @@ export const DashboardPage: React.FC = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: emp.id,
|
id: emp.id,
|
||||||
name: emp.name,
|
name: `${emp.name} (from ${originalContractor?.name || "other"})`,
|
||||||
role: "Employee",
|
role: "Employee",
|
||||||
department: emp.department_name || "",
|
department: emp.department_name || "",
|
||||||
subDepartment: empAllocation?.sub_department_name || "-",
|
subDepartment: empAllocation?.sub_department_name || "-",
|
||||||
activity: empAllocation?.description || empAllocation?.activity ||
|
activity: empAllocation?.description || empAllocation?.activity || "-",
|
||||||
"Swapped",
|
|
||||||
status: empAttendance
|
status: empAttendance
|
||||||
? (empAttendance.status === "CheckedIn" ||
|
? (empAttendance.status === "CheckedIn" ||
|
||||||
empAttendance.status === "CheckedOut"
|
empAttendance.status === "CheckedOut"
|
||||||
@@ -325,7 +414,7 @@ export const DashboardPage: React.FC = () => {
|
|||||||
|
|
||||||
return supervisorNode;
|
return supervisorNode;
|
||||||
});
|
});
|
||||||
}, [isSuperAdmin, employees, attendance, allocations]);
|
}, [isSuperAdmin, employees, attendance, allocations, activeSwaps, departments]);
|
||||||
|
|
||||||
// Build hierarchy data for Supervisor view (department-specific)
|
// Build hierarchy data for Supervisor view (department-specific)
|
||||||
const supervisorHierarchyData = useMemo(() => {
|
const supervisorHierarchyData = useMemo(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user