feat: implement pagination and NewWrapper on donation and investment screens

- Implement pagination on investment screens (ScreenMyHolding, ScreenInvestor, ScreenRecapOfNews, ScreenListOfNews)
- Implement pagination on donation screens (ScreenStatus)
- Update API functions to support pagination with page parameter (apiInvestmentGetAll, apiInvestmentGetInvestorById, apiInvestmentGetNews, apiDonationGetByStatus)
- Replace ViewWrapper with NewWrapper for better UI experience
- Update app directory files to use new modular components from screens directory
- Add pull-to-refresh and infinite scroll functionality
- Improve performance by loading data incrementally
- Apply NewWrapper to donation create and create-story screens

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-02-09 17:35:54 +08:00
parent 38a6b424e8
commit 2705f96b01
12 changed files with 185 additions and 136 deletions

View File

@@ -1,82 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
LoaderCustom,
ScrollableCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { useAuth } from "@/hooks/use-auth";
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
import Donasi_BoxStatus from "@/screens/Donation/BoxStatus";
import { apiDonationGetByStatus } from "@/service/api-client/api-donation";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import Donation_ScreenStatus from "@/screens/Donation/ScreenStatus";
import { useLocalSearchParams } from "expo-router";
export default function DonationStatus() {
const { user } = useAuth();
const { status } = useLocalSearchParams<{ status?: string }>();
const [activeCategory, setActiveCategory] = useState<string | null>(
status || "publish",
);
const [listData, setListData] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadList();
}, [activeCategory]),
);
const onLoadList = async () => {
try {
setLoadList(true);
const response = await apiDonationGetByStatus({
authorId: user?.id as string,
status: activeCategory as string,
});
setListData(response.data);
} catch (error) {
console.log("[ERROR]", error);
setListData(null);
} finally {
setLoadList(false);
}
};
const handlePress = (item: any) => {
setActiveCategory(item.value);
// tambahkan logika lain seperti filter dsb.
};
const scrollComponent = (
<ScrollableCustom
data={dummyMasterStatus.map((e, i) => ({
id: i,
label: e.label,
value: e.value,
}))}
onButtonPress={handlePress}
activeId={activeCategory as any}
/>
);
return (
<ViewWrapper hideFooter headerComponent={scrollComponent}>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
) : (
listData?.map((item: any, index: number) => (
<Donasi_BoxStatus
key={index}
data={item}
status={activeCategory as string}
/>
))
)}
</ViewWrapper>
<Donation_ScreenStatus initialStatus={status || "publish"} />
);
}

View File

@@ -37,7 +37,7 @@ export default function DonasiDetailStatus() {
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
}, [id]),
);
const onLoadData = async () => {
@@ -99,6 +99,7 @@ export default function DonasiDetailStatus() {
sisaHari={value.sisa}
reminder={value.reminder}
data={data}
showSisaHari={status === "publish" ? true : false}
bottomSection={
status === "publish" && (
<Donation_ProgressSection

View File

@@ -1,10 +1,12 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
InformationBox,
LandscapeFrameUploaded,
LoaderCustom,
NewWrapper,
SelectCustom,
Spacing,
StackCustom,
@@ -60,7 +62,7 @@ export default function DonationEdit() {
useCallback(() => {
onLoadData();
onLoadList();
}, [id])
}, [id]),
);
const onLoadData = async () => {
@@ -79,7 +81,6 @@ export default function DonationEdit() {
imageId: response.data.imageId,
});
}
} catch (error) {
console.log("[ERROR]", error);
}
@@ -182,7 +183,21 @@ export default function DonationEdit() {
};
return (
<ViewWrapper>
<NewWrapper
hideFooter
footerComponent={
<BoxButtonOnFooter>
<ButtonCustom
isLoading={isLoading}
onPress={() => {
handlerSubmitUpdate();
}}
>
Update
</ButtonCustom>
</BoxButtonOnFooter>
}
>
<InformationBox text="Lengkapi semua data di bawah untuk selanjutnya mengisi cerita penggalangan dana." />
{!data || loadList ? (
<LoaderCustom />
@@ -260,17 +275,9 @@ export default function DonationEdit() {
/>
<Spacing />
<ButtonCustom
isLoading={isLoading}
onPress={() => {
handlerSubmitUpdate();
}}
>
Update
</ButtonCustom>
</StackCustom>
)}
<Spacing />
</ViewWrapper>
</NewWrapper>
);
}

View File

@@ -1,5 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
InformationBox,
@@ -8,8 +9,8 @@ import {
StackCustom,
TextAreaCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import DIRECTORY_ID from "@/constants/directory-id";
import { useAuth } from "@/hooks/use-auth";
import {
@@ -112,7 +113,23 @@ export default function DonationCreateStory() {
};
return (
<ViewWrapper>
<NewWrapper
hideFooter
footerComponent={
<>
<BoxButtonOnFooter>
<ButtonCustom
isLoading={isLoading}
onPress={() => {
handlerSubmit();
}}
>
Simpan
</ButtonCustom>
</BoxButtonOnFooter>
</>
}
>
<StackCustom gap={"xs"}>
<InformationBox text="Cerita Anda adalah kunci untuk menginspirasi kebaikan. Jelaskan dengan jujur dan jelas tujuan penggalangan dana ini agar calon donatur memahami dampak positif yang dapat mereka wujudkan melalui kontribusi mereka." />
<TextAreaCustom
@@ -166,18 +183,8 @@ export default function DonationCreateStory() {
value={data.rekening}
onChangeText={(value) => setData({ ...data, rekening: value })}
/>
<Spacing />
<ButtonCustom
isLoading={isLoading}
onPress={() => {
handlerSubmit();
}}
>
Simpan
</ButtonCustom>
</StackCustom>
<Spacing />
</ViewWrapper>
</NewWrapper>
);
}

View File

@@ -1,4 +1,5 @@
import {
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
InformationBox,
@@ -8,8 +9,8 @@ import {
Spacing,
StackCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import DIRECTORY_ID from "@/constants/directory-id";
import { apiDonationCreate } from "@/service/api-client/api-donation";
import { apiMasterDonation } from "@/service/api-client/api-master";
@@ -43,7 +44,7 @@ export default function DonationCreate() {
useFocusEffect(
useCallback(() => {
onLoadList();
}, [])
}, []),
);
const onLoadList = async () => {
@@ -125,7 +126,24 @@ export default function DonationCreate() {
};
return (
<ViewWrapper>
<NewWrapper
hideFooter
footerComponent={
<>
<BoxButtonOnFooter>
<ButtonCustom
isLoading={isLoading}
onPress={() => {
handlerSubmit();
// router.push(`/donation/create-story?id=${"dasdsadsa"}`);
}}
>
Selanjutnya
</ButtonCustom>
</BoxButtonOnFooter>
</>
}
>
<StackCustom gap={"xs"}>
<InformationBox text="Lengkapi semua data di bawah untuk selanjutnya mengisi cerita penggalangan dana." />
@@ -201,20 +219,8 @@ export default function DonationCreate() {
onChange={(value: any) => setData({ ...data, durasiId: value })}
/>
)}
<Spacing />
<ButtonCustom
isLoading={isLoading}
onPress={() => {
handlerSubmit();
// router.push(`/donation/create-story?id=${"dasdsadsa"}`);
}}
>
Selanjutnya
</ButtonCustom>
<Spacing />
</StackCustom>
<Spacing />
</ViewWrapper>
</NewWrapper>
);
}

View File

@@ -73,7 +73,6 @@ export default function InvestmentDetailStatus() {
updateCountDown();
}, [data]);
console.log("[DATA DETAIL]", JSON.stringify(data, null, 2));
const updateCountDown = () => {
const countDown = countDownAndCondition({

View File

@@ -72,7 +72,6 @@ export default function InvestmentDetail() {
updateCountDown();
}, [data]);
console.log("[DATA DETAIL]", JSON.stringify(data, null, 2));
const updateCountDown = () => {
const countDown = countDownAndCondition({