diff --git a/app/(application)/(user)/event/(tabs)/history.tsx b/app/(application)/(user)/event/(tabs)/history.tsx index 661f68b..64fc940 100644 --- a/app/(application)/(user)/event/(tabs)/history.tsx +++ b/app/(application)/(user)/event/(tabs)/history.tsx @@ -1,104 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { ButtonCustom, LoaderCustom, Spacing, TextCustom } from "@/components"; -import ViewWrapper from "@/components/_ShareComponent/ViewWrapper"; -import { AccentColor, MainColor } from "@/constants/color-palet"; -import { useAuth } from "@/hooks/use-auth"; -import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection"; -import { apiEventGetAll } from "@/service/api-client/api-event"; -import { dateTimeView } from "@/utils/dateTimeView"; -import _ from "lodash"; -import { useEffect, useState } from "react"; -import { View } from "react-native"; +import Event_ScreenHistory from "@/screens/Event/ScreenHistory"; export default function EventHistory() { - const [activeCategory, setActiveCategory] = useState("all"); - const { user } = useAuth(); - const [listData, setListData] = useState([]); - const [isLoadList, setIsLoadList] = useState(false); - - useEffect(() => { - onLoadData({ userId: user?.id }); - }, [user?.id, activeCategory]); - - async function onLoadData({ userId }: { userId?: string }) { - try { - setIsLoadList(true); - const response = await apiEventGetAll({ - category: activeCategory === "all" ? "all-history" : "my-history", - userId: userId, - }); - if (response.success) { - setListData(response.data); - } - } catch (error) { - console.log("[ERROR]", error); - } finally { - setIsLoadList(false); - } - } - - const handlePress = (item: any) => { - setActiveCategory(item); - // tambahkan logika lain seperti filter dsb. - }; - - const headerComponent = ( - - handlePress("all")} - > - Semua Riwayat - - - handlePress("main")} - > - Riwayat Saya - - - ); - return ( - - {isLoadList ? ( - - ) : _.isEmpty(listData) ? ( - Belum ada riwayat - ) : ( - listData.map((item: any, index: number) => ( - - {dateTimeView({ date: item?.tanggal, withoutTime: true })} - - } - href={`/event/${item.id}/history`} - /> - )) - )} - + <> + + ); } diff --git a/app/(application)/(user)/event/(tabs)/status.tsx b/app/(application)/(user)/event/(tabs)/status.tsx index 5f7713c..589d1b9 100644 --- a/app/(application)/(user)/event/(tabs)/status.tsx +++ b/app/(application)/(user)/event/(tabs)/status.tsx @@ -1,101 +1,10 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import { - BoxWithHeaderSection, - Grid, - LoaderCustom, - ScrollableCustom, - StackCustom, - TextCustom, -} from "@/components"; -import ViewWrapper from "@/components/_ShareComponent/ViewWrapper"; -import { useAuth } from "@/hooks/use-auth"; -import { dummyMasterStatus } from "@/lib/dummy-data/_master/status"; -import { apiEventGetByStatus } from "@/service/api-client/api-event"; -import { useFocusEffect, useLocalSearchParams } from "expo-router"; -import _ from "lodash"; -import { useCallback, useState } from "react"; +import Event_ScreenStatus from "@/screens/Event/ScreenStatus"; export default function EventStatus() { - 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 apiEventGetByStatus({ - id: id!, - status: activeCategory!, - }); - // console.log("Response", JSON.stringify(response.data, null, 2)); - 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 tabsComponent = ( - ({ - 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) => ( - - - - - - {item?.title} - - - - - {new Date(item?.tanggal).toLocaleDateString()} - - - - - {item?.deskripsi} - - - )) - )} - + <> + + ); } diff --git a/app/(application)/(user)/event/[id]/edit.tsx b/app/(application)/(user)/event/[id]/edit.tsx index acaebee..b0c3a35 100644 --- a/app/(application)/(user)/event/[id]/edit.tsx +++ b/app/(application)/(user)/event/[id]/edit.tsx @@ -1,7 +1,9 @@ /* eslint-disable react-hooks/exhaustive-deps */ import { + BoxButtonOnFooter, ButtonCustom, LoaderCustom, + NewWrapper, SelectCustom, Spacing, StackCustom, @@ -10,6 +12,7 @@ import { TextInputCustom, ViewWrapper, } from "@/components"; +import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent"; import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom"; import { apiEventGetOne, @@ -48,7 +51,7 @@ export default function EventEdit() { useFocusEffect( useCallback(() => { onLoadData(); - }, [id]) + }, [id]), ); async function onLoadData() { @@ -100,6 +103,15 @@ export default function EventEdit() { const startDate = new Date(selectedDate as any); const endDate = new Date(selectedEndDate as any); + if (!startDate) { + Toast.show({ + type: "info", + text1: "Info", + text2: "Tanggal mulai tidak valid", + }); + return false; + } + if (startDate >= endDate) { Toast.show({ type: "info", @@ -146,7 +158,7 @@ export default function EventEdit() { const validateDateRange = ( selectedDate: string | Date, - selectedEndDate: string | Date + selectedEndDate: string | Date, ): { isValid: boolean; error?: string } => { const startDate = new Date(selectedDate); const endDate = new Date(selectedEndDate); @@ -174,9 +186,19 @@ export default function EventEdit() { return ( <> - + + + + } + > {isLoadData ? ( - + ) : ( setData({ ...data, title: value })} /> - ({ - label: item.name, - value: item.id, - }))} - value={data?.eventMaster_TipeAcaraId || ""} - onChange={(value) => { - console.log(value); - setData({ ...data, eventMaster_TipeAcaraId: value }); - }} - /> - setData({ ...data, lokasi: value })} + showCount + value={data?.deskripsi} + onChangeText={(value) => setData({ ...data, deskripsi: value })} /> + @@ -242,31 +253,37 @@ export default function EventEdit() { { validateDateRange( selectedDate as any, - selectedEndDate as any + selectedEndDate as any, ).error } )} - + {/* */} - setData({ ...data, deskripsi: value })} + ({ + label: item.name, + value: item.id, + }))} + value={data?.eventMaster_TipeAcaraId || ""} + onChange={(value) => { + console.log(value); + setData({ ...data, eventMaster_TipeAcaraId: value }); + }} /> - - setData({ ...data, lokasi: value })} /> )} - + ); } diff --git a/app/(application)/(user)/event/create.tsx b/app/(application)/(user)/event/create.tsx index aa746a0..5006439 100644 --- a/app/(application)/(user)/event/create.tsx +++ b/app/(application)/(user)/event/create.tsx @@ -1,12 +1,13 @@ import { + BoxButtonOnFooter, ButtonCustom, + NewWrapper, SelectCustom, Spacing, StackCustom, TextAreaCustom, TextCustom, TextInputCustom, - ViewWrapper, } from "@/components"; import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom"; import { useAuth } from "@/hooks/use-auth"; @@ -100,7 +101,6 @@ export default function EventCreate() { setIsLoading(false); } }; - const buttonSubmit = ( - + {buttonSubmit}} + > setData({ ...data, title: value })} /> - ({ - label: item.name, - value: item.id, - }))} - value={data?.eventMaster_TipeAcaraId || null} - onChange={(value: any) => - setData({ ...data, eventMaster_TipeAcaraId: value }) - } - /> - - setData({ ...data, lokasi: value })} + showCount + value={data?.deskripsi || ""} + onChangeText={(value: any) => + setData({ ...data, deskripsi: value }) + } /> )} + ({ + label: item.name, + value: item.id, + }))} + value={data?.eventMaster_TipeAcaraId || null} + onChange={(value: any) => + setData({ ...data, eventMaster_TipeAcaraId: value }) + } + /> + + setData({ ...data, lokasi: value })} + /> - - - setData({ ...data, deskripsi: value }) - } - /> - - {buttonSubmit} - + ); } diff --git a/docs/prompt-for-qwen-code.md b/docs/prompt-for-qwen-code.md index d3d30d8..7385ef8 100644 --- a/docs/prompt-for-qwen-code.md +++ b/docs/prompt-for-qwen-code.md @@ -1,9 +1,10 @@ -File utama: screens/Admin/Notification-Admin/ScreenNotificationAdmin2.tsx -Fun fecth: apiGetNotificationsById -File fetch: service/api-notifications.ts +File utama: screens/Event/ScreenHistory.tsx +Fun fecth: apiEventGetAll +File fetch: service/api-client/api-event.ts File komponen wrapper: components/_ShareComponent/NewWrapper.tsx +File refrensi: screens/Job/MainViewStatus2.tsx Terapkan pagination pada file "File utama" Analisa juga file "File utama" , jika belum menggunakan NewWrapper pada file "File komponen wrapper" , maka terapkan juga dan ganti wrapper lama yaitu komponen ViewWrapper @@ -13,6 +14,8 @@ Komponen pagination yang digunaka berada pada file hooks/use-pagination.tsx dan Perbaiki fetch "Fun fecth" , pada file "File fetch" Jika tidak ada props page maka tambahkan props page dan default page: "1" +Anda bisa menggunakan refrensi dari "File refrensi" jika butuh pemahaman dengan tipe fitur yang sama + Gunakan bahasa indonesia pada cli agar saya mudah membacanya. diff --git a/screens/Event/ScreenHistory.tsx b/screens/Event/ScreenHistory.tsx new file mode 100644 index 0000000..0064762 --- /dev/null +++ b/screens/Event/ScreenHistory.tsx @@ -0,0 +1,121 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { ButtonCustom, Spacing, TextCustom } from "@/components"; +import NewWrapper from "@/components/_ShareComponent/NewWrapper"; +import { AccentColor, MainColor } from "@/constants/color-palet"; +import { createPaginationComponents } from "@/helpers/paginationHelpers"; +import { useAuth } from "@/hooks/use-auth"; +import { usePagination } from "@/hooks/use-pagination"; +import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection"; +import { apiEventGetAll } from "@/service/api-client/api-event"; +import { dateTimeView } from "@/utils/dateTimeView"; +import _ from "lodash"; +import { useState } from "react"; +import { RefreshControl, View } from "react-native"; + +const PAGE_SIZE = 5; + +export default function Event_ScreenHistory() { + const [activeCategory, setActiveCategory] = useState("all"); + const { user } = useAuth(); + + // Setup pagination + const pagination = usePagination({ + fetchFunction: async (page) => { + return await apiEventGetAll({ + category: activeCategory === "all" ? "all-history" : "my-history", + userId: user?.id, + page: String(page), + }); + }, + pageSize: PAGE_SIZE, + dependencies: [user?.id, activeCategory], + onError: (error) => console.error("[ERROR] Fetch event history:", error), + }); + + // Generate komponen + const { ListEmptyComponent, ListFooterComponent } = createPaginationComponents({ + loading: pagination.loading, + refreshing: pagination.refreshing, + listData: pagination.listData, + emptyMessage: "Belum ada riwayat", + skeletonCount: 5, + skeletonHeight: 100, + }); + + // Render item event + const renderEventItem = ({ item }: { item: any }) => ( + + {dateTimeView({ date: item?.tanggal, withoutTime: true })} + + } + href={`/event/${item.id}/history`} + /> + ); + + const handlePress = (item: any) => { + setActiveCategory(item); + // Reset pagination saat kategori berubah + pagination.reset(); + }; + + const headerComponent = ( + + handlePress("all")} + > + Semua Riwayat + + + handlePress("main")} + > + Riwayat Saya + + + ); + + return ( + + } + onEndReached={pagination.loadMore} + ListEmptyComponent={ListEmptyComponent} + ListFooterComponent={ListFooterComponent} + hideFooter + /> + ); +} diff --git a/screens/Event/ScreenStatus.tsx b/screens/Event/ScreenStatus.tsx new file mode 100644 index 0000000..c8984d7 --- /dev/null +++ b/screens/Event/ScreenStatus.tsx @@ -0,0 +1,121 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import { + BoxWithHeaderSection, + Grid, + 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 { apiEventGetByStatus } from "@/service/api-client/api-event"; +import { useFocusEffect, useLocalSearchParams } from "expo-router"; +import _ from "lodash"; +import { useState } from "react"; +import { RefreshControl, View } from "react-native"; + +const PAGE_SIZE = 10; + +export default function Event_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 apiEventGetByStatus({ + id: id!, + status: activeCategory!, + page: String(page), + }); + }, + pageSize: PAGE_SIZE, + dependencies: [id, activeCategory], + onError: (error) => console.error("[ERROR] Fetch event 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 event + const renderEventItem = ({ item }: { item: any }) => ( + + + + + + {item?.title} + + + + + {new Date(item?.tanggal).toLocaleDateString()} + + + + + {item?.deskripsi} + + + ); + + const handlePress = (item: any) => { + setActiveCategory(item.value); + // Reset pagination saat kategori berubah + pagination.reset(); + }; + + const tabsComponent = ( + ({ + id: i, + label: e.label, + value: e.value, + }))} + onButtonPress={handlePress} + activeId={activeCategory as any} + /> + ); + + return ( + + {tabsComponent} + + } + listData={pagination.listData} + renderItem={renderEventItem} + refreshControl={ + + } + onEndReached={pagination.loadMore} + ListEmptyComponent={ListEmptyComponent} + ListFooterComponent={ListFooterComponent} + /> + ); +} diff --git a/service/api-client/api-event.ts b/service/api-client/api-event.ts index ecd0b0d..cacbf79 100644 --- a/service/api-client/api-event.ts +++ b/service/api-client/api-event.ts @@ -14,12 +14,14 @@ export async function apiEventCreate(data: any) { export async function apiEventGetByStatus({ id, status, + page = "1", }: { id: string; status: string; + page?: string; }) { try { - const response = await apiConfig.get(`/mobile/event/${id}/${status}`); + const response = await apiConfig.get(`/mobile/event/${id}/${status}?page=${page}`); return response.data; } catch (error) { throw error; @@ -79,15 +81,18 @@ export async function apiEventDelete({ id }: { id: string }) { export async function apiEventGetAll({ category, userId, + page = "1", }: { category?: "beranda" | "contribution" | "all-history" | "my-history"; userId?: string; + page?: string; }) { try { const categoryEvent = category ? `?category=${category}` : ""; const userIdCreator = userId ? `&userId=${userId}` : ""; + const pageParam = `&page=${page}`; const response = await apiConfig.get( - `/mobile/event${categoryEvent}${userIdCreator}` + `/mobile/event${categoryEvent}${userIdCreator}${pageParam}` ); return response.data; } catch (error) {