Compare commits

..

3 Commits

Author SHA1 Message Date
76fb14ed0c Event
Fix: Intergrasi pada UI
- Beranda, Kontibusi & Riwayat
- Detail beranda, detail kontribusi & detail riwayat
- List partisipan

### No issue
2025-09-15 17:17:50 +08:00
1d2153b253 Event
Fix: Intergrasi tampilan ke API

Package.json
Fix: Pem baharuan SDK53 -> SDK54

### No Issue
2025-09-12 15:57:30 +08:00
005b798688 Event
Fix:
- UI: status, detail status, delete button, detail utama, tampilan utama
- Semua terintergrasi ke API mobile

### No Issue
2025-09-12 14:14:37 +08:00
21 changed files with 1101 additions and 18485 deletions

View File

@@ -36,6 +36,7 @@ export default {
plugins: [ plugins: [
'expo-router', 'expo-router',
'expo-web-browser',
[ [
'expo-splash-screen', 'expo-splash-screen',
{ {

View File

@@ -1,43 +1,114 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
AvatarCustom,
AvatarUsernameAndOtherComponent, AvatarUsernameAndOtherComponent,
BoxWithHeaderSection, BoxWithHeaderSection,
Grid, LoaderCustom,
Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom,
ViewWrapper ViewWrapper
} from "@/components"; } from "@/components";
import React from "react"; 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,
});
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> <ViewWrapper hideFooter>
{Array.from({ length: 10 }).map((_, index) => ( {isLoadList ? (
<BoxWithHeaderSection key={index} href={`/event/${index}/contribution`}> <LoaderCustom />
<StackCustom> ) : _.isEmpty(listData) ? (
<AvatarUsernameAndOtherComponent <TextCustom align="center">Belum ada kontribusi</TextCustom>
avatarHref={`/profile/${index}`} ) : (
rightComponent={ listData.map((item: any, index: number) => (
<TextCustom truncate> <BoxWithHeaderSection
{new Date().toLocaleDateString()} key={index}
</TextCustom> href={`/event/${item?.Event?.id}/contribution`}
} >
/> <StackCustom>
<AvatarUsernameAndOtherComponent
avatar={item?.User?.Profile?.imageId}
avatarHref={`/profile/${item?.User?.Profile?.id}`}
name={item?.User?.username}
rightComponent={
<TextCustom truncate>
{dateTimeView({
date: item?.Event?.tanggal,
withoutTime: true,
})}
</TextCustom>
}
/>
<TextCustom bold align="center" size="xlarge"> <TextCustom bold align="center" size="xlarge">
Judul Event Disini {item?.Event?.title}
</TextCustom> </TextCustom>
<Spacing height={10} />
<Grid> {/* <Grid>
{Array.from({ length: 4 }).map((_, index2) => ( {item?.Event?.Event_Peserta?.map(
<Grid.Col span={3} key={index2}> (item2: any, index2: number) => (
<AvatarCustom size="sm" href={`/profile/${index2}`} /> <Grid.Col
</Grid.Col> style={{ alignItems: "center" }}
))} span={12 / item?.Event?.Event_Peserta?.length}
</Grid> key={index2}
</StackCustom> >
</BoxWithHeaderSection> <AvatarComp
))} size="base"
href={`/profile/${item2?.User?.Profile?.id}`}
fileId={item2?.User?.Profile?.imageId}
/>
</Grid.Col>
)
)}
</Grid> */}
</StackCustom>
</BoxWithHeaderSection>
))
)}
</ViewWrapper> </ViewWrapper>
); );
} }

View File

@@ -1,12 +1,41 @@
import { ButtonCustom, Spacing, TextCustom } from "@/components"; /* eslint-disable react-hooks/exhaustive-deps */
import { ButtonCustom, LoaderCustom, Spacing, TextCustom } from "@/components";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper"; import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { AccentColor, MainColor } from "@/constants/color-palet"; import { AccentColor, MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth";
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection"; import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
import { useState } from "react"; 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 { View } from "react-native";
export default function EventHistory() { export default function EventHistory() {
const [activeCategory, setActiveCategory] = useState<string | null>("all"); const [activeCategory, setActiveCategory] = useState<string | null>("all");
const { user } = useAuth();
const [listData, setListData] = useState<any>([]);
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) => { const handlePress = (item: any) => {
setActiveCategory(item); setActiveCategory(item);
@@ -52,17 +81,24 @@ export default function EventHistory() {
return ( return (
<ViewWrapper headerComponent={headerComponent} hideFooter> <ViewWrapper headerComponent={headerComponent} hideFooter>
{Array.from({ length: 10 }).map((_, index) => ( {isLoadList ? (
<Event_BoxPublishSection <LoaderCustom />
key={index.toString()} ) : _.isEmpty(listData) ? (
id={index.toString()} <TextCustom align="center">Belum ada riwayat</TextCustom>
username={`Riwayat ${activeCategory === "main" ? "Saya" : "Semua"}`} ) : (
rightComponentAvatar={ listData.map((item: any, index: number) => (
<TextCustom>{new Date().toLocaleDateString()}</TextCustom> <Event_BoxPublishSection
} key={index.toString()}
href={`/event/${index}/history`} data={item}
/> rightComponentAvatar={
))} <TextCustom>
{dateTimeView({ date: item?.tanggal, withoutTime: true })}
</TextCustom>
}
href={`/event/${item.id}/history`}
/>
))
)}
</ViewWrapper> </ViewWrapper>
); );
} }

View File

@@ -1,9 +1,36 @@
import { LoaderCustom, TextCustom } from "@/components";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper"; import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import FloatingButton from "@/components/Button/FloatingButton"; import FloatingButton from "@/components/Button/FloatingButton";
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection"; import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
import { router } from "expo-router"; 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 <ViewWrapper
hideFooter hideFooter
@@ -11,13 +38,24 @@ export default function EventBeranda() {
<FloatingButton onPress={() => router.push("/event/create")} /> <FloatingButton onPress={() => router.push("/event/create")} />
} }
> >
{Array.from({ length: 10 }).map((_, index) => ( {isLoadData ? (
<Event_BoxPublishSection <LoaderCustom />
key={index} ) : _.isEmpty(listData) ? (
id={index.toString()} <TextCustom align="center">Belum ada event</TextCustom>
href={`/event/${index}/publish`} ) : (
/> 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> </ViewWrapper>
); );
} }

View File

@@ -1,20 +1,43 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
DotButton, DotButton,
DrawerCustom, DrawerCustom,
LoaderCustom,
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
ViewWrapper,
Spacing, Spacing,
ViewWrapper,
} from "@/components"; } from "@/components";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection"; import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish"; import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
import { apiEventGetOne } from "@/service/api-client/api-event";
import { router, Stack, useLocalSearchParams } from "expo-router"; import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react"; import { useEffect, useState } from "react";
export default function EventDetailContribution() { export default function EventDetailContribution() {
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false); const [openDrawer, setOpenDrawer] = useState(false);
const [data, setData] = useState<any>();
const [isLoadData, setIsLoadData] = useState(false);
useEffect(() => {
onLoadData();
}, [id]);
const onLoadData = async () => {
try {
setIsLoadData(true);
const response = await apiEventGetOne({ id: id as string });
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoadData(false);
}
};
const handlePress = (item: IMenuDrawerItem) => { const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH ", item.path); console.log("PATH ", item.path);
@@ -32,18 +55,22 @@ export default function EventDetailContribution() {
}} }}
/> />
<ViewWrapper> <ViewWrapper>
<Event_BoxDetailPublishSection /> {isLoadData ? (
<LoaderCustom />
) : (
<Event_BoxDetailPublishSection data={data} />
)}
<Spacing /> <Spacing />
</ViewWrapper> </ViewWrapper>
<DrawerCustom <DrawerCustom
isVisible={openDrawer} isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)} closeDrawer={() => setOpenDrawer(false)}
height={250} height={"auto"}
> >
<MenuDrawerDynamicGrid <MenuDrawerDynamicGrid
data={menuDrawerPublishEvent({ id: id as string })} data={menuDrawerPublishEvent({ id: id as string })}
columns={4} columns={4}
onPressItem={handlePress} onPressItem={handlePress as any}
/> />
</DrawerCustom> </DrawerCustom>
</> </>

View File

@@ -80,7 +80,7 @@ export default function EventEdit() {
} }
}; };
const validateDate = () => { const validateDate = async () => {
if ( if (
data?.title === "" || data?.title === "" ||
data?.lokasi === "" || data?.lokasi === "" ||
@@ -92,7 +92,8 @@ export default function EventEdit() {
text1: "Info", text1: "Info",
text2: "Lengkapi semua data", text2: "Lengkapi semua data",
}); });
return;
return false;
} }
const startDate = new Date(selectedDate as any); const startDate = new Date(selectedDate as any);
@@ -104,12 +105,17 @@ export default function EventEdit() {
text1: "Info", text1: "Info",
text2: "Ubah tanggal berakhirnya event", text2: "Ubah tanggal berakhirnya event",
}); });
return;
return false;
} }
return true;
}; };
const handlerSubmit = async () => { const handlerSubmit = async () => {
validateDate(); const isValid = await validateDate();
if (!isValid) return;
try { try {
setIsLoading(true); setIsLoading(true);
const newData = { const newData = {

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
DotButton, DotButton,
DrawerCustom, DrawerCustom,
@@ -9,12 +10,29 @@ import { IMenuDrawerItem } from "@/components/_Interface/types";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection"; import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish"; import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
import { apiEventGetOne } from "@/service/api-client/api-event";
import { router, Stack, useLocalSearchParams } from "expo-router"; import { router, Stack, useLocalSearchParams } from "expo-router";
import { useState } from "react"; import { useEffect, useState } from "react";
export default function EventDetailHistory() { export default function EventDetailHistory() {
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false); const [openDrawer, setOpenDrawer] = useState(false);
const [data, setData] = useState();
useEffect(() => {
onLoadData();
}, [id]);
const onLoadData = async () => {
try {
const response = await apiEventGetOne({ id: id as string });
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const handlePress = (item: IMenuDrawerItem) => { const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH ", item.path); console.log("PATH ", item.path);
@@ -32,7 +50,7 @@ export default function EventDetailHistory() {
}} }}
/> />
<ViewWrapper> <ViewWrapper>
<Event_BoxDetailPublishSection /> <Event_BoxDetailPublishSection data={data} />
<Spacing /> <Spacing />
</ViewWrapper> </ViewWrapper>
<DrawerCustom <DrawerCustom
@@ -43,7 +61,7 @@ export default function EventDetailHistory() {
<MenuDrawerDynamicGrid <MenuDrawerDynamicGrid
data={menuDrawerPublishEvent({ id: id as string })} data={menuDrawerPublishEvent({ id: id as string })}
columns={4} columns={4}
onPressItem={handlePress} onPressItem={handlePress as any}
/> />
</DrawerCustom> </DrawerCustom>
</> </>

View File

@@ -1,17 +1,102 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
AvatarUsernameAndOtherComponent, AvatarUsernameAndOtherComponent,
BadgeCustom,
BaseBox, BaseBox,
LoaderCustom,
TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import {
apiEventGetOne,
apiEventListOfParticipants,
} from "@/service/api-client/api-event";
import { useLocalSearchParams } from "expo-router";
import { useEffect, useState } from "react";
import { View } from "react-native";
export default function EventListOfParticipants() { export default function EventListOfParticipants() {
const { id } = useLocalSearchParams();
const [startDate, setStartDate] = useState();
const [listData, setListData] = useState([]);
const [isLoadData, setIsLoadData] = useState(false);
useEffect(() => {
handlerLoadData();
}, [id]);
const handlerLoadData = () => {
try {
setIsLoadData(true);
onLoadData();
onLoadList();
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoadData(false);
}
};
const onLoadData = async () => {
try {
const response = await apiEventGetOne({ id: id as string });
if (response.success) {
setStartDate(response.data.tanggal);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const onLoadList = async () => {
try {
const response = await apiEventListOfParticipants({ id: id as string });
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
return ( return (
<ViewWrapper> <ViewWrapper>
{Array.from({ length: 10 }).map((_, index) => ( {isLoadData ? (
<BaseBox key={index} paddingBlock={0}> <LoaderCustom />
<AvatarUsernameAndOtherComponent avatarHref={`/profile/${index}`} /> ) : listData.length === 0 ? (
</BaseBox> <TextCustom align="center">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={
new Date().getTime() > new Date(startDate as any).getTime() ? (
<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> </ViewWrapper>
); );
} }

View File

@@ -1,22 +1,73 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
ButtonCustom, AlertDefaultSystem,
DotButton, ButtonCustom,
DrawerCustom, DotButton,
MenuDrawerDynamicGrid, DrawerCustom,
Spacing, LoaderCustom,
ViewWrapper MenuDrawerDynamicGrid,
ViewWrapper,
} from "@/components"; } from "@/components";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import LeftButtonCustom from "@/components/Button/BackButton"; import LeftButtonCustom from "@/components/Button/BackButton";
import { useAuth } from "@/hooks/use-auth";
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection"; import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish"; import { menuDrawerPublishEvent } from "@/screens/Event/menuDrawerPublish";
import { router, Stack, useLocalSearchParams } from "expo-router"; import {
import { useState } from "react"; apiEventCheckParticipants,
import { Alert } from "react-native"; apiEventGetOne,
apiEventJoin,
} from "@/service/api-client/api-event";
import {
router,
Stack,
useFocusEffect,
useLocalSearchParams,
} from "expo-router";
import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function EventDetailPublish() { export default function EventDetailPublish() {
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const { user } = useAuth();
const [openDrawer, setOpenDrawer] = useState(false); const [openDrawer, setOpenDrawer] = useState(false);
const [isLoadingData, setIsLoadingData] = useState(false);
const [isLoadingJoin, setIsLoadingJoin] = useState(false);
const [data, setData] = useState();
const [isParticipant, setIsParticipant] = useState<boolean | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [])
);
async function onLoadData() {
try {
setIsLoadingData(true);
const response = await apiEventGetOne({ id: id as string });
if (response.success) {
setData(response.data);
const responseCheckParticipants = await apiEventCheckParticipants({
id: id as string,
userId: user?.id as string,
});
if (
responseCheckParticipants.success &&
responseCheckParticipants.data
) {
setIsParticipant(true);
}
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoadingData(false);
}
}
const handlePress = (item: IMenuDrawerItem) => { const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH ", item.path); console.log("PATH ", item.path);
@@ -24,15 +75,61 @@ export default function EventDetailPublish() {
setOpenDrawer(false); setOpenDrawer(false);
}; };
const footerButton = ( const handlerJoin = async () => {
<ButtonCustom const userId = user?.id;
backgroundColor="green" if (!userId) {
textColor="white" return Toast.show({
onPress={() => Alert.alert("Anda berhasil join event ini")} type: "error",
> text2: "Anda belum login",
Join });
</ButtonCustom> }
);
try {
setIsLoadingJoin(true);
const response = await apiEventJoin({
id: id as string,
userId: userId as string,
});
if (response.success) {
router.navigate(
`/(application)/(user)/event/${id}/list-of-participants`
);
Toast.show({
type: "success",
text1: "Anda berhasil join",
});
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoadingJoin(false);
}
};
const footerButton = () => {
return (
<>
<ButtonCustom
disabled={isParticipant as any}
isLoading={isLoadingJoin}
backgroundColor="green"
textColor="white"
onPress={() =>
AlertDefaultSystem({
title: "Join event",
message: "Anda yakin ingin join sebagai peserta event ?",
textLeft: "Tidak",
textRight: "Ya",
onPressLeft: () => {},
onPressRight: () => handlerJoin(),
})
}
>
{isParticipant ? "Anda sudah tergabung" : "Join"}
</ButtonCustom>
</>
);
};
return ( return (
<> <>
@@ -44,19 +141,25 @@ export default function EventDetailPublish() {
}} }}
/> />
<ViewWrapper> <ViewWrapper>
<Event_BoxDetailPublishSection footerButton={footerButton} /> {isLoadingData ? (
<Spacing /> <LoaderCustom />
) : (
<Event_BoxDetailPublishSection
data={data}
footerButton={footerButton()}
/>
)}
</ViewWrapper> </ViewWrapper>
<DrawerCustom <DrawerCustom
isVisible={openDrawer} isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)} closeDrawer={() => setOpenDrawer(false)}
height={250} height={"auto"}
> >
<MenuDrawerDynamicGrid <MenuDrawerDynamicGrid
data={menuDrawerPublishEvent({ id: id as string })} data={menuDrawerPublishEvent({ id: id as string })}
columns={4} columns={4}
onPressItem={handlePress} onPressItem={handlePress as any}
/> />
</DrawerCustom> </DrawerCustom>
</> </>

View File

@@ -2,6 +2,7 @@ import {
AvatarComp, AvatarComp,
ClickableCustom, ClickableCustom,
Grid, Grid,
LoaderCustom,
Spacing, Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom,
@@ -19,6 +20,7 @@ import { useEffect, useState } from "react";
export default function UserSearch() { export default function UserSearch() {
const [data, setData] = useState<any[]>([]); const [data, setData] = useState<any[]>([]);
const [search, setSearch] = useState<string>(""); const [search, setSearch] = useState<string>("");
const [isLoadList, setIsLoadList] = useState(false);
useEffect(() => { useEffect(() => {
onLoadData(search); onLoadData(search);
@@ -26,11 +28,14 @@ export default function UserSearch() {
const onLoadData = async (search: string) => { const onLoadData = async (search: string) => {
try { try {
setIsLoadList(true);
const response = await apiAllUser({ search: search }); const response = await apiAllUser({ search: search });
console.log("[DATA USER] >", JSON.stringify(response.data, null, 2)); console.log("[DATA USER] >", JSON.stringify(response.data, null, 2));
setData(response.data); setData(response.data);
} catch (error) { } catch (error) {
console.log("Error fetching data", error); console.log("Error fetching data", error);
} finally {
setIsLoadList(false);
} }
}; };
@@ -60,7 +65,9 @@ export default function UserSearch() {
} }
> >
<StackCustom> <StackCustom>
{!_.isEmpty(data) ? ( {isLoadList ? (
<LoaderCustom />
) : !_.isEmpty(data) ? (
data?.map((e, index) => { data?.map((e, index) => {
return ( return (
<ClickableCustom <ClickableCustom

585
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { AccentColor, MainColor } from "@/constants/color-palet"; import { AccentColor, MainColor } from "@/constants/color-palet";
import { TEXT_SIZE_SMALL } from "@/constants/constans-value"; import { TEXT_SIZE_SMALL } from "@/constants/constans-value";
import { StyleSheet, Text, TouchableOpacity, View } from "react-native"; import { StyleSheet, Text, TouchableOpacity, View } from "react-native";
@@ -5,19 +6,23 @@ import { IMenuDrawerItem } from "../_Interface/types";
import { Href } from "expo-router"; import { Href } from "expo-router";
type IMenuDrawerItemProps = { type IMenuDrawerItemProps = {
icon: React.ReactNode; icon: React.ReactNode;
label: string; label: string;
value?: string; value?: string;
path?: Href | string; path?: Href | string;
color?: string; color?: string;
} };
interface MenuDrawerDynamicGridProps { interface MenuDrawerDynamicGridProps {
data: IMenuDrawerItemProps[]; data: IMenuDrawerItemProps[];
columns?: number; columns?: number;
onPressItem?: (item: IMenuDrawerItemProps) => void; onPressItem?: (item: IMenuDrawerItemProps) => void;
} }
const MenuDrawerDynamicGrid = ({ data, columns = 4, onPressItem }: MenuDrawerDynamicGridProps) => { const MenuDrawerDynamicGrid = ({
data,
columns = 4,
onPressItem,
}: MenuDrawerDynamicGridProps) => {
const numColumns = columns; const numColumns = columns;
return ( return (

View File

@@ -1,7 +1,6 @@
import { ImageSourcePropType } from "react-native";
import Divider from "../Divider/Divider"; import Divider from "../Divider/Divider";
import Grid from "../Grid/GridCustom"; import Grid from "../Grid/GridCustom";
import AvatarCustom from "../Image/AvatarCustom"; import AvatarComp from "../Image/AvatarComp";
import TextCustom from "../Text/TextCustom"; import TextCustom from "../Text/TextCustom";
const AvatarUsernameAndOtherComponent = ({ const AvatarUsernameAndOtherComponent = ({
@@ -12,7 +11,7 @@ const AvatarUsernameAndOtherComponent = ({
withBottomLine = false, withBottomLine = false,
}: { }: {
avatarHref?: string; avatarHref?: string;
avatar?: ImageSourcePropType; avatar?: string;
name?: string; name?: string;
rightComponent?: React.ReactNode; rightComponent?: React.ReactNode;
withBottomLine?: boolean; withBottomLine?: boolean;
@@ -21,7 +20,7 @@ const AvatarUsernameAndOtherComponent = ({
<> <>
<Grid containerStyle={{ zIndex: 10 }}> <Grid containerStyle={{ zIndex: 10 }}>
<Grid.Col span={2}> <Grid.Col span={2}>
<AvatarCustom source={avatar} href={avatarHref as any} /> <AvatarComp fileId={avatar} href={avatarHref as any} size="base" />
</Grid.Col> </Grid.Col>
<Grid.Col <Grid.Col
span={rightComponent ? 6 : 10} span={rightComponent ? 6 : 10}

18107
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,65 +11,69 @@
"lint": "expo lint" "lint": "expo lint"
}, },
"dependencies": { "dependencies": {
"@expo/vector-icons": "^14.1.0", "@expo/vector-icons": "^15.0.2",
"@react-native-async-storage/async-storage": "2.1.2", "@react-native-async-storage/async-storage": "2.2.0",
"@react-native-community/datetimepicker": "8.4.1", "@react-native-community/datetimepicker": "8.4.4",
"@react-navigation/bottom-tabs": "^7.4.2", "@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/drawer": "^7.5.2", "@react-navigation/drawer": "^7.3.9",
"@react-navigation/elements": "^2.3.8", "@react-navigation/elements": "^2.3.8",
"@react-navigation/native": "^7.1.6", "@react-navigation/native": "^7.1.6",
"@react-navigation/native-stack": "^7.3.21", "@react-navigation/native-stack": "^7.3.10",
"@types/lodash": "^4.17.20", "@types/lodash": "^4.17.20",
"@types/react-native-vector-icons": "^6.4.18", "@types/react-native-vector-icons": "^6.4.18",
"axios": "^1.11.0", "axios": "^1.11.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"expo": "53.0.17", "expo": "^54.0.0",
"expo-blur": "~14.1.5", "expo-blur": "~15.0.7",
"expo-camera": "~16.1.10", "expo-camera": "~17.0.7",
"expo-clipboard": "~7.1.5", "expo-clipboard": "~8.0.7",
"expo-constants": "~17.1.7", "expo-constants": "~18.0.8",
"expo-dev-client": "~5.2.4", "expo-dev-client": "~6.0.12",
"expo-document-picker": "~13.1.6", "expo-document-picker": "~14.0.7",
"expo-file-system": "~18.1.11", "expo-file-system": "~19.0.12",
"expo-font": "~13.3.2", "expo-font": "~14.0.8",
"expo-haptics": "~14.1.4", "expo-haptics": "~15.0.7",
"expo-image": "~2.3.2", "expo-image": "~3.0.8",
"expo-image-picker": "~16.1.4", "expo-image-picker": "~17.0.8",
"expo-linking": "~7.1.7", "expo-linking": "~8.0.8",
"expo-router": "~5.1.3", "expo-router": "~6.0.1",
"expo-splash-screen": "~0.30.10", "expo-splash-screen": "~31.0.9",
"expo-status-bar": "~2.2.3", "expo-status-bar": "~3.0.8",
"expo-symbols": "~0.4.5", "expo-symbols": "~1.0.7",
"expo-system-ui": "~5.0.10", "expo-system-ui": "~6.0.7",
"expo-web-browser": "~14.2.0", "expo-web-browser": "~15.0.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "19.0.0", "react": "19.1.0",
"react-dom": "19.0.0", "react-dom": "19.1.0",
"react-native": "0.79.5", "react-native": "0.81.4",
"react-native-dotenv": "^3.4.11", "react-native-dotenv": "^3.4.11",
"react-native-gesture-handler": "~2.24.0", "react-native-gesture-handler": "~2.28.0",
"react-native-international-phone-number": "^0.9.3", "react-native-international-phone-number": "^0.9.3",
"react-native-maps": "1.20.1", "react-native-maps": "1.20.1",
"react-native-otp-entry": "^1.8.5", "react-native-otp-entry": "^1.8.5",
"react-native-pager-view": "6.7.1", "react-native-pager-view": "6.9.1",
"react-native-paper": "^5.14.5", "react-native-paper": "^5.14.5",
"react-native-qrcode-svg": "^6.3.15", "react-native-qrcode-svg": "^6.3.15",
"react-native-reanimated": "~3.17.4", "react-native-reanimated": "~4.1.0",
"react-native-safe-area-context": "5.4.0", "react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.11.1", "react-native-screens": "~4.16.0",
"react-native-svg": "15.11.2", "react-native-svg": "15.12.1",
"react-native-toast-message": "^2.3.3", "react-native-toast-message": "^2.3.3",
"react-native-vector-icons": "^10.2.0", "react-native-vector-icons": "^10.2.0",
"react-native-web": "~0.20.0", "react-native-web": "^0.21.0",
"react-native-webview": "13.13.5" "react-native-webview": "13.15.0",
"react-native-worklets": "^0.5.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",
"@types/react": "~19.0.10", "@types/react": "~19.1.10",
"eslint": "^9.25.0", "eslint": "^9.25.0",
"eslint-config-expo": "~9.2.0", "eslint-config-expo": "~10.0.0",
"expo-module-scripts": "^4.1.10", "expo-module-scripts": "^4.1.10",
"typescript": "~5.8.3" "typescript": "~5.9.2"
}, },
"private": true "private": true,
"engines": {
"bun": ">=1.0.0"
}
} }

View File

@@ -98,7 +98,7 @@ export default function LoginView() {
} }
if (token && !isAdmin) { if (token && !isAdmin) {
return <Redirect href={"/(application)/(user)/event/(tabs)"} />; return <Redirect href={"/(application)/(user)/home"} />;
} }
if (token && isAdmin) { if (token && isAdmin) {

View File

@@ -1,21 +1,49 @@
import { import { AvatarUsernameAndOtherComponent, BoxWithHeaderSection, Grid, StackCustom, TextCustom } from "@/components";
BaseBox, import { dateTimeView } from "@/utils/dateTimeView";
Grid,
StackCustom,
TextCustom
} from "@/components";
export default function Event_BoxDetailPublishSection({ export default function Event_BoxDetailPublishSection({
data,
footerButton, footerButton,
}: { }: {
data: any;
footerButton?: React.ReactNode; footerButton?: React.ReactNode;
}) { }) {
const listData = [
{
title: "Lokasi",
value: data?.lokasi || "-",
},
{
title: "Tipe Acara",
value: data?.EventMaster_TipeAcara?.name || "-",
},
{
title: "Tanggal Mulai",
value: dateTimeView({ date: data?.tanggal }) || "-",
},
{
title: "Tanggal Berakhir",
value: dateTimeView({ date: data?.tanggalSelesai }) || "-",
},
{
title: "Deskripsi",
value: data?.deskripsi || "-",
},
];
return ( return (
<> <>
<BaseBox> <BoxWithHeaderSection>
<StackCustom> <StackCustom>
<AvatarUsernameAndOtherComponent
avatarHref={`/profile/${data?.Author?.Profile?.id}`}
name={data?.Author?.username || "-"}
avatar={data?.Author?.Profile?.imageId || ""}
/>
<TextCustom bold align="center" size="xlarge"> <TextCustom bold align="center" size="xlarge">
Judul event publish {data?.title || "-"}
</TextCustom> </TextCustom>
{listData.map((item, index) => ( {listData.map((item, index) => (
<Grid key={index}> <Grid key={index}>
@@ -28,34 +56,9 @@ export default function Event_BoxDetailPublishSection({
</Grid> </Grid>
))} ))}
</StackCustom> </StackCustom>
</BaseBox> </BoxWithHeaderSection>
{footerButton} {footerButton}
</> </>
); );
} }
const listData = [
{
title: "Lokasi",
value:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
},
{
title: "Tipe Acara",
value: "Workshop",
},
{
title: "Tanggal Mulai",
value: "Senin, 18 Juli 2025, 10:00 WIB",
},
{
title: "Tanggal Berakhir",
value: "Selasa, 19 Juli 2025, 12:00 WIB",
},
{
title: "Deskripsi",
value:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
},
];

View File

@@ -7,21 +7,15 @@ import {
import { Href } from "expo-router"; import { Href } from "expo-router";
export default function Event_BoxPublishSection({ export default function Event_BoxPublishSection({
id,
title,
username,
description,
href, href,
data,
// Avatar // Avatar
sourceAvatar, sourceAvatar,
rightComponentAvatar, rightComponentAvatar,
}: { }: {
id: string;
title?: string;
username?: string;
description?: string;
href: Href; href: Href;
data: any;
// Avatar // Avatar
sourceAvatar?: string; sourceAvatar?: string;
@@ -32,18 +26,15 @@ export default function Event_BoxPublishSection({
<BoxWithHeaderSection href={href}> <BoxWithHeaderSection href={href}>
<StackCustom gap={"xs"}> <StackCustom gap={"xs"}>
<AvatarUsernameAndOtherComponent <AvatarUsernameAndOtherComponent
avatarHref={`/profile/${id}`} avatarHref={`/profile/${data?.Author?.Profile?.id}`}
name={username || "Lorem ipsum dolor sit"} name={data?.Author?.username || "-"}
rightComponent={rightComponentAvatar} rightComponent={rightComponentAvatar}
avatar={sourceAvatar as any} avatar={data?.Author?.Profile?.imageId || ""}
/> />
<TextCustom truncate bold> <TextCustom truncate bold>
{title || "Lorem ipsum dolor sit"} {data?.title || "-"}
</TextCustom>
<TextCustom truncate={2}>
{description ||
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro sed doloremque tempora soluta. Dolorem ex quidem ipsum tempora, ipsa, obcaecati quia suscipit numquam, voluptates commodi porro impedit natus quos doloremque!"}
</TextCustom> </TextCustom>
<TextCustom truncate={2}>{data?.deskripsi || "-"}</TextCustom>
</StackCustom> </StackCustom>
</BoxWithHeaderSection> </BoxWithHeaderSection>
</> </>

View File

@@ -1,5 +1,8 @@
import { AlertDefaultSystem, ButtonCustom, Grid } from "@/components"; import { AlertDefaultSystem, ButtonCustom, Grid } from "@/components";
import { apiEventUpdateStatus } from "@/service/api-client/api-event"; import {
apiEventDelete,
apiEventUpdateStatus,
} from "@/service/api-client/api-event";
import { router } from "expo-router"; import { router } from "expo-router";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
@@ -118,9 +121,26 @@ export default function Event_ButtonStatusSection({
message: "Apakah Anda yakin ingin menghapus data ini?", message: "Apakah Anda yakin ingin menghapus data ini?",
textLeft: "Batal", textLeft: "Batal",
textRight: "Hapus", textRight: "Hapus",
onPressRight: () => { onPressRight: async () => {
console.log("Hapus"); try {
router.back(); const response = await apiEventDelete({ id: id });
if (response.success) {
Toast.show({
type: "success",
text1: response.message,
});
router.back();
} else {
Toast.show({
type: "info",
text1: "Info",
text2: response.message,
});
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
}
}, },
}); });
}; };

View File

@@ -44,15 +44,19 @@ export async function apiEventUpdateStatus({
}) { }) {
try { try {
const response = await apiConfig.put(`/mobile/event/${id}/${status}`); const response = await apiConfig.put(`/mobile/event/${id}/${status}`);
console.log("[SUCCESS]", response.data);
return response.data; return response.data;
} catch (error) { } catch (error) {
// console.log("[ERROR FECTH]", error);
throw error; throw error;
} }
} }
export async function apiEventUpdateData({ id, data }: { id: string; data: any }) { export async function apiEventUpdateData({
id,
data,
}: {
id: string;
data: any;
}) {
try { try {
const response = await apiConfig.put(`/mobile/event/${id}`, { const response = await apiConfig.put(`/mobile/event/${id}`, {
data: data, data: data,
@@ -62,3 +66,75 @@ export async function apiEventUpdateData({ id, data }: { id: string; data: any }
throw error; throw error;
} }
} }
export async function apiEventDelete({ id }: { id: string }) {
try {
const response = await apiConfig.delete(`/mobile/event/${id}`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiEventGetAll({
category,
userId,
}: {
category?: "beranda" | "contribution" | "all-history" | "my-history";
userId?: string;
}) {
try {
const categoryEvent = category ? `?category=${category}` : "";
const userIdCreator = userId ? `&userId=${userId}` : "";
const response = await apiConfig.get(
`/mobile/event${categoryEvent}${userIdCreator}`
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiEventJoin({
id,
userId,
}: {
id: string;
userId?: string;
}) {
try {
const response = await apiConfig.post(`/mobile/event/${id}/participants`, {
userId: userId,
});
return response.data;
} catch (error) {
throw error;
}
}
export async function apiEventListOfParticipants({ id }: { id?: string }) {
try {
const response = await apiConfig.get(`/mobile/event/${id}/participants`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiEventCheckParticipants({
id,
userId,
}: {
id: string;
userId?: string;
}) {
try {
const selectUserId = userId ? `?userId=${userId}` : "";
const response = await apiConfig.get(
`/mobile/event/${id}/check-participants${selectUserId}`
);
return response.data;
} catch (error) {
throw error;
}
}

View File

@@ -7,6 +7,8 @@ export const dateTimeView = ({
date: any; date: any;
withoutTime?: boolean; withoutTime?: boolean;
}) => { }) => {
const newDate = dayjs(date).format(`DD-MM-YYYY, ${withoutTime ? "" : "HH:mm"}`); const newDate = dayjs(date).format(
`DD-MM-YYYY ${withoutTime ? "" : ", HH:mm"}`
);
return newDate; return newDate;
}; };