- SectionProgress: progress bar animated, badge persentase, label status, task count - SectionReport: header ikon, left accent border, TextExpandable dengan label Indonesia - SectionLink: tap langsung buka URL, ikon per domain, long press untuk hapus - SectionFile: icon container konsisten 30×30 di semua section - SectionCancel: card subtle dengan warna error, konsisten dengan visual language baru - TextExpandable: fix bug show/hide tidak muncul setelah content diupdate - Tambah 14 style class baru di Styles.ts untuk menggantikan inline style - Terapkan semua perubahan ke fitur division/task - Fix menu "Edit Tugas" di sectionTanggalTugasTask yang terpotong karena overflow Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
250 lines
8.9 KiB
TypeScript
250 lines
8.9 KiB
TypeScript
import Styles from "@/constants/Styles";
|
|
import { apiDeleteProjectTask, apiGetProjectOne, apiUpdateStatusProjectTask } from "@/lib/api";
|
|
import { setUpdateProject } from "@/lib/projectUpdate";
|
|
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 ModalSelect from "../modalSelect";
|
|
import SkeletonTask from "../skeletonTask";
|
|
import Text from "../Text";
|
|
import ModalListDetailTugasProject from "./modalListDetailTugasProject";
|
|
|
|
type Props = {
|
|
id: string;
|
|
title: string;
|
|
desc: string;
|
|
status: 1;
|
|
dateStart: string;
|
|
dateEnd: string;
|
|
createdAt: string;
|
|
files?: { name: string; extension: string }[];
|
|
};
|
|
|
|
export default function SectionTanggalTugasProject({ status, member, refreshing }: { status: number | undefined, member: boolean, refreshing?: boolean }) {
|
|
const { colors } = useTheme();
|
|
const entityUser = useSelector((state: any) => state.user)
|
|
const dispatch = useDispatch()
|
|
const update = useSelector((state: any) => state.projectUpdate)
|
|
const [isModal, setModal] = useState(false);
|
|
const [isSelect, setSelect] = useState(false);
|
|
const { token, decryptToken } = useAuthSession();
|
|
const [modalDetail, setModalDetail] = useState(false)
|
|
const { id } = useLocalSearchParams<{ id: string }>();
|
|
const [data, setData] = useState<Props[]>([]);
|
|
const [loading, setLoading] = useState(true)
|
|
const arrSkeleton = Array.from({ length: 5 });
|
|
const [tugas, setTugas] = useState({
|
|
id: '',
|
|
status: 0,
|
|
})
|
|
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
|
|
|
async function handleLoad(loading: boolean) {
|
|
try {
|
|
setLoading(loading)
|
|
const hasil = await decryptToken(String(token?.current));
|
|
const response = await apiGetProjectOne({
|
|
user: hasil,
|
|
cat: "task",
|
|
id: id,
|
|
});
|
|
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 handleUpdate(status: number) {
|
|
try {
|
|
const hasil = await decryptToken(String(token?.current));
|
|
const response = await apiUpdateStatusProjectTask({
|
|
user: hasil,
|
|
idProject: id,
|
|
status: status,
|
|
}, tugas.id);
|
|
if (response.success) {
|
|
dispatch(setUpdateProject({ ...update, progress: !update.progress, task: !update.task }))
|
|
setSelect(false);
|
|
Toast.show({ type: 'small', text1: 'Berhasil mengubah data', })
|
|
}
|
|
} catch (error: any) {
|
|
console.error(error);
|
|
const message = error?.response?.data?.message || "Gagal mengubah data"
|
|
|
|
Toast.show({ type: 'small', text1: message })
|
|
}
|
|
}
|
|
|
|
async function handleDelete() {
|
|
try {
|
|
const hasil = await decryptToken(String(token?.current));
|
|
const response = await apiDeleteProjectTask({
|
|
user: hasil,
|
|
idProject: id,
|
|
}, tugas.id);
|
|
if (response.success) {
|
|
dispatch(setUpdateProject({ ...update, progress: !update.progress, task: !update.task }))
|
|
setModal(false);
|
|
Toast.show({ type: 'small', text1: 'Berhasil menghapus data', })
|
|
}
|
|
} catch (error: any) {
|
|
console.error(error);
|
|
const message = error?.response?.data?.message || "Gagal menghapus data"
|
|
|
|
Toast.show({ type: 'small', text1: message })
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<View style={[Styles.mb15, Styles.mt10]}>
|
|
<Text style={[Styles.textDefaultSemiBold, Styles.mv05]}>
|
|
Tanggal & Tugas
|
|
</Text>
|
|
<View>
|
|
{
|
|
loading ?
|
|
arrSkeleton.map((item, index) => {
|
|
return (
|
|
<SkeletonTask key={index} />
|
|
)
|
|
})
|
|
:
|
|
data.length > 0
|
|
?
|
|
data.map((item, index) => {
|
|
return (
|
|
<ItemSectionTanggalTugas
|
|
key={index}
|
|
done={item.status === 1}
|
|
title={item.title}
|
|
dateStart={item.dateStart}
|
|
dateEnd={item.dateEnd}
|
|
files={item.files ?? []}
|
|
onPress={() => {
|
|
setTugas({ id: item.id, status: item.status })
|
|
setModal(true)
|
|
}}
|
|
/>
|
|
);
|
|
})
|
|
:
|
|
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada tugas</Text>
|
|
}
|
|
</View>
|
|
</View>
|
|
|
|
<DrawerBottom
|
|
animation="slide"
|
|
isVisible={isModal}
|
|
setVisible={setModal}
|
|
title="Menu"
|
|
>
|
|
<View style={Styles.rowItemsCenter}>
|
|
{(member || (entityUser.role != "user" && entityUser.role != "coadmin")) && status != 3 && (
|
|
<MenuItemRow
|
|
icon={<MaterialCommunityIcons name="list-status" color={colors.text} size={25} />}
|
|
title="Update Status"
|
|
onPress={() => {
|
|
setModal(false);
|
|
setTimeout(() => setSelect(true), 600)
|
|
}}
|
|
/>
|
|
)}
|
|
<MenuItemRow
|
|
icon={<MaterialCommunityIcons name="file-multiple-outline" color={colors.text} size={25} />}
|
|
title="File Tugas"
|
|
onPress={() => {
|
|
setModal(false);
|
|
router.push(`/project/${id}/tugas-file/${tugas.id}?member=${member}`);
|
|
}}
|
|
/>
|
|
<MenuItemRow
|
|
icon={<MaterialCommunityIcons name="clock-time-three-outline" color={colors.text} size={25} />}
|
|
title="Detail Waktu"
|
|
onPress={() => {
|
|
setModal(false);
|
|
setTimeout(() => setModalDetail(true), 600)
|
|
}}
|
|
/>
|
|
</View>
|
|
{(member || (entityUser.role != "user" && entityUser.role != "coadmin")) && status != 3 && (
|
|
<View style={[Styles.rowItemsCenter, Styles.mt15]}>
|
|
<MenuItemRow
|
|
icon={<MaterialCommunityIcons name="pencil-outline" color={colors.text} size={25} />}
|
|
title="Edit Tugas"
|
|
onPress={() => {
|
|
setModal(false);
|
|
router.push(`/project/update/${tugas.id}`);
|
|
}}
|
|
/>
|
|
<MenuItemRow
|
|
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
|
title="Hapus Tugas"
|
|
onPress={() => {
|
|
setModal(false)
|
|
setTimeout(() => setShowDeleteModal(true), 600)
|
|
}}
|
|
/>
|
|
</View>
|
|
)}
|
|
</DrawerBottom>
|
|
|
|
<ModalConfirmation
|
|
visible={showDeleteModal}
|
|
title="Konfirmasi"
|
|
message="Apakah anda yakin ingin menghapus data ini?"
|
|
onConfirm={() => {
|
|
setShowDeleteModal(false)
|
|
handleDelete()
|
|
}}
|
|
onCancel={() => setShowDeleteModal(false)}
|
|
confirmText="Hapus"
|
|
cancelText="Batal"
|
|
/>
|
|
|
|
<ModalSelect
|
|
category="status-task"
|
|
close={() => { setSelect(false) }}
|
|
onSelect={(value) => {
|
|
handleUpdate(Number(value.val))
|
|
}}
|
|
title="Status"
|
|
open={isSelect}
|
|
valChoose={String(tugas.status)}
|
|
/>
|
|
|
|
<ModalListDetailTugasProject
|
|
isVisible={modalDetail}
|
|
setVisible={setModalDetail}
|
|
idTask={tugas.id}
|
|
/>
|
|
</>
|
|
);
|
|
}
|