Integrasi API: Voting admin
Add: - app/(application)/admin/voting/[id]/[status]/reject-input.tsx - app/(application)/admin/voting/history.tsx - components/Box/ReportBox.tsx - screens/Admin/Voting/ - utils/colorBadge.ts Fix: - app/(application)/(user)/job/[id]/[status]/detail.tsx - app/(application)/(user)/voting/[id]/[status]/detail.tsx - app/(application)/admin/job/[id]/[status]/index.tsx - app/(application)/admin/job/[id]/[status]/reject-input.tsx - app/(application)/admin/voting/[id]/[status]/index.tsx - app/(application)/admin/voting/[id]/reject-input.tsx - app/(application)/admin/voting/[status]/status.tsx - components/Container/CircleContainer.tsx - components/Text/TextCustom.tsx - components/_ShareComponent/Admin/ButtonReview.tsx - screens/Admin/Job/funUpdateStatus.ts - screens/Admin/listPageAdmin.tsx - service/api-admin/api-admin-voting.ts ### No Issue
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
} from "@/components";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import ReportBox from "@/components/Box/ReportBox";
|
||||
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
|
||||
import Job_ButtonStatusSection from "@/screens/Job/ButtonStatusSection";
|
||||
import { apiJobGetOne } from "@/service/api-client/api-job";
|
||||
@@ -70,7 +71,13 @@ export default function JobDetailStatus() {
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
<StackCustom>
|
||||
<StackCustom gap={"xs"}>
|
||||
{data &&
|
||||
data?.catatan &&
|
||||
(status === "draft" || status === "rejected") && (
|
||||
<ReportBox text={data?.catatan} />
|
||||
)}
|
||||
|
||||
<Job_BoxDetailSection data={data} />
|
||||
<Job_ButtonStatusSection
|
||||
id={id as string}
|
||||
|
||||
@@ -8,11 +8,13 @@ import {
|
||||
LoaderCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import ReportBox from "@/components/Box/ReportBox";
|
||||
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
|
||||
import { Voting_BoxDetailSection } from "@/screens/Voting/BoxDetailSection";
|
||||
import Voting_ButtonStatusSection from "@/screens/Voting/ButtonStatusSection";
|
||||
@@ -49,6 +51,8 @@ export default function VotingDetailStatus() {
|
||||
setLoadingGetData(true);
|
||||
const response = await apiVotingGetOne({ id: id as string });
|
||||
|
||||
console.log("[DATA BY ID]", JSON.stringify(response, null, 2));
|
||||
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
@@ -127,6 +131,13 @@ export default function VotingDetailStatus() {
|
||||
</BaseBox>
|
||||
)}
|
||||
<Spacing height={0} />
|
||||
|
||||
{data &&
|
||||
data?.catatan &&
|
||||
(status === "draft" || status === "rejected") && (
|
||||
<ReportBox text={data?.catatan} />
|
||||
)}
|
||||
|
||||
<Voting_BoxDetailSection data={data as any} />
|
||||
{status === "publish" ? (
|
||||
<Voting_BoxDetailHasilVotingSection
|
||||
|
||||
@@ -14,7 +14,7 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
|
||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import funUpdateStatus from "@/screens/Admin/Job/funUpdateStatus";
|
||||
import funUpdateStatusJob from "@/screens/Admin/Job/funUpdateStatus";
|
||||
import { apiAdminJobGetById } from "@/service/api-admin/api-admin-job";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
@@ -88,7 +88,7 @@ export default function AdminJobDetailStatus() {
|
||||
changeStatus: "publish" | "review" | "reject";
|
||||
}) => {
|
||||
try {
|
||||
const response = await funUpdateStatus({
|
||||
const response = await funUpdateStatusJob({
|
||||
id: id as string,
|
||||
changeStatus,
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||
import funUpdateStatus from "@/screens/Admin/Job/funUpdateStatus";
|
||||
import funUpdateStatusJob from "@/screens/Admin/Job/funUpdateStatus";
|
||||
import { apiAdminJobGetById } from "@/service/api-admin/api-admin-job";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
@@ -45,7 +45,7 @@ export default function AdminJobRejectInput() {
|
||||
}) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await funUpdateStatus({
|
||||
const response = await funUpdateStatusJob({
|
||||
id: id as string,
|
||||
changeStatus,
|
||||
data: data,
|
||||
|
||||
@@ -14,8 +14,11 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
|
||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
||||
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8";
|
||||
import ReportBox from "@/components/Box/ReportBox";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import funUpdateStatusVoting from "@/screens/Admin/Voting/funUpdateStatus";
|
||||
import { apiAdminVotingById } from "@/service/api-admin/api-admin-voting";
|
||||
import { colorBadge } from "@/utils/colorBadge";
|
||||
import { dateTimeView } from "@/utils/dateTimeView";
|
||||
import { Entypo } from "@expo/vector-icons";
|
||||
import dayjs from "dayjs";
|
||||
@@ -23,11 +26,14 @@ import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
import { List } from "react-native-paper";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function AdminVotingDetail() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
|
||||
const [data, setData] = useState<any | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
console.log("[status]", status);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
@@ -41,8 +47,6 @@ export default function AdminVotingDetail() {
|
||||
id: id as string,
|
||||
});
|
||||
|
||||
console.log("[DATA BY ID]", JSON.stringify(response, null, 2));
|
||||
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
@@ -51,18 +55,6 @@ export default function AdminVotingDetail() {
|
||||
}
|
||||
};
|
||||
|
||||
const colorBadge = () => {
|
||||
if (status === "publish") {
|
||||
return MainColor.green;
|
||||
} else if (status === "review") {
|
||||
return MainColor.orange;
|
||||
} else if (status === "reject") {
|
||||
return MainColor.red;
|
||||
} else {
|
||||
return MainColor.placeholder;
|
||||
}
|
||||
};
|
||||
|
||||
const listData = [
|
||||
{
|
||||
label: "Username",
|
||||
@@ -76,8 +68,8 @@ export default function AdminVotingDetail() {
|
||||
label: "Status",
|
||||
value:
|
||||
data && data?.Voting_Status?.name ? (
|
||||
<BadgeCustom color={colorBadge()}>
|
||||
{_.startCase(data?.Voting_Status?.name)}
|
||||
<BadgeCustom color={colorBadge({ status: status as string })}>
|
||||
{status === "history" ? "Riwayat" : _.startCase(status as string)}
|
||||
</BadgeCustom>
|
||||
) : (
|
||||
"-"
|
||||
@@ -116,6 +108,59 @@ export default function AdminVotingDetail() {
|
||||
},
|
||||
];
|
||||
|
||||
const handleUpdate = async ({
|
||||
changeStatus,
|
||||
}: {
|
||||
changeStatus: "publish" | "review" | "reject";
|
||||
}) => {
|
||||
try {
|
||||
const dateNow = new Date();
|
||||
// const dateNowHour = dateNow.getHours();
|
||||
// const awalVoteHour = dayjs(data?.awalVote).hour();
|
||||
|
||||
const isBefore = dayjs(dateNow).diff(dayjs(data?.awalVote), "hours") < 0;
|
||||
console.log("[IS BEFORE]", isBefore);
|
||||
|
||||
if (!isBefore) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Tanggal & waktu telah lewat",
|
||||
text2: "Silahkan report dan ubah tanggal & waktu voting",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Berhasil mempublikasikan data",
|
||||
});
|
||||
|
||||
setIsLoading(true);
|
||||
const response = await funUpdateStatusVoting({
|
||||
id: id as string,
|
||||
changeStatus,
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Gagal mempublikasikan data",
|
||||
});
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Berhasil mempublikasikan data",
|
||||
});
|
||||
|
||||
router.back();
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
@@ -133,52 +178,59 @@ export default function AdminVotingDetail() {
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
|
||||
{status === "publish" && (
|
||||
<BaseBox>
|
||||
<TextCustom bold align="center">
|
||||
Hasil Voting
|
||||
</TextCustom>
|
||||
<Spacing />
|
||||
<Grid>
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<Grid.Col
|
||||
key={index}
|
||||
span={3}
|
||||
style={{ paddingRight: 3, paddingLeft: 3 }}
|
||||
>
|
||||
<StackCustom gap={"sm"}>
|
||||
<CircleContainer
|
||||
value={(index % 3) * 3}
|
||||
style={{ alignSelf: "center" }}
|
||||
/>
|
||||
<TextCustom size="small" align="center">
|
||||
Pilihan {index + 1}
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
))}
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
)}
|
||||
{status === "publish" ||
|
||||
(status === "history" && (
|
||||
<BaseBox>
|
||||
<TextCustom bold align="center">
|
||||
Hasil Voting
|
||||
</TextCustom>
|
||||
<Spacing />
|
||||
<Grid>
|
||||
{data?.Voting_DaftarNamaVote?.map(
|
||||
(item: any, index: number) => (
|
||||
<Grid.Col
|
||||
key={index}
|
||||
span={12 / data?.Voting_DaftarNamaVote?.length}
|
||||
style={{ paddingRight: 3, paddingLeft: 3 }}
|
||||
>
|
||||
<StackCustom gap={"sm"}>
|
||||
<CircleContainer
|
||||
value={item?.jumlah}
|
||||
style={{ alignSelf: "center" }}
|
||||
/>
|
||||
<TextCustom size="small" align="center">
|
||||
{item?.value}
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
)
|
||||
)}
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
))}
|
||||
|
||||
{data &&
|
||||
data?.catatan &&
|
||||
(status === "review" || status === "reject") && (
|
||||
<ReportBox text={data?.catatan} />
|
||||
)}
|
||||
|
||||
{status === "review" && (
|
||||
<AdminButtonReview
|
||||
isLoading={isLoading}
|
||||
onPublish={() => {
|
||||
AlertDefaultSystem({
|
||||
title: "Publish",
|
||||
message: "Apakah anda yakin ingin mempublikasikan data ini?",
|
||||
textLeft: "Cancel",
|
||||
textRight: "Publish",
|
||||
onPressLeft: () => {
|
||||
router.back();
|
||||
},
|
||||
onPressRight: () => {
|
||||
router.back();
|
||||
handleUpdate({ changeStatus: "publish" });
|
||||
},
|
||||
});
|
||||
}}
|
||||
onReject={() => {
|
||||
router.push(`/admin/voting/${id}/reject-input`);
|
||||
router.push(`/admin/voting/${id}/${status}/reject-input`);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -187,7 +239,7 @@ export default function AdminVotingDetail() {
|
||||
<AdminButtonReject
|
||||
title="Tambah Catatan"
|
||||
onReject={() => {
|
||||
router.push(`/admin/voting/${id}/reject-input`);
|
||||
router.push(`/admin/voting/${id}/${status}/reject-input`);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
113
app/(application)/admin/voting/[id]/[status]/reject-input.tsx
Normal file
113
app/(application)/admin/voting/[id]/[status]/reject-input.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BoxButtonOnFooter,
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||
import funUpdateStatusVoting from "@/screens/Admin/Voting/funUpdateStatus";
|
||||
import { apiAdminVotingById } from "@/service/api-admin/api-admin-voting";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function AdminVotingRejectInput() {
|
||||
const { id, status } = useLocalSearchParams();
|
||||
const [data, setData] = useState("");
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiAdminVotingById({
|
||||
id: id as string,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setData(response.data.catatan);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdate = async ({
|
||||
changeStatus,
|
||||
}: {
|
||||
changeStatus: "publish" | "review" | "reject";
|
||||
}) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await funUpdateStatusVoting({
|
||||
id: id as string,
|
||||
changeStatus,
|
||||
data: data,
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
Toast.show({
|
||||
type: "error",
|
||||
text1: "Report gagal",
|
||||
});
|
||||
}
|
||||
|
||||
Toast.show({
|
||||
type: "success",
|
||||
text1: "Report berhasil",
|
||||
});
|
||||
|
||||
if (status === "review") {
|
||||
router.replace(`/admin/voting/reject/status`);
|
||||
} else if (status === "reject") {
|
||||
router.back();
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const buttonSubmit = (
|
||||
<BoxButtonOnFooter>
|
||||
<AdminButtonReject
|
||||
title="Reject"
|
||||
isLoading={isLoading}
|
||||
onReject={() =>
|
||||
AlertDefaultSystem({
|
||||
title: "Reject",
|
||||
message: "Apakah anda yakin ingin menolak data ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => handleUpdate({ changeStatus: "reject" }),
|
||||
})
|
||||
}
|
||||
/>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
footerComponent={buttonSubmit}
|
||||
headerComponent={<AdminBackButtonAntTitle title="Penolakan Voting" />}
|
||||
>
|
||||
<TextAreaCustom
|
||||
value={data}
|
||||
onChangeText={setData}
|
||||
placeholder="Masukan alasan"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
|
||||
export default function AdminVotingRejectInput() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [value, setValue] = useState(id as string);
|
||||
const buttonSubmit = (
|
||||
<BoxButtonOnFooter>
|
||||
<AdminButtonReject
|
||||
title="Reject"
|
||||
onReject={() =>
|
||||
AlertDefaultSystem({
|
||||
title: "Reject",
|
||||
message: "Apakah anda yakin ingin menolak data ini?",
|
||||
textLeft: "Batal",
|
||||
textRight: "Ya",
|
||||
onPressRight: () => {
|
||||
console.log("value:", value);
|
||||
router.replace(`/admin/voting/reject/status`);
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</BoxButtonOnFooter>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
footerComponent={buttonSubmit}
|
||||
// edgesFooter={[]}
|
||||
headerComponent={<AdminBackButtonAntTitle title="Penolakan Voting" />}
|
||||
>
|
||||
<TextAreaCustom
|
||||
value={value}
|
||||
onChangeText={setValue}
|
||||
placeholder="Masukan alasan"
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ActionIcon,
|
||||
BaseBox,
|
||||
LoaderCustom,
|
||||
SearchInput,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
@@ -18,13 +16,11 @@ import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
|
||||
import { Octicons } from "@expo/vector-icons";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useState, useCallback } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
import { Divider } from "react-native-paper";
|
||||
|
||||
export default function AdminVotingStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
console.log("[STATUS]", status);
|
||||
|
||||
const [list, setList] = useState<any | null>(null);
|
||||
const [loadList, setLoadList] = useState(false);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
@@ -43,8 +39,6 @@ export default function AdminVotingStatus() {
|
||||
search,
|
||||
});
|
||||
|
||||
// console.log("[LIST BY STATUS]", JSON.stringify(response, null, 2));
|
||||
|
||||
if (response.success) {
|
||||
setList(response.data);
|
||||
}
|
||||
@@ -79,31 +73,43 @@ export default function AdminVotingStatus() {
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{loadList ? <LoaderCustom/> : _.isEmpty(list) ? <TextCustom align="center" bold color="gray">Belum ada data</TextCustom> : list.map((item: any, i: number) => (
|
||||
<AdminTableValue
|
||||
key={i}
|
||||
value1={
|
||||
<ActionIcon
|
||||
icon={
|
||||
<Octicons
|
||||
name="eye"
|
||||
size={ICON_SIZE_BUTTON}
|
||||
color="black"
|
||||
/>
|
||||
}
|
||||
onPress={() => {
|
||||
router.push(`/admin/voting/${item.id}/${status}`);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
value2={<TextCustom truncate={1}>{item?.Author?.username || "-"}</TextCustom>}
|
||||
value3={
|
||||
<TextCustom align="center" truncate={2}>
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
{loadList ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(list) ? (
|
||||
<TextCustom align="center" bold color="gray">
|
||||
Belum ada data
|
||||
</TextCustom>
|
||||
) : (
|
||||
list.map((item: any, i: number) => (
|
||||
<AdminTableValue
|
||||
key={i}
|
||||
value1={
|
||||
<ActionIcon
|
||||
icon={
|
||||
<Octicons
|
||||
name="eye"
|
||||
size={ICON_SIZE_BUTTON}
|
||||
color="black"
|
||||
/>
|
||||
}
|
||||
onPress={() => {
|
||||
router.push(`/admin/voting/${item.id}/${status}`);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
value2={
|
||||
<TextCustom truncate={1}>
|
||||
{item?.Author?.username || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
value3={
|
||||
<TextCustom align="center" truncate={2}>
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
|
||||
104
app/(application)/admin/voting/history.tsx
Normal file
104
app/(application)/admin/voting/history.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ActionIcon,
|
||||
LoaderCustom,
|
||||
SearchInput,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
|
||||
import { Octicons } from "@expo/vector-icons";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
import { Divider } from "react-native-paper";
|
||||
|
||||
export default function AdminVotingHistory() {
|
||||
const [list, setList] = useState<any | null>(null);
|
||||
const [loadList, setLoadList] = useState(false);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [ search])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadList(true);
|
||||
const response = await apiAdminVoting({
|
||||
category: "history",
|
||||
search,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setList(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rightComponent = (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari"
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper headerComponent={<AdminTitlePage title="Voting" />}>
|
||||
<AdminComp_BoxTitle
|
||||
title="Riwayat"
|
||||
rightComponent={rightComponent}
|
||||
/>
|
||||
|
||||
<StackCustom gap={"sm"}>
|
||||
<AdminTitleTable
|
||||
title1="Aksi"
|
||||
title2="Username"
|
||||
title3="Judul Voting"
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{loadList ? <LoaderCustom/> : _.isEmpty(list) ? <TextCustom align="center" bold color="gray">Belum ada data</TextCustom> : list.map((item: any, i: number) => (
|
||||
<AdminTableValue
|
||||
key={i}
|
||||
value1={
|
||||
<ActionIcon
|
||||
icon={
|
||||
<Octicons
|
||||
name="eye"
|
||||
size={ICON_SIZE_BUTTON}
|
||||
color="black"
|
||||
/>
|
||||
}
|
||||
onPress={() => {
|
||||
router.push(`/admin/voting/${item.id}/history`);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
value2={<TextCustom truncate={1}>{item?.Author?.username || "-"}</TextCustom>}
|
||||
value3={
|
||||
<TextCustom align="center" truncate={2}>
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
16
components/Box/ReportBox.tsx
Normal file
16
components/Box/ReportBox.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import StackCustom from "../Stack/StackCustom";
|
||||
import TextCustom from "../Text/TextCustom";
|
||||
import BaseBox from "./BaseBox";
|
||||
|
||||
export default function ReportBox({ text }: { text: string }) {
|
||||
return (
|
||||
<BaseBox>
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom bold>
|
||||
Catatan penolakan
|
||||
</TextCustom>
|
||||
<TextCustom>{text}</TextCustom>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
);
|
||||
}
|
||||
@@ -1,27 +1,39 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import React from "react";
|
||||
import { StyleProp, StyleSheet, TextInput, View, ViewStyle } from "react-native";
|
||||
import {
|
||||
StyleProp,
|
||||
StyleSheet,
|
||||
View,
|
||||
ViewStyle
|
||||
} from "react-native";
|
||||
import TextCustom from "../Text/TextCustom";
|
||||
|
||||
interface CircularInputProps {
|
||||
value?: string | number
|
||||
value?: string | number;
|
||||
onChange?: (value: string | number) => void;
|
||||
icon?: React.ReactNode;
|
||||
style?: StyleProp<ViewStyle>
|
||||
style?: StyleProp<ViewStyle>;
|
||||
}
|
||||
|
||||
const CircularInput: React.FC<CircularInputProps> = ({ value, onChange, icon, style }) => {
|
||||
const CircularInput: React.FC<CircularInputProps> = ({
|
||||
value,
|
||||
onChange,
|
||||
icon,
|
||||
style,
|
||||
}) => {
|
||||
return (
|
||||
<View style={[styles.circleContainer, style]}>
|
||||
{icon ? (
|
||||
icon
|
||||
) : (
|
||||
<TextInput
|
||||
value={String(value)}
|
||||
onChangeText={onChange}
|
||||
<TextCustom
|
||||
// text={String(value)}
|
||||
style={styles.input}
|
||||
keyboardType="numeric"
|
||||
maxLength={2} // Batasan maksimal karakter
|
||||
/>
|
||||
// keyboardType="numeric"
|
||||
// maxLength={2} // Batasan maksimal karakter
|
||||
>
|
||||
{value}
|
||||
</TextCustom>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
@@ -39,7 +51,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
input: {
|
||||
color: MainColor.yellow, // Warna kuning
|
||||
fontSize: 24,
|
||||
fontSize: 18,
|
||||
fontWeight: "bold",
|
||||
textAlign: "center",
|
||||
padding: 0,
|
||||
|
||||
@@ -23,7 +23,7 @@ interface TextCustomProps {
|
||||
bold?: boolean;
|
||||
semiBold?: boolean;
|
||||
size?: "default" | "large" | "small" | "xlarge" | number
|
||||
color?: "default" | "yellow" | "red" | "gray" | "green" | "black"
|
||||
color?: "default" | "yellow" | "red" | "gray" | "green" | "black" | "orange"
|
||||
align?: TextAlign; // Prop untuk alignment
|
||||
truncate?: boolean | number;
|
||||
onPress?: () => void;
|
||||
@@ -62,6 +62,7 @@ const TextCustom: React.FC<TextCustomProps> = ({
|
||||
else if (color === "gray") selectedStyles.push(styles.gray);
|
||||
else if (color === "green") selectedStyles.push(styles.green);
|
||||
else if (color === "black") selectedStyles.push(styles.black);
|
||||
else if (color === "orange") selectedStyles.push(styles.orange);
|
||||
|
||||
// Alignment
|
||||
if (align) {
|
||||
@@ -140,4 +141,7 @@ export const styles = StyleSheet.create({
|
||||
black: {
|
||||
color: MainColor.black,
|
||||
},
|
||||
orange: {
|
||||
color: MainColor.orange,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,9 +4,11 @@ import Grid from "@/components/Grid/GridCustom";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
|
||||
export default function AdminButtonReview({
|
||||
isLoading,
|
||||
onPublish,
|
||||
onReject,
|
||||
}: {
|
||||
isLoading?: boolean;
|
||||
onPublish: () => void;
|
||||
onReject: () => void;
|
||||
}) {
|
||||
@@ -15,6 +17,7 @@ export default function AdminButtonReview({
|
||||
<Grid>
|
||||
<Grid.Col span={6} style={{ paddingRight: 10 }}>
|
||||
<ButtonCustom
|
||||
isLoading={isLoading}
|
||||
iconLeft={<IconPublish />}
|
||||
backgroundColor={MainColor.green}
|
||||
textColor="white"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { apiAdminJobUpdate } from "@/service/api-admin/api-admin-job";
|
||||
|
||||
const funUpdateStatus = async ({
|
||||
const funUpdateStatusJob = async ({
|
||||
id,
|
||||
changeStatus,
|
||||
data,
|
||||
@@ -23,4 +23,4 @@ const funUpdateStatus = async ({
|
||||
}
|
||||
};
|
||||
|
||||
export default funUpdateStatus;
|
||||
export default funUpdateStatusJob;
|
||||
|
||||
26
screens/Admin/Voting/funUpdateStatus.ts
Normal file
26
screens/Admin/Voting/funUpdateStatus.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { apiAdminVotingUpdateStatus } from "@/service/api-admin/api-admin-voting";
|
||||
|
||||
const funUpdateStatusVoting = async ({
|
||||
id,
|
||||
changeStatus,
|
||||
data,
|
||||
}: {
|
||||
id: string;
|
||||
changeStatus: "publish" | "review" | "reject";
|
||||
data?: string;
|
||||
}) => {
|
||||
try {
|
||||
const response = await apiAdminVotingUpdateStatus({
|
||||
id: id,
|
||||
status: changeStatus as any,
|
||||
data: data,
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export default funUpdateStatusVoting;
|
||||
@@ -49,7 +49,7 @@ const adminListMenu: NavbarItem[] = [
|
||||
{ label: "Publish", link: "/admin/voting/publish/status" },
|
||||
{ label: "Review", link: "/admin/voting/review/status" },
|
||||
{ label: "Reject", link: "/admin/voting/reject/status" },
|
||||
{ label: "Riwayat", link: "/admin/voting/riwayat/status" },
|
||||
{ label: "Riwayat", link: "/admin/voting/history" },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -142,7 +142,7 @@ const superAdminListMenu: NavbarItem[] = [
|
||||
{ label: "Publish", link: "/admin/voting/publish/status" },
|
||||
{ label: "Review", link: "/admin/voting/review/status" },
|
||||
{ label: "Reject", link: "/admin/voting/reject/status" },
|
||||
{ label: "Riwayat", link: "/admin/voting/riwayat/status" },
|
||||
{ label: "Riwayat", link: "/admin/voting/history" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -17,11 +17,7 @@ export async function apiAdminVoting({
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiAdminVotingById({
|
||||
id,
|
||||
}: {
|
||||
id: string;
|
||||
}) {
|
||||
export async function apiAdminVotingById({ id }: { id: string }) {
|
||||
try {
|
||||
const response = await apiConfig.get(`/mobile/admin/voting/${id}`);
|
||||
return response.data;
|
||||
@@ -29,3 +25,25 @@ export async function apiAdminVotingById({
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiAdminVotingUpdateStatus({
|
||||
id,
|
||||
data,
|
||||
status,
|
||||
}: {
|
||||
id: string;
|
||||
data?: string;
|
||||
status: "publish" | "review" | "reject";
|
||||
}) {
|
||||
try {
|
||||
const response = await apiConfig.put(
|
||||
`/mobile/admin/voting/${id}?status=${status}`,
|
||||
{
|
||||
data: data,
|
||||
}
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
14
utils/colorBadge.ts
Normal file
14
utils/colorBadge.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
|
||||
export const colorBadge = ({ status }: { status: string }) => {
|
||||
const statusLowerCase = status.toLowerCase();
|
||||
if (statusLowerCase === "publish") {
|
||||
return MainColor.green;
|
||||
} else if (statusLowerCase === "review") {
|
||||
return MainColor.orange;
|
||||
} else if (statusLowerCase === "reject") {
|
||||
return MainColor.red;
|
||||
} else {
|
||||
return MainColor.placeholder;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user