2025-09-15 19:46:52 +00:00
|
|
|
|
import React, { useState } from 'react'
|
|
|
|
|
|
import { FaClock, FaUser, FaPlus, FaEdit, FaCheck, FaTimes, FaEye, FaUsers } from 'react-icons/fa'
|
|
|
|
|
|
import { LeaveStatusEnum, HrOvertime } from '../../../types/hr'
|
|
|
|
|
|
import DataTable, { Column } from '../../../components/common/DataTable'
|
|
|
|
|
|
import { mockOvertimes } from '../../../mocks/mockOvertimes'
|
|
|
|
|
|
import { mockEmployees } from '../../../mocks/mockEmployees'
|
|
|
|
|
|
import { mockDepartments } from '../../../mocks/mockDepartments'
|
|
|
|
|
|
import Widget from '../../../components/common/Widget'
|
|
|
|
|
|
import { getLeaveStatusColor, getLeaveStatusText } from '../../../utils/erp'
|
|
|
|
|
|
import { Container } from '@/components/shared'
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const OvertimeManagement: React.FC = () => {
|
|
|
|
|
|
// Overtime states
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const [overtimes, setOvertimes] = useState<HrOvertime[]>(mockOvertimes)
|
|
|
|
|
|
const [overtimeSelectedStatus, setOvertimeSelectedStatus] = useState<string>('all')
|
|
|
|
|
|
const [overtimeSelectedPeriod, setOvertimeSelectedPeriod] = useState<string>('all')
|
|
|
|
|
|
const [searchOvertimesTerm, setSearchOvertimesTerm] = useState('')
|
|
|
|
|
|
const [selectedDepartment, setSelectedDepartment] = useState<string>('all')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Overtime modal states
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const [showOvertimeAddModal, setShowOvertimeAddModal] = useState(false)
|
|
|
|
|
|
const [showOvertimeEditModal, setShowOvertimeEditModal] = useState(false)
|
|
|
|
|
|
const [showOvertimeViewModal, setShowOvertimeViewModal] = useState(false)
|
|
|
|
|
|
const [showOvertimeRejectModal, setShowOvertimeRejectModal] = useState(false)
|
|
|
|
|
|
const [showBulkOvertimeModal, setShowBulkOvertimeModal] = useState(false)
|
|
|
|
|
|
const [selectedOvertime, setSelectedOvertime] = useState<HrOvertime | null>(null)
|
|
|
|
|
|
const [overtimeRejectReason, setOvertimeRejectReason] = useState('')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Overtime form state
|
|
|
|
|
|
const [overtimeFormData, setOvertimeFormData] = useState({
|
2025-09-15 19:46:52 +00:00
|
|
|
|
employeeId: '',
|
|
|
|
|
|
date: '',
|
|
|
|
|
|
startTime: '',
|
|
|
|
|
|
endTime: '',
|
|
|
|
|
|
reason: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
rate: 1.5,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Bulk overtime form state
|
|
|
|
|
|
const [bulkOvertimeFormData, setBulkOvertimeFormData] = useState({
|
2025-09-15 19:46:52 +00:00
|
|
|
|
departmentId: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
selectedEmployees: [] as string[],
|
2025-09-15 19:46:52 +00:00
|
|
|
|
date: '',
|
|
|
|
|
|
startTime: '',
|
|
|
|
|
|
endTime: '',
|
|
|
|
|
|
reason: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
rate: 1.5,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Get employees by department
|
|
|
|
|
|
const getEmployeesByDepartment = (departmentId: string) => {
|
2025-10-22 08:20:11 +00:00
|
|
|
|
return mockEmployees.filter((emp) => emp.departmentId === departmentId)
|
2025-09-15 19:46:52 +00:00
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Get selected employee object
|
|
|
|
|
|
const getSelectedEmployee = (employeeId: string) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return mockEmployees.find((emp) => emp.id === employeeId)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Overtime handlers
|
|
|
|
|
|
const handleOvertimeAdd = () => {
|
|
|
|
|
|
setOvertimeFormData({
|
2025-09-15 19:46:52 +00:00
|
|
|
|
employeeId: '',
|
|
|
|
|
|
date: '',
|
|
|
|
|
|
startTime: '',
|
|
|
|
|
|
endTime: '',
|
|
|
|
|
|
reason: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
rate: 1.5,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
|
|
|
|
|
setShowOvertimeAddModal(true)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleOvertimeEdit = (overtime: HrOvertime) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedOvertime(overtime)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
setOvertimeFormData({
|
|
|
|
|
|
employeeId: overtime.employeeId,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
date: new Date(overtime.date).toISOString().split('T')[0],
|
2025-09-15 09:31:47 +00:00
|
|
|
|
startTime: overtime.startTime,
|
|
|
|
|
|
endTime: overtime.endTime,
|
|
|
|
|
|
reason: overtime.reason,
|
|
|
|
|
|
rate: overtime.rate || 1.5,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
|
|
|
|
|
setShowOvertimeEditModal(true)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleOvertimeApprove = (id: string) => {
|
|
|
|
|
|
setOvertimes((prevOvertimes) =>
|
|
|
|
|
|
prevOvertimes.map((overtime) =>
|
|
|
|
|
|
overtime.id === id
|
|
|
|
|
|
? {
|
|
|
|
|
|
...overtime,
|
|
|
|
|
|
status: LeaveStatusEnum.Approved,
|
|
|
|
|
|
lastModificationTime: new Date(),
|
|
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
: overtime,
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
alert('Mesai talebi onaylandı!')
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleOvertimeReject = (id: string, reason?: string) => {
|
|
|
|
|
|
if (reason) {
|
|
|
|
|
|
setOvertimes((prevOvertimes) =>
|
|
|
|
|
|
prevOvertimes.map((overtime) =>
|
|
|
|
|
|
overtime.id === id
|
|
|
|
|
|
? {
|
|
|
|
|
|
...overtime,
|
|
|
|
|
|
status: LeaveStatusEnum.Rejected,
|
|
|
|
|
|
lastModificationTime: new Date(),
|
|
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
: overtime,
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
|
|
|
|
|
setShowOvertimeRejectModal(false)
|
|
|
|
|
|
setOvertimeRejectReason('')
|
|
|
|
|
|
alert('Mesai talebi reddedildi!')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
} else {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const overtime = overtimes.find((o) => o.id === id)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
if (overtime) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedOvertime(overtime)
|
|
|
|
|
|
setShowOvertimeRejectModal(true)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleOvertimeView = (overtime: HrOvertime) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setSelectedOvertime(overtime)
|
|
|
|
|
|
setShowOvertimeViewModal(true)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleSubmitOvertimeAdd = () => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
!overtimeFormData.employeeId ||
|
|
|
|
|
|
!overtimeFormData.date ||
|
|
|
|
|
|
!overtimeFormData.startTime ||
|
|
|
|
|
|
!overtimeFormData.endTime
|
|
|
|
|
|
) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
alert('Lütfen tüm gerekli alanları doldurun!')
|
|
|
|
|
|
return
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const startTime = new Date(`2000-01-01 ${overtimeFormData.startTime}`)
|
|
|
|
|
|
const endTime = new Date(`2000-01-01 ${overtimeFormData.endTime}`)
|
|
|
|
|
|
const totalHours = (endTime.getTime() - startTime.getTime()) / (1000 * 60 * 60)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
if (totalHours <= 0) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
alert('Bitiş saati başlangıç saatinden sonra olmalıdır!')
|
|
|
|
|
|
return
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const newOvertime: HrOvertime = {
|
|
|
|
|
|
id: `ot_${Date.now()}`,
|
|
|
|
|
|
employeeId: overtimeFormData.employeeId,
|
|
|
|
|
|
date: new Date(overtimeFormData.date),
|
|
|
|
|
|
startTime: overtimeFormData.startTime,
|
|
|
|
|
|
endTime: overtimeFormData.endTime,
|
|
|
|
|
|
totalHours: totalHours,
|
|
|
|
|
|
reason: overtimeFormData.reason,
|
|
|
|
|
|
status: LeaveStatusEnum.Pending,
|
|
|
|
|
|
rate: overtimeFormData.rate,
|
|
|
|
|
|
amount: totalHours * 100 * overtimeFormData.rate, // Assuming 100 TL/hour base rate
|
|
|
|
|
|
creationTime: new Date(),
|
|
|
|
|
|
lastModificationTime: new Date(),
|
2025-09-15 19:46:52 +00:00
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setOvertimes((prevOvertimes) => [...prevOvertimes, newOvertime])
|
|
|
|
|
|
setShowOvertimeAddModal(false)
|
|
|
|
|
|
alert('Mesai talebi başarıyla oluşturuldu!')
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleSubmitOvertimeEdit = () => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (!selectedOvertime) return
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
!overtimeFormData.employeeId ||
|
|
|
|
|
|
!overtimeFormData.date ||
|
|
|
|
|
|
!overtimeFormData.startTime ||
|
|
|
|
|
|
!overtimeFormData.endTime
|
|
|
|
|
|
) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
alert('Lütfen tüm gerekli alanları doldurun!')
|
|
|
|
|
|
return
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const startTime = new Date(`2000-01-01 ${overtimeFormData.startTime}`)
|
|
|
|
|
|
const endTime = new Date(`2000-01-01 ${overtimeFormData.endTime}`)
|
|
|
|
|
|
const totalHours = (endTime.getTime() - startTime.getTime()) / (1000 * 60 * 60)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
if (totalHours <= 0) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
alert('Bitiş saati başlangıç saatinden sonra olmalıdır!')
|
|
|
|
|
|
return
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setOvertimes((prevOvertimes) =>
|
|
|
|
|
|
prevOvertimes.map((overtime) =>
|
|
|
|
|
|
overtime.id === selectedOvertime.id
|
|
|
|
|
|
? {
|
|
|
|
|
|
...overtime,
|
|
|
|
|
|
employeeId: overtimeFormData.employeeId,
|
|
|
|
|
|
date: new Date(overtimeFormData.date),
|
|
|
|
|
|
startTime: overtimeFormData.startTime,
|
|
|
|
|
|
endTime: overtimeFormData.endTime,
|
|
|
|
|
|
totalHours: totalHours,
|
|
|
|
|
|
reason: overtimeFormData.reason,
|
|
|
|
|
|
rate: overtimeFormData.rate,
|
|
|
|
|
|
amount: totalHours * 100 * overtimeFormData.rate,
|
|
|
|
|
|
lastModificationTime: new Date(),
|
|
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
: overtime,
|
|
|
|
|
|
),
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setShowOvertimeEditModal(false)
|
|
|
|
|
|
setSelectedOvertime(null)
|
|
|
|
|
|
alert('Mesai talebi başarıyla güncellendi!')
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleSubmitOvertimeReject = () => {
|
|
|
|
|
|
if (!selectedOvertime || !overtimeRejectReason.trim()) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
alert('Lütfen red nedeni giriniz!')
|
|
|
|
|
|
return
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
handleOvertimeReject(selectedOvertime.id, overtimeRejectReason)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleBulkOvertimeAdd = () => {
|
|
|
|
|
|
setBulkOvertimeFormData({
|
2025-09-15 19:46:52 +00:00
|
|
|
|
departmentId: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
selectedEmployees: [],
|
2025-09-15 19:46:52 +00:00
|
|
|
|
date: '',
|
|
|
|
|
|
startTime: '',
|
|
|
|
|
|
endTime: '',
|
|
|
|
|
|
reason: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
rate: 1.5,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
|
|
|
|
|
setShowBulkOvertimeModal(true)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
const handleSubmitBulkOvertime = () => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
!bulkOvertimeFormData.selectedEmployees.length ||
|
|
|
|
|
|
!bulkOvertimeFormData.date ||
|
|
|
|
|
|
!bulkOvertimeFormData.startTime ||
|
|
|
|
|
|
!bulkOvertimeFormData.endTime
|
|
|
|
|
|
) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
alert('Lütfen en az bir personel seçin ve tüm gerekli alanları doldurun!')
|
|
|
|
|
|
return
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const startTime = new Date(`2000-01-01 ${bulkOvertimeFormData.startTime}`)
|
|
|
|
|
|
const endTime = new Date(`2000-01-01 ${bulkOvertimeFormData.endTime}`)
|
|
|
|
|
|
const totalHours = (endTime.getTime() - startTime.getTime()) / (1000 * 60 * 60)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
if (totalHours <= 0) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
alert('Bitiş saati başlangıç saatinden sonra olmalıdır!')
|
|
|
|
|
|
return
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const newOvertimes: HrOvertime[] = bulkOvertimeFormData.selectedEmployees.map((employeeId) => {
|
|
|
|
|
|
const employee = getSelectedEmployee(employeeId)
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: `ot_${Date.now()}_${employeeId}`,
|
|
|
|
|
|
employeeId: employeeId,
|
|
|
|
|
|
employee: employee,
|
|
|
|
|
|
date: new Date(bulkOvertimeFormData.date),
|
|
|
|
|
|
startTime: bulkOvertimeFormData.startTime,
|
|
|
|
|
|
endTime: bulkOvertimeFormData.endTime,
|
|
|
|
|
|
totalHours: totalHours,
|
|
|
|
|
|
reason: bulkOvertimeFormData.reason,
|
|
|
|
|
|
status: LeaveStatusEnum.Pending,
|
|
|
|
|
|
rate: bulkOvertimeFormData.rate,
|
|
|
|
|
|
amount: totalHours * 100 * bulkOvertimeFormData.rate, // 100 TL/hour base rate
|
|
|
|
|
|
creationTime: new Date(),
|
|
|
|
|
|
lastModificationTime: new Date(),
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
setOvertimes((prevOvertimes) => [...prevOvertimes, ...newOvertimes])
|
|
|
|
|
|
setShowBulkOvertimeModal(false)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
setBulkOvertimeFormData({
|
2025-09-15 19:46:52 +00:00
|
|
|
|
departmentId: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
selectedEmployees: [],
|
2025-09-15 19:46:52 +00:00
|
|
|
|
date: '',
|
|
|
|
|
|
startTime: '',
|
|
|
|
|
|
endTime: '',
|
|
|
|
|
|
reason: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
rate: 1.5,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
|
|
|
|
|
alert(`${newOvertimes.length} personel için toplu mesai talebi başarıyla oluşturuldu!`)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Filter overtime data
|
|
|
|
|
|
const filteredOvertimes = overtimes.filter((overtime) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (overtimeSelectedStatus !== 'all' && overtime.status !== overtimeSelectedStatus) {
|
|
|
|
|
|
return false
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
if (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
selectedDepartment !== 'all' &&
|
2025-09-15 09:31:47 +00:00
|
|
|
|
overtime.employee?.department?.name !== selectedDepartment
|
|
|
|
|
|
) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return false
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (overtimeSelectedPeriod !== 'all') {
|
|
|
|
|
|
const now = new Date()
|
|
|
|
|
|
const overtimeDate = new Date(overtime.date)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
switch (overtimeSelectedPeriod) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
case 'this-month': {
|
|
|
|
|
|
const currentMonth = now.getMonth()
|
|
|
|
|
|
const currentYear = now.getFullYear()
|
2025-09-15 09:31:47 +00:00
|
|
|
|
return (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
overtimeDate.getMonth() === currentMonth && overtimeDate.getFullYear() === currentYear
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
case 'last-month': {
|
|
|
|
|
|
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
return (
|
|
|
|
|
|
overtimeDate.getMonth() === lastMonth.getMonth() &&
|
|
|
|
|
|
overtimeDate.getFullYear() === lastMonth.getFullYear()
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
case 'last-3-months': {
|
|
|
|
|
|
const threeMonthsAgo = new Date(now.getFullYear(), now.getMonth() - 3)
|
|
|
|
|
|
return overtimeDate >= threeMonthsAgo
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
default:
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return true
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Search filter
|
2025-09-15 19:46:52 +00:00
|
|
|
|
if (searchOvertimesTerm.trim() !== '') {
|
2025-09-15 09:31:47 +00:00
|
|
|
|
if (overtime.employeeId !== searchOvertimesTerm) {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return false
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
return true
|
|
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
// Overtime table columns
|
|
|
|
|
|
const overtimeColumns: Column<HrOvertime>[] = [
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'employee',
|
|
|
|
|
|
header: 'Personel',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (overtime: HrOvertime) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const employee = mockEmployees.find((emp) => emp.id === overtime.employeeId)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
return (
|
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
|
<FaUser className="w-4 h-4 text-gray-500" />
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<div className="font-medium text-gray-900">{employee?.fullName}</div>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="text-sm text-gray-500">{employee?.code}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'date',
|
|
|
|
|
|
header: 'Tarih',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (overtime: HrOvertime) => (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<div className="text-sm">{new Date(overtime.date).toLocaleDateString('tr-TR')}</div>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'time',
|
|
|
|
|
|
header: 'Mesai Saatleri',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (overtime: HrOvertime) => (
|
|
|
|
|
|
<div className="text-sm">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
{overtime.startTime} - {overtime.endTime}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="text-gray-500">{overtime.totalHours} saat</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'reason',
|
|
|
|
|
|
header: 'Neden',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (overtime: HrOvertime) => (
|
|
|
|
|
|
<div className="text-sm max-w-xs truncate" title={overtime.reason}>
|
|
|
|
|
|
{overtime.reason}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'rate',
|
|
|
|
|
|
header: 'Çarpan',
|
|
|
|
|
|
render: (overtime: HrOvertime) => <div className="text-sm">x{overtime.rate}</div>,
|
2025-09-15 09:31:47 +00:00
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'amount',
|
|
|
|
|
|
header: 'Tutar',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (overtime: HrOvertime) => (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<div className="text-sm font-medium">{overtime.amount?.toLocaleString('tr-TR')} ₺</div>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'status',
|
|
|
|
|
|
header: 'Durum',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (overtime: HrOvertime) => (
|
|
|
|
|
|
<span
|
|
|
|
|
|
className={`px-2 py-1 text-xs font-medium rounded-full ${getLeaveStatusColor(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
overtime.status,
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
{getLeaveStatusText(overtime.status)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2025-09-15 19:46:52 +00:00
|
|
|
|
key: 'actions',
|
|
|
|
|
|
header: 'İşlemler',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
render: (overtime: HrOvertime) => (
|
|
|
|
|
|
<div className="flex gap-1">
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => handleOvertimeView(overtime)}
|
|
|
|
|
|
className="p-1 text-blue-600 hover:bg-blue-50 rounded"
|
|
|
|
|
|
title="Görüntüle"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaEye className="w-4 h-4" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
|
|
{overtime.status === LeaveStatusEnum.Pending && (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => handleOvertimeApprove(overtime.id)}
|
|
|
|
|
|
className="p-1 text-green-600 hover:bg-green-50 rounded"
|
|
|
|
|
|
title="Onayla"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaCheck className="w-4 h-4" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => handleOvertimeReject(overtime.id)}
|
|
|
|
|
|
className="p-1 text-red-600 hover:bg-red-50 rounded"
|
|
|
|
|
|
title="Reddet"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaTimes className="w-4 h-4" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => handleOvertimeEdit(overtime)}
|
|
|
|
|
|
className="p-1 text-gray-600 hover:bg-gray-50 rounded"
|
|
|
|
|
|
title="Düzenle"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaEdit className="w-4 h-4" />
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
2025-09-15 19:46:52 +00:00
|
|
|
|
]
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
return (
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<Container>
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
{/* Header */}
|
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
|
<div>
|
2025-09-15 21:02:48 +00:00
|
|
|
|
<h2 className="text-2xl font-bold text-gray-900">Mesai Yönetimi</h2>
|
|
|
|
|
|
<p className="text-gray-600">Personel mesai talepleri ve onay süreçleri</p>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div className="flex gap-2">
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={handleOvertimeAdd}
|
|
|
|
|
|
className="flex items-center gap-2 px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaPlus className="w-4 h-4" />
|
|
|
|
|
|
Yeni Mesai Talebi
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={handleBulkOvertimeAdd}
|
|
|
|
|
|
className="flex items-center gap-2 px-3 py-1.5 text-sm bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors"
|
|
|
|
|
|
>
|
|
|
|
|
|
<FaUsers className="w-4 h-4" />
|
|
|
|
|
|
Toplu Mesai
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
|
|
|
|
|
|
{/* Stats Cards */}
|
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
|
|
|
|
<Widget title="Toplam Mesai" value={overtimes.length} color="blue" icon="FaClock" />
|
|
|
|
|
|
|
|
|
|
|
|
<Widget
|
|
|
|
|
|
title="Beklemede"
|
|
|
|
|
|
value={overtimes.filter((o) => o.status === LeaveStatusEnum.Pending).length}
|
|
|
|
|
|
color="yellow"
|
|
|
|
|
|
icon="FaClock"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<Widget
|
|
|
|
|
|
title="Onaylanan"
|
|
|
|
|
|
value={overtimes.filter((o) => o.status === LeaveStatusEnum.Approved).length}
|
|
|
|
|
|
color="green"
|
|
|
|
|
|
icon="FaCheck"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<Widget
|
|
|
|
|
|
title="Toplam Tutar"
|
|
|
|
|
|
value={`${overtimes
|
|
|
|
|
|
.filter((o) => o.status === LeaveStatusEnum.Approved)
|
|
|
|
|
|
.reduce((sum, o) => sum + (o.amount || 0), 0)
|
|
|
|
|
|
.toLocaleString('tr-TR')} ₺`}
|
|
|
|
|
|
color="purple"
|
|
|
|
|
|
icon="FaDollarSign"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Filters */}
|
|
|
|
|
|
<div className="flex gap-3 items-center">
|
|
|
|
|
|
<select
|
|
|
|
|
|
value={selectedDepartment}
|
|
|
|
|
|
onChange={(e) => setSelectedDepartment(e.target.value)}
|
|
|
|
|
|
className="px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
2025-09-15 09:31:47 +00:00
|
|
|
|
>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<option value="all">Tüm Departmanlar</option>
|
|
|
|
|
|
{mockDepartments.map((dept) => (
|
|
|
|
|
|
<option key={dept.id} value={dept.name}>
|
|
|
|
|
|
{dept.name}
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
|
|
|
|
<select
|
|
|
|
|
|
value={searchOvertimesTerm}
|
|
|
|
|
|
onChange={(e) => setSearchOvertimesTerm(e.target.value)}
|
|
|
|
|
|
className="w-full px-3 py-1.5 border border-gray-300 text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
2025-09-15 09:31:47 +00:00
|
|
|
|
>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<option value="">Personel seçiniz...</option>
|
|
|
|
|
|
{mockEmployees.map((employee) => (
|
|
|
|
|
|
<option key={employee.id} value={employee.id}>
|
|
|
|
|
|
{employee.fullName} ({employee.code})
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
|
|
|
|
|
|
<select
|
|
|
|
|
|
value={overtimeSelectedStatus}
|
|
|
|
|
|
onChange={(e) => setOvertimeSelectedStatus(e.target.value)}
|
|
|
|
|
|
className="border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
|
|
|
|
>
|
|
|
|
|
|
<option value="all">Tüm Durumlar</option>
|
2025-09-17 09:46:58 +00:00
|
|
|
|
{Object.values(LeaveStatusEnum).map((status) => (
|
|
|
|
|
|
<option key={status} value={status}>
|
|
|
|
|
|
{getLeaveStatusText(status)}
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
</select>
|
|
|
|
|
|
|
|
|
|
|
|
<select
|
|
|
|
|
|
value={overtimeSelectedPeriod}
|
|
|
|
|
|
onChange={(e) => setOvertimeSelectedPeriod(e.target.value)}
|
|
|
|
|
|
className="border border-gray-300 rounded-lg px-3 py-1.5 text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
|
|
|
|
>
|
|
|
|
|
|
<option value="all">Tüm Dönemler</option>
|
|
|
|
|
|
<option value="this-month">Bu Ay</option>
|
|
|
|
|
|
<option value="last-month">Geçen Ay</option>
|
|
|
|
|
|
<option value="last-3-months">Son 3 Ay</option>
|
|
|
|
|
|
</select>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{/* Data Table */}
|
|
|
|
|
|
<div className="bg-white rounded-lg shadow-sm border">
|
|
|
|
|
|
<DataTable data={filteredOvertimes} columns={overtimeColumns} />
|
|
|
|
|
|
</div>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{filteredOvertimes.length === 0 && (
|
|
|
|
|
|
<div className="text-center py-12">
|
|
|
|
|
|
<FaClock className="w-10 h-10 text-gray-400 mx-auto mb-3" />
|
|
|
|
|
|
<h3 className="text-base font-medium text-gray-900 mb-2">Mesai talebi bulunamadı</h3>
|
|
|
|
|
|
<p className="text-gray-500">Seçilen kriterlere uygun mesai talebi bulunmamaktadır.</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Overtime Add/Edit Modal */}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{(showOvertimeAddModal || (showOvertimeEditModal && selectedOvertime)) && (
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
|
|
|
|
<div className="bg-white rounded-lg p-4 w-full max-w-md">
|
|
|
|
|
|
<h3 className="text-base font-semibold mb-4">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{showOvertimeAddModal ? 'Yeni Mesai Talebi' : 'Mesai Talebini Düzenle'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</h3>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Personel</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<select
|
|
|
|
|
|
value={overtimeFormData.employeeId}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setOvertimeFormData({
|
|
|
|
|
|
...overtimeFormData,
|
|
|
|
|
|
employeeId: e.target.value,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
>
|
|
|
|
|
|
<option value="">Personel seçiniz...</option>
|
|
|
|
|
|
{mockEmployees.map((employee) => (
|
|
|
|
|
|
<option key={employee.id} value={employee.id}>
|
|
|
|
|
|
{employee.fullName} ({employee.code})
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Tarih</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<input
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
value={overtimeFormData.date}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setOvertimeFormData({
|
|
|
|
|
|
...overtimeFormData,
|
|
|
|
|
|
date: e.target.value,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Başlangıç Saati
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="time"
|
|
|
|
|
|
value={overtimeFormData.startTime}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setOvertimeFormData({
|
|
|
|
|
|
...overtimeFormData,
|
|
|
|
|
|
startTime: e.target.value,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Bitiş Saati
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="time"
|
|
|
|
|
|
value={overtimeFormData.endTime}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setOvertimeFormData({
|
|
|
|
|
|
...overtimeFormData,
|
|
|
|
|
|
endTime: e.target.value,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Mesai Çarpanı
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<select
|
|
|
|
|
|
value={overtimeFormData.rate}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setOvertimeFormData({
|
|
|
|
|
|
...overtimeFormData,
|
|
|
|
|
|
rate: parseFloat(e.target.value),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
>
|
|
|
|
|
|
<option value={1.5}>x1.5 (Normal Mesai)</option>
|
|
|
|
|
|
<option value={2.0}>x2.0 (Hafta Sonu/Tatil)</option>
|
|
|
|
|
|
<option value={2.5}>x2.5 (Resmi Tatil)</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Neden</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<textarea
|
|
|
|
|
|
value={overtimeFormData.reason}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setOvertimeFormData({
|
|
|
|
|
|
...overtimeFormData,
|
|
|
|
|
|
reason: e.target.value,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
rows={3}
|
|
|
|
|
|
placeholder="Mesai nedenini belirtiniz..."
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex justify-end gap-2 mt-6">
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setShowOvertimeAddModal(false)
|
|
|
|
|
|
setShowOvertimeEditModal(false)
|
|
|
|
|
|
setSelectedOvertime(null)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
className="px-3 py-1.5 text-sm text-gray-600 border border-gray-300 rounded-md hover:bg-gray-50"
|
|
|
|
|
|
>
|
|
|
|
|
|
İptal
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
2025-09-15 19:46:52 +00:00
|
|
|
|
onClick={showOvertimeAddModal ? handleSubmitOvertimeAdd : handleSubmitOvertimeEdit}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
className="px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
|
|
|
|
|
>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{showOvertimeAddModal ? 'Oluştur' : 'Güncelle'}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Overtime View Modal */}
|
|
|
|
|
|
{showOvertimeViewModal && selectedOvertime && (
|
|
|
|
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
|
|
|
|
<div className="bg-white rounded-lg p-4 w-full max-w-lg">
|
|
|
|
|
|
<h3 className="text-base font-semibold mb-4">Mesai Detayları</h3>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Personel</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<p className="text-gray-900">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{mockEmployees.find((emp) => emp.id === selectedOvertime.employeeId)
|
|
|
|
|
|
?.fullName || selectedOvertime.employeeId}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Tarih</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<p className="text-gray-900">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{new Date(selectedOvertime.date).toLocaleDateString('tr-TR')}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Başlangıç Saati</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<p className="text-gray-900">{selectedOvertime.startTime}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Bitiş Saati</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<p className="text-gray-900">{selectedOvertime.endTime}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="grid grid-cols-3 gap-4">
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Toplam Saat</label>
|
|
|
|
|
|
<p className="text-gray-900">{selectedOvertime.totalHours} saat</p>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Çarpan</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<p className="text-gray-900">x{selectedOvertime.rate}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Tutar</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<p className="text-gray-900 font-medium">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{selectedOvertime.amount?.toLocaleString('tr-TR')} ₺
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Durum</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<span
|
|
|
|
|
|
className={`px-2 py-1 text-xs font-medium rounded-full ${getLeaveStatusColor(
|
2025-09-15 19:46:52 +00:00
|
|
|
|
selectedOvertime.status,
|
2025-09-15 09:31:47 +00:00
|
|
|
|
)}`}
|
|
|
|
|
|
>
|
|
|
|
|
|
{getLeaveStatusText(selectedOvertime.status)}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Neden</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<p className="text-gray-900">{selectedOvertime.reason}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{selectedOvertime.approvedBy && (
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700">Onaylayan</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<p className="text-gray-900">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{mockEmployees.find((emp) => emp.id === selectedOvertime.approvedBy)?.fullName}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex justify-end mt-6">
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setShowOvertimeViewModal(false)
|
|
|
|
|
|
setSelectedOvertime(null)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
className="px-3 py-1.5 text-sm bg-gray-600 text-white rounded-md hover:bg-gray-700"
|
|
|
|
|
|
>
|
|
|
|
|
|
Kapat
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Overtime Reject Modal */}
|
|
|
|
|
|
{showOvertimeRejectModal && selectedOvertime && (
|
|
|
|
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
|
|
|
|
<div className="bg-white rounded-lg p-4 w-full max-w-md">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h3 className="text-base font-semibold mb-4">Mesai Talebini Reddet</h3>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
|
<p className="text-gray-700 mb-2">
|
|
|
|
|
|
<strong>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{mockEmployees.find((emp) => emp.id === selectedOvertime.employeeId)?.fullName ||
|
|
|
|
|
|
selectedOvertime.employeeId}
|
|
|
|
|
|
</strong>{' '}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
adlı personelin mesai talebini reddetmek üzeresiniz.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="mb-4">
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Red Nedeni <span className="text-red-500">*</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
value={overtimeRejectReason}
|
|
|
|
|
|
onChange={(e) => setOvertimeRejectReason(e.target.value)}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
rows={3}
|
|
|
|
|
|
placeholder="Reddedilme nedenini belirtiniz..."
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex justify-end gap-2">
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setShowOvertimeRejectModal(false)
|
|
|
|
|
|
setSelectedOvertime(null)
|
|
|
|
|
|
setOvertimeRejectReason('')
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
className="px-3 py-1.5 text-sm text-gray-600 border border-gray-300 rounded-md hover:bg-gray-50"
|
|
|
|
|
|
>
|
|
|
|
|
|
İptal
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={handleSubmitOvertimeReject}
|
|
|
|
|
|
className="px-3 py-1.5 text-sm bg-red-600 text-white rounded-md hover:bg-red-700"
|
|
|
|
|
|
>
|
|
|
|
|
|
Reddet
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Bulk Overtime Modal */}
|
|
|
|
|
|
{showBulkOvertimeModal && (
|
|
|
|
|
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
|
|
|
|
|
<div className="bg-white rounded-lg p-4 w-full max-w-2xl max-h-[90vh] overflow-y-auto">
|
|
|
|
|
|
<h3 className="text-base font-semibold mb-4">Toplu Mesai Talebi</h3>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
|
{/* Department Selection */}
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Departman</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<select
|
|
|
|
|
|
value={bulkOvertimeFormData.departmentId}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
setBulkOvertimeFormData({
|
|
|
|
|
|
...bulkOvertimeFormData,
|
|
|
|
|
|
departmentId: e.target.value,
|
|
|
|
|
|
selectedEmployees: [],
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
>
|
|
|
|
|
|
<option value="">Departman seçiniz...</option>
|
|
|
|
|
|
{mockDepartments.map((department) => (
|
|
|
|
|
|
<option key={department.id} value={department.id}>
|
|
|
|
|
|
{department.name} ({department.code})
|
|
|
|
|
|
</option>
|
|
|
|
|
|
))}
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Employee Selection */}
|
|
|
|
|
|
{bulkOvertimeFormData.departmentId && (
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Personeller
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<div className="border border-gray-300 rounded-md p-3 max-h-40 overflow-y-auto">
|
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
|
<label className="flex items-center">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="checkbox"
|
|
|
|
|
|
checked={
|
2025-09-15 19:46:52 +00:00
|
|
|
|
getEmployeesByDepartment(bulkOvertimeFormData.departmentId).length >
|
|
|
|
|
|
0 &&
|
2025-09-15 09:31:47 +00:00
|
|
|
|
bulkOvertimeFormData.selectedEmployees.length ===
|
2025-09-15 19:46:52 +00:00
|
|
|
|
getEmployeesByDepartment(bulkOvertimeFormData.departmentId).length
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
onChange={(e) => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const departmentEmployees = getEmployeesByDepartment(
|
|
|
|
|
|
bulkOvertimeFormData.departmentId,
|
|
|
|
|
|
)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
setBulkOvertimeFormData({
|
|
|
|
|
|
...bulkOvertimeFormData,
|
|
|
|
|
|
selectedEmployees: e.target.checked
|
|
|
|
|
|
? departmentEmployees.map((emp) => emp.id)
|
|
|
|
|
|
: [],
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
className="mr-2"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<span className="font-medium">Tümünü Seç</span>
|
|
|
|
|
|
</label>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{getEmployeesByDepartment(bulkOvertimeFormData.departmentId).map(
|
|
|
|
|
|
(employee) => (
|
|
|
|
|
|
<label key={employee.id} className="flex items-center">
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="checkbox"
|
|
|
|
|
|
checked={bulkOvertimeFormData.selectedEmployees.includes(employee.id)}
|
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
|
setBulkOvertimeFormData({
|
|
|
|
|
|
...bulkOvertimeFormData,
|
|
|
|
|
|
selectedEmployees: e.target.checked
|
|
|
|
|
|
? [...bulkOvertimeFormData.selectedEmployees, employee.id]
|
|
|
|
|
|
: bulkOvertimeFormData.selectedEmployees.filter(
|
|
|
|
|
|
(id) => id !== employee.id,
|
|
|
|
|
|
),
|
|
|
|
|
|
})
|
|
|
|
|
|
}}
|
|
|
|
|
|
className="mr-2"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<span>
|
|
|
|
|
|
{employee.fullName} ({employee.code})
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</label>
|
|
|
|
|
|
),
|
|
|
|
|
|
)}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p className="text-sm text-gray-500 mt-1">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
{bulkOvertimeFormData.selectedEmployees.length} personel seçildi
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Date */}
|
|
|
|
|
|
<div>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Mesai Tarihi</label>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<input
|
|
|
|
|
|
type="date"
|
|
|
|
|
|
value={bulkOvertimeFormData.date}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setBulkOvertimeFormData({
|
|
|
|
|
|
...bulkOvertimeFormData,
|
|
|
|
|
|
date: e.target.value,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Time Range */}
|
|
|
|
|
|
<div className="grid grid-cols-2 gap-4">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Başlangıç Saati
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="time"
|
|
|
|
|
|
value={bulkOvertimeFormData.startTime}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setBulkOvertimeFormData({
|
|
|
|
|
|
...bulkOvertimeFormData,
|
|
|
|
|
|
startTime: e.target.value,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Bitiş Saati
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<input
|
|
|
|
|
|
type="time"
|
|
|
|
|
|
value={bulkOvertimeFormData.endTime}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setBulkOvertimeFormData({
|
|
|
|
|
|
...bulkOvertimeFormData,
|
|
|
|
|
|
endTime: e.target.value,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Rate */}
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Mesai Çarpanı
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<select
|
|
|
|
|
|
value={bulkOvertimeFormData.rate}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setBulkOvertimeFormData({
|
|
|
|
|
|
...bulkOvertimeFormData,
|
|
|
|
|
|
rate: parseFloat(e.target.value),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
>
|
|
|
|
|
|
<option value={1.5}>x1.5 (Normal Mesai)</option>
|
|
|
|
|
|
<option value={2.0}>x2.0 (Hafta Sonu/Tatil)</option>
|
|
|
|
|
|
<option value={2.5}>x2.5 (Resmi Tatil)</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Reason */}
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
|
|
|
|
|
Neden (Opsiyonel)
|
|
|
|
|
|
</label>
|
|
|
|
|
|
<textarea
|
|
|
|
|
|
value={bulkOvertimeFormData.reason}
|
|
|
|
|
|
onChange={(e) =>
|
|
|
|
|
|
setBulkOvertimeFormData({
|
|
|
|
|
|
...bulkOvertimeFormData,
|
|
|
|
|
|
reason: e.target.value,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
|
|
|
|
rows={3}
|
|
|
|
|
|
placeholder="Mesai nedenini belirtiniz..."
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Summary */}
|
|
|
|
|
|
{bulkOvertimeFormData.selectedEmployees.length > 0 &&
|
|
|
|
|
|
bulkOvertimeFormData.startTime &&
|
|
|
|
|
|
bulkOvertimeFormData.endTime && (
|
|
|
|
|
|
<div className="bg-blue-50 p-3 rounded-lg">
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<h4 className="text-sm font-medium text-blue-900 mb-2">Özet</h4>
|
2025-09-15 09:31:47 +00:00
|
|
|
|
<div className="text-sm text-blue-800 space-y-1">
|
|
|
|
|
|
<p>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="font-medium">Personel Sayısı:</span>{' '}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
{bulkOvertimeFormData.selectedEmployees.length}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
{(() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
const startTime = new Date(`2000-01-01 ${bulkOvertimeFormData.startTime}`)
|
|
|
|
|
|
const endTime = new Date(`2000-01-01 ${bulkOvertimeFormData.endTime}`)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const totalHours =
|
2025-09-15 19:46:52 +00:00
|
|
|
|
(endTime.getTime() - startTime.getTime()) / (1000 * 60 * 60)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
const totalAmount =
|
|
|
|
|
|
totalHours *
|
|
|
|
|
|
100 *
|
|
|
|
|
|
bulkOvertimeFormData.rate *
|
2025-09-15 19:46:52 +00:00
|
|
|
|
bulkOvertimeFormData.selectedEmployees.length
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
|
|
|
|
|
return totalHours > 0 ? (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<p>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="font-medium">Mesai Süresi:</span> {totalHours} saat
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
<span className="font-medium">Çarpan:</span> x
|
|
|
|
|
|
{bulkOvertimeFormData.rate}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
<span className="font-medium">Toplam Tutar:</span>{' '}
|
|
|
|
|
|
{totalAmount.toLocaleString('tr-TR')} ₺
|
2025-09-15 09:31:47 +00:00
|
|
|
|
</p>
|
|
|
|
|
|
</>
|
2025-09-15 19:46:52 +00:00
|
|
|
|
) : null
|
2025-09-15 09:31:47 +00:00
|
|
|
|
})()}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="flex justify-end gap-2 mt-6">
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={() => {
|
2025-09-15 19:46:52 +00:00
|
|
|
|
setShowBulkOvertimeModal(false)
|
2025-09-15 09:31:47 +00:00
|
|
|
|
setBulkOvertimeFormData({
|
2025-09-15 19:46:52 +00:00
|
|
|
|
departmentId: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
selectedEmployees: [],
|
2025-09-15 19:46:52 +00:00
|
|
|
|
date: '',
|
|
|
|
|
|
startTime: '',
|
|
|
|
|
|
endTime: '',
|
|
|
|
|
|
reason: '',
|
2025-09-15 09:31:47 +00:00
|
|
|
|
rate: 1.5,
|
2025-09-15 19:46:52 +00:00
|
|
|
|
})
|
2025-09-15 09:31:47 +00:00
|
|
|
|
}}
|
|
|
|
|
|
className="px-3 py-1.5 text-sm text-gray-600 border border-gray-300 rounded-md hover:bg-gray-50"
|
|
|
|
|
|
>
|
|
|
|
|
|
İptal
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
onClick={handleSubmitBulkOvertime}
|
|
|
|
|
|
className="px-3 py-1.5 text-sm bg-green-600 text-white rounded-md hover:bg-green-700"
|
|
|
|
|
|
>
|
|
|
|
|
|
Toplu Mesai Oluştur
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
2025-09-15 19:46:52 +00:00
|
|
|
|
</Container>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
2025-09-15 09:31:47 +00:00
|
|
|
|
|
2025-09-15 19:46:52 +00:00
|
|
|
|
export default OvertimeManagement
|