Integrasi API: Admin Voting

Add:
-  service/api-admin/api-admin-voting.ts

Fix:
- app/(application)/admin/voting/[id]/[status]/index.tsx
- app/(application)/admin/voting/[status]/status.tsx
- app/(application)/admin/voting/index.tsx

### No Issue
This commit is contained in:
2025-10-20 17:39:12 +08:00
parent 1fd9694ebf
commit 57285e5697
4 changed files with 210 additions and 94 deletions

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
AlertDefaultSystem, AlertDefaultSystem,
BadgeCustom, BadgeCustom,
@@ -14,14 +15,42 @@ import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview"; import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8"; import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { apiAdminVotingById } from "@/service/api-admin/api-admin-voting";
import { dateTimeView } from "@/utils/dateTimeView";
import { Entypo } from "@expo/vector-icons";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { router, useLocalSearchParams } from "expo-router"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useCallback, useState } from "react";
import { List } from "react-native-paper"; import { List } from "react-native-paper";
export default function AdminVotingDetail() { export default function AdminVotingDetail() {
const { id, status } = useLocalSearchParams(); const { id, status } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminVotingById({
id: id as string,
});
console.log("[DATA BY ID]", JSON.stringify(response, null, 2));
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const colorBadge = () => { const colorBadge = () => {
if (status === "publish") { if (status === "publish") {
return MainColor.green; return MainColor.green;
@@ -37,63 +66,53 @@ export default function AdminVotingDetail() {
const listData = [ const listData = [
{ {
label: "Username", label: "Username",
value: "Bagas Banuna", value: (data && data?.Author?.username) || "-",
}, },
{ {
label: "Judul", label: "Judul",
value: `Judul Proyek: ${id}Lorem ipsum dolor sit amet consectetur adipisicing elit.`, value: (data && data?.title) || "-",
}, },
{ {
label: "Status", label: "Status",
value: ( value:
<BadgeCustom color={colorBadge()}> data && data?.Voting_Status?.name ? (
{_.startCase(status as string)} <BadgeCustom color={colorBadge()}>
</BadgeCustom> {_.startCase(data?.Voting_Status?.name)}
), </BadgeCustom>
) : (
"-"
),
}, },
{ {
label: "Mulai Voting", label: "Mulai Voting",
value: dayjs().format("DD/MM/YYYY"), value:
(data && data?.awalVote && dateTimeView({ date: data?.awalVote })) ||
"-",
}, },
{ {
label: "Voting Berakhir", label: "Voting Berakhir",
value: dayjs().format("DD/MM/YYYY"), value:
(data && data?.akhirVote && dateTimeView({ date: data?.akhirVote })) ||
"-",
}, },
{ {
label: "Deskripsi", label: "Deskripsi",
value: "Lorem ipsum dolor sit amet consectetur adipisicing elit.", value: (data && data?.deskripsi) || "-",
}, },
{ {
label: "Daftar Pilhan", label: "Daftar Pilihan",
value: ( value:
<> data && data?.Voting_DaftarNamaVote
<List.Item ? data?.Voting_DaftarNamaVote?.map((item: any, i: number) => (
title={<TextCustom>Pilihan 1</TextCustom>} <List.Item
left={(props) => ( key={i}
<List.Icon {...props} icon="circle" color={MainColor.yellow} /> title={<TextCustom>{item?.value}</TextCustom>}
)} left={(props) => (
/> <Entypo name="chevron-right" color={MainColor.yellow} />
<List.Item )}
title={<TextCustom>Pilihan 2</TextCustom>} />
left={(props) => ( ))
<List.Icon {...props} icon="circle" color={MainColor.yellow} /> : "-",
)}
/>
<List.Item
title={<TextCustom>Pilihan 3</TextCustom>}
left={(props) => (
<List.Icon {...props} icon="circle" color={MainColor.yellow} />
)}
/>
<List.Item
title={<TextCustom>Pilihan 4</TextCustom>}
left={(props) => (
<List.Icon {...props} icon="circle" color={MainColor.yellow} />
)}
/>
</>
),
}, },
]; ];
@@ -121,17 +140,23 @@ export default function AdminVotingDetail() {
</TextCustom> </TextCustom>
<Spacing /> <Spacing />
<Grid> <Grid>
{Array.from({ length: 4 }).map((_, index) => (
{Array.from({length: 4}).map((_, index) => ( <Grid.Col
<Grid.Col key={index} span={3} style={{ paddingRight: 3, paddingLeft: 3 }}> key={index}
<StackCustom gap={"sm"}> span={3}
<CircleContainer value={index % 3 * 3} style={{ alignSelf: "center" }} /> style={{ paddingRight: 3, paddingLeft: 3 }}
<TextCustom size="small" align="center"> >
Pilihan {index + 1} <StackCustom gap={"sm"}>
</TextCustom> <CircleContainer
</StackCustom> value={(index % 3) * 3}
</Grid.Col> style={{ alignSelf: "center" }}
))} />
<TextCustom size="small" align="center">
Pilihan {index + 1}
</TextCustom>
</StackCustom>
</Grid.Col>
))}
</Grid> </Grid>
</BaseBox> </BaseBox>
)} )}

View File

@@ -1,27 +1,66 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { import {
ActionIcon, ActionIcon,
BaseBox, BaseBox,
SearchInput, LoaderCustom,
Spacing, SearchInput,
TextCustom, Spacing,
ViewWrapper, StackCustom,
TextCustom,
ViewWrapper,
} from "@/components"; } from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage"; import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle"; import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue"; import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage"; import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
import { Octicons } from "@expo/vector-icons"; import { Octicons } from "@expo/vector-icons";
import { router, useLocalSearchParams } from "expo-router"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useState, useCallback } from "react";
import { Divider } from "react-native-paper"; import { Divider } from "react-native-paper";
export default function AdminVotingStatus() { export default function AdminVotingStatus() {
const { status } = useLocalSearchParams(); const { status } = useLocalSearchParams();
console.log("[STATUS]", status);
const [list, setList] = useState<any | null>(null);
const [loadList, setLoadList] = useState(false);
const [search, setSearch] = useState<string>("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [status, search])
);
const onLoadData = async () => {
try {
setLoadList(true);
const response = await apiAdminVoting({
category: status as "publish" | "review" | "reject" as any,
search,
});
// console.log("[LIST BY STATUS]", JSON.stringify(response, null, 2));
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = ( const rightComponent = (
<SearchInput <SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }} containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari" placeholder="Cari"
value={search}
onChangeText={setSearch}
/> />
); );
return ( return (
@@ -32,18 +71,17 @@ export default function AdminVotingStatus() {
rightComponent={rightComponent} rightComponent={rightComponent}
/> />
<BaseBox> <StackCustom gap={"sm"}>
<AdminTitleTable <AdminTitleTable
title1="Aksi" title1="Aksi"
title2="Username" title2="Username"
title3="Judul Voting" title3="Judul Voting"
/> />
<Spacing />
<Divider /> <Divider />
{Array.from({ length: 10 }).map((_, index) => ( {loadList ? <LoaderCustom/> : _.isEmpty(list) ? <TextCustom align="center" bold color="gray">Belum ada data</TextCustom> : list.map((item: any, i: number) => (
<AdminTableValue <AdminTableValue
key={index} key={i}
value1={ value1={
<ActionIcon <ActionIcon
icon={ icon={
@@ -54,22 +92,19 @@ export default function AdminVotingStatus() {
/> />
} }
onPress={() => { onPress={() => {
router.push(`/admin/voting/${index}/${status}`); router.push(`/admin/voting/${item.id}/${status}`);
}} }}
/> />
} }
value2={<TextCustom truncate={1}>Username username</TextCustom>} value2={<TextCustom truncate={1}>{item?.Author?.username || "-"}</TextCustom>}
value3={ value3={
<TextCustom truncate={2}> <TextCustom align="center" truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. {item?.title || "-"}
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae mollitia.
</TextCustom> </TextCustom>
} }
/> />
))} ))}
</BaseBox> </StackCustom>
</ViewWrapper> </ViewWrapper>
</> </>
); );

View File

@@ -8,8 +8,56 @@ import {
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard"; import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage"; import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
import { useFocusEffect } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminVoting() { export default function AdminVoting() {
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [])
);
const onLoadData = async () => {
try {
const response = await apiAdminVoting({
category: "dashboard",
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const listData = [
{
label: "Publish",
value: (data && data?.publish) || 0,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Review",
value: (data && data?.review) || 0,
icon: <IconReview size={25} color={MainColor.orange} />,
},
{
label: "Reject",
value: (data && data?.reject) || 0,
icon: <IconReject size={25} color={MainColor.red} />,
},
{
label: "Riwayat",
value: (data && data?.history) || 0,
icon: <IconArchive size={25} color={MainColor.white_gray} />,
},
];
return ( return (
<> <>
<ViewWrapper> <ViewWrapper>
@@ -24,26 +72,3 @@ export default function AdminVoting() {
</> </>
); );
} }
const listData = [
{
label: "Publish",
value: 4,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Review",
value: 7,
icon: <IconReview size={25} color={MainColor.orange} />,
},
{
label: "Reject",
value: 5,
icon: <IconReject size={25} color={MainColor.red} />,
},
{
label: "Riwayat",
value: 5,
icon: <IconArchive size={25} color={MainColor.white_gray} />,
},
];

View File

@@ -0,0 +1,31 @@
import { apiConfig } from "../api-config";
export async function apiAdminVoting({
category,
search,
}: {
category: "dashboard" | "history" | "publish" | "review" | "report";
search?: string;
}) {
try {
const response = await apiConfig.get(
`/mobile/admin/voting?category=${category}&search=${search}`
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminVotingById({
id,
}: {
id: string;
}) {
try {
const response = await apiConfig.get(`/mobile/admin/voting/${id}`);
return response.data;
} catch (error) {
throw error;
}
}