Compare commits

..

6 Commits

Author SHA1 Message Date
36dbfa3296 Integrasi API: Admin Event & Event user ui
Fix:
Metode konfirmasi pada event menggunakan QR Code
Perbaikan file pada:
- app/(application)/(user)/event/(tabs)/index.tsx
- app/(application)/(user)/event/[id]/confirmation.tsx
- app/(application)/(user)/event/[id]/publish.tsx
- app/(application)/admin/event/[id]/[status]/index.tsx
- app/(application)/admin/event/[id]/reject-input.tsx
- app/(application)/admin/event/[status]/status.tsx
- service/api-admin/api-admin-event.ts
- service/api-client/api-event.ts

### Issue: Belum menyertakan fungsi konfrimasu kehadiran
2025-10-23 17:47:36 +08:00
966e55597c Integrasi API: Admin event & QR Code event
Add:
- app/(application)/(user)/event/[id]/confirmation.tsx
- screens/Admin/Event/
- service/api-admin/api-admin-event.ts

Fix:
- app.config.js : penambahan DEEP_LINK_URL untuk url QR Code
- app/(application)/(user)/event/create.tsx
- app/(application)/admin/event/[id]/[status]/index.tsx
- app/(application)/admin/event/[id]/reject-input.tsx
- app/(application)/admin/event/[status]/status.tsx
- app/(application)/admin/event/index.tsx
- app/(application)/admin/job/[id]/[status]/index.tsx
- screens/Admin/listPageAdmin.tsx
- service/api-config.ts
-

### No Issue
2025-10-22 15:45:58 +08:00
4da55a5a8a 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
2025-10-21 16:52:17 +08:00
faf0f36e53 UI Fix: Pada tampilan ios bagian button ada yang ta terlihat dan sudah di perbaiki
### No Issue
2025-10-21 11:17:37 +08:00
57285e5697 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
2025-10-20 17:39:12 +08:00
1fd9694ebf Integrasi API: Admin Forum
Fix:
app/(application)/admin/forum/[id]/index.tsx
app/(application)/admin/forum/[id]/list-comment.tsx
app/(application)/admin/forum/[id]/list-report-comment.tsx
app/(application)/admin/forum/[id]/list-report-posting.tsx
app/(application)/admin/forum/index.tsx
app/(application)/admin/forum/posting.tsx
app/(application)/admin/forum/report-comment.tsx
app/(application)/admin/forum/report-posting.tsx
screens/Admin/listPageAdmin.tsx
service/api-admin/api-admin-forum.ts

### No Issue
2025-10-20 16:34:38 +08:00
47 changed files with 2115 additions and 702 deletions

View File

@@ -70,5 +70,6 @@ export default {
// Tambahkan environment variables ke sini
API_BASE_URL: process.env.API_BASE_URL,
BASE_URL: process.env.BASE_URL,
DEEP_LINK_URL: process.env.DEEP_LINK_URL,
},
};

View File

@@ -44,6 +44,8 @@ export default function EventBeranda() {
<TextCustom align="center">Belum ada event</TextCustom>
) : (
listData.map((item: any, index) => (
<Event_BoxPublishSection
key={index}
href={`/event/${item.id}/publish`}

View File

@@ -0,0 +1,500 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable react-hooks/exhaustive-deps */
import {
BaseBox,
ButtonCustom,
CenterCustom,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth";
import {
apiEventConfirmationAction,
apiEventGetConfirmation,
apiEventJoin,
} from "@/service/api-client/api-event";
import { Ionicons } from "@expo/vector-icons";
import dayjs from "dayjs";
import isBetween from "dayjs/plugin/isBetween";
import {
Redirect,
router,
Stack,
useFocusEffect,
useLocalSearchParams,
} from "expo-router";
import React, { useCallback, useState } from "react";
import { View } from "react-native";
import Toast from "react-native-toast-message";
// Extend Day.js dengan plugin isBetween
dayjs.extend(isBetween);
interface DataEvent {
id: string;
title: string;
tanggal: Date;
tanggalSelesai: Date;
lokasi: string;
Author: {
id: string;
username: string;
Profile: {
id: string;
name: string;
};
};
}
export default function UserEventConfirmation() {
const { token } = useAuth();
const { id, userId: authorId } = useLocalSearchParams();
const { user } = useAuth();
const [data, setData] = useState<DataEvent | null>(null);
const [peserta, setPeserta] = useState<boolean | null>(null);
const [konfirmasi, setKonfirmasi] = useState<boolean | null>(null);
useFocusEffect(
useCallback(() => {
checkTokenAndDataParticipants() || console.log("Token is null");
}, [token, id, user?.id])
);
const checkTokenAndDataParticipants = async () => {
if (!token) {
return <Redirect href={`/`} />;
}
try {
const response = await apiEventGetConfirmation({
id: id as string,
userId: user?.id as string,
});
console.log("[RES CONFIRMATION]", JSON.stringify(response, null, 2));
if (response.success) {
setData(response.data?.dataEvent);
setPeserta(response.data?.peserta);
setKonfirmasi(response.data?.kehadiran);
}
} catch (error) {
console.log("[ERROR CONFIRMATION]", error);
}
};
const handlerReturn = () => {
const now = dayjs(); // asumsi: UTC, sesuai dengan API
// --- [1] Loading & Data tidak ditemukan ---
if (data === undefined || data === null) {
if (peserta === null && konfirmasi === null) {
return <LoaderCustom />;
}
return (
<BaseBox>
<TextCustom bold align="center" size={"large"}>
Data Tidak Ditemukan
</TextCustom>
<BackToOtherPath path="home" />
</BaseBox>
);
}
// --- [2] Ambil waktu event dari `data` ---
const eventStart = dayjs(data.tanggal);
const eventEnd = dayjs(data.tanggalSelesai);
// --- [3] Definisikan jendela konfirmasi: 1 jam sebelum mulai → 1 jam setelah selesai ---
const confirmationStart = eventStart.subtract(1, "hour");
const confirmationEnd = eventEnd.add(1, "hour");
const isWithinConfirmationWindow = now.isBetween(
confirmationStart,
confirmationEnd,
null,
"[]"
);
// --- [4] Status waktu event (untuk pesan UI) ---
const isBeforeEvent = now.isBefore(eventStart);
const isAfterEvent = now.isAfter(eventEnd);
const isDuringEvent = !isBeforeEvent && !isAfterEvent;
// --- [5] Handle berdasarkan waktu dan status peserta/konfirmasi ---
// 🟢 Acara sudah selesai
if (isAfterEvent) {
if (peserta === false) {
return (
<TamplateBox data={data}>
<TamplateText text="Event telah selesai, sehingga konfirmasi kehadiran sudah tidak dapat dilakukan. Terima kasih atas perhatian dan minat Anda. Kami berharap dapat bertemu di acara kami berikutnya." />
<BackToOtherPath
path="event"
id={data.id}
isAfterEvent={isAfterEvent}
/>
</TamplateBox>
);
}
return (
<TamplateBox data={data}>
<TamplateText
text={`Event telah selesai, anda terdaftar sebagai peserta dan${
konfirmasi
? "Anda telah mengonfirmasi kehadiran."
: "Anda tidak mengonfirmasi kehadiran."
}. Terima kasih atas perhatian dan minat Anda. Kami berharap dapat bertemu di acara kami berikutnya.`}
/>
<BackToOtherPath
path="event"
id={data.id}
isAfterEvent={isAfterEvent}
/>
</TamplateBox>
);
}
// 🔵 Acara belum mulai & belum terdaftar
if (isBeforeEvent) {
if (peserta === false) {
return (
<NotStarted_And_UserNotParticipan
id={data.id}
userId={user?.id as string}
data={data}
/>
);
}
// Peserta sudah daftar → cek apakah sudah boleh konfirmasi
if (isWithinConfirmationWindow && peserta === true) {
if (konfirmasi === false) {
return (
<TamplateBox data={data}>
<TamplateText text="Konfirmasi Kehadiran" />
</TamplateBox>
);
}
return (
<TamplateBox data={data}>
<TamplateText text="Anda telah mengonfirmasi kehadiran." />
<BackToOtherPath
path="event"
id={data.id}
isAfterEvent={isAfterEvent}
/>
</TamplateBox>
);
}
return (
<TamplateBox data={data}>
<TamplateText text="Anda terdaftar sebagai peserta. Konfirmasi kehadiran dibuka 1 jam sebelum acara dimulai." />
<BackToOtherPath
path="event"
id={data.id}
isAfterEvent={isAfterEvent}
/>
</TamplateBox>
);
}
// 🟡 Acara sedang berlangsung & belum terdaftar
if (isDuringEvent) {
if (peserta === false) {
return (
<UserNotParticipan_And_DuringEvent
id={data.id}
userId={user?.id as string}
data={data}
/>
);
}
if (peserta === true) {
if (isWithinConfirmationWindow) {
if (konfirmasi === false) {
return (
<TamplateBox data={data}>
<TamplateText text="Konfirmasi Kehadiran" />
</TamplateBox>
);
}
return (
<TamplateBox data={data}>
<TamplateText text="Anda telah mengonfirmasi kehadiran dalam event ini. Terima kasih dan selamat menikmati acara. Have fun!" />
<BackToOtherPath
path="event"
id={data.id}
isAfterEvent={isAfterEvent}
/>
</TamplateBox>
);
}
// Ini sangat jarang terjadi selama event berlangsung, tapi aman
return (
<TamplateBox data={data}>
<TamplateText text="Konfirmasi kehadiran tidak tersedia saat ini." />
<BackToOtherPath path="home" />
</TamplateBox>
);
}
}
// 🛑 Fallback aman
return (
<BaseBox>
<StackCustom>
<TamplateText text="Terjadi kesalahan tak terduga pada logika waktu." />
<BackToOtherPath path="home" />
</StackCustom>
</BaseBox>
);
};
return (
<>
<Stack.Screen
options={{
title: "Konfirmasi Event",
headerLeft: () => (
<Ionicons
name="arrow-back"
size={20}
color={MainColor.yellow}
onPress={() =>
router.navigate("/(application)/(user)/event/create")
}
/>
),
}}
/>
<ViewWrapper>{handlerReturn()}</ViewWrapper>
</>
);
}
const TamplateBox = ({
data,
children,
}: {
data: DataEvent;
children: React.ReactNode;
}) => {
return (
<>
<CenterCustom>
<BaseBox>
<StackCustom gap={"lg"}>
<StackCustom gap={"sm"}>
<TextCustom bold align="center" size={"large"}>
{data?.title}
</TextCustom>
<View
style={{
flexDirection: "row",
justifyContent: "center",
// backgroundColor: AccentColor.blue,
// borderColor: AccentColor.blue,
// borderWidth: 1,
// borderRadius: 4,
// padding: 3
}}
>
<TextCustom align="center" size="small" bold>
{dayjs(data?.tanggal).format("DD MMM YYYY: HH:mm")}
</TextCustom>
<TextCustom align="center" size="small" bold>
{" "}
-{" "}
</TextCustom>
<TextCustom align="center" size="small" bold>
{dayjs(data?.tanggalSelesai).format("DD MMM YYYY: HH:mm")}
</TextCustom>
</View>
</StackCustom>
{children}
</StackCustom>
</BaseBox>
</CenterCustom>
</>
);
};
const TamplateText = ({ text }: { text: string }) => {
return (
<>
<TextCustom align="center">{text}</TextCustom>
</>
);
};
type BackToOtherPathProps =
| { path: "home" | "beranda-event"; id?: never; isAfterEvent?: never }
| { path: "event"; id: string; isAfterEvent: boolean };
const BackToOtherPath = ({ path, id, isAfterEvent }: BackToOtherPathProps) => {
return (
<>
{path === "home" ? (
<ButtonCustom
onPress={() => {
router.replace("/(application)/home");
}}
>
Home
</ButtonCustom>
) : (
<View
style={{
flexDirection: "row",
gap: 10,
justifyContent: "center",
}}
>
<ButtonCustom
onPress={() => {
router.replace("/(application)/home");
}}
>
Home
</ButtonCustom>
<ButtonCustom
onPress={() => {
if (path === "event") {
if (isAfterEvent) {
router.push(`/(application)/(user)/event/${id}/history`);
} else {
router.push(`/(application)/(user)/event/${id}/publish`);
}
} else if (path === "beranda-event") {
router.push(`/(application)/(user)/event`);
} else {
console.log("[PATH]", path);
}
}}
>
Lihat {path === "event" ? "Event" : "Beranda Event"}
</ButtonCustom>
</View>
)}
</>
);
};
// 🔵 Acara belum mulai & belum terdaftar
const NotStarted_And_UserNotParticipan = ({
id,
userId,
data,
}: {
id: string;
userId: string;
data: DataEvent;
}) => {
const [isLoading, setIsLoading] = useState<boolean>(false);
const handlerJoinEvent = async () => {
try {
setIsLoading(true);
const response = await apiEventJoin({
id: id as string,
userId: userId as string,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Anda gagal join",
});
return;
}
Toast.show({
type: "success",
text1: "Anda berhasil join",
});
router.navigate(`/(application)/(user)/event/${id}/publish`);
} catch (error) {
console.log("[ERROR JOIN EVENT]", error);
} finally {
setIsLoading(false);
}
};
return (
<>
<TamplateBox data={data}>
<TamplateText text="Anda belum terdaftar sebagai peserta & Event belum dimulai. Silahkan daftarkan diri anda terlebih dahulu" />
<ButtonCustom onPress={handlerJoinEvent} isLoading={isLoading}>
Join
</ButtonCustom>
</TamplateBox>
</>
);
};
// 🟡 ZONA ACARA BERLANGSUNG
// Acara sedang berlangsung & belum terdaftar
const UserNotParticipan_And_DuringEvent = ({
id,
userId,
data,
}: {
id: string;
userId: string;
data: DataEvent;
}) => {
const [isLoading, setIsLoading] = useState<boolean>(false);
const handlerSubmit = async () => {
try {
setIsLoading(true);
const response = await apiEventConfirmationAction({
id: id as string,
userId: userId as string,
category: "join_and_confirm",
});
// console.log("[RES JOIN & CONFIRMATION EVENT]", response);
if (!response.success) {
Toast.show({
type: "error",
text1: "Anda gagal join & konfirmasi",
});
return;
}
Toast.show({
type: "success",
text1: "Anda berhasil join & konfirmasi",
});
router.navigate(`/(application)/(user)/event/${id}/publish`);
} catch (error) {
console.log("[ERROR JOIN & CONFIRMATION EVENT]", error);
} finally {
setIsLoading(false);
}
};
return (
<>
<TamplateBox data={data}>
<TamplateText text="Anda belum terdaftar sebagai peserta & Event sedang berlangsung. Silahkan daftarkan diri anda & Konfirmasi kehadiran" />
<ButtonCustom onPress={() => handlerSubmit()} isLoading={isLoading}>
Join & Konfirmasi
</ButtonCustom>
</TamplateBox>
</>
);
};

View File

@@ -55,6 +55,8 @@ export default function EventDetailPublish() {
userId: user?.id as string,
});
console.log("[RES CHECK PARTICIPANTS]", responseCheckParticipants);
if (
responseCheckParticipants.success &&
responseCheckParticipants.data
@@ -69,6 +71,8 @@ export default function EventDetailPublish() {
}
}
console.log("[participans]", isParticipant);
const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH ", item.path);
router.navigate(item.path as any);

View File

@@ -191,7 +191,7 @@ export default function EventCreate() {
placeholder="Masukkan deskripsi event"
required
showCount
maxLength={100}
maxLength={1000}
onChangeText={(value: any) =>
setData({ ...data, deskripsi: value })
}

View File

@@ -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}

View File

@@ -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

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
AlertDefaultSystem,
@@ -15,98 +16,96 @@ 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 { MainColor } from "@/constants/color-palet";
import ReportBox from "@/components/Box/ReportBox";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import dayjs from "dayjs";
import { router, useLocalSearchParams } from "expo-router";
import { useAuth } from "@/hooks/use-auth";
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
import { DEEP_LINK_URL } from "@/service/api-config";
import { colorBadge } from "@/utils/colorBadge";
import { dateTimeView } from "@/utils/dateTimeView";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import React from "react";
import React, { useCallback } from "react";
import QRCode from "react-native-qrcode-svg";
export default function AdminEventDetail() {
const { user } = useAuth();
const { id, status } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = React.useState(false);
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;
console.log("[ID QRCODE]", id);
console.log("[STATUS Detail]", status);
const [openDrawer, setOpenDrawer] = React.useState(false);
const newURL = DEEP_LINK_URL
console.log("[DEEP LINK URL]", newURL);
const [data, setData] = React.useState<any | null>(null);
const deepLinkURL = `${DEEP_LINK_URL}/--/event/${id}/confirmation?userId=${user?.id}`;
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminEventById({
id: id as string,
});
// console.log(`[RES DATA BY ID: ${id}]`, JSON.stringify(response, null, 2));
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const listData = [
{
label: "Pembuat Event",
value: `Bagas Banuna ${id}`,
value: (data && data?.Author?.username) || "-",
},
{
label: "Judul Event",
value: `Event 123`,
value: (data && data?.title) || "-",
},
{
label: "Status",
value: (
<BadgeCustom color={colorBadge()}>
{_.startCase(status as string)}
</BadgeCustom>
),
value:
(data && (
<BadgeCustom color={colorBadge({ status: status as string })}>
{_.startCase(status as string)}
</BadgeCustom>
)) ||
"-",
},
{
label: "Lokasi",
value: "Lokasi Event",
value: (data && data?.lokasi) || "-",
},
{
label: "Tipe Acara",
value: "Tipe Acara",
value: (data && data?.EventMaster_TipeAcara?.name) || "-",
},
{
label: "Mulai Event",
value: dayjs().format("DD/MM/YYYY HH:mm:ss"),
value:
(data && data?.tanggal && dateTimeView({ date: data?.tanggal })) || "-",
},
{
label: "Event Berakhir",
value: dayjs().add(3, "day").format("DD/MM/YYYY HH:mm:ss"),
value:
(data &&
data?.tanggalSelesai &&
dateTimeView({ date: data?.tanggalSelesai })) ||
"-",
},
{
label: "Deskripsi",
value: "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
value: (data && data?.deskripsi) || "-",
},
// {
// label: "Daftar Tipe Acara",
// value: (
// <>
// <List.Item
// title={<TextCustom>Pilihan 1</TextCustom>}
// left={(props) => (
// <List.Icon {...props} icon="circle" 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} />
// )}
// />
// </>
// ),
// },
];
const rightComponent = (
@@ -118,6 +117,23 @@ export default function AdminEventDetail() {
/>
);
const handlerSubmit = async () => {
try {
const response = await funUpdateStatusEvent({
id: id as string,
changeStatus: "publish",
});
console.log("[RES PUBLISH]", JSON.stringify(response, null, 2));
if (response.success) {
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
}
};
return (
<>
<ViewWrapper
@@ -125,7 +141,7 @@ export default function AdminEventDetail() {
<AdminBackButtonAntTitle
title={`Detail Data`}
rightComponent={
(status === "publish" || status === "riwayat") && rightComponent
(status === "publish" || status === "history") && rightComponent
}
/>
}
@@ -143,12 +159,19 @@ export default function AdminEventDetail() {
<Spacing />
</BaseBox>
{(status === "publish" || status === "riwayat") && (
{data &&
data?.catatan &&
(status === "reject" || status === "review") && (
<ReportBox text={data?.catatan} />
)}
{(status === "publish" || status === "history") && (
<BaseBox>
<StackCustom style={{ alignItems: "center" }}>
<TextCustom bold>QR Code Event</TextCustom>
<QRCode
value="https://google.com"
value={deepLinkURL}
size={200}
// logo={require("@/assets/images/logo-hipmi.png")}
// logoSize={70}
@@ -168,16 +191,11 @@ export default function AdminEventDetail() {
message: "Apakah anda yakin ingin mempublikasikan data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressLeft: () => {
router.back();
},
onPressRight: () => {
router.back();
},
onPressRight: () => handlerSubmit(),
});
}}
onReject={() => {
router.push(`/admin/event/${id}/reject-input`);
router.push(`/admin/event/${id}/reject-input?status=${status}`);
}}
/>
)}
@@ -186,7 +204,7 @@ export default function AdminEventDetail() {
<AdminButtonReject
title="Tambah Catatan"
onReject={() => {
router.push(`/admin/event/${id}/reject-input`);
router.push(`/admin/event/${id}/reject-input?status=${status}`);
}}
/>
)}

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AlertDefaultSystem,
BoxButtonOnFooter,
@@ -6,15 +7,78 @@ import {
} 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";
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminEventRejectInput() {
const { id } = useLocalSearchParams();
const [value, setValue] = useState(id as string);
const { id, status } = useLocalSearchParams();
const [data, setData] = useState<any>("");
const [isLoading, setIsLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminEventById({
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 funUpdateStatusEvent({
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/event/reject/status`);
} else if (status === "reject") {
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<AdminButtonReject
isLoading={isLoading}
title="Reject"
onReject={() =>
AlertDefaultSystem({
@@ -22,12 +86,8 @@ export default function AdminEventRejectInput() {
message: "Apakah anda yakin ingin menolak data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressLeft: () => {
router.back();
},
onPressRight: () => {
console.log("value:", value);
router.replace(`/admin/event/reject/status`);
handleUpdate({ changeStatus: "reject" });
},
})
}
@@ -42,8 +102,8 @@ export default function AdminEventRejectInput() {
headerComponent={<AdminBackButtonAntTitle title="Penolakan Event" />}
>
<TextAreaCustom
value={value}
onChangeText={setValue}
value={data}
onChangeText={setData}
placeholder="Masukan alasan"
required
showCount

View File

@@ -1,27 +1,67 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
LoaderCustom,
SearchInput,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
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 { apiAdminEvent } from "@/service/api-admin/api-admin-event";
import { Octicons } from "@expo/vector-icons";
import { router, useLocalSearchParams } from "expo-router";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminEventStatus() {
const { status } = useLocalSearchParams();
console.log("[STATUS EVENT]", status);
const [listData, setListData] = useState<any[] | null>(null);
const [loadData, setLoadData] = useState(false);
const [search, setSearch] = useState<string>("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [status, search])
);
const onLoadData = async () => {
try {
setLoadData(true);
const response = await apiAdminEvent({
category: status as "publish" | "review" | "reject" | "history" as any,
search,
});
console.log(
`[RES LIST BY STATUS: ${status}]`,
JSON.stringify(response, null, 2)
);
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadData(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
value={search}
onChangeText={(value) => setSearch(value)}
/>
);
return (
@@ -32,44 +72,50 @@ export default function AdminEventStatus() {
rightComponent={rightComponent}
/>
<BaseBox>
<StackCustom gap={"sm"}>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Event"
/>
<Spacing />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/event/${index}/${status}`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
{loadData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center" size="small" color="gray">Belum ada data</TextCustom>
) : (
listData?.map((item, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/event/${item.id}/${status}`);
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
}
value3={
<TextCustom align="center" truncate={2}>
{item?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);

View File

@@ -9,13 +9,67 @@ import {
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { apiAdminEvent } from "@/service/api-admin/api-admin-event";
import { useFocusEffect } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminVoting() {
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [])
);
const onLoadData = async () => {
try {
const response = await apiAdminEvent({
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.placeholder} />,
},
{
label: "Tipe Acara",
value: (data && data.typeOfEvent) || 0,
icon: <IconList size={25} color={MainColor.placeholder} />,
},
];
return (
<>
<ViewWrapper>
<AdminTitlePage title="Event" />
<Spacing />
<StackCustom gap={"xs"}>
{listData.map((item, i) => (
<AdminComp_BoxDashboard key={i} item={item} />
@@ -25,31 +79,3 @@ export default function AdminVoting() {
</>
);
}
const listData = [
{
label: "Publish",
value: 3,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Review",
value: 8,
icon: <IconReview size={25} color={MainColor.orange} />,
},
{
label: "Reject",
value: 4,
icon: <IconReject size={25} color={MainColor.red} />,
},
{
label: "Riwayat",
value: 6,
icon: <IconArchive size={25} color={MainColor.placeholder} />,
},
{
label: "Tipe Acara",
value: 7,
icon: <IconList size={25} color={MainColor.placeholder} />,
},
];

View File

@@ -22,11 +22,7 @@ import { useCallback, useState } from "react";
export default function AdminForumDetailPosting() {
const { id } = useLocalSearchParams();
const [openDrawerPage, setOpenDrawerPage] = useState(false);
const [selectedId, setSelectedId] = useState<any>();
const [data, setData] = useState<any | null>(null);
const [listComment, setListComment] = useState<any[] | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
@@ -38,7 +34,6 @@ export default function AdminForumDetailPosting() {
const response = await apiAdminForumPostingById({
id: id as string,
});
console.log("[RES DATA]", JSON.stringify(response, null, 2));
if (response.success) {
setData(response.data);
@@ -86,14 +81,25 @@ export default function AdminForumDetailPosting() {
<AdminBackButtonAntTitle
title="Detail Posting"
rightComponent={
<ActionIcon
icon={<IconDot size={16} color={MainColor.darkblue} />}
onPress={() => setOpenDrawerPage(true)}
/>
data &&
data?.isActive && (
<ActionIcon
icon={<IconDot size={16} color={MainColor.darkblue} />}
onPress={() => setOpenDrawerPage(true)}
/>
)
}
/>
}
>
{data && !data?.isActive && (
<BaseBox>
<TextCustom bold align="center" color="red">
Postingan ini telah di nonaktifkan
</TextCustom>
</BaseBox>
)}
<BaseBox>
<StackCustom gap={"sm"}>
{listDataAction.map((item, i) => (
@@ -112,47 +118,6 @@ export default function AdminForumDetailPosting() {
<TextCustom>{(data && data?.diskusi) || "-"}</TextCustom>
</StackCustom>
</BaseBox>
{/* <AdminComp_BoxTitle title="Komentar" rightComponent={rightComponent} /> */}
{/* <StackCustom>
<AdminTitleTable title1="Aksi" title2="Username" title3="Komentar" />
<Divider />
{!listComment ? (
<LoaderCustom />
) : _.isEmpty(listComment) ? (
<TextCustom align="center" color="gray">
Tidak ada komentar
</TextCustom>
) : (
listComment?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Ionicons
name="ellipsis-vertical-outline"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
setSelectedId(index + 1);
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2}>{item?.komentar || "-"}</TextCustom>
}
/>
))
)}
</StackCustom> */}
</ViewWrapper>
<DrawerCustom

View File

@@ -1,41 +1,24 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
AlertDefaultSystem,
DrawerCustom,
LoaderCustom,
MenuDrawerDynamicGrid,
StackCustom,
TextCustom,
ViewWrapper,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import { IconView } from "@/components/_Icon/IconComponent";
import { IconOpenTo } from "@/components/_Icon/IconOpenTo";
import { IconTrash } from "@/components/_Icon/IconTrash";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { MainColor } from "@/constants/color-palet";
import {
ICON_SIZE_BUTTON,
ICON_SIZE_MEDIUM,
ICON_SIZE_XLARGE,
} from "@/constants/constans-value";
import { apiAdminForumCommentById } from "@/service/api-admin/api-admin-forum";
import { Ionicons } from "@expo/vector-icons";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminForumListComment() {
const { id } = useLocalSearchParams();
const [openDrawerAction, setOpenDrawerAction] = useState(false);
console.log("[ID]", id);
const [listComment, setListComment] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
@@ -45,49 +28,32 @@ export default function AdminForumListComment() {
const onLoadComment = async () => {
try {
setLoadList(true);
const response = await apiAdminForumCommentById({
id: id as string,
category: "get-all",
});
console.log("[RES COMMENT]", JSON.stringify(response, null, 2));
if (response.success) {
setListComment(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
setListComment([]);
} finally {
setLoadList(false);
}
};
const handlerAction = (item: { value: string; path: string }) => {
if (item.value === "delete") {
AlertDefaultSystem({
title: "Hapus Posting",
message: "Apakah Anda yakin ingin menghapus posting ini?",
textLeft: "Batal",
textRight: "Hapus",
onPressRight: () => {
Toast.show({
type: "success",
text1: "Posting berhasil dihapus",
});
},
});
} else {
router.navigate(item.path as any);
}
setOpenDrawerAction(false);
};
return (
<>
<ViewWrapper
headerComponent={<AdminBackButtonAntTitle title="Daftar Komentar" />}
>
<StackCustom>
<AdminTitleTable title1="Aksi" title2="Username" title3="Komentar" />
<AdminTitleTable title1="Aksi" title2="Report" title3="Komentar" />
<Divider />
{!listComment ? (
{loadList ? (
<LoaderCustom />
) : _.isEmpty(listComment) ? (
<TextCustom align="center" color="gray">
@@ -108,7 +74,7 @@ export default function AdminForumListComment() {
}
value2={
<TextCustom truncate={1}>
{item?.Author?.username || "-"}
{item?.countReport || 0}
</TextCustom>
}
value3={
@@ -120,34 +86,6 @@ export default function AdminForumListComment() {
</StackCustom>
</ViewWrapper>
<DrawerCustom
isVisible={openDrawerAction}
closeDrawer={() => setOpenDrawerAction(false)}
height={"auto"}
>
<MenuDrawerDynamicGrid
data={[
{
icon: <IconView />,
label: "Detail Komentar",
value: "detail",
path: `admin/forum/${id}/list-report-comment`,
},
{
icon: (
<IconTrash size={ICON_SIZE_MEDIUM} color={MainColor.white} />
),
label: "Hapus Komentar",
value: "delete",
path: "",
color: MainColor.red,
},
]}
onPressItem={(item) => {
handlerAction(item as any);
}}
/>
</DrawerCustom>
</>
);
}

View File

@@ -4,8 +4,8 @@ import {
AlertDefaultSystem,
BaseBox,
DrawerCustom,
LoaderCustom,
MenuDrawerDynamicGrid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
@@ -19,18 +19,31 @@ import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminForumCommentById } from "@/service/api-admin/api-admin-forum";
import {
apiAdminForumCommentById,
apiAdminForumDeactivateComment,
apiAdminForumListReportCommentById,
} from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminForumReportComment() {
const { id } = useLocalSearchParams();
console.log("[ID]", id);
const [data, setData] = useState<any | null>(null);
const [listReport, setListReport] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
const [openDrawer, setOpenDrawer] = useState(false);
const [openDrawerAction, setOpenDrawerAction] = useState(false);
const [selectedReport, setSelectedReport] = useState({
id: "",
username: "",
kategori: "",
keterangan: "",
deskripsi: "",
});
useFocusEffect(
useCallback(() => {
@@ -40,18 +53,28 @@ export default function AdminForumReportComment() {
const onLoadData = async () => {
try {
setLoadList(true);
const response = await apiAdminForumCommentById({
id: id as string,
category: "get-one",
});
console.log("[RES GET ONE COMMENT]", JSON.stringify(response, null, 2));
const responseReport = await apiAdminForumListReportCommentById({
id: id as string,
});
if (response.success) {
setData(response.data);
}
if (responseReport.success) {
setListReport(responseReport.data);
}
} catch (error) {
console.log("[ERROR]", error);
setData(null);
setListReport([]);
} finally {
setLoadList(false);
}
};
@@ -85,34 +108,52 @@ export default function AdminForumReportComment() {
<AdminComp_BoxTitle title="Daftar Report Komentar" />
<BaseBox>
<StackCustom>
<AdminTitleTable
title1="Aksi"
title2="Username"
title2="Pelapor"
title3="Kategori Report"
/>
<Spacing />
<Divider />
{Array.from({ length: 5 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => {
setOpenDrawerAction(true);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2} align="center">
SPAM
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(listReport) ? (
<TextCustom align="center" color="gray">
Tidak ada report
</TextCustom>
) : (
listReport?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => {
setOpenDrawerAction(true);
setSelectedReport({
id: item.id,
username: item.User?.username,
kategori: item.ForumMaster_KategoriReport?.title,
keterangan: item.ForumMaster_KategoriReport?.deskripsi,
deskripsi: item.deskripsi,
});
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.User?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.ForumMaster_KategoriReport?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
<DrawerCustom
@@ -136,7 +177,19 @@ export default function AdminForumReportComment() {
message: "Apakah Anda yakin ingin menghapus komentar ini?",
textLeft: "Batal",
textRight: "Hapus",
onPressRight: () => {
onPressRight: async () => {
const deleteComment = await apiAdminForumDeactivateComment({
id: id as string,
});
if (!deleteComment.success) {
Toast.show({
type: "error",
text1: "Komentar gagal dihapus",
});
return;
}
setOpenDrawer(false);
Toast.show({
type: "success",
@@ -154,37 +207,39 @@ export default function AdminForumReportComment() {
closeDrawer={() => setOpenDrawerAction(false)}
height={"auto"}
>
{listDataAction.map((item, i) => (
<StackCustom>
<GridDetail_4_8
key={i}
label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>}
label={<TextCustom bold>Pelapor</TextCustom>}
value={<TextCustom>{selectedReport?.username || "-"}</TextCustom>}
/>
))}
{selectedReport?.kategori && (
<>
<GridDetail_4_8
label={<TextCustom bold>Kategori Report</TextCustom>}
value={
<TextCustom>{selectedReport?.kategori || "-"}</TextCustom>
}
/>
<GridDetail_4_8
label={<TextCustom bold>Keterangan</TextCustom>}
value={
<TextCustom>{selectedReport?.keterangan || "-"}</TextCustom>
}
/>
</>
)}
{selectedReport?.deskripsi && (
<GridDetail_4_8
label={<TextCustom bold>Deskripsi</TextCustom>}
value={
<TextCustom>{selectedReport?.deskripsi || "-"}</TextCustom>
}
/>
)}
</StackCustom>
</DrawerCustom>
</>
);
}
const listData = [
{
label: "Username",
value: "Username",
},
];
const listDataAction = [
{
label: "Username",
value: "Riyusa",
},
{
label: "Kategori Report",
value: "SPAM",
},
{
label: "Deskripsi",
value:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis asperiores quidem deleniti architecto eaque et nostrum, ad consequuntur eveniet quisquam quae voluptatum ducimus! Dolorem nobis modi officia debitis, beatae mollitia.",
},
];

View File

@@ -1,11 +1,12 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
AlertDefaultSystem,
BadgeCustom,
BaseBox,
DrawerCustom,
LoaderCustom,
MenuDrawerDynamicGrid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
@@ -19,15 +20,64 @@ import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { router } from "expo-router";
import { useState } from "react";
import {
apiAdminForumDeactivatePosting,
apiAdminForumListReportPostingById,
apiAdminForumPostingById,
} from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminForumReportPosting() {
const { id } = useLocalSearchParams();
const [openDrawerPage, setOpenDrawerPage] = useState(false);
const [openDrawerAction, setOpenDrawerAction] = useState(false);
const [data, setData] = useState<any | null>(null);
const [listReport, setListReport] = useState<any[] | null>(null);
const [loadListReport, setLoadListReport] = useState(false);
const [selectedReport, setSelectedReport] = useState({
id: "",
username: "",
kategori: "",
keterangan: "",
deskripsi: "",
});
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
setLoadListReport(true);
const response = await apiAdminForumPostingById({
id: id as string,
});
const responseReport = await apiAdminForumListReportPostingById({
id: id as string,
});
if (response.success) {
setData(response.data);
}
if (responseReport.success) {
setListReport(responseReport.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadListReport(false);
}
};
return (
<>
<ViewWrapper
@@ -45,50 +95,86 @@ export default function AdminForumReportPosting() {
>
<BaseBox>
<StackCustom gap={"sm"}>
{listData.map((item, i) => (
<GridDetail_4_8
key={i}
label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>}
/>
))}
<TextCustom bold>Posting</TextCustom>
<TextCustom>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Asperiores cupiditate nobis dignissimos explicabo quo unde dolorum
numquam eos ab laborum fugiat illo nam velit quibusdam, maxime
assumenda aut vero provident!
</TextCustom>
<GridDetail_4_8
label={<TextCustom bold>Username</TextCustom>}
value={<TextCustom>{data?.Author?.username || "-"}</TextCustom>}
/>
<GridDetail_4_8
label={<TextCustom bold>Status</TextCustom>}
value={
data && data?.ForumMaster_StatusPosting?.status ? (
<BadgeCustom
color={
data?.ForumMaster_StatusPosting?.status === "Open"
? MainColor.green
: MainColor.red
}
>
{data?.ForumMaster_StatusPosting?.status === "Open"
? "Open"
: "Close"}
</BadgeCustom>
) : (
<TextCustom>{"-"}</TextCustom>
)
}
/>
<GridDetail_4_8
label={<TextCustom bold>Postingan</TextCustom>}
value={<TextCustom>{data?.diskusi || "-"}</TextCustom>}
/>
</StackCustom>
</BaseBox>
<AdminComp_BoxTitle title="Daftar Report Posting" />
<BaseBox>
<StackCustom gap={"sm"}>
<AdminTitleTable
title1="Aksi"
title2="Username"
title2="Pelapor"
title3="Kategori Report"
/>
<Spacing />
<Divider />
{Array.from({ length: 5 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => setOpenDrawerAction(true)}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2} align="center">
SPAM
</TextCustom>
}
/>
))}
</BaseBox>
{loadListReport ? (
<LoaderCustom />
) : _.isEmpty(listReport) ? (
<TextCustom align="center" color={"gray"}>
Belum ada report
</TextCustom>
) : (
listReport?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => {
setOpenDrawerAction(true);
setSelectedReport({
id: item?.id,
username: item?.User?.username,
kategori: item?.ForumMaster_KategoriReport?.title,
keterangan: item?.ForumMaster_KategoriReport?.deskripsi,
deskripsi: item?.deskripsi,
});
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.User?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.ForumMaster_KategoriReport?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
<DrawerCustom
@@ -112,13 +198,25 @@ export default function AdminForumReportPosting() {
message: "Apakah Anda yakin ingin menghapus posting ini?",
textLeft: "Batal",
textRight: "Hapus",
onPressRight: () => {
onPressRight: async () => {
const response = await apiAdminForumDeactivatePosting({
id: id as string,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Posting gagal dihapus",
});
return;
}
setOpenDrawerPage(false);
Toast.show({
type: "success",
text1: "Posting berhasil dihapus",
});
router.back()
router.back();
},
});
}}
@@ -130,41 +228,39 @@ export default function AdminForumReportPosting() {
closeDrawer={() => setOpenDrawerAction(false)}
height={"auto"}
>
{listDataAction.map((item, i) => (
<StackCustom>
<GridDetail_4_8
key={i}
label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>}
label={<TextCustom bold>Pelapor</TextCustom>}
value={<TextCustom>{selectedReport?.username || "-"}</TextCustom>}
/>
))}
{selectedReport?.kategori && (
<>
<GridDetail_4_8
label={<TextCustom bold>Kategori Report</TextCustom>}
value={
<TextCustom>{selectedReport?.kategori || "-"}</TextCustom>
}
/>
<GridDetail_4_8
label={<TextCustom bold>Keterangan</TextCustom>}
value={
<TextCustom>{selectedReport?.keterangan || "-"}</TextCustom>
}
/>
</>
)}
{selectedReport?.deskripsi && (
<GridDetail_4_8
label={<TextCustom bold>Deskripsi</TextCustom>}
value={
<TextCustom>{selectedReport?.deskripsi || "-"}</TextCustom>
}
/>
)}
</StackCustom>
</DrawerCustom>
</>
);
}
const listData = [
{
label: "Username",
value: "Username",
},
{
label: "Status",
value: <BadgeCustom color={MainColor.green}>Open</BadgeCustom>,
},
];
const listDataAction = [
{
label: "Username",
value: "Firman Nusantara",
},
{
label: "Kategori Report",
value: "SPAM",
},
{
label: "Deskripsi",
value:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis asperiores quidem deleniti architecto eaque et nostrum, ad consequuntur eveniet quisquam quae voluptatum ducimus! Dolorem nobis modi officia debitis, beatae mollitia.",
},
];

View File

@@ -22,7 +22,6 @@ export default function AdminForum() {
category: "dashboard",
});
console.log("[RES DASHBOARD]", JSON.stringify(response, null, 2));
if (response.success) {
setData(response.data);
}
@@ -39,12 +38,12 @@ export default function AdminForum() {
},
{
label: "Report Posting",
value: data?.report_posting || 0,
value: data?.reportPosting || 0,
icon: <IconReport size={25} color={MainColor.orange} />,
},
{
label: "Report Comment",
value: data?.report_comment || 0,
value: data?.reportComment || 0,
icon: <IconReport size={25} color={MainColor.red} />,
},
];

View File

@@ -1,10 +1,8 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
LoaderCustom,
SearchInput,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
@@ -39,8 +37,7 @@ export default function AdminForumPosting() {
category: "posting",
search: search,
});
// console.log("[RES LIST POSTING]", JSON.stringify(response, null, 2));
if (response.success) {
setList(response.data);
}

View File

@@ -1,9 +1,9 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
Divider,
LoaderCustom,
SearchInput,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
@@ -14,14 +14,48 @@ import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { router } from "expo-router";
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminForumReportComment() {
const [listData, setListData] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState<boolean>(false);
const [search, setSearch] = useState<string>("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [search])
);
const onLoadData = async () => {
try {
setLoadList(true);
const response = await apiAdminForum({
category: "report_comment",
search: search,
});
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari Komentar"
value={search}
onChangeText={setSearch}
/>
);
@@ -29,36 +63,56 @@ export default function AdminForumReportComment() {
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Forum" />}>
<AdminComp_BoxTitle
title="Report Comment"
title="Report Komentar"
rightComponent={rightComponent}
/>
<BaseBox>
<AdminTitleTable title1="Aksi" title2="Pelapor" title3="Jenis Laporan" />
<Spacing />
<StackCustom gap={"sm"}>
<AdminTitleTable
title1="Aksi"
title2="Pelapor"
title3="Jenis Laporan"
/>
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<IconView size={ICON_SIZE_BUTTON} color={MainColor.black} />
}
onPress={() => {
router.push(`/admin/forum/${index + 1}/list-report-comment`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2} align="center">
SPAM
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center" color="gray">
Belum ada data
</TextCustom>
) : (
listData?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<IconView
size={ICON_SIZE_BUTTON}
color={MainColor.black}
/>
}
onPress={() => {
router.push(
`/admin/forum/${item?.Forum_Komentar?.id}/list-report-comment`
);
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.User?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.ForumMaster_KategoriReport?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);

View File

@@ -1,12 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
Divider,
LoaderCustom,
SearchInput,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
ViewWrapper
} from "@/components";
import { IconView } from "@/components/_Icon/IconComponent";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
@@ -15,17 +15,47 @@ import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { router } from "expo-router";
import { useState } from "react";
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
export default function AdminForumReportPosting() {
const [openDrawer, setOpenDrawer] = useState(false);
const [id, setId] = useState<any>();
const [listData, setListData] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState<boolean>(false);
const [search, setSearch] = useState<string>("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [search])
);
const onLoadData = async () => {
try {
setLoadList(true);
const response = await apiAdminForum({
category: "report_posting",
search: search,
});
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
placeholder="Cari Postingan"
value={search}
onChangeText={setSearch}
/>
);
@@ -37,36 +67,49 @@ export default function AdminForumReportPosting() {
rightComponent={rightComponent}
/>
<BaseBox>
<StackCustom gap={"sm"}>
<AdminTitleTable title1="Aksi" title2="Pelapor" title3="Postingan" />
<Spacing />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<IconView size={ICON_SIZE_BUTTON} color={MainColor.black} />
}
onPress={() => {
router.push(`/admin/forum/${id}/list-report-posting`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2} align="center">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
Omnis laborum doloremque eius velit voluptate corrupti vel,
provident quaerat tempore animi sed accusamus amet.
Temporibus, praesentium? Rem voluptatum nesciunt voluptas
repellat.
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center" color="gray">
Belum ada data
</TextCustom>
) : (
listData?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<IconView
size={ICON_SIZE_BUTTON}
color={MainColor.black}
/>
}
onPress={() => {
router.push(
`/admin/forum/${item?.Forum_Posting?.id}/list-report-posting`
);
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.User?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.Forum_Posting?.diskusi || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);

View File

@@ -13,8 +13,9 @@ import {
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import ReportBox from "@/components/Box/ReportBox";
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 +89,7 @@ export default function AdminJobDetailStatus() {
changeStatus: "publish" | "review" | "reject";
}) => {
try {
const response = await funUpdateStatus({
const response = await funUpdateStatusJob({
id: id as string,
changeStatus,
});
@@ -141,13 +142,8 @@ export default function AdminJobDetailStatus() {
</StackCustom>
</BaseBox>
{data && data?.catatan && (
<BaseBox>
<StackCustom>
<TextCustom bold>Catatan report</TextCustom>
<TextCustom>{data?.catatan}</TextCustom>
</StackCustom>
</BaseBox>
{data && data?.catatan && (status === "reject" || status === "review") && (
<ReportBox text={data?.catatan}/>
)}
{status === "review" && (

View File

@@ -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,

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AlertDefaultSystem,
BadgeCustom,
@@ -13,90 +14,153 @@ 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";
import { router, useLocalSearchParams } from "expo-router";
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);
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;
console.log("[status]", status);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminVotingById({
id: id as string,
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const listData = [
{
label: "Username",
value: "Bagas Banuna",
value: (data && data?.Author?.username) || "-",
},
{
label: "Judul",
value: `Judul Proyek: ${id}Lorem ipsum dolor sit amet consectetur adipisicing elit.`,
value: (data && data?.title) || "-",
},
{
label: "Status",
value: (
<BadgeCustom color={colorBadge()}>
{_.startCase(status as string)}
</BadgeCustom>
),
value:
data && data?.Voting_Status?.name ? (
<BadgeCustom color={colorBadge({ status: status as string })}>
{status === "history" ? "Riwayat" : _.startCase(status as string)}
</BadgeCustom>
) : (
"-"
),
},
{
label: "Mulai Voting",
value: dayjs().format("DD/MM/YYYY"),
value:
(data && data?.awalVote && dateTimeView({ date: data?.awalVote })) ||
"-",
},
{
label: "Voting Berakhir",
value: dayjs().format("DD/MM/YYYY"),
value:
(data && data?.akhirVote && dateTimeView({ date: data?.akhirVote })) ||
"-",
},
{
label: "Deskripsi",
value: "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
value: (data && data?.deskripsi) || "-",
},
{
label: "Daftar Pilhan",
value: (
<>
<List.Item
title={<TextCustom>Pilihan 1</TextCustom>}
left={(props) => (
<List.Icon {...props} icon="circle" 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} />
)}
/>
</>
),
label: "Daftar Pilihan",
value:
data && data?.Voting_DaftarNamaVote
? data?.Voting_DaftarNamaVote?.map((item: any, i: number) => (
<List.Item
key={i}
title={<TextCustom>{item?.value}</TextCustom>}
left={(props) => (
<Entypo name="chevron-right" color={MainColor.yellow} />
)}
/>
))
: "-",
},
];
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
@@ -114,46 +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`);
}}
/>
)}
@@ -162,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`);
}}
/>
)}

View 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>
</>
);
}

View File

@@ -1,55 +0,0 @@
import {
AlertDefaultSystem,
BoxButtonOnFooter,
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",
onPressLeft: () => {
router.back();
},
onPressRight: () => {
console.log("value:", value);
router.replace(`/admin/voting/reject/status`);
},
})
}
/>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper
footerComponent={buttonSubmit}
headerComponent={<AdminBackButtonAntTitle title="Penolakan Voting" />}
>
<TextAreaCustom
value={value}
onChangeText={setValue}
placeholder="Masukan alasan"
required
showCount
maxLength={1000}
/>
</ViewWrapper>
</>
);
}

View File

@@ -1,27 +1,60 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
SearchInput,
Spacing,
TextCustom,
ViewWrapper,
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, useLocalSearchParams } from "expo-router";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminVotingStatus() {
const { status } = useLocalSearchParams();
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,
});
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 (
@@ -32,44 +65,52 @@ export default function AdminVotingStatus() {
rightComponent={rightComponent}
/>
<BaseBox>
<StackCustom gap={"sm"}>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Voting"
/>
<Spacing />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/voting/${index}/${status}`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
{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>
</>
);

View 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>
</>
);
}

View File

@@ -8,8 +8,56 @@ import {
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
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() {
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 (
<>
<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

@@ -17,7 +17,6 @@
"axios": "^1.11.0",
"dayjs": "^1.11.13",
"expo": "^54.0.0",
"expo-blur": "~15.0.7",
"expo-camera": "~17.0.7",
"expo-clipboard": "~8.0.7",
"expo-constants": "~18.0.8",
@@ -1153,8 +1152,6 @@
"expo-asset": ["expo-asset@12.0.8", "", { "dependencies": { "@expo/image-utils": "^0.8.7", "expo-constants": "~18.0.8" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-jj2U8zw9+7orST2rlQGULYiqPoECOuUyffs2NguGrq84bYbkM041T7TOMXH2raPVJnM9lEAP54ezI6XL+GVYqw=="],
"expo-blur": ["expo-blur@15.0.7", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-SugQQbQd+zRPy8z2G5qDD4NqhcD7srBF7fN7O7yq6q7ZFK59VWvpDxtMoUkmSfdxgqONsrBN/rLdk00USADrMg=="],
"expo-camera": ["expo-camera@17.0.7", "", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-jdZfijfFjlVAuuIkDheA41YKpigPjqsN0juRvgyr7Lcyz+fvwZ3/RP50/n/hvuozH657wHxPfiyVVFa00z8TcQ=="],
"expo-clipboard": ["expo-clipboard@8.0.7", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-zvlfFV+wB2QQrQnHWlo0EKHAkdi2tycLtE+EXFUWTPZYkgu1XcH+aiKfd4ul7Z0SDF+1IuwoiW9AA9eO35aj3Q=="],

View 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>
);
}

View File

@@ -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,

View File

@@ -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,
},
});

View File

@@ -14,7 +14,7 @@ export default function AdminButtonReject({
return (
<>
<ButtonCustom
iconLeft={<IconReject />}
iconLeft={<IconReject size={16} />}
backgroundColor={MainColor.red}
textColor="white"
onPress={onReject}

View File

@@ -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"

View File

@@ -12,7 +12,7 @@ import {
StyleProp,
ViewStyle,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { NativeSafeAreaViewProps, SafeAreaView } from "react-native-safe-area-context";
interface ViewWrapperProps {
children: React.ReactNode;
@@ -21,6 +21,7 @@ interface ViewWrapperProps {
footerComponent?: React.ReactNode;
floatingButton?: React.ReactNode;
hideFooter?: boolean;
edgesFooter?: NativeSafeAreaViewProps["edges"];
style?: StyleProp<ViewStyle>;
}
@@ -37,6 +38,7 @@ const ViewWrapper = ({
footerComponent,
floatingButton,
hideFooter = false,
edgesFooter =[],
style,
}: ViewWrapperProps) => {
const assetBackground = require("../../assets/images/main-background.png");
@@ -77,7 +79,7 @@ const ViewWrapper = ({
{footerComponent ? (
<SafeAreaView
edges={["bottom"]}
edges={Platform.OS === "ios" ? edgesFooter : ["bottom"]}
style={{
backgroundColor: MainColor.darkblue,
height: OS_HEIGHT

View File

@@ -23,7 +23,7 @@ export {
// OS Height
const OS_ANDROID_HEIGHT = 115
const OS_IOS_HEIGHT = 65
const OS_IOS_HEIGHT = 70
const OS_HEIGHT = Platform.OS === "ios" ? OS_IOS_HEIGHT : OS_ANDROID_HEIGHT
// Text Size

View File

@@ -24,7 +24,6 @@
"axios": "^1.11.0",
"dayjs": "^1.11.13",
"expo": "^54.0.0",
"expo-blur": "~15.0.7",
"expo-camera": "~17.0.7",
"expo-clipboard": "~8.0.7",
"expo-constants": "~18.0.8",

View File

@@ -0,0 +1,23 @@
import { apiAdminEventUpdateStatus } from "@/service/api-admin/api-admin-event";
export const funUpdateStatusEvent = async ({
id,
changeStatus,
data,
}: {
id: string;
changeStatus: "publish" | "review" | "reject";
data?: string;
}) => {
try {
const response = await apiAdminEventUpdateStatus({
id: id,
changeStatus: changeStatus as any,
data: data,
});
return response;
} catch (error) {
console.log("[ERROR]", error);
throw error;
}
};

View File

@@ -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;

View 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;

View File

@@ -38,7 +38,7 @@ const adminListMenu: NavbarItem[] = [
{ label: "Review", link: "/admin/event/review/status" },
{ label: "Reject", link: "/admin/event/reject/status" },
{ label: "Tipe Acara", link: "/admin/event/type-of-event" },
{ label: "Riwayat", link: "/admin/event/riwayat/status" },
{ label: "Riwayat", link: "/admin/event/history/status" },
],
},
{
@@ -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" },
],
},
{
@@ -69,7 +69,7 @@ const adminListMenu: NavbarItem[] = [
{ label: "Dashboard", link: "/admin/forum" },
{ label: "Posting", link: "/admin/forum/posting" },
{ label: "Report Posting", link: "/admin/forum/report-posting" },
{ label: "Report Comment", link: "/admin/forum/report-comment" },
{ label: "Report Komentar", link: "/admin/forum/report-comment" },
],
},
{
@@ -131,7 +131,7 @@ const superAdminListMenu: NavbarItem[] = [
{ label: "Review", link: "/admin/event/review/status" },
{ label: "Reject", link: "/admin/event/reject/status" },
{ label: "Tipe Acara", link: "/admin/event/type-of-event" },
{ label: "Riwayat", link: "/admin/event/riwayat/status" },
{ label: "Riwayat", link: "/admin/event/history/status" },
],
},
{
@@ -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" },
],
},
{
@@ -162,7 +162,7 @@ const superAdminListMenu: NavbarItem[] = [
{ label: "Dashboard", link: "/admin/forum" },
{ label: "Posting", link: "/admin/forum/posting" },
{ label: "Report Posting", link: "/admin/forum/report-posting" },
{ label: "Report Comment", link: "/admin/forum/report-comment" },
{ label: "Report Komentar", link: "/admin/forum/report-comment" },
],
},
{

View File

@@ -0,0 +1,50 @@
import { apiConfig } from "../api-config";
export async function apiAdminEvent({
category,
search,
}: {
category: "dashboard" | "history" | "publish" | "review" | "type-of-event";
search?: string;
}) {
try {
const response = await apiConfig.get(
`/mobile/admin/event?category=${category}&search=${search}`
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminEventById({ id }: { id: string }) {
try {
const response = await apiConfig.get(`/mobile/admin/event/${id}`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminEventUpdateStatus({
id,
changeStatus,
data,
}: {
id: string;
changeStatus: "publish" | "review" | "reject";
data?: string;
}) {
try {
const response = await apiConfig.put(
`/mobile/admin/event/${id}?status=${changeStatus}`,
{
data: data,
}
);
return response.data;
} catch (error) {
throw error;
}
}

View File

@@ -41,3 +41,51 @@ export async function apiAdminForumCommentById({
throw error;
}
}
export async function apiAdminForumListReportCommentById({
id,
}: {
id: string;
}) {
try {
const response = await apiConfig.get(
`/mobile/admin/forum/${id}/report-comment`
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminForumDeactivateComment({ id }: { id: string }) {
try {
const response = await apiConfig.put(`/mobile/admin/forum/${id}/comment`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminForumListReportPostingById({
id,
}: {
id: string;
}) {
try {
const response = await apiConfig.get(
`/mobile/admin/forum/${id}/report-posting`
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminForumDeactivatePosting({ id }: { id: string }) {
try {
const response = await apiConfig.put(`/mobile/admin/forum/${id}`);
return response.data;
} catch (error) {
throw error;
}
}

View File

@@ -0,0 +1,49 @@
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;
}
}
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;
}
}

View File

@@ -138,3 +138,42 @@ export async function apiEventCheckParticipants({
throw error;
}
}
export async function apiEventGetConfirmation({
id,
userId,
}: {
id: string;
userId?: string;
}) {
try {
const response = await apiConfig.get(`/mobile/event/${id}/confirmation?userId=${userId}`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiEventConfirmationAction({
id,
userId,
category,
}: {
id: string;
userId?: string;
category?: "join_and_confirm" | "confirmation";
}) {
try {
const response = await apiConfig.post(
`/mobile/event/${id}/confirmation?category=${category}`,
{
data: {
userId: userId,
},
}
);
return response.data;
} catch (error) {
throw error;
}
}

View File

@@ -3,6 +3,7 @@ import axios, { AxiosInstance } from "axios";
import Constants from "expo-constants";
export const BASE_URL = Constants.expoConfig?.extra?.BASE_URL;
export const API_BASE_URL = Constants.expoConfig?.extra?.API_BASE_URL;
export const DEEP_LINK_URL = Constants.expoConfig?.extra?.DEEP_LINK_URL;
export const apiConfig: AxiosInstance = axios.create({
baseURL: API_BASE_URL,

View File

@@ -195,7 +195,14 @@ export const GStyles = StyleSheet.create({
bottomBar: {
backgroundColor: MainColor.darkblue,
borderTopColor: AccentColor.blue,
borderTopWidth: 1,
// borderTopWidth: 0.5,
height: "100%",
justifyContent: "center",
shadowColor: AccentColor.blue,
shadowOffset: { width: 0, height: -5},
shadowOpacity: 0.4,
shadowRadius: 40,
elevation: 8, // untuk Android
},
bottomBarContainer: {
paddingHorizontal: 15,

14
utils/colorBadge.ts Normal file
View 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;
}
};