import { deleteNote, exportTimekeeping, getListMaster, getTheTimesheet, updateMultipleUserWorkingTime, updateNote, updateWorkingDays, } from '@/api/Admin' import { update, Xdelete } from '@/rtk/helpers/CRUD' import { exportFile, get } from '@/rtk/helpers/apiService' import { Avatar, Box, Button, Drawer, Image, Menu, MultiSelect, Select, Table, Text, Textarea, TextInput, Tooltip, Modal, } from '@mantine/core' import { useDisclosure } from '@mantine/hooks' import { notifications } from '@mantine/notifications' import { IconCheck, IconExclamationMark, IconPointFilled, IconTrash, IconX, IconFileExcel, } from '@tabler/icons-react' import moment from 'moment' import { useEffect, useState } from 'react' import classes from './Timekeeping.module.css' interface User { id: number name: string email: string email_verified_at: string | null permission: string remember_token: string | null avatar: string created_at: string | null updated_at: string | null } interface DataReason { id: number c_code: string c_name: string } interface DataTimeType { id: number c_code: string c_name: string } interface HistoryValue { id: number name: string user_id: number status: string time_string: string image: string created_at: string updated_at: string } interface NoteValue { id: number note: string reason: string timeType: string reasonName: string timeTypeName: string } interface History { values: HistoryValue[] total: number day: number notes: NoteValue[] } interface UserData { user: User history: History[] } const Timekeeping = () => { const [opened1, { open: open1, close: close1 }] = useDisclosure(false) const [opened2, { open: open2, close: close2 }] = useDisclosure(false) const [disableBtn, setDisableBtn] = useState(false) const [daysInMonth, setDaysInMonth] = useState( Array.from({ length: 31 }, (_, index) => index + 1), ) const [customAddData, setCustomAddData] = useState<{ data: string[] type: string day: number }>({ data: [], type: '', day: 0, }) const [customAddNotes, setCustomAddNotes] = useState<{ user: { id: number name: string } type: string reason: string note: string day: number notes: NoteValue[] }>({ user: { id: 0, name: '', }, type: '', reason: '', note: '', day: 0, notes: [], }) const [workingDays, setWorkingDays] = useState(30) const [data, setData] = useState([]) const [date, setDate] = useState({ month: (new Date().getMonth() + 1).toString(), year: new Date().getFullYear().toString(), }) const [dataTimeType, setDataTimeType] = useState([]) const [dataReason, setDataReason] = useState([]) const [exportModalOpened, setExportModalOpened] = useState(false) const [exportOption, setExportOption] = useState('default') const [isDeleteConfirmOpen, setIsDeleteConfirmOpen] = useState(false) const [noteToDelete, setNoteToDelete] = useState(null) const getListMasterByType = async (type: string) => { try { const params = { type: type, } const res = await get(getListMaster, params) if (res.status) { return res.data } } catch (error: any) { notifications.show({ title: 'Error', message: error.message ?? error, color: 'red', }) } return [] } useEffect(() => { const fetchData = async () => { const resultTimeType = await getListMasterByType('TIME_TYPE') setDataTimeType(resultTimeType) const resultReason = await getListMasterByType('REASON') setDataReason(resultReason) } fetchData() }, []) const getTimeSheet = async () => { try { const res = await get(getTheTimesheet, { month: date.month, year: date.year, }) if (res.status) { setData( res.data.filter((u: UserData) => u.user.permission.includes('staff')), ) if ( res.data.find( (item: UserData) => item.user.id === customAddNotes.user.id, )?.user.id ) { setCustomAddNotes({ ...customAddNotes, notes: (res.data .find((item: UserData) => item.user.id === customAddNotes.user.id) ?.history?.find( (itemHistory: History) => itemHistory.day === customAddNotes.day, )?.notes ?? []) as NoteValue[], }) } setDaysInMonth( Array.from({ length: getDaysInMonth() }, (_, index) => index + 1), ) setWorkingDays(res.working_days ?? 30) } } catch (error: any) { console.log(error) notifications.show({ title: 'Error', message: error.message ?? error, color: 'red', }) } } function getDayName(dateString: string) { // Tạo đối tượng Date từ chuỗi ngày tháng năm const date = new Date(dateString) // Lấy ngày trong tuần (0-6) const dayNumber = date.getDay() // Mảng chứa các tên ngày bằng tiếng Việt const daysInVietnamese = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] // Trả về tên ngày bằng tiếng Việt return daysInVietnamese[dayNumber] } function getDaysInMonth() { // Tạo một đối tượng Date cho ngày đầu tiên của tháng tiếp theo // Chú ý: tháng trong đối tượng Date là từ 0 (tháng 1) đến 11 (tháng 12) const days = new Date(parseInt(date.year), parseInt(date.month), 0) // Trả về ngày, tức là số ngày trong tháng return days.getDate() } const updateMultipleUser = async ( users: number[], day: number, type: string, ) => { try { await update( updateMultipleUserWorkingTime, { users: users, year: date.year, month: date.month, day: day, type: type, }, getTimeSheet, ) setDisableBtn(false) } catch (error) { console.log(error) } } const updateInfoNote = async ( users: { id: number name: string }, day: number, type: string, reason: string, note: string, ) => { try { await update( updateNote, { month: date.month, year: date.year, users: users, type: type, reason: reason, note: note, day: day, }, getTimeSheet, ) setDisableBtn(false) } catch (error) { console.log(error) } } const handleUpdateWorkingDays = async () => { try { await update( updateWorkingDays, { working_days: workingDays, year: date.year, month: date.month, }, getTimeSheet, ) } catch (error) { console.log(error) } } // const handleUpdateCacheMonth = async () => { // try { // await update( // updateCacheMonth, // { // year: date.year, // month: date.month, // }, // getTimeSheet, // ) // } catch (error) { // console.log(error) // } // } useEffect(() => { getTimeSheet() }, [date]) const showTooltipNote = (user: UserData, d: number) => { return user.history .find((h) => h.day === d && h.notes && h.notes.length > 0) ?.notes.map((v, index) => { return (

- {v.reasonName} ({v.timeTypeName}): {v.note}

) }) } const showTooltipAllNote = (user: UserData) => { return ( {user.history .filter((h) => h.notes && h.notes.length > 0) .map((h, index) => ( ))}
Day {h.day}: {h.notes.map((v, noteIndex) => (

- {v.reasonName} ({v.timeTypeName}): {v.note}

))}
) } const showTooltip = (user: UserData, total: number, d: number) => { return (
{`Total: ${(total / 60 / 60).toFixed(1)}h`} {user.history .find((h) => h.day === d) ?.values.map((v) => { return (

{v.status + ': ' + v.time_string}

{' '} {v.image && ( )}
) })} {showTooltipNote(user, d)}
) } const handleDelete = async (id: number) => { try { await Xdelete( deleteNote, { id: id, month: date.month, year: date.year }, getTimeSheet, ) } catch (error) { console.log(error) } } const handleOpenNote = (user: UserData, d: number) => { open2() setCustomAddNotes({ ...customAddNotes, day: d, user: { id: user.user.id, name: user.user.name, }, notes: user.history.find((h) => h.day === d && h.notes && h.notes.length > 0) ?.notes ?? [], }) } const handleExport = async (option: string) => { try { const timestamp = moment().format('DDMMYYYY_HHmmss') const fileName = `Timekeeping_${date.month}_${date.year}_${timestamp}.xlsx` await exportFile( exportTimekeeping, { month: date.month, year: date.year, working_days: workingDays, option: option, }, fileName, ) setExportModalOpened(false) } catch (error) { console.error('Export error:', error) } } const handleConfirmDelete = async () => { if (noteToDelete) { await handleDelete(noteToDelete.id) setIsDeleteConfirmOpen(false) setNoteToDelete(null) } } const openDeleteConfirm = (note: any) => { setNoteToDelete(note) setIsDeleteConfirmOpen(true) } const DeleteConfirmModal = () => ( { setIsDeleteConfirmOpen(false) setNoteToDelete(null) }} centered size="sm" classNames={{ content: classes.deleteModal, }} > Confirm Delete This action will change the ticket status to Refused{' '} and delete all related notes. Are you sure you want to proceed? ) return (

Timekeeping

Add custom worklog} > { return { value: user.user.id.toString(), label: user.user.name } })} onChange={(e) => { setCustomAddData({ ...customAddData, data: e }) }} /> ({ value: user.c_code.toString(), label: user.c_name, }))} onChange={(e) => { setCustomAddNotes({ ...customAddNotes, reason: e! }) }} /> {customAddNotes.reason != '' && (