Fix: Semua tampilan sudah terintegrasi API

### No Issue
This commit is contained in:
2025-09-19 17:51:08 +08:00
parent 391430de46
commit 333b1d2512
15 changed files with 521 additions and 120 deletions

View File

@@ -1,15 +1,57 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
LoaderCustom,
TextCustom,
ViewWrapper ViewWrapper
} from "@/components"; } from "@/components";
import { useAuth } from "@/hooks/use-auth";
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection"; import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
import { apiVotingGetAll } from "@/service/api-client/api-voting";
import { useFocusEffect } from "expo-router";
import _ from "lodash";
import { useState, useCallback } from "react";
export default function VotingContribution() { export default function VotingContribution() {
const { user } = useAuth();
const [listData, setListData] = useState<any>([]);
const [loadingGetData, setLoadingGetData] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [])
);
const onLoadData = async () => {
try {
setLoadingGetData(true);
const response = await apiVotingGetAll({
category: "contribution",
authorId: user?.id as string,
});
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadingGetData(false);
}
};
return ( return (
<ViewWrapper hideFooter> <ViewWrapper hideFooter>
{Array.from({ length: 5 }).map((_, index) => ( {loadingGetData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center">Tidak ada kontribusi</TextCustom>
) : listData.map((item: any, index: number) => (
<Voting_BoxPublishSection <Voting_BoxPublishSection
data={item}
key={index} key={index}
href={`/voting/${index}/contribution`} href={`/voting/${item.id}/contribution`}
/> />
))} ))}
</ViewWrapper> </ViewWrapper>

View File

@@ -1,11 +1,44 @@
import { ViewWrapper } from "@/components"; /* eslint-disable react-hooks/exhaustive-deps */
import { LoaderCustom, TextCustom, ViewWrapper } from "@/components";
import TabsTwoButtonCustom from "@/components/_ShareComponent/TabsTwoHeaderCustom"; import TabsTwoButtonCustom from "@/components/_ShareComponent/TabsTwoHeaderCustom";
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection"; import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
import { useState } from "react"; import { useAuth } from "@/hooks/use-auth";
import { useCallback, useState } from "react";
import { apiVotingGetAll } from "@/service/api-client/api-voting";
import { useFocusEffect } from "expo-router";
import _ from "lodash";
export default function VotingHistory() { export default function VotingHistory() {
const { user } = useAuth();
const [activeCategory, setActiveCategory] = useState<string | null>("all"); const [activeCategory, setActiveCategory] = useState<string | null>("all");
const [listData, setListData] = useState<any>([]);
const [loadingGetData, setLoadingGetData] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [activeCategory])
);
const onLoadData = async () => {
try {
setLoadingGetData(true);
const response = await apiVotingGetAll({
category: activeCategory === "all" ? "all-history" : "my-history",
authorId: user?.id as string,
});
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadingGetData(false);
}
};
const handlePress = (item: any) => { const handlePress = (item: any) => {
setActiveCategory(item); setActiveCategory(item);
// tambahkan logika lain seperti filter dsb. // tambahkan logika lain seperti filter dsb.
@@ -25,13 +58,20 @@ export default function VotingHistory() {
/> />
} }
> >
{Array.from({ length: 10 }).map((_, index) => ( {loadingGetData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center">Tidak ada riwayat</TextCustom>
) : (
listData.map((item: any, index: number) => (
<Voting_BoxPublishSection <Voting_BoxPublishSection
key={index} key={index}
id={activeCategory as any} id={item.id}
href={`/voting/${index}/history`} data={item}
href={`/voting/${item.id}/history`}
/> />
))} ))
)}
</ViewWrapper> </ViewWrapper>
); );
} }

View File

@@ -8,23 +8,28 @@ import {
} from "@/components"; } from "@/components";
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection"; import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
import { apiVotingGetAll } from "@/service/api-client/api-voting"; import { apiVotingGetAll } from "@/service/api-client/api-voting";
import { router } from "expo-router"; import { router, useFocusEffect } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useEffect, useState } from "react"; import { useCallback, useState } from "react";
export default function VotingBeranda() { export default function VotingBeranda() {
const [listData, setListData] = useState<any>([]); const [listData, setListData] = useState<any>([]);
const [loadingGetData, setLoadingGetData] = useState(false); const [loadingGetData, setLoadingGetData] = useState(false);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
useEffect(() => { useFocusEffect(
useCallback(() => {
onLoadData(); onLoadData();
}, [search]); }, [search])
);
const onLoadData = async () => { const onLoadData = async () => {
try { try {
setLoadingGetData(true); setLoadingGetData(true);
const response = await apiVotingGetAll({ search }); const response = await apiVotingGetAll({
search,
category: "beranda",
});
if (response.success) { if (response.success) {
setListData(response.data); setListData(response.data);
} }

View File

@@ -2,17 +2,24 @@
import { import {
AlertDefaultSystem, AlertDefaultSystem,
BackButton, BackButton,
BaseBox,
DotButton, DotButton,
DrawerCustom, DrawerCustom,
LoaderCustom,
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
Spacing, Spacing,
TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon"; import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
import { Voting_BoxDetailSection } from "@/screens/Voting/BoxDetailSection"; import { Voting_BoxDetailSection } from "@/screens/Voting/BoxDetailSection";
import Voting_ButtonStatusSection from "@/screens/Voting/ButtonStatusSection"; import Voting_ButtonStatusSection from "@/screens/Voting/ButtonStatusSection";
import { apiVotingGetOne } from "@/service/api-client/api-voting"; import {
apiVotingGetOne,
apiVotingUpdateData,
} from "@/service/api-client/api-voting";
import { import {
router, router,
Stack, Stack,
@@ -20,12 +27,14 @@ import {
useLocalSearchParams, useLocalSearchParams,
} from "expo-router"; } from "expo-router";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function VotingDetailStatus() { export default function VotingDetailStatus() {
const { id, status } = useLocalSearchParams(); const { id, status } = useLocalSearchParams();
const [openDrawerDraft, setOpenDrawerDraft] = useState(false); const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
const [openDrawerPublish, setOpenDrawerPublish] = useState(false); const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [loadingGetData, setLoadingGetData] = useState(false);
const [data, setData] = useState<any>(null); const [data, setData] = useState<any>(null);
@@ -37,12 +46,16 @@ export default function VotingDetailStatus() {
const onLoadData = async () => { const onLoadData = async () => {
try { try {
setLoadingGetData(true);
const response = await apiVotingGetOne({ id: id as string }); const response = await apiVotingGetOne({ id: id as string });
if (response.success) { if (response.success) {
setData(response.data); setData(response.data);
} }
} catch (error) { } catch (error) {
console.log("[ERROR]", error); console.log("[ERROR]", error);
} finally {
setLoadingGetData(false);
} }
}; };
@@ -59,9 +72,24 @@ export default function VotingDetailStatus() {
message: "Apakah Anda yakin ingin mengarsipkan voting ini?", message: "Apakah Anda yakin ingin mengarsipkan voting ini?",
textLeft: "Batal", textLeft: "Batal",
textRight: "Ya", textRight: "Ya",
onPressRight: () => { onPressRight: async () => {
console.log("Arsip voting"); try {
const response = await apiVotingUpdateData({
id: id as string,
data: data.isArsip ? false : true,
category: "archive",
});
if (response.success) {
Toast.show({
type: "success",
text1: response.message,
});
router.back(); router.back();
}
} catch (error) {
console.log("[ERROR]", error);
}
}, },
}); });
} }
@@ -73,7 +101,7 @@ export default function VotingDetailStatus() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Detail ${status}`, title: `Detail`,
headerLeft: () => <BackButton />, headerLeft: () => <BackButton />,
headerRight: () => headerRight: () =>
status === "draft" ? ( status === "draft" ? (
@@ -84,14 +112,37 @@ export default function VotingDetailStatus() {
}} }}
/> />
<ViewWrapper> <ViewWrapper>
{loadingGetData ? (
<LoaderCustom />
) : (
<>
{status === "publish" && (
<BaseBox>
<TextCustom bold>
Status:{" "}
<TextCustom color={data?.isArsip ? "red" : "green"}>
{data?.isArsip ? "Arsip" : "Publish"}
</TextCustom>
</TextCustom>
</BaseBox>
)}
<Spacing height={0} />
<Voting_BoxDetailSection data={data as any} /> <Voting_BoxDetailSection data={data as any} />
{status === "publish" ? (
<Voting_BoxDetailHasilVotingSection
listData={data?.Voting_DaftarNamaVote}
/>
) : (
<Voting_ButtonStatusSection <Voting_ButtonStatusSection
isLoading={isLoading} isLoading={isLoading}
onSetLoading={setIsLoading} onSetLoading={setIsLoading}
id={id as string} id={id as string}
status={status as string} status={status as string}
/> />
)}
<Spacing /> <Spacing />
</>
)}
</ViewWrapper> </ViewWrapper>
{/* ========= Draft Drawer ========= */} {/* ========= Draft Drawer ========= */}

View File

@@ -1,22 +1,76 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
AvatarUsernameAndOtherComponent, AvatarUsernameAndOtherComponent,
BackButton, BackButton,
DotButton, DotButton,
DrawerCustom, DrawerCustom,
LoaderCustom,
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
Spacing, Spacing,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import { IconContribution } from "@/components/_Icon"; import { IconContribution } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import { useAuth } from "@/hooks/use-auth";
import { Voting_BoxDetailContributionSection } from "@/screens/Voting/BoxDetailContribution"; import { Voting_BoxDetailContributionSection } from "@/screens/Voting/BoxDetailContribution";
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection"; import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
import {
apiVotingContribution,
apiVotingGetOne,
} from "@/service/api-client/api-voting";
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 VotingDetailContribution() { export default function VotingDetailContribution() {
const { user } = useAuth();
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const [openDrawerPublish, setOpenDrawerPublish] = useState(false); const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
const [data, setData] = useState<any>(null);
const [loadingGetData, setLoadingGetData] = useState(false);
const [nameChoice, setNameChoice] = useState("");
useEffect(() => {
handlerLoadData();
}, [id, user?.id]);
async function handlerLoadData() {
try {
setLoadingGetData(true);
await onLoadData();
await onLoadCheckContribution();
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadingGetData(false);
}
}
const onLoadData = async () => {
try {
const response = await apiVotingGetOne({ id: id as string });
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const onLoadCheckContribution = async () => {
try {
const response = await apiVotingContribution({
id: id as string,
authorId: user?.id as string,
category: "checked",
});
if (response.success) {
setNameChoice(response.data.nameChoice);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const handlePressPublish = (item: IMenuDrawerItem) => { const handlePressPublish = (item: IMenuDrawerItem) => {
router.navigate(item.path as any); router.navigate(item.path as any);
@@ -36,11 +90,27 @@ export default function VotingDetailContribution() {
/> />
<ViewWrapper> <ViewWrapper>
{loadingGetData ? (
<LoaderCustom />
) : (
<>
<Voting_BoxDetailContributionSection <Voting_BoxDetailContributionSection
headerAvatar={<AvatarUsernameAndOtherComponent />} data={data}
nameChoice={nameChoice}
headerAvatar={
<AvatarUsernameAndOtherComponent
avatar={data?.Author?.Profile?.imageId || ""}
name={data?.Author?.username || "Username"}
avatarHref={`/profile/${data?.Author?.Profile?.id}`}
/>
}
/>
<Voting_BoxDetailHasilVotingSection
listData={data?.Voting_DaftarNamaVote}
/> />
<Voting_BoxDetailHasilVotingSection />
<Spacing /> <Spacing />
</>
)}
</ViewWrapper> </ViewWrapper>
{/* ========= Publish Drawer ========= */} {/* ========= Publish Drawer ========= */}

View File

@@ -150,6 +150,7 @@ export default function VotingEdit() {
const response = await apiVotingUpdateData({ const response = await apiVotingUpdateData({
id: id as string, id: id as string,
data: newData, data: newData,
category: "edit",
}); });
if (response.success) { if (response.success) {

View File

@@ -1,23 +1,78 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
AvatarUsernameAndOtherComponent, AvatarUsernameAndOtherComponent,
BackButton, BackButton,
DotButton, DotButton,
DrawerCustom, DrawerCustom,
LoaderCustom,
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
Spacing, Spacing,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import { IconContribution } from "@/components/_Icon"; import { IconContribution } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import { useAuth } from "@/hooks/use-auth";
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection"; import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
import { Voting_BoxDetailHistorySection } from "@/screens/Voting/BoxDetailHistorySection"; import { Voting_BoxDetailHistorySection } from "@/screens/Voting/BoxDetailHistorySection";
import {
apiVotingContribution,
apiVotingGetOne,
} from "@/service/api-client/api-voting";
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 VotingDetailHistory() { export default function VotingDetailHistory() {
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const { user } = useAuth();
const [openDrawerPublish, setOpenDrawerPublish] = useState(false); const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
const [data, setData] = useState<any>(null);
const [loadingGetData, setLoadingGetData] = useState(false);
const [nameChoice, setNameChoice] = useState("");
useEffect(() => {
handlerLoadData();
}, [id, user?.id]);
async function handlerLoadData() {
try {
setLoadingGetData(true);
await onLoadData();
await onLoadCheckContribution();
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadingGetData(false);
}
}
const onLoadData = async () => {
try {
const response = await apiVotingGetOne({ id: id as string });
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const onLoadCheckContribution = async () => {
try {
const response = await apiVotingContribution({
id: id as string,
authorId: user?.id as string,
category: "checked",
});
if (response.success) {
setNameChoice(response.data.nameChoice);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const handlePressPublish = (item: IMenuDrawerItem) => { const handlePressPublish = (item: IMenuDrawerItem) => {
router.navigate(item.path as any); router.navigate(item.path as any);
setOpenDrawerPublish(false); setOpenDrawerPublish(false);
@@ -35,11 +90,27 @@ export default function VotingDetailHistory() {
}} }}
/> />
<ViewWrapper> <ViewWrapper>
{loadingGetData ? (
<LoaderCustom />
) : (
<>
<Voting_BoxDetailHistorySection <Voting_BoxDetailHistorySection
headerAvatar={<AvatarUsernameAndOtherComponent />} data={data}
nameChoice={nameChoice}
headerAvatar={
<AvatarUsernameAndOtherComponent
avatar={data?.Author?.Profile?.imageId || ""}
name={data?.Author?.username || "Username"}
avatarHref={`/profile/${data?.Author?.Profile?.id}`}
/>
}
/>
<Voting_BoxDetailHasilVotingSection
listData={data?.Voting_DaftarNamaVote}
/> />
<Voting_BoxDetailHasilVotingSection />
<Spacing /> <Spacing />
</>
)}
</ViewWrapper> </ViewWrapper>
{/* ========= Publish Drawer ========= */} {/* ========= Publish Drawer ========= */}

View File

@@ -9,17 +9,19 @@ import {
LoaderCustom, LoaderCustom,
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
StackCustom, StackCustom,
TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import { IconArchive, IconContribution } from "@/components/_Icon"; import { IconArchive, IconContribution } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types"; import { IMenuDrawerItem } from "@/components/_Interface/types";
import { useAuth } from "@/hooks/use-auth";
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection"; import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
import { Voting_BoxDetailPublishSection } from "@/screens/Voting/BoxDetailPublishSection"; import { Voting_BoxDetailPublishSection } from "@/screens/Voting/BoxDetailPublishSection";
import { import {
apiVotingCheckContribution, apiVotingContribution,
apiVotingGetOne, apiVotingGetOne,
apiVotingUpdateData,
} from "@/service/api-client/api-voting"; } from "@/service/api-client/api-voting";
import { today } from "@/utils/dateTimeView";
import { import {
router, router,
Stack, Stack,
@@ -27,14 +29,11 @@ import {
useLocalSearchParams, useLocalSearchParams,
} from "expo-router"; } from "expo-router";
import React, { useCallback, useState } from "react"; import React, { useCallback, useState } from "react";
import { useAuth } from "@/hooks/use-auth"; import Toast from "react-native-toast-message";
export default function VotingDetail() { export default function VotingDetail() {
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const { user } = useAuth(); const { user } = useAuth();
console.log("[ID]", id);
const dateNow = new Date();
console.log("[DATE NOW]", dateNow);
const [openDrawerPublish, setOpenDrawerPublish] = useState(false); const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
const [data, setData] = useState<any>(null); const [data, setData] = useState<any>(null);
@@ -42,8 +41,6 @@ export default function VotingDetail() {
const [isContribution, setIsContribution] = useState(false); const [isContribution, setIsContribution] = useState(false);
const [nameChoice, setNameChoice] = useState(""); const [nameChoice, setNameChoice] = useState("");
console.log("[DATA AWAL]", data?.awalVote);
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
handlerLoadData(); handlerLoadData();
@@ -65,7 +62,6 @@ export default function VotingDetail() {
const onLoadData = async () => { const onLoadData = async () => {
try { try {
const response = await apiVotingGetOne({ id: id as string }); const response = await apiVotingGetOne({ id: id as string });
// console.log("[DATA]", JSON.stringify(response.data, null, 2));
if (response.success) { if (response.success) {
setData(response.data); setData(response.data);
} }
@@ -76,11 +72,12 @@ export default function VotingDetail() {
const onLoadCheckContribution = async () => { const onLoadCheckContribution = async () => {
try { try {
const response = await apiVotingCheckContribution({ const response = await apiVotingContribution({
id: id as string, id: id as string,
authorId: user?.id as string, authorId: user?.id as string,
category: "checked",
}); });
console.log("[DATA CONTRIBUION]", response.data);
if (response.success) { if (response.success) {
setIsContribution(response.data.isContribution); setIsContribution(response.data.isContribution);
setNameChoice(response.data.nameChoice); setNameChoice(response.data.nameChoice);
@@ -97,9 +94,24 @@ export default function VotingDetail() {
message: "Apakah Anda yakin ingin mengarsipkan voting ini?", message: "Apakah Anda yakin ingin mengarsipkan voting ini?",
textLeft: "Batal", textLeft: "Batal",
textRight: "Ya", textRight: "Ya",
onPressRight: () => { onPressRight: async () => {
console.log("Hapus"); try {
const response = await apiVotingUpdateData({
id: id as string,
data: data.isArsip ? false : true,
category: "archive",
});
if (response.success) {
Toast.show({
type: "success",
text1: response.message,
});
router.back(); router.back();
}
} catch (error) {
console.log("[ERROR]", error);
}
}, },
}); });
} }
@@ -124,8 +136,8 @@ export default function VotingDetail() {
<LoaderCustom /> <LoaderCustom />
) : ( ) : (
<StackCustom gap={"xs"}> <StackCustom gap={"xs"}>
{dateNow < new Date(data?.awalVote) && ( {today.getDate() < new Date(data?.awalVote).getDate() && (
<InformationBox text="Untuk sementara voting ini belum di buka. Voting akan dimulai sesuai dengan tanggal awal pemilihan, dan akan ditutup sesuai dengan tanggal akhir pemilihan." /> <InformationBox text="Untuk sementara voting tidak dapat dilakukan. Voting dapat dimulai sesuai dengan tanggal awal pemilihan, dan akan ditutup sesuai dengan tanggal akhir pemilihan." />
)} )}
<Voting_BoxDetailPublishSection <Voting_BoxDetailPublishSection
data={data} data={data}
@@ -155,7 +167,9 @@ export default function VotingDetail() {
height={"auto"} height={"auto"}
> >
<MenuDrawerDynamicGrid <MenuDrawerDynamicGrid
data={[ data={
user?.id === data?.Author?.id
? [
{ {
icon: <IconContribution />, icon: <IconContribution />,
label: "Daftar Kontributor", label: "Daftar Kontributor",
@@ -166,7 +180,15 @@ export default function VotingDetail() {
label: "Update Arsip", label: "Update Arsip",
path: "" as any, path: "" as any,
}, },
]} ]
: [
{
icon: <IconContribution />,
label: "Daftar Kontributor",
path: `/voting/${id}/list-of-contributor`,
},
]
}
onPressItem={handlePressPublish as any} onPressItem={handlePressPublish as any}
/> />
</DrawerCustom> </DrawerCustom>

View File

@@ -1,26 +1,69 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
AvatarUsernameAndOtherComponent, AvatarUsernameAndOtherComponent,
BadgeCustom, BadgeCustom,
BaseBox, BaseBox,
LoaderCustom,
TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import { apiVotingContribution } from "@/service/api-client/api-voting";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
export default function Voting_ListOfContributor() { export default function Voting_ListOfContributor() {
const { id } = useLocalSearchParams();
const [listData, setListData] = useState<any>([]);
const [isLoadData, setIsLoadData] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadList();
}, [id])
);
const onLoadList = async () => {
try {
setIsLoadData(true);
const response = await apiVotingContribution({
id: id as string,
authorId: "",
category: "list",
});
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoadData(false);
}
};
return ( return (
<ViewWrapper> <ViewWrapper>
{Array.from({ length: 10 }).map((_, index) => ( {isLoadData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center">Tidak ada kontributor</TextCustom>
) : (
listData.map((item: any, index: number) => (
<BaseBox paddingTop={5} paddingBottom={5} key={index.toString()}> <BaseBox paddingTop={5} paddingBottom={5} key={index.toString()}>
<AvatarUsernameAndOtherComponent <AvatarUsernameAndOtherComponent
avatar={item?.Author?.Profile?.imageId || ""}
name={item?.Author?.username || "Username"}
avatarHref={`/profile/${item?.Author?.Profile?.id}`}
rightComponent={ rightComponent={
<BadgeCustom <BadgeCustom style={{ alignSelf: "flex-end" }}>
style={{alignSelf: "flex-end" }} {item?.Voting_DaftarNamaVote?.value}
>
Pilihan {index + 1}
</BadgeCustom> </BadgeCustom>
} }
/> />
</BaseBox> </BaseBox>
))} ))
)}
</ViewWrapper> </ViewWrapper>
); );
} }

View File

@@ -9,23 +9,32 @@ import { GStyles } from "@/styles/global-styles";
import { Voting_ComponentDetailDataSection } from "./ComponentDetailDataSection"; import { Voting_ComponentDetailDataSection } from "./ComponentDetailDataSection";
export function Voting_BoxDetailContributionSection({ export function Voting_BoxDetailContributionSection({
data,
headerAvatar, headerAvatar,
nameChoice,
}: { }: {
data: any;
headerAvatar?: React.ReactNode; headerAvatar?: React.ReactNode;
nameChoice?: string;
}) { }) {
return ( return (
<> <>
<BoxWithHeaderSection> <BoxWithHeaderSection>
{headerAvatar ? headerAvatar : <Spacing />} {headerAvatar && (
<>
{headerAvatar}
<Spacing />
</>
)}
<StackCustom gap={"lg"}> <StackCustom gap={"lg"}>
<Voting_ComponentDetailDataSection /> <Voting_ComponentDetailDataSection data={data} />
<StackCustom gap={"xs"}> <StackCustom gap={"sm"}>
<TextCustom bold size="small" align="center"> <TextCustom bold size="small" align="center">
Pilihan Anda Pilihan Anda
</TextCustom> </TextCustom>
<BadgeCustom style={[GStyles.alignSelfCenter]}> <BadgeCustom variant="light" size="lg" style={[GStyles.alignSelfCenter]}>
Pilihan 1 {nameChoice || "-"}
</BadgeCustom> </BadgeCustom>
</StackCustom> </StackCustom>
</StackCustom> </StackCustom>

View File

@@ -9,7 +9,7 @@ import {
export default function Voting_BoxDetailHasilVotingSection({ export default function Voting_BoxDetailHasilVotingSection({
listData, listData,
}: { }: {
listData?: any[]; listData: any[];
}) { }) {
return ( return (
<> <>

View File

@@ -1,21 +1,36 @@
import { import {
BadgeCustom,
BoxWithHeaderSection, BoxWithHeaderSection,
Spacing, Spacing,
StackCustom StackCustom,
TextCustom
} from "@/components"; } from "@/components";
import { Voting_ComponentDetailDataSection } from "./ComponentDetailDataSection"; import { Voting_ComponentDetailDataSection } from "./ComponentDetailDataSection";
import { GStyles } from "@/styles/global-styles";
export function Voting_BoxDetailHistorySection({ export function Voting_BoxDetailHistorySection({
headerAvatar, headerAvatar,
data,
nameChoice,
}: { }: {
headerAvatar?: React.ReactNode; headerAvatar?: React.ReactNode;
data: any;
nameChoice: string;
}) { }) {
return ( return (
<> <>
<BoxWithHeaderSection> <BoxWithHeaderSection>
{headerAvatar ? headerAvatar : <Spacing />} {headerAvatar ? headerAvatar : <Spacing />}
<StackCustom> <StackCustom>
<Voting_ComponentDetailDataSection /> <Voting_ComponentDetailDataSection data={data} />
<StackCustom gap={"sm"}>
<TextCustom bold size="small" align="center">
Pilihan Anda
</TextCustom>
<BadgeCustom variant="light" size="lg" style={[GStyles.alignSelfCenter]}>
{nameChoice || "-"}
</BadgeCustom>
</StackCustom>
</StackCustom> </StackCustom>
</BoxWithHeaderSection> </BoxWithHeaderSection>
</> </>

View File

@@ -1,18 +1,19 @@
import { import {
AlertDefaultSystem,
BadgeCustom, BadgeCustom,
BoxWithHeaderSection, BoxWithHeaderSection,
ButtonCustom, ButtonCustom,
CenterCustom,
Spacing, Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom
} from "@/components"; } from "@/components";
import { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom"; import { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom";
import { apiVotingVote } from "@/service/api-client/api-voting";
import { today } from "@/utils/dateTimeView";
import { router } from "expo-router";
import { useState } from "react"; import { useState } from "react";
import { View } from "react-native"; import { View } from "react-native";
import { Voting_ComponentDetailDataSection } from "./ComponentDetailDataSection"; import { Voting_ComponentDetailDataSection } from "./ComponentDetailDataSection";
import { apiVotingVote } from "@/service/api-client/api-voting";
import { useAuth } from "@/hooks/use-auth";
export function Voting_BoxDetailPublishSection({ export function Voting_BoxDetailPublishSection({
headerAvatar, headerAvatar,
@@ -40,7 +41,10 @@ export function Voting_BoxDetailPublishSection({
id: data?.id, id: data?.id,
data: newData, data: newData,
}); });
console.log("[RES VOTE]", response);
if (response.success) {
router.push(`/voting/${data?.id}/list-of-contributor`);
}
} catch (error) { } catch (error) {
console.log("[ERROR]", error); console.log("[ERROR]", error);
} }
@@ -79,13 +83,30 @@ export function Voting_BoxDetailPublishSection({
<RadioGroup value={value} onChange={setValue}> <RadioGroup value={value} onChange={setValue}>
{data?.Voting_DaftarNamaVote?.map((item: any, i: number) => ( {data?.Voting_DaftarNamaVote?.map((item: any, i: number) => (
<View key={i}> <View key={i}>
<RadioCustom label={item?.value} value={item?.id} /> <RadioCustom
disabled={
today.getDate() < new Date(data?.awalVote).getDate()
}
label={item?.value}
value={item?.id}
/>
</View> </View>
))} ))}
</RadioGroup> </RadioGroup>
</StackCustom> </StackCustom>
<ButtonCustom disabled={value === ""} onPress={handlerSubmitVote}> <ButtonCustom
disabled={value === ""}
onPress={() => {
AlertDefaultSystem({
title: "Anda melaukan voting",
message: "Yakin dengan pilihan anda ini ?",
textLeft: "Batal",
textRight: "Ya",
onPressRight: () => handlerSubmitVote(),
});
}}
>
Vote Vote
</ButtonCustom> </ButtonCustom>
</> </>

View File

@@ -62,12 +62,15 @@ export async function apiVotingDelete({ id }: { id: string }) {
export async function apiVotingUpdateData({ export async function apiVotingUpdateData({
id, id,
data, data,
category,
}: { }: {
id: string; id: string;
data: any; data: any;
category: "edit" | "archive";
}) { }) {
const categoryQuery = `?category=${category}`;
try { try {
const response = await apiConfig.put(`/mobile/voting/${id}`, { const response = await apiConfig.put(`/mobile/voting/${id}${categoryQuery}`, {
data: data, data: data,
}); });
return response.data; return response.data;
@@ -76,10 +79,12 @@ export async function apiVotingUpdateData({
} }
} }
export async function apiVotingGetAll({ search }: { search: string }) { export async function apiVotingGetAll({ search, category, authorId }: { search?: string, category: "beranda" | "contribution" | "all-history" | "my-history", authorId?: string }) {
try { try {
const searchQuery = search ? `?search=${search}` : ""; const categoryQuery = category ? `?category=${category}` : "";
const response = await apiConfig.get(`/mobile/voting${searchQuery}`); const searchQuery = search ? `&search=${search}` : "";
const authorIdQuery = authorId ? `&authorId=${authorId}` : "";
const response = await apiConfig.get(`/mobile/voting${categoryQuery}${searchQuery}${authorIdQuery}`);
return response.data; return response.data;
} catch (error) { } catch (error) {
throw error; throw error;
@@ -97,18 +102,23 @@ export async function apiVotingVote({ id, data }: { id: string; data: any }) {
} }
} }
export async function apiVotingCheckContribution({ export async function apiVotingContribution({
id, id,
authorId, authorId,
category,
}: { }: {
id: string; id: string;
authorId: string; authorId: string;
category: "list" | "checked";
}) { }) {
const query =
category === "list"
? "?category=list"
: `?category=checked&authorId=${authorId}`;
try { try {
const response = await apiConfig.get( const response = await apiConfig.get(
`/mobile/voting/${id}/contribution?authorId=${authorId}` `/mobile/voting/${id}/contribution${query}`
); );
console.log("[DATA CONTRIBUION]", response.data);
return response.data; return response.data;
} catch (error) { } catch (error) {
throw error; throw error;

View File

@@ -1,5 +1,6 @@
import dayjs from "dayjs"; import dayjs from "dayjs";
export const today = new Date();
export const dateTimeView = ({ export const dateTimeView = ({
date, date,
withoutTime = false, withoutTime = false,