diff --git a/android/app/build.gradle b/android/app/build.gradle
index 0874adf..b761b19 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -100,7 +100,7 @@ packagingOptions {
applicationId 'com.bip.hipmimobileapp'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 3
+ versionCode 4
versionName "1.0.1"
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
diff --git a/app/(application)/(user)/_layout.tsx b/app/(application)/(user)/_layout.tsx
index 1aca1e3..7d75dd1 100644
--- a/app/(application)/(user)/_layout.tsx
+++ b/app/(application)/(user)/_layout.tsx
@@ -53,7 +53,9 @@ export default function UserLayout() {
/>
{/* ========== Notification Section ========= */}
-
// ),
}}
- />
+ /> */}
{/* ========== Event Section ========= */}
+
(
-
- ),
+ // NOTE: DIPINDAH DI FILE /Event/(Tabs)/_layout.tsx
+ // headerLeft: () => (
+ //
+ // ),
}}
/>
+
,
- // Note: headerLeft di pindahkan ke Tabs Layout
+ // NOTE: headerLeft di pindahkan ke Tabs Layout
}}
/>
();
+
+ console.log("from", from);
+ console.log("category", category);
+
+ // Atur header secara dinamis
+ useLayoutEffect(() => {
+ navigation.setOptions({
+ headerLeft: () => (
+
+ ),
+ });
+ }, [from, router, navigation]);
+
return (
();
+
const id = user?.id || "";
const [activeCategory, setActiveCategory] = useState(
- "publish"
+ status || "publish"
);
const [listData, setListData] = useState([]);
const [loadingGetData, setLoadingGetData] = useState(false);
@@ -73,7 +75,7 @@ export default function EventStatus() {
listData.map((item: any, i) => (
diff --git a/app/(application)/(user)/event/create.tsx b/app/(application)/(user)/event/create.tsx
index dffebc9..3c98340 100644
--- a/app/(application)/(user)/event/create.tsx
+++ b/app/(application)/(user)/event/create.tsx
@@ -14,7 +14,7 @@ import { apiEventCreate } from "@/service/api-client/api-event";
import { apiMasterEventType } from "@/service/api-client/api-master";
import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
import { router } from "expo-router";
-import React, { useEffect, useState } from "react";
+import { useEffect, useState } from "react";
import Toast from "react-native-toast-message";
interface EventCreateProps {
diff --git a/app/(application)/(user)/job/(tabs)/_layout.tsx b/app/(application)/(user)/job/(tabs)/_layout.tsx
index 6a63193..fedfb7c 100644
--- a/app/(application)/(user)/job/(tabs)/_layout.tsx
+++ b/app/(application)/(user)/job/(tabs)/_layout.tsx
@@ -1,6 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { BackButton } from "@/components";
import { IconHome, IconStatus } from "@/components/_Icon";
+import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
import { TabsStyles } from "@/styles/tabs-styles";
import { Ionicons } from "@expo/vector-icons";
import {
@@ -23,19 +24,7 @@ export default function JobTabsLayout() {
useLayoutEffect(() => {
navigation.setOptions({
headerLeft: () => (
- {
- if (from === "notifications") {
- router.replace(`/notifications?category=${category}`);
- } else {
- if (from) {
- router.replace(`/${from}` as any);
- } else {
- router.navigate("/home");
- }
- }
- }}
- />
+
),
});
}, [from, router, navigation]);
diff --git a/app/(application)/(user)/notifications/index.tsx b/app/(application)/(user)/notifications/index.tsx
index 15be826..37ff3ac 100644
--- a/app/(application)/(user)/notifications/index.tsx
+++ b/app/(application)/(user)/notifications/index.tsx
@@ -1,20 +1,27 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
+ AlertDefaultSystem,
+ BackButton,
BaseBox,
+ DrawerCustom,
+ MenuDrawerDynamicGrid,
NewWrapper,
ScrollableCustom,
StackCustom,
TextCustom,
} from "@/components";
+import { IconDot } from "@/components/_Icon/IconComponent";
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
import NoDataText from "@/components/_ShareComponent/NoDataText";
-import { AccentColor } from "@/constants/color-palet";
+import { AccentColor, MainColor } from "@/constants/color-palet";
+import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth";
import { useNotificationStore } from "@/hooks/use-notification-store";
import { apiGetNotificationsById } from "@/service/api-notifications";
import { listOfcategoriesAppNotification } from "@/types/type-notification-category";
import { formatChatTime } from "@/utils/formatChatTime";
-import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
+import { Ionicons } from "@expo/vector-icons";
+import { router, Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { RefreshControl, View } from "react-native";
@@ -39,8 +46,9 @@ const fixPath = ({
const separator = deepLink.includes("?") ? "&" : "?";
- const fixedPath =
- `${deepLink}${separator}from=notifications&category=${_.lowerCase(categoryApp)}`;
+ const fixedPath = `${deepLink}${separator}from=notifications&category=${_.lowerCase(
+ categoryApp
+ )}`;
console.log("Fix Path", fixedPath);
@@ -103,6 +111,9 @@ export default function Notifications() {
const [listData, setListData] = useState([]);
const [refreshing, setRefreshing] = useState(false);
const [loading, setLoading] = useState(false);
+ const [openDrawer, setOpenDrawer] = useState(false);
+
+ const { markAsReadAll } = useNotificationStore();
const handlePress = (item: any) => {
setActiveCategory(item.value);
@@ -142,33 +153,96 @@ export default function Notifications() {
};
return (
- ({
- id: i,
- label: e.label,
- value: e.value,
- }))}
- onButtonPress={handlePress}
- activeId={activeCategory as string}
+ <>
+ ,
+ headerRight: () => (
+ setOpenDrawer(true)}
+ />
+ ),
+ }}
+ />
+
+ ({
+ id: i,
+ label: e.label,
+ value: e.value,
+ }))}
+ onButtonPress={handlePress}
+ activeId={activeCategory as string}
+ />
+ }
+ refreshControl={
+
+ }
+ >
+ {loading ? (
+
+ ) : _.isEmpty(listData) ? (
+
+ ) : (
+ listData.map((e, i) => (
+
+
+
+ ))
+ )}
+
+
+ setOpenDrawer(false)}
+ height={"auto"}
+ >
+
+ ),
+ path: "",
+ },
+ ]}
+ onPressItem={(item: any) => {
+ console.log("Item", item.value);
+ if (item.value === "read-all") {
+ AlertDefaultSystem({
+ title: "Tandai Semua Dibaca",
+ message:
+ "Apakah Anda yakin ingin menandai semua notifikasi dibaca?",
+ textLeft: "Batal",
+ textRight: "Ya",
+ onPressRight: () => {
+ markAsReadAll(user?.id as any);
+ const data = _.cloneDeep(listData);
+ data.forEach((e) => {
+ e.isRead = true;
+ });
+ setListData(data);
+ onRefresh();
+ setOpenDrawer(false);
+ },
+ });
+ }
+ }}
/>
- }
- refreshControl={
-
- }
- >
- {loading ? (
-
- ) : _.isEmpty(listData) ? (
-
- ) : (
- listData.map((e, i) => (
-
-
-
- ))
- )}
-
+
+ >
);
}
diff --git a/app/(application)/admin/event/[id]/[status]/index.tsx b/app/(application)/admin/event/[id]/[status]/index.tsx
index a0d0c49..78608fc 100644
--- a/app/(application)/admin/event/[id]/[status]/index.tsx
+++ b/app/(application)/admin/event/[id]/[status]/index.tsx
@@ -126,6 +126,7 @@ export default function AdminEventDetail() {
const response = await funUpdateStatusEvent({
id: id as string,
changeStatus: "publish",
+ data: {catatan: "", senderId: user?.id as string}
});
if (!response.success) {
diff --git a/app/(application)/admin/event/[id]/reject-input.tsx b/app/(application)/admin/event/[id]/reject-input.tsx
index 5e06668..ca61964 100644
--- a/app/(application)/admin/event/[id]/reject-input.tsx
+++ b/app/(application)/admin/event/[id]/reject-input.tsx
@@ -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 { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
@@ -14,9 +15,13 @@ import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminEventRejectInput() {
+ const { user } = useAuth();
const { id, status } = useLocalSearchParams();
- const [data, setData] = useState("");
+ const [data, setData] = useState({
+ catatan: "",
+ senderId: "",
+ });
const [isLoading, setIsLoading] = useState(false);
useFocusEffect(
@@ -45,10 +50,16 @@ export default function AdminEventRejectInput() {
}) => {
try {
setIsLoading(true);
+
+ const newData = {
+ catatan: data,
+ senderId: user?.id as string,
+ };
+
const response = await funUpdateStatusEvent({
id: id as string,
changeStatus,
- data: data,
+ data: newData,
});
if (!response.success) {
diff --git a/app/(application)/admin/notification/index.tsx b/app/(application)/admin/notification/index.tsx
index 6b36a62..9875bd5 100644
--- a/app/(application)/admin/notification/index.tsx
+++ b/app/(application)/admin/notification/index.tsx
@@ -1,20 +1,26 @@
import {
+ AlertDefaultSystem,
BackButton,
BaseBox,
+ DrawerCustom,
+ MenuDrawerDynamicGrid,
NewWrapper,
ScrollableCustom,
StackCustom,
TextCustom,
} from "@/components";
import { IconPlus } from "@/components/_Icon";
+import { IconDot } from "@/components/_Icon/IconComponent";
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
import NoDataText from "@/components/_ShareComponent/NoDataText";
import { AccentColor, MainColor } from "@/constants/color-palet";
+import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth";
import { useNotificationStore } from "@/hooks/use-notification-store";
import { apiGetNotificationsById } from "@/service/api-notifications";
import { listOfcategoriesAppNotification } from "@/types/type-notification-category";
import { formatChatTime } from "@/utils/formatChatTime";
+import { Ionicons } from "@expo/vector-icons";
import { router, Stack, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
@@ -70,6 +76,9 @@ export default function AdminNotification() {
const [listData, setListData] = useState([]);
const [refreshing, setRefreshing] = useState(false);
const [loading, setLoading] = useState(false);
+ const [openDrawer, setOpenDrawer] = useState(false);
+
+ const { markAsReadAll } = useNotificationStore();
const handlePress = (item: any) => {
setActiveCategory(item.value);
@@ -89,7 +98,7 @@ export default function AdminNotification() {
id: user?.id as any,
category: activeCategory as any,
});
-
+
if (response.success) {
setListData(response.data);
} else {
@@ -114,12 +123,12 @@ export default function AdminNotification() {
options={{
title: "Admin Notifikasi",
headerLeft: () => ,
- // headerRight: () => (
- // router.push("/test-notifications")}
- // />
- // ),
+ headerRight: () => (
+ setOpenDrawer(true)}
+ />
+ ),
}}
/>
@@ -154,6 +163,51 @@ export default function AdminNotification() {
))
)}
+
+ setOpenDrawer(false)}
+ height={"auto"}
+ >
+
+ ),
+ path: "",
+ },
+ ]}
+ onPressItem={(item: any) => {
+ console.log("Item", item.value);
+ if (item.value === "read-all") {
+ AlertDefaultSystem({
+ title: "Tandai Semua Dibaca",
+ message:
+ "Apakah Anda yakin ingin menandai semua notifikasi dibaca?",
+ textLeft: "Batal",
+ textRight: "Ya",
+ onPressRight: () => {
+ markAsReadAll(user?.id as any);
+ const data = _.cloneDeep(listData);
+ data.forEach((e) => {
+ e.isRead = true;
+ });
+ setListData(data);
+ onRefresh();
+ setOpenDrawer(false);
+ },
+ });
+ }
+ }}
+ />
+
>
);
}
diff --git a/components/Button/BackButtonFromNotification.tsx b/components/Button/BackButtonFromNotification.tsx
new file mode 100644
index 0000000..93d942d
--- /dev/null
+++ b/components/Button/BackButtonFromNotification.tsx
@@ -0,0 +1,29 @@
+import { useRouter } from "expo-router";
+import { BackButton } from "..";
+
+export default function BackButtonFromNotification({
+ from,
+ category,
+}: {
+ from: string;
+ category?: string;
+}) {
+ const router = useRouter();
+ return (
+ <>
+ {
+ if (from === "notifications") {
+ router.replace(`/notifications?category=${category}`);
+ } else {
+ if (from) {
+ router.replace(`/${from}` as any);
+ } else {
+ router.navigate("/home");
+ }
+ }
+ }}
+ />
+ >
+ );
+}
diff --git a/components/Notification/NotificationInitializer.tsx b/components/Notification/NotificationInitializer.tsx
index 2a6e7f8..f69e307 100644
--- a/components/Notification/NotificationInitializer.tsx
+++ b/components/Notification/NotificationInitializer.tsx
@@ -49,7 +49,8 @@ export default function NotificationInitializer() {
const fcmToken = await getToken(messagingInstance);
if (!fcmToken) {
- logout();
+ console.warn("Tidak bisa mendapatkan FCM token");
+ // logout();
return;
}
diff --git a/hipmi-note.md b/hipmi-note.md
index 2f0281d..73d454e 100644
--- a/hipmi-note.md
+++ b/hipmi-note.md
@@ -13,3 +13,13 @@ Exp: open ios/HIPMIBADUNG.xcworkspace
perubahan versi : npm version patch
ios: bunx expo prebuild --platform ios
android: bunx expo prebuild --platform android
+
+### Android
+adb devices : cek device yang terhubung
+Note: izinkan perangkat dulu agar statusnya tidak unauthorized
+
+adb install android/app/build/outputs/apk/debug/app-debug.apk : install apk ke device
+Note:
+Gunakan flag -s (serial) di perintah adb untuk menentukan target
+adb -s <0G52319V261040B2 ini adalah id nya> install android/app/build/outputs/apk/debug/app-debug.apk
+
diff --git a/hooks/use-notification-store.tsx b/hooks/use-notification-store.tsx
index 0c557ad..f361612 100644
--- a/hooks/use-notification-store.tsx
+++ b/hooks/use-notification-store.tsx
@@ -40,6 +40,7 @@ type NotificationContextType = {
notif: Omit
) => void;
markAsRead: (id: string) => void;
+ markAsReadAll: (id: string) => void;
syncUnreadCount: () => Promise;
};
@@ -48,6 +49,7 @@ const NotificationContext = createContext({
unreadCount: 0,
addNotification: () => {},
markAsRead: () => {},
+ markAsReadAll: () => {},
syncUnreadCount: async () => {},
});
@@ -98,7 +100,7 @@ export const NotificationProvider = ({ children }: { children: ReactNode }) => {
const markAsRead = async (id: string) => {
try {
- const response = await apiNotificationMarkAsRead({ id });
+ const response = await apiNotificationMarkAsRead({ id, category: "one" });
console.log("🚀 Response Mark As Read:", response);
if (response.success) {
@@ -114,6 +116,25 @@ export const NotificationProvider = ({ children }: { children: ReactNode }) => {
}
};
+ const markAsReadAll = async (id: string) => {
+ try {
+ const response = await apiNotificationMarkAsRead({ id, category: "all" });
+ console.log("🚀 Response Mark As Read All:", response);
+
+ if (response.success) {
+ const cloneNotifications = [...notifications];
+ const index = cloneNotifications.findIndex((n) => n?.data?.id === id);
+ if (index !== -1) {
+ cloneNotifications[index].isRead = true;
+ setNotifications(cloneNotifications);
+ }
+ }
+ } catch (error) {
+ console.error("Gagal mark as read:", error);
+ }
+ };
+
+
const syncUnreadCount = async () => {
try {
const count = await apiNotificationUnreadCount({
@@ -133,8 +154,9 @@ export const NotificationProvider = ({ children }: { children: ReactNode }) => {
value={{
notifications,
addNotification,
- markAsRead,
unreadCount,
+ markAsRead,
+ markAsReadAll,
syncUnreadCount,
}}
>
diff --git a/screens/Admin/Event/funUpdateStatus.ts b/screens/Admin/Event/funUpdateStatus.ts
index 7a8e9ac..5e535d5 100644
--- a/screens/Admin/Event/funUpdateStatus.ts
+++ b/screens/Admin/Event/funUpdateStatus.ts
@@ -1,4 +1,5 @@
import { apiAdminEventUpdateStatus } from "@/service/api-admin/api-admin-event";
+import { RejectedData } from "@/types/type-collect-other";
export const funUpdateStatusEvent = async ({
id,
@@ -7,17 +8,19 @@ export const funUpdateStatusEvent = async ({
}: {
id: string;
changeStatus: "publish" | "review" | "reject";
- data?: string;
+ data?: RejectedData;
}) => {
try {
+ console.log("[DATA]", data);
const response = await apiAdminEventUpdateStatus({
id: id,
changeStatus: changeStatus as any,
- data: data,
+ data: data as any,
});
return response;
+
} catch (error) {
console.log("[ERROR]", error);
throw error;
}
-};
\ No newline at end of file
+};
diff --git a/service/api-notifications.ts b/service/api-notifications.ts
index 04427db..a20dbeb 100644
--- a/service/api-notifications.ts
+++ b/service/api-notifications.ts
@@ -1,6 +1,6 @@
import {
NotificationProp,
- TypeNotificationCategoryApp
+ TypeNotificationCategoryApp,
} from "@/types/type-notification-category";
import { apiConfig } from "./api-config";
@@ -78,9 +78,22 @@ export async function apiNotificationUnreadCount({
}
}
-export async function apiNotificationMarkAsRead({ id }: { id: string }) {
+/**
+ * @param id | notification id atau user id
+ * @param category | "all" | "one" , jika "all" id yang harus di masukan adalah user id, jika "one" id yang harus di masukan adalah notification id
+ * @type {string}
+ */
+export async function apiNotificationMarkAsRead({
+ id,
+ category,
+}: {
+ id: string;
+ category: "all" | "one";
+}) {
try {
- const response = await apiConfig.put(`/mobile/notification/${id}`);
+ const response = await apiConfig.put(
+ `/mobile/notification/${id}?category=${category}`
+ );
return response.data;
} catch (error) {
throw error;
diff --git a/types/type-collect-other.ts b/types/type-collect-other.ts
new file mode 100644
index 0000000..36e3ffa
--- /dev/null
+++ b/types/type-collect-other.ts
@@ -0,0 +1,4 @@
+export type RejectedData = {
+ catatan?: string;
+ senderId: string;
+};
diff --git a/utils/formatChatTime.ts b/utils/formatChatTime.ts
index 2c86136..9df79ed 100644
--- a/utils/formatChatTime.ts
+++ b/utils/formatChatTime.ts
@@ -15,21 +15,21 @@ export const formatChatTime = (date: string | Date): string => {
const messageDate = dayjs(date);
const now = dayjs();
- // Jika hari ini
+ // Hari ini
if (messageDate.isSame(now, 'day')) {
- return messageDate.format('HH:mm'); // contoh: "14.30"
+ return messageDate.format('HH.mm'); // "14.30"
}
- // Jika kemarin
+ // Kemarin
if (messageDate.isSame(now.subtract(1, 'day'), 'day')) {
- return messageDate.format('dddd HH:mm');
+ return `Kemarin, ${messageDate.format('HH.mm')}`; // "Kemarin, 14.30"
}
- // Jika dalam 7 hari terakhir (tapi bukan kemarin/ hari ini)
+ // Dalam 7 hari terakhir (termasuk hari ini & kemarin sudah di-handle, jadi aman)
if (now.diff(messageDate, 'day') < 7) {
- return messageDate.format('dddd HH:mm'); // contoh: "Senin 14:30"
+ return `${messageDate.format('dddd')}, ${messageDate.format('HH.mm')}`; // "Senin, 13.00"
}
- // Lebih dari seminggu lalu → tampilkan tanggal
- return messageDate.format('D MMM YYYY HH:mm'); // contoh: "12 Mei 2024 14:30"
-};
+ // Lebih dari 7 hari lalu
+ return messageDate.format('DD - MM - YYYY, HH.mm'); // "05 - 11 - 2025, 14.00"
+};
\ No newline at end of file