import Styles from "@/constants/Styles"; import { apiApproveRejectTaskTugas, apiDeleteTaskTugas, apiGetTaskOne, apiGetTaskTugasApprovals, apiSubmitTaskTugas } from "@/lib/api"; import { setUpdateTask } from "@/lib/taskUpdate"; import { useAuthSession } from "@/providers/AuthProvider"; import { useTheme } from "@/providers/ThemeProvider"; import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons"; import { router, useLocalSearchParams } from "expo-router"; import { useEffect, useState } from "react"; import { View } from "react-native"; import Toast from "react-native-toast-message"; import { useDispatch, useSelector } from "react-redux"; import DrawerBottom from "../drawerBottom"; import ItemSectionTanggalTugas from "../itemSectionTanggalTugas"; import MenuItemRow from "../menuItemRow"; import ModalConfirmation from "../ModalConfirmation"; import ModalRiwayatApproval from "../ModalRiwayatApproval"; import ModalTolakApproval from "../ModalTolakApproval"; import SkeletonTask from "../skeletonTask"; import Text from "../Text"; import ModalListDetailTugasTask from "./modalListDetailTugasTask"; type Props = { id: string; title: string; status: number; dateStart: string; dateEnd: string; files?: { name: string; extension: string }[]; } type ApprovalRecord = { id: string status: number note?: string submitter: { name: string } approver?: { name: string } createdAt: string } export default function SectionTanggalTugasTask({ refreshing, isMemberDivision, isAdminDivision, status, idGroup }: { refreshing: boolean, isMemberDivision: boolean, isAdminDivision: boolean, status?: number, idGroup: string }) { const { colors } = useTheme() const dispatch = useDispatch() const entityUser = useSelector((state: any) => state.user); const update = useSelector((state: any) => state.taskUpdate) const [isModal, setModal] = useState(false) const { token, decryptToken } = useAuthSession() const [loading, setLoading] = useState(true) const [loadingAction, setLoadingAction] = useState(false) const [loadingRiwayat, setLoadingRiwayat] = useState(false) const arrSkeleton = Array.from({ length: 5 }) const [modalDetail, setModalDetail] = useState(false) const [modalRiwayat, setModalRiwayat] = useState(false) const [modalTolak, setModalTolak] = useState(false) const [modalKonfirmasiSetujui, setModalKonfirmasiSetujui] = useState(false) const [modalPersetujuan, setModalPersetujuan] = useState(false) const [riwayatData, setRiwayatData] = useState([]) const { id, detail } = useLocalSearchParams<{ id: string, detail: string }>(); const [data, setData] = useState([]) const [tugas, setTugas] = useState({ id: '', status: 0 }) const [showDeleteModal, setShowDeleteModal] = useState(false) const isApprover = (entityUser.isApprover && entityUser.idGroup === idGroup) || ['supadmin', 'developer'].includes(entityUser.role) const isAdmin = entityUser.role !== 'user' && entityUser.role !== 'coadmin' const canTakeAction = isMemberDivision || isAdmin async function handleLoad(loading: boolean) { try { setLoading(loading) const hasil = await decryptToken(String(token?.current)) const response = await apiGetTaskOne({ id: detail, user: hasil, cat: 'task' }) setData(response.data) } catch (error) { console.error(error) } finally { setLoading(false) } } useEffect(() => { handleLoad(false) }, [update.task]) useEffect(() => { if (refreshing) handleLoad(false) }, [refreshing]) useEffect(() => { handleLoad(true) }, []) async function handleLoadRiwayat() { try { setLoadingRiwayat(true) const hasil = await decryptToken(String(token?.current)) const response = await apiGetTaskTugasApprovals({ user: hasil, id: tugas.id }) setRiwayatData(response.data ?? []) } catch (error) { console.error(error) } finally { setLoadingRiwayat(false) } } async function handleSubmitAjukan() { try { setLoadingAction(true) const hasil = await decryptToken(String(token?.current)) const response = await apiSubmitTaskTugas({ user: hasil, id: tugas.id }) if (response.success) { dispatch(setUpdateTask({ ...update, progress: !update.progress, task: !update.task })) Toast.show({ type: 'small', text1: 'Berhasil mengajukan task untuk persetujuan' }) } else { Toast.show({ type: 'small', text1: response.message }) } } catch (error: any) { Toast.show({ type: 'small', text1: error?.response?.data?.message || "Gagal mengajukan persetujuan" }) } finally { setLoadingAction(false) setModal(false) } } async function handleSetujui() { try { setLoadingAction(true) const hasil = await decryptToken(String(token?.current)) const response = await apiApproveRejectTaskTugas({ user: hasil, id: tugas.id, action: 'approve' }) if (response.success) { dispatch(setUpdateTask({ ...update, progress: !update.progress, task: !update.task })) Toast.show({ type: 'small', text1: 'Tugas berhasil disetujui' }) } else { Toast.show({ type: 'small', text1: response.message }) } } catch (error: any) { Toast.show({ type: 'small', text1: error?.response?.data?.message || "Gagal menyetujui tugas" }) } finally { setLoadingAction(false) setModalKonfirmasiSetujui(false) } } async function handleTolak(note: string) { try { setLoadingAction(true) const hasil = await decryptToken(String(token?.current)) const response = await apiApproveRejectTaskTugas({ user: hasil, id: tugas.id, action: 'reject', note }) if (response.success) { dispatch(setUpdateTask({ ...update, progress: !update.progress, task: !update.task })) Toast.show({ type: 'small', text1: 'Tugas berhasil ditolak' }) } else { Toast.show({ type: 'small', text1: response.message }) } } catch (error: any) { Toast.show({ type: 'small', text1: error?.response?.data?.message || "Gagal menolak tugas" }) } finally { setLoadingAction(false) setModalTolak(false) } } async function handleDelete() { try { const hasil = await decryptToken(String(token?.current)); const response = await apiDeleteTaskTugas({ user: hasil, idProject: detail }, tugas.id); if (response.success) { dispatch(setUpdateTask({ ...update, progress: !update.progress, task: !update.task })) Toast.show({ type: 'small', text1: 'Berhasil menghapus data' }) } else { Toast.show({ type: 'small', text1: response.message }) } } catch (error: any) { Toast.show({ type: 'small', text1: error?.response?.data?.message || "Gagal menghapus data" }) } } const canApprove = isApprover || isAdminDivision const showAjukan = (isMemberDivision || canApprove) && tugas.status === 0 && status !== 3 const showApproverActions = canApprove && tugas.status === 2 return ( <> Tanggal & Tugas {loading ? arrSkeleton.map((_, index) => ) : data.length > 0 ? data.map((item, index) => ( { setTugas({ id: item.id, status: item.status }) setModal(true) }} /> )) : Tidak ada tugas } {/* Drawer menu */} {/* Baris 1 — selalu tampil */} } title="File Tugas" onPress={() => { setModal(false) router.push(`/division/${id}/task/${detail}/tugas-file/${tugas.id}?member=${isMemberDivision}`) }} /> } title="Detail Waktu" onPress={() => { setModal(false) setTimeout(() => setModalDetail(true), 600) }} /> } title="Riwayat" onPress={() => { setModal(false) handleLoadRiwayat() setTimeout(() => setModalRiwayat(true), 600) }} /> {/* Separator */} {(showAjukan || showApproverActions || (canTakeAction && isAdmin && status !== 3)) && ( )} {/* Baris 2 — aksi kondisional */} {showAjukan && ( } title="Ajukan Selesai" disabled={loadingAction} onPress={() => { setModal(false) setTimeout(() => handleSubmitAjukan(), 600) }} /> )} {showApproverActions && ( } title="Persetujuan" disabled={loadingAction} onPress={() => { setModal(false) setTimeout(() => setModalPersetujuan(true), 600) }} /> )} {canTakeAction && isAdmin && status !== 3 && ( <> } title="Edit Tugas" onPress={() => { setModal(false) router.push(`./update/${tugas.id}`) }} /> } title="Hapus Tugas" onPress={() => { setModal(false) setTimeout(() => setShowDeleteModal(true), 600) }} /> )} {/* Drawer persetujuan */} } title="Setujui" color={colors.success} disabled={loadingAction} onPress={() => { setModalPersetujuan(false) setTimeout(() => setModalKonfirmasiSetujui(true), 600) }} /> } title="Tolak" color={colors.error} disabled={loadingAction} onPress={() => { setModalPersetujuan(false) setTimeout(() => setModalTolak(true), 600) }} /> { setShowDeleteModal(false); handleDelete() }} onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" /> setModalKonfirmasiSetujui(false)} confirmText="Setujui" cancelText="Batal" /> ) }