Compare commits
No commits in common. "00929c92d1e9cad7c21037202563d40d123346ef" and "e38b09bd2df533251042af04846877cb33a02a3a" have entirely different histories.
00929c92d1
...
e38b09bd2d
@ -1,4 +1,4 @@
|
|||||||
version: "3.8"
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
hrms-frontend:
|
hrms-frontend:
|
||||||
@ -17,4 +17,4 @@ services:
|
|||||||
|
|
||||||
networks:
|
networks:
|
||||||
hrms-network:
|
hrms-network:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
@ -1,26 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "AA001",
|
|
||||||
"employeeId": "MGR001",
|
|
||||||
"employeeName": "Michael Scott",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-15",
|
|
||||||
"fromDate": "2026-07-25",
|
|
||||||
"toDate": "2026-07-26",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Family trip."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "AA002",
|
|
||||||
"employeeId": "MGR002",
|
|
||||||
"employeeName": "Jane Doe",
|
|
||||||
"leaveType": "Sick Leave",
|
|
||||||
"applicationDate": "2026-07-16",
|
|
||||||
"fromDate": "2026-07-22",
|
|
||||||
"toDate": "2026-07-22",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Fever."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@ -1,242 +1,22 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "MA005", "employeeId": "EMP005", "employeeName": "Michael Brown", "leaveType": "Casual Leave", "applicationDate": "2026-07-16", "fromDate": "2026-07-28", "toDate": "2026-07-29", "leaveDays": 2, "status": "Pending", "reason": "Attending a family wedding." },
|
||||||
"id": "MA005",
|
{ "id": "MA006", "employeeId": "EMP006", "employeeName": "Emily Davis", "leaveType": "Optional Holiday", "applicationDate": "2026-07-17", "fromDate": "2026-08-01", "toDate": "2026-08-01", "leaveDays": 1, "status": "Pending", "reason": "Festival celebration." },
|
||||||
"employeeId": "EMP005",
|
{ "id": "MA007", "employeeId": "EMP007", "employeeName": "Chris Wilson", "leaveType": "Casual Leave", "applicationDate": "2026-07-18", "fromDate": "2026-08-03", "toDate": "2026-08-04", "leaveDays": 2, "status": "Approved", "reason": "Personal commitments." },
|
||||||
"employeeName": "Michael Brown",
|
{ "id": "MA008", "employeeId": "EMP008", "employeeName": "Sophia Johnson", "leaveType": "Optional Holiday", "applicationDate": "2026-07-19", "fromDate": "2026-08-05", "toDate": "2026-08-05", "leaveDays": 1, "status": "Rejected", "reason": "Festival observance." },
|
||||||
"leaveType": "Casual Leave",
|
{ "id": "MA009", "employeeId": "EMP009", "employeeName": "Daniel Lee", "leaveType": "Casual Leave", "applicationDate": "2026-07-20", "fromDate": "2026-08-06", "toDate": "2026-08-08", "leaveDays": 3, "status": "Pending", "reason": "Family emergency." },
|
||||||
"applicationDate": "2026-07-16",
|
{ "id": "MA010", "employeeId": "EMP010", "employeeName": "Olivia Martin", "leaveType": "Optional Holiday", "applicationDate": "2026-07-21", "fromDate": "2026-08-10", "toDate": "2026-08-10", "leaveDays": 1, "status": "Approved", "reason": "Cultural celebration." },
|
||||||
"fromDate": "2026-07-28",
|
{ "id": "MA011", "employeeId": "EMP011", "employeeName": "James Taylor", "leaveType": "Casual Leave", "applicationDate": "2026-07-22", "fromDate": "2026-08-12", "toDate": "2026-08-13", "leaveDays": 2, "status": "Pending", "reason": "Home relocation." },
|
||||||
"toDate": "2026-07-29",
|
{ "id": "MA012", "employeeId": "EMP012", "employeeName": "Emma Garcia", "leaveType": "Optional Holiday", "applicationDate": "2026-07-23", "fromDate": "2026-08-14", "toDate": "2026-08-14", "leaveDays": 1, "status": "Pending", "reason": "Religious observance." },
|
||||||
"leaveDays": 2,
|
{ "id": "MA013", "employeeId": "EMP013", "employeeName": "William Harris", "leaveType": "Casual Leave", "applicationDate": "2026-07-24", "fromDate": "2026-08-17", "toDate": "2026-08-18", "leaveDays": 2, "status": "Approved", "reason": "Health checkup." },
|
||||||
"status": "Pending",
|
{ "id": "MA014", "employeeId": "EMP014", "employeeName": "Ava Clark", "leaveType": "Optional Holiday", "applicationDate": "2026-07-25", "fromDate": "2026-08-19", "toDate": "2026-08-19", "leaveDays": 1, "status": "Pending", "reason": "Community event." },
|
||||||
"reason": "Attending a family wedding."
|
{ "id": "MA015", "employeeId": "EMP015", "employeeName": "Benjamin Lewis", "leaveType": "Casual Leave", "applicationDate": "2026-07-26", "fromDate": "2026-08-20", "toDate": "2026-08-22", "leaveDays": 3, "status": "Rejected", "reason": "Vacation with family." },
|
||||||
},
|
{ "id": "MA016", "employeeId": "EMP016", "employeeName": "Mia Walker", "leaveType": "Optional Holiday", "applicationDate": "2026-07-27", "fromDate": "2026-08-24", "toDate": "2026-08-24", "leaveDays": 1, "status": "Approved", "reason": "Festival celebration." },
|
||||||
{
|
{ "id": "MA017", "employeeId": "EMP017", "employeeName": "Ethan Hall", "leaveType": "Casual Leave", "applicationDate": "2026-07-28", "fromDate": "2026-08-25", "toDate": "2026-08-26", "leaveDays": 2, "status": "Pending", "reason": "Personal travel." },
|
||||||
"id": "MA006",
|
{ "id": "MA018", "employeeId": "EMP018", "employeeName": "Charlotte Allen", "leaveType": "Optional Holiday", "applicationDate": "2026-07-29", "fromDate": "2026-08-27", "toDate": "2026-08-27", "leaveDays": 1, "status": "Pending", "reason": "Special occasion." },
|
||||||
"employeeId": "EMP006",
|
{ "id": "MA019", "employeeId": "EMP019", "employeeName": "Alexander Young", "leaveType": "Casual Leave", "applicationDate": "2026-07-30", "fromDate": "2026-08-28", "toDate": "2026-08-30", "leaveDays": 3, "status": "Approved", "reason": "Family gathering." },
|
||||||
"employeeName": "Emily Davis",
|
{ "id": "MA020", "employeeId": "EMP020", "employeeName": "Amelia King", "leaveType": "Optional Holiday", "applicationDate": "2026-07-31", "fromDate": "2026-09-01", "toDate": "2026-09-01", "leaveDays": 1, "status": "Pending", "reason": "Festival leave." },
|
||||||
"leaveType": "Optional Holiday",
|
{ "id": "MA021", "employeeId": "EMP021", "employeeName": "Matthew Scott", "leaveType": "Casual Leave", "applicationDate": "2026-08-01", "fromDate": "2026-09-02", "toDate": "2026-09-03", "leaveDays": 2, "status": "Rejected", "reason": "Personal errands." },
|
||||||
"applicationDate": "2026-07-17",
|
{ "id": "MA022", "employeeId": "EMP022", "employeeName": "Harper Green", "leaveType": "Optional Holiday", "applicationDate": "2026-08-02", "fromDate": "2026-09-04", "toDate": "2026-09-04", "leaveDays": 1, "status": "Approved", "reason": "Religious holiday." },
|
||||||
"fromDate": "2026-08-01",
|
{ "id": "MA023", "employeeId": "EMP023", "employeeName": "David Baker", "leaveType": "Casual Leave", "applicationDate": "2026-08-03", "fromDate": "2026-09-07", "toDate": "2026-09-08", "leaveDays": 2, "status": "Pending", "reason": "Attending a seminar." },
|
||||||
"toDate": "2026-08-01",
|
{ "id": "MA024", "employeeId": "EMP024", "employeeName": "Ella Adams", "leaveType": "Optional Holiday", "applicationDate": "2026-08-04", "fromDate": "2026-09-09", "toDate": "2026-09-09", "leaveDays": 1, "status": "Pending", "reason": "Festival observance." }
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Festival celebration."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA007",
|
|
||||||
"employeeId": "EMP007",
|
|
||||||
"employeeName": "Chris Wilson",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-18",
|
|
||||||
"fromDate": "2026-08-03",
|
|
||||||
"toDate": "2026-08-04",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "Personal commitments."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA008",
|
|
||||||
"employeeId": "EMP008",
|
|
||||||
"employeeName": "Sophia Johnson",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-19",
|
|
||||||
"fromDate": "2026-08-05",
|
|
||||||
"toDate": "2026-08-05",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Rejected",
|
|
||||||
"reason": "Festival observance."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA009",
|
|
||||||
"employeeId": "EMP009",
|
|
||||||
"employeeName": "Daniel Lee",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-20",
|
|
||||||
"fromDate": "2026-08-06",
|
|
||||||
"toDate": "2026-08-08",
|
|
||||||
"leaveDays": 3,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Family emergency."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA010",
|
|
||||||
"employeeId": "EMP010",
|
|
||||||
"employeeName": "Olivia Martin",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-21",
|
|
||||||
"fromDate": "2026-08-10",
|
|
||||||
"toDate": "2026-08-10",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "Cultural celebration."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA011",
|
|
||||||
"employeeId": "EMP011",
|
|
||||||
"employeeName": "James Taylor",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-22",
|
|
||||||
"fromDate": "2026-08-12",
|
|
||||||
"toDate": "2026-08-13",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Home relocation."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA012",
|
|
||||||
"employeeId": "EMP012",
|
|
||||||
"employeeName": "Emma Garcia",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-23",
|
|
||||||
"fromDate": "2026-08-14",
|
|
||||||
"toDate": "2026-08-14",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Religious observance."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA013",
|
|
||||||
"employeeId": "EMP013",
|
|
||||||
"employeeName": "William Harris",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-24",
|
|
||||||
"fromDate": "2026-08-17",
|
|
||||||
"toDate": "2026-08-18",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "Health checkup."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA014",
|
|
||||||
"employeeId": "EMP014",
|
|
||||||
"employeeName": "Ava Clark",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-25",
|
|
||||||
"fromDate": "2026-08-19",
|
|
||||||
"toDate": "2026-08-19",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Community event."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA015",
|
|
||||||
"employeeId": "EMP015",
|
|
||||||
"employeeName": "Benjamin Lewis",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-26",
|
|
||||||
"fromDate": "2026-08-20",
|
|
||||||
"toDate": "2026-08-22",
|
|
||||||
"leaveDays": 3,
|
|
||||||
"status": "Rejected",
|
|
||||||
"reason": "Vacation with family."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA016",
|
|
||||||
"employeeId": "EMP016",
|
|
||||||
"employeeName": "Mia Walker",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-27",
|
|
||||||
"fromDate": "2026-08-24",
|
|
||||||
"toDate": "2026-08-24",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "Festival celebration."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA017",
|
|
||||||
"employeeId": "EMP017",
|
|
||||||
"employeeName": "Ethan Hall",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-28",
|
|
||||||
"fromDate": "2026-08-25",
|
|
||||||
"toDate": "2026-08-26",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Personal travel."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA018",
|
|
||||||
"employeeId": "EMP018",
|
|
||||||
"employeeName": "Charlotte Allen",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-29",
|
|
||||||
"fromDate": "2026-08-27",
|
|
||||||
"toDate": "2026-08-27",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Special occasion."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA019",
|
|
||||||
"employeeId": "EMP019",
|
|
||||||
"employeeName": "Alexander Young",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-30",
|
|
||||||
"fromDate": "2026-08-28",
|
|
||||||
"toDate": "2026-08-30",
|
|
||||||
"leaveDays": 3,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "Family gathering."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA020",
|
|
||||||
"employeeId": "EMP020",
|
|
||||||
"employeeName": "Amelia King",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-31",
|
|
||||||
"fromDate": "2026-09-01",
|
|
||||||
"toDate": "2026-09-01",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Festival leave."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA021",
|
|
||||||
"employeeId": "EMP021",
|
|
||||||
"employeeName": "Matthew Scott",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-08-01",
|
|
||||||
"fromDate": "2026-09-02",
|
|
||||||
"toDate": "2026-09-03",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Rejected",
|
|
||||||
"reason": "Personal errands."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA022",
|
|
||||||
"employeeId": "EMP022",
|
|
||||||
"employeeName": "Harper Green",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-08-02",
|
|
||||||
"fromDate": "2026-09-04",
|
|
||||||
"toDate": "2026-09-04",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "Religious holiday."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA023",
|
|
||||||
"employeeId": "EMP023",
|
|
||||||
"employeeName": "David Baker",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-08-03",
|
|
||||||
"fromDate": "2026-09-07",
|
|
||||||
"toDate": "2026-09-08",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Attending a seminar."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "MA024",
|
|
||||||
"employeeId": "EMP024",
|
|
||||||
"employeeName": "Ella Adams",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-08-04",
|
|
||||||
"fromDate": "2026-09-09",
|
|
||||||
"toDate": "2026-09-09",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Festival observance."
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,26 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "SA001",
|
|
||||||
"employeeId": "ADM001",
|
|
||||||
"employeeName": "Alice Williams",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-10",
|
|
||||||
"fromDate": "2026-08-01",
|
|
||||||
"toDate": "2026-08-01",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Festival observance."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "SA002",
|
|
||||||
"employeeId": "ADM002",
|
|
||||||
"employeeName": "Bob Smith",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-12",
|
|
||||||
"fromDate": "2026-08-05",
|
|
||||||
"toDate": "2026-08-06",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Personal work."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@ -1,30 +1,6 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "date": "2026-07-01", "timeIn": "08:50 AM", "timeOut": "06:00 PM", "loggedHours": "9h 10m", "status": "Present" },
|
||||||
"date": "2026-07-01",
|
{ "date": "2026-07-02", "timeIn": "09:00 AM", "timeOut": "06:00 PM", "loggedHours": "9h 00m", "status": "Present" },
|
||||||
"timeIn": "08:50 AM",
|
{ "date": "2026-07-07", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Holiday" },
|
||||||
"timeOut": "06:00 PM",
|
{ "date": "2026-07-15", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Absent" }
|
||||||
"loggedHours": "9h 10m",
|
|
||||||
"status": "Present"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-02",
|
|
||||||
"timeIn": "09:00 AM",
|
|
||||||
"timeOut": "06:00 PM",
|
|
||||||
"loggedHours": "9h 00m",
|
|
||||||
"status": "Present"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-07",
|
|
||||||
"timeIn": "-",
|
|
||||||
"timeOut": "-",
|
|
||||||
"loggedHours": "-",
|
|
||||||
"status": "Holiday"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-15",
|
|
||||||
"timeIn": "-",
|
|
||||||
"timeOut": "-",
|
|
||||||
"loggedHours": "-",
|
|
||||||
"status": "Absent"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,32 +1,5 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "REG001", "empId": "EMP001", "empName": "Alice", "reqCheckIn": "09:30 AM", "reqCheckOut": "06:30 PM", "date": "2024-07-15", "reason": "Forgot to punch in due to system issue.", "status": "Pending" },
|
||||||
"id": "REG001",
|
{ "id": "REG002", "empId": "EMP003", "empName": "Charlie", "reqCheckIn": "10:00 AM", "reqCheckOut": "07:00 PM", "date": "2024-07-16", "reason": "Late arrival due to traffic.", "status": "Pending" },
|
||||||
"empId": "EMP001",
|
{ "id": "REG003", "empId": "EMP005", "empName": "Eve", "reqCheckIn": "08:45 AM", "reqCheckOut": "05:15 PM", "date": "2024-07-14", "reason": "Mispunch while swiping card.", "status": "Pending" }
|
||||||
"empName": "Alice",
|
|
||||||
"reqCheckIn": "09:30 AM",
|
|
||||||
"reqCheckOut": "06:30 PM",
|
|
||||||
"date": "2024-07-15",
|
|
||||||
"reason": "Forgot to punch in due to system issue.",
|
|
||||||
"status": "Pending"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "REG002",
|
|
||||||
"empId": "EMP003",
|
|
||||||
"empName": "Charlie",
|
|
||||||
"reqCheckIn": "10:00 AM",
|
|
||||||
"reqCheckOut": "07:00 PM",
|
|
||||||
"date": "2024-07-16",
|
|
||||||
"reason": "Late arrival due to traffic.",
|
|
||||||
"status": "Pending"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "REG003",
|
|
||||||
"empId": "EMP005",
|
|
||||||
"empName": "Eve",
|
|
||||||
"reqCheckIn": "08:45 AM",
|
|
||||||
"reqCheckOut": "05:15 PM",
|
|
||||||
"date": "2024-07-14",
|
|
||||||
"reason": "Mispunch while swiping card.",
|
|
||||||
"status": "Pending"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,722 +1,91 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "EMP001", "employeeName": "Alice", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-01" },
|
||||||
"id": "EMP001",
|
{ "id": "EMP002", "employeeName": "Bob", "branch": "Pune", "department": "Training", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-01" },
|
||||||
"employeeName": "Alice",
|
{ "id": "EMP003", "employeeName": "Charlie", "branch": "Hyderabad", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-01" },
|
||||||
"branch": "Bengaluru",
|
{ "id": "EMP004", "employeeName": "David", "branch": "Bengaluru", "department": "Placement", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-01" },
|
||||||
"department": "IT",
|
{ "id": "EMP005", "employeeName": "Eve", "branch": "Pune", "department": "Digital Marketing", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-01" },
|
||||||
"company": "CLRI",
|
{ "id": "EMP006", "employeeName": "Frank", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-01" },
|
||||||
"isCheckedIn": true,
|
{ "id": "EMP007", "employeeName": "Grace", "branch": "Hyderabad", "department": "Training", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-01" },
|
||||||
"date": "2026-07-01"
|
{ "id": "EMP008", "employeeName": "Heidi", "branch": "Bengaluru", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-01" },
|
||||||
},
|
|
||||||
{
|
{ "id": "EMP001", "employeeName": "Alice", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-02" },
|
||||||
"id": "EMP002",
|
{ "id": "EMP002", "employeeName": "Bob", "branch": "Pune", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-02" },
|
||||||
"employeeName": "Bob",
|
{ "id": "EMP003", "employeeName": "Charlie", "branch": "Hyderabad", "department": "Analytics", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-02" },
|
||||||
"branch": "Pune",
|
{ "id": "EMP004", "employeeName": "David", "branch": "Bengaluru", "department": "Placement", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-02" },
|
||||||
"department": "Training",
|
{ "id": "EMP005", "employeeName": "Eve", "branch": "Pune", "department": "Digital Marketing", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-02" },
|
||||||
"company": "CLRI",
|
{ "id": "EMP006", "employeeName": "Frank", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-02" },
|
||||||
"isCheckedIn": false,
|
{ "id": "EMP007", "employeeName": "Grace", "branch": "Hyderabad", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-02" },
|
||||||
"date": "2026-07-01"
|
{ "id": "EMP008", "employeeName": "Heidi", "branch": "Bengaluru", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-02" },
|
||||||
},
|
|
||||||
{
|
{ "id": "EMP001", "employeeName": "Alice", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-03" },
|
||||||
"id": "EMP003",
|
{ "id": "EMP002", "employeeName": "Bob", "branch": "Pune", "department": "Training", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-03" },
|
||||||
"employeeName": "Charlie",
|
{ "id": "EMP003", "employeeName": "Charlie", "branch": "Hyderabad", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-03" },
|
||||||
"branch": "Hyderabad",
|
{ "id": "EMP004", "employeeName": "David", "branch": "Bengaluru", "department": "Placement", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-03" },
|
||||||
"department": "Analytics",
|
{ "id": "EMP005", "employeeName": "Eve", "branch": "Pune", "department": "Digital Marketing", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-03" },
|
||||||
"company": "CLRI",
|
{ "id": "EMP006", "employeeName": "Frank", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-03" },
|
||||||
"isCheckedIn": true,
|
{ "id": "EMP007", "employeeName": "Grace", "branch": "Hyderabad", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-03" },
|
||||||
"date": "2026-07-01"
|
{ "id": "EMP008", "employeeName": "Heidi", "branch": "Bengaluru", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-03" },
|
||||||
},
|
|
||||||
{
|
{ "id": "EMP001", "employeeName": "Alice", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-04" },
|
||||||
"id": "EMP004",
|
{ "id": "EMP002", "employeeName": "Bob", "branch": "Pune", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-04" },
|
||||||
"employeeName": "David",
|
{ "id": "EMP003", "employeeName": "Charlie", "branch": "Hyderabad", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-04" },
|
||||||
"branch": "Bengaluru",
|
{ "id": "EMP004", "employeeName": "David", "branch": "Bengaluru", "department": "Placement", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-04" },
|
||||||
"department": "Placement",
|
{ "id": "EMP005", "employeeName": "Eve", "branch": "Pune", "department": "Digital Marketing", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-04" },
|
||||||
"company": "CLRI",
|
{ "id": "EMP006", "employeeName": "Frank", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-04" },
|
||||||
"isCheckedIn": false,
|
{ "id": "EMP007", "employeeName": "Grace", "branch": "Hyderabad", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-04" },
|
||||||
"date": "2026-07-01"
|
{ "id": "EMP008", "employeeName": "Heidi", "branch": "Bengaluru", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-04" },
|
||||||
},
|
|
||||||
{
|
{ "id": "EMP001", "employeeName": "Alice", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-05" },
|
||||||
"id": "EMP005",
|
{ "id": "EMP002", "employeeName": "Bob", "branch": "Pune", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-05" },
|
||||||
"employeeName": "Eve",
|
{ "id": "EMP003", "employeeName": "Charlie", "branch": "Hyderabad", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-05" },
|
||||||
"branch": "Pune",
|
{ "id": "EMP004", "employeeName": "David", "branch": "Bengaluru", "department": "Placement", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-05" },
|
||||||
"department": "Digital Marketing",
|
{ "id": "EMP005", "employeeName": "Eve", "branch": "Pune", "department": "Digital Marketing", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-05" },
|
||||||
"company": "CLRI",
|
{ "id": "EMP006", "employeeName": "Frank", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-05" },
|
||||||
"isCheckedIn": true,
|
{ "id": "EMP007", "employeeName": "Grace", "branch": "Hyderabad", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-05" },
|
||||||
"date": "2026-07-01"
|
{ "id": "EMP008", "employeeName": "Heidi", "branch": "Bengaluru", "department": "Analytics", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-05" },
|
||||||
},
|
|
||||||
{
|
{ "id": "EMP001", "employeeName": "Alice", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-06" },
|
||||||
"id": "EMP006",
|
{ "id": "EMP002", "employeeName": "Bob", "branch": "Pune", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-06" },
|
||||||
"employeeName": "Frank",
|
{ "id": "EMP003", "employeeName": "Charlie", "branch": "Hyderabad", "department": "Analytics", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-06" },
|
||||||
"branch": "Bengaluru",
|
{ "id": "EMP004", "employeeName": "David", "branch": "Bengaluru", "department": "Placement", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-06" },
|
||||||
"department": "IT",
|
{ "id": "EMP005", "employeeName": "Eve", "branch": "Pune", "department": "Digital Marketing", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-06" },
|
||||||
"company": "CLRI",
|
{ "id": "EMP006", "employeeName": "Frank", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-06" },
|
||||||
"isCheckedIn": true,
|
{ "id": "EMP007", "employeeName": "Grace", "branch": "Hyderabad", "department": "Training", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-06" },
|
||||||
"date": "2026-07-01"
|
{ "id": "EMP008", "employeeName": "Heidi", "branch": "Bengaluru", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-06" },
|
||||||
},
|
|
||||||
{
|
{ "id": "EMP001", "employeeName": "Alice", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-07" },
|
||||||
"id": "EMP007",
|
{ "id": "EMP002", "employeeName": "Bob", "branch": "Pune", "department": "Training", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-07" },
|
||||||
"employeeName": "Grace",
|
{ "id": "EMP003", "employeeName": "Charlie", "branch": "Hyderabad", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-07" },
|
||||||
"branch": "Hyderabad",
|
{ "id": "EMP004", "employeeName": "David", "branch": "Bengaluru", "department": "Placement", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-07" },
|
||||||
"department": "Training",
|
{ "id": "EMP005", "employeeName": "Eve", "branch": "Pune", "department": "Digital Marketing", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-07" },
|
||||||
"company": "CLRI",
|
{ "id": "EMP006", "employeeName": "Frank", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-07" },
|
||||||
"isCheckedIn": false,
|
{ "id": "EMP007", "employeeName": "Grace", "branch": "Hyderabad", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-07" },
|
||||||
"date": "2026-07-01"
|
{ "id": "EMP008", "employeeName": "Heidi", "branch": "Bengaluru", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-07" },
|
||||||
},
|
|
||||||
{
|
{ "id": "EMP001", "employeeName": "Alice", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-08" },
|
||||||
"id": "EMP008",
|
{ "id": "EMP002", "employeeName": "Bob", "branch": "Pune", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-08" },
|
||||||
"employeeName": "Heidi",
|
{ "id": "EMP003", "employeeName": "Charlie", "branch": "Hyderabad", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-08" },
|
||||||
"branch": "Bengaluru",
|
{ "id": "EMP004", "employeeName": "David", "branch": "Bengaluru", "department": "Placement", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-08" },
|
||||||
"department": "Analytics",
|
{ "id": "EMP005", "employeeName": "Eve", "branch": "Pune", "department": "Digital Marketing", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-08" },
|
||||||
"company": "CLRI",
|
{ "id": "EMP006", "employeeName": "Frank", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-08" },
|
||||||
"isCheckedIn": true,
|
{ "id": "EMP007", "employeeName": "Grace", "branch": "Hyderabad", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-08" },
|
||||||
"date": "2026-07-01"
|
{ "id": "EMP008", "employeeName": "Heidi", "branch": "Bengaluru", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-08" },
|
||||||
},
|
|
||||||
{
|
{ "id": "EMP001", "employeeName": "Alice", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-09" },
|
||||||
"id": "EMP001",
|
{ "id": "EMP002", "employeeName": "Bob", "branch": "Pune", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-09" },
|
||||||
"employeeName": "Alice",
|
{ "id": "EMP003", "employeeName": "Charlie", "branch": "Hyderabad", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-09" },
|
||||||
"branch": "Bengaluru",
|
{ "id": "EMP004", "employeeName": "David", "branch": "Bengaluru", "department": "Placement", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-09" },
|
||||||
"department": "IT",
|
{ "id": "EMP005", "employeeName": "Eve", "branch": "Pune", "department": "Digital Marketing", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-09" },
|
||||||
"company": "CLRI",
|
{ "id": "EMP006", "employeeName": "Frank", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-09" },
|
||||||
"isCheckedIn": true,
|
{ "id": "EMP007", "employeeName": "Grace", "branch": "Hyderabad", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-09" },
|
||||||
"date": "2026-07-02"
|
{ "id": "EMP008", "employeeName": "Heidi", "branch": "Bengaluru", "department": "Analytics", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-09" },
|
||||||
},
|
|
||||||
{
|
{ "id": "EMP001", "employeeName": "Alice", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-10" },
|
||||||
"id": "EMP002",
|
{ "id": "EMP002", "employeeName": "Bob", "branch": "Pune", "department": "Training", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-10" },
|
||||||
"employeeName": "Bob",
|
{ "id": "EMP003", "employeeName": "Charlie", "branch": "Hyderabad", "department": "Analytics", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-10" },
|
||||||
"branch": "Pune",
|
{ "id": "EMP004", "employeeName": "David", "branch": "Bengaluru", "department": "Placement", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-10" },
|
||||||
"department": "Training",
|
{ "id": "EMP005", "employeeName": "Eve", "branch": "Pune", "department": "Digital Marketing", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-10" },
|
||||||
"company": "CLRI",
|
{ "id": "EMP006", "employeeName": "Frank", "branch": "Bengaluru", "department": "IT", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-10" },
|
||||||
"isCheckedIn": true,
|
{ "id": "EMP007", "employeeName": "Grace", "branch": "Hyderabad", "department": "Training", "company": "CLRI", "isCheckedIn": false, "date": "2026-07-10" },
|
||||||
"date": "2026-07-02"
|
{ "id": "EMP008", "employeeName": "Heidi", "branch": "Bengaluru", "department": "Analytics", "company": "CLRI", "isCheckedIn": true, "date": "2026-07-10" }
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"employeeName": "Charlie",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-02"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"employeeName": "David",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Placement",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-02"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"employeeName": "Eve",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Digital Marketing",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-02"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"employeeName": "Frank",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-02"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"employeeName": "Grace",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-02"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"employeeName": "Heidi",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-02"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP001",
|
|
||||||
"employeeName": "Alice",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-03"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP002",
|
|
||||||
"employeeName": "Bob",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-03"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"employeeName": "Charlie",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-03"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"employeeName": "David",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Placement",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-03"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"employeeName": "Eve",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Digital Marketing",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-03"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"employeeName": "Frank",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-03"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"employeeName": "Grace",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-03"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"employeeName": "Heidi",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-03"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP001",
|
|
||||||
"employeeName": "Alice",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-04"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP002",
|
|
||||||
"employeeName": "Bob",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-04"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"employeeName": "Charlie",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-04"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"employeeName": "David",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Placement",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-04"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"employeeName": "Eve",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Digital Marketing",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-04"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"employeeName": "Frank",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-04"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"employeeName": "Grace",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-04"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"employeeName": "Heidi",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-04"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP001",
|
|
||||||
"employeeName": "Alice",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-05"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP002",
|
|
||||||
"employeeName": "Bob",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-05"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"employeeName": "Charlie",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-05"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"employeeName": "David",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Placement",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-05"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"employeeName": "Eve",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Digital Marketing",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-05"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"employeeName": "Frank",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-05"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"employeeName": "Grace",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-05"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"employeeName": "Heidi",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-05"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP001",
|
|
||||||
"employeeName": "Alice",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP002",
|
|
||||||
"employeeName": "Bob",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"employeeName": "Charlie",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"employeeName": "David",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Placement",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"employeeName": "Eve",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Digital Marketing",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"employeeName": "Frank",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"employeeName": "Grace",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"employeeName": "Heidi",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-06"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP001",
|
|
||||||
"employeeName": "Alice",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-07"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP002",
|
|
||||||
"employeeName": "Bob",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-07"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"employeeName": "Charlie",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-07"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"employeeName": "David",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Placement",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-07"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"employeeName": "Eve",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Digital Marketing",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-07"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"employeeName": "Frank",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-07"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"employeeName": "Grace",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-07"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"employeeName": "Heidi",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-07"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP001",
|
|
||||||
"employeeName": "Alice",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-08"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP002",
|
|
||||||
"employeeName": "Bob",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-08"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"employeeName": "Charlie",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-08"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"employeeName": "David",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Placement",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-08"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"employeeName": "Eve",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Digital Marketing",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-08"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"employeeName": "Frank",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-08"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"employeeName": "Grace",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-08"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"employeeName": "Heidi",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-08"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP001",
|
|
||||||
"employeeName": "Alice",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-09"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP002",
|
|
||||||
"employeeName": "Bob",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-09"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"employeeName": "Charlie",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-09"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"employeeName": "David",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Placement",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-09"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"employeeName": "Eve",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Digital Marketing",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-09"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"employeeName": "Frank",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-09"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"employeeName": "Grace",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-09"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"employeeName": "Heidi",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-09"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP001",
|
|
||||||
"employeeName": "Alice",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP002",
|
|
||||||
"employeeName": "Bob",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"employeeName": "Charlie",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"employeeName": "David",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Placement",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"employeeName": "Eve",
|
|
||||||
"branch": "Pune",
|
|
||||||
"department": "Digital Marketing",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"employeeName": "Frank",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "IT",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"employeeName": "Grace",
|
|
||||||
"branch": "Hyderabad",
|
|
||||||
"department": "Training",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": false,
|
|
||||||
"date": "2026-07-10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"employeeName": "Heidi",
|
|
||||||
"branch": "Bengaluru",
|
|
||||||
"department": "Analytics",
|
|
||||||
"company": "CLRI",
|
|
||||||
"isCheckedIn": true,
|
|
||||||
"date": "2026-07-10"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,142 +1,22 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "date": "2026-07-01", "timeIn": "09:05 AM", "timeOut": "06:15 PM", "loggedHours": "9h 10m", "status": "Present" },
|
||||||
"date": "2026-07-01",
|
{ "date": "2026-07-02", "timeIn": "10:30 AM", "timeOut": "06:30 PM", "loggedHours": "8h 00m", "status": "Late" },
|
||||||
"timeIn": "09:05 AM",
|
{ "date": "2026-07-03", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Leave" },
|
||||||
"timeOut": "06:15 PM",
|
{ "date": "2026-07-04", "timeIn": "10:12 AM", "timeOut": "06:30 PM", "loggedHours": "8h 18m", "status": "Late" },
|
||||||
"loggedHours": "9h 10m",
|
{ "date": "2026-07-06", "timeIn": "09:00 AM", "timeOut": "05:05 PM", "loggedHours": "8h 05m", "status": "Present" },
|
||||||
"status": "Present"
|
{ "date": "2026-07-07", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Holiday" },
|
||||||
},
|
{ "date": "2026-07-08", "timeIn": "09:15 AM", "timeOut": "02:00 PM", "loggedHours": "4h 45m", "status": "Half Day" },
|
||||||
{
|
{ "date": "2026-07-10", "timeIn": "08:45 AM", "timeOut": "05:30 PM", "loggedHours": "8h 45m", "status": "WFH" },
|
||||||
"date": "2026-07-02",
|
{ "date": "2026-07-13", "timeIn": "08:50 AM", "timeOut": "06:00 PM", "loggedHours": "9h 10m", "status": "Present" },
|
||||||
"timeIn": "10:30 AM",
|
{ "date": "2026-07-15", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Absent" },
|
||||||
"timeOut": "06:30 PM",
|
{ "date": "2026-07-17", "timeIn": "09:15 AM", "timeOut": "-", "loggedHours": "-", "status": "Mispunch" },
|
||||||
"loggedHours": "8h 00m",
|
{ "date": "2026-07-20", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Holiday" },
|
||||||
"status": "Late"
|
{ "date": "2026-07-21", "timeIn": "09:00 AM", "timeOut": "06:00 PM", "loggedHours": "9h 00m", "status": "Present" },
|
||||||
},
|
{ "date": "2026-07-26", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Holiday" },
|
||||||
{
|
{ "date": "2026-07-27", "timeIn": "08:50 AM", "timeOut": "06:00 PM", "loggedHours": "9h 10m", "status": "WFH" },
|
||||||
"date": "2026-07-03",
|
{ "date": "2026-07-28", "timeIn": "08:50 AM", "timeOut": "06:00 PM", "loggedHours": "9h 10m", "status": "Present" },
|
||||||
"timeIn": "-",
|
{ "date": "2026-07-29","timeIn": "08:50 AM", "timeOut": "06:00 PM", "loggedHours": "9h 10m", "status": "Present" },
|
||||||
"timeOut": "-",
|
{ "date": "2026-07-30", "timeIn": "08:50 AM", "timeOut": "06:00 PM", "loggedHours": "9h 10m", "status": "Present" },
|
||||||
"loggedHours": "-",
|
{ "date": "2026-07-31", "timeIn": "08:50 AM", "timeOut": "06:00 PM", "loggedHours": "9h 10m","status": "Present" },
|
||||||
"status": "Leave"
|
{ "date": "2026-08-01", "timeIn": "08:50 AM", "timeOut": "06:00 PM", "loggedHours": "9h 10m", "status": "Present" }
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-04",
|
|
||||||
"timeIn": "10:12 AM",
|
|
||||||
"timeOut": "06:30 PM",
|
|
||||||
"loggedHours": "8h 18m",
|
|
||||||
"status": "Late"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-06",
|
|
||||||
"timeIn": "09:00 AM",
|
|
||||||
"timeOut": "05:05 PM",
|
|
||||||
"loggedHours": "8h 05m",
|
|
||||||
"status": "Present"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-07",
|
|
||||||
"timeIn": "-",
|
|
||||||
"timeOut": "-",
|
|
||||||
"loggedHours": "-",
|
|
||||||
"status": "Holiday"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-08",
|
|
||||||
"timeIn": "09:15 AM",
|
|
||||||
"timeOut": "02:00 PM",
|
|
||||||
"loggedHours": "4h 45m",
|
|
||||||
"status": "Half Day"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-10",
|
|
||||||
"timeIn": "08:45 AM",
|
|
||||||
"timeOut": "05:30 PM",
|
|
||||||
"loggedHours": "8h 45m",
|
|
||||||
"status": "WFH"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-13",
|
|
||||||
"timeIn": "08:50 AM",
|
|
||||||
"timeOut": "06:00 PM",
|
|
||||||
"loggedHours": "9h 10m",
|
|
||||||
"status": "Present"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-15",
|
|
||||||
"timeIn": "-",
|
|
||||||
"timeOut": "-",
|
|
||||||
"loggedHours": "-",
|
|
||||||
"status": "Absent"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-17",
|
|
||||||
"timeIn": "09:15 AM",
|
|
||||||
"timeOut": "-",
|
|
||||||
"loggedHours": "-",
|
|
||||||
"status": "Mispunch"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-20",
|
|
||||||
"timeIn": "-",
|
|
||||||
"timeOut": "-",
|
|
||||||
"loggedHours": "-",
|
|
||||||
"status": "Holiday"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-21",
|
|
||||||
"timeIn": "09:00 AM",
|
|
||||||
"timeOut": "06:00 PM",
|
|
||||||
"loggedHours": "9h 00m",
|
|
||||||
"status": "Present"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-26",
|
|
||||||
"timeIn": "-",
|
|
||||||
"timeOut": "-",
|
|
||||||
"loggedHours": "-",
|
|
||||||
"status": "Holiday"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-27",
|
|
||||||
"timeIn": "08:50 AM",
|
|
||||||
"timeOut": "06:00 PM",
|
|
||||||
"loggedHours": "9h 10m",
|
|
||||||
"status": "WFH"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-28",
|
|
||||||
"timeIn": "08:50 AM",
|
|
||||||
"timeOut": "06:00 PM",
|
|
||||||
"loggedHours": "9h 10m",
|
|
||||||
"status": "Present"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-29",
|
|
||||||
"timeIn": "08:50 AM",
|
|
||||||
"timeOut": "06:00 PM",
|
|
||||||
"loggedHours": "9h 10m",
|
|
||||||
"status": "Present"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-30",
|
|
||||||
"timeIn": "08:50 AM",
|
|
||||||
"timeOut": "06:00 PM",
|
|
||||||
"loggedHours": "9h 10m",
|
|
||||||
"status": "Present"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-31",
|
|
||||||
"timeIn": "08:50 AM",
|
|
||||||
"timeOut": "06:00 PM",
|
|
||||||
"loggedHours": "9h 10m",
|
|
||||||
"status": "Present"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-08-01",
|
|
||||||
"timeIn": "08:50 AM",
|
|
||||||
"timeOut": "06:00 PM",
|
|
||||||
"loggedHours": "9h 10m",
|
|
||||||
"status": "Present"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,44 +1,8 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "date": "2026-07-01", "timeIn": "09:05 AM", "timeOut": "06:15 PM", "loggedHours": "9h 10m", "status": "Present" },
|
||||||
"date": "2026-07-01",
|
{ "date": "2026-07-02", "timeIn": "10:30 AM", "timeOut": "06:30 PM", "loggedHours": "8h 00m", "status": "Late" },
|
||||||
"timeIn": "09:05 AM",
|
{ "date": "2026-07-03", "timeIn": "-", "timeOut": "-", "loggedHours": "-", "status": "Leave" },
|
||||||
"timeOut": "06:15 PM",
|
{ "date": "2026-07-04", "timeIn": "09:00 AM", "timeOut": "05:05 PM", "loggedHours": "8h 05m", "status": "Present" },
|
||||||
"loggedHours": "9h 10m",
|
{ "date": "2026-07-08", "timeIn": "09:15 AM", "timeOut": "02:00 PM", "loggedHours": "4h 45m", "status": "Half Day" },
|
||||||
"status": "Present"
|
{ "date": "2026-07-17", "timeIn": "09:15 AM", "timeOut": "-", "loggedHours": "-", "status": "Mispunch" }
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-02",
|
|
||||||
"timeIn": "10:30 AM",
|
|
||||||
"timeOut": "06:30 PM",
|
|
||||||
"loggedHours": "8h 00m",
|
|
||||||
"status": "Late"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-03",
|
|
||||||
"timeIn": "-",
|
|
||||||
"timeOut": "-",
|
|
||||||
"loggedHours": "-",
|
|
||||||
"status": "Leave"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-04",
|
|
||||||
"timeIn": "09:00 AM",
|
|
||||||
"timeOut": "05:05 PM",
|
|
||||||
"loggedHours": "8h 05m",
|
|
||||||
"status": "Present"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-08",
|
|
||||||
"timeIn": "09:15 AM",
|
|
||||||
"timeOut": "02:00 PM",
|
|
||||||
"loggedHours": "4h 45m",
|
|
||||||
"status": "Half Day"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-17",
|
|
||||||
"timeIn": "09:15 AM",
|
|
||||||
"timeOut": "-",
|
|
||||||
"loggedHours": "-",
|
|
||||||
"status": "Mispunch"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,23 +1,5 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "date": "2026-07-01", "timeIn": "09:30 AM", "timeOut": "06:30 PM", "loggedHours": "9h 00m", "status": "Late" },
|
||||||
"date": "2026-07-01",
|
{ "date": "2026-07-03", "timeIn": "08:45 AM", "timeOut": "05:30 PM", "loggedHours": "8h 45m", "status": "WFH" },
|
||||||
"timeIn": "09:30 AM",
|
{ "date": "2026-07-10", "timeIn": "09:00 AM", "timeOut": "-", "loggedHours": "-", "status": "Mispunch" }
|
||||||
"timeOut": "06:30 PM",
|
|
||||||
"loggedHours": "9h 00m",
|
|
||||||
"status": "Late"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-03",
|
|
||||||
"timeIn": "08:45 AM",
|
|
||||||
"timeOut": "05:30 PM",
|
|
||||||
"loggedHours": "8h 45m",
|
|
||||||
"status": "WFH"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"date": "2026-07-10",
|
|
||||||
"timeIn": "09:00 AM",
|
|
||||||
"timeOut": "-",
|
|
||||||
"loggedHours": "-",
|
|
||||||
"status": "Mispunch"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,22 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "A001", "leaveType": "Casual Leave", "applicationDate": "2026-05-10", "fromDate": "2026-06-10", "toDate": "2026-06-11", "leaveDays": 2, "status": "Approved", "reason": "Medical appointment." },
|
||||||
"id": "A001",
|
{ "id": "A002", "leaveType": "Casual Leave", "applicationDate": "2026-06-20", "fromDate": "2026-07-01", "toDate": "2026-07-02", "leaveDays": 2, "status": "Withdrawn", "reason": "Changed plans." }
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-05-10",
|
|
||||||
"fromDate": "2026-06-10",
|
|
||||||
"toDate": "2026-06-11",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "Medical appointment."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "A002",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-06-20",
|
|
||||||
"fromDate": "2026-07-01",
|
|
||||||
"toDate": "2026-07-02",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Withdrawn",
|
|
||||||
"reason": "Changed plans."
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,16 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "leaveType": "Casual Leave", "credited": 15, "utilized": 5, "lapsed": 0, "balance": 10 },
|
||||||
"leaveType": "Casual Leave",
|
{ "leaveType": "Optional Holiday", "credited": 4, "utilized": 0, "lapsed": 0, "balance": 4 }
|
||||||
"credited": 15,
|
|
||||||
"utilized": 5,
|
|
||||||
"lapsed": 0,
|
|
||||||
"balance": 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"credited": 4,
|
|
||||||
"utilized": 0,
|
|
||||||
"lapsed": 0,
|
|
||||||
"balance": 4
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,82 +1,10 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "L001", "leaveType": "Casual Leave", "applicationDate": "2026-07-10", "fromDate": "2026-07-04", "toDate": "2026-07-05", "leaveDays": 2, "status": "Approved", "reason": "Family function out of town." },
|
||||||
"id": "L001",
|
{ "id": "L002", "leaveType": "Optional Holiday", "applicationDate": "2026-07-15", "fromDate": "2026-07-14", "toDate": "2026-07-14", "leaveDays": 1, "status": "Pending", "reason": "Personal work." },
|
||||||
"leaveType": "Casual Leave",
|
{ "id": "L003", "leaveType": "Casual Leave", "applicationDate": "2026-07-20", "fromDate": "2026-06-10", "toDate": "2026-06-11", "leaveDays": 2, "status": "Withdrawn", "reason": "Changed plans." },
|
||||||
"applicationDate": "2026-07-10",
|
{ "id": "L004", "leaveType": "Casual Leave", "applicationDate": "2026-07-22", "fromDate": "2026-06-20", "toDate": "2026-06-20", "leaveDays": 1, "status": "Approved", "reason": "Medical appointment." },
|
||||||
"fromDate": "2026-07-04",
|
{ "id": "L005", "leaveType": "Optional Holiday", "applicationDate": "2026-07-18", "fromDate": "2026-07-01", "toDate": "2026-07-02", "leaveDays": 2, "status": "Pending", "reason": "Religious festival." },
|
||||||
"toDate": "2026-07-05",
|
{ "id": "L006", "leaveType": "Casual Leave", "applicationDate": "2026-07-02", "fromDate": "2026-05-15", "toDate": "2026-05-16", "leaveDays": 2, "status": "Approved", "reason": "Friend's wedding." },
|
||||||
"leaveDays": 2,
|
{ "id": "L007", "leaveType": "Casual Leave", "applicationDate": "2026-07-10", "fromDate": "2026-04-25", "toDate": "2026-04-26", "leaveDays": 2, "status": "Approved", "reason": "House shifting." },
|
||||||
"status": "Approved",
|
{ "id": "L008", "leaveType": "Optional Holiday", "applicationDate": "2026-07-22", "fromDate": "2026-07-21", "toDate": "2026-07-21", "leaveDays": 1, "status": "Pending", "reason": "Personal work." }
|
||||||
"reason": "Family function out of town."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "L002",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-15",
|
|
||||||
"fromDate": "2026-07-14",
|
|
||||||
"toDate": "2026-07-14",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Personal work."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "L003",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-20",
|
|
||||||
"fromDate": "2026-06-10",
|
|
||||||
"toDate": "2026-06-11",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Withdrawn",
|
|
||||||
"reason": "Changed plans."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "L004",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-22",
|
|
||||||
"fromDate": "2026-06-20",
|
|
||||||
"toDate": "2026-06-20",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "Medical appointment."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "L005",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-18",
|
|
||||||
"fromDate": "2026-07-01",
|
|
||||||
"toDate": "2026-07-02",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Religious festival."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "L006",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-02",
|
|
||||||
"fromDate": "2026-05-15",
|
|
||||||
"toDate": "2026-05-16",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "Friend's wedding."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "L007",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-07-10",
|
|
||||||
"fromDate": "2026-04-25",
|
|
||||||
"toDate": "2026-04-26",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "House shifting."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "L008",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-07-22",
|
|
||||||
"fromDate": "2026-07-21",
|
|
||||||
"toDate": "2026-07-21",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Personal work."
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,16 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "leaveType": "Casual Leave", "credited": 10, "utilized": 8, "lapsed": 0, "balance": 2 },
|
||||||
"leaveType": "Casual Leave",
|
{ "leaveType": "Optional Holiday", "credited": 4, "utilized": 3, "lapsed": 0, "balance": 1 }
|
||||||
"credited": 10,
|
|
||||||
"utilized": 8,
|
|
||||||
"lapsed": 0,
|
|
||||||
"balance": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"credited": 4,
|
|
||||||
"utilized": 3,
|
|
||||||
"lapsed": 0,
|
|
||||||
"balance": 1
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,134 +1,14 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "LA001", "employeeId": "EMP001", "employeeName": "Alice Williams", "designation": "Software Engineer", "department": "IT", "managerName": "Bob Smith", "leaveType": "Casual Leave", "leaveDays": 2, "status": "Approved" },
|
||||||
"id": "LA001",
|
{ "id": "LA002", "employeeId": "EMP002", "employeeName": "Bob Smith", "designation": "Senior Software Engineer", "department": "IT", "managerName": "Charlie Brown", "leaveType": "Sick Leave", "leaveDays": 1, "status": "Pending" },
|
||||||
"employeeId": "EMP001",
|
{ "id": "LA003", "employeeId": "EMP003", "employeeName": "Charlie Brown", "designation": "Trainer", "department": "Training", "managerName": "Diana Prince", "leaveType": "Optional Holiday", "leaveDays": 1, "status": "Rejected" },
|
||||||
"employeeName": "Alice Williams",
|
{ "id": "LA004", "employeeId": "EMP004", "employeeName": "Diana Prince", "designation": "Data Analyst", "department": "Analytics", "managerName": "Evan Wright", "leaveType": "Casual Leave", "leaveDays": 3, "status": "Approved" },
|
||||||
"designation": "Software Engineer",
|
{ "id": "LA005", "employeeId": "EMP005", "employeeName": "Evan Wright", "designation": "QA Tester", "department": "Software Dev", "managerName": "Fiona Gallagher", "leaveType": "Sick Leave", "leaveDays": 2, "status": "Pending" },
|
||||||
"department": "IT",
|
{ "id": "LA006", "employeeId": "EMP006", "employeeName": "Fiona Gallagher", "designation": "HR Executive", "department": "HR", "managerName": "George Costanza", "leaveType": "Casual Leave", "leaveDays": 1, "status": "Withdrawn" },
|
||||||
"managerName": "Bob Smith",
|
{ "id": "LA007", "employeeId": "EMP007", "employeeName": "George Costanza", "designation": "Placement Officer", "department": "Placement", "managerName": "Hannah Abbott", "leaveType": "Optional Holiday", "leaveDays": 1, "status": "Approved" },
|
||||||
"leaveType": "Casual Leave",
|
{ "id": "LA008", "employeeId": "EMP008", "employeeName": "Hannah Abbott", "designation": "Support Engineer", "department": "Support", "managerName": "Ian Malcolm", "leaveType": "Sick Leave", "leaveDays": 4, "status": "Pending" },
|
||||||
"leaveDays": 2,
|
{ "id": "LA009", "employeeId": "EMP009", "employeeName": "Ian Malcolm", "designation": "Lead Trainer", "department": "Training", "managerName": "Jane Porter", "leaveType": "Casual Leave", "leaveDays": 2, "status": "Approved" },
|
||||||
"status": "Approved"
|
{ "id": "LA010", "employeeId": "EMP010", "employeeName": "Jane Porter", "designation": "Project Manager", "department": "Software Dev", "managerName": "Kevin Hart", "leaveType": "Optional Holiday", "leaveDays": 1, "status": "Rejected" },
|
||||||
},
|
{ "id": "LA011", "employeeId": "EMP011", "employeeName": "Kevin Hart", "designation": "Software Engineer", "department": "IT", "managerName": "Laura Croft", "leaveType": "Sick Leave", "leaveDays": 1, "status": "Pending" },
|
||||||
{
|
{ "id": "LA012", "employeeId": "EMP012", "employeeName": "Laura Croft", "designation": "Data Analyst", "department": "Analytics", "managerName": "Alice Williams", "leaveType": "Casual Leave", "leaveDays": 2, "status": "Approved" }
|
||||||
"id": "LA002",
|
|
||||||
"employeeId": "EMP002",
|
|
||||||
"employeeName": "Bob Smith",
|
|
||||||
"designation": "Senior Software Engineer",
|
|
||||||
"department": "IT",
|
|
||||||
"managerName": "Charlie Brown",
|
|
||||||
"leaveType": "Sick Leave",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "LA003",
|
|
||||||
"employeeId": "EMP003",
|
|
||||||
"employeeName": "Charlie Brown",
|
|
||||||
"designation": "Trainer",
|
|
||||||
"department": "Training",
|
|
||||||
"managerName": "Diana Prince",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Rejected"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "LA004",
|
|
||||||
"employeeId": "EMP004",
|
|
||||||
"employeeName": "Diana Prince",
|
|
||||||
"designation": "Data Analyst",
|
|
||||||
"department": "Analytics",
|
|
||||||
"managerName": "Evan Wright",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"leaveDays": 3,
|
|
||||||
"status": "Approved"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "LA005",
|
|
||||||
"employeeId": "EMP005",
|
|
||||||
"employeeName": "Evan Wright",
|
|
||||||
"designation": "QA Tester",
|
|
||||||
"department": "Software Dev",
|
|
||||||
"managerName": "Fiona Gallagher",
|
|
||||||
"leaveType": "Sick Leave",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Pending"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "LA006",
|
|
||||||
"employeeId": "EMP006",
|
|
||||||
"employeeName": "Fiona Gallagher",
|
|
||||||
"designation": "HR Executive",
|
|
||||||
"department": "HR",
|
|
||||||
"managerName": "George Costanza",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Withdrawn"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "LA007",
|
|
||||||
"employeeId": "EMP007",
|
|
||||||
"employeeName": "George Costanza",
|
|
||||||
"designation": "Placement Officer",
|
|
||||||
"department": "Placement",
|
|
||||||
"managerName": "Hannah Abbott",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Approved"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "LA008",
|
|
||||||
"employeeId": "EMP008",
|
|
||||||
"employeeName": "Hannah Abbott",
|
|
||||||
"designation": "Support Engineer",
|
|
||||||
"department": "Support",
|
|
||||||
"managerName": "Ian Malcolm",
|
|
||||||
"leaveType": "Sick Leave",
|
|
||||||
"leaveDays": 4,
|
|
||||||
"status": "Pending"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "LA009",
|
|
||||||
"employeeId": "EMP009",
|
|
||||||
"employeeName": "Ian Malcolm",
|
|
||||||
"designation": "Lead Trainer",
|
|
||||||
"department": "Training",
|
|
||||||
"managerName": "Jane Porter",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Approved"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "LA010",
|
|
||||||
"employeeId": "EMP010",
|
|
||||||
"employeeName": "Jane Porter",
|
|
||||||
"designation": "Project Manager",
|
|
||||||
"department": "Software Dev",
|
|
||||||
"managerName": "Kevin Hart",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Rejected"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "LA011",
|
|
||||||
"employeeId": "EMP011",
|
|
||||||
"employeeName": "Kevin Hart",
|
|
||||||
"designation": "Software Engineer",
|
|
||||||
"department": "IT",
|
|
||||||
"managerName": "Laura Croft",
|
|
||||||
"leaveType": "Sick Leave",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "LA012",
|
|
||||||
"employeeId": "EMP012",
|
|
||||||
"employeeName": "Laura Croft",
|
|
||||||
"designation": "Data Analyst",
|
|
||||||
"department": "Analytics",
|
|
||||||
"managerName": "Alice Williams",
|
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Approved"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,52 +1,7 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "employeeId": "EMP001", "employeeName": "Alice Williams", "month": 7, "year": 2026, "openingBalance": 12, "closingBalance": 10, "casualLeave": 8, "optionalLeave": 2 },
|
||||||
"employeeId": "EMP001",
|
{ "employeeId": "EMP002", "employeeName": "Bob Smith", "month": 7, "year": 2026, "openingBalance": 15, "closingBalance": 14, "casualLeave": 10, "optionalLeave": 4 },
|
||||||
"employeeName": "Alice Williams",
|
{ "employeeId": "EMP001", "employeeName": "Alice Williams", "month": 6, "year": 2026, "openingBalance": 15, "closingBalance": 12, "casualLeave": 10, "optionalLeave": 2 },
|
||||||
"month": 7,
|
{ "employeeId": "EMP003", "employeeName": "Charlie Brown", "month": 7, "year": 2026, "openingBalance": 10, "closingBalance": 8, "casualLeave": 6, "optionalLeave": 2 },
|
||||||
"year": 2026,
|
{ "employeeId": "EMP004", "employeeName": "Diana Prince", "month": 7, "year": 2026, "openingBalance": 12, "closingBalance": 12, "casualLeave": 12, "optionalLeave": 0 }
|
||||||
"openingBalance": 12,
|
|
||||||
"closingBalance": 10,
|
|
||||||
"casualLeave": 8,
|
|
||||||
"optionalLeave": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"employeeId": "EMP002",
|
|
||||||
"employeeName": "Bob Smith",
|
|
||||||
"month": 7,
|
|
||||||
"year": 2026,
|
|
||||||
"openingBalance": 15,
|
|
||||||
"closingBalance": 14,
|
|
||||||
"casualLeave": 10,
|
|
||||||
"optionalLeave": 4
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"employeeId": "EMP001",
|
|
||||||
"employeeName": "Alice Williams",
|
|
||||||
"month": 6,
|
|
||||||
"year": 2026,
|
|
||||||
"openingBalance": 15,
|
|
||||||
"closingBalance": 12,
|
|
||||||
"casualLeave": 10,
|
|
||||||
"optionalLeave": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"employeeId": "EMP003",
|
|
||||||
"employeeName": "Charlie Brown",
|
|
||||||
"month": 7,
|
|
||||||
"year": 2026,
|
|
||||||
"openingBalance": 10,
|
|
||||||
"closingBalance": 8,
|
|
||||||
"casualLeave": 6,
|
|
||||||
"optionalLeave": 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"employeeId": "EMP004",
|
|
||||||
"employeeName": "Diana Prince",
|
|
||||||
"month": 7,
|
|
||||||
"year": 2026,
|
|
||||||
"openingBalance": 12,
|
|
||||||
"closingBalance": 12,
|
|
||||||
"casualLeave": 12,
|
|
||||||
"optionalLeave": 0
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,22 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "M001", "leaveType": "Casual Leave", "applicationDate": "2026-06-12", "fromDate": "2026-07-04", "toDate": "2026-07-05", "leaveDays": 2, "status": "Approved", "reason": "Family function." },
|
||||||
"id": "M001",
|
{ "id": "M002", "leaveType": "Optional Holiday", "applicationDate": "2026-06-18", "fromDate": "2026-07-14", "toDate": "2026-07-14", "leaveDays": 1, "status": "Pending", "reason": "Personal work." }
|
||||||
"leaveType": "Casual Leave",
|
|
||||||
"applicationDate": "2026-06-12",
|
|
||||||
"fromDate": "2026-07-04",
|
|
||||||
"toDate": "2026-07-05",
|
|
||||||
"leaveDays": 2,
|
|
||||||
"status": "Approved",
|
|
||||||
"reason": "Family function."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "M002",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-06-18",
|
|
||||||
"fromDate": "2026-07-14",
|
|
||||||
"toDate": "2026-07-14",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Personal work."
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,16 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "leaveType": "Casual Leave", "credited": 12, "utilized": 4, "lapsed": 0, "balance": 8 },
|
||||||
"leaveType": "Casual Leave",
|
{ "leaveType": "Optional Holiday", "credited": 4, "utilized": 1, "lapsed": 0, "balance": 3 }
|
||||||
"credited": 12,
|
|
||||||
"utilized": 4,
|
|
||||||
"lapsed": 0,
|
|
||||||
"balance": 8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"credited": 4,
|
|
||||||
"utilized": 1,
|
|
||||||
"lapsed": 0,
|
|
||||||
"balance": 3
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,12 +1,3 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "S001", "leaveType": "Optional Holiday", "applicationDate": "2026-06-22", "fromDate": "2026-07-21", "toDate": "2026-07-21", "leaveDays": 1, "status": "Pending", "reason": "Festival." }
|
||||||
"id": "S001",
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"applicationDate": "2026-06-22",
|
|
||||||
"fromDate": "2026-07-21",
|
|
||||||
"toDate": "2026-07-21",
|
|
||||||
"leaveDays": 1,
|
|
||||||
"status": "Pending",
|
|
||||||
"reason": "Festival."
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,16 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "leaveType": "Casual Leave", "credited": 20, "utilized": 2, "lapsed": 0, "balance": 18 },
|
||||||
"leaveType": "Casual Leave",
|
{ "leaveType": "Optional Holiday", "credited": 4, "utilized": 2, "lapsed": 0, "balance": 2 }
|
||||||
"credited": 20,
|
|
||||||
"utilized": 2,
|
|
||||||
"lapsed": 0,
|
|
||||||
"balance": 18
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"leaveType": "Optional Holiday",
|
|
||||||
"credited": 4,
|
|
||||||
"utilized": 2,
|
|
||||||
"lapsed": 0,
|
|
||||||
"balance": 2
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,134 +1,14 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "EMP001", "name": "Alice Williams", "designationName": "Software Engineer", "deptName": "IT", "branchName": "Bengaluru", "companyName": "CLRI", "status": "Active", "isAdmin": true },
|
||||||
"id": "EMP001",
|
{ "id": "EMP002", "name": "Bob Smith", "designationName": "Senior Software Engineer", "deptName": "IT", "branchName": "Bengaluru", "companyName": "CLRI", "status": "Active", "isAdmin": false },
|
||||||
"name": "Alice Williams",
|
{ "id": "EMP003", "name": "Charlie Brown", "designationName": "Trainer", "deptName": "Training", "branchName": "Pune", "companyName": "CLRI", "status": "Disabled", "isAdmin": false },
|
||||||
"email": "alice@example.com",
|
{ "id": "EMP004", "name": "Diana Prince", "designationName": "Data Analyst", "deptName": "Analytics", "branchName": "Bengaluru", "companyName": "CLRI", "status": "Active", "isAdmin": true },
|
||||||
"designationName": "Software Engineer",
|
{ "id": "EMP005", "name": "Evan Wright", "designationName": "QA Tester", "deptName": "Software Dev", "branchName": "Mumbai", "companyName": "TechNova Solutions", "status": "Active", "isAdmin": false },
|
||||||
"deptName": "IT",
|
{ "id": "EMP006", "name": "Fiona Gallagher", "designationName": "HR Executive", "deptName": "HR", "branchName": "Chennai", "companyName": "Infotech Ltd", "status": "Active", "isAdmin": false },
|
||||||
"branchName": "Bengaluru",
|
{ "id": "EMP007", "name": "George Costanza", "designationName": "Placement Officer", "deptName": "Placement", "branchName": "Bengaluru", "companyName": "CLRI", "status": "Active", "isAdmin": true },
|
||||||
"companyName": "CLRI",
|
{ "id": "EMP008", "name": "Hannah Abbott", "designationName": "Support Engineer", "deptName": "Support", "branchName": "Ahmedabad", "companyName": "Global Systems", "status": "Disabled", "isAdmin": false },
|
||||||
"status": "Active",
|
{ "id": "EMP009", "name": "Ian Malcolm", "designationName": "Lead Trainer", "deptName": "Training", "branchName": "Pune", "companyName": "CLRI", "status": "Active", "isAdmin": false },
|
||||||
"isAdmin": true
|
{ "id": "EMP010", "name": "Jane Porter", "designationName": "Project Manager", "deptName": "Software Dev", "branchName": "Mumbai", "companyName": "TechNova Solutions", "status": "Active", "isAdmin": true },
|
||||||
},
|
{ "id": "EMP011", "name": "Kevin Hart", "designationName": "Software Engineer", "deptName": "IT", "branchName": "Bengaluru", "companyName": "CLRI", "status": "Active", "isAdmin": false },
|
||||||
{
|
{ "id": "EMP012", "name": "Laura Croft", "designationName": "Data Analyst", "deptName": "Analytics", "branchName": "Bengaluru", "companyName": "CLRI", "status": "Active", "isAdmin": false }
|
||||||
"id": "EMP002",
|
|
||||||
"name": "Bob Smith",
|
|
||||||
"email": "bob@example.com",
|
|
||||||
"designationName": "Senior Software Engineer",
|
|
||||||
"deptName": "IT",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active",
|
|
||||||
"isAdmin": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"name": "Charlie Brown",
|
|
||||||
"email": "charlie@example.com",
|
|
||||||
"designationName": "Trainer",
|
|
||||||
"deptName": "Training",
|
|
||||||
"branchName": "Pune",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Disabled",
|
|
||||||
"isAdmin": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"name": "Diana Prince",
|
|
||||||
"email": "diana@example.com",
|
|
||||||
"designationName": "Data Analyst",
|
|
||||||
"deptName": "Analytics",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active",
|
|
||||||
"isAdmin": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"name": "Evan Wright",
|
|
||||||
"email": "evan@example.com",
|
|
||||||
"designationName": "QA Tester",
|
|
||||||
"deptName": "Software Dev",
|
|
||||||
"branchName": "Mumbai",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"status": "Active",
|
|
||||||
"isAdmin": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"name": "Fiona Gallagher",
|
|
||||||
"email": "fiona@example.com",
|
|
||||||
"designationName": "HR Executive",
|
|
||||||
"deptName": "HR",
|
|
||||||
"branchName": "Chennai",
|
|
||||||
"companyName": "Infotech Ltd",
|
|
||||||
"status": "Active",
|
|
||||||
"isAdmin": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"name": "George Costanza",
|
|
||||||
"email": "george@example.com",
|
|
||||||
"designationName": "Placement Officer",
|
|
||||||
"deptName": "Placement",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active",
|
|
||||||
"isAdmin": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"name": "Hannah Abbott",
|
|
||||||
"email": "hannah@example.com",
|
|
||||||
"designationName": "Support Engineer",
|
|
||||||
"deptName": "Support",
|
|
||||||
"branchName": "Ahmedabad",
|
|
||||||
"companyName": "Global Systems",
|
|
||||||
"status": "Disabled",
|
|
||||||
"isAdmin": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP009",
|
|
||||||
"name": "Ian Malcolm",
|
|
||||||
"email": "ian@example.com",
|
|
||||||
"designationName": "Lead Trainer",
|
|
||||||
"deptName": "Training",
|
|
||||||
"branchName": "Pune",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active",
|
|
||||||
"isAdmin": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP010",
|
|
||||||
"name": "Jane Porter",
|
|
||||||
"email": "jane@example.com",
|
|
||||||
"designationName": "Project Manager",
|
|
||||||
"deptName": "Software Dev",
|
|
||||||
"branchName": "Mumbai",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"status": "Active",
|
|
||||||
"isAdmin": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP011",
|
|
||||||
"name": "Kevin Hart",
|
|
||||||
"email": "kevin@example.com",
|
|
||||||
"designationName": "Software Engineer",
|
|
||||||
"deptName": "IT",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active",
|
|
||||||
"isAdmin": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP012",
|
|
||||||
"name": "Laura Croft",
|
|
||||||
"email": "laura@example.com",
|
|
||||||
"designationName": "Data Analyst",
|
|
||||||
"deptName": "Analytics",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active",
|
|
||||||
"isAdmin": false
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,86 +1,14 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "BR001", "name": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"id": "BR001",
|
{ "id": "BR002", "name": "Pune", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"name": "Bengaluru",
|
{ "id": "BR003", "name": "Hyderabad", "companyId": "CMP001", "companyName": "CLRI", "status": "Disabled" },
|
||||||
"companyId": "CMP001",
|
{ "id": "BR004", "name": "Mumbai", "companyId": "CMP002", "companyName": "TechNova Solutions", "status": "Active" },
|
||||||
"companyName": "CLRI",
|
{ "id": "BR005", "name": "Delhi", "companyId": "CMP002", "companyName": "TechNova Solutions", "status": "Active" },
|
||||||
"status": "Active"
|
{ "id": "BR006", "name": "Chennai", "companyId": "CMP003", "companyName": "Infotech Ltd", "status": "Active" },
|
||||||
},
|
{ "id": "BR007", "name": "Kolkata", "companyId": "CMP003", "companyName": "Infotech Ltd", "status": "Disabled" },
|
||||||
{
|
{ "id": "BR008", "name": "Ahmedabad", "companyId": "CMP004", "companyName": "Global Systems", "status": "Active" },
|
||||||
"id": "BR002",
|
{ "id": "BR009", "name": "Jaipur", "companyId": "CMP004", "companyName": "Global Systems", "status": "Active" },
|
||||||
"name": "Pune",
|
{ "id": "BR010", "name": "Surat", "companyId": "CMP005", "companyName": "Alpha Corp", "status": "Disabled" },
|
||||||
"companyId": "CMP001",
|
{ "id": "BR011", "name": "Lucknow", "companyId": "CMP006", "companyName": "Beta Industries", "status": "Active" },
|
||||||
"companyName": "CLRI",
|
{ "id": "BR012", "name": "Chandigarh", "companyId": "CMP006", "companyName": "Beta Industries", "status": "Active" }
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "BR003",
|
|
||||||
"name": "Hyderabad",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "BR004",
|
|
||||||
"name": "Mumbai",
|
|
||||||
"companyId": "CMP002",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "BR005",
|
|
||||||
"name": "Delhi",
|
|
||||||
"companyId": "CMP002",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "BR006",
|
|
||||||
"name": "Chennai",
|
|
||||||
"companyId": "CMP003",
|
|
||||||
"companyName": "Infotech Ltd",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "BR007",
|
|
||||||
"name": "Kolkata",
|
|
||||||
"companyId": "CMP003",
|
|
||||||
"companyName": "Infotech Ltd",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "BR008",
|
|
||||||
"name": "Ahmedabad",
|
|
||||||
"companyId": "CMP004",
|
|
||||||
"companyName": "Global Systems",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "BR009",
|
|
||||||
"name": "Jaipur",
|
|
||||||
"companyId": "CMP004",
|
|
||||||
"companyName": "Global Systems",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "BR010",
|
|
||||||
"name": "Surat",
|
|
||||||
"companyId": "CMP005",
|
|
||||||
"companyName": "Alpha Corp",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "BR011",
|
|
||||||
"name": "Lucknow",
|
|
||||||
"companyId": "CMP006",
|
|
||||||
"companyName": "Beta Industries",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "BR012",
|
|
||||||
"name": "Chandigarh",
|
|
||||||
"companyId": "CMP006",
|
|
||||||
"companyName": "Beta Industries",
|
|
||||||
"status": "Active"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,62 +1,14 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "CMP001", "name": "CLRI", "status": "Active" },
|
||||||
"id": "CMP001",
|
{ "id": "CMP002", "name": "TechNova Solutions", "status": "Active" },
|
||||||
"name": "CLRI",
|
{ "id": "CMP003", "name": "Infotech Ltd", "status": "Disabled" },
|
||||||
"status": "Active"
|
{ "id": "CMP004", "name": "Global Systems", "status": "Active" },
|
||||||
},
|
{ "id": "CMP005", "name": "Alpha Corp", "status": "Disabled" },
|
||||||
{
|
{ "id": "CMP006", "name": "Beta Industries", "status": "Active" },
|
||||||
"id": "CMP002",
|
{ "id": "CMP007", "name": "Gamma Tech", "status": "Active" },
|
||||||
"name": "TechNova Solutions",
|
{ "id": "CMP008", "name": "Delta Services", "status": "Active" },
|
||||||
"status": "Active"
|
{ "id": "CMP009", "name": "Epsilon Enterprises", "status": "Disabled" },
|
||||||
},
|
{ "id": "CMP010", "name": "Zeta Labs", "status": "Active" },
|
||||||
{
|
{ "id": "CMP011", "name": "Eta Group", "status": "Active" },
|
||||||
"id": "CMP003",
|
{ "id": "CMP012", "name": "Theta Digital", "status": "Active" }
|
||||||
"name": "Infotech Ltd",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "CMP004",
|
|
||||||
"name": "Global Systems",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "CMP005",
|
|
||||||
"name": "Alpha Corp",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "CMP006",
|
|
||||||
"name": "Beta Industries",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "CMP007",
|
|
||||||
"name": "Gamma Tech",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "CMP008",
|
|
||||||
"name": "Delta Services",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "CMP009",
|
|
||||||
"name": "Epsilon Enterprises",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "CMP010",
|
|
||||||
"name": "Zeta Labs",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "CMP011",
|
|
||||||
"name": "Eta Group",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "CMP012",
|
|
||||||
"name": "Theta Digital",
|
|
||||||
"status": "Active"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,110 +1,14 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "DEP001", "name": "IT", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"id": "DEP001",
|
{ "id": "DEP002", "name": "Training", "branchId": "BR002", "branchName": "Pune", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"name": "IT",
|
{ "id": "DEP003", "name": "Analytics", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Disabled" },
|
||||||
"branchId": "BR001",
|
{ "id": "DEP004", "name": "Placement", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"branchName": "Bengaluru",
|
{ "id": "DEP005", "name": "Digital Marketing", "branchId": "BR002", "branchName": "Pune", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"companyId": "CMP001",
|
{ "id": "DEP006", "name": "Software Dev", "branchId": "BR004", "branchName": "Mumbai", "companyId": "CMP002", "companyName": "TechNova Solutions", "status": "Active" },
|
||||||
"companyName": "CLRI",
|
{ "id": "DEP007", "name": "QA", "branchId": "BR004", "branchName": "Mumbai", "companyId": "CMP002", "companyName": "TechNova Solutions", "status": "Active" },
|
||||||
"status": "Active"
|
{ "id": "DEP008", "name": "HR", "branchId": "BR006", "branchName": "Chennai", "companyId": "CMP003", "companyName": "Infotech Ltd", "status": "Active" },
|
||||||
},
|
{ "id": "DEP009", "name": "Sales", "branchId": "BR006", "branchName": "Chennai", "companyId": "CMP003", "companyName": "Infotech Ltd", "status": "Disabled" },
|
||||||
{
|
{ "id": "DEP010", "name": "Support", "branchId": "BR008", "branchName": "Ahmedabad", "companyId": "CMP004", "companyName": "Global Systems", "status": "Active" },
|
||||||
"id": "DEP002",
|
{ "id": "DEP011", "name": "Admin", "branchId": "BR010", "branchName": "Surat", "companyId": "CMP005", "companyName": "Alpha Corp", "status": "Active" },
|
||||||
"name": "Training",
|
{ "id": "DEP012", "name": "Operations", "branchId": "BR011", "branchName": "Lucknow", "companyId": "CMP006", "companyName": "Beta Industries", "status": "Active" }
|
||||||
"branchId": "BR002",
|
|
||||||
"branchName": "Pune",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DEP003",
|
|
||||||
"name": "Analytics",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DEP004",
|
|
||||||
"name": "Placement",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DEP005",
|
|
||||||
"name": "Digital Marketing",
|
|
||||||
"branchId": "BR002",
|
|
||||||
"branchName": "Pune",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DEP006",
|
|
||||||
"name": "Software Dev",
|
|
||||||
"branchId": "BR004",
|
|
||||||
"branchName": "Mumbai",
|
|
||||||
"companyId": "CMP002",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DEP007",
|
|
||||||
"name": "QA",
|
|
||||||
"branchId": "BR004",
|
|
||||||
"branchName": "Mumbai",
|
|
||||||
"companyId": "CMP002",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DEP008",
|
|
||||||
"name": "HR",
|
|
||||||
"branchId": "BR006",
|
|
||||||
"branchName": "Chennai",
|
|
||||||
"companyId": "CMP003",
|
|
||||||
"companyName": "Infotech Ltd",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DEP009",
|
|
||||||
"name": "Sales",
|
|
||||||
"branchId": "BR006",
|
|
||||||
"branchName": "Chennai",
|
|
||||||
"companyId": "CMP003",
|
|
||||||
"companyName": "Infotech Ltd",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DEP010",
|
|
||||||
"name": "Support",
|
|
||||||
"branchId": "BR008",
|
|
||||||
"branchName": "Ahmedabad",
|
|
||||||
"companyId": "CMP004",
|
|
||||||
"companyName": "Global Systems",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DEP011",
|
|
||||||
"name": "Admin",
|
|
||||||
"branchId": "BR010",
|
|
||||||
"branchName": "Surat",
|
|
||||||
"companyId": "CMP005",
|
|
||||||
"companyName": "Alpha Corp",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DEP012",
|
|
||||||
"name": "Operations",
|
|
||||||
"branchId": "BR011",
|
|
||||||
"branchName": "Lucknow",
|
|
||||||
"companyId": "CMP006",
|
|
||||||
"companyName": "Beta Industries",
|
|
||||||
"status": "Active"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,134 +1,14 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "DSG001", "name": "Software Engineer", "deptId": "DEP001", "deptName": "IT", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"id": "DSG001",
|
{ "id": "DSG002", "name": "Senior Software Engineer", "deptId": "DEP001", "deptName": "IT", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"name": "Software Engineer",
|
{ "id": "DSG003", "name": "IT Manager", "deptId": "DEP001", "deptName": "IT", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Disabled" },
|
||||||
"deptId": "DEP001",
|
{ "id": "DSG004", "name": "Trainer", "deptId": "DEP002", "deptName": "Training", "branchId": "BR002", "branchName": "Pune", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"deptName": "IT",
|
{ "id": "DSG005", "name": "Lead Trainer", "deptId": "DEP002", "deptName": "Training", "branchId": "BR002", "branchName": "Pune", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"branchId": "BR001",
|
{ "id": "DSG006", "name": "Data Analyst", "deptId": "DEP003", "deptName": "Analytics", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"branchName": "Bengaluru",
|
{ "id": "DSG007", "name": "Placement Officer", "deptId": "DEP004", "deptName": "Placement", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"companyId": "CMP001",
|
{ "id": "DSG008", "name": "QA Tester", "deptId": "DEP006", "deptName": "Software Dev", "branchId": "BR004", "branchName": "Mumbai", "companyId": "CMP002", "companyName": "TechNova Solutions", "status": "Active" },
|
||||||
"companyName": "CLRI",
|
{ "id": "DSG009", "name": "Project Manager", "deptId": "DEP006", "deptName": "Software Dev", "branchId": "BR004", "branchName": "Mumbai", "companyId": "CMP002", "companyName": "TechNova Solutions", "status": "Active" },
|
||||||
"status": "Active"
|
{ "id": "DSG010", "name": "HR Executive", "deptId": "DEP008", "deptName": "HR", "branchId": "BR006", "branchName": "Chennai", "companyId": "CMP003", "companyName": "Infotech Ltd", "status": "Active" },
|
||||||
},
|
{ "id": "DSG011", "name": "Sales Head", "deptId": "DEP009", "deptName": "Sales", "branchId": "BR006", "branchName": "Chennai", "companyId": "CMP003", "companyName": "Infotech Ltd", "status": "Disabled" },
|
||||||
{
|
{ "id": "DSG012", "name": "Support Engineer", "deptId": "DEP010", "deptName": "Support", "branchId": "BR008", "branchName": "Ahmedabad", "companyId": "CMP004", "companyName": "Global Systems", "status": "Active" }
|
||||||
"id": "DSG002",
|
|
||||||
"name": "Senior Software Engineer",
|
|
||||||
"deptId": "DEP001",
|
|
||||||
"deptName": "IT",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DSG003",
|
|
||||||
"name": "IT Manager",
|
|
||||||
"deptId": "DEP001",
|
|
||||||
"deptName": "IT",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DSG004",
|
|
||||||
"name": "Trainer",
|
|
||||||
"deptId": "DEP002",
|
|
||||||
"deptName": "Training",
|
|
||||||
"branchId": "BR002",
|
|
||||||
"branchName": "Pune",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DSG005",
|
|
||||||
"name": "Lead Trainer",
|
|
||||||
"deptId": "DEP002",
|
|
||||||
"deptName": "Training",
|
|
||||||
"branchId": "BR002",
|
|
||||||
"branchName": "Pune",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DSG006",
|
|
||||||
"name": "Data Analyst",
|
|
||||||
"deptId": "DEP003",
|
|
||||||
"deptName": "Analytics",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DSG007",
|
|
||||||
"name": "Placement Officer",
|
|
||||||
"deptId": "DEP004",
|
|
||||||
"deptName": "Placement",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DSG008",
|
|
||||||
"name": "QA Tester",
|
|
||||||
"deptId": "DEP006",
|
|
||||||
"deptName": "Software Dev",
|
|
||||||
"branchId": "BR004",
|
|
||||||
"branchName": "Mumbai",
|
|
||||||
"companyId": "CMP002",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DSG009",
|
|
||||||
"name": "Project Manager",
|
|
||||||
"deptId": "DEP006",
|
|
||||||
"deptName": "Software Dev",
|
|
||||||
"branchId": "BR004",
|
|
||||||
"branchName": "Mumbai",
|
|
||||||
"companyId": "CMP002",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DSG010",
|
|
||||||
"name": "HR Executive",
|
|
||||||
"deptId": "DEP008",
|
|
||||||
"deptName": "HR",
|
|
||||||
"branchId": "BR006",
|
|
||||||
"branchName": "Chennai",
|
|
||||||
"companyId": "CMP003",
|
|
||||||
"companyName": "Infotech Ltd",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DSG011",
|
|
||||||
"name": "Sales Head",
|
|
||||||
"deptId": "DEP009",
|
|
||||||
"deptName": "Sales",
|
|
||||||
"branchId": "BR006",
|
|
||||||
"branchName": "Chennai",
|
|
||||||
"companyId": "CMP003",
|
|
||||||
"companyName": "Infotech Ltd",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "DSG012",
|
|
||||||
"name": "Support Engineer",
|
|
||||||
"deptId": "DEP010",
|
|
||||||
"deptName": "Support",
|
|
||||||
"branchId": "BR008",
|
|
||||||
"branchName": "Ahmedabad",
|
|
||||||
"companyId": "CMP004",
|
|
||||||
"companyName": "Global Systems",
|
|
||||||
"status": "Active"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,170 +1,14 @@
|
|||||||
[
|
[
|
||||||
{
|
{ "id": "EMP001", "name": "Alice Williams", "designationId": "DSG001", "designationName": "Software Engineer", "deptId": "DEP001", "deptName": "IT", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"id": "EMP001",
|
{ "id": "EMP002", "name": "Bob Smith", "designationId": "DSG002", "designationName": "Senior Software Engineer", "deptId": "DEP001", "deptName": "IT", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"name": "Alice Williams",
|
{ "id": "EMP003", "name": "Charlie Brown", "designationId": "DSG004", "designationName": "Trainer", "deptId": "DEP002", "deptName": "Training", "branchId": "BR002", "branchName": "Pune", "companyId": "CMP001", "companyName": "CLRI", "status": "Disabled" },
|
||||||
"email": "AliceWilliams@gmail.com",
|
{ "id": "EMP004", "name": "Diana Prince", "designationId": "DSG006", "designationName": "Data Analyst", "deptId": "DEP003", "deptName": "Analytics", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"designationId": "DSG001",
|
{ "id": "EMP005", "name": "Evan Wright", "designationId": "DSG008", "designationName": "QA Tester", "deptId": "DEP006", "deptName": "Software Dev", "branchId": "BR004", "branchName": "Mumbai", "companyId": "CMP002", "companyName": "TechNova Solutions", "status": "Active" },
|
||||||
"designationName": "Software Engineer",
|
{ "id": "EMP006", "name": "Fiona Gallagher", "designationId": "DSG010", "designationName": "HR Executive", "deptId": "DEP008", "deptName": "HR", "branchId": "BR006", "branchName": "Chennai", "companyId": "CMP003", "companyName": "Infotech Ltd", "status": "Active" },
|
||||||
"deptId": "DEP001",
|
{ "id": "EMP007", "name": "George Costanza", "designationId": "DSG007", "designationName": "Placement Officer", "deptId": "DEP004", "deptName": "Placement", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"deptName": "IT",
|
{ "id": "EMP008", "name": "Hannah Abbott", "designationId": "DSG012", "designationName": "Support Engineer", "deptId": "DEP010", "deptName": "Support", "branchId": "BR008", "branchName": "Ahmedabad", "companyId": "CMP004", "companyName": "Global Systems", "status": "Disabled" },
|
||||||
"branchId": "BR001",
|
{ "id": "EMP009", "name": "Ian Malcolm", "designationId": "DSG005", "designationName": "Lead Trainer", "deptId": "DEP002", "deptName": "Training", "branchId": "BR002", "branchName": "Pune", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"branchName": "Bengaluru",
|
{ "id": "EMP010", "name": "Jane Porter", "designationId": "DSG009", "designationName": "Project Manager", "deptId": "DEP006", "deptName": "Software Dev", "branchId": "BR004", "branchName": "Mumbai", "companyId": "CMP002", "companyName": "TechNova Solutions", "status": "Active" },
|
||||||
"companyId": "CMP001",
|
{ "id": "EMP011", "name": "Kevin Hart", "designationId": "DSG001", "designationName": "Software Engineer", "deptId": "DEP001", "deptName": "IT", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" },
|
||||||
"companyName": "CLRI",
|
{ "id": "EMP012", "name": "Laura Croft", "designationId": "DSG006", "designationName": "Data Analyst", "deptId": "DEP003", "deptName": "Analytics", "branchId": "BR001", "branchName": "Bengaluru", "companyId": "CMP001", "companyName": "CLRI", "status": "Active" }
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP002",
|
|
||||||
"name": "Bob Smith",
|
|
||||||
"email": "BobSmith@gmail.com",
|
|
||||||
"designationId": "DSG002",
|
|
||||||
"designationName": "Senior Software Engineer",
|
|
||||||
"deptId": "DEP001",
|
|
||||||
"deptName": "IT",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP003",
|
|
||||||
"name": "Charlie Brown",
|
|
||||||
"email": "CharlieBrown@gmail.com",
|
|
||||||
"designationId": "DSG004",
|
|
||||||
"designationName": "Trainer",
|
|
||||||
"deptId": "DEP002",
|
|
||||||
"deptName": "Training",
|
|
||||||
"branchId": "BR002",
|
|
||||||
"branchName": "Pune",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP004",
|
|
||||||
"name": "Diana Prince",
|
|
||||||
"email": "DianaPrince@gmail.com",
|
|
||||||
"designationId": "DSG006",
|
|
||||||
"designationName": "Data Analyst",
|
|
||||||
"deptId": "DEP003",
|
|
||||||
"deptName": "Analytics",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP005",
|
|
||||||
"name": "Evan Wright",
|
|
||||||
"email": "EvanWright@gmail.com",
|
|
||||||
"designationId": "DSG008",
|
|
||||||
"designationName": "QA Tester",
|
|
||||||
"deptId": "DEP006",
|
|
||||||
"deptName": "Software Dev",
|
|
||||||
"branchId": "BR004",
|
|
||||||
"branchName": "Mumbai",
|
|
||||||
"companyId": "CMP002",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP006",
|
|
||||||
"name": "Fiona Gallagher",
|
|
||||||
"email": "FionaGallagher@gmail.com",
|
|
||||||
"designationId": "DSG010",
|
|
||||||
"designationName": "HR Executive",
|
|
||||||
"deptId": "DEP008",
|
|
||||||
"deptName": "HR",
|
|
||||||
"branchId": "BR006",
|
|
||||||
"branchName": "Chennai",
|
|
||||||
"companyId": "CMP003",
|
|
||||||
"companyName": "Infotech Ltd",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP007",
|
|
||||||
"name": "George Costanza",
|
|
||||||
"email": "GeorgeCostanza@gmail.com",
|
|
||||||
"designationId": "DSG007",
|
|
||||||
"designationName": "Placement Officer",
|
|
||||||
"deptId": "DEP004",
|
|
||||||
"deptName": "Placement",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP008",
|
|
||||||
"name": "Hannah Abbott",
|
|
||||||
"email": "HannahAbbott@gmail.com",
|
|
||||||
"designationId": "DSG012",
|
|
||||||
"designationName": "Support Engineer",
|
|
||||||
"deptId": "DEP010",
|
|
||||||
"deptName": "Support",
|
|
||||||
"branchId": "BR008",
|
|
||||||
"branchName": "Ahmedabad",
|
|
||||||
"companyId": "CMP004",
|
|
||||||
"companyName": "Global Systems",
|
|
||||||
"status": "Disabled"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP009",
|
|
||||||
"name": "Ian Malcolm",
|
|
||||||
"email": "IanMalcolm@gmail.com",
|
|
||||||
"designationId": "DSG005",
|
|
||||||
"designationName": "Lead Trainer",
|
|
||||||
"deptId": "DEP002",
|
|
||||||
"deptName": "Training",
|
|
||||||
"branchId": "BR002",
|
|
||||||
"branchName": "Pune",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP010",
|
|
||||||
"name": "Jane Porter",
|
|
||||||
"email": "JanePorter@gmail.com",
|
|
||||||
"designationId": "DSG009",
|
|
||||||
"designationName": "Project Manager",
|
|
||||||
"deptId": "DEP006",
|
|
||||||
"deptName": "Software Dev",
|
|
||||||
"branchId": "BR004",
|
|
||||||
"branchName": "Mumbai",
|
|
||||||
"companyId": "CMP002",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP011",
|
|
||||||
"name": "Kevin Hart",
|
|
||||||
"email": "KevinHart@gmail.com",
|
|
||||||
"designationId": "DSG001",
|
|
||||||
"designationName": "Software Engineer",
|
|
||||||
"deptId": "DEP001",
|
|
||||||
"deptName": "IT",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "EMP012",
|
|
||||||
"name": "Laura Croft",
|
|
||||||
"email": "LauraCroft@gmail.com",
|
|
||||||
"designationId": "DSG006",
|
|
||||||
"designationName": "Data Analyst",
|
|
||||||
"deptId": "DEP003",
|
|
||||||
"deptName": "Analytics",
|
|
||||||
"branchId": "BR001",
|
|
||||||
"branchName": "Bengaluru",
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"status": "Active"
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
@ -1,50 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"companyId": "CMP001",
|
|
||||||
"companyName": "CLRI",
|
|
||||||
"policies": [
|
|
||||||
{
|
|
||||||
"id": "POL001",
|
|
||||||
"title": "Casual Leave Policy",
|
|
||||||
"category": "Leave",
|
|
||||||
"description": "Employees are entitled to 12 casual leaves per calendar year. A maximum of 3 casual leaves can be carried forward to the next year. Leaves must be approved by the reporting manager at least 2 days in advance."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "POL002",
|
|
||||||
"title": "Late Arrival Rule",
|
|
||||||
"category": "Attendance",
|
|
||||||
"description": "Employees are expected to check in by 09:30 AM. A grace period of 15 minutes is allowed. More than 3 late arrivals in a month will result in a half-day leave deduction."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "POL003",
|
|
||||||
"title": "Loss of Pay (LOP)",
|
|
||||||
"category": "Leave",
|
|
||||||
"description": "If an employee exhausts all available leave balances and cannot attend work, the days will be marked as LOP. LOP is deducted from the monthly salary on a pro-rata basis."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "POL004",
|
|
||||||
"title": "Work From Home (WFH)",
|
|
||||||
"category": "General",
|
|
||||||
"description": "Employees are allowed to avail WFH for up to 4 days a month, subject to prior approval from their manager. WFH cannot be combined with weekends or holidays."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"companyId": "CMP002",
|
|
||||||
"companyName": "TechNova Solutions",
|
|
||||||
"policies": [
|
|
||||||
{
|
|
||||||
"id": "POL005",
|
|
||||||
"title": "Optional Holidays",
|
|
||||||
"category": "Leave",
|
|
||||||
"description": "Employees can choose up to 2 optional holidays from the list provided by the HR department. These are not encashable and cannot be carried forward."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "POL006",
|
|
||||||
"title": "Shift Timings",
|
|
||||||
"category": "Attendance",
|
|
||||||
"description": "General shift is from 09:00 AM to 06:00 PM. Employees must complete a minimum of 8.5 hours per day, excluding the lunch break."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
// src/api/client.ts
|
||||||
|
|
||||||
// Generic GET request for any module
|
// Generic GET request for any module
|
||||||
export const fetchMockData = async <T>(endpoint: string, delay = 500): Promise<T> => {
|
export const fetchMockData = async <T>(endpoint: string, delay = 500): Promise<T> => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
@ -10,5 +12,5 @@ export const fetchMockData = async <T>(endpoint: string, delay = 500): Promise<T
|
|||||||
export const postMockData = async <T>(endpoint: string, payload: T, delay = 800): Promise<T> => {
|
export const postMockData = async <T>(endpoint: string, payload: T, delay = 800): Promise<T> => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
console.log(`[Mock POST] /mockApi/${endpoint}`, payload);
|
console.log(`[Mock POST] /mockApi/${endpoint}`, payload);
|
||||||
return payload;
|
return payload;
|
||||||
};
|
};
|
||||||
@ -20,8 +20,6 @@ const routeNameMap: Record<string, string> = {
|
|||||||
employees: 'Manage Employees',
|
employees: 'Manage Employees',
|
||||||
'manage-admins': 'Manage Admins',
|
'manage-admins': 'Manage Admins',
|
||||||
policies: 'Policy and Rules',
|
policies: 'Policy and Rules',
|
||||||
applications: 'Leave Applications',
|
|
||||||
ledger: 'Ledger Report'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Breadcrumbs() {
|
export default function Breadcrumbs() {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { Routes, Route, Navigate } from 'react-router-dom';
|
|||||||
import { useIsFetching } from '@tanstack/react-query';
|
import { useIsFetching } from '@tanstack/react-query';
|
||||||
import Sidebar from './Sidebar';
|
import Sidebar from './Sidebar';
|
||||||
import Navbar from './Navbar';
|
import Navbar from './Navbar';
|
||||||
import Breadcrumbs from './Breadcrumbs';
|
import Breadcrumbs from './Breadcrumbs'; // <-- Import Breadcrumbs
|
||||||
import DashboardRouter from '../../features/dashboard/routes/DashboardRouter';
|
import DashboardRouter from '../../features/dashboard/routes/DashboardRouter';
|
||||||
import AttendanceRouter from '../../features/attendance/routes/AttendanceRouter';
|
import AttendanceRouter from '../../features/attendance/routes/AttendanceRouter';
|
||||||
import LeaveRouter from '../../features/leave/routes/LeaveRouter';
|
import LeaveRouter from '../../features/leave/routes/LeaveRouter';
|
||||||
@ -11,7 +11,7 @@ import ApprovalRouter from '../../features/leave/routes/ApprovalRouter';
|
|||||||
import AttendanceSummaryRouter from '../../features/attendance/routes/AttendanceSummaryRouter';
|
import AttendanceSummaryRouter from '../../features/attendance/routes/AttendanceSummaryRouter';
|
||||||
import LeaveSummaryRouter from '../../features/leave/routes/LeaveSummaryRouter';
|
import LeaveSummaryRouter from '../../features/leave/routes/LeaveSummaryRouter';
|
||||||
import MasterDataRouter from '../../features/employee-management/routes/MasterDataRouter';
|
import MasterDataRouter from '../../features/employee-management/routes/MasterDataRouter';
|
||||||
import PolicyRouter from '../../features/employee-management/routes/PolicyRouter';
|
import PolicyRouter from '../../features/dashboard/routes/PolicyRouter';
|
||||||
import ManageAdminsRouter from '../../features/employee-management/routes/ManageAdminsRouter';
|
import ManageAdminsRouter from '../../features/employee-management/routes/ManageAdminsRouter';
|
||||||
|
|
||||||
export default function MainLayout() {
|
export default function MainLayout() {
|
||||||
@ -21,17 +21,18 @@ export default function MainLayout() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-screen bg-app-bg overflow-hidden">
|
<div className="flex h-screen bg-app-bg overflow-hidden">
|
||||||
<Sidebar isCollapsed={isCollapsed} setIsCollapsed={setIsCollapsed} />
|
<Sidebar isCollapsed={isCollapsed} setIsCollapsed={setIsCollapsed} />
|
||||||
|
|
||||||
<div className="flex-1 flex flex-col relative overflow-hidden">
|
<div className="flex-1 flex flex-col relative overflow-hidden">
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
{isFetching > 0 && (
|
{isFetching > 0 && (
|
||||||
<div className="absolute top-16 left-0 right-0 h-1 bg-primary z-50 animate-pulse" />
|
<div className="absolute top-16 left-0 right-0 h-1 bg-primary z-50 animate-pulse" />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<main className="flex-1 overflow-y-auto p-6">
|
<main className="flex-1 overflow-y-auto p-6">
|
||||||
|
{/* Add Breadcrumbs here so it shows on every page */}
|
||||||
<Breadcrumbs />
|
<Breadcrumbs />
|
||||||
|
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/dashboard" element={<DashboardRouter />} />
|
<Route path="/dashboard" element={<DashboardRouter />} />
|
||||||
<Route path="/attendance" element={<AttendanceRouter />} />
|
<Route path="/attendance" element={<AttendanceRouter />} />
|
||||||
|
|||||||
@ -22,13 +22,18 @@ const roleSearchData: Record<UserRole, { name: string; path: string }[]> = {
|
|||||||
{ name: 'Admin Dashboard', path: '/dashboard' },
|
{ name: 'Admin Dashboard', path: '/dashboard' },
|
||||||
{ name: 'All Attendance Logs', path: '/attendance' },
|
{ name: 'All Attendance Logs', path: '/attendance' },
|
||||||
{ name: 'All Leave Requests', path: '/leave' },
|
{ name: 'All Leave Requests', path: '/leave' },
|
||||||
{ name: 'Pending Approvals', path: '/approval' },
|
|
||||||
|
// Attendance Summary
|
||||||
{ name: 'Attendance Summary Dashboard', path: '/attendance-summary/dashboard' },
|
{ name: 'Attendance Summary Dashboard', path: '/attendance-summary/dashboard' },
|
||||||
{ name: 'Monthly Attendance Report', path: '/attendance-summary/monthly' },
|
{ name: 'Monthly Attendance Report', path: '/attendance-summary/monthly' },
|
||||||
{ name: 'Daily Attendance Report', path: '/attendance-summary/daily' },
|
{ name: 'Daily Attendance Report', path: '/attendance-summary/daily' },
|
||||||
{ name: 'Attendance Regularization', path: '/attendance-summary/regularization' },
|
{ name: 'Attendance Regularization', path: '/attendance-summary/regularization' },
|
||||||
|
|
||||||
|
// Leave Summary
|
||||||
{ name: 'Leave Applications', path: '/leave-summary/applications' },
|
{ name: 'Leave Applications', path: '/leave-summary/applications' },
|
||||||
{ name: 'Leave Ledger Report', path: '/leave-summary/ledger' },
|
{ name: 'Leave Ledger Report', path: '/leave-summary/ledger' },
|
||||||
|
|
||||||
|
// Master Data
|
||||||
{ name: 'Master Data Dashboard', path: '/master-data/dashboard' },
|
{ name: 'Master Data Dashboard', path: '/master-data/dashboard' },
|
||||||
{ name: 'Manage Company', path: '/master-data/company' },
|
{ name: 'Manage Company', path: '/master-data/company' },
|
||||||
{ name: 'Manage Branches', path: '/master-data/branches' },
|
{ name: 'Manage Branches', path: '/master-data/branches' },
|
||||||
@ -40,18 +45,25 @@ const roleSearchData: Record<UserRole, { name: string; path: string }[]> = {
|
|||||||
{ name: 'SuperAdmin Dashboard', path: '/dashboard' },
|
{ name: 'SuperAdmin Dashboard', path: '/dashboard' },
|
||||||
{ name: 'Global Attendance Logs', path: '/attendance' },
|
{ name: 'Global Attendance Logs', path: '/attendance' },
|
||||||
{ name: 'Global Leave Requests', path: '/leave' },
|
{ name: 'Global Leave Requests', path: '/leave' },
|
||||||
{ name: 'Pending Approvals', path: '/approval' },
|
|
||||||
|
// Attendance Summary
|
||||||
{ name: 'Attendance Summary Dashboard', path: '/attendance-summary/dashboard' },
|
{ name: 'Attendance Summary Dashboard', path: '/attendance-summary/dashboard' },
|
||||||
{ name: 'Monthly Attendance Report', path: '/attendance-summary/monthly' },
|
{ name: 'Monthly Attendance Report', path: '/attendance-summary/monthly' },
|
||||||
{ name: 'Daily Attendance Report', path: '/attendance-summary/daily' },
|
{ name: 'Daily Attendance Report', path: '/attendance-summary/daily' },
|
||||||
|
|
||||||
|
// Leave Summary
|
||||||
{ name: 'Leave Applications', path: '/leave-summary/applications' },
|
{ name: 'Leave Applications', path: '/leave-summary/applications' },
|
||||||
{ name: 'Leave Ledger Report', path: '/leave-summary/ledger' },
|
{ name: 'Leave Ledger Report', path: '/leave-summary/ledger' },
|
||||||
|
|
||||||
|
// Master Data
|
||||||
{ name: 'Master Data Dashboard', path: '/master-data/dashboard' },
|
{ name: 'Master Data Dashboard', path: '/master-data/dashboard' },
|
||||||
{ name: 'Manage Company', path: '/master-data/company' },
|
{ name: 'Manage Company', path: '/master-data/company' },
|
||||||
{ name: 'Manage Branches', path: '/master-data/branches' },
|
{ name: 'Manage Branches', path: '/master-data/branches' },
|
||||||
{ name: 'Manage Departments', path: '/master-data/departments' },
|
{ name: 'Manage Departments', path: '/master-data/departments' },
|
||||||
{ name: 'Manage Designations', path: '/master-data/designations' },
|
{ name: 'Manage Designations', path: '/master-data/designations' },
|
||||||
{ name: 'Manage Employees', path: '/master-data/employees' },
|
{ name: 'Manage Employees', path: '/master-data/employees' },
|
||||||
|
|
||||||
|
// SuperAdmin Specific
|
||||||
{ name: 'Manage Admins', path: '/manage-admins' },
|
{ name: 'Manage Admins', path: '/manage-admins' },
|
||||||
{ name: 'Policy and Rules', path: '/policies' },
|
{ name: 'Policy and Rules', path: '/policies' },
|
||||||
],
|
],
|
||||||
@ -99,29 +111,29 @@ export default function Navbar() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-app-card shadow-sm h-16 flex items-center justify-between px-4 md:px-6 border-b border-app-border relative z-30">
|
<div className="bg-app-card shadow-sm h-16 flex items-center justify-between px-6 border-b border-app-border relative z-30">
|
||||||
|
|
||||||
<div className="flex items-center shrink-0">
|
<div className="flex items-center">
|
||||||
<h2 className="text-lg font-bold text-primary tracking-wide">CLRI HRMS</h2>
|
<h2 className="text-lg font-bold text-primary tracking-wide">CLRI HRMS</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center space-x-2 md:space-x-4 ml-auto">
|
<div className="flex items-center space-x-4">
|
||||||
|
|
||||||
<div className="relative flex items-center">
|
<div className="relative flex items-center">
|
||||||
{isSearchOpen ? (
|
{isSearchOpen ? (
|
||||||
<div className="flex items-center border border-app-border rounded-lg shadow-sm bg-app-card">
|
<div className="flex items-center border border-app-border rounded-lg shadow-sm bg-app-card">
|
||||||
<Search size={18} className="ml-3 text-text-light shrink-0" />
|
<Search size={18} className="ml-3 text-text-light" />
|
||||||
<input
|
<input
|
||||||
ref={searchInputRef}
|
ref={searchInputRef}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search..."
|
placeholder="Search modules..."
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
className="w-32 sm:w-44 px-3 py-2 text-sm focus:outline-none rounded-lg text-text-primary min-w-0"
|
className="w-56 px-3 py-2 text-sm focus:outline-none rounded-lg text-text-primary"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={() => { setIsSearchOpen(false); setSearchQuery(''); }}
|
onClick={() => { setIsSearchOpen(false); setSearchQuery(''); }}
|
||||||
className="p-2 text-text-light hover:text-text-muted shrink-0"
|
className="p-2 text-text-light hover:text-text-muted"
|
||||||
>
|
>
|
||||||
<X size={18} />
|
<X size={18} />
|
||||||
</button>
|
</button>
|
||||||
@ -129,7 +141,7 @@ export default function Navbar() {
|
|||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsSearchOpen(true)}
|
onClick={() => setIsSearchOpen(true)}
|
||||||
className="p-2 text-text-secondary hover:bg-app-muted rounded-full transition-colors shrink-0"
|
className="p-2 text-text-secondary hover:bg-app-muted rounded-full transition-colors"
|
||||||
>
|
>
|
||||||
<Search size={22} />
|
<Search size={22} />
|
||||||
</button>
|
</button>
|
||||||
@ -160,7 +172,7 @@ export default function Navbar() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative shrink-0">
|
<div className="relative">
|
||||||
<button
|
<button
|
||||||
onClick={() => setDropdownOpen(!dropdownOpen)}
|
onClick={() => setDropdownOpen(!dropdownOpen)}
|
||||||
className="flex items-center space-x-2 p-1 rounded-lg hover:bg-app-muted transition-colors"
|
className="flex items-center space-x-2 p-1 rounded-lg hover:bg-app-muted transition-colors"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import type { RootState } from '../../store/store';
|
import type { RootState } from '../../store/store';
|
||||||
import { getSidebarItems, type NavItem } from './types/sidebarConfig';
|
import { getSidebarItems, type NavItem } from './types/sidebarConfig';
|
||||||
@ -23,13 +23,6 @@ export default function Sidebar({ isCollapsed, setIsCollapsed }: { isCollapsed:
|
|||||||
const currentPath = location.pathname;
|
const currentPath = location.pathname;
|
||||||
|
|
||||||
const [openDropdowns, setOpenDropdowns] = useState<Record<string, boolean>>({});
|
const [openDropdowns, setOpenDropdowns] = useState<Record<string, boolean>>({});
|
||||||
const [pinnedFlyout, setPinnedFlyout] = useState<string | null>(null);
|
|
||||||
|
|
||||||
// Close any pinned flyout when the route changes or the sidebar is expanded
|
|
||||||
useEffect(() => {
|
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
||||||
setPinnedFlyout(null);
|
|
||||||
}, [location.pathname, isCollapsed]);
|
|
||||||
|
|
||||||
const toggleDropdown = (name: string) => {
|
const toggleDropdown = (name: string) => {
|
||||||
setOpenDropdowns(prev => ({ ...prev, [name]: !prev[name] }));
|
setOpenDropdowns(prev => ({ ...prev, [name]: !prev[name] }));
|
||||||
@ -39,92 +32,52 @@ export default function Sidebar({ isCollapsed, setIsCollapsed }: { isCollapsed:
|
|||||||
const Icon = iconMap[item.icon];
|
const Icon = iconMap[item.icon];
|
||||||
const hasChildren = item.children && item.children.length > 0;
|
const hasChildren = item.children && item.children.length > 0;
|
||||||
const isDropdownOpen = openDropdowns[item.name];
|
const isDropdownOpen = openDropdowns[item.name];
|
||||||
const isPinned = pinnedFlyout === item.name;
|
|
||||||
|
|
||||||
const isChildActive = hasChildren && item.children!.some(child => currentPath === child.path);
|
const isChildActive = hasChildren && item.children!.some(child => currentPath === child.path);
|
||||||
const isActive = currentPath === item.path;
|
const isActive = currentPath === item.path;
|
||||||
|
|
||||||
// COLLAPSED STATE: Render Flyout Submenus
|
if (hasChildren && isCollapsed) {
|
||||||
if (isCollapsed) {
|
return item.children!.map(child => renderNavItem(child));
|
||||||
return (
|
|
||||||
<li key={item.name} className="mb-1 relative group">
|
|
||||||
{hasChildren ? (
|
|
||||||
<button
|
|
||||||
// Toggle pin on click
|
|
||||||
onClick={() => setPinnedFlyout(prev => prev === item.name ? null : item.name)}
|
|
||||||
className={`w-full flex items-center justify-center px-4 py-3 transition-colors duration-200 ${isChildActive || isPinned ? 'text-white border-l-4 border-action' : 'text-blue-100 hover:bg-sidebar-hover hover:text-white border-l-4 border-transparent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{Icon && <Icon size={20} className="shrink-0" />}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<Link
|
|
||||||
to={item.path!}
|
|
||||||
className={`w-full flex items-center justify-center px-4 py-3 transition-colors duration-200 ${isActive ? 'bg-sidebar-active text-white border-l-4 border-action' : 'text-blue-100 hover:bg-sidebar-hover hover:text-white border-l-4 border-transparent'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{Icon && <Icon size={20} className="flex-shrink-0" />}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Flyout Submenu or Tooltip on Hover/Pin */}
|
|
||||||
{/* If pinned, it forces 'block'. If not pinned, 'hidden' but 'group-hover:block' shows it on hover */}
|
|
||||||
<div
|
|
||||||
className={`${isPinned ? 'block' : 'hidden'} group-hover:block absolute left-full ml-4 top-0 z-50 shadow-lg`}
|
|
||||||
>
|
|
||||||
{hasChildren ? (
|
|
||||||
<ul className="bg-sidebar min-w-[200px] rounded-lg border border-sidebar-hover py-2">
|
|
||||||
{item.children!.map(child => {
|
|
||||||
const ChildIcon = iconMap[child.icon];
|
|
||||||
return (
|
|
||||||
<li key={child.name}>
|
|
||||||
<Link
|
|
||||||
to={child.path!}
|
|
||||||
className={`flex items-center px-4 py-2 text-sm w-full transition-colors ${currentPath === child.path ? 'bg-sidebar-active text-white' : 'text-blue-100 hover:bg-sidebar-hover hover:text-white'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{ChildIcon && <ChildIcon size={16} className="mr-3" />}
|
|
||||||
{child.name}
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
) : (
|
|
||||||
<div className="bg-sidebar-active text-white text-sm px-3 py-1 rounded whitespace-nowrap pointer-events-none">
|
|
||||||
{item.name}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EXPANDED STATE: Render Normal Dropdowns
|
|
||||||
return (
|
return (
|
||||||
<li key={item.name} className="mb-1 relative group">
|
<li key={item.name} className="mb-1 relative group">
|
||||||
{hasChildren ? (
|
{hasChildren ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => toggleDropdown(item.name)}
|
onClick={() => toggleDropdown(item.name)}
|
||||||
className={`w-full flex items-center px-4 py-3 transition-colors duration-200 text-left ${isChildActive ? 'text-white' : 'text-blue-100 hover:bg-sidebar-hover hover:text-white'
|
className={`w-full flex items-center px-4 py-3 transition-colors duration-200 text-left ${
|
||||||
} border-l-4 ${isChildActive ? 'border-action' : 'border-transparent'}`}
|
isCollapsed ? 'justify-center' : ''
|
||||||
|
} ${
|
||||||
|
isChildActive ? 'text-white' : 'text-blue-100 hover:bg-sidebar-hover hover:text-white'
|
||||||
|
} border-l-4 ${isChildActive ? 'border-action' : 'border-transparent'}`}
|
||||||
>
|
>
|
||||||
{Icon && <Icon size={20} className="flex-shrink-0" />}
|
{Icon && <Icon size={20} className="flex-shrink-0" />}
|
||||||
<span className="ml-4 text-sm font-medium flex-grow">{item.name}</span>
|
{!isCollapsed && <span className="ml-4 text-sm font-medium flex-grow">{item.name}</span>}
|
||||||
<ChevronDown size={16} className={`transition-transform ${isDropdownOpen ? 'rotate-180' : ''}`} />
|
{!isCollapsed && (
|
||||||
|
<ChevronDown size={16} className={`transition-transform ${isDropdownOpen ? 'rotate-180' : ''}`} />
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<Link
|
<Link
|
||||||
to={item.path!}
|
to={item.path!}
|
||||||
className={`flex items-center px-4 py-3 transition-colors duration-200 text-left ${isActive ? 'bg-sidebar-active text-white border-l-4 border-action' : 'text-blue-100 hover:bg-sidebar-hover hover:text-white border-l-4 border-transparent'
|
className={`flex items-center px-4 py-3 transition-colors duration-200 text-left ${
|
||||||
}`}
|
isCollapsed ? 'justify-center' : ''
|
||||||
|
} ${
|
||||||
|
isActive ? 'bg-sidebar-active text-white border-l-4 border-action' : 'text-blue-100 hover:bg-sidebar-hover hover:text-white border-l-4 border-transparent'
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{Icon && <Icon size={20} className="flex-shrink-0" />}
|
{Icon && <Icon size={20} className="flex-shrink-0" />}
|
||||||
<span className="ml-4 text-sm font-medium">{item.name}</span>
|
{!isCollapsed && <span className="ml-4 text-sm font-medium">{item.name}</span>}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isDropdownOpen && hasChildren && (
|
{isCollapsed && (
|
||||||
|
<div className="absolute left-full ml-4 top-1/2 -translate-y-1/2 z-50 bg-sidebar-active text-white text-sm px-3 py-1 rounded shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none whitespace-nowrap">
|
||||||
|
{item.name}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isDropdownOpen && !isCollapsed && (
|
||||||
<ul className="mt-1 ml-4 border-l border-blue-800/50 pl-2">
|
<ul className="mt-1 ml-4 border-l border-blue-800/50 pl-2">
|
||||||
{item.children!.map(child => renderNavItem(child))}
|
{item.children!.map(child => renderNavItem(child))}
|
||||||
</ul>
|
</ul>
|
||||||
@ -138,7 +91,8 @@ export default function Sidebar({ isCollapsed, setIsCollapsed }: { isCollapsed:
|
|||||||
<div className={`flex items-center h-16 border-b border-primary/30 px-4 flex-shrink-0 ${isCollapsed ? 'justify-center' : 'justify-between'}`}>
|
<div className={`flex items-center h-16 border-b border-primary/30 px-4 flex-shrink-0 ${isCollapsed ? 'justify-center' : 'justify-between'}`}>
|
||||||
{!isCollapsed && (
|
{!isCollapsed && (
|
||||||
<Link to="/dashboard" className="flex items-center">
|
<Link to="/dashboard" className="flex items-center">
|
||||||
<img src="logo.gif" alt="Company Logo" className="h-13.5 w-71 cursor-pointer" />
|
{/* Fixed Tailwind sizing for GIF alignment */}
|
||||||
|
<img src="logo.gif" alt="Company Logo" className="h-25 w-25 object-contain cursor-pointer" />
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<button onClick={() => setIsCollapsed(!isCollapsed)} className="p-2 rounded hover:bg-sidebar-hover transition-colors">
|
<button onClick={() => setIsCollapsed(!isCollapsed)} className="p-2 rounded hover:bg-sidebar-hover transition-colors">
|
||||||
@ -146,12 +100,7 @@ export default function Sidebar({ isCollapsed, setIsCollapsed }: { isCollapsed:
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/*
|
<nav className="flex-1 mt-4 overflow-x-visible">
|
||||||
Conditional Overflow Logic:
|
|
||||||
- Collapsed: 'overflow-x-visible' allows flyout menus to appear outside the sidebar bounds.
|
|
||||||
- Expanded: 'overflow-y-auto overflow-x-hidden' enables vertical scrolling to see all dropdowns.
|
|
||||||
*/}
|
|
||||||
<nav className={`flex-1 mt-4 ${isCollapsed ? 'overflow-x-visible' : 'overflow-y-auto overflow-x-hidden'}`}>
|
|
||||||
<ul>
|
<ul>
|
||||||
{navItems.map(item => renderNavItem(item))}
|
{navItems.map(item => renderNavItem(item))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@ -19,8 +19,8 @@ export const getSidebarItems = (role: UserRole): NavItem[] => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const adminItems: NavItem[] = [
|
const adminItems: NavItem[] = [
|
||||||
{
|
{
|
||||||
name: 'Attendance Summary',
|
name: 'Attendance Summary',
|
||||||
icon: 'BarChart3',
|
icon: 'BarChart3',
|
||||||
children: [
|
children: [
|
||||||
{ name: 'Dashboard', icon: 'LayoutDashboard', path: '/attendance-summary/dashboard' },
|
{ name: 'Dashboard', icon: 'LayoutDashboard', path: '/attendance-summary/dashboard' },
|
||||||
@ -29,16 +29,16 @@ export const getSidebarItems = (role: UserRole): NavItem[] => {
|
|||||||
...(role === 'admin' ? [{ name: 'Regularization', icon: 'ClipboardCheck', path: '/attendance-summary/regularization' }] : [])
|
...(role === 'admin' ? [{ name: 'Regularization', icon: 'ClipboardCheck', path: '/attendance-summary/regularization' }] : [])
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Leave Summary',
|
name: 'Leave Summary',
|
||||||
icon: 'ClipboardList',
|
icon: 'ClipboardList',
|
||||||
children: [
|
children: [
|
||||||
{ name: 'Leave Applications', icon: 'FileText', path: '/leave-summary/applications' },
|
{ name: 'Leave Applications', icon: 'FileText', path: '/leave-summary/applications' },
|
||||||
{ name: 'Ledger Report', icon: 'Receipt', path: '/leave-summary/ledger' },
|
{ name: 'Ledger Report', icon: 'Receipt', path: '/leave-summary/ledger' },
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Master Data',
|
name: 'Master Data',
|
||||||
icon: 'Database',
|
icon: 'Database',
|
||||||
children: [
|
children: [
|
||||||
{ name: 'Dashboard', icon: 'LayoutDashboard', path: '/master-data/dashboard' },
|
{ name: 'Dashboard', icon: 'LayoutDashboard', path: '/master-data/dashboard' },
|
||||||
@ -52,13 +52,12 @@ export const getSidebarItems = (role: UserRole): NavItem[] => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (role === 'admin') {
|
if (role === 'admin') {
|
||||||
return [...baseItems, { name: 'Approval', icon: 'CheckCircle', path: '/approval' }, ...adminItems];
|
return [...baseItems, ...adminItems];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role === 'superadmin') {
|
if (role === 'superadmin') {
|
||||||
return [
|
return [
|
||||||
...baseItems,
|
...baseItems,
|
||||||
{ name: 'Approval', icon: 'CheckCircle', path: '/approval' },
|
|
||||||
...adminItems,
|
...adminItems,
|
||||||
{ name: 'Manage Admins', icon: 'ShieldUser', path: '/manage-admins' },
|
{ name: 'Manage Admins', icon: 'ShieldUser', path: '/manage-admins' },
|
||||||
{ name: 'Policy and Rules', icon: 'ScrollText', path: '/policies' },
|
{ name: 'Policy and Rules', icon: 'ScrollText', path: '/policies' },
|
||||||
|
|||||||
@ -14,23 +14,23 @@ export default function ConfirmationModal({ title, message, onConfirm, onClose }
|
|||||||
<button onClick={onClose} className="absolute top-4 right-4 text-text-muted hover:text-text-primary">
|
<button onClick={onClose} className="absolute top-4 right-4 text-text-muted hover:text-text-primary">
|
||||||
<X size={24} />
|
<X size={24} />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-absent-100 mb-4">
|
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-absent-100 mb-4">
|
||||||
<AlertTriangle className="h-6 w-6 text-absent-500" />
|
<AlertTriangle className="h-6 w-6 text-absent-500" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 className="text-lg font-medium text-text-primary mb-2">{title}</h3>
|
<h3 className="text-lg font-medium text-text-primary mb-2">{title}</h3>
|
||||||
<p className="text-sm text-text-muted mb-6">{message}</p>
|
<p className="text-sm text-text-muted mb-6">{message}</p>
|
||||||
|
|
||||||
<div className="flex justify-center space-x-3">
|
<div className="flex justify-center space-x-3">
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="px-4 py-2 bg-app-muted text-text-primary rounded-lg hover:bg-app-border transition-colors text-sm font-medium"
|
className="px-4 py-2 bg-app-muted text-text-primary rounded-lg hover:bg-app-border transition-colors text-sm font-medium"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={onConfirm}
|
onClick={onConfirm}
|
||||||
className="px-4 py-2 bg-absent-500 text-white rounded-lg hover:bg-absent-600 transition-colors text-sm font-medium"
|
className="px-4 py-2 bg-absent-500 text-white rounded-lg hover:bg-absent-600 transition-colors text-sm font-medium"
|
||||||
>
|
>
|
||||||
Yes, Delete
|
Yes, Delete
|
||||||
|
|||||||
@ -10,8 +10,10 @@ export default function ErrorState({
|
|||||||
fullScreen = false
|
fullScreen = false
|
||||||
}: ErrorStateProps) {
|
}: ErrorStateProps) {
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-col items-center justify-center bg-app-card rounded-xl shadow-md border border-app-border p-8 text-absent-600 ${fullScreen ? 'h-screen w-screen fixed top-0 left-0 z-50' : 'h-64 w-full'
|
<div className={`flex flex-col items-center justify-center bg-app-card rounded-xl shadow-md border border-app-border p-8 text-absent-600 ${
|
||||||
}`}>
|
fullScreen ? 'h-screen w-screen fixed top-0 left-0 z-50' : 'h-64 w-full'
|
||||||
|
}`}>
|
||||||
|
{/* Icon wrapped in a circular background to look like a card illustration */}
|
||||||
<div className="p-3 bg-absent-100 rounded-full mb-4">
|
<div className="p-3 bg-absent-100 rounded-full mb-4">
|
||||||
<AlertCircle size={32} className="text-absent-500" />
|
<AlertCircle size={32} className="text-absent-500" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,18 +8,19 @@ interface LoaderProps {
|
|||||||
|
|
||||||
export default function Loader({
|
export default function Loader({
|
||||||
message = 'Loading...',
|
message = 'Loading...',
|
||||||
color = '#0A66C2',
|
color = '#0A66C2', // Default to your primary brand color
|
||||||
fullScreen = false
|
fullScreen = false
|
||||||
}: LoaderProps) {
|
}: LoaderProps) {
|
||||||
return (
|
return (
|
||||||
<div className={`flex flex-col items-center justify-center bg-app-card rounded-xl shadow-md border border-app-border p-8 ${fullScreen ? 'h-screen w-screen fixed top-0 left-0 z-50' : 'h-64 w-full'
|
<div className={`flex flex-col items-center justify-center bg-app-card rounded-xl shadow-md border border-app-border p-8 ${
|
||||||
}`}>
|
fullScreen ? 'h-screen w-screen fixed top-0 left-0 z-50' : 'h-64 w-full'
|
||||||
<FadeLoader
|
}`}>
|
||||||
color={color}
|
<FadeLoader
|
||||||
height={15}
|
color={color}
|
||||||
width={5}
|
height={15}
|
||||||
radius={2}
|
width={5}
|
||||||
margin={2}
|
radius={2}
|
||||||
|
margin={2}
|
||||||
/>
|
/>
|
||||||
{message && <span className="mt-6 text-sm font-medium text-text-muted">{message}</span>}
|
{message && <span className="mt-6 text-sm font-medium text-text-muted">{message}</span>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -5,13 +5,19 @@ import type { RootState } from '../../../store/store';
|
|||||||
import type { UserRole } from '../../../store/roleSlice';
|
import type { UserRole } from '../../../store/roleSlice';
|
||||||
import type { AttendanceRecord } from '../types/attendance';
|
import type { AttendanceRecord } from '../types/attendance';
|
||||||
|
|
||||||
|
// 1. Define endpoints per role (Easy to swap to real API URLs later)
|
||||||
const ATTENDANCE_ENDPOINTS: Record<UserRole, string> = {
|
const ATTENDANCE_ENDPOINTS: Record<UserRole, string> = {
|
||||||
employee: 'attendance/employee_attendance.json',
|
employee: 'attendance/employee_attendance.json',
|
||||||
manager: 'attendance/manager_attendance.json',
|
manager: 'attendance/manager_attendance.json',
|
||||||
admin: 'attendance/admin_attendance.json',
|
admin: 'attendance/admin_attendance.json',
|
||||||
superadmin: 'attendance/superadmin_attendance.json',
|
superadmin: 'attendance/superadmin_attendance.json',
|
||||||
|
|
||||||
|
// Future real backend example:
|
||||||
|
// employee: '/api/v1/attendance/me',
|
||||||
|
// manager: '/api/v1/attendance/team',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 2. Create a single dynamic hook
|
||||||
export const useAttendanceData = () => {
|
export const useAttendanceData = () => {
|
||||||
const role = useSelector((state: RootState) => state.role.currentRole);
|
const role = useSelector((state: RootState) => state.role.currentRole);
|
||||||
|
|
||||||
@ -28,7 +34,7 @@ export interface SummaryRecord {
|
|||||||
department: string;
|
department: string;
|
||||||
company: string;
|
company: string;
|
||||||
isCheckedIn: boolean;
|
isCheckedIn: boolean;
|
||||||
date: string;
|
date: string; // Added date field
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useAttendanceSummary = () => {
|
export const useAttendanceSummary = () => {
|
||||||
|
|||||||
@ -11,6 +11,8 @@ interface AttendanceRecordsTableProps {
|
|||||||
initialToDate?: string;
|
initialToDate?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Updated to handle empty strings safely
|
||||||
const getDaysDifference = (start: string, end: string) => {
|
const getDaysDifference = (start: string, end: string) => {
|
||||||
if (!start || !end) return 0;
|
if (!start || !end) return 0;
|
||||||
const startDate = new Date(start + 'T00:00:00');
|
const startDate = new Date(start + 'T00:00:00');
|
||||||
|
|||||||
@ -80,7 +80,7 @@ export default function DailySummary({ selectedDate, data }: DailySummaryProps)
|
|||||||
{isMispunch && (
|
{isMispunch && (
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
{isApplied ? (
|
{isApplied ? (
|
||||||
<button
|
<button
|
||||||
disabled
|
disabled
|
||||||
className="w-full flex items-center justify-center px-4 py-2 bg-app-muted text-text-light rounded-lg cursor-not-allowed text-sm font-medium"
|
className="w-full flex items-center justify-center px-4 py-2 bg-app-muted text-text-light rounded-lg cursor-not-allowed text-sm font-medium"
|
||||||
>
|
>
|
||||||
@ -88,7 +88,7 @@ export default function DailySummary({ selectedDate, data }: DailySummaryProps)
|
|||||||
Applied for Regularization
|
Applied for Regularization
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowModal(true)}
|
onClick={() => setShowModal(true)}
|
||||||
className="w-full flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors text-sm font-medium"
|
className="w-full flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors text-sm font-medium"
|
||||||
>
|
>
|
||||||
@ -99,8 +99,9 @@ export default function DailySummary({ selectedDate, data }: DailySummaryProps)
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Render Separated Modal */}
|
||||||
{showModal && (
|
{showModal && (
|
||||||
<RegularizationModal
|
<RegularizationModal
|
||||||
selectedDate={selectedDate}
|
selectedDate={selectedDate}
|
||||||
onClose={() => setShowModal(false)}
|
onClose={() => setShowModal(false)}
|
||||||
onApply={handleApplyRegularization}
|
onApply={handleApplyRegularization}
|
||||||
|
|||||||
@ -34,36 +34,36 @@ export default function RegularizationModal({ selectedDate, onClose, onApply }:
|
|||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Check-In Time</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Check-In Time</label>
|
||||||
<input
|
<input
|
||||||
type="time"
|
type="time"
|
||||||
name="checkIn"
|
name="checkIn"
|
||||||
value={regData.checkIn}
|
value={regData.checkIn}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Check-Out Time</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Check-Out Time</label>
|
||||||
<input
|
<input
|
||||||
type="time"
|
type="time"
|
||||||
name="checkOut"
|
name="checkOut"
|
||||||
value={regData.checkOut}
|
value={regData.checkOut}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Reason</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Reason</label>
|
||||||
<textarea
|
<textarea
|
||||||
name="reason"
|
name="reason"
|
||||||
value={regData.reason}
|
value={regData.reason}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
rows={3}
|
rows={3}
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card resize-none"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card resize-none"
|
||||||
placeholder="Brief reason for mispunch..."
|
placeholder="Brief reason for mispunch..."
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ interface AttendanceChartsProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function AttendanceCharts({ dailyData, trendData }: AttendanceChartsProps) {
|
export default function AttendanceCharts({ dailyData, trendData }: AttendanceChartsProps) {
|
||||||
// Pie Chart Data
|
// Memoize Pie Chart Data
|
||||||
const { pieData, checkedInCount, notCheckedInCount } = useMemo(() => {
|
const { pieData, checkedInCount, notCheckedInCount } = useMemo(() => {
|
||||||
const checkedIn = dailyData.filter(r => r.isCheckedIn).length;
|
const checkedIn = dailyData.filter(r => r.isCheckedIn).length;
|
||||||
const notCheckedIn = dailyData.filter(r => !r.isCheckedIn).length;
|
const notCheckedIn = dailyData.filter(r => !r.isCheckedIn).length;
|
||||||
@ -23,7 +23,7 @@ export default function AttendanceCharts({ dailyData, trendData }: AttendanceCha
|
|||||||
};
|
};
|
||||||
}, [dailyData]);
|
}, [dailyData]);
|
||||||
|
|
||||||
// Line Chart Data
|
// Memoize Line Chart Data
|
||||||
const { xAxisData, lineCheckedInData, lineNotCheckedInData } = useMemo(() => {
|
const { xAxisData, lineCheckedInData, lineNotCheckedInData } = useMemo(() => {
|
||||||
const trendMap = trendData.reduce((acc, record) => {
|
const trendMap = trendData.reduce((acc, record) => {
|
||||||
const day = record.date.split('-')[2]; // Extract "01" from "2024-07-01"
|
const day = record.date.split('-')[2]; // Extract "01" from "2024-07-01"
|
||||||
|
|||||||
@ -25,12 +25,14 @@ export default function RegularizationModal({ record, onClose, onAction }: Regul
|
|||||||
</button>
|
</button>
|
||||||
<h3 className="text-xl font-bold text-text-primary mb-1">Regularization Request</h3>
|
<h3 className="text-xl font-bold text-text-primary mb-1">Regularization Request</h3>
|
||||||
<p className="text-sm text-text-muted mb-6">Request ID: {record.id}</p>
|
<p className="text-sm text-text-muted mb-6">Request ID: {record.id}</p>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
{/* Separated Employee ID */}
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
<div className="flex justify-between border-b border-app-border pb-2">
|
||||||
<span className="text-sm text-text-muted">Employee ID</span>
|
<span className="text-sm text-text-muted">Employee ID</span>
|
||||||
<span className="text-sm font-medium text-text-primary">{record.empId}</span>
|
<span className="text-sm font-medium text-text-primary">{record.empId}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Separated Employee Name */}
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
<div className="flex justify-between border-b border-app-border pb-2">
|
||||||
<span className="text-sm text-text-muted">Employee Name</span>
|
<span className="text-sm text-text-muted">Employee Name</span>
|
||||||
<span className="text-sm font-medium text-text-primary">{record.empName}</span>
|
<span className="text-sm font-medium text-text-primary">{record.empName}</span>
|
||||||
@ -60,14 +62,14 @@ export default function RegularizationModal({ record, onClose, onAction }: Regul
|
|||||||
<div className="flex justify-end space-x-3 mt-6">
|
<div className="flex justify-end space-x-3 mt-6">
|
||||||
{record.status === 'Pending' ? (
|
{record.status === 'Pending' ? (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => onAction(record.id, 'Rejected')}
|
onClick={() => onAction(record.id, 'Rejected')}
|
||||||
className="flex items-center px-4 py-2 bg-absent-500 text-white rounded-lg hover:bg-absent-600 transition-colors text-sm font-medium"
|
className="flex items-center px-4 py-2 bg-absent-500 text-white rounded-lg hover:bg-absent-600 transition-colors text-sm font-medium"
|
||||||
>
|
>
|
||||||
<Ban size={16} className="mr-2" /> Reject
|
<Ban size={16} className="mr-2" /> Reject
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => onAction(record.id, 'Approved')}
|
onClick={() => onAction(record.id, 'Approved')}
|
||||||
className="flex items-center px-4 py-2 bg-present-600 text-white rounded-lg hover:bg-present-700 transition-colors text-sm font-medium"
|
className="flex items-center px-4 py-2 bg-present-600 text-white rounded-lg hover:bg-present-700 transition-colors text-sm font-medium"
|
||||||
>
|
>
|
||||||
<Check size={16} className="mr-2" /> Approve
|
<Check size={16} className="mr-2" /> Approve
|
||||||
|
|||||||
@ -24,9 +24,9 @@ export default function RegularizationTable({ records, onAction }: Regularizatio
|
|||||||
const [viewRecord, setViewRecord] = useState<RegularizationRecord | null>(null);
|
const [viewRecord, setViewRecord] = useState<RegularizationRecord | null>(null);
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(10); // Default 10 entries per page
|
||||||
|
|
||||||
// Default sort: date descending
|
// Default sort: date descending (latest first)
|
||||||
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
||||||
key: 'date',
|
key: 'date',
|
||||||
direction: 'descending'
|
direction: 'descending'
|
||||||
@ -35,7 +35,7 @@ export default function RegularizationTable({ records, onAction }: Regularizatio
|
|||||||
// Filter data based on search query
|
// Filter data based on search query
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
if (!searchQuery) return records;
|
if (!searchQuery) return records;
|
||||||
return records.filter(record =>
|
return records.filter(record =>
|
||||||
record.empId.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.empId.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.empName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.empName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.status.toLowerCase().includes(searchQuery.toLowerCase())
|
record.status.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
@ -113,9 +113,9 @@ export default function RegularizationTable({ records, onAction }: Regularizatio
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{(['empId', 'empName', 'date', 'reqCheckIn', 'reqCheckOut', 'reason', 'status'] as SortKey[]).map((key) => (
|
{(['empId', 'empName', 'date', 'reqCheckIn', 'reqCheckOut', 'reason', 'status'] as SortKey[]).map((key) => (
|
||||||
<th
|
<th
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => requestSort(key)}
|
onClick={() => requestSort(key)}
|
||||||
className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors"
|
className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors"
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
@ -181,13 +181,13 @@ export default function RegularizationTable({ records, onAction }: Regularizatio
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{viewRecord && (
|
{viewRecord && (
|
||||||
<RegularizationModal
|
<RegularizationModal
|
||||||
record={viewRecord}
|
record={viewRecord}
|
||||||
onClose={() => setViewRecord(null)}
|
onClose={() => setViewRecord(null)}
|
||||||
onAction={(id, status) => {
|
onAction={(id, status) => {
|
||||||
onAction(id, status);
|
onAction(id, status);
|
||||||
setViewRecord(null);
|
setViewRecord(null);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -28,6 +28,7 @@ export default function EmployeeAttendance() {
|
|||||||
return map;
|
return map;
|
||||||
}, [attendanceRecords]);
|
}, [attendanceRecords]);
|
||||||
|
|
||||||
|
// Use the global Loader component here
|
||||||
if (isLoading) return <Loader message="Loading attendance data..." />;
|
if (isLoading) return <Loader message="Loading attendance data..." />;
|
||||||
if (isError) return <ErrorState message="Failed to load attendance data." />;
|
if (isError) return <ErrorState message="Failed to load attendance data." />;
|
||||||
|
|
||||||
|
|||||||
@ -7,10 +7,10 @@ import { useAdminRegularizations, type RegularizationRecord } from '../../api/us
|
|||||||
|
|
||||||
export default function AdminRegularization() {
|
export default function AdminRegularization() {
|
||||||
const { data: records, isLoading, isError } = useAdminRegularizations();
|
const { data: records, isLoading, isError } = useAdminRegularizations();
|
||||||
|
|
||||||
// Local state to handle instant UI updates for approvals/rejections
|
// Local state to handle instant UI updates for approvals/rejections
|
||||||
const [localOverrides, setLocalOverrides] = useState<Record<string, RegularizationRecord>>({});
|
const [localOverrides, setLocalOverrides] = useState<Record<string, RegularizationRecord>>({});
|
||||||
|
|
||||||
const combinedRecords = useMemo(() => {
|
const combinedRecords = useMemo(() => {
|
||||||
if (!records) return [];
|
if (!records) return [];
|
||||||
return records.map(r => localOverrides[r.id] || r);
|
return records.map(r => localOverrides[r.id] || r);
|
||||||
@ -24,7 +24,7 @@ export default function AdminRegularization() {
|
|||||||
...prev,
|
...prev,
|
||||||
[id]: { ...targetRecord, status }
|
[id]: { ...targetRecord, status }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
toast.success(`Regularization request ${status.toLowerCase()} successfully.`);
|
toast.success(`Regularization request ${status.toLowerCase()} successfully.`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export default function AttendanceDashboard() {
|
|||||||
// Filter for the entire selected month (Line Chart)
|
// Filter for the entire selected month (Line Chart)
|
||||||
const monthlyTrendData = useMemo(() => {
|
const monthlyTrendData = useMemo(() => {
|
||||||
if (!records) return [];
|
if (!records) return [];
|
||||||
const selectedMonth = date.substring(0, 7);
|
const selectedMonth = date.substring(0, 7); // Extracts "YYYY-MM"
|
||||||
return records.filter(r =>
|
return records.filter(r =>
|
||||||
r.date.startsWith(selectedMonth) &&
|
r.date.startsWith(selectedMonth) &&
|
||||||
(branch === 'All' || r.branch === branch) &&
|
(branch === 'All' || r.branch === branch) &&
|
||||||
@ -103,6 +103,7 @@ export default function AttendanceDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Reusable Stat Cards */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
<StatCard title="Total Employees" value={stats.total} Icon={Users} colorClass="text-primary" bgClass="bg-primary/10" />
|
<StatCard title="Total Employees" value={stats.total} Icon={Users} colorClass="text-primary" bgClass="bg-primary/10" />
|
||||||
<StatCard title="Total Checked In" value={stats.checkedIn} Icon={UserCheck} colorClass="text-present-700" bgClass="bg-present-100" />
|
<StatCard title="Total Checked In" value={stats.checkedIn} Icon={UserCheck} colorClass="text-present-700" bgClass="bg-present-100" />
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
import { useState, useMemo } from 'react';
|
import { useState, useMemo } from 'react';
|
||||||
import { Download, FileText } from 'lucide-react';
|
import { Download, FileText } from 'lucide-react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@ -14,6 +16,7 @@ const yesterday = new Date();
|
|||||||
yesterday.setDate(yesterday.getDate() - 1);
|
yesterday.setDate(yesterday.getDate() - 1);
|
||||||
const yesterdayStr = `${yesterday.getFullYear()}-${String(yesterday.getMonth() + 1).padStart(2, '0')}-${String(yesterday.getDate()).padStart(2, '0')}`;
|
const yesterdayStr = `${yesterday.getFullYear()}-${String(yesterday.getMonth() + 1).padStart(2, '0')}-${String(yesterday.getDate()).padStart(2, '0')}`;
|
||||||
|
|
||||||
|
// Dummy Data - Dynamically using today's and yesterday's date
|
||||||
const dummyDailyData = [
|
const dummyDailyData = [
|
||||||
{ id: 'EMP001', name: 'Alice', company: 'CLRI', branch: 'Bengaluru', department: 'IT', designation: 'Software Engineer', date: todayStr, checkIn: '09:05 AM', checkOut: '06:15 PM', status: 'Present' },
|
{ id: 'EMP001', name: 'Alice', company: 'CLRI', branch: 'Bengaluru', department: 'IT', designation: 'Software Engineer', date: todayStr, checkIn: '09:05 AM', checkOut: '06:15 PM', status: 'Present' },
|
||||||
{ id: 'EMP002', name: 'Bob', company: 'CLRI', branch: 'Pune', department: 'Training', designation: 'Trainer', date: todayStr, checkIn: '--', checkOut: '--', status: 'Absent' },
|
{ id: 'EMP002', name: 'Bob', company: 'CLRI', branch: 'Pune', department: 'Training', designation: 'Trainer', date: todayStr, checkIn: '--', checkOut: '--', status: 'Absent' },
|
||||||
@ -24,6 +27,7 @@ const dummyDailyData = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function DailyReport() {
|
export default function DailyReport() {
|
||||||
|
// Manage all filter state in one object
|
||||||
const [filters, setFilters] = useState<DailyReportFilterValues>({
|
const [filters, setFilters] = useState<DailyReportFilterValues>({
|
||||||
date: getTodayDate(),
|
date: getTodayDate(),
|
||||||
company: 'All',
|
company: 'All',
|
||||||
@ -34,6 +38,7 @@ export default function DailyReport() {
|
|||||||
employeeId: '',
|
employeeId: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Client-side filtering logic
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
return dummyDailyData.filter(record => {
|
return dummyDailyData.filter(record => {
|
||||||
const matchesDate = record.date === filters.date;
|
const matchesDate = record.date === filters.date;
|
||||||
@ -64,6 +69,8 @@ export default function DailyReport() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|
||||||
|
{/* Header & Download Button */}
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm flex flex-col md:flex-row md:items-center justify-between gap-4">
|
<div className="bg-app-card p-6 rounded-lg shadow-sm flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<div className="p-3 bg-primary/10 rounded-full">
|
<div className="p-3 bg-primary/10 rounded-full">
|
||||||
@ -83,6 +90,7 @@ export default function DailyReport() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Reusable Filters Component */}
|
||||||
<DailyReportFilters filters={filters} onFilterChange={setFilters} />
|
<DailyReportFilters filters={filters} onFilterChange={setFilters} />
|
||||||
|
|
||||||
{/* Data Table */}
|
{/* Data Table */}
|
||||||
@ -93,7 +101,7 @@ export default function DailyReport() {
|
|||||||
<table className="min-w-full divide-y divide-app-border">
|
<table className="min-w-full divide-y divide-app-border">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Emp CODE</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Emp ID</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Name</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Name</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Company</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Company</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Branch</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Branch</th>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { Download, FileText } from 'lucide-react';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import ReportFilters, { type ReportFilterValues } from '../../components/summary/MonthlyReportFilters';
|
import ReportFilters, { type ReportFilterValues } from '../../components/summary/MonthlyReportFilters';
|
||||||
|
|
||||||
|
// 1. Dummy Data for testing
|
||||||
const dummyReportData = [
|
const dummyReportData = [
|
||||||
{ id: 'EMP001', name: 'Alice', company: 'CLRI', branch: 'Bengaluru', department: 'IT', designation: 'Software Engineer', date: '2024-07-01', status: 'Present' },
|
{ id: 'EMP001', name: 'Alice', company: 'CLRI', branch: 'Bengaluru', department: 'IT', designation: 'Software Engineer', date: '2024-07-01', status: 'Present' },
|
||||||
{ id: 'EMP002', name: 'Bob', company: 'CLRI', branch: 'Pune', department: 'Training', designation: 'Trainer', date: '2024-07-01', status: 'Absent' },
|
{ id: 'EMP002', name: 'Bob', company: 'CLRI', branch: 'Pune', department: 'Training', designation: 'Trainer', date: '2024-07-01', status: 'Absent' },
|
||||||
@ -12,6 +13,7 @@ const dummyReportData = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function MonthlyReport() {
|
export default function MonthlyReport() {
|
||||||
|
// 2. Default Filters State
|
||||||
const [filters, setFilters] = useState<ReportFilterValues>({
|
const [filters, setFilters] = useState<ReportFilterValues>({
|
||||||
company: 'All',
|
company: 'All',
|
||||||
branch: 'All',
|
branch: 'All',
|
||||||
@ -23,6 +25,7 @@ export default function MonthlyReport() {
|
|||||||
toDate: '',
|
toDate: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 3. Client-side filtering logic (for current dummy data testing)
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
return dummyReportData.filter(record => {
|
return dummyReportData.filter(record => {
|
||||||
const matchesName = filters.employeeName ? record.name.toLowerCase().includes(filters.employeeName.toLowerCase()) : true;
|
const matchesName = filters.employeeName ? record.name.toLowerCase().includes(filters.employeeName.toLowerCase()) : true;
|
||||||
@ -31,7 +34,7 @@ export default function MonthlyReport() {
|
|||||||
const matchesBranch = filters.branch === 'All' || record.branch === filters.branch;
|
const matchesBranch = filters.branch === 'All' || record.branch === filters.branch;
|
||||||
const matchesDept = filters.department === 'All' || record.department === filters.department;
|
const matchesDept = filters.department === 'All' || record.department === filters.department;
|
||||||
const matchesDesg = filters.designation === 'All' || record.designation === filters.designation;
|
const matchesDesg = filters.designation === 'All' || record.designation === filters.designation;
|
||||||
|
|
||||||
const recordDate = new Date(record.date);
|
const recordDate = new Date(record.date);
|
||||||
const matchesFrom = filters.fromDate ? recordDate >= new Date(filters.fromDate) : true;
|
const matchesFrom = filters.fromDate ? recordDate >= new Date(filters.fromDate) : true;
|
||||||
const matchesTo = filters.toDate ? recordDate <= new Date(filters.toDate) : true;
|
const matchesTo = filters.toDate ? recordDate <= new Date(filters.toDate) : true;
|
||||||
@ -41,11 +44,14 @@ export default function MonthlyReport() {
|
|||||||
}, [filters]);
|
}, [filters]);
|
||||||
|
|
||||||
const handleDownload = () => {
|
const handleDownload = () => {
|
||||||
|
// In the future, this will trigger an API call to get an Excel/CSV file
|
||||||
toast.success("Preparing report for download...");
|
toast.success("Preparing report for download...");
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|
||||||
|
{/* Header & Download Button */}
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm flex flex-col md:flex-row md:items-center justify-between gap-4">
|
<div className="bg-app-card p-6 rounded-lg shadow-sm flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<div className="p-3 bg-primary/10 rounded-full">
|
<div className="p-3 bg-primary/10 rounded-full">
|
||||||
@ -56,7 +62,7 @@ export default function MonthlyReport() {
|
|||||||
<p className="text-sm text-text-muted">Filter and download detailed attendance reports.</p>
|
<p className="text-sm text-text-muted">Filter and download detailed attendance reports.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleDownload}
|
onClick={handleDownload}
|
||||||
className="flex items-center justify-center px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary-hover transition-colors font-medium"
|
className="flex items-center justify-center px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary-hover transition-colors font-medium"
|
||||||
>
|
>
|
||||||
@ -64,14 +70,17 @@ export default function MonthlyReport() {
|
|||||||
Download Report
|
Download Report
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Reusable Filters Component */}
|
||||||
<ReportFilters filters={filters} onFilterChange={setFilters} />
|
<ReportFilters filters={filters} onFilterChange={setFilters} />
|
||||||
|
|
||||||
|
{/* Dummy Data Table (Proves filters are working) */}
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm overflow-x-auto">
|
<div className="bg-app-card p-6 rounded-lg shadow-sm overflow-x-auto">
|
||||||
<h3 className="text-lg font-semibold text-text-primary mb-4">Filtered Results ({filteredData.length})</h3>
|
<h3 className="text-lg font-semibold text-text-primary mb-4">Filtered Results ({filteredData.length})</h3>
|
||||||
<table className="min-w-full divide-y divide-app-border">
|
<table className="min-w-full divide-y divide-app-border">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Emp CODE</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Emp ID</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Name</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Name</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Branch</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Branch</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Department</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Department</th>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { Routes, Route, Navigate } from 'react-router-dom';
|
|||||||
import AttendanceDashboard from '../pages/summary/AttendanceDashboard';
|
import AttendanceDashboard from '../pages/summary/AttendanceDashboard';
|
||||||
import MonthlyReport from '../pages/summary/MonthlyReport';
|
import MonthlyReport from '../pages/summary/MonthlyReport';
|
||||||
import DailyReport from '../pages/summary/DailyReport';
|
import DailyReport from '../pages/summary/DailyReport';
|
||||||
import AdminRegularization from '../pages/summary/AdminRegularization';
|
import AdminRegularization from '../pages/summary/AdminRegularization'; // New Import
|
||||||
|
|
||||||
export default function AttendanceSummaryRouter() {
|
export default function AttendanceSummaryRouter() {
|
||||||
return (
|
return (
|
||||||
@ -11,7 +11,7 @@ export default function AttendanceSummaryRouter() {
|
|||||||
<Route path="/dashboard" element={<AttendanceDashboard />} />
|
<Route path="/dashboard" element={<AttendanceDashboard />} />
|
||||||
<Route path="/monthly" element={<MonthlyReport />} />
|
<Route path="/monthly" element={<MonthlyReport />} />
|
||||||
<Route path="/daily" element={<DailyReport />} />
|
<Route path="/daily" element={<DailyReport />} />
|
||||||
<Route path="/regularization" element={<AdminRegularization />} />
|
<Route path="/regularization" element={<AdminRegularization />} /> {/* New Route */}
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -8,4 +8,5 @@ export interface AttendanceRecord {
|
|||||||
status: AttendanceStatus;
|
status: AttendanceStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add this line to fix the error
|
||||||
export type SortKey = keyof AttendanceRecord;
|
export type SortKey = keyof AttendanceRecord;
|
||||||
@ -1,75 +1,8 @@
|
|||||||
import { Users, CalendarCheck, ClipboardCheck, Plane, Building } from 'lucide-react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
export default function AdminDashboard() {
|
export default function AdminDashboard() {
|
||||||
const stats = [
|
return (
|
||||||
{ title: 'Total Employees', value: '245', icon: Users, color: 'text-primary', bg: 'bg-primary/10' },
|
<div className="bg-app-card rounded-lg shadow-sm p-6">
|
||||||
{ title: 'Present Today', value: '198', icon: CalendarCheck, color: 'text-present-700', bg: 'bg-present-100' },
|
<h2 className="text-2xl font-bold text-default">Admin Dashboard</h2>
|
||||||
{ title: 'Pending Regularizations', value: '4', icon: ClipboardCheck, color: 'text-late-700', bg: 'bg-late-100' },
|
<p className="text-text-muted mt-2">Welcome Admin!</p>
|
||||||
{ title: 'On Leave', value: '12', icon: Plane, color: 'text-absent-700', bg: 'bg-absent-100' },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
|
||||||
<h2 className="text-2xl font-bold text-text-primary">Welcome back, Admin!</h2>
|
|
||||||
<p className="text-text-muted mt-1">Here is the organizational overview for today.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
||||||
{stats.map((stat, index) => (
|
|
||||||
<div key={index} className="bg-app-card p-6 rounded-lg shadow-sm flex items-center justify-between transition-transform hover:scale-[1.02]">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-text-muted font-medium">{stat.title}</p>
|
|
||||||
<p className={`text-2xl font-bold mt-2 ${stat.color}`}>{stat.value}</p>
|
|
||||||
</div>
|
|
||||||
<div className={`p-3 rounded-full ${stat.bg}`}>
|
|
||||||
<stat.icon className={stat.color} size={28} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
|
||||||
<h3 className="text-lg font-semibold text-text-primary mb-4">Quick Links</h3>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<Link to="/master-data/employees" className="flex items-center p-4 border border-app-border rounded-lg hover:bg-app-muted transition-colors">
|
|
||||||
<Users size={20} className="text-primary mr-3" />
|
|
||||||
<span className="text-sm font-medium text-text-primary">Manage Employees</span>
|
|
||||||
</Link>
|
|
||||||
<Link to="/attendance-summary/dashboard" className="flex items-center p-4 border border-app-border rounded-lg hover:bg-app-muted transition-colors">
|
|
||||||
<CalendarCheck size={20} className="text-present-700 mr-3" />
|
|
||||||
<span className="text-sm font-medium text-text-primary">Attendance Summary</span>
|
|
||||||
</Link>
|
|
||||||
<Link to="/leave-summary/applications" className="flex items-center p-4 border border-app-border rounded-lg hover:bg-app-muted transition-colors">
|
|
||||||
<ClipboardCheck size={20} className="text-late-700 mr-3" />
|
|
||||||
<span className="text-sm font-medium text-text-primary">Leave Applications</span>
|
|
||||||
</Link>
|
|
||||||
<Link to="/master-data/company" className="flex items-center p-4 border border-app-border rounded-lg hover:bg-app-muted transition-colors">
|
|
||||||
<Building size={20} className="text-primary mr-3" />
|
|
||||||
<span className="text-sm font-medium text-text-primary">Master Data</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
|
||||||
<h3 className="text-lg font-semibold text-text-primary mb-4">Department-wise Summary</h3>
|
|
||||||
<div className="space-y-3">
|
|
||||||
{[
|
|
||||||
{ dept: 'IT', present: 45, total: 50 },
|
|
||||||
{ dept: 'Training', present: 30, total: 35 },
|
|
||||||
{ dept: 'Analytics', present: 20, total: 25 },
|
|
||||||
{ dept: 'Placement', present: 15, total: 20 },
|
|
||||||
].map((d, idx) => (
|
|
||||||
<div key={idx} className="flex items-center justify-between border-b border-app-border pb-3 last:border-b-0 last:pb-0">
|
|
||||||
<span className="text-sm text-text-secondary">{d.dept} Department</span>
|
|
||||||
<span className="text-sm font-medium text-text-primary">{d.present} / {d.total} Present</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,49 +1,8 @@
|
|||||||
import { CalendarCheck, Plane, Clock } from 'lucide-react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
export default function EmployeeDashboard() {
|
export default function EmployeeDashboard() {
|
||||||
const stats = [
|
|
||||||
{ title: 'Attendance Status', value: 'Present', icon: CalendarCheck, color: 'text-present-700', bg: 'bg-present-100' },
|
|
||||||
{ title: 'Leave Balance', value: '12 Days', icon: Plane, color: 'text-primary', bg: 'bg-primary/10' },
|
|
||||||
{ title: 'Check-In Time', value: '09:05 AM', icon: Clock, color: 'text-late-700', bg: 'bg-late-100' },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="bg-app-card rounded-lg shadow-sm p-6">
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
<h2 className="text-2xl font-bold text-default">Employee Dashboard</h2>
|
||||||
<h2 className="text-2xl font-bold text-text-primary">Welcome back, Employee!</h2>
|
<p className="text-text-muted mt-2">Welcome User!</p>
|
||||||
<p className="text-text-muted mt-1">Here is your attendance and leave summary for today.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
||||||
{stats.map((stat, index) => (
|
|
||||||
<div key={index} className="bg-app-card p-6 rounded-lg shadow-sm flex items-center justify-between transition-transform hover:scale-[1.02]">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-text-muted font-medium">{stat.title}</p>
|
|
||||||
<p className={`text-2xl font-bold mt-2 ${stat.color}`}>{stat.value}</p>
|
|
||||||
</div>
|
|
||||||
<div className={`p-3 rounded-full ${stat.bg}`}>
|
|
||||||
<stat.icon className={stat.color} size={28} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
|
||||||
<h3 className="text-lg font-semibold text-text-primary mb-4">Quick Actions</h3>
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<Link to="/leave" className="flex items-center p-4 border border-app-border rounded-lg hover:bg-app-muted transition-colors">
|
|
||||||
<Plane size={20} className="text-primary mr-3" />
|
|
||||||
<span className="text-sm font-medium text-text-primary">Check Leave Balance</span>
|
|
||||||
</Link>
|
|
||||||
<Link to="/attendance" className="flex items-center p-4 border border-app-border rounded-lg hover:bg-app-muted transition-colors">
|
|
||||||
<CalendarCheck size={20} className="text-present-700 mr-3" />
|
|
||||||
<span className="text-sm font-medium text-text-primary">View Attendance</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,85 +1,8 @@
|
|||||||
import { CheckCircle, Users, Plane, ArrowRight } from 'lucide-react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
export default function ManagerDashboard() {
|
export default function ManagerDashboard() {
|
||||||
const stats = [
|
|
||||||
{ title: 'Pending Approvals', value: '5', icon: CheckCircle, color: 'text-late-700', bg: 'bg-late-100' },
|
|
||||||
{ title: 'Team Present Today', value: '18 / 20', icon: Users, color: 'text-present-700', bg: 'bg-present-100' },
|
|
||||||
{ title: 'Team On Leave', value: '2', icon: Plane, color: 'text-absent-700', bg: 'bg-absent-100' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const recentApprovals = [
|
|
||||||
{ id: 'LA001', name: 'Alice Williams', type: 'Casual Leave', days: 2 },
|
|
||||||
{ id: 'LA002', name: 'Bob Smith', type: 'Sick Leave', days: 1 },
|
|
||||||
{ id: 'LA004', name: 'Diana Prince', type: 'Casual Leave', days: 3 },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="bg-app-card rounded-lg shadow-sm p-6">
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
<h2 className="text-2xl font-bold text-default">Manager Dashboard</h2>
|
||||||
<h2 className="text-2xl font-bold text-text-primary">Welcome back, Manager!</h2>
|
<p className="text-text-muted mt-2">Welcome Manager!</p>
|
||||||
<p className="text-text-muted mt-1">Here is your team overview for today.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
||||||
{stats.map((stat, index) => (
|
|
||||||
<div key={index} className="bg-app-card p-6 rounded-lg shadow-sm flex items-center justify-between transition-transform hover:scale-[1.02]">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-text-muted font-medium">{stat.title}</p>
|
|
||||||
<p className={`text-2xl font-bold mt-2 ${stat.color}`}>{stat.value}</p>
|
|
||||||
</div>
|
|
||||||
<div className={`p-3 rounded-full ${stat.bg}`}>
|
|
||||||
<stat.icon className={stat.color} size={28} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
|
||||||
<div className="flex justify-between items-center mb-4">
|
|
||||||
<h3 className="text-lg font-semibold text-text-primary">Pending Leave Requests</h3>
|
|
||||||
<Link to="/approval" className="text-primary text-sm font-medium hover:text-primary-hover flex items-center">
|
|
||||||
View All <ArrowRight size={14} className="ml-1" />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-3">
|
|
||||||
{recentApprovals.map((req) => (
|
|
||||||
<div key={req.id} className="flex items-center justify-between p-3 border border-app-border rounded-lg">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-text-primary">{req.name}</p>
|
|
||||||
<p className="text-xs text-text-muted">{req.type} • {req.days} Day(s)</p>
|
|
||||||
</div>
|
|
||||||
<Link to="/approval" className="px-3 py-1 bg-primary/10 text-primary text-xs font-medium rounded-full">Review</Link>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
|
||||||
<h3 className="text-lg font-semibold text-text-primary mb-4">Team Attendance Today</h3>
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<span className="text-sm text-text-secondary">Present</span>
|
|
||||||
<div className="w-2/3 bg-app-muted rounded-full h-2.5">
|
|
||||||
<div className="bg-present-500 h-2.5 rounded-full" style={{ width: '90%' }}></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<span className="text-sm text-text-secondary">On Leave</span>
|
|
||||||
<div className="w-2/3 bg-app-muted rounded-full h-2.5">
|
|
||||||
<div className="bg-absent-400 h-2.5 rounded-full" style={{ width: '10%' }}></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<span className="text-sm text-text-secondary">Late Arrivals</span>
|
|
||||||
<div className="w-2/3 bg-app-muted rounded-full h-2.5">
|
|
||||||
<div className="bg-late-500 h-2.5 rounded-full" style={{ width: '15%' }}></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,87 +1,8 @@
|
|||||||
import { Building, Network, Users, ShieldUser, ArrowRight, ScrollText } from 'lucide-react';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
|
|
||||||
export default function SuperAdminDashboard() {
|
export default function SuperAdminDashboard() {
|
||||||
const stats = [
|
|
||||||
{ title: 'Total Companies', value: '3', icon: Building, color: 'text-primary', bg: 'bg-primary/10' },
|
|
||||||
{ title: 'Total Branches', value: '12', icon: Network, color: 'text-primary', bg: 'bg-primary/10' },
|
|
||||||
{ title: 'Global Employees', value: '450', icon: Users, color: 'text-present-700', bg: 'bg-present-100' },
|
|
||||||
{ title: 'Total Admins', value: '5', icon: ShieldUser, color: 'text-late-700', bg: 'bg-late-100' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const companies = [
|
|
||||||
{ name: 'CLRI', employees: 245, status: 'Active' },
|
|
||||||
{ name: 'TechNova Solutions', employees: 120, status: 'Active' },
|
|
||||||
{ name: 'Infotech Ltd', employees: 85, status: 'Active' },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="bg-app-card rounded-lg shadow-sm p-6">
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
<h2 className="text-2xl font-bold text-default">Super Admin Dashboard</h2>
|
||||||
<h2 className="text-2xl font-bold text-text-primary">Welcome back, SuperAdmin!</h2>
|
<p className="text-text-muted mt-2">Welcome Super Admin!</p>
|
||||||
<p className="text-text-muted mt-1">Here is the global organizational overview.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
||||||
{stats.map((stat, index) => (
|
|
||||||
<div key={index} className="bg-app-card p-6 rounded-lg shadow-sm flex items-center justify-between transition-transform hover:scale-[1.02]">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm text-text-muted font-medium">{stat.title}</p>
|
|
||||||
<p className={`text-2xl font-bold mt-2 ${stat.color}`}>{stat.value}</p>
|
|
||||||
</div>
|
|
||||||
<div className={`p-3 rounded-full ${stat.bg}`}>
|
|
||||||
<stat.icon className={stat.color} size={28} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
|
||||||
<h3 className="text-lg font-semibold text-text-primary mb-4">Company Overview</h3>
|
|
||||||
<div className="space-y-3">
|
|
||||||
{companies.map((c, idx) => (
|
|
||||||
<div key={idx} className="flex items-center justify-between p-3 border border-app-border rounded-lg">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Building size={18} className="text-text-light mr-3" />
|
|
||||||
<span className="text-sm font-medium text-text-primary">{c.name}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center space-x-4">
|
|
||||||
<span className="text-sm text-text-secondary">{c.employees} Employees</span>
|
|
||||||
<span className="px-2 py-1 rounded-full text-xs font-medium bg-present-100 text-present-700">{c.status}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
|
||||||
<h3 className="text-lg font-semibold text-text-primary mb-4">SuperAdmin Actions</h3>
|
|
||||||
<div className="space-y-3">
|
|
||||||
<Link to="/manage-admins" className="flex items-center justify-between p-4 border border-app-border rounded-lg hover:bg-app-muted transition-colors">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<ShieldUser size={20} className="text-primary mr-3" />
|
|
||||||
<span className="text-sm font-medium text-text-primary">Manage Admins</span>
|
|
||||||
</div>
|
|
||||||
<ArrowRight size={18} className="text-text-light" />
|
|
||||||
</Link>
|
|
||||||
<Link to="/policies" className="flex items-center justify-between p-4 border border-app-border rounded-lg hover:bg-app-muted transition-colors">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<ScrollText size={20} className="text-primary mr-3" />
|
|
||||||
<span className="text-sm font-medium text-text-primary">Policy and Rules</span>
|
|
||||||
</div>
|
|
||||||
<ArrowRight size={18} className="text-text-light" />
|
|
||||||
</Link>
|
|
||||||
<Link to="/master-data/company" className="flex items-center justify-between p-4 border border-app-border rounded-lg hover:bg-app-muted transition-colors">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<Building size={20} className="text-primary mr-3" />
|
|
||||||
<span className="text-sm font-medium text-text-primary">Manage Master Data</span>
|
|
||||||
</div>
|
|
||||||
<ArrowRight size={18} className="text-text-light" />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,13 +1,17 @@
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import type { RootState } from '../../../store/store';
|
import type { RootState } from '../../../store/store';
|
||||||
import ManagePolicies from '../pages/ManagePolicies';
|
|
||||||
|
|
||||||
export default function PolicyRouter() {
|
export default function PolicyRouter() {
|
||||||
const role = useSelector((state: RootState) => state.role.currentRole);
|
const role = useSelector((state: RootState) => state.role.currentRole);
|
||||||
|
|
||||||
if (role === 'superadmin') {
|
if (role === 'superadmin') {
|
||||||
return <ManagePolicies />;
|
return (
|
||||||
|
<div className="bg-app-card p-6 rounded-lg shadow">
|
||||||
|
<h2 className="text-2xl font-bold text-primary">Policy and Rules</h2>
|
||||||
|
<p className="mt-2 text-text-muted">Configure global leave policies, LOP rules, and attendance shift timings.</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: SuperAdmins only.</div>;
|
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: SuperAdmins only.</div>;
|
||||||
}
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { fetchMockData } from '../../../api/client';
|
import { fetchMockData } from '../../../api/client';
|
||||||
import type { Employee } from './useEmployeeData';
|
import type { Employee } from './useEmployeeData';
|
||||||
|
|
||||||
|
// Extends Employee to include admin status
|
||||||
export interface AdminEmployee extends Employee {
|
export interface AdminEmployee extends Employee {
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import { fetchMockData } from '../../../api/client';
|
|||||||
export interface Employee {
|
export interface Employee {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
|
||||||
designationId: string;
|
designationId: string;
|
||||||
designationName: string;
|
designationName: string;
|
||||||
deptId: string;
|
deptId: string;
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { fetchMockData } from '../../../api/client';
|
|
||||||
|
|
||||||
export interface Policy {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
category: string;
|
|
||||||
description: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CompanyPolicy {
|
|
||||||
companyId: string;
|
|
||||||
companyName: string;
|
|
||||||
policies: Policy[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const usePolicies = () => {
|
|
||||||
return useQuery<CompanyPolicy[]>({
|
|
||||||
queryKey: ['policies'],
|
|
||||||
queryFn: () => fetchMockData<CompanyPolicy[]>('master-data/policies.json'),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@ -44,25 +44,25 @@ export default function BranchFormModal({ branch, companies, onClose, onSave }:
|
|||||||
<h3 className="text-xl font-bold text-text-primary mb-6">
|
<h3 className="text-xl font-bold text-text-primary mb-6">
|
||||||
{branch ? 'Edit Branch' : 'Add New Branch'}
|
{branch ? 'Edit Branch' : 'Add New Branch'}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Branch Name</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Branch Name</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
||||||
placeholder="Enter branch name..."
|
placeholder="Enter branch name..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Company</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Company</label>
|
||||||
<select
|
<select
|
||||||
value={companyId}
|
value={companyId}
|
||||||
onChange={(e) => setCompanyId(e.target.value)}
|
onChange={(e) => setCompanyId(e.target.value)}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
||||||
>
|
>
|
||||||
@ -76,9 +76,9 @@ export default function BranchFormModal({ branch, companies, onClose, onSave }:
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Status</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Status</label>
|
||||||
<select
|
<select
|
||||||
value={status}
|
value={status}
|
||||||
onChange={(e) => setStatus(e.target.value as 'Active' | 'Disabled')}
|
onChange={(e) => setStatus(e.target.value as 'Active' | 'Disabled')}
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
||||||
>
|
>
|
||||||
<option value="Active">Active</option>
|
<option value="Active">Active</option>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export default function BranchTable({ records, onEdit, onDelete }: BranchTablePr
|
|||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(10);
|
||||||
|
|
||||||
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
||||||
key: 'name',
|
key: 'name',
|
||||||
direction: 'ascending'
|
direction: 'ascending'
|
||||||
@ -31,7 +31,7 @@ export default function BranchTable({ records, onEdit, onDelete }: BranchTablePr
|
|||||||
|
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
if (!searchQuery) return records;
|
if (!searchQuery) return records;
|
||||||
return records.filter(record =>
|
return records.filter(record =>
|
||||||
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.companyName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.companyName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
@ -109,13 +109,13 @@ export default function BranchTable({ records, onEdit, onDelete }: BranchTablePr
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{(['id', 'name', 'companyName', 'status'] as SortKey[]).map((key) => (
|
{(['id', 'name', 'companyName', 'status'] as SortKey[]).map((key) => (
|
||||||
<th
|
<th
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => requestSort(key)}
|
onClick={() => requestSort(key)}
|
||||||
className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors"
|
className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors"
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{key === 'id' ? 'Branch CODE' : key === 'companyName' ? 'Company' : key.charAt(0).toUpperCase() + key.slice(1)} {getSortIcon(key)}
|
{key === 'id' ? 'Branch ID' : key === 'companyName' ? 'Company' : key.charAt(0).toUpperCase() + key.slice(1)} {getSortIcon(key)}
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -37,25 +37,25 @@ export default function CompanyFormModal({ company, onClose, onSave }: CompanyFo
|
|||||||
<h3 className="text-xl font-bold text-text-primary mb-6">
|
<h3 className="text-xl font-bold text-text-primary mb-6">
|
||||||
{company ? 'Edit Company' : 'Add New Company'}
|
{company ? 'Edit Company' : 'Add New Company'}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Company Name</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Company Name</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
||||||
placeholder="Enter company name..."
|
placeholder="Enter company name..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Status</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Status</label>
|
||||||
<select
|
<select
|
||||||
value={status}
|
value={status}
|
||||||
onChange={(e) => setStatus(e.target.value as 'Active' | 'Disabled')}
|
onChange={(e) => setStatus(e.target.value as 'Active' | 'Disabled')}
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
||||||
>
|
>
|
||||||
<option value="Active">Active</option>
|
<option value="Active">Active</option>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export default function CompanyTable({ records, onEdit, onDelete }: CompanyTable
|
|||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(10);
|
||||||
|
|
||||||
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
||||||
key: 'name',
|
key: 'name',
|
||||||
direction: 'ascending'
|
direction: 'ascending'
|
||||||
@ -31,7 +31,7 @@ export default function CompanyTable({ records, onEdit, onDelete }: CompanyTable
|
|||||||
|
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
if (!searchQuery) return records;
|
if (!searchQuery) return records;
|
||||||
return records.filter(record =>
|
return records.filter(record =>
|
||||||
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.status.toLowerCase().includes(searchQuery.toLowerCase())
|
record.status.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
@ -108,13 +108,13 @@ export default function CompanyTable({ records, onEdit, onDelete }: CompanyTable
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{(['id', 'name', 'status'] as SortKey[]).map((key) => (
|
{(['id', 'name', 'status'] as SortKey[]).map((key) => (
|
||||||
<th
|
<th
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => requestSort(key)}
|
onClick={() => requestSort(key)}
|
||||||
className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors"
|
className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors"
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{key === 'id' ? 'Company CODE' : key.charAt(0).toUpperCase() + key.slice(1)} {getSortIcon(key)}
|
{key === 'id' ? 'Company ID' : key.charAt(0).toUpperCase() + key.slice(1)} {getSortIcon(key)}
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export default function DepartmentFormModal({ department, companies, branches, o
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const selectedCompany = companies.find(c => c.id === companyId);
|
const selectedCompany = companies.find(c => c.id === companyId);
|
||||||
const selectedBranch = branches.find(b => b.id === branchId);
|
const selectedBranch = branches.find(b => b.id === branchId);
|
||||||
|
|
||||||
onSave({
|
onSave({
|
||||||
name,
|
name,
|
||||||
companyId,
|
companyId,
|
||||||
@ -67,25 +67,25 @@ export default function DepartmentFormModal({ department, companies, branches, o
|
|||||||
<h3 className="text-xl font-bold text-text-primary mb-6">
|
<h3 className="text-xl font-bold text-text-primary mb-6">
|
||||||
{department ? 'Edit Department' : 'Add New Department'}
|
{department ? 'Edit Department' : 'Add New Department'}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Department Name</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Department Name</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
||||||
placeholder="Enter department name..."
|
placeholder="Enter department name..."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Company</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Company</label>
|
||||||
<select
|
<select
|
||||||
value={companyId}
|
value={companyId}
|
||||||
onChange={handleCompanyChange}
|
onChange={handleCompanyChange}
|
||||||
required
|
required
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
||||||
>
|
>
|
||||||
@ -99,9 +99,9 @@ export default function DepartmentFormModal({ department, companies, branches, o
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Branch</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Branch</label>
|
||||||
<select
|
<select
|
||||||
value={branchId}
|
value={branchId}
|
||||||
onChange={(e) => setBranchId(e.target.value)}
|
onChange={(e) => setBranchId(e.target.value)}
|
||||||
required
|
required
|
||||||
disabled={!companyId || filteredBranches.length === 0}
|
disabled={!companyId || filteredBranches.length === 0}
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card disabled:opacity-50 disabled:cursor-not-allowed"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
@ -116,9 +116,9 @@ export default function DepartmentFormModal({ department, companies, branches, o
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">Status</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">Status</label>
|
||||||
<select
|
<select
|
||||||
value={status}
|
value={status}
|
||||||
onChange={(e) => setStatus(e.target.value as 'Active' | 'Disabled')}
|
onChange={(e) => setStatus(e.target.value as 'Active' | 'Disabled')}
|
||||||
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
className="w-full px-3 py-2 border border-app-border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-primary text-text-secondary bg-app-card"
|
||||||
>
|
>
|
||||||
<option value="Active">Active</option>
|
<option value="Active">Active</option>
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export default function DepartmentTable({ records, onEdit, onDelete }: Departmen
|
|||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(10);
|
||||||
|
|
||||||
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
||||||
key: 'name',
|
key: 'name',
|
||||||
direction: 'ascending'
|
direction: 'ascending'
|
||||||
@ -31,7 +31,7 @@ export default function DepartmentTable({ records, onEdit, onDelete }: Departmen
|
|||||||
|
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
if (!searchQuery) return records;
|
if (!searchQuery) return records;
|
||||||
return records.filter(record =>
|
return records.filter(record =>
|
||||||
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.branchName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.branchName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
@ -110,13 +110,13 @@ export default function DepartmentTable({ records, onEdit, onDelete }: Departmen
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{(['id', 'name', 'branchName', 'companyName', 'status'] as SortKey[]).map((key) => (
|
{(['id', 'name', 'branchName', 'companyName', 'status'] as SortKey[]).map((key) => (
|
||||||
<th
|
<th
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => requestSort(key)}
|
onClick={() => requestSort(key)}
|
||||||
className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors"
|
className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors"
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{key === 'id' ? 'Dept CODE' : key === 'name' ? 'Department' : key === 'branchName' ? 'Branch' : key === 'companyName' ? 'Company' : key.charAt(0).toUpperCase() + key.slice(1)} {getSortIcon(key)}
|
{key === 'id' ? 'Dept ID' : key === 'name' ? 'Department' : key === 'branchName' ? 'Branch' : key === 'companyName' ? 'Company' : key.charAt(0).toUpperCase() + key.slice(1)} {getSortIcon(key)}
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -82,13 +82,13 @@ export default function DesignationFormModal({ designation, companies, branches,
|
|||||||
<h3 className="text-xl font-bold text-text-primary mb-6">
|
<h3 className="text-xl font-bold text-text-primary mb-6">
|
||||||
{designation ? 'Edit Designation' : 'Add New Designation'}
|
{designation ? 'Edit Designation' : 'Add New Designation'}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className={labelClass}>Designation Name</label>
|
<label className={labelClass}>Designation Name</label>
|
||||||
<input type="text" value={name} onChange={(e) => setName(e.target.value)} required className={inputClass} placeholder="Enter designation name..." />
|
<input type="text" value={name} onChange={(e) => setName(e.target.value)} required className={inputClass} placeholder="Enter designation name..." />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className={labelClass}>Company</label>
|
<label className={labelClass}>Company</label>
|
||||||
<select value={companyId} onChange={handleCompanyChange} required className={inputClass}>
|
<select value={companyId} onChange={handleCompanyChange} required className={inputClass}>
|
||||||
|
|||||||
@ -23,14 +23,14 @@ export default function DesignationTable({ records, onEdit, onDelete }: Designat
|
|||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(10);
|
||||||
|
|
||||||
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
||||||
key: 'name', direction: 'ascending'
|
key: 'name', direction: 'ascending'
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
if (!searchQuery) return records;
|
if (!searchQuery) return records;
|
||||||
return records.filter(record =>
|
return records.filter(record =>
|
||||||
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.deptName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.deptName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
@ -90,7 +90,7 @@ export default function DesignationTable({ records, onEdit, onDelete }: Designat
|
|||||||
};
|
};
|
||||||
|
|
||||||
const formatHeader = (key: string) => {
|
const formatHeader = (key: string) => {
|
||||||
if (key === 'id') return 'Designation CODE';
|
if (key === 'id') return 'Designation ID';
|
||||||
if (key === 'name') return 'Designation';
|
if (key === 'name') return 'Designation';
|
||||||
if (key === 'deptName') return 'Department';
|
if (key === 'deptName') return 'Department';
|
||||||
if (key === 'branchName') return 'Branch';
|
if (key === 'branchName') return 'Branch';
|
||||||
|
|||||||
@ -18,7 +18,6 @@ interface EmployeeFormModalProps {
|
|||||||
|
|
||||||
export default function EmployeeFormModal({ employee, companies, branches, departments, designations, onClose, onSave }: EmployeeFormModalProps) {
|
export default function EmployeeFormModal({ employee, companies, branches, departments, designations, onClose, onSave }: EmployeeFormModalProps) {
|
||||||
const [name, setName] = useState('');
|
const [name, setName] = useState('');
|
||||||
const [email, setEmail] = useState('');
|
|
||||||
const [companyId, setCompanyId] = useState('');
|
const [companyId, setCompanyId] = useState('');
|
||||||
const [branchId, setBranchId] = useState('');
|
const [branchId, setBranchId] = useState('');
|
||||||
const [deptId, setDeptId] = useState('');
|
const [deptId, setDeptId] = useState('');
|
||||||
@ -29,7 +28,6 @@ export default function EmployeeFormModal({ employee, companies, branches, depar
|
|||||||
if (employee) {
|
if (employee) {
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
setName(employee.name);
|
setName(employee.name);
|
||||||
setEmail(employee.email);
|
|
||||||
setCompanyId(employee.companyId);
|
setCompanyId(employee.companyId);
|
||||||
setBranchId(employee.branchId);
|
setBranchId(employee.branchId);
|
||||||
setDeptId(employee.deptId);
|
setDeptId(employee.deptId);
|
||||||
@ -37,7 +35,6 @@ export default function EmployeeFormModal({ employee, companies, branches, depar
|
|||||||
setStatus(employee.status);
|
setStatus(employee.status);
|
||||||
} else {
|
} else {
|
||||||
setName('');
|
setName('');
|
||||||
setEmail('');
|
|
||||||
setCompanyId(companies.length > 0 ? companies[0].id : '');
|
setCompanyId(companies.length > 0 ? companies[0].id : '');
|
||||||
setBranchId('');
|
setBranchId('');
|
||||||
setDeptId('');
|
setDeptId('');
|
||||||
@ -77,7 +74,6 @@ export default function EmployeeFormModal({ employee, companies, branches, depar
|
|||||||
|
|
||||||
onSave({
|
onSave({
|
||||||
name,
|
name,
|
||||||
email,
|
|
||||||
companyId,
|
companyId,
|
||||||
companyName: selectedCompany?.name || '',
|
companyName: selectedCompany?.name || '',
|
||||||
branchId,
|
branchId,
|
||||||
@ -102,18 +98,13 @@ export default function EmployeeFormModal({ employee, companies, branches, depar
|
|||||||
<h3 className="text-xl font-bold text-text-primary mb-6">
|
<h3 className="text-xl font-bold text-text-primary mb-6">
|
||||||
{employee ? 'Edit Employee' : 'Add New Employee'}
|
{employee ? 'Edit Employee' : 'Add New Employee'}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className={labelClass}>Employee Name</label>
|
<label className={labelClass}>Employee Name</label>
|
||||||
<input type="text" value={name} onChange={(e) => setName(e.target.value)} required className={inputClass} placeholder="Enter employee name..." />
|
<input type="text" value={name} onChange={(e) => setName(e.target.value)} required className={inputClass} placeholder="Enter employee name..." />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className={labelClass}>Employee Email</label>
|
|
||||||
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required className={inputClass} placeholder="Enter email address..." />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className={labelClass}>Company</label>
|
<label className={labelClass}>Company</label>
|
||||||
<select value={companyId} onChange={handleCompanyChange} required className={inputClass}>
|
<select value={companyId} onChange={handleCompanyChange} required className={inputClass}>
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
import { useState, useMemo } from 'react';
|
import { useState, useMemo } from 'react';
|
||||||
import { ArrowUpDown, ArrowUp, ArrowDown, Search, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Eye, Pencil, Trash2 } from 'lucide-react';
|
import { ArrowUpDown, ArrowUp, ArrowDown, Search, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Eye, Pencil, Trash2 } from 'lucide-react'; // Added Eye
|
||||||
import type { Employee } from '../../api/useEmployeeData';
|
import type { Employee } from '../../api/useEmployeeData';
|
||||||
|
|
||||||
interface EmployeeTableProps {
|
interface EmployeeTableProps {
|
||||||
records: Employee[];
|
records: Employee[];
|
||||||
onView: (employee: Employee) => void;
|
onView: (employee: Employee) => void; // Added onView
|
||||||
onEdit: (employee: Employee) => void;
|
onEdit: (employee: Employee) => void;
|
||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SortKey = 'id' | 'email' | 'name' | 'designationName' | 'deptName' | 'branchName' | 'companyName' | 'status';
|
type SortKey = 'id' | 'name' | 'designationName' | 'deptName' | 'branchName' | 'companyName' | 'status';
|
||||||
type SortDirection = 'ascending' | 'descending';
|
type SortDirection = 'ascending' | 'descending';
|
||||||
|
|
||||||
const getStatusClass = (status: string) => {
|
const getStatusClass = (status: string) => {
|
||||||
@ -24,17 +24,16 @@ export default function EmployeeTable({ records, onView, onEdit, onDelete }: Emp
|
|||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(10);
|
||||||
|
|
||||||
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
||||||
key: 'name', direction: 'ascending'
|
key: 'name', direction: 'ascending'
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
if (!searchQuery) return records;
|
if (!searchQuery) return records;
|
||||||
return records.filter(record =>
|
return records.filter(record =>
|
||||||
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.email.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
||||||
record.designationName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.designationName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.deptName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.deptName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.branchName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.branchName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
@ -93,9 +92,8 @@ export default function EmployeeTable({ records, onView, onEdit, onDelete }: Emp
|
|||||||
};
|
};
|
||||||
|
|
||||||
const formatHeader = (key: string) => {
|
const formatHeader = (key: string) => {
|
||||||
if (key === 'id') return 'Emp CODE';
|
if (key === 'id') return 'Emp ID';
|
||||||
if (key === 'name') return 'Employee Name';
|
if (key === 'name') return 'Employee Name';
|
||||||
if (key === 'email') return 'Employee Email';
|
|
||||||
if (key === 'designationName') return 'Designation';
|
if (key === 'designationName') return 'Designation';
|
||||||
if (key === 'deptName') return 'Department';
|
if (key === 'deptName') return 'Department';
|
||||||
if (key === 'branchName') return 'Branch';
|
if (key === 'branchName') return 'Branch';
|
||||||
@ -123,7 +121,7 @@ export default function EmployeeTable({ records, onView, onEdit, onDelete }: Emp
|
|||||||
<table className="min-w-full divide-y divide-app-border">
|
<table className="min-w-full divide-y divide-app-border">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{(['id', 'name', 'email', 'designationName', 'deptName', 'branchName', 'companyName', 'status'] as SortKey[]).map((key) => (
|
{(['id', 'name', 'designationName', 'deptName', 'branchName', 'companyName', 'status'] as SortKey[]).map((key) => (
|
||||||
<th key={key} onClick={() => requestSort(key)} className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors">
|
<th key={key} onClick={() => requestSort(key)} className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{formatHeader(key)} {getSortIcon(key)}
|
{formatHeader(key)} {getSortIcon(key)}
|
||||||
@ -139,7 +137,6 @@ export default function EmployeeTable({ records, onView, onEdit, onDelete }: Emp
|
|||||||
<tr key={record.id} className="hover:bg-app-muted transition-colors">
|
<tr key={record.id} className="hover:bg-app-muted transition-colors">
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-primary">{record.id}</td>
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-primary">{record.id}</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.name}</td>
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.name}</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.email}</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.designationName}</td>
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.designationName}</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.deptName}</td>
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.deptName}</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.branchName}</td>
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.branchName}</td>
|
||||||
@ -162,7 +159,7 @@ export default function EmployeeTable({ records, onView, onEdit, onDelete }: Emp
|
|||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={9} className="px-6 py-8 text-center text-sm text-text-muted">No employees found.</td>
|
<td colSpan={8} className="px-6 py-8 text-center text-sm text-text-muted">No employees found.</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -22,8 +22,9 @@ export default function EmployeeViewModal({ employee, onClose }: EmployeeViewMod
|
|||||||
<X size={24} />
|
<X size={24} />
|
||||||
</button>
|
</button>
|
||||||
<h3 className="text-xl font-bold text-text-primary mb-6">Employee Details</h3>
|
<h3 className="text-xl font-bold text-text-primary mb-6">Employee Details</h3>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
{/* Employee ID moved here to match other fields */}
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
<div className="flex justify-between border-b border-app-border pb-2">
|
||||||
<span className="text-sm text-text-muted">Employee ID</span>
|
<span className="text-sm text-text-muted">Employee ID</span>
|
||||||
<span className="text-sm font-medium text-text-primary">{employee.id}</span>
|
<span className="text-sm font-medium text-text-primary">{employee.id}</span>
|
||||||
@ -32,10 +33,6 @@ export default function EmployeeViewModal({ employee, onClose }: EmployeeViewMod
|
|||||||
<span className="text-sm text-text-muted">Employee Name</span>
|
<span className="text-sm text-text-muted">Employee Name</span>
|
||||||
<span className="text-sm font-medium text-text-primary">{employee.name}</span>
|
<span className="text-sm font-medium text-text-primary">{employee.name}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
|
||||||
<span className="text-sm text-text-muted">Employee Email</span>
|
|
||||||
<span className="text-sm font-medium text-text-primary">{employee.email}</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
<div className="flex justify-between border-b border-app-border pb-2">
|
||||||
<span className="text-sm text-text-muted">Company</span>
|
<span className="text-sm text-text-muted">Company</span>
|
||||||
<span className="text-sm font-medium text-text-primary">{employee.companyName}</span>
|
<span className="text-sm font-medium text-text-primary">{employee.companyName}</span>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ interface AdminTableProps {
|
|||||||
onDelete: (id: string) => void;
|
onDelete: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SortKey = 'id' | 'email' | 'name' | 'designationName' | 'deptName' | 'branchName' | 'companyName' | 'status' | 'isAdmin';
|
type SortKey = 'id' | 'name' | 'designationName' | 'deptName' | 'branchName' | 'companyName' | 'status' | 'isAdmin';
|
||||||
type SortDirection = 'ascending' | 'descending';
|
type SortDirection = 'ascending' | 'descending';
|
||||||
|
|
||||||
const getStatusClass = (status: string) => {
|
const getStatusClass = (status: string) => {
|
||||||
@ -32,7 +32,6 @@ export default function AdminTable({ records, onToggleAdmin, onDelete }: AdminTa
|
|||||||
if (!searchQuery) return records;
|
if (!searchQuery) return records;
|
||||||
return records.filter(record =>
|
return records.filter(record =>
|
||||||
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.email.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
||||||
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.designationName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.designationName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.deptName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.deptName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
@ -45,6 +44,7 @@ export default function AdminTable({ records, onToggleAdmin, onDelete }: AdminTa
|
|||||||
const sortedData = useMemo(() => {
|
const sortedData = useMemo(() => {
|
||||||
const sortableData = [...filteredData];
|
const sortableData = [...filteredData];
|
||||||
sortableData.sort((a, b) => {
|
sortableData.sort((a, b) => {
|
||||||
|
// Handle boolean sorting for isAdmin
|
||||||
if (sortConfig.key === 'isAdmin') {
|
if (sortConfig.key === 'isAdmin') {
|
||||||
const valA = a.isAdmin ? 1 : 0;
|
const valA = a.isAdmin ? 1 : 0;
|
||||||
const valB = b.isAdmin ? 1 : 0;
|
const valB = b.isAdmin ? 1 : 0;
|
||||||
@ -98,9 +98,8 @@ export default function AdminTable({ records, onToggleAdmin, onDelete }: AdminTa
|
|||||||
};
|
};
|
||||||
|
|
||||||
const formatHeader = (key: string) => {
|
const formatHeader = (key: string) => {
|
||||||
if (key === 'id') return 'Emp CODE';
|
if (key === 'id') return 'Emp ID';
|
||||||
if (key === 'name') return 'Employee Name';
|
if (key === 'name') return 'Employee Name';
|
||||||
if (key === 'email') return 'Employee Email';
|
|
||||||
if (key === 'designationName') return 'Designation';
|
if (key === 'designationName') return 'Designation';
|
||||||
if (key === 'deptName') return 'Department';
|
if (key === 'deptName') return 'Department';
|
||||||
if (key === 'branchName') return 'Branch';
|
if (key === 'branchName') return 'Branch';
|
||||||
@ -112,7 +111,7 @@ export default function AdminTable({ records, onToggleAdmin, onDelete }: AdminTa
|
|||||||
return (
|
return (
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
<div className="bg-app-card p-6 rounded-lg shadow-sm">
|
||||||
<div className="flex flex-col md:flex-row md:items-center justify-between mb-6 gap-4">
|
<div className="flex flex-col md:flex-row md:items-center justify-between mb-6 gap-4">
|
||||||
<h2 className="text-lg font-semibold text-text-primary">Admins List</h2>
|
<h2 className="text-lg font-semibold text-text-primary">Employees & Admins List</h2>
|
||||||
<div className="relative w-full md:w-64">
|
<div className="relative w-full md:w-64">
|
||||||
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-light" />
|
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-text-light" />
|
||||||
<input
|
<input
|
||||||
@ -129,7 +128,7 @@ export default function AdminTable({ records, onToggleAdmin, onDelete }: AdminTa
|
|||||||
<table className="min-w-full divide-y divide-app-border">
|
<table className="min-w-full divide-y divide-app-border">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{(['id', 'name', 'email', 'designationName', 'deptName', 'branchName', 'companyName', 'status', 'isAdmin'] as SortKey[]).map((key) => (
|
{(['id', 'name', 'designationName', 'deptName', 'branchName', 'companyName', 'status', 'isAdmin'] as SortKey[]).map((key) => (
|
||||||
<th key={key} onClick={() => requestSort(key)} className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors">
|
<th key={key} onClick={() => requestSort(key)} className="px-6 py-3 text-left text-xs font-semibold text-text-muted uppercase tracking-wider cursor-pointer hover:bg-app-muted transition-colors">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{formatHeader(key)} {getSortIcon(key)}
|
{formatHeader(key)} {getSortIcon(key)}
|
||||||
@ -145,7 +144,6 @@ export default function AdminTable({ records, onToggleAdmin, onDelete }: AdminTa
|
|||||||
<tr key={record.id} className="hover:bg-app-muted transition-colors">
|
<tr key={record.id} className="hover:bg-app-muted transition-colors">
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-primary">{record.id}</td>
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-primary">{record.id}</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.name}</td>
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.name}</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.email}</td>
|
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.designationName}</td>
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.designationName}</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.deptName}</td>
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.deptName}</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.branchName}</td>
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-text-secondary">{record.branchName}</td>
|
||||||
@ -154,6 +152,7 @@ export default function AdminTable({ records, onToggleAdmin, onDelete }: AdminTa
|
|||||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getStatusClass(record.status)}`}>{record.status}</span>
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getStatusClass(record.status)}`}>{record.status}</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
||||||
|
{/* Admin Toggle Switch */}
|
||||||
<button
|
<button
|
||||||
onClick={() => onToggleAdmin(record.id)}
|
onClick={() => onToggleAdmin(record.id)}
|
||||||
className={`relative inline-flex items-center h-6 w-11 rounded-full transition-colors ${record.isAdmin ? 'bg-primary' : 'bg-app-border'}`}
|
className={`relative inline-flex items-center h-6 w-11 rounded-full transition-colors ${record.isAdmin ? 'bg-primary' : 'bg-app-border'}`}
|
||||||
@ -171,7 +170,7 @@ export default function AdminTable({ records, onToggleAdmin, onDelete }: AdminTa
|
|||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={10} className="px-6 py-8 text-center text-sm text-text-muted">No employees found.</td>
|
<td colSpan={9} className="px-6 py-8 text-center text-sm text-text-muted">No employees found.</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@ -18,7 +18,7 @@ export default function ManageAdmins() {
|
|||||||
const { data: branches, isLoading: branchLoading, isError: branchError } = useBranches();
|
const { data: branches, isLoading: branchLoading, isError: branchError } = useBranches();
|
||||||
const { data: departments, isLoading: depLoading, isError: depError } = useDepartments();
|
const { data: departments, isLoading: depLoading, isError: depError } = useDepartments();
|
||||||
const { data: designations, isLoading: dsgLoading, isError: dsgError } = useDesignations();
|
const { data: designations, isLoading: dsgLoading, isError: dsgError } = useDesignations();
|
||||||
|
|
||||||
const [localRecords, setLocalRecords] = useState<AdminEmployee[]>([]);
|
const [localRecords, setLocalRecords] = useState<AdminEmployee[]>([]);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [deleteId, setDeleteId] = useState<string | null>(null);
|
const [deleteId, setDeleteId] = useState<string | null>(null);
|
||||||
@ -78,7 +78,7 @@ export default function ManageAdmins() {
|
|||||||
<p className="text-sm text-text-muted">Toggle admin privileges or remove employees.</p>
|
<p className="text-sm text-text-muted">Toggle admin privileges or remove employees.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleAddClick}
|
onClick={handleAddClick}
|
||||||
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
||||||
>
|
>
|
||||||
@ -87,30 +87,30 @@ export default function ManageAdmins() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AdminTable
|
<AdminTable
|
||||||
records={localRecords}
|
records={localRecords}
|
||||||
onToggleAdmin={handleToggleAdmin}
|
onToggleAdmin={handleToggleAdmin}
|
||||||
onDelete={handleDeleteClick}
|
onDelete={handleDeleteClick}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isModalOpen && (
|
{isModalOpen && (
|
||||||
<EmployeeFormModal
|
<EmployeeFormModal
|
||||||
employee={null}
|
employee={null}
|
||||||
companies={companies || []}
|
companies={companies || []}
|
||||||
branches={branches || []}
|
branches={branches || []}
|
||||||
departments={departments || []}
|
departments={departments || []}
|
||||||
designations={designations || []}
|
designations={designations || []}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{deleteId && (
|
{deleteId && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
title="Delete Employee?"
|
title="Delete Employee?"
|
||||||
message="Are you sure you want to delete this employee? This action cannot be undone."
|
message="Are you sure you want to delete this employee? This action cannot be undone."
|
||||||
onConfirm={confirmDelete}
|
onConfirm={confirmDelete}
|
||||||
onClose={() => setDeleteId(null)}
|
onClose={() => setDeleteId(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,11 +12,11 @@ import { useCompanies } from '../api/useCompanyData';
|
|||||||
export default function ManageBranches() {
|
export default function ManageBranches() {
|
||||||
const { data: apiRecords, isLoading: branchesLoading, isError: branchesError } = useBranches();
|
const { data: apiRecords, isLoading: branchesLoading, isError: branchesError } = useBranches();
|
||||||
const { data: companies, isLoading: companiesLoading, isError: companiesError } = useCompanies();
|
const { data: companies, isLoading: companiesLoading, isError: companiesError } = useCompanies();
|
||||||
|
|
||||||
const [localRecords, setLocalRecords] = useState<Branch[]>([]);
|
const [localRecords, setLocalRecords] = useState<Branch[]>([]);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [editingBranch, setEditingBranch] = useState<Branch | null>(null);
|
const [editingBranch, setEditingBranch] = useState<Branch | null>(null);
|
||||||
const [deleteId, setDeleteId] = useState<string | null>(null);
|
const [deleteId, setDeleteId] = useState<string | null>(null); // New state
|
||||||
|
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-render
|
// eslint-disable-next-line react-hooks/set-state-in-render
|
||||||
@ -34,7 +34,7 @@ export default function ManageBranches() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteClick = (id: string) => {
|
const handleDeleteClick = (id: string) => {
|
||||||
setDeleteId(id);
|
setDeleteId(id); // Open confirmation modal
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmDelete = () => {
|
const confirmDelete = () => {
|
||||||
@ -72,7 +72,7 @@ export default function ManageBranches() {
|
|||||||
<p className="text-sm text-text-muted">Add, edit, or disable branch locations.</p>
|
<p className="text-sm text-text-muted">Add, edit, or disable branch locations.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleAddClick}
|
onClick={handleAddClick}
|
||||||
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
||||||
>
|
>
|
||||||
@ -84,20 +84,21 @@ export default function ManageBranches() {
|
|||||||
<BranchTable records={localRecords} onEdit={handleEditClick} onDelete={handleDeleteClick} />
|
<BranchTable records={localRecords} onEdit={handleEditClick} onDelete={handleDeleteClick} />
|
||||||
|
|
||||||
{isModalOpen && (
|
{isModalOpen && (
|
||||||
<BranchFormModal
|
<BranchFormModal
|
||||||
branch={editingBranch}
|
branch={editingBranch}
|
||||||
companies={companies || []}
|
companies={companies || []}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Delete Confirmation Modal */}
|
||||||
{deleteId && (
|
{deleteId && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
title="Delete Branch?"
|
title="Delete Branch?"
|
||||||
message="Are you sure you want to delete this branch? This action cannot be undone."
|
message="Are you sure you want to delete this branch? This action cannot be undone."
|
||||||
onConfirm={confirmDelete}
|
onConfirm={confirmDelete}
|
||||||
onClose={() => setDeleteId(null)}
|
onClose={() => setDeleteId(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { Building, Plus } from 'lucide-react';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import Loader from '../../../components/ui/Loader';
|
import Loader from '../../../components/ui/Loader';
|
||||||
import ErrorState from '../../../components/ui/ErrorState';
|
import ErrorState from '../../../components/ui/ErrorState';
|
||||||
import ConfirmationModal from '../../../components/ui/ConfirmationModal';
|
import ConfirmationModal from '../../../components/ui/ConfirmationModal'; // New Import
|
||||||
import CompanyTable from '../components/company/CompanyTable';
|
import CompanyTable from '../components/company/CompanyTable';
|
||||||
import CompanyFormModal from '../components/company/CompanyFormModal';
|
import CompanyFormModal from '../components/company/CompanyFormModal';
|
||||||
import { useCompanies, type Company } from '../api/useCompanyData';
|
import { useCompanies, type Company } from '../api/useCompanyData';
|
||||||
@ -13,7 +13,7 @@ export default function ManageCompanies() {
|
|||||||
const [localRecords, setLocalRecords] = useState<Company[]>([]);
|
const [localRecords, setLocalRecords] = useState<Company[]>([]);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [editingCompany, setEditingCompany] = useState<Company | null>(null);
|
const [editingCompany, setEditingCompany] = useState<Company | null>(null);
|
||||||
const [deleteId, setDeleteId] = useState<string | null>(null);
|
const [deleteId, setDeleteId] = useState<string | null>(null); // New state for delete confirmation
|
||||||
|
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-render
|
// eslint-disable-next-line react-hooks/set-state-in-render
|
||||||
@ -31,7 +31,7 @@ export default function ManageCompanies() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteClick = (id: string) => {
|
const handleDeleteClick = (id: string) => {
|
||||||
setDeleteId(id);
|
setDeleteId(id); // Open confirmation modal instead of deleting directly
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmDelete = () => {
|
const confirmDelete = () => {
|
||||||
@ -69,7 +69,7 @@ export default function ManageCompanies() {
|
|||||||
<p className="text-sm text-text-muted">Add, edit, or disable company entities.</p>
|
<p className="text-sm text-text-muted">Add, edit, or disable company entities.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleAddClick}
|
onClick={handleAddClick}
|
||||||
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
||||||
>
|
>
|
||||||
@ -81,19 +81,20 @@ export default function ManageCompanies() {
|
|||||||
<CompanyTable records={localRecords} onEdit={handleEditClick} onDelete={handleDeleteClick} />
|
<CompanyTable records={localRecords} onEdit={handleEditClick} onDelete={handleDeleteClick} />
|
||||||
|
|
||||||
{isModalOpen && (
|
{isModalOpen && (
|
||||||
<CompanyFormModal
|
<CompanyFormModal
|
||||||
company={editingCompany}
|
company={editingCompany}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Delete Confirmation Modal */}
|
||||||
{deleteId && (
|
{deleteId && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
title="Delete Company?"
|
title="Delete Company?"
|
||||||
message="Are you sure you want to delete this company? This action cannot be undone."
|
message="Are you sure you want to delete this company? This action cannot be undone."
|
||||||
onConfirm={confirmDelete}
|
onConfirm={confirmDelete}
|
||||||
onClose={() => setDeleteId(null)}
|
onClose={() => setDeleteId(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -14,11 +14,11 @@ export default function ManageDepartments() {
|
|||||||
const { data: apiRecords, isLoading: depLoading, isError: depError } = useDepartments();
|
const { data: apiRecords, isLoading: depLoading, isError: depError } = useDepartments();
|
||||||
const { data: companies, isLoading: compLoading, isError: compError } = useCompanies();
|
const { data: companies, isLoading: compLoading, isError: compError } = useCompanies();
|
||||||
const { data: branches, isLoading: branchLoading, isError: branchError } = useBranches();
|
const { data: branches, isLoading: branchLoading, isError: branchError } = useBranches();
|
||||||
|
|
||||||
const [localRecords, setLocalRecords] = useState<Department[]>([]);
|
const [localRecords, setLocalRecords] = useState<Department[]>([]);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [editingDepartment, setEditingDepartment] = useState<Department | null>(null);
|
const [editingDepartment, setEditingDepartment] = useState<Department | null>(null);
|
||||||
const [deleteId, setDeleteId] = useState<string | null>(null);
|
const [deleteId, setDeleteId] = useState<string | null>(null); // New state
|
||||||
|
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-render
|
// eslint-disable-next-line react-hooks/set-state-in-render
|
||||||
@ -36,7 +36,7 @@ export default function ManageDepartments() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteClick = (id: string) => {
|
const handleDeleteClick = (id: string) => {
|
||||||
setDeleteId(id);
|
setDeleteId(id); // Open confirmation modal
|
||||||
};
|
};
|
||||||
|
|
||||||
const confirmDelete = () => {
|
const confirmDelete = () => {
|
||||||
@ -74,7 +74,7 @@ export default function ManageDepartments() {
|
|||||||
<p className="text-sm text-text-muted">Add, edit, or disable departments.</p>
|
<p className="text-sm text-text-muted">Add, edit, or disable departments.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleAddClick}
|
onClick={handleAddClick}
|
||||||
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
||||||
>
|
>
|
||||||
@ -86,21 +86,22 @@ export default function ManageDepartments() {
|
|||||||
<DepartmentTable records={localRecords} onEdit={handleEditClick} onDelete={handleDeleteClick} />
|
<DepartmentTable records={localRecords} onEdit={handleEditClick} onDelete={handleDeleteClick} />
|
||||||
|
|
||||||
{isModalOpen && (
|
{isModalOpen && (
|
||||||
<DepartmentFormModal
|
<DepartmentFormModal
|
||||||
department={editingDepartment}
|
department={editingDepartment}
|
||||||
companies={companies || []}
|
companies={companies || []}
|
||||||
branches={branches || []}
|
branches={branches || []}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Delete Confirmation Modal */}
|
||||||
{deleteId && (
|
{deleteId && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
title="Delete Department?"
|
title="Delete Department?"
|
||||||
message="Are you sure you want to delete this department? This action cannot be undone."
|
message="Are you sure you want to delete this department? This action cannot be undone."
|
||||||
onConfirm={confirmDelete}
|
onConfirm={confirmDelete}
|
||||||
onClose={() => setDeleteId(null)}
|
onClose={() => setDeleteId(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export default function ManageDesignations() {
|
|||||||
const { data: companies, isLoading: compLoading, isError: compError } = useCompanies();
|
const { data: companies, isLoading: compLoading, isError: compError } = useCompanies();
|
||||||
const { data: branches, isLoading: branchLoading, isError: branchError } = useBranches();
|
const { data: branches, isLoading: branchLoading, isError: branchError } = useBranches();
|
||||||
const { data: departments, isLoading: depLoading, isError: depError } = useDepartments();
|
const { data: departments, isLoading: depLoading, isError: depError } = useDepartments();
|
||||||
|
|
||||||
const [localRecords, setLocalRecords] = useState<Designation[]>([]);
|
const [localRecords, setLocalRecords] = useState<Designation[]>([]);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [editingDesignation, setEditingDesignation] = useState<Designation | null>(null);
|
const [editingDesignation, setEditingDesignation] = useState<Designation | null>(null);
|
||||||
@ -76,7 +76,7 @@ export default function ManageDesignations() {
|
|||||||
<p className="text-sm text-text-muted">Add, edit, or disable job designations.</p>
|
<p className="text-sm text-text-muted">Add, edit, or disable job designations.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleAddClick}
|
onClick={handleAddClick}
|
||||||
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
||||||
>
|
>
|
||||||
@ -88,22 +88,22 @@ export default function ManageDesignations() {
|
|||||||
<DesignationTable records={localRecords} onEdit={handleEditClick} onDelete={handleDeleteClick} />
|
<DesignationTable records={localRecords} onEdit={handleEditClick} onDelete={handleDeleteClick} />
|
||||||
|
|
||||||
{isModalOpen && (
|
{isModalOpen && (
|
||||||
<DesignationFormModal
|
<DesignationFormModal
|
||||||
designation={editingDesignation}
|
designation={editingDesignation}
|
||||||
companies={companies || []}
|
companies={companies || []}
|
||||||
branches={branches || []}
|
branches={branches || []}
|
||||||
departments={departments || []}
|
departments={departments || []}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{deleteId && (
|
{deleteId && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
title="Delete Designation?"
|
title="Delete Designation?"
|
||||||
message="Are you sure you want to delete this designation? This action cannot be undone."
|
message="Are you sure you want to delete this designation? This action cannot be undone."
|
||||||
onConfirm={confirmDelete}
|
onConfirm={confirmDelete}
|
||||||
onClose={() => setDeleteId(null)}
|
onClose={() => setDeleteId(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import ErrorState from '../../../components/ui/ErrorState';
|
|||||||
import ConfirmationModal from '../../../components/ui/ConfirmationModal';
|
import ConfirmationModal from '../../../components/ui/ConfirmationModal';
|
||||||
import EmployeeTable from '../components/employee/EmployeeTable';
|
import EmployeeTable from '../components/employee/EmployeeTable';
|
||||||
import EmployeeFormModal from '../components/employee/EmployeeFormModal';
|
import EmployeeFormModal from '../components/employee/EmployeeFormModal';
|
||||||
import EmployeeViewModal from '../components/employee/EmployeeViewModal';
|
import EmployeeViewModal from '../components/employee/EmployeeViewModal'; // New Import
|
||||||
import { useEmployees, type Employee } from '../api/useEmployeeData';
|
import { useEmployees, type Employee } from '../api/useEmployeeData';
|
||||||
import { useCompanies } from '../api/useCompanyData';
|
import { useCompanies } from '../api/useCompanyData';
|
||||||
import { useBranches } from '../api/useBranchData';
|
import { useBranches } from '../api/useBranchData';
|
||||||
@ -19,11 +19,11 @@ export default function ManageEmployees() {
|
|||||||
const { data: branches, isLoading: branchLoading, isError: branchError } = useBranches();
|
const { data: branches, isLoading: branchLoading, isError: branchError } = useBranches();
|
||||||
const { data: departments, isLoading: depLoading, isError: depError } = useDepartments();
|
const { data: departments, isLoading: depLoading, isError: depError } = useDepartments();
|
||||||
const { data: designations, isLoading: dsgLoading, isError: dsgError } = useDesignations();
|
const { data: designations, isLoading: dsgLoading, isError: dsgError } = useDesignations();
|
||||||
|
|
||||||
const [localRecords, setLocalRecords] = useState<Employee[]>([]);
|
const [localRecords, setLocalRecords] = useState<Employee[]>([]);
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [editingEmployee, setEditingEmployee] = useState<Employee | null>(null);
|
const [editingEmployee, setEditingEmployee] = useState<Employee | null>(null);
|
||||||
const [viewEmployee, setViewEmployee] = useState<Employee | null>(null);
|
const [viewEmployee, setViewEmployee] = useState<Employee | null>(null); // New state for viewing
|
||||||
const [deleteId, setDeleteId] = useState<string | null>(null);
|
const [deleteId, setDeleteId] = useState<string | null>(null);
|
||||||
|
|
||||||
useMemo(() => {
|
useMemo(() => {
|
||||||
@ -87,7 +87,7 @@ export default function ManageEmployees() {
|
|||||||
<p className="text-sm text-text-muted">Add, edit, or disable employee profiles.</p>
|
<p className="text-sm text-text-muted">Add, edit, or disable employee profiles.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleAddClick}
|
onClick={handleAddClick}
|
||||||
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
className="flex items-center justify-center px-4 py-2 bg-action text-white rounded-lg hover:bg-action-hover transition-colors font-medium"
|
||||||
>
|
>
|
||||||
@ -96,38 +96,39 @@ export default function ManageEmployees() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EmployeeTable
|
<EmployeeTable
|
||||||
records={localRecords}
|
records={localRecords}
|
||||||
onView={handleViewClick}
|
onView={handleViewClick}
|
||||||
onEdit={handleEditClick}
|
onEdit={handleEditClick}
|
||||||
onDelete={handleDeleteClick}
|
onDelete={handleDeleteClick}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isModalOpen && (
|
{isModalOpen && (
|
||||||
<EmployeeFormModal
|
<EmployeeFormModal
|
||||||
employee={editingEmployee}
|
employee={editingEmployee}
|
||||||
companies={companies || []}
|
companies={companies || []}
|
||||||
branches={branches || []}
|
branches={branches || []}
|
||||||
departments={departments || []}
|
departments={departments || []}
|
||||||
designations={designations || []}
|
designations={designations || []}
|
||||||
onClose={() => setIsModalOpen(false)}
|
onClose={() => setIsModalOpen(false)}
|
||||||
onSave={handleSave}
|
onSave={handleSave}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* View Modal */}
|
||||||
{viewEmployee && (
|
{viewEmployee && (
|
||||||
<EmployeeViewModal
|
<EmployeeViewModal
|
||||||
employee={viewEmployee}
|
employee={viewEmployee}
|
||||||
onClose={() => setViewEmployee(null)}
|
onClose={() => setViewEmployee(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{deleteId && (
|
{deleteId && (
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
title="Delete Employee?"
|
title="Delete Employee?"
|
||||||
message="Are you sure you want to delete this employee? This action cannot be undone."
|
message="Are you sure you want to delete this employee? This action cannot be undone."
|
||||||
onConfirm={confirmDelete}
|
onConfirm={confirmDelete}
|
||||||
onClose={() => setDeleteId(null)}
|
onClose={() => setDeleteId(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
import { Building, ScrollText, Briefcase, CalendarCheck, FileText } from 'lucide-react';
|
|
||||||
import Loader from '../../../components/ui/Loader';
|
|
||||||
import ErrorState from '../../../components/ui/ErrorState';
|
|
||||||
import { usePolicies } from '../api/usePolicyData';
|
|
||||||
|
|
||||||
const getCategoryClass = (category: string) => {
|
|
||||||
switch (category) {
|
|
||||||
case 'Leave': return 'bg-wfh-100 text-wfh-700';
|
|
||||||
case 'Attendance': return 'bg-late-100 text-late-700';
|
|
||||||
case 'General': return 'bg-present-100 text-present-700';
|
|
||||||
default: return 'bg-app-muted text-text-secondary';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getCategoryIcon = (category: string) => {
|
|
||||||
switch (category) {
|
|
||||||
case 'Leave': return <Briefcase size={12} />;
|
|
||||||
case 'Attendance': return <CalendarCheck size={12} />;
|
|
||||||
case 'General': return <FileText size={12} />;
|
|
||||||
default: return <FileText size={12} />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function ManagePolicies() {
|
|
||||||
const { data: companyPolicies, isLoading, isError } = usePolicies();
|
|
||||||
|
|
||||||
if (isLoading) return <Loader message="Loading policies..." />;
|
|
||||||
if (isError) return <ErrorState message="Failed to load policies." />;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm flex items-center space-x-3">
|
|
||||||
<div className="p-3 bg-primary/10 rounded-full">
|
|
||||||
<ScrollText className="text-primary" size={28} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2 className="text-xl font-bold text-text-primary">Policy and Rules</h2>
|
|
||||||
<p className="text-sm text-text-muted">Company-wise leave policies, LOP rules, and attendance timings.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{companyPolicies?.map((companyData) => (
|
|
||||||
<div key={companyData.companyId} className="bg-app-card p-6 rounded-lg shadow-sm">
|
|
||||||
<div className="flex items-center space-x-2 mb-6 border-b border-app-border pb-4">
|
|
||||||
<Building size={20} className="text-primary" />
|
|
||||||
<h3 className="text-lg font-semibold text-text-primary">{companyData.companyName}</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-4">
|
|
||||||
{companyData.policies.map((policy) => (
|
|
||||||
<div key={policy.id} className="border border-app-border rounded-lg p-4 hover:bg-app-muted transition-colors">
|
|
||||||
<div className="flex flex-col md:flex-row md:items-start md:justify-between mb-2">
|
|
||||||
<h4 className="text-sm font-semibold text-text-primary">{policy.title}</h4>
|
|
||||||
<span className={`flex items-center space-x-1 px-2 py-1 rounded-full text-xs font-medium w-fit ${getCategoryClass(policy.category)}`}>
|
|
||||||
{getCategoryIcon(policy.category)}
|
|
||||||
<span>{policy.category}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p className="text-sm text-text-secondary leading-relaxed">{policy.description}</p>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { Building, Network, Briefcase, Tag, Users } from 'lucide-react';
|
import { Building, Network, Briefcase, Tag, Users } from 'lucide-react';
|
||||||
import Loader from '../../../components/ui/Loader';
|
import Loader from '../../../components/ui/Loader';
|
||||||
import ErrorState from '../../../components/ui/ErrorState';
|
import ErrorState from '../../../components/ui/ErrorState';
|
||||||
import StatCard from '../../attendance/components/summary/StatCard';
|
import StatCard from '../../attendance/components/summary/StatCard'; // Reusing StatCard
|
||||||
import { useMasterDataSummary } from '../api/useMasterData';
|
import { useMasterDataSummary } from '../api/useMasterData';
|
||||||
|
|
||||||
export default function MasterDataDashboard() {
|
export default function MasterDataDashboard() {
|
||||||
@ -18,40 +18,40 @@ export default function MasterDataDashboard() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
<StatCard
|
<StatCard
|
||||||
title="Total Companies"
|
title="Total Companies"
|
||||||
value={summary?.totalCompanies || 0}
|
value={summary?.totalCompanies || 0}
|
||||||
Icon={Building}
|
Icon={Building}
|
||||||
colorClass="text-primary"
|
colorClass="text-primary"
|
||||||
bgClass="bg-primary/10"
|
bgClass="bg-primary/10"
|
||||||
/>
|
/>
|
||||||
<StatCard
|
<StatCard
|
||||||
title="Total Branches"
|
title="Total Branches"
|
||||||
value={summary?.totalBranches || 0}
|
value={summary?.totalBranches || 0}
|
||||||
Icon={Network}
|
Icon={Network}
|
||||||
colorClass="text-primary"
|
colorClass="text-primary"
|
||||||
bgClass="bg-primary/10"
|
bgClass="bg-primary/10"
|
||||||
/>
|
/>
|
||||||
<StatCard
|
<StatCard
|
||||||
title="Total Departments"
|
title="Total Departments"
|
||||||
value={summary?.totalDepartments || 0}
|
value={summary?.totalDepartments || 0}
|
||||||
Icon={Briefcase}
|
Icon={Briefcase}
|
||||||
colorClass="text-primary"
|
colorClass="text-primary"
|
||||||
bgClass="bg-primary/10"
|
bgClass="bg-primary/10"
|
||||||
/>
|
/>
|
||||||
<StatCard
|
<StatCard
|
||||||
title="Total Designations"
|
title="Total Designations"
|
||||||
value={summary?.totalDesignations || 0}
|
value={summary?.totalDesignations || 0}
|
||||||
Icon={Tag}
|
Icon={Tag}
|
||||||
colorClass="text-primary"
|
colorClass="text-primary"
|
||||||
bgClass="bg-primary/10"
|
bgClass="bg-primary/10"
|
||||||
/>
|
/>
|
||||||
<StatCard
|
<StatCard
|
||||||
title="Total Employees"
|
title="Total Employees"
|
||||||
value={summary?.totalEmployees || 0}
|
value={summary?.totalEmployees || 0}
|
||||||
Icon={Users}
|
Icon={Users}
|
||||||
colorClass="text-primary"
|
colorClass="text-primary"
|
||||||
bgClass="bg-primary/10"
|
bgClass="bg-primary/10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,6 +8,6 @@ export default function ManageAdminsRouter() {
|
|||||||
if (role === 'superadmin') {
|
if (role === 'superadmin') {
|
||||||
return <ManageAdmins />;
|
return <ManageAdmins />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: SuperAdmins only.</div>;
|
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: SuperAdmins only.</div>;
|
||||||
}
|
}
|
||||||
@ -4,7 +4,7 @@ import ManageCompanies from '../pages/ManageCompanies';
|
|||||||
import ManageBranches from '../pages/ManageBranches';
|
import ManageBranches from '../pages/ManageBranches';
|
||||||
import ManageDepartments from '../pages/ManageDepartments';
|
import ManageDepartments from '../pages/ManageDepartments';
|
||||||
import ManageDesignations from '../pages/ManageDesignations';
|
import ManageDesignations from '../pages/ManageDesignations';
|
||||||
import ManageEmployees from '../pages/ManageEmployees';
|
import ManageEmployees from '../pages/ManageEmployees'; // New Import
|
||||||
|
|
||||||
export default function MasterDataRouter() {
|
export default function MasterDataRouter() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -40,8 +40,10 @@ export const useApplyLeave = () => {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
|
// In a real backend, this would be a POST to /api/v1/leave/apply
|
||||||
mutationFn: (payload: LeaveRequestPayload) => postMockData(`leave/${role}_leave_requests`, payload),
|
mutationFn: (payload: LeaveRequestPayload) => postMockData(`leave/${role}_leave_requests`, payload),
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
|
// Refetch the summary for the specific role that just applied
|
||||||
queryClient.invalidateQueries({ queryKey: ['leaveSummary', role] });
|
queryClient.invalidateQueries({ queryKey: ['leaveSummary', role] });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -60,10 +62,28 @@ export const useLeaveHistory = () => {
|
|||||||
export const useManagerApprovals = () => {
|
export const useManagerApprovals = () => {
|
||||||
return useQuery<ManagerLeaveApproval[]>({
|
return useQuery<ManagerLeaveApproval[]>({
|
||||||
queryKey: ['leaveApprovals', 'manager'],
|
queryKey: ['leaveApprovals', 'manager'],
|
||||||
|
// Fix: Point to the exact path where you created the JSON file
|
||||||
queryFn: () => fetchMockData<ManagerLeaveApproval[]>('approvals/manager_pending_leaves.json'),
|
queryFn: () => fetchMockData<ManagerLeaveApproval[]>('approvals/manager_pending_leaves.json'),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const useUpdateLeaveApproval = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
|
// In the future: axios.put('/api/v1/leave/approvals/:id', { status })
|
||||||
|
mutationFn: async ({ id, status }: { id: string; status: ApprovalStatus }) => {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500)); // simulate network
|
||||||
|
console.log(`[Mock PUT] Updated leave ${id} to ${status}`);
|
||||||
|
return { id, status };
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
// Refetch the list to show the updated status
|
||||||
|
queryClient.invalidateQueries({ queryKey: ['leaveApprovals', 'manager'] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useLeaveApplications = () => {
|
export const useLeaveApplications = () => {
|
||||||
return useQuery<LeaveApplicationSummary[]>({
|
return useQuery<LeaveApplicationSummary[]>({
|
||||||
queryKey: ['leaveApplications'],
|
queryKey: ['leaveApplications'],
|
||||||
@ -76,33 +96,4 @@ export const useLeaveLedger = () => {
|
|||||||
queryKey: ['leaveLedger'],
|
queryKey: ['leaveLedger'],
|
||||||
queryFn: () => fetchMockData<LeaveLedgerRecord[]>('leave/leave_ledger.json'),
|
queryFn: () => fetchMockData<LeaveLedgerRecord[]>('leave/leave_ledger.json'),
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
export const useAdminApprovals = () => {
|
|
||||||
return useQuery<ManagerLeaveApproval[]>({
|
|
||||||
queryKey: ['leaveApprovals', 'admin'],
|
|
||||||
queryFn: () => fetchMockData<ManagerLeaveApproval[]>('approvals/admin_pending_leaves.json'),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useSuperAdminApprovals = () => {
|
|
||||||
return useQuery<ManagerLeaveApproval[]>({
|
|
||||||
queryKey: ['leaveApprovals', 'superadmin'],
|
|
||||||
queryFn: () => fetchMockData<ManagerLeaveApproval[]>('approvals/superadmin_pending_leaves.json'),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useUpdateLeaveApproval = () => {
|
|
||||||
const queryClient = useQueryClient();
|
|
||||||
return useMutation({
|
|
||||||
mutationFn: async ({ id, status }: { id: string; status: ApprovalStatus }) => {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
||||||
console.log(`[Mock PUT] Updated leave ${id} to ${status}`);
|
|
||||||
return { id, status };
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
queryClient.invalidateQueries({ queryKey: ['leaveApprovals'] });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
@ -29,12 +29,14 @@ export default function ApprovalModal({ record, onClose, onAction }: ApprovalMod
|
|||||||
</button>
|
</button>
|
||||||
<h3 className="text-xl font-bold text-text-primary mb-1">Leave Application Details</h3>
|
<h3 className="text-xl font-bold text-text-primary mb-1">Leave Application Details</h3>
|
||||||
<p className="text-sm text-text-muted mb-6">Application ID: {record.id}</p>
|
<p className="text-sm text-text-muted mb-6">Application ID: {record.id}</p>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
{/* Separated Employee ID */}
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
<div className="flex justify-between border-b border-app-border pb-2">
|
||||||
<span className="text-sm text-text-muted">Employee ID</span>
|
<span className="text-sm text-text-muted">Employee ID</span>
|
||||||
<span className="text-sm font-medium text-text-primary">{record.employeeId}</span>
|
<span className="text-sm font-medium text-text-primary">{record.employeeId}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Separated Employee Name */}
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
<div className="flex justify-between border-b border-app-border pb-2">
|
||||||
<span className="text-sm text-text-muted">Employee Name</span>
|
<span className="text-sm text-text-muted">Employee Name</span>
|
||||||
<span className="text-sm font-medium text-text-primary">{record.employeeName}</span>
|
<span className="text-sm font-medium text-text-primary">{record.employeeName}</span>
|
||||||
@ -47,7 +49,7 @@ export default function ApprovalModal({ record, onClose, onAction }: ApprovalMod
|
|||||||
<span className="text-sm text-text-muted">Application Date</span>
|
<span className="text-sm text-text-muted">Application Date</span>
|
||||||
<span className="text-sm font-medium text-text-primary">{formatReadableDate(record.applicationDate)}</span>
|
<span className="text-sm font-medium text-text-primary">{formatReadableDate(record.applicationDate)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
<div className="flex justify-between border-b border-app-border pb-2">
|
||||||
<span className="text-sm text-text-muted">Duration</span>
|
<span className="text-sm text-text-muted">Duration</span>
|
||||||
<span className="text-sm font-medium text-text-primary text-right">
|
<span className="text-sm font-medium text-text-primary text-right">
|
||||||
@ -55,7 +57,7 @@ export default function ApprovalModal({ record, onClose, onAction }: ApprovalMod
|
|||||||
<span className="text-text-muted">({record.leaveDays} Days)</span>
|
<span className="text-text-muted">({record.leaveDays} Days)</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
<div className="flex justify-between border-b border-app-border pb-2">
|
||||||
<span className="text-sm text-text-muted">Current Status</span>
|
<span className="text-sm text-text-muted">Current Status</span>
|
||||||
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getStatusClass(record.status)}`}>{record.status}</span>
|
<span className={`px-2 py-1 rounded-full text-xs font-medium ${getStatusClass(record.status)}`}>{record.status}</span>
|
||||||
@ -69,14 +71,14 @@ export default function ApprovalModal({ record, onClose, onAction }: ApprovalMod
|
|||||||
<div className="flex justify-end space-x-3 mt-6">
|
<div className="flex justify-end space-x-3 mt-6">
|
||||||
{record.status === 'Pending' ? (
|
{record.status === 'Pending' ? (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={() => onAction(record.id, 'Rejected')}
|
onClick={() => onAction(record.id, 'Rejected')}
|
||||||
className="flex items-center px-4 py-2 bg-absent-500 text-white rounded-lg hover:bg-absent-600 transition-colors text-sm font-medium"
|
className="flex items-center px-4 py-2 bg-absent-500 text-white rounded-lg hover:bg-absent-600 transition-colors text-sm font-medium"
|
||||||
>
|
>
|
||||||
<Ban size={16} className="mr-2" /> Reject
|
<Ban size={16} className="mr-2" /> Reject
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => onAction(record.id, 'Approved')}
|
onClick={() => onAction(record.id, 'Approved')}
|
||||||
className="flex items-center px-4 py-2 bg-present-600 text-white rounded-lg hover:bg-present-700 transition-colors text-sm font-medium"
|
className="flex items-center px-4 py-2 bg-present-600 text-white rounded-lg hover:bg-present-700 transition-colors text-sm font-medium"
|
||||||
>
|
>
|
||||||
<Check size={16} className="mr-2" /> Approve
|
<Check size={16} className="mr-2" /> Approve
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export default function ApprovalTable({ records, onAction }: ApprovalTableProps)
|
|||||||
const [viewRecord, setViewRecord] = useState<ManagerLeaveApproval | null>(null);
|
const [viewRecord, setViewRecord] = useState<ManagerLeaveApproval | null>(null);
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(10); // Default 10 entries per page
|
||||||
|
|
||||||
// Default sort: applicationDate descending (last applied first)
|
// Default sort: applicationDate descending (last applied first)
|
||||||
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
||||||
@ -190,7 +190,7 @@ export default function ApprovalTable({ records, onAction }: ApprovalTableProps)
|
|||||||
onClose={() => setViewRecord(null)}
|
onClose={() => setViewRecord(null)}
|
||||||
onAction={(id, status) => {
|
onAction={(id, status) => {
|
||||||
onAction(id, status);
|
onAction(id, status);
|
||||||
setViewRecord(null);
|
setViewRecord(null); // Close modal immediately on action
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -19,16 +19,16 @@ const calculateLeaveDays = (from: string, fromMode: DayMode, to: string, toMode:
|
|||||||
return days;
|
return days;
|
||||||
};
|
};
|
||||||
|
|
||||||
// logic: +1 day, if Sunday -> Monday, with safe local formatting
|
// Fixed logic: +1 day, if Sunday -> Monday, with safe local formatting
|
||||||
const calculateResumptionDate = (to: string) => {
|
const calculateResumptionDate = (to: string) => {
|
||||||
if (!to) return '';
|
if (!to) return '';
|
||||||
const date = new Date(to + 'T00:00:00');
|
const date = new Date(to + 'T00:00:00');
|
||||||
date.setDate(date.getDate() + 1); // Add 1 day
|
date.setDate(date.getDate() + 1); // Add 1 day
|
||||||
|
|
||||||
if (date.getDay() === 0) { // 0 is Sunday
|
if (date.getDay() === 0) { // 0 is Sunday
|
||||||
date.setDate(date.getDate() + 1); // Make it Monday
|
date.setDate(date.getDate() + 1); // Make it Monday
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format safely in local time
|
// Format safely in local time
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear();
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
@ -89,7 +89,7 @@ export default function ApplyLeaveForm({ summaryData }: ApplyLeaveFormProps) {
|
|||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm font-medium text-primary bg-primary/10 px-3 py-1 rounded-full">{selectedLeaveType}</span>
|
<span className="text-sm font-medium text-primary bg-primary/10 px-3 py-1 rounded-full">{selectedLeaveType}</span>
|
||||||
<button type="button" onClick={() => setShowForm(false)} className="flex items-center px-4 py-1 bg-absent-500 text-white rounded-lg hover:bg-absent-600 transition-colors text-sm ">Cancel</button>
|
<button type="button" onClick={() => setShowForm(false)} className="text-text-muted hover:text-text-primary text-sm">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-text-secondary mb-1">From Date</label>
|
<label className="block text-sm font-medium text-text-secondary mb-1">From Date</label>
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export default function LeaveApplicationModal({ record, onClose }: LeaveApplicat
|
|||||||
<X size={24} />
|
<X size={24} />
|
||||||
</button>
|
</button>
|
||||||
<h3 className="text-xl font-bold text-text-primary mb-6">Leave Application Details</h3>
|
<h3 className="text-xl font-bold text-text-primary mb-6">Leave Application Details</h3>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
<div className="flex justify-between border-b border-app-border pb-2">
|
||||||
<span className="text-sm text-text-muted">Application ID</span>
|
<span className="text-sm text-text-muted">Application ID</span>
|
||||||
|
|||||||
@ -25,14 +25,14 @@ export default function LeaveApplicationsTable({ records }: LeaveApplicationsTab
|
|||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [pageSize, setPageSize] = useState(10);
|
const [pageSize, setPageSize] = useState(10);
|
||||||
|
|
||||||
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
const [sortConfig, setSortConfig] = useState<{ key: SortKey; direction: SortDirection }>({
|
||||||
key: 'employeeName', direction: 'ascending'
|
key: 'employeeName', direction: 'ascending'
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
if (!searchQuery) return records;
|
if (!searchQuery) return records;
|
||||||
return records.filter(record =>
|
return records.filter(record =>
|
||||||
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.id.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.employeeId.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.employeeId.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
record.employeeName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
record.employeeName.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
@ -95,7 +95,7 @@ export default function LeaveApplicationsTable({ records }: LeaveApplicationsTab
|
|||||||
|
|
||||||
const formatHeader = (key: string) => {
|
const formatHeader = (key: string) => {
|
||||||
if (key === 'id') return 'App ID';
|
if (key === 'id') return 'App ID';
|
||||||
if (key === 'employeeId') return 'Emp CODE';
|
if (key === 'employeeId') return 'Emp ID';
|
||||||
if (key === 'employeeName') return 'Employee Name';
|
if (key === 'employeeName') return 'Employee Name';
|
||||||
if (key === 'managerName') return 'Manager Name';
|
if (key === 'managerName') return 'Manager Name';
|
||||||
if (key === 'leaveType') return 'Leave Type';
|
if (key === 'leaveType') return 'Leave Type';
|
||||||
@ -190,9 +190,9 @@ export default function LeaveApplicationsTable({ records }: LeaveApplicationsTab
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{viewRecord && (
|
{viewRecord && (
|
||||||
<LeaveApplicationModal
|
<LeaveApplicationModal
|
||||||
record={viewRecord}
|
record={viewRecord}
|
||||||
onClose={() => setViewRecord(null)}
|
onClose={() => setViewRecord(null)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -11,6 +11,7 @@ interface LeaveHistoryTableProps {
|
|||||||
|
|
||||||
type SortDirection = 'ascending' | 'descending';
|
type SortDirection = 'ascending' | 'descending';
|
||||||
|
|
||||||
|
// Updated to handle empty strings safely
|
||||||
const getDaysDifference = (start: string, end: string) => {
|
const getDaysDifference = (start: string, end: string) => {
|
||||||
if (!start || !end) return 0;
|
if (!start || !end) return 0;
|
||||||
const startDate = new Date(start + 'T00:00:00');
|
const startDate = new Date(start + 'T00:00:00');
|
||||||
@ -20,9 +21,10 @@ const getDaysDifference = (start: string, end: string) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function LeaveHistoryTable({ records }: LeaveHistoryTableProps) {
|
export default function LeaveHistoryTable({ records }: LeaveHistoryTableProps) {
|
||||||
|
// Default to empty strings to show all records initially
|
||||||
const [fromDate, setFromDate] = useState('');
|
const [fromDate, setFromDate] = useState('');
|
||||||
const [toDate, setToDate] = useState('');
|
const [toDate, setToDate] = useState('');
|
||||||
|
|
||||||
const [sortConfig, setSortConfig] = useState<{ key: LeaveSortKey; direction: SortDirection }>({
|
const [sortConfig, setSortConfig] = useState<{ key: LeaveSortKey; direction: SortDirection }>({
|
||||||
key: 'applicationDate',
|
key: 'applicationDate',
|
||||||
direction: 'descending'
|
direction: 'descending'
|
||||||
@ -33,11 +35,13 @@ export default function LeaveHistoryTable({ records }: LeaveHistoryTableProps) {
|
|||||||
const [viewRecord, setViewRecord] = useState<LeaveHistoryRecord | null>(null);
|
const [viewRecord, setViewRecord] = useState<LeaveHistoryRecord | null>(null);
|
||||||
const [withdrawRecord, setWithdrawRecord] = useState<LeaveHistoryRecord | null>(null);
|
const [withdrawRecord, setWithdrawRecord] = useState<LeaveHistoryRecord | null>(null);
|
||||||
|
|
||||||
|
// Use an overrides object to handle instant UI updates for withdrawals
|
||||||
const [localOverrides, setLocalOverrides] = useState<Record<string, LeaveHistoryRecord>>({});
|
const [localOverrides, setLocalOverrides] = useState<Record<string, LeaveHistoryRecord>>({});
|
||||||
const combinedRecords = useMemo(() => {
|
const combinedRecords = useMemo(() => {
|
||||||
return records.map(r => localOverrides[r.id] || r);
|
return records.map(r => localOverrides[r.id] || r);
|
||||||
}, [records, localOverrides]);
|
}, [records, localOverrides]);
|
||||||
|
|
||||||
|
// Filter specifically by applicationDate
|
||||||
const filteredData = useMemo(() => {
|
const filteredData = useMemo(() => {
|
||||||
return combinedRecords.filter((record) => {
|
return combinedRecords.filter((record) => {
|
||||||
const isAfterFrom = fromDate ? record.applicationDate >= fromDate : true;
|
const isAfterFrom = fromDate ? record.applicationDate >= fromDate : true;
|
||||||
@ -102,14 +106,16 @@ export default function LeaveHistoryTable({ records }: LeaveHistoryTableProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleWithdraw = (id: string) => {
|
const handleWithdraw = (id: string) => {
|
||||||
|
// Find the current record (either from original array or overrides)
|
||||||
const targetRecord = combinedRecords.find(r => r.id === id);
|
const targetRecord = combinedRecords.find(r => r.id === id);
|
||||||
if (!targetRecord) return;
|
if (!targetRecord) return;
|
||||||
|
|
||||||
|
// Update local override for instant UI feedback - changes status, does NOT delete
|
||||||
setLocalOverrides(prev => ({
|
setLocalOverrides(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
[id]: { ...targetRecord, status: 'Withdrawn' }
|
[id]: { ...targetRecord, status: 'Withdrawn' }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
toast.success("Leave application withdrawn successfully.");
|
toast.success("Leave application withdrawn successfully.");
|
||||||
setWithdrawRecord(null);
|
setWithdrawRecord(null);
|
||||||
setViewRecord(null);
|
setViewRecord(null);
|
||||||
@ -195,6 +201,7 @@ export default function LeaveHistoryTable({ records }: LeaveHistoryTableProps) {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
<div className="flex flex-col md:flex-row md:items-center justify-between mt-4 pt-4 border-t border-app-border gap-4">
|
<div className="flex flex-col md:flex-row md:items-center justify-between mt-4 pt-4 border-t border-app-border gap-4">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<span className="text-sm text-text-muted">Show</span>
|
<span className="text-sm text-text-muted">Show</span>
|
||||||
@ -221,22 +228,23 @@ export default function LeaveHistoryTable({ records }: LeaveHistoryTableProps) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Render Separated Modals */}
|
||||||
{viewRecord && (
|
{viewRecord && (
|
||||||
<ViewLeaveModal
|
<ViewLeaveModal
|
||||||
record={viewRecord}
|
record={viewRecord}
|
||||||
onClose={() => setViewRecord(null)}
|
onClose={() => setViewRecord(null)}
|
||||||
onWithdraw={(rec) => {
|
onWithdraw={(rec) => {
|
||||||
setViewRecord(null);
|
setViewRecord(null);
|
||||||
setWithdrawRecord(rec);
|
setWithdrawRecord(rec);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{withdrawRecord && (
|
{withdrawRecord && (
|
||||||
<WithdrawLeaveModal
|
<WithdrawLeaveModal
|
||||||
record={withdrawRecord}
|
record={withdrawRecord}
|
||||||
onClose={() => setWithdrawRecord(null)}
|
onClose={() => setWithdrawRecord(null)}
|
||||||
onConfirm={handleWithdraw}
|
onConfirm={handleWithdraw}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export default function ViewLeaveModal({ record, onClose, onWithdraw }: ViewLeav
|
|||||||
</button>
|
</button>
|
||||||
<h3 className="text-xl font-bold text-text-primary mb-1">Leave Details</h3>
|
<h3 className="text-xl font-bold text-text-primary mb-1">Leave Details</h3>
|
||||||
<p className="text-sm text-text-muted mb-6">Application ID: {record.id}</p>
|
<p className="text-sm text-text-muted mb-6">Application ID: {record.id}</p>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex justify-between border-b border-app-border pb-2">
|
<div className="flex justify-between border-b border-app-border pb-2">
|
||||||
<span className="text-sm text-text-muted">Leave Type</span>
|
<span className="text-sm text-text-muted">Leave Type</span>
|
||||||
|
|||||||
@ -31,33 +31,33 @@ export default function LedgerFilters({ filters, onFilterChange, onSearch }: Led
|
|||||||
<form onSubmit={onSearch} className="bg-app-card p-4 rounded-lg shadow-sm grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4 items-end">
|
<form onSubmit={onSearch} className="bg-app-card p-4 rounded-lg shadow-sm grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4 items-end">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<label className={labelClass}>Employee ID</label>
|
<label className={labelClass}>Employee ID</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="empId"
|
name="empId"
|
||||||
value={filters.empId}
|
value={filters.empId}
|
||||||
onChange={(e) => onFilterChange({ ...filters, empId: e.target.value })}
|
onChange={(e) => onFilterChange({ ...filters, empId: e.target.value })}
|
||||||
className={inputClass}
|
className={inputClass}
|
||||||
placeholder="e.g. EMP001"
|
placeholder="e.g. EMP001"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<label className={labelClass}>Employee Name</label>
|
<label className={labelClass}>Employee Name</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="empName"
|
name="empName"
|
||||||
value={filters.empName}
|
value={filters.empName}
|
||||||
onChange={(e) => onFilterChange({ ...filters, empName: e.target.value })}
|
onChange={(e) => onFilterChange({ ...filters, empName: e.target.value })}
|
||||||
className={inputClass}
|
className={inputClass}
|
||||||
placeholder="e.g. Alice"
|
placeholder="e.g. Alice"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<label className={labelClass}>Month</label>
|
<label className={labelClass}>Month</label>
|
||||||
<select
|
<select
|
||||||
value={filters.month}
|
value={filters.month}
|
||||||
onChange={(e) => onFilterChange({ ...filters, month: Number(e.target.value) })}
|
onChange={(e) => onFilterChange({ ...filters, month: Number(e.target.value) })}
|
||||||
className={inputClass}
|
className={inputClass}
|
||||||
>
|
>
|
||||||
{monthsArray.map(m => <option key={m.value} value={m.value}>{m.label}</option>)}
|
{monthsArray.map(m => <option key={m.value} value={m.value}>{m.label}</option>)}
|
||||||
@ -66,17 +66,17 @@ export default function LedgerFilters({ filters, onFilterChange, onSearch }: Led
|
|||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<label className={labelClass}>Year</label>
|
<label className={labelClass}>Year</label>
|
||||||
<select
|
<select
|
||||||
value={filters.year}
|
value={filters.year}
|
||||||
onChange={(e) => onFilterChange({ ...filters, year: Number(e.target.value) })}
|
onChange={(e) => onFilterChange({ ...filters, year: Number(e.target.value) })}
|
||||||
className={inputClass}
|
className={inputClass}
|
||||||
>
|
>
|
||||||
{yearsArray.map(y => <option key={y} value={y}>{y}</option>)}
|
{yearsArray.map(y => <option key={y} value={y}>{y}</option>)}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="flex items-center justify-center px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary-hover transition-colors font-medium h-[38px]"
|
className="flex items-center justify-center px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary-hover transition-colors font-medium h-[38px]"
|
||||||
>
|
>
|
||||||
<Search size={18} className="mr-2" />
|
<Search size={18} className="mr-2" />
|
||||||
|
|||||||
@ -8,6 +8,7 @@ interface LedgerTableProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function LedgerTable({ records, isSearched, month, year }: LedgerTableProps) {
|
export default function LedgerTable({ records, isSearched, month, year }: LedgerTableProps) {
|
||||||
|
// Helper to format the month number into a readable name (e.g., "July 2026")
|
||||||
const formatMonthYear = () => {
|
const formatMonthYear = () => {
|
||||||
const monthName = new Date(year, month - 1).toLocaleString('default', { month: 'long' });
|
const monthName = new Date(year, month - 1).toLocaleString('default', { month: 'long' });
|
||||||
return `${monthName} ${year}`;
|
return `${monthName} ${year}`;
|
||||||
@ -24,8 +25,9 @@ export default function LedgerTable({ records, isSearched, month, year }: Ledger
|
|||||||
<table className="min-w-full divide-y divide-app-border">
|
<table className="min-w-full divide-y divide-app-border">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Emp CODE</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Emp ID</th>
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Employee Name</th>
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">Employee Name</th>
|
||||||
|
{/* Dynamically display Month/Year in the headers */}
|
||||||
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">
|
<th className="px-4 py-3 text-left text-xs font-semibold text-text-muted uppercase">
|
||||||
Opening Balance ({formatMonthYear()})
|
Opening Balance ({formatMonthYear()})
|
||||||
</th>
|
</th>
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
import { toast } from 'sonner';
|
|
||||||
import Loader from '../../../../components/ui/Loader';
|
|
||||||
import ErrorState from '../../../../components/ui/ErrorState';
|
|
||||||
import ApprovalTable from '../../components/approval/ApprovalTable';
|
|
||||||
import { useAdminApprovals, useUpdateLeaveApproval } from '../../api/useLeaveData';
|
|
||||||
|
|
||||||
export default function AdminApproval() {
|
|
||||||
const { data: approvals, isLoading, isError } = useAdminApprovals();
|
|
||||||
const { mutate } = useUpdateLeaveApproval();
|
|
||||||
|
|
||||||
const handleAction = (id: string, status: 'Approved' | 'Rejected') => {
|
|
||||||
mutate({ id, status }, {
|
|
||||||
onSuccess: () => {
|
|
||||||
toast.success(`Leave request ${status.toLowerCase()} successfully.`);
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
toast.error("Failed to update leave status. Please try again.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoading) return <Loader message="Loading manager leave requests..." />;
|
|
||||||
if (isError) return <ErrorState message="Failed to load leave requests." />;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{approvals && <ApprovalTable records={approvals} onAction={handleAction} />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
import { toast } from 'sonner';
|
|
||||||
import Loader from '../../../../components/ui/Loader';
|
|
||||||
import ErrorState from '../../../../components/ui/ErrorState';
|
|
||||||
import ApprovalTable from '../../components/approval/ApprovalTable';
|
|
||||||
import { useSuperAdminApprovals, useUpdateLeaveApproval } from '../../api/useLeaveData';
|
|
||||||
|
|
||||||
export default function SuperAdminApproval() {
|
|
||||||
const { data: approvals, isLoading, isError } = useSuperAdminApprovals();
|
|
||||||
const { mutate } = useUpdateLeaveApproval();
|
|
||||||
|
|
||||||
const handleAction = (id: string, status: 'Approved' | 'Rejected') => {
|
|
||||||
mutate({ id, status }, {
|
|
||||||
onSuccess: () => {
|
|
||||||
toast.success(`Leave request ${status.toLowerCase()} successfully.`);
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
toast.error("Failed to update leave status. Please try again.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isLoading) return <Loader message="Loading admin leave requests..." />;
|
|
||||||
if (isError) return <ErrorState message="Failed to load leave requests." />;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{approvals && <ApprovalTable records={approvals} onAction={handleAction} />}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -14,11 +14,16 @@ export default function AdminLeave() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
|
|
||||||
|
{/* Top Section: Summary & Apply Form */}
|
||||||
<div className="flex flex-col lg:flex-row gap-6">
|
<div className="flex flex-col lg:flex-row gap-6">
|
||||||
{summaryData && <LeaveSummaryTable data={summaryData} />}
|
{summaryData && <LeaveSummaryTable data={summaryData} />}
|
||||||
{summaryData && <ApplyLeaveForm summaryData={summaryData} />}
|
{summaryData && <ApplyLeaveForm summaryData={summaryData} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom Section: Leave History Table */}
|
||||||
{historyData && <LeaveHistoryTable records={historyData} />}
|
{historyData && <LeaveHistoryTable records={historyData} />}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -14,11 +14,16 @@ export default function EmployeeLeave() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
|
|
||||||
|
{/* Top Section: Summary & Apply Form */}
|
||||||
<div className="flex flex-col lg:flex-row gap-6">
|
<div className="flex flex-col lg:flex-row gap-6">
|
||||||
{summaryData && <LeaveSummaryTable data={summaryData} />}
|
{summaryData && <LeaveSummaryTable data={summaryData} />}
|
||||||
{summaryData && <ApplyLeaveForm summaryData={summaryData} />}
|
{summaryData && <ApplyLeaveForm summaryData={summaryData} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom Section: Leave History Table */}
|
||||||
{historyData && <LeaveHistoryTable records={historyData} />}
|
{historyData && <LeaveHistoryTable records={historyData} />}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -9,7 +9,7 @@ import LedgerTable from '../../components/leave/ledger/LedgerTable';
|
|||||||
|
|
||||||
export default function LedgerReport() {
|
export default function LedgerReport() {
|
||||||
const { data: ledgerData, isLoading, isError } = useLeaveLedger();
|
const { data: ledgerData, isLoading, isError } = useLeaveLedger();
|
||||||
|
|
||||||
// Initialize filters with current month and year
|
// Initialize filters with current month and year
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const [filters, setFilters] = useState<LedgerFilterValues>({
|
const [filters, setFilters] = useState<LedgerFilterValues>({
|
||||||
@ -18,7 +18,7 @@ export default function LedgerReport() {
|
|||||||
month: today.getMonth() + 1,
|
month: today.getMonth() + 1,
|
||||||
year: today.getFullYear(),
|
year: today.getFullYear(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Search Trigger State
|
// Search Trigger State
|
||||||
const [isSearched, setIsSearched] = useState(false);
|
const [isSearched, setIsSearched] = useState(false);
|
||||||
const [searchResults, setSearchResults] = useState<LeaveLedgerRecord[]>([]);
|
const [searchResults, setSearchResults] = useState<LeaveLedgerRecord[]>([]);
|
||||||
@ -32,7 +32,7 @@ export default function LedgerReport() {
|
|||||||
const matchesEmpName = filters.empName ? record.employeeName.toLowerCase().includes(filters.empName.toLowerCase()) : true;
|
const matchesEmpName = filters.empName ? record.employeeName.toLowerCase().includes(filters.empName.toLowerCase()) : true;
|
||||||
const matchesMonth = record.month === Number(filters.month);
|
const matchesMonth = record.month === Number(filters.month);
|
||||||
const matchesYear = record.year === Number(filters.year);
|
const matchesYear = record.year === Number(filters.year);
|
||||||
|
|
||||||
return matchesEmpId && matchesEmpName && matchesMonth && matchesYear;
|
return matchesEmpId && matchesEmpName && matchesMonth && matchesYear;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -45,6 +45,7 @@ export default function LedgerReport() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
{/* Header */}
|
||||||
<div className="bg-app-card p-6 rounded-lg shadow-sm flex items-center space-x-3">
|
<div className="bg-app-card p-6 rounded-lg shadow-sm flex items-center space-x-3">
|
||||||
<div className="p-3 bg-primary/10 rounded-full">
|
<div className="p-3 bg-primary/10 rounded-full">
|
||||||
<FileText className="text-primary" size={28} />
|
<FileText className="text-primary" size={28} />
|
||||||
@ -55,17 +56,19 @@ export default function LedgerReport() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LedgerFilters
|
{/* Reusable Filters Component */}
|
||||||
filters={filters}
|
<LedgerFilters
|
||||||
onFilterChange={setFilters}
|
filters={filters}
|
||||||
onSearch={handleSearch}
|
onFilterChange={setFilters}
|
||||||
|
onSearch={handleSearch}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LedgerTable
|
{/* Reusable Table Component */}
|
||||||
records={searchResults}
|
<LedgerTable
|
||||||
isSearched={isSearched}
|
records={searchResults}
|
||||||
month={filters.month}
|
isSearched={isSearched}
|
||||||
year={filters.year}
|
month={filters.month}
|
||||||
|
year={filters.year}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -14,11 +14,16 @@ export default function ManagerLeave() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
|
|
||||||
|
{/* Top Section: Summary & Apply Form */}
|
||||||
<div className="flex flex-col lg:flex-row gap-6">
|
<div className="flex flex-col lg:flex-row gap-6">
|
||||||
{summaryData && <LeaveSummaryTable data={summaryData} />}
|
{summaryData && <LeaveSummaryTable data={summaryData} />}
|
||||||
{summaryData && <ApplyLeaveForm summaryData={summaryData} />}
|
{summaryData && <ApplyLeaveForm summaryData={summaryData} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom Section: Leave History Table */}
|
||||||
{historyData && <LeaveHistoryTable records={historyData} />}
|
{historyData && <LeaveHistoryTable records={historyData} />}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -14,11 +14,16 @@ export default function SuperAdminLeaveLeave() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
|
|
||||||
|
{/* Top Section: Summary & Apply Form */}
|
||||||
<div className="flex flex-col lg:flex-row gap-6">
|
<div className="flex flex-col lg:flex-row gap-6">
|
||||||
{summaryData && <LeaveSummaryTable data={summaryData} />}
|
{summaryData && <LeaveSummaryTable data={summaryData} />}
|
||||||
{summaryData && <ApplyLeaveForm summaryData={summaryData} />}
|
{summaryData && <ApplyLeaveForm summaryData={summaryData} />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Bottom Section: Leave History Table */}
|
||||||
{historyData && <LeaveHistoryTable records={historyData} />}
|
{historyData && <LeaveHistoryTable records={historyData} />}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,15 +1,13 @@
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import type { RootState } from '../../../store/store';
|
import type { RootState } from '../../../store/store';
|
||||||
import ManagerApproval from '../pages/approval/ManagerApproval';
|
import ManagerApproval from '../pages/approval/ManagerApproval';
|
||||||
import AdminApproval from '../pages/approval/AdminApproval';
|
|
||||||
import SuperAdminApproval from '../pages/approval/SuperAdminApproval';
|
|
||||||
|
|
||||||
export default function ApprovalRouter() {
|
export default function ApprovalRouter() {
|
||||||
const role = useSelector((state: RootState) => state.role.currentRole);
|
const role = useSelector((state: RootState) => state.role.currentRole);
|
||||||
|
|
||||||
if (role === 'manager') return <ManagerApproval />;
|
if (role === 'manager') {
|
||||||
if (role === 'admin') return <AdminApproval />;
|
return <ManagerApproval />;
|
||||||
if (role === 'superadmin') return <SuperAdminApproval />;
|
}
|
||||||
|
|
||||||
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: Managers and Admins only.</div>;
|
return <div className="bg-app-card p-6 rounded-lg shadow text-absent-500">Access Denied: Managers only.</div>;
|
||||||
}
|
}
|
||||||
@ -7,6 +7,9 @@ import SuperAdminLeaveLeave from '../pages/leave/SuperAdminLeave';
|
|||||||
|
|
||||||
export default function LeaveRouter() {
|
export default function LeaveRouter() {
|
||||||
const role = useSelector((state: RootState) => state.role.currentRole);
|
const role = useSelector((state: RootState) => state.role.currentRole);
|
||||||
|
|
||||||
|
// Since the hooks dynamically fetch based on role,
|
||||||
|
// the EmployeeLeave component works perfectly for all roles!
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case 'employee': return <EmployeeLeave />;
|
case 'employee': return <EmployeeLeave />;
|
||||||
case 'manager': return <ManagerLeave />;
|
case 'manager': return <ManagerLeave />;
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||||
import LeaveApplications from '../pages/leave/LeaveApplications';
|
import LeaveApplications from '../pages/leave/LeaveApplications';
|
||||||
import LedgerReport from '../pages/leave/LedgerReport';
|
import LedgerReport from '../pages/leave/LedgerReport'; // New Import
|
||||||
|
|
||||||
export default function LeaveSummaryRouter() {
|
export default function LeaveSummaryRouter() {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Navigate to="/leave-summary/applications" replace />} />
|
<Route path="/" element={<Navigate to="/leave-summary/applications" replace />} />
|
||||||
<Route path="/applications" element={<LeaveApplications />} />
|
<Route path="/applications" element={<LeaveApplications />} />
|
||||||
<Route path="/ledger" element={<LedgerReport />} />
|
<Route path="/ledger" element={<LedgerReport />} /> {/* Updated Route */}
|
||||||
</Routes>
|
</Routes>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user