Compare commits

...

2 Commits

Author SHA1 Message Date
465e01015e Penambahan notifikasi untuk fitur voting
Fix:
- app/(application)/(user)/event/[id]/publish.tsx
- app/(application)/(user)/voting/(tabs)/_layout.tsx
- app/(application)/(user)/voting/(tabs)/status.tsx
- app/(application)/(user)/voting/[id]/index.tsx
- app/(application)/(user)/voting/create.tsx
- app/(application)/admin/voting/[id]/[status]/index.tsx
- app/(application)/admin/voting/[id]/[status]/reject-input.tsx
- screens/Admin/Event/funUpdateStatus.ts
- screens/Admin/Voting/funUpdateStatus.ts
- service/api-admin/api-admin-voting.ts
- types/type-collect-other.ts

### No Issue
2026-01-15 17:39:39 +08:00
3b15871ad4 Notifikasi join event
Fix:
modified:   app/(application)/(user)/event/[id]/history.tsx
        modified:   app/(application)/(user)/event/[id]/publish.tsx
        modified:   app/(application)/(user)/event/create.tsx
        modified:   app/(application)/(user)/notifications/index.tsx

### No Issue
2026-01-15 13:54:14 +08:00
14 changed files with 113 additions and 47 deletions

View File

@@ -56,7 +56,7 @@ export default function EventDetailHistory() {
<DrawerCustom
isVisible={openDrawer}
closeDrawer={() => setOpenDrawer(false)}
height={250}
height={"auto"}
>
<MenuDrawerDynamicGrid
data={menuDrawerPublishEvent({ id: id as string })}

View File

@@ -1,14 +1,15 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AlertDefaultSystem,
BackButton,
ButtonCustom,
DotButton,
DrawerCustom,
LoaderCustom,
MenuDrawerDynamicGrid,
ViewWrapper,
} from "@/components";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import LeftButtonCustom from "@/components/Button/BackButton";
import { useAuth } from "@/hooks/use-auth";
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
@@ -18,23 +19,26 @@ import {
apiEventGetOne,
apiEventJoin,
} from "@/service/api-client/api-event";
import dayjs from "dayjs";
import {
Redirect,
router,
Stack,
useFocusEffect,
useLocalSearchParams,
} from "expo-router";
import { useCallback, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import Toast from "react-native-toast-message";
export default function EventDetailPublish() {
const { id } = useLocalSearchParams();
const now = new Date().toISOString();
const { user } = useAuth();
const { id } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = useState(false);
const [isLoadingData, setIsLoadingData] = useState(false);
const [isLoadingJoin, setIsLoadingJoin] = useState(false);
const [data, setData] = useState();
const [data, setData] = useState<any>();
const [isParticipant, setIsParticipant] = useState<boolean | null>(null);
useFocusEffect(
@@ -55,8 +59,6 @@ export default function EventDetailPublish() {
userId: user?.id as string,
});
console.log("[RES CHECK PARTICIPANTS]", responseCheckParticipants);
if (
responseCheckParticipants.success &&
responseCheckParticipants.data
@@ -108,7 +110,24 @@ export default function EventDetailPublish() {
}
};
const footerButton = () => {
const isEventFinished =
id && data?.tanggalSelesai && dayjs(data.tanggalSelesai).isBefore(now);
useEffect(() => {
if (isEventFinished) {
router.replace(`/(application)/(user)/event/${id}/history`);
}
}, [isEventFinished, id]);
if (isEventFinished) {
return (
<ViewWrapper>
<CustomSkeleton />
</ViewWrapper>
);
}
const FooterButton = () => {
return (
<>
<ButtonCustom
@@ -138,17 +157,17 @@ export default function EventDetailPublish() {
<Stack.Screen
options={{
title: `Event Publish`,
headerLeft: () => <LeftButtonCustom />,
headerLeft: () => <BackButton onPress={() => router.back()} />,
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
}}
/>
<ViewWrapper>
{isLoadingData ? (
<LoaderCustom />
<CustomSkeleton height={400} />
) : (
<Event_BoxDetailPublishSection
data={data}
footerButton={footerButton()}
footerButton={FooterButton()}
/>
)}
</ViewWrapper>

View File

@@ -78,23 +78,6 @@ export default function EventCreate() {
return;
}
// if (selectedDate) {
// console.log("Tanggal yang dipilih:", selectedDate);
// console.log(`ISO Format ${Platform.OS}:`, selectedDate.toString());
// // Kirim ke API atau proses lanjutan
// } else {
// console.log("Tanggal belum dipilih");
// }
// if (selectedEndDate) {
// console.log("Tanggal yang dipilih:", selectedEndDate);
// console.log(`ISO Format ${Platform.OS}:`, selectedEndDate.toString());
// // Kirim ke API atau proses lanjutan
// } else {
// console.log("Tanggal berakhir belum dipilih");
// }
try {
setIsLoading(true);
@@ -110,7 +93,7 @@ export default function EventCreate() {
const response = await apiEventCreate(newData);
console.log("Response", JSON.stringify(response, null, 2));
router.replace("/event/status");
router.replace("/event/status?status=review");
} catch (error) {
console.log(error);
} finally {

View File

@@ -78,7 +78,7 @@ const BoxNotification = ({
categoryApp: data.kategoriApp,
});
router.replace(newPath as any);
router.navigate(newPath as any);
selectedCategory(activeCategory as string);
if (!data.isRead) {

View File

@@ -1,13 +1,37 @@
import {
IconContribution,
IconHistory,
IconHome,
IconStatus,
IconContribution,
IconHistory,
IconHome,
IconStatus,
} from "@/components/_Icon";
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
import { TabsStyles } from "@/styles/tabs-styles";
import { Tabs } from "expo-router";
import { Tabs, useLocalSearchParams, useNavigation, router } from "expo-router";
import { useLayoutEffect } from "react";
export default function VotingTabsLayout() {
const navigation = useNavigation();
const { from, category } = useLocalSearchParams<{
from?: string;
category?: string;
}>();
console.log("from", from);
console.log("category", category);
// Atur header secara dinamis
useLayoutEffect(() => {
navigation.setOptions({
headerLeft: () => (
<BackButtonFromNotification
from={from as string}
category={category as string}
/>
),
});
}, [from, router, navigation]);
return (
<Tabs screenOptions={TabsStyles}>
<Tabs.Screen

View File

@@ -12,15 +12,17 @@ import { useAuth } from "@/hooks/use-auth";
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
import { apiVotingGetByStatus } from "@/service/api-client/api-voting";
import { dateTimeView } from "@/utils/dateTimeView";
import { useFocusEffect } from "expo-router";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
export default function VotingStatus() {
const { user } = useAuth();
const { status } = useLocalSearchParams<{ status?: string }>();
const id = user?.id || "";
const [activeCategory, setActiveCategory] = useState<string | null>(
"publish"
status || "publish"
);
const [listData, setListData] = useState([]);
@@ -86,8 +88,14 @@ export default function VotingStatus() {
style={{ width: "70%", alignSelf: "center" }}
variant="light"
>
{item?.awalVote && dateTimeView({date: item?.awalVote, withoutTime: true})} -{" "}
{item?.akhirVote && dateTimeView({date: item?.akhirVote, withoutTime: true})}
{item?.awalVote &&
dateTimeView({
date: item?.awalVote,
withoutTime: true,
})}{" "}
-{" "}
{item?.akhirVote &&
dateTimeView({ date: item?.akhirVote, withoutTime: true })}
</BadgeCustom>
</StackCustom>
</BaseBox>

View File

@@ -13,6 +13,7 @@ import {
} from "@/components";
import { IconArchive, IconContribution } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import { useAuth } from "@/hooks/use-auth";
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
import { Voting_BoxDetailPublishSection } from "@/screens/Voting/BoxDetailPublishSection";
@@ -22,13 +23,14 @@ import {
apiVotingUpdateData,
} from "@/service/api-client/api-voting";
import { today } from "@/utils/dateTimeView";
import dayjs from "dayjs";
import {
router,
Stack,
useFocusEffect,
useLocalSearchParams,
} from "expo-router";
import React, { useCallback, useState } from "react";
import React, { useCallback, useEffect, useState } from "react";
import Toast from "react-native-toast-message";
export default function VotingDetail() {
@@ -119,6 +121,23 @@ export default function VotingDetail() {
setOpenDrawerPublish(false);
};
const now = new Date().toISOString();
const isEventFinished = id && data?.akhirVote && dayjs(data.akhirVote).isBefore(now);
useEffect(() => {
if (isEventFinished) {
router.replace(`/(application)/(user)/voting/${id}/history`);
}
}, [isEventFinished, id]);
if (isEventFinished) {
return (
<ViewWrapper>
<CustomSkeleton />
</ViewWrapper>
);
}
return (
<>
<Stack.Screen

View File

@@ -79,7 +79,7 @@ export default function VotingCreate() {
type: "success",
text1: "Data berhasil disimpan",
});
router.replace("/(application)/(user)/voting/(tabs)/status");
router.replace("/(application)/(user)/voting/(tabs)/status?status=review");
} else {
Toast.show({
type: "error",

View File

@@ -16,6 +16,7 @@ import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import ReportBox from "@/components/Box/ReportBox";
import { MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth";
import funUpdateStatusVoting from "@/screens/Admin/Voting/funUpdateStatus";
import { apiAdminVotingById } from "@/service/api-admin/api-admin-voting";
import { colorBadgeStatus } from "@/utils/colorBadge";
@@ -29,6 +30,7 @@ import { List } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminVotingDetail() {
const { user } = useAuth();
const { id, status } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [isLoading, setIsLoading] = useState(false);
@@ -139,6 +141,10 @@ export default function AdminVotingDetail() {
const response = await funUpdateStatusVoting({
id: id as string,
changeStatus,
data: {
senderId: user?.id as string,
catatan: "",
},
});
if (!response.success) {

View File

@@ -7,6 +7,7 @@ import {
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import { useAuth } from "@/hooks/use-auth";
import funUpdateStatusVoting from "@/screens/Admin/Voting/funUpdateStatus";
import { apiAdminVotingById } from "@/service/api-admin/api-admin-voting";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
@@ -14,6 +15,7 @@ import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminVotingRejectInput() {
const { user } = useAuth()
const { id, status } = useLocalSearchParams();
const [data, setData] = useState("");
const [isLoading, setIsLoading] = useState(false);
@@ -48,7 +50,10 @@ export default function AdminVotingRejectInput() {
const response = await funUpdateStatusVoting({
id: id as string,
changeStatus,
data: data,
data: {
catatan: data,
senderId: user?.id as string,
},
});
if (!response.success) {

View File

@@ -1,5 +1,5 @@
import { apiAdminEventUpdateStatus } from "@/service/api-admin/api-admin-event";
import { RejectedData } from "@/types/type-collect-other";
import { typeRejectedData } from "@/types/type-collect-other";
export const funUpdateStatusEvent = async ({
id,
@@ -8,7 +8,7 @@ export const funUpdateStatusEvent = async ({
}: {
id: string;
changeStatus: "publish" | "review" | "reject";
data?: RejectedData;
data?: typeRejectedData;
}) => {
try {
console.log("[DATA]", data);

View File

@@ -1,4 +1,5 @@
import { apiAdminVotingUpdateStatus } from "@/service/api-admin/api-admin-voting";
import { typeRejectedData } from "@/types/type-collect-other";
const funUpdateStatusVoting = async ({
id,
@@ -7,7 +8,7 @@ const funUpdateStatusVoting = async ({
}: {
id: string;
changeStatus: "publish" | "review" | "reject";
data?: string;
data?: typeRejectedData;
}) => {
try {
const response = await apiAdminVotingUpdateStatus({

View File

@@ -1,3 +1,4 @@
import { typeRejectedData } from "@/types/type-collect-other";
import { apiConfig } from "../api-config";
export async function apiAdminVoting({
@@ -32,7 +33,7 @@ export async function apiAdminVotingUpdateStatus({
status,
}: {
id: string;
data?: string;
data?: typeRejectedData;
status: "publish" | "review" | "reject";
}) {
try {

View File

@@ -1,4 +1,4 @@
export type RejectedData = {
export type typeRejectedData = {
catatan?: string;
senderId: string;
};