From d0abd14047a625f2c98162ecc2063f13da31b54a Mon Sep 17 00:00:00 2001 From: bagasbanuna Date: Wed, 4 Feb 2026 17:44:57 +0800 Subject: [PATCH] Fix Loaddata Voting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Voting – User - app/(application)/(user)/voting/(tabs)/status.tsx - app/(application)/(user)/voting/create.tsx Screens – Voting - screens/Voting/ButtonStatusSection.tsx API Client - service/api-client/api-voting.ts Global - app/+not-found.tsx - styles/global-styles.ts Docs - docs/prompt-for-qwen-code.md Untracked (New Files) - screens/Voting/ScreenStatus.tsx ### No issue --- .../(user)/voting/(tabs)/status.tsx | 104 +-------------- app/(application)/(user)/voting/create.tsx | 16 ++- app/+not-found.tsx | 14 +- docs/prompt-for-qwen-code.md | 10 +- screens/Voting/ButtonStatusSection.tsx | 6 +- screens/Voting/ScreenStatus.tsx | 125 ++++++++++++++++++ service/api-client/api-voting.ts | 4 +- styles/global-styles.ts | 1 + 8 files changed, 162 insertions(+), 118 deletions(-) create mode 100644 screens/Voting/ScreenStatus.tsx diff --git a/app/(application)/(user)/voting/(tabs)/status.tsx b/app/(application)/(user)/voting/(tabs)/status.tsx index 9760a80..ea49e2d 100644 --- a/app/(application)/(user)/voting/(tabs)/status.tsx +++ b/app/(application)/(user)/voting/(tabs)/status.tsx @@ -1,106 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { - BadgeCustom, - BaseBox, - LoaderCustom, - ScrollableCustom, - StackCustom, - TextCustom, - ViewWrapper, -} from "@/components"; -import { useAuth } from "@/hooks/use-auth"; -import { dummyMasterStatus } from "@/lib/dummy-data/_master/status"; -import { apiVotingGetByStatus } from "@/service/api-client/api-voting"; -import { dateTimeView } from "@/utils/dateTimeView"; -import { useFocusEffect, useLocalSearchParams } from "expo-router"; -import _ from "lodash"; -import { useCallback, useState } from "react"; +import Voting_ScreenStatus from "@/screens/Voting/ScreenStatus"; export default function VotingStatus() { - const { user } = useAuth(); - const { status } = useLocalSearchParams<{ status?: string }>(); - - const id = user?.id || ""; - const [activeCategory, setActiveCategory] = useState( - status || "publish" - ); - - const [listData, setListData] = useState([]); - const [loadingGetData, setLoadingGetData] = useState(false); - - useFocusEffect( - useCallback(() => { - onLoadData(); - }, [activeCategory, id]) - ); - - async function onLoadData() { - try { - setLoadingGetData(true); - const response = await apiVotingGetByStatus({ - id: id as string, - status: activeCategory!, - }); - setListData(response.data); - } catch (error) { - console.log(error); - } finally { - setLoadingGetData(false); - } - } - - const handlePress = (item: any) => { - setActiveCategory(item.value); - // tambahkan logika lain seperti filter dsb. - }; - - const scrollComponent = ( - ({ - id: i, - label: e.label, - value: e.value, - }))} - onButtonPress={handlePress} - activeId={activeCategory as any} - /> - ); - return ( - - {loadingGetData ? ( - - ) : _.isEmpty(listData) ? ( - Tidak ada data {activeCategory} - ) : ( - listData.map((item: any, i: number) => ( - - - - {item?.title || ""} - - - {item?.awalVote && - dateTimeView({ - date: item?.awalVote, - withoutTime: true, - })}{" "} - -{" "} - {item?.akhirVote && - dateTimeView({ date: item?.akhirVote, withoutTime: true })} - - - - )) - )} - + <> + + ); } diff --git a/app/(application)/(user)/voting/create.tsx b/app/(application)/(user)/voting/create.tsx index 7e6d56a..1821425 100644 --- a/app/(application)/(user)/voting/create.tsx +++ b/app/(application)/(user)/voting/create.tsx @@ -3,11 +3,12 @@ import { BoxButtonOnFooter, ButtonCustom, CenterCustom, + NewWrapper, Spacing, StackCustom, TextAreaCustom, TextInputCustom, - ViewWrapper + ViewWrapper, } from "@/components"; import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom"; import { MainColor } from "@/constants/color-palet"; @@ -79,7 +80,9 @@ export default function VotingCreate() { type: "success", text1: "Data berhasil disimpan", }); - router.replace("/(application)/(user)/voting/(tabs)/status?status=review"); + router.replace( + "/(application)/(user)/voting/(tabs)/status?status=review", + ); } else { Toast.show({ type: "error", @@ -106,7 +109,7 @@ export default function VotingCreate() { }; return ( - + - {listVote.map((item, index) => ( setListVote( listVote.map((item, i) => - i === index ? { ...item, value } : item - ) + i === index ? { ...item, value } : item, + ), ) } /> @@ -198,6 +200,6 @@ export default function VotingCreate() { - + ); } diff --git a/app/+not-found.tsx b/app/+not-found.tsx index 20371a6..dc6c3a8 100644 --- a/app/+not-found.tsx +++ b/app/+not-found.tsx @@ -1,11 +1,21 @@ import { BackButton, StackCustom, TextCustom, ViewWrapper } from "@/components"; -import { Stack } from "expo-router"; +import { router, Stack } from "expo-router"; export default function NotFoundScreen() { + // Setelah (dengan penanganan): + const handleBack = () => { + if (router.canGoBack()) { + router.back(); + } else { + // Alternatif action ketika tidak bisa kembali + router.replace('/'); // atau navigasi ke halaman default + } + }; + return ( <> }} + options={{ headerShown: true, title: "", headerLeft: () => handleBack()} /> }} /> -File utama: screens/Event/ScreenListOfParticipants.tsx -Function fecth: apiEventListOfParticipants -File function fetch: service/api-client/api-event.ts +File utama: screens/Voting/ScreenStatus.tsx +Function fecth: apiVotingGetByStatus +File function fetch: service/api-client/api-voting.ts File komponen wrapper: components/_ShareComponent/NewWrapper.tsx Terapkan pagination pada file "File utama" @@ -15,8 +15,8 @@ Jika tidak ada props page maka tambahkan props page dan default page: "1" Gunakan bahasa indonesia pada cli agar saya mudah membacanya. - +File refrensi: screens/Event/ScreenStatus.tsx +Anda bisa menggunakan refrensi dari "File refrensi" jika butuh pemahaman dengan tipe fitur yang sama diff --git a/screens/Voting/ButtonStatusSection.tsx b/screens/Voting/ButtonStatusSection.tsx index 451d791..9de27c6 100644 --- a/screens/Voting/ButtonStatusSection.tsx +++ b/screens/Voting/ButtonStatusSection.tsx @@ -36,7 +36,7 @@ export default function Voting_ButtonStatusSection({ type: "success", text1: response.message, }); - router.back(); + router.replace(`/voting/${id}/draft/detail`); } else { Toast.show({ type: "info", @@ -73,7 +73,7 @@ export default function Voting_ButtonStatusSection({ type: "success", text1: response.message, }); - router.back(); + router.replace(`/voting/${id}/review/detail`); } else { Toast.show({ type: "info", @@ -110,7 +110,7 @@ export default function Voting_ButtonStatusSection({ type: "success", text1: response.message, }); - router.back(); + router.replace(`/voting/${id}/draft/detail`); } else { Toast.show({ type: "info", diff --git a/screens/Voting/ScreenStatus.tsx b/screens/Voting/ScreenStatus.tsx new file mode 100644 index 0000000..06c98e9 --- /dev/null +++ b/screens/Voting/ScreenStatus.tsx @@ -0,0 +1,125 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { + BadgeCustom, + BaseBox, + ScrollableCustom, + StackCustom, + TextCustom, +} from "@/components"; +import NewWrapper from "@/components/_ShareComponent/NewWrapper"; +import { MainColor } from "@/constants/color-palet"; +import { createPaginationComponents } from "@/helpers/paginationHelpers"; +import { useAuth } from "@/hooks/use-auth"; +import { usePagination } from "@/hooks/use-pagination"; +import { dummyMasterStatus } from "@/lib/dummy-data/_master/status"; +import { apiVotingGetByStatus } from "@/service/api-client/api-voting"; +import { dateTimeView } from "@/utils/dateTimeView"; +import { useLocalSearchParams } from "expo-router"; +import { useState } from "react"; +import { RefreshControl, View } from "react-native"; + +const PAGE_SIZE = 10; + +export default function Voting_ScreenStatus() { + const { user } = useAuth(); + const { status } = useLocalSearchParams<{ status?: string }>(); + + const id = user?.id || ""; + const [activeCategory, setActiveCategory] = useState( + status || "publish", + ); + + // Setup pagination + const pagination = usePagination({ + fetchFunction: async (page) => { + if (!id) return { data: [] }; + + return await apiVotingGetByStatus({ + id: id as string, + status: activeCategory!, + page: String(page), + }); + }, + pageSize: PAGE_SIZE, + dependencies: [id, activeCategory], + onError: (error) => console.error("[ERROR] Fetch voting by status:", error), + }); + + // Generate komponen + const { ListEmptyComponent, ListFooterComponent } = + createPaginationComponents({ + loading: pagination.loading, + refreshing: pagination.refreshing, + listData: pagination.listData, + emptyMessage: `Tidak ada data ${activeCategory}`, + skeletonCount: 5, + skeletonHeight: 100, + }); + + // Render item voting + const renderVotingItem = ({ item }: { item: any }) => ( + + + + {item?.title || ""} + + + {item?.awalVote && + dateTimeView({ + date: item?.awalVote, + withoutTime: true, + })}{" "} + -{" "} + {item?.akhirVote && + dateTimeView({ date: item?.akhirVote, withoutTime: true })} + + + + ); + + const handlePress = (item: any) => { + setActiveCategory(item.value); + // Reset pagination saat kategori berubah + pagination.reset(); + }; + + const scrollComponent = ( + ({ + id: i, + label: e.label, + value: e.value, + }))} + onButtonPress={handlePress} + activeId={activeCategory as any} + /> + ); + + return ( + {scrollComponent}} + listData={pagination.listData} + renderItem={renderVotingItem} + refreshControl={ + + } + onEndReached={pagination.loadMore} + ListEmptyComponent={ListEmptyComponent} + ListFooterComponent={ListFooterComponent} + hideFooter + /> + ); +} diff --git a/service/api-client/api-voting.ts b/service/api-client/api-voting.ts index 159eea2..4cbe63e 100644 --- a/service/api-client/api-voting.ts +++ b/service/api-client/api-voting.ts @@ -14,12 +14,14 @@ export async function apiVotingCreate(data: any) { export async function apiVotingGetByStatus({ id, status, + page = "1", }: { id: string; status: string; + page?: string; }) { try { - const response = await apiConfig.get(`/mobile/voting/${id}/${status}`); + const response = await apiConfig.get(`/mobile/voting/${id}/${status}?page=${page}`); return response.data; } catch (error) { throw error; diff --git a/styles/global-styles.ts b/styles/global-styles.ts index 0b3817b..fed6179 100644 --- a/styles/global-styles.ts +++ b/styles/global-styles.ts @@ -18,6 +18,7 @@ export const GStyles = StyleSheet.create({ flex: 1, paddingInline: PADDING_MEDIUM, paddingTop: PADDING_EXTRA_SMALL, + paddingBottom: 5, // paddingBlock: PADDING_EXTRA_SMALL, backgroundColor: MainColor.darkblue, },