diff --git a/app/(application)/discussion/[id].tsx b/app/(application)/discussion/[id].tsx index e47e18a..bc364bf 100644 --- a/app/(application)/discussion/[id].tsx +++ b/app/(application)/discussion/[id].tsx @@ -1,11 +1,9 @@ import AppHeader from "@/components/AppHeader"; -import BorderBottomItem from "@/components/borderBottomItem"; import BorderBottomItem2 from "@/components/borderBottomItem2"; import HeaderRightDiscussionGeneralDetail from "@/components/discussion_general/headerDiscussionDetail"; import DrawerBottom from "@/components/drawerBottom"; import ImageUser from "@/components/imageNew"; import { InputForm } from "@/components/inputForm"; -import LabelStatus from "@/components/labelStatus"; import MenuItemRow from "@/components/menuItemRow"; import ModalConfirmation from "@/components/ModalConfirmation"; import Skeleton from "@/components/skeleton"; @@ -271,29 +269,36 @@ export default function DetailDiscussionGeneral() { borderType="all" bgColor="white" icon={ - - + + } title={data?.title} titleShowAll={true} subtitle={ - !data?.isActive ? - - : - + + + {!data?.isActive ? 'Arsip' : data?.status == 1 ? 'Buka' : 'Tutup'} + + } desc={data?.desc} leftBottomInfo={ - - {dataKomentar.length} Komentar + + {dataKomentar.length} Komentar } rightBottomInfo={ - - {data?.createdAt} - + {data?.createdAt} } /> } @@ -306,36 +311,56 @@ export default function DetailDiscussionGeneral() { ) }) : - dataKomentar.map((item, i) => { - return ( - + dataKomentar.map((item, i) => ( + { + setDetailMore((prev: any) => + prev.includes(item.id) + ? prev.filter((id: string) => id !== item.id) + : [...prev, item.id] + ) + }} + onLongPress={() => { + item.idUser == entities.id && data?.status != 2 && data?.isActive && handleMenuKomentar(item.id, item.comment) + }} + style={({ pressed }) => [ + Styles.discussionCommentCard, + { + backgroundColor: pressed ? colors.icon + '10' : colors.card, + borderColor: colors.icon + '20', } - title={item.username} - rightTopInfo={item.createdAt} - desc={item.comment} - rightBottomInfo={item.isEdited ? "Edited" : ""} - descEllipsize={detailMore.includes(item.id) ? false : true} - bgColor="white" - onPress={() => { - setDetailMore((prev: any) => { - if (prev.includes(item.id)) { - return prev.filter((id: string) => id !== item.id) - } else { - return [...prev, item.id] - } - }) - }} - onLongPress={() => { - item.idUser == entities.id && data?.status != 2 && data?.isActive && handleMenuKomentar(item.id, item.comment) - }} - /> - ) - }) + ]} + > + + {/* Name + time */} + + + + + {item.username} + + {item.isEdited && ( + + diedit + + )} + + + {item.createdAt} + + + + {/* Comment text */} + + {item.comment} + + + + )) } @@ -372,15 +397,14 @@ export default function DetailDiscussionGeneral() { multiline focus={viewEdit} itemRight={ - { - (!loadingSendKomentar && selectKomentar.comment != '' && !regexOnlySpacesOrEnter.test(selectKomentar.comment) && data?.status === 1 && data?.isActive && (memberDiscussion || (entityUser.role != "user" && entityUser.role != "coadmin"))) - && handleEditKomentar() - }} - style={[ - Platform.OS == 'android' && Styles.mb12, - ]} + { + (!loadingSendKomentar && selectKomentar.comment != '' && !regexOnlySpacesOrEnter.test(selectKomentar.comment) && data?.status === 1 && data?.isActive && (memberDiscussion || (entityUser.role != "user" && entityUser.role != "coadmin"))) + && handleEditKomentar() + }} + style={[Platform.OS == 'android' && Styles.mb12]} > - + } /> @@ -398,15 +422,14 @@ export default function DetailDiscussionGeneral() { multiline focus={viewEdit} itemRight={ - { - (!loadingSendKomentar && komentar != '' && !regexOnlySpacesOrEnter.test(komentar) && data?.status === 1 && data?.isActive && (memberDiscussion || (entityUser.role != "user" && entityUser.role != "coadmin"))) - && handleKomentar() - }} - style={[ - Platform.OS == 'android' && Styles.mb12, - ]} + { + (!loadingSendKomentar && komentar != '' && !regexOnlySpacesOrEnter.test(komentar) && data?.status === 1 && data?.isActive && (memberDiscussion || (entityUser.role != "user" && entityUser.role != "coadmin"))) + && handleKomentar() + }} + style={[Platform.OS == 'android' && Styles.mb12]} > - + } /> diff --git a/app/(application)/discussion/index.tsx b/app/(application)/discussion/index.tsx index 14b521d..c7f4bb1 100644 --- a/app/(application)/discussion/index.tsx +++ b/app/(application)/discussion/index.tsx @@ -1,23 +1,20 @@ -import BorderBottomItem from "@/components/borderBottomItem"; import ButtonTab from "@/components/buttonTab"; import InputSearch from "@/components/inputSearch"; import LabelStatus from "@/components/labelStatus"; import SkeletonContent from "@/components/skeletonContent"; import Text from "@/components/Text"; import WrapTab from "@/components/wrapTab"; -import { ColorsStatus } from "@/constants/ColorsStatus"; import Styles from "@/constants/Styles"; import { apiGetDiscussionGeneral } from "@/lib/api"; import { useAuthSession } from "@/providers/AuthProvider"; import { useTheme } from "@/providers/ThemeProvider"; -import { AntDesign, Feather, Ionicons, MaterialIcons } from "@expo/vector-icons"; +import { AntDesign, Feather } from "@expo/vector-icons"; import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; import { router, useLocalSearchParams } from "expo-router"; import { useEffect, useMemo, useState } from "react"; -import { RefreshControl, View, VirtualizedList } from "react-native"; +import { FlatList, Pressable, RefreshControl, View } from "react-native"; import { useSelector } from "react-redux"; - type Props = { id: string title: string @@ -38,7 +35,6 @@ export default function Discussion() { const [status, setStatus] = useState<'true' | 'false'>(active == 'false' ? 'false' : 'true') const [refreshing, setRefreshing] = useState(false) - // TanStack Query for Discussions with Infinite Scroll const { data, fetchNextPage, @@ -50,12 +46,12 @@ export default function Discussion() { queryKey: ['discussions', { status, search, group }], queryFn: async ({ pageParam = 1 }) => { const hasil = await decryptToken(String(token?.current)) - const response = await apiGetDiscussionGeneral({ - user: hasil, - active: status, - search: search, - group: String(group), - page: pageParam + const response = await apiGetDiscussionGeneral({ + user: hasil, + active: status, + search: search, + group: String(group), + page: pageParam }) return response; }, @@ -67,17 +63,14 @@ export default function Discussion() { staleTime: 0, }) - // Flatten pages into a single data array const flatData = useMemo(() => { return data?.pages.flatMap(page => page.data) || []; }, [data]) - // Get nameGroup from the first available page const nameGroup = useMemo(() => { return data?.pages[0]?.filter?.name || ""; }, [data]) - // Refetch when manual update state changes useEffect(() => { refetch() }, [update, refetch]) @@ -88,113 +81,129 @@ export default function Discussion() { setRefreshing(false) }; - const loadMoreData = () => { - if (hasNextPage && !isFetchingNextPage) { - fetchNextPage() - } - }; - - const arrSkeleton = [0, 1, 2, 3, 4] - - const getItem = (_data: unknown, index: number): Props => ({ - id: flatData[index]?.id, - title: flatData[index]?.title, - desc: flatData[index]?.desc, - status: flatData[index]?.status, - total_komentar: flatData[index]?.total_komentar, - createdAt: flatData[index]?.createdAt, - }) + const isOpen = (item: Props) => item.status === 1 return ( - - - { - entityUser.role != "user" && entityUser.role != "coadmin" && + + {/* Header controls */} + + {entityUser.role != "user" && entityUser.role != "coadmin" && ( { setStatus("true") }} + onPress={() => setStatus("true")} label="Aktif" icon={} - n={2} /> + n={2} + /> { setStatus("false") }} + onPress={() => setStatus("false")} label="Arsip" icon={} - n={2} /> + n={2} + /> - } - + )} - { - (entityUser.role == "supadmin" || entityUser.role == "developer") && - - Filter : + {(entityUser.role == "supadmin" || entityUser.role == "developer") && ( + + Filter: - } + )} - - { - isLoading ? - arrSkeleton.map((item: any, i: number) => { - return ( - - ) - }) - : - flatData.length > 0 - ? - flatData.length} - getItem={getItem} - renderItem={({ item, index }: { item: Props, index: number }) => { - return ( - { router.push(`/discussion/${item.id}`) }} - borderType="bottom" - icon={ - - } - title={item.title} - subtitle={ - status != "false" && - } - rightTopInfo={item.createdAt} - desc={item.desc?.replace(/<[^>]*>?/gm, ' ').replace(/\r?\n|\r/g, ' ')} - leftBottomInfo={ - - - Diskusikan - - } - rightBottomInfo={`${item.total_komentar} Komentar`} - /> - ) - }} - keyExtractor={(item, index) => String(index)} - onEndReached={loadMoreData} - onEndReachedThreshold={0.5} - showsVerticalScrollIndicator={false} - refreshControl={ - - } + {/* List */} + + {isLoading ? ( + [0, 1, 2, 3, 4].map((_, i) => ) + ) : flatData.length === 0 ? ( + + + + Tidak ada diskusi + + + ) : ( + String(i)} + showsVerticalScrollIndicator={false} + onEndReached={() => { + if (hasNextPage && !isFetchingNextPage) fetchNextPage() + }} + onEndReachedThreshold={0.5} + refreshControl={ + - : - Tidak ada data - } + } + ItemSeparatorComponent={() => } + renderItem={({ item }: { item: Props }) => ( + router.push(`/discussion/${item.id}`)} + style={({ pressed }) => [ + Styles.discussionCard, + { + backgroundColor: pressed ? colors.icon + '10' : colors.card, + borderColor: colors.icon + '20', + } + ]} + > + {/* Top row: icon + title + status badge */} + + {/* Discussion icon */} + + + + + {/* Title + status badge */} + + + {item.title} + + {status !== "false" && ( + + + {isOpen(item) ? 'Buka' : 'Tutup'} + + + )} + + + + {/* Description */} + {item.desc ? ( + + {item.desc.replace(/<[^>]*>?/gm, ' ').replace(/\r?\n|\r/g, ' ')} + + ) : null} + + {/* Bottom row: comment count + date */} + + + + + {item.total_komentar} Komentar + + + + {item.createdAt} + + + + )} + /> + )} ); -} \ No newline at end of file +} diff --git a/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx b/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx index e85030f..bf1301b 100644 --- a/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/discussion/[detail]/index.tsx @@ -1,11 +1,9 @@ import AppHeader from "@/components/AppHeader"; -import BorderBottomItem from "@/components/borderBottomItem"; import BorderBottomItem2 from "@/components/borderBottomItem2"; import HeaderRightDiscussionDetail from "@/components/discussion/headerDiscussionDetail"; import DrawerBottom from "@/components/drawerBottom"; import ImageUser from "@/components/imageNew"; import { InputForm } from "@/components/inputForm"; -import LabelStatus from "@/components/labelStatus"; import MenuItemRow from "@/components/menuItemRow"; import ModalConfirmation from "@/components/ModalConfirmation"; import Skeleton from "@/components/skeleton"; @@ -24,7 +22,7 @@ import { import { getDB } from "@/lib/firebaseDatabase"; import { useAuthSession } from "@/providers/AuthProvider"; import { useTheme } from "@/providers/ThemeProvider"; -import { Feather, Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"; +import { Feather, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"; import { ref } from "@react-native-firebase/database"; import { useHeaderHeight } from '@react-navigation/elements'; import { router, Stack, useLocalSearchParams } from "expo-router"; @@ -87,24 +85,15 @@ export default function DiscussionDetail() { const [detailMore, setDetailMore] = useState([]) const entities = useSelector((state: any) => state.entities) const [isVisible, setVisible] = useState(false) - const [selectKomentar, setSelectKomentar] = useState({ - id: '', - comment: '' - }) + const [selectKomentar, setSelectKomentar] = useState({ id: '', comment: '' }) const [viewEdit, setViewEdit] = useState(false) const [showDeleteModal, setShowDeleteModal] = useState(false) - - useEffect(() => { const onValueChange = reference.on('value', snapshot => { - if (snapshot.val() == null) { - reference.set({ trigger: true }) - } + if (snapshot.val() == null) { reference.set({ trigger: true }) } handleLoadComment(false) }); - - // Stop listening for updates when no longer required return () => reference.off('value', onValueChange); }, []); @@ -115,23 +104,12 @@ export default function DiscussionDetail() { }); } - async function handleLoad(loading: boolean) { try { setLoading(loading) const hasil = await decryptToken(String(token?.current)); - const response = await apiGetDiscussionOne({ - id: detail, - user: hasil, - cat: "data", - }); - - const responseFile = await apiGetDiscussionOne({ - id: detail, - user: hasil, - cat: "file", - }); - + const response = await apiGetDiscussionOne({ id: detail, user: hasil, cat: "data" }); + const responseFile = await apiGetDiscussionOne({ id: detail, user: hasil, cat: "file" }); setData(response.data); setFileDiscussion(responseFile.data) setIsCreator(response.data.createdBy == hasil); @@ -146,11 +124,7 @@ export default function DiscussionDetail() { try { setLoadingKomentar(loading) const hasil = await decryptToken(String(token?.current)); - const response = await apiGetDiscussionOne({ - id: detail, - user: hasil, - cat: "comment", - }); + const response = await apiGetDiscussionOne({ id: detail, user: hasil, cat: "comment" }); setDataComment(response.data); } catch (error) { console.error(error); @@ -162,17 +136,8 @@ export default function DiscussionDetail() { async function handleCheckMember() { try { const hasil = await decryptToken(String(token?.current)); - const response = await apiGetDivisionOneFeature({ - id, - user: hasil, - cat: "check-member", - }); - - const response2 = await apiGetDivisionOneFeature({ - id, - user: hasil, - cat: "check-admin", - }); + const response = await apiGetDivisionOneFeature({ id, user: hasil, cat: "check-member" }); + const response2 = await apiGetDivisionOneFeature({ id, user: hasil, cat: "check-admin" }); setIsMemberDivision(response.data); setIsAdminDivision(response2.data); } catch (error) { @@ -180,33 +145,18 @@ export default function DiscussionDetail() { } } - useEffect(() => { - handleLoad(false); - }, [update.data]); - - useEffect(() => { - handleLoad(true) - handleLoadComment(true); - handleCheckMember(); - }, []); + useEffect(() => { handleLoad(false); }, [update.data]); + useEffect(() => { handleLoad(true); handleLoadComment(true); handleCheckMember(); }, []); async function handleKomentar() { try { setLoadingSend(true); const hasil = await decryptToken(String(token?.current)); - const response = await apiSendDiscussionCommentar({ - id: detail, - data: { comment: komentar, user: hasil }, - }); - if (response.success) { - setKomentar("") - updateTrigger() - } + const response = await apiSendDiscussionCommentar({ id: detail, data: { comment: komentar, user: hasil } }); + if (response.success) { setKomentar(""); updateTrigger() } } catch (error: any) { console.error(error); - const message = error?.response?.data?.message || "Gagal menambahkan komentar" - - Toast.show({ type: 'small', text1: message }) + Toast.show({ type: 'small', text1: error?.response?.data?.message || "Gagal menambahkan komentar" }) } finally { setLoadingSend(false); } @@ -216,20 +166,11 @@ export default function DiscussionDetail() { try { setLoadingSend(true); const hasil = await decryptToken(String(token?.current)); - const response = await apiEditDiscussionCommentar({ - id: selectKomentar.id, - data: { comment: selectKomentar.comment, user: hasil }, - }); - if (response.success) { - updateTrigger() - } else { - Toast.show({ type: 'small', text1: response.message }) - } - } catch (error : any ) { + const response = await apiEditDiscussionCommentar({ id: selectKomentar.id, data: { comment: selectKomentar.comment, user: hasil } }); + if (response.success) { updateTrigger() } else { Toast.show({ type: 'small', text1: response.message }) } + } catch (error: any) { console.error(error); - const message = error?.response?.data?.message || "Gagal mengedit komentar" - - Toast.show({ type: 'small', text1: message }) + Toast.show({ type: 'small', text1: error?.response?.data?.message || "Gagal mengedit komentar" }) } finally { setLoadingSend(false); handleViewEditKomentar() @@ -240,20 +181,11 @@ export default function DiscussionDetail() { try { setLoadingSend(true); const hasil = await decryptToken(String(token?.current)); - const response = await apiDeleteDiscussionCommentar({ - id: selectKomentar.id, - data: { user: hasil }, - }); - if (response.success) { - updateTrigger() - } else { - Toast.show({ type: 'small', text1: response.message }) - } - } catch (error : any ) { + const response = await apiDeleteDiscussionCommentar({ id: selectKomentar.id, data: { user: hasil } }); + if (response.success) { updateTrigger() } else { Toast.show({ type: 'small', text1: response.message }) } + } catch (error: any) { console.error(error); - const message = error?.response?.data?.message || "Gagal menghapus komentar" - - Toast.show({ type: 'small', text1: message }) + Toast.show({ type: 'small', text1: error?.response?.data?.message || "Gagal menghapus komentar" }) } finally { setLoadingSend(false) setVisible(false) @@ -265,13 +197,11 @@ export default function DiscussionDetail() { setVisible(true) } - function handleViewEditKomentar() { setVisible(false) setViewEdit(!viewEdit) } - const handleRefresh = async () => { setRefreshing(true) handleLoad(false) @@ -280,27 +210,15 @@ export default function DiscussionDetail() { setRefreshing(false) }; + const canWrite = data?.status != 2 && data?.isActive && ((entityUser.role != "user" && entityUser.role != "coadmin") || isMemberDivision) + const isOpen = data?.status === 1 + return ( <> ( - // { - // router.back(); - // }} - // /> - // ), headerTitle: "Diskusi", headerTitleAlign: "center", - // headerRight: () => - // (entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision || isCreator ? - // : (<>) - // , header: () => ( router.back()} right={ ((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision || isCreator) ? - : (<>) + : undefined } /> ) }} /> - + - } + showsVerticalScrollIndicator={false} + refreshControl={} > - - { - loading ? - - : - - } - title={data?.username} - subtitle={ - data?.isActive ? ( - data?.status == 1 ? ( - - ) : ( - + + {loading ? ( + + ) : ( + + } + title={data?.username} + titleShowAll={true} + subtitle={ + + + {!data?.isActive ? 'Arsip' : isOpen ? 'Buka' : 'Tutup'} + + + } + desc={data?.desc} + leftBottomInfo={ + + + {dataComment.length} Komentar + + } + rightBottomInfo={{data?.createdAt}} + /> + )} + + + {loadingKomentar ? ( + arrSkeleton.map((_, i) => ( + + )) + ) : ( + dataComment.map((item, i) => ( + { + setDetailMore((prev: any) => + prev.includes(item.id) ? prev.filter((id: string) => id !== item.id) : [...prev, item.id] ) - ) : ( - - ) - } - rightTopInfo={data?.createdAt} - desc={data?.desc} - leftBottomInfo={ - - - - {dataComment.length} Komentar + }} + onLongPress={() => { + item.idUser == entities.id && data?.status != 2 && data?.isActive && handleMenuKomentar(item.id, item.comment) + }} + style={({ pressed }) => [ + Styles.discussionCommentCard, + { backgroundColor: pressed ? colors.icon + '10' : colors.card, borderColor: colors.icon + '20' } + ]} + > + + + + + + {item.username} + + {item.isEdited && ( + diedit + )} + + + {item.createdAt} + + + + {item.comment} - } - /> - } - - - { - loadingKomentar ? - arrSkeleton.map((item, index) => ( - - )) - : - dataComment.map((item, index) => ( - - } - title={item.username} - rightTopInfo={item.createdAt} - desc={item.comment} - rightBottomInfo={item.isEdited ? "Edited" : ""} - descEllipsize={detailMore.includes(item.id) ? false : true} - bgColor="white" - onPress={() => { - setDetailMore((prev: any) => { - if (prev.includes(item.id)) { - return prev.filter((id: string) => id !== item.id) - } else { - return [...prev, item.id] - } - }) - }} - onLongPress={() => { - item.idUser == entities.id && data?.status != 2 && data?.isActive && handleMenuKomentar(item.id, item.comment) - }} - /> - )) - } - + + )) + )} - - - { - viewEdit ? - <> - - - - Edit Komentar - - handleViewEditKomentar()}> - - - - setSelectKomentar({ ...selectKomentar, comment: val })} - value={selectKomentar.comment} - itemRight={ - { - selectKomentar.comment != "" && - !regexOnlySpacesOrEnter.test(selectKomentar.comment) && - !loadingSend && - data?.status != 2 && - data?.isActive && - (((entityUser.role == "user" || - entityUser.role == "coadmin") && - isMemberDivision) || - entityUser.role == "admin" || - entityUser.role == "supadmin" || - entityUser.role == "developer" || - entityUser.role == "cosupadmin") && - handleEditKomentar(); - }} - style={[ - Platform.OS == 'android' && Styles.mb12, - ]} - > - - - } - /> - - : - data?.status != 2 && data?.isActive && ((entityUser.role != "user" && entityUser.role != "coadmin") || - isMemberDivision) - ? - { - komentar != "" && - !regexOnlySpacesOrEnter.test(komentar) && - !loadingSend && - data?.status != 2 && - data?.isActive && - (((entityUser.role == "user" || - entityUser.role == "coadmin") && - isMemberDivision) || - entityUser.role == "admin" || - entityUser.role == "supadmin" || - entityUser.role == "developer" || - entityUser.role == "cosupadmin") && - handleKomentar(); - }} - style={[ - Platform.OS == 'android' && Styles.mb12, - ]} - > - - - } - /> - : - - - { - data?.status == 2 ? "Diskusi telah ditutup" : data?.isActive == false ? "Diskusi telah diarsipkan" : "Hanya anggota divisi yang dapat memberikan komentar" - } - - - } + + + {viewEdit ? ( + <> + + + + Edit Komentar + + handleViewEditKomentar()}> + + + + setSelectKomentar({ ...selectKomentar, comment: val })} + value={selectKomentar.comment} + itemRight={ + { + selectKomentar.comment != "" && !regexOnlySpacesOrEnter.test(selectKomentar.comment) && !loadingSend && data?.status != 2 && data?.isActive + && (((entityUser.role == "user" || entityUser.role == "coadmin") && isMemberDivision) || entityUser.role == "admin" || entityUser.role == "supadmin" || entityUser.role == "developer" || entityUser.role == "cosupadmin") + && handleEditKomentar(); + }} + style={[Platform.OS == 'android' && Styles.mb12]} + > + + + } + /> + + ) : canWrite ? ( + { + komentar != "" && !regexOnlySpacesOrEnter.test(komentar) && !loadingSend && data?.status != 2 && data?.isActive + && (((entityUser.role == "user" || entityUser.role == "coadmin") && isMemberDivision) || entityUser.role == "admin" || entityUser.role == "supadmin" || entityUser.role == "developer" || entityUser.role == "cosupadmin") + && handleKomentar(); + }} + style={[Platform.OS == 'android' && Styles.mb12]} + > + + + } + /> + ) : ( + + + {data?.status == 2 ? "Diskusi telah ditutup" : data?.isActive == false ? "Diskusi telah diarsipkan" : "Hanya anggota divisi yang dapat memberikan komentar"} + + + )} + - } - title="Edit" - onPress={() => { handleViewEditKomentar() }} - /> - } - title="Hapus" - onPress={() => { - setVisible(false) - setTimeout(() => { - setShowDeleteModal(true) - }, 600) - }} - /> + } title="Edit" onPress={() => handleViewEditKomentar()} /> + } title="Hapus" onPress={() => { setVisible(false); setTimeout(() => setShowDeleteModal(true), 600) }} /> @@ -566,10 +404,7 @@ export default function DiscussionDetail() { visible={showDeleteModal} title="Konfirmasi" message="Apakah anda yakin ingin menghapus komentar?" - onConfirm={() => { - setShowDeleteModal(false) - handleDeleteKomentar() - }} + onConfirm={() => { setShowDeleteModal(false); handleDeleteKomentar() }} onCancel={() => setShowDeleteModal(false)} confirmText="Hapus" cancelText="Batal" diff --git a/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx b/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx index 43feec0..75dee0b 100644 --- a/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx +++ b/app/(application)/division/[id]/(fitur-division)/discussion/index.tsx @@ -1,8 +1,6 @@ -import BorderBottomItem from "@/components/borderBottomItem"; import ButtonTab from "@/components/buttonTab"; import ImageUser from "@/components/imageNew"; import InputSearch from "@/components/inputSearch"; -import LabelStatus from "@/components/labelStatus"; import SkeletonContent from "@/components/skeletonContent"; import Text from "@/components/Text"; import WrapTab from "@/components/wrapTab"; @@ -11,13 +9,12 @@ import Styles from "@/constants/Styles"; import { apiGetDiscussion, apiGetDivisionOneFeature } from "@/lib/api"; import { useAuthSession } from "@/providers/AuthProvider"; import { useTheme } from "@/providers/ThemeProvider"; -import { AntDesign, Feather, Ionicons } from "@expo/vector-icons"; +import { AntDesign, Feather } from "@expo/vector-icons"; import { router, useLocalSearchParams } from "expo-router"; import { useEffect, useState } from "react"; -import { RefreshControl, View, VirtualizedList } from "react-native"; +import { FlatList, Pressable, RefreshControl, View } from "react-native"; import { useSelector } from "react-redux"; - type Props = { id: string, title: string, @@ -30,7 +27,6 @@ type Props = { isActive: boolean } - export default function DiscussionDivision() { const { colors } = useTheme(); const { id, active } = useLocalSearchParams<{ id: string, active?: string }>() @@ -51,17 +47,8 @@ export default function DiscussionDivision() { async function handleCheckMember() { try { const hasil = await decryptToken(String(token?.current)); - const response = await apiGetDivisionOneFeature({ - id, - user: hasil, - cat: "check-member", - }); - - const response2 = await apiGetDivisionOneFeature({ - id, - user: hasil, - cat: "check-admin", - }); + const response = await apiGetDivisionOneFeature({ id, user: hasil, cat: "check-member" }); + const response2 = await apiGetDivisionOneFeature({ id, user: hasil, cat: "check-admin" }); setIsMemberDivision(response.data); setIsAdminDivision(response2.data); } catch (error) { @@ -80,8 +67,6 @@ export default function DiscussionDivision() { setData(response.data) } else if (thisPage > 1 && response.data.length > 0) { setData([...data, ...response.data]) - } else { - return; } } catch (error) { console.error(error) @@ -91,26 +76,15 @@ export default function DiscussionDivision() { } } - useEffect(() => { - handleLoad(false, 1) - }, [update.data]) - - - useEffect(() => { - handleLoad(true, 1) - }, [status, search]) + useEffect(() => { handleLoad(false, 1) }, [update.data]) + useEffect(() => { handleLoad(true, 1) }, [status, search]) + useEffect(() => { handleCheckMember() }, []) const loadMoreData = () => { if (waiting) return - setTimeout(() => { - handleLoad(false, page + 1) - }, 1000); + setTimeout(() => { handleLoad(false, page + 1) }, 1000); } - useEffect(() => { - handleCheckMember() - }, []) - const handleRefresh = async () => { setRefreshing(true) handleLoad(false, 1) @@ -118,100 +92,101 @@ export default function DiscussionDivision() { setRefreshing(false) }; - const getItem = (_data: unknown, index: number): Props => ({ - id: data[index].id, - title: data[index].title, - desc: data[index].desc, - status: data[index].status, - user_name: data[index].user_name, - img: data[index].img, - total_komentar: data[index].total_komentar, - createdAt: data[index].createdAt, - isActive: data[index].isActive, - }) + const isOpen = (item: Props) => item.status === 1 return ( - - { - ((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision) && - + + {((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision) && ( + { setStatus("true") }} + onPress={() => setStatus("true")} label="Aktif" icon={} - n={2} /> + n={2} + /> { setStatus("false") }} + onPress={() => setStatus("false")} label="Arsip" icon={} - n={2} /> + n={2} + /> - } + )} + + {loading ? ( + arrSkeleton.map((_, i) => ) + ) : data.length === 0 ? ( + + + + Tidak ada diskusi + + + ) : ( + String(i)} + showsVerticalScrollIndicator={false} + onEndReached={loadMoreData} + onEndReachedThreshold={0.5} + refreshControl={ + + } + ItemSeparatorComponent={() => } + renderItem={({ item }: { item: Props }) => ( + router.push(`./discussion/${item.id}`)} + style={({ pressed }) => [ + Styles.discussionCard, + { backgroundColor: pressed ? colors.icon + '10' : colors.card, borderColor: colors.icon + '20' } + ]} + > + + + + + {item.user_name} + + {status === "true" && ( + + + {isOpen(item) ? 'Buka' : 'Tutup'} + + + )} + + - - { - loading ? - arrSkeleton.map((item: any, i: number) => { - return ( - - ) - }) - : - data.length > 0 ? - data.length} - getItem={getItem} - renderItem={({ item, index }: { item: Props, index: number }) => { - return ( - { router.push(`./discussion/${item.id}`) }} - borderType="bottom" - icon={ - - } - title={item.user_name} - subtitle={ - status == "true" ? item.status == 1 ? : : <> - } - rightTopInfo={item.createdAt} - desc={item.desc} - leftBottomInfo={ - - - Diskusikan - - } - rightBottomInfo={item.total_komentar + ' Komentar'} - bgColor="transparent" - /> - ) - }} - keyExtractor={(item, index) => String(index)} - onEndReached={loadMoreData} - onEndReachedThreshold={0.5} - showsVerticalScrollIndicator={false} - refreshControl={ - - } - /> - : - (Tidak ada diskusi) - } + {item.desc ? ( + + {item.desc} + + ) : null} + + + + + + {item.total_komentar} Komentar + + + + {item.createdAt} + + + + )} + /> + )} ); -} \ No newline at end of file +} diff --git a/constants/Styles.ts b/constants/Styles.ts index b9fab98..e3b2462 100644 --- a/constants/Styles.ts +++ b/constants/Styles.ts @@ -1113,6 +1113,62 @@ const Styles = StyleSheet.create({ flex: 1, marginLeft: 10, }, + discussionCard: { + borderRadius: 10, + borderWidth: 1, + padding: 14, + }, + discussionIconCircle: { + width: 40, + height: 40, + borderRadius: 20, + alignItems: 'center', + justifyContent: 'center', + flexShrink: 0, + }, + discussionIconCircleLg: { + width: 44, + height: 44, + borderRadius: 22, + alignItems: 'center', + justifyContent: 'center', + }, + discussionStatusPill: { + alignSelf: 'flex-start', + marginTop: 3, + paddingHorizontal: 8, + paddingVertical: 2, + borderRadius: 20, + borderWidth: 1, + }, + discussionStatusText: { + fontSize: 11, + fontWeight: '600', + }, + discussionCardIndent: { + marginLeft: 50, + }, + discussionSeparator: { + height: 8, + }, + discussionCommentText: { + fontSize: 12, + marginLeft: 5, + }, + discussionDateText: { + fontSize: 11, + }, + discussionCommentCard: { + borderRadius: 10, + borderWidth: 1, + padding: 12, + marginBottom: 8, + flexDirection: 'row', + }, + discussionEditedText: { + fontSize: 10, + fontStyle: 'italic', + }, }) export default Styles; \ No newline at end of file