feat: add byMonth method to AttendanceController and corresponding service logic
This commit is contained in:
parent
86cc8637bd
commit
4758b19d2a
@ -52,4 +52,12 @@ public function byDate(Request $request): ?array
|
|||||||
|
|
||||||
return $this->service->getByDate($date);
|
return $this->service->getByDate($date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function byMonth(Request $request): array
|
||||||
|
{
|
||||||
|
$year = $request->integer('year', now()->year);
|
||||||
|
$month = $request->integer('month', now()->month);
|
||||||
|
|
||||||
|
return $this->service->getByMonthData($year, $month);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@ public function __construct(
|
|||||||
public function getIndexData(int $year, int $month): array
|
public function getIndexData(int $year, int $month): array
|
||||||
{
|
{
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
$isAdmin = self::hasAnyRole([Role::DEVELOPER, Role::OWNER]);
|
$isAdmin = self::hasAnyRole([Role::DEVELOPER, Role::OWNER, Role::ADMIN_TOKO, Role::DIREKTUR]);
|
||||||
$hrSettings = app(HRSettings::class);
|
$hrSettings = app(HRSettings::class);
|
||||||
|
|
||||||
$employeeId = $isAdmin ? null : $user->employee?->id;
|
$employeeId = $isAdmin ? null : $user->employee?->id;
|
||||||
@ -72,6 +72,18 @@ public function getByDate(string $date): ?array
|
|||||||
return $attendance ? $this->formatAttendance($attendance) : null;
|
return $attendance ? $this->formatAttendance($attendance) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getByMonthData(int $year, int $month): array
|
||||||
|
{
|
||||||
|
$employeeId = null;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'attendances' => $this->getByMonth($year, $month, $employeeId),
|
||||||
|
'leaves' => $this->getLeavesByMonth($year, $month, $employeeId),
|
||||||
|
'employees' => $this->employeeService->getAll(),
|
||||||
|
'monthStats' => $this->getMonthStats($year, $month, $employeeId),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function getToday(): ?array
|
public function getToday(): ?array
|
||||||
{
|
{
|
||||||
return $this->getByDate(now()->toDateString());
|
return $this->getByDate(now()->toDateString());
|
||||||
@ -252,18 +264,31 @@ private function getMonthStatsForEmployee(int $year, int $month, Carbon $startOf
|
|||||||
|
|
||||||
private function getMonthStatsForAll(int $year, int $month, Carbon $startOfMonth, Carbon $statEnd, int $workingDays): array
|
private function getMonthStatsForAll(int $year, int $month, Carbon $startOfMonth, Carbon $statEnd, int $workingDays): array
|
||||||
{
|
{
|
||||||
$totalEmployees = Employee::whereHas('user', fn ($q) => $q->where('is_active', true))->count();
|
$totalEmployees = Employee::whereHas('user', function ($q) {
|
||||||
|
$q->active()
|
||||||
|
->whereHas('roles', fn($rq) => $rq->whereHas('permissions', fn($pq) => $pq->where('name', 'attendances.view')));
|
||||||
|
})->count();
|
||||||
|
|
||||||
$presentCount = Attendance::whereYear('attendance_date', $year)
|
$presentCount = Attendance::whereYear('attendance_date', $year)
|
||||||
->whereMonth('attendance_date', $month)
|
->whereMonth('attendance_date', $month)
|
||||||
->where('attendance_date', '<=', $statEnd->toDateString())
|
->where('attendance_date', '<=', $statEnd->toDateString())
|
||||||
->whereHas('employee', fn ($q) => $q->whereHas('user', fn ($uq) => $uq->where('is_active', true)))
|
->whereHas('employee', function ($q) {
|
||||||
|
$q->whereHas('user', function ($uq) {
|
||||||
|
$uq->active()
|
||||||
|
->whereHas('roles', fn($rq) => $rq->whereHas('permissions', fn($pq) => $pq->where('name', 'attendances.view')));
|
||||||
|
});
|
||||||
|
})
|
||||||
->count();
|
->count();
|
||||||
|
|
||||||
$leaveRequests = LeaveRequest::approved()
|
$leaveRequests = LeaveRequest::approved()
|
||||||
->where('start_date', '<=', $statEnd)
|
->where('start_date', '<=', $statEnd)
|
||||||
->where('end_date', '>=', $startOfMonth)
|
->where('end_date', '>=', $startOfMonth)
|
||||||
->whereHas('employee', fn ($q) => $q->whereHas('user', fn ($uq) => $uq->where('is_active', true)))
|
->whereHas('employee', function ($q) {
|
||||||
|
$q->whereHas('user', function ($uq) {
|
||||||
|
$uq->active()
|
||||||
|
->whereHas('roles', fn($rq) => $rq->whereHas('permissions', fn($pq) => $pq->where('name', 'attendances.view')));
|
||||||
|
});
|
||||||
|
})
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
$leaveDays = 0;
|
$leaveDays = 0;
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import {
|
import {
|
||||||
index as attendanceIndex,
|
byMonth as attendanceByMonth,
|
||||||
store,
|
store,
|
||||||
update,
|
update,
|
||||||
} from '@/routes/admin/hr/attendances';
|
} from '@/routes/admin/hr/attendances';
|
||||||
@ -24,7 +24,7 @@ import {
|
|||||||
LogIn,
|
LogIn,
|
||||||
LogOut,
|
LogOut,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState, useCallback } from 'react';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
type Attendance = {
|
type Attendance = {
|
||||||
@ -147,9 +147,9 @@ function formatMinutes(minutes: number | null): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function AttendanceIndex({
|
export default function AttendanceIndex({
|
||||||
attendances,
|
attendances: initialAttendances,
|
||||||
leaves,
|
leaves: initialLeaves,
|
||||||
employees = [],
|
employees: initialEmployees = [],
|
||||||
todayAttendance,
|
todayAttendance,
|
||||||
currentYear,
|
currentYear,
|
||||||
currentMonth,
|
currentMonth,
|
||||||
@ -174,10 +174,30 @@ export default function AttendanceIndex({
|
|||||||
const [detailAttendance, setDetailAttendance] = useState<Attendance | null>(
|
const [detailAttendance, setDetailAttendance] = useState<Attendance | null>(
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
const [attendances, setAttendances] = useState<Attendance[]>(initialAttendances);
|
||||||
|
const [leaves, setLeaves] = useState<Leave[]>(initialLeaves);
|
||||||
|
const [employees, setEmployees] = useState<Employee[]>(initialEmployees);
|
||||||
|
const [loadingMonth, setLoadingMonth] = useState(false);
|
||||||
|
|
||||||
const viewYear = viewDate.getFullYear();
|
const viewYear = viewDate.getFullYear();
|
||||||
const viewMonth = viewDate.getMonth() + 1;
|
const viewMonth = viewDate.getMonth() + 1;
|
||||||
|
|
||||||
|
const fetchMonthData = useCallback(async (year: number, month: number) => {
|
||||||
|
setLoadingMonth(true);
|
||||||
|
try {
|
||||||
|
const url = attendanceByMonth.url({ query: { year, month } });
|
||||||
|
const res = await fetch(url);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
setAttendances(data.attendances);
|
||||||
|
setLeaves(data.leaves);
|
||||||
|
setEmployees(data.employees);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoadingMonth(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const attendanceByDate = useMemo(() => {
|
const attendanceByDate = useMemo(() => {
|
||||||
const map = new Map<string, Attendance[]>();
|
const map = new Map<string, Attendance[]>();
|
||||||
attendances.forEach((att) => {
|
attendances.forEach((att) => {
|
||||||
@ -254,47 +274,20 @@ export default function AttendanceIndex({
|
|||||||
const handlePrevMonth = () => {
|
const handlePrevMonth = () => {
|
||||||
const newDate = subMonths(viewDate, 1);
|
const newDate = subMonths(viewDate, 1);
|
||||||
setViewDate(newDate);
|
setViewDate(newDate);
|
||||||
|
fetchMonthData(newDate.getFullYear(), newDate.getMonth() + 1);
|
||||||
if (!isAdmin) {
|
|
||||||
router.get(
|
|
||||||
attendanceIndex.url(),
|
|
||||||
{
|
|
||||||
year: newDate.getFullYear(),
|
|
||||||
month: newDate.getMonth() + 1,
|
|
||||||
},
|
|
||||||
{ preserveState: true, preserveScroll: true },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNextMonth = () => {
|
const handleNextMonth = () => {
|
||||||
const newDate = addMonths(viewDate, 1);
|
const newDate = addMonths(viewDate, 1);
|
||||||
setViewDate(newDate);
|
setViewDate(newDate);
|
||||||
|
fetchMonthData(newDate.getFullYear(), newDate.getMonth() + 1);
|
||||||
if (!isAdmin) {
|
|
||||||
router.get(
|
|
||||||
attendanceIndex.url(),
|
|
||||||
{
|
|
||||||
year: newDate.getFullYear(),
|
|
||||||
month: newDate.getMonth() + 1,
|
|
||||||
},
|
|
||||||
{ preserveState: true, preserveScroll: true },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGoToToday = () => {
|
const handleGoToToday = () => {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
setViewDate(now);
|
setViewDate(now);
|
||||||
setSelectedDate(now);
|
setSelectedDate(now);
|
||||||
|
fetchMonthData(now.getFullYear(), now.getMonth() + 1);
|
||||||
if (!isAdmin) {
|
|
||||||
router.get(
|
|
||||||
attendanceIndex.url(),
|
|
||||||
{ year: now.getFullYear(), month: now.getMonth() + 1 },
|
|
||||||
{ preserveState: true, preserveScroll: true },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCameraCapture = (dataUrl: string) => {
|
const handleCameraCapture = (dataUrl: string) => {
|
||||||
@ -374,17 +367,10 @@ export default function AttendanceIndex({
|
|||||||
Menampilkan presensi dari notifikasi.
|
Menampilkan presensi dari notifikasi.
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.get(
|
const now = new Date();
|
||||||
attendanceIndex.url(),
|
setViewDate(now);
|
||||||
{
|
setSelectedDate(now);
|
||||||
year: currentYear,
|
fetchMonthData(currentYear, currentMonth);
|
||||||
month: currentMonth,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
replace: true,
|
|
||||||
preserveState: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
className="ml-1 text-primary underline underline-offset-4 hover:text-primary/80"
|
className="ml-1 text-primary underline underline-offset-4 hover:text-primary/80"
|
||||||
>
|
>
|
||||||
@ -495,8 +481,7 @@ export default function AttendanceIndex({
|
|||||||
|
|
||||||
const todayMidnight = new Date();
|
const todayMidnight = new Date();
|
||||||
todayMidnight.setHours(0, 0, 0, 0);
|
todayMidnight.setHours(0, 0, 0, 0);
|
||||||
const [cYear, cMonth, cDay] = String(cell.date).split('-').map(Number);
|
const cellDate = new Date(cell.date);
|
||||||
const cellDate = new Date(cYear, cMonth - 1, cDay);
|
|
||||||
cellDate.setHours(0, 0, 0, 0);
|
cellDate.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
const isPastDate = cellDate < todayMidnight;
|
const isPastDate = cellDate < todayMidnight;
|
||||||
@ -543,7 +528,7 @@ export default function AttendanceIndex({
|
|||||||
<div className="mt-1 flex flex-col gap-0.5 overflow-hidden">
|
<div className="mt-1 flex flex-col gap-0.5 overflow-hidden">
|
||||||
{isAdmin ? (
|
{isAdmin ? (
|
||||||
<>
|
<>
|
||||||
{[...employees].sort((a, b) => a.name.localeCompare(b.name)).map((emp) => {
|
{cell.isCurrentMonth && !isFutureDate && [...employees].sort((a, b) => a.name.localeCompare(b.name)).map((emp) => {
|
||||||
const att = dayAttendances.find((a) => a.employee_id === emp.id);
|
const att = dayAttendances.find((a) => a.employee_id === emp.id);
|
||||||
const leave = dayLeaves.find((l) => l.employee_id === emp.id);
|
const leave = dayLeaves.find((l) => l.employee_id === emp.id);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user