Compare commits

...

3 Commits

Author SHA1 Message Date
d0abd14047 Fix Loaddata Voting
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
2026-02-04 17:44:57 +08:00
5b2be20469 Fix Loaddata pada event dan perbaikan tampilan pada NewWrapper
Event – User
- app/(application)/(user)/event/(tabs)/contribution.tsx
- app/(application)/(user)/event/(tabs)/index.tsx
- app/(application)/(user)/event/[id]/list-of-participants.tsx

Voting – User
- app/(application)/(user)/voting/(tabs)/history.tsx

Components
- components/Notification/NotificationInitializer.tsx
- components/_ShareComponent/NewWrapper.tsx

Screens – Event
- screens/Event/BoxPublishSection.tsx
- screens/Event/ButtonStatusSection.tsx
- screens/Event/ScreenHistory.tsx
- screens/Event/ScreenStatus.tsx

Screens – Forum
- screens/Forum/ViewBeranda3.tsx

API Client
- service/api-client/api-event.ts

Styles
- styles/global-styles.ts

Docs
- docs/prompt-for-qwen-code.md

Untracked (New Files)
- screens/Event/ScreenBeranda.tsx
- screens/Event/ScreenContribution.tsx
- screens/Event/ScreenListOfParticipants.tsx

#### No Issue
2026-02-04 16:56:48 +08:00
60177a1087 Fix Component Datetime IOS
Components
- components/DateInput/DataTimeAndroid.tsx
- components/DateInput/DateTimeIOS.tsx
- components/Notification/NotificationInitializer.tsx

Screens
- screens/Event/ScreenStatus.tsx

Docs
- docs/prompt-for-qwen-code.md

### No Issue
2026-02-04 12:00:00 +08:00
24 changed files with 647 additions and 484 deletions

View File

@@ -1,115 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { import Event_ScreenContribution from "@/screens/Event/ScreenContribution";
AvatarUsernameAndOtherComponent,
BoxWithHeaderSection,
LoaderCustom,
Spacing,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import { useAuth } from "@/hooks/use-auth";
import {
apiEventGetAll
} from "@/service/api-client/api-event";
import { dateTimeView } from "@/utils/dateTimeView";
import { useFocusEffect } from "expo-router";
import _ from "lodash";
import React, { useCallback, useState } from "react";
export default function EventContribution() { export default function EventContribution() {
const { user } = useAuth();
const [listData, setListData] = useState<any>([]);
const [isLoadList, setIsLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [user?.id])
);
async function onLoadData() {
try {
setIsLoadList(true);
const response = await apiEventGetAll({
category: "contribution",
userId: user?.id,
});
console.log("[DATA] ", JSON.stringify(response.data, null, 2));
if (response.success) {
setListData(response.data);
// const responseListParticipants = await apiEventListOfParticipants({
// id: response?.data?.Event?.id,
// });
// console.log(
// "[LIST PARTICIPANTS]",
// JSON.stringify(responseListParticipants.data, null, 2)
// );
// if (responseListParticipants.success) {
// setListParticipants(responseListParticipants.data);
// }
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoadList(false);
}
}
return ( return (
<ViewWrapper hideFooter> <>
{isLoadList ? ( <Event_ScreenContribution />
<LoaderCustom /> </>
) : _.isEmpty(listData) ? (
<TextCustom align="center">Belum ada kontribusi</TextCustom>
) : (
listData.map((item: any, index: number) => (
<BoxWithHeaderSection
key={index}
href={`/event/${item?.Event?.id}/contribution`}
>
<StackCustom>
<AvatarUsernameAndOtherComponent
avatar={item?.Event?.Author?.Profile?.imageId}
avatarHref={`/profile/${item?.Event?.Author?.Profile?.id}`}
name={item?.Event?.Author?.username}
rightComponent={
<TextCustom truncate>
{dateTimeView({
date: item?.Event?.tanggal,
withoutTime: true,
})}
</TextCustom>
}
/>
<TextCustom bold align="center" size="xlarge" truncate={2}>
{item?.Event?.title}
</TextCustom>
<Spacing height={0} />
{/* <Grid>
{item?.Event?.Event_Peserta?.map(
(item2: any, index2: number) => (
<Grid.Col
style={{ alignItems: "center" }}
span={12 / item?.Event?.Event_Peserta?.length}
key={index2}
>
<AvatarComp
size="base"
href={`/profile/${item2?.User?.Profile?.id}`}
fileId={item2?.User?.Profile?.imageId}
/>
</Grid.Col>
)
)}
</Grid> */}
</StackCustom>
</BoxWithHeaderSection>
))
)}
</ViewWrapper>
); );
} }

View File

@@ -1,63 +1,9 @@
import { LoaderCustom, TextCustom } from "@/components"; import Event_ScreenBeranda from "@/screens/Event/ScreenBeranda";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import FloatingButton from "@/components/Button/FloatingButton";
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
import { apiEventGetAll } from "@/service/api-client/api-event";
import { dateTimeView } from "@/utils/dateTimeView";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
export default function EventBeranda() { export default function EventBeranda() {
const [listData, setListData] = useState([]);
const [isLoadData, setIsLoadData] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [])
);
const onLoadData = async () => {
try {
setIsLoadData(true);
const response = await apiEventGetAll({category: "beranda"});
// console.log("Response", JSON.stringify(response.data, null, 2));
setListData(response.data);
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoadData(false);
}
};
return ( return (
<ViewWrapper <>
hideFooter <Event_ScreenBeranda />
floatingButton={ </>
<FloatingButton onPress={() => router.push("/event/create")} />
}
>
{isLoadData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center">Belum ada event</TextCustom>
) : (
listData.map((item: any, index) => (
<Event_BoxPublishSection
key={index}
href={`/event/${item.id}/publish`}
data={item}
rightComponentAvatar={
<TextCustom>
{dateTimeView({ date: item?.tanggal, withoutTime: true })}
</TextCustom>
}
/>
))
)}
</ViewWrapper>
); );
} }

View File

@@ -1,110 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { import Event_ScreenListOfParticipants from "@/screens/Event/ScreenListOfParticipants";
AvatarUsernameAndOtherComponent,
BadgeCustom,
BaseBox,
LoaderCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import {
apiEventGetOne,
apiEventListOfParticipants,
} from "@/service/api-client/api-event";
import dayjs, { Dayjs } from "dayjs";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { View } from "react-native";
export default function EventListOfParticipants() { export default function EventListOfParticipants() {
const { id } = useLocalSearchParams();
const [startDate, setStartDate] = useState<Dayjs | undefined>();
const [listData, setListData] = useState<any[] | null>(null);
const [loadtData, setLoadData] = useState(false);
useFocusEffect(
useCallback(() => {
handlerLoadData();
}, [id])
);
const handlerLoadData = () => {
try {
onLoadData();
onLoadList();
} catch (error) {
console.log("[ERROR]", error);
}
};
const onLoadData = async () => {
try {
const response = await apiEventGetOne({ id: id as string });
if (response.success) {
const date = dayjs(response.data.tanggal);
setStartDate(date);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const onLoadList = async () => {
try {
setLoadData(true);
const response = await apiEventListOfParticipants({ id: id as string });
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadData(false);
}
};
return ( return (
<ViewWrapper> <>
{loadtData && !listData ? ( <Event_ScreenListOfParticipants />
<LoaderCustom /> </>
) : _.isEmpty(listData) ? (
<TextCustom align="center" color="gray">
Belum ada peserta
</TextCustom>
) : (
listData?.map((item: any, index: number) => (
<BaseBox key={index}>
<AvatarUsernameAndOtherComponent
avatar={item?.User?.Profile?.imageId}
name={item?.User?.username}
avatarHref={`/profile/${item?.User?.Profile?.id}`}
rightComponent={
startDate && startDate.subtract(1, "hour").diff(dayjs()) < 0 ? (
<View
style={{
justifyContent: "flex-end",
}}
>
<BadgeCustom color={item?.isPresent ? "green" : "red"}>
{item?.isPresent ? "Hadir" : "Tidak Hadir"}
</BadgeCustom>
</View>
) : (
<View
style={{
justifyContent: "flex-end",
}}
>
<BadgeCustom color="gray">-</BadgeCustom>
</View>
)
}
/>
</BaseBox>
))
)}
</ViewWrapper>
); );
} }

View File

@@ -1,106 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { import Voting_ScreenStatus from "@/screens/Voting/ScreenStatus";
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";
export default function VotingStatus() { export default function VotingStatus() {
const { user } = useAuth();
const { status } = useLocalSearchParams<{ status?: string }>();
const id = user?.id || "";
const [activeCategory, setActiveCategory] = useState<string | null>(
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 = (
<ScrollableCustom
data={dummyMasterStatus.map((e, i) => ({
id: i,
label: e.label,
value: e.value,
}))}
onButtonPress={handlePress}
activeId={activeCategory as any}
/>
);
return ( return (
<ViewWrapper headerComponent={scrollComponent} hideFooter> <>
{loadingGetData ? ( <Voting_ScreenStatus />
<LoaderCustom /> </>
) : _.isEmpty(listData) ? (
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
) : (
listData.map((item: any, i: number) => (
<BaseBox
key={i}
paddingTop={20}
paddingBottom={20}
href={`/voting/${item.id}/${activeCategory}/detail`}
>
<StackCustom>
<TextCustom align="center" bold truncate={2} size="large">
{item?.title || ""}
</TextCustom>
<BadgeCustom
style={{ width: "70%", alignSelf: "center" }}
variant="light"
>
{item?.awalVote &&
dateTimeView({
date: item?.awalVote,
withoutTime: true,
})}{" "}
-{" "}
{item?.akhirVote &&
dateTimeView({ date: item?.akhirVote, withoutTime: true })}
</BadgeCustom>
</StackCustom>
</BaseBox>
))
)}
</ViewWrapper>
); );
} }

View File

@@ -3,11 +3,12 @@ import {
BoxButtonOnFooter, BoxButtonOnFooter,
ButtonCustom, ButtonCustom,
CenterCustom, CenterCustom,
NewWrapper,
Spacing, Spacing,
StackCustom, StackCustom,
TextAreaCustom, TextAreaCustom,
TextInputCustom, TextInputCustom,
ViewWrapper ViewWrapper,
} from "@/components"; } from "@/components";
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom"; import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
@@ -79,7 +80,9 @@ export default function VotingCreate() {
type: "success", type: "success",
text1: "Data berhasil disimpan", text1: "Data berhasil disimpan",
}); });
router.replace("/(application)/(user)/voting/(tabs)/status?status=review"); router.replace(
"/(application)/(user)/voting/(tabs)/status?status=review",
);
} else { } else {
Toast.show({ Toast.show({
type: "error", type: "error",
@@ -106,7 +109,7 @@ export default function VotingCreate() {
}; };
return ( return (
<ViewWrapper footerComponent={buttonSubmit()}> <NewWrapper footerComponent={buttonSubmit()}>
<StackCustom gap={"xs"}> <StackCustom gap={"xs"}>
<TextInputCustom <TextInputCustom
label="Judul Voting" label="Judul Voting"
@@ -142,7 +145,6 @@ export default function VotingCreate() {
} }
/> />
{listVote.map((item, index) => ( {listVote.map((item, index) => (
<TextInputCustom <TextInputCustom
key={index} key={index}
@@ -153,8 +155,8 @@ export default function VotingCreate() {
onChangeText={(value: any) => onChangeText={(value: any) =>
setListVote( setListVote(
listVote.map((item, i) => listVote.map((item, i) =>
i === index ? { ...item, value } : item i === index ? { ...item, value } : item,
) ),
) )
} }
/> />
@@ -198,6 +200,6 @@ export default function VotingCreate() {
<Spacing /> <Spacing />
</StackCustom> </StackCustom>
</ViewWrapper> </NewWrapper>
); );
} }

View File

@@ -1,11 +1,21 @@
import { BackButton, StackCustom, TextCustom, ViewWrapper } from "@/components"; import { BackButton, StackCustom, TextCustom, ViewWrapper } from "@/components";
import { Stack } from "expo-router"; import { router, Stack } from "expo-router";
export default function NotFoundScreen() { 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 ( return (
<> <>
<Stack.Screen <Stack.Screen
options={{ headerShown: true, title: "", headerLeft: () => <BackButton /> }} options={{ headerShown: true, title: "", headerLeft: () => <BackButton onPress={() => handleBack()} /> }}
/> />
<ViewWrapper> <ViewWrapper>
<StackCustom <StackCustom

View File

@@ -1,4 +1,3 @@
// DateTimeInput.tsx // DateTimeInput.tsx
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { GStyles } from "@/styles/global-styles"; import { GStyles } from "@/styles/global-styles";
@@ -7,7 +6,14 @@ import DateTimePicker, {
DateTimePickerEvent, DateTimePickerEvent,
} from "@react-native-community/datetimepicker"; } from "@react-native-community/datetimepicker";
import React, { useCallback, useState } from "react"; import React, { useCallback, useState } from "react";
import { Pressable, StyleProp, Text, View, ViewStyle } from "react-native"; import {
Keyboard,
Pressable,
StyleProp,
Text,
View,
ViewStyle,
} from "react-native";
import Grid from "../Grid/GridCustom"; import Grid from "../Grid/GridCustom";
import TextCustom from "../Text/TextCustom"; import TextCustom from "../Text/TextCustom";
@@ -53,7 +59,7 @@ const DateTimeInput_Android: React.FC<DateTimeInputProps> = ({
const [selectedDate, setSelectedDate] = useState<Date>(value as any); const [selectedDate, setSelectedDate] = useState<Date>(value as any);
const [selectedTime, setSelectedTime] = useState<Date>(value as any); const [selectedTime, setSelectedTime] = useState<Date>(value as any);
console.log("Date Android", value) console.log("Date Android", value);
// Fungsi untuk menggabungkan tanggal dan waktu // Fungsi untuk menggabungkan tanggal dan waktu
const combineDateAndTime = useCallback((date: Date, time: Date): Date => { const combineDateAndTime = useCallback((date: Date, time: Date): Date => {
@@ -62,7 +68,7 @@ const DateTimeInput_Android: React.FC<DateTimeInputProps> = ({
time.getHours(), time.getHours(),
time.getMinutes(), time.getMinutes(),
time.getSeconds(), time.getSeconds(),
time.getMilliseconds() time.getMilliseconds(),
); );
return combined; return combined;
}, []); }, []);
@@ -92,10 +98,12 @@ const DateTimeInput_Android: React.FC<DateTimeInputProps> = ({
}; };
const toggleDatePicker = () => { const toggleDatePicker = () => {
Keyboard.dismiss();
setShowDate(!showDate); setShowDate(!showDate);
}; };
const toggleTimePicker = () => { const toggleTimePicker = () => {
Keyboard.dismiss();
setShowTime(!showTime); setShowTime(!showTime);
}; };

View File

@@ -1,13 +1,22 @@
// DateTimeInput.tsx // DateTimeInput.tsx
import { MainColor } from "@/constants/color-palet"; import { AccentColor, MainColor } from "@/constants/color-palet";
import { GStyles } from "@/styles/global-styles"; import { GStyles } from "@/styles/global-styles";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import DateTimePicker, { import DateTimePicker, {
DateTimePickerEvent, DateTimePickerEvent,
} from "@react-native-community/datetimepicker"; } from "@react-native-community/datetimepicker";
import dayjs from "dayjs"; import dayjs from "dayjs";
import React, { useState } from "react"; import React, { useState, useRef } from "react";
import { Button, StyleProp, Text, View, ViewStyle } from "react-native"; import {
Button,
StyleProp,
Text,
View,
ViewStyle,
Keyboard,
TouchableOpacity,
Modal,
} from "react-native";
import ClickableCustom from "../Clickable/ClickableCustom"; import ClickableCustom from "../Clickable/ClickableCustom";
import TextCustom from "../Text/TextCustom"; import TextCustom from "../Text/TextCustom";
@@ -50,20 +59,35 @@ const DateTimeInput_IOS: React.FC<DateTimeInputProps> = ({
}) => { }) => {
const [show, setShow] = useState(false); const [show, setShow] = useState(false);
const [selectedDate, setSelectedDate] = useState<Date | undefined>( const [selectedDate, setSelectedDate] = useState<Date | undefined>(
value as any value as any,
);
// State sementara untuk menyimpan nilai yang dipilih
const [tempSelectedDate, setTempSelectedDate] = useState<Date | undefined>(
value as any,
); );
const handleConfirm = (event: any, date?: Date) => { const handleConfirm = (event: any, date?: Date) => {
if (event.type === "set" && date !== undefined) { if (event.type === "set" && date !== undefined) {
setSelectedDate(date); // Hanya perbarui state sementara, bukan state utama
onChange(date as any); setTempSelectedDate(date);
} }
}; };
const handlePress = () => { const handlePress = () => {
// Sembunyikan keyboard sebelum menampilkan date picker
Keyboard.dismiss();
// Set state sementara ke nilai saat ini
setTempSelectedDate(selectedDate);
setShow(!show); setShow(!show);
}; };
// Fungsi untuk menangani klik di luar area picker
const handleOutsidePress = () => {
if (show) {
setShow(false);
}
};
return ( return (
<> <>
<ClickableCustom <ClickableCustom
@@ -112,84 +136,125 @@ const DateTimeInput_IOS: React.FC<DateTimeInputProps> = ({
))} ))}
</ClickableCustom> </ClickableCustom>
{show && ( <Modal
<> animationType="fade"
<View transparent={true}
visible={show}
onRequestClose={() => setShow(false)}
>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.5)", // Efek blur dengan background semi-transparan
}}
>
<TouchableOpacity
style={{ style={{
position: "absolute", position: "absolute",
zIndex: 15, top: 0,
backgroundColor: "white",
borderRadius: 8,
padding: 10,
// top: 0,
bottom: 0, bottom: 0,
left: 0, left: 0,
right: 0, right: 0,
borderColor: "#ccc", }}
activeOpacity={1}
onPress={handleOutsidePress}
>
<View style={{ flex: 1 }} />
</TouchableOpacity>
<View
style={{
zIndex: 15,
backgroundColor: MainColor.white_gray,
borderRadius: 16,
padding: 20,
marginHorizontal: 20,
width: "95%",
maxWidth: 400,
borderColor: MainColor.placeholder,
borderWidth: 1, borderWidth: 1,
}} }}
onStartShouldSetResponder={() => true} // Mencegah event bubbling ke TouchableOpacity induk
onResponderRelease={() => {}} // Handler kosong untuk mencegah event bubbling
> >
{/* <View style={{ alignItems: "flex-start" }}> {/* <View
<Ionicons style={{
name="close" flexDirection: "row",
size={20} justifyContent: "space-between",
color="black" alignItems: "center",
onPress={() => { marginBottom: 10,
setShow(false); }}
setSelectedDate(undefined); >
<Text
style={{
fontSize: 18,
fontWeight: "bold",
color: MainColor.black,
}} }}
/> >
Pilih Tanggal & Waktu
</Text>
<TouchableOpacity onPress={() => setShow(false)}>
<Ionicons
name="close"
size={24}
color={AccentColor.blackgray}
/>
</TouchableOpacity>
</View> */} </View> */}
<DateTimePicker <DateTimePicker
value={selectedDate || new Date()} value={tempSelectedDate || new Date()}
mode={"datetime"} mode={"datetime"}
display="spinner" display="inline"
onChange={handleConfirm} onChange={handleConfirm}
minimumDate={minimumDate} minimumDate={minimumDate}
maximumDate={maximumDate} maximumDate={maximumDate}
themeVariant="light" themeVariant="light"
/> />
<View style={{ flexDirection: "row", gap: 10 }}> <View style={{ flexDirection: "row", gap: 10, marginTop: 15 }}>
<ClickableCustom <TouchableOpacity
onPress={() => { onPress={() => {
setShow(false) setShow(false);
setSelectedDate(undefined) // Kembalikan ke nilai sebelumnya jika batal
setTempSelectedDate(selectedDate);
}} }}
style={{ style={{
flex: 1,
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
padding: 12, padding: 12,
borderRadius: 10, borderRadius: 10,
backgroundColor: MainColor.placeholder, backgroundColor: MainColor.placeholder,
marginTop: 10,
width: "48%",
}} }}
> >
<TextCustom color="black">Batal</TextCustom> <TextCustom color="black">Batal</TextCustom>
</ClickableCustom> </TouchableOpacity>
<ClickableCustom <TouchableOpacity
onPress={() => { onPress={() => {
setShow(false) // Simpan nilai yang dipilih ke state utama hanya saat OK ditekan
onChange(selectedDate as any) setSelectedDate(tempSelectedDate);
onChange(tempSelectedDate as any);
setShow(false);
}} }}
style={{ style={{
flex: 1,
alignItems: "center", alignItems: "center",
justifyContent: "center", justifyContent: "center",
padding: 12, padding: 12,
borderRadius: 10, borderRadius: 10,
backgroundColor: MainColor.darkblue, backgroundColor: MainColor.darkblue,
marginTop: 10,
width: "48%",
}} }}
> >
<TextCustom>OK</TextCustom> <TextCustom>OK</TextCustom>
</ClickableCustom> </TouchableOpacity>
</View> </View>
</View> </View>
</> </View>
)} </Modal>
</> </>
); );
}; };

View File

@@ -49,9 +49,8 @@ export default function NotificationInitializer() {
const fcmToken = await getToken(messagingInstance); const fcmToken = await getToken(messagingInstance);
if (!fcmToken) { if (!fcmToken) {
console.warn("Tidak bisa mendapatkan FCM token"); console.log("Tidak bisa mendapatkan FCM token");
// logout(); // logout();
return;
} }
console.log("✅ FCM Token:", fcmToken); console.log("✅ FCM Token:", fcmToken);

View File

@@ -101,12 +101,14 @@ const NewWrapper = (props: NewWrapperProps) => {
renderItem={listProps.renderItem} renderItem={listProps.renderItem}
keyExtractor={ keyExtractor={
listProps.keyExtractor || listProps.keyExtractor ||
((item) => { ((item, index) => {
if (item.id == null) { if (item.id == null) {
console.warn("Item tanpa 'id':", item); console.warn("Item tanpa 'id':", item);
return `fallback-${JSON.stringify(item)}`; return `fallback-${index}-${JSON.stringify(item)}`;
} }
return String(item.id);
// Gabungkan ID dengan indeks untuk mencegah duplikasi
return `${String(item.id)}-${index}`;
}) })
} }

View File

@@ -1,25 +1,26 @@
<!-- Start Penerapan Pagination --> <!-- ===================== Start Penerapan Pagination ===================== -->
File utama: screens/Event/ScreenHistory.tsx File utama: screens/Voting/ScreenStatus.tsx
Fun fecth: apiEventGetAll Function fecth: apiVotingGetByStatus
File fetch: service/api-client/api-event.ts File function fetch: service/api-client/api-voting.ts
File komponen wrapper: components/_ShareComponent/NewWrapper.tsx File komponen wrapper: components/_ShareComponent/NewWrapper.tsx
File refrensi: screens/Job/MainViewStatus2.tsx
Terapkan pagination pada file "File utama" 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 Analisa juga file "File utama" , jika belum menggunakan NewWrapper pada file "File komponen wrapper" , maka terapkan juga dan ganti wrapper lama yaitu komponen ViewWrapper
Komponen pagination yang digunaka berada pada file hooks/use-pagination.tsx dan helpers/paginationHelpers.tsx Komponen pagination yang digunaka berada pada file hooks/use-pagination.tsx dan helpers/paginationHelpers.tsx
Perbaiki fetch "Fun fecth" , pada file "File fetch" Perbaiki fetch "Function fecth" , pada file "File function fetch"
Jika tidak ada props page maka tambahkan props page dan default page: "1" 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. Gunakan bahasa indonesia pada cli agar saya mudah membacanya.
<!-- End Penerapan Pagination --> File refrensi: screens/Event/ScreenStatus.tsx
Anda bisa menggunakan refrensi dari "File refrensi" jika butuh pemahaman dengan tipe fitur yang sama
<!-- ===================== End Penerapan Pagination ===================== -->
<!-- Start Penerapan NewWrapper --> <!-- Start Penerapan NewWrapper -->
Terapkan NewWrapper pada file: screens/Forum/DetailForum.tsx Terapkan NewWrapper pada file: screens/Forum/DetailForum.tsx
Component yang digunakan: components/_ShareComponent/NewWrapper.tsx , karena ini adalah halaman detail saya ingin anda fokus pada props pada NewWrapper. Seperti Component yang digunakan: components/_ShareComponent/NewWrapper.tsx , karena ini adalah halaman detail saya ingin anda fokus pada props pada NewWrapper. Seperti
<!-- -->

View File

@@ -1,6 +1,7 @@
import { import {
AvatarUsernameAndOtherComponent, AvatarUsernameAndOtherComponent,
BoxWithHeaderSection, BoxWithHeaderSection,
Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom,
} from "@/components"; } from "@/components";

View File

@@ -31,7 +31,7 @@ export default function Event_ButtonStatusSection({
type: "success", type: "success",
text1: response.message, text1: response.message,
}); });
router.back(); router.replace(`/event/(tabs)/status?status=draft`);
} else { } else {
Toast.show({ Toast.show({
type: "info", type: "info",
@@ -65,7 +65,7 @@ export default function Event_ButtonStatusSection({
type: "success", type: "success",
text1: response.message, text1: response.message,
}); });
router.back(); router.replace(`/event/(tabs)/status?status=review`);
} else { } else {
Toast.show({ Toast.show({
type: "info", type: "info",
@@ -99,7 +99,7 @@ export default function Event_ButtonStatusSection({
type: "success", type: "success",
text1: response.message, text1: response.message,
}); });
router.back(); router.replace(`/event/(tabs)/status?status=draft`);
} else { } else {
Toast.show({ Toast.show({
type: "info", type: "info",

View File

@@ -0,0 +1,75 @@
import { TextCustom } from "@/components";
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import FloatingButton from "@/components/Button/FloatingButton";
import { MainColor } from "@/constants/color-palet";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
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 { router } from "expo-router";
import { RefreshControl } from "react-native";
export default function Event_ScreenBeranda() {
// Setup pagination
const pagination = usePagination({
fetchFunction: async (page) => {
return await apiEventGetAll({
category: "beranda",
page: String(page),
});
},
pageSize: PAGINATION_DEFAULT_TAKE,
dependencies: [],
onError: (error) => console.error("[ERROR] Fetch event beranda:", error),
});
// Generate komponen
const { ListEmptyComponent, ListFooterComponent } = createPaginationComponents({
loading: pagination.loading,
refreshing: pagination.refreshing,
listData: pagination.listData,
emptyMessage: "Belum ada event",
skeletonCount: PAGINATION_DEFAULT_TAKE,
skeletonHeight: 100,
});
// Render item event
const renderEventItem = ({ item }: { item: any }) => (
<Event_BoxPublishSection
key={item.id}
href={`/event/${item.id}/publish`}
data={item}
rightComponentAvatar={
<TextCustom>
{dateTimeView({ date: item?.tanggal, withoutTime: true })}
</TextCustom>
}
/>
);
return (
<NewWrapper
listData={pagination.listData}
renderItem={renderEventItem}
refreshControl={
<RefreshControl
tintColor={MainColor.yellow}
colors={[MainColor.yellow]}
refreshing={pagination.refreshing}
onRefresh={pagination.onRefresh}
/>
}
onEndReached={pagination.loadMore}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
hideFooter
floatingButton={
<FloatingButton onPress={() => router.push("/event/create")} />
}
/>
);
}

View File

@@ -0,0 +1,102 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AvatarUsernameAndOtherComponent,
BoxWithHeaderSection,
Spacing,
StackCustom,
TextCustom
} from "@/components";
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
import { useAuth } from "@/hooks/use-auth";
import { usePagination } from "@/hooks/use-pagination";
import { apiEventGetAll } from "@/service/api-client/api-event";
import { dateTimeView } from "@/utils/dateTimeView";
import { RefreshControl } from "react-native";
export default function Event_ScreenContribution() {
const { user } = useAuth();
// Setup pagination
const pagination = usePagination({
fetchFunction: async (page) => {
if (!user?.id) return { data: [] };
return await apiEventGetAll({
category: "contribution",
userId: user?.id,
page: String(page),
});
},
pageSize: PAGINATION_DEFAULT_TAKE,
dependencies: [user?.id],
onError: (error) =>
console.error("[ERROR] Fetch event contribution:", error),
});
// Generate komponen
const { ListEmptyComponent, ListFooterComponent } =
createPaginationComponents({
loading: pagination.loading,
refreshing: pagination.refreshing,
listData: pagination.listData,
emptyMessage: "Belum ada kontribusi",
skeletonCount: PAGINATION_DEFAULT_TAKE,
skeletonHeight: 100,
});
// Render item event
const renderEventItem = ({ item }: { item: any }) => (
<BoxWithHeaderSection
key={item?.id}
href={`/event/${item?.Event?.id}/contribution`}
>
<StackCustom>
<AvatarUsernameAndOtherComponent
avatar={item?.Event?.Author?.Profile?.imageId}
avatarHref={`/profile/${item?.Event?.Author?.Profile?.id}`}
name={item?.Event?.Author?.username}
rightComponent={
<TextCustom truncate>
{dateTimeView({
date: item?.Event?.tanggal,
withoutTime: true,
})}
</TextCustom>
}
/>
<TextCustom bold align="center" size="xlarge" truncate={2}>
{item?.Event?.title}
</TextCustom>
<Spacing height={0} />
</StackCustom>
</BoxWithHeaderSection>
);
// useEffect(() => {
// pagination.onRefresh();
// }, []);
return (
<NewWrapper
listData={pagination.listData}
renderItem={renderEventItem}
refreshControl={
<RefreshControl
tintColor={MainColor.yellow}
colors={[MainColor.yellow]}
refreshing={pagination.refreshing}
onRefresh={pagination.onRefresh}
/>
}
onEndReached={pagination.loadMore}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
hideFooter
/>
);
}

View File

@@ -2,6 +2,7 @@
import { ButtonCustom, Spacing, TextCustom } from "@/components"; import { ButtonCustom, Spacing, TextCustom } from "@/components";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { AccentColor, MainColor } from "@/constants/color-palet"; import { AccentColor, MainColor } from "@/constants/color-palet";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { createPaginationComponents } from "@/helpers/paginationHelpers"; import { createPaginationComponents } from "@/helpers/paginationHelpers";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { usePagination } from "@/hooks/use-pagination"; import { usePagination } from "@/hooks/use-pagination";
@@ -12,8 +13,6 @@ import _ from "lodash";
import { useState } from "react"; import { useState } from "react";
import { RefreshControl, View } from "react-native"; import { RefreshControl, View } from "react-native";
const PAGE_SIZE = 5;
export default function Event_ScreenHistory() { export default function Event_ScreenHistory() {
const [activeCategory, setActiveCategory] = useState<string | null>("all"); const [activeCategory, setActiveCategory] = useState<string | null>("all");
const { user } = useAuth(); const { user } = useAuth();
@@ -27,25 +26,26 @@ export default function Event_ScreenHistory() {
page: String(page), page: String(page),
}); });
}, },
pageSize: PAGE_SIZE, pageSize: PAGINATION_DEFAULT_TAKE,
dependencies: [user?.id, activeCategory], dependencies: [user?.id, activeCategory],
onError: (error) => console.error("[ERROR] Fetch event history:", error), onError: (error) => console.error("[ERROR] Fetch event history:", error),
}); });
// Generate komponen // Generate komponen
const { ListEmptyComponent, ListFooterComponent } = createPaginationComponents({ const { ListEmptyComponent, ListFooterComponent } =
loading: pagination.loading, createPaginationComponents({
refreshing: pagination.refreshing, loading: pagination.loading,
listData: pagination.listData, refreshing: pagination.refreshing,
emptyMessage: "Belum ada riwayat", listData: pagination.listData,
skeletonCount: 5, emptyMessage: "Belum ada riwayat",
skeletonHeight: 100, skeletonCount: PAGINATION_DEFAULT_TAKE,
}); skeletonHeight: 100,
});
// Render item event // Render item event
const renderEventItem = ({ item }: { item: any }) => ( const renderEventItem = ({ item }: { item: any }) => (
<Event_BoxPublishSection <Event_BoxPublishSection
key={item.id} key={item && item?.id}
data={item} data={item}
rightComponentAvatar={ rightComponentAvatar={
<TextCustom> <TextCustom>

View File

@@ -0,0 +1,121 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AvatarUsernameAndOtherComponent,
BadgeCustom,
BaseBox
} from "@/components";
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet";
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
import { usePagination } from "@/hooks/use-pagination";
import {
apiEventGetOne,
apiEventListOfParticipants,
} from "@/service/api-client/api-event";
import dayjs, { Dayjs } from "dayjs";
import { useLocalSearchParams } from "expo-router";
import { useEffect, useState } from "react";
import { RefreshControl, View } from "react-native";
export default function Event_ScreenListOfParticipants() {
const { id } = useLocalSearchParams();
const [startDate, setStartDate] = useState<Dayjs | undefined>();
// Setup pagination
const pagination = usePagination({
fetchFunction: async (page) => {
return await apiEventListOfParticipants({
id: id as string,
page: String(page),
});
},
pageSize: PAGINATION_DEFAULT_TAKE,
dependencies: [id],
onError: (error) =>
console.error("[ERROR] Fetch event participants:", error),
});
useEffect(() => {
onLoadData();
}, []);
// Fetch event data separately (not part of pagination)
// useFocusEffect(() => {
// onLoadData();
// pagination.onRefresh();
// });
const onLoadData = async () => {
try {
const response = await apiEventGetOne({ id: id as string });
if (response.success) {
const date = dayjs(response.data.tanggal);
setStartDate(date);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
// Generate komponen
const { ListEmptyComponent, ListFooterComponent } =
createPaginationComponents({
loading: pagination.loading,
refreshing: pagination.refreshing,
listData: pagination.listData,
emptyMessage: "Belum ada peserta",
skeletonCount: PAGINATION_DEFAULT_TAKE,
skeletonHeight: 100,
});
// Render item participant
const renderParticipantItem = ({ item }: { item: any }) => (
<BaseBox key={item.id}>
<AvatarUsernameAndOtherComponent
avatar={item?.User?.Profile?.imageId}
name={item?.User?.username}
avatarHref={`/profile/${item?.User?.Profile?.id}`}
rightComponent={
startDate && startDate.subtract(1, "hour").diff(dayjs()) < 0 ? (
<View
style={{
justifyContent: "flex-end",
}}
>
<BadgeCustom color={item?.isPresent ? "green" : "red"}>
{item?.isPresent ? "Hadir" : "Tidak Hadir"}
</BadgeCustom>
</View>
) : (
<View
style={{
justifyContent: "flex-end",
}}
>
<BadgeCustom color="gray">-</BadgeCustom>
</View>
)
}
/>
</BaseBox>
);
return (
<NewWrapper
listData={pagination.listData}
renderItem={renderParticipantItem}
refreshControl={
<RefreshControl
tintColor={MainColor.yellow}
colors={[MainColor.yellow]}
refreshing={pagination.refreshing}
onRefresh={pagination.onRefresh}
/>
}
onEndReached={pagination.loadMore}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
/>
);
}

View File

@@ -7,26 +7,23 @@ import {
TextCustom, TextCustom,
} from "@/components"; } from "@/components";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet"; import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
import { createPaginationComponents } from "@/helpers/paginationHelpers"; import { createPaginationComponents } from "@/helpers/paginationHelpers";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { usePagination } from "@/hooks/use-pagination"; import { usePagination } from "@/hooks/use-pagination";
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status"; import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
import { apiEventGetByStatus } from "@/service/api-client/api-event"; import { apiEventGetByStatus } from "@/service/api-client/api-event";
import { useFocusEffect, useLocalSearchParams } from "expo-router"; import { useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useState } from "react"; import { useState } from "react";
import { RefreshControl, View } from "react-native"; import { RefreshControl, View } from "react-native";
const PAGE_SIZE = 10;
export default function Event_ScreenStatus() { export default function Event_ScreenStatus() {
const { user } = useAuth(); const { user } = useAuth();
const { status } = useLocalSearchParams<{ status?: string }>(); const { status } = useLocalSearchParams<{ status?: string }>();
const id = user?.id || ""; const id = user?.id || "";
const [activeCategory, setActiveCategory] = useState<string | null>( const [activeCategory, setActiveCategory] = useState<string | null>(
status || "publish" status || "publish",
); );
// Setup pagination // Setup pagination
@@ -40,20 +37,21 @@ export default function Event_ScreenStatus() {
page: String(page), page: String(page),
}); });
}, },
pageSize: PAGE_SIZE, pageSize: PAGINATION_DEFAULT_TAKE,
dependencies: [id, activeCategory], dependencies: [id, activeCategory],
onError: (error) => console.error("[ERROR] Fetch event by status:", error), onError: (error) => console.error("[ERROR] Fetch event by status:", error),
}); });
// Generate komponen // Generate komponen
const { ListEmptyComponent, ListFooterComponent } = createPaginationComponents({ const { ListEmptyComponent, ListFooterComponent } =
loading: pagination.loading, createPaginationComponents({
refreshing: pagination.refreshing, loading: pagination.loading,
listData: pagination.listData, refreshing: pagination.refreshing,
emptyMessage: `Tidak ada data ${activeCategory}`, listData: pagination.listData,
skeletonCount: 5, emptyMessage: `Tidak ada data ${activeCategory}`,
skeletonHeight: 100, skeletonCount: PAGINATION_DEFAULT_TAKE,
}); skeletonHeight: 100,
});
// Render item event // Render item event
const renderEventItem = ({ item }: { item: any }) => ( const renderEventItem = ({ item }: { item: any }) => (
@@ -100,11 +98,8 @@ export default function Event_ScreenStatus() {
return ( return (
<NewWrapper <NewWrapper
headerComponent={ hideFooter
<View style={{ paddingTop: 8 }}> headerComponent={<View style={{ paddingTop: 8 }}>{tabsComponent}</View>}
{tabsComponent}
</View>
}
listData={pagination.listData} listData={pagination.listData}
renderItem={renderEventItem} renderItem={renderEventItem}
refreshControl={ refreshControl={

View File

@@ -2,7 +2,7 @@ import {
AvatarComp, AvatarComp,
BackButton, BackButton,
FloatingButton, FloatingButton,
SearchInput SearchInput,
} from "@/components"; } from "@/components";
import NewWrapper from "@/components/_ShareComponent/NewWrapper"; import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
@@ -101,8 +101,9 @@ export default function Forum_ViewBeranda3() {
/> />
<NewWrapper <NewWrapper
hideFooter
headerComponent={ headerComponent={
<View style={{ paddingTop: 8 }}> <View style={{ paddingTop: 8 }}>
<SearchInput <SearchInput
placeholder="Cari topik diskusi" placeholder="Cari topik diskusi"
onChangeText={_.debounce((text) => setSearch(text), 500)} onChangeText={_.debounce((text) => setSearch(text), 500)}

View File

@@ -36,7 +36,7 @@ export default function Voting_ButtonStatusSection({
type: "success", type: "success",
text1: response.message, text1: response.message,
}); });
router.back(); router.replace(`/voting/${id}/draft/detail`);
} else { } else {
Toast.show({ Toast.show({
type: "info", type: "info",
@@ -73,7 +73,7 @@ export default function Voting_ButtonStatusSection({
type: "success", type: "success",
text1: response.message, text1: response.message,
}); });
router.back(); router.replace(`/voting/${id}/review/detail`);
} else { } else {
Toast.show({ Toast.show({
type: "info", type: "info",
@@ -110,7 +110,7 @@ export default function Voting_ButtonStatusSection({
type: "success", type: "success",
text1: response.message, text1: response.message,
}); });
router.back(); router.replace(`/voting/${id}/draft/detail`);
} else { } else {
Toast.show({ Toast.show({
type: "info", type: "info",

View File

@@ -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<string | null>(
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 }) => (
<BaseBox
key={item.id}
paddingTop={20}
paddingBottom={20}
href={`/voting/${item.id}/${activeCategory}/detail`}
>
<StackCustom>
<TextCustom align="center" bold truncate={2} size="large">
{item?.title || ""}
</TextCustom>
<BadgeCustom
style={{ width: "70%", alignSelf: "center" }}
variant="light"
>
{item?.awalVote &&
dateTimeView({
date: item?.awalVote,
withoutTime: true,
})}{" "}
-{" "}
{item?.akhirVote &&
dateTimeView({ date: item?.akhirVote, withoutTime: true })}
</BadgeCustom>
</StackCustom>
</BaseBox>
);
const handlePress = (item: any) => {
setActiveCategory(item.value);
// Reset pagination saat kategori berubah
pagination.reset();
};
const scrollComponent = (
<ScrollableCustom
data={dummyMasterStatus.map((e, i) => ({
id: i,
label: e.label,
value: e.value,
}))}
onButtonPress={handlePress}
activeId={activeCategory as any}
/>
);
return (
<NewWrapper
headerComponent={<View style={{ paddingTop: 8 }}>{scrollComponent}</View>}
listData={pagination.listData}
renderItem={renderVotingItem}
refreshControl={
<RefreshControl
tintColor={MainColor.yellow}
colors={[MainColor.yellow]}
refreshing={pagination.refreshing}
onRefresh={pagination.onRefresh}
/>
}
onEndReached={pagination.loadMore}
ListEmptyComponent={ListEmptyComponent}
ListFooterComponent={ListFooterComponent}
hideFooter
/>
);
}

View File

@@ -117,9 +117,16 @@ export async function apiEventJoin({
} }
} }
export async function apiEventListOfParticipants({ id }: { id?: string }) { export async function apiEventListOfParticipants({
id,
page = "1"
}: {
id?: string;
page?: string;
}) {
try { try {
const response = await apiConfig.get(`/mobile/event/${id}/participants`); const pageParam = page ? `?page=${page}` : "";
const response = await apiConfig.get(`/mobile/event/${id}/participants${pageParam}`);
return response.data; return response.data;
} catch (error) { } catch (error) {
throw error; throw error;

View File

@@ -14,12 +14,14 @@ export async function apiVotingCreate(data: any) {
export async function apiVotingGetByStatus({ export async function apiVotingGetByStatus({
id, id,
status, status,
page = "1",
}: { }: {
id: string; id: string;
status: string; status: string;
page?: string;
}) { }) {
try { try {
const response = await apiConfig.get(`/mobile/voting/${id}/${status}`); const response = await apiConfig.get(`/mobile/voting/${id}/${status}?page=${page}`);
return response.data; return response.data;
} catch (error) { } catch (error) {
throw error; throw error;

View File

@@ -17,7 +17,9 @@ export const GStyles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
paddingInline: PADDING_MEDIUM, paddingInline: PADDING_MEDIUM,
paddingBlock: PADDING_EXTRA_SMALL, paddingTop: PADDING_EXTRA_SMALL,
paddingBottom: 5,
// paddingBlock: PADDING_EXTRA_SMALL,
backgroundColor: MainColor.darkblue, backgroundColor: MainColor.darkblue,
}, },
containerWithBackground: { containerWithBackground: {