From 7c85e35c617a3dc900393449c895485cae04d788 Mon Sep 17 00:00:00 2001 From: bagasbanuna Date: Tue, 6 Jan 2026 12:27:30 +0800 Subject: [PATCH] Note: - Fitur notifikasi ke admin dari user baru - Notifikasi ke user bahwa akunnya telah terverifikasi Fix: - app/(application)/(user)/notifications/index.tsx - app/(application)/(user)/waiting-room.tsx - app/(application)/admin/super-admin/[id]/index.tsx - app/(application)/admin/user-access/[id]/index.tsx - context/AuthContext.tsx - screens/Home/tabsList.ts - service/api-admin/api-admin-user-access.ts - service/api-device-token.ts - service/api-notifications.ts - types/type-notification-category.ts Add: - lib/routeApp.ts ### No Issue --- .../(user)/notifications/index.tsx | 15 +++-- app/(application)/(user)/waiting-room.tsx | 4 +- .../admin/super-admin/[id]/index.tsx | 1 + .../admin/user-access/[id]/index.tsx | 23 ++++++++ context/AuthContext.tsx | 55 ++----------------- lib/routeApp.ts | 9 +++ screens/Home/tabsList.ts | 13 ++++- service/api-admin/api-admin-user-access.ts | 4 +- service/api-device-token.ts | 4 +- service/api-notifications.ts | 49 +++++++++++------ types/type-notification-category.ts | 18 +++++- 11 files changed, 112 insertions(+), 83 deletions(-) create mode 100644 lib/routeApp.ts diff --git a/app/(application)/(user)/notifications/index.tsx b/app/(application)/(user)/notifications/index.tsx index 8c27f5f..313f952 100644 --- a/app/(application)/(user)/notifications/index.tsx +++ b/app/(application)/(user)/notifications/index.tsx @@ -3,9 +3,10 @@ import { NewWrapper, ScrollableCustom, StackCustom, - TextCustom + TextCustom, } from "@/components"; import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent"; +import NoDataText from "@/components/_ShareComponent/NoDataText"; import { AccentColor } from "@/constants/color-palet"; import { useAuth } from "@/hooks/use-auth"; import { useNotificationStore } from "@/hooks/use-notification-store"; @@ -13,11 +14,14 @@ import { apiGetNotificationsById } from "@/service/api-notifications"; import { listOfcategoriesAppNotification } from "@/types/type-notification-category"; import { formatChatTime } from "@/utils/formatChatTime"; import { router, useFocusEffect } from "expo-router"; +import _ from "lodash"; import { useCallback, useState } from "react"; import { RefreshControl, View } from "react-native"; const selectedCategory = (value: string) => { - const category = listOfcategoriesAppNotification.find((c) => c.value === value); + const category = listOfcategoriesAppNotification.find( + (c) => c.value === value + ); return category?.label; }; @@ -83,7 +87,8 @@ export default function Notifications() { id: user?.id as any, category: activeCategory as any, }); - // console.log("Response Notification", JSON.stringify(response, null, 2)); + + console.log("Response Notification", JSON.stringify(response, null, 2)); if (response.success) { setListData(response.data); } else { @@ -120,7 +125,9 @@ export default function Notifications() { } > {loading ? ( - + + ) : _.isEmpty(listData) ? ( + ) : ( listData.map((e, i) => ( diff --git a/app/(application)/(user)/waiting-room.tsx b/app/(application)/(user)/waiting-room.tsx index 000020f..a2a9c85 100644 --- a/app/(application)/(user)/waiting-room.tsx +++ b/app/(application)/(user)/waiting-room.tsx @@ -1,12 +1,10 @@ import { AlertDefaultSystem, BoxButtonOnFooter, - ButtonCenteredOnly, ButtonCustom, InformationBox, NewWrapper, - StackCustom, - ViewWrapper, + StackCustom } from "@/components"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; import { useAuth } from "@/hooks/use-auth"; diff --git a/app/(application)/admin/super-admin/[id]/index.tsx b/app/(application)/admin/super-admin/[id]/index.tsx index 4e791c3..9e40471 100644 --- a/app/(application)/admin/super-admin/[id]/index.tsx +++ b/app/(application)/admin/super-admin/[id]/index.tsx @@ -48,6 +48,7 @@ export default function SuperAdminDetail() { const response = await apiAdminUserAccessUpdateStatus({ id: id as string, role: data?.masterUserRoleId === "2" ? "user" : "admin", + category: "role" }); if (!response.success) { diff --git a/app/(application)/admin/user-access/[id]/index.tsx b/app/(application)/admin/user-access/[id]/index.tsx index 3905612..17c8468 100644 --- a/app/(application)/admin/user-access/[id]/index.tsx +++ b/app/(application)/admin/user-access/[id]/index.tsx @@ -9,15 +9,21 @@ import { } from "@/components"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import GridTwoView from "@/components/_ShareComponent/GridTwoView"; +import { useAuth } from "@/hooks/use-auth"; +import { routeUser } from "@/lib/routeApp"; import { apiAdminUserAccessGetById, apiAdminUserAccessUpdateStatus, } from "@/service/api-admin/api-admin-user-access"; +import { + apiNotificationsSendById +} from "@/service/api-notifications"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import { useCallback, useState } from "react"; import Toast from "react-native-toast-message"; export default function AdminUserAccessDetail() { + const { user } = useAuth(); const { id } = useLocalSearchParams(); const [data, setData] = useState(null); const [loadData, setLoadData] = useState(false); @@ -33,6 +39,7 @@ export default function AdminUserAccessDetail() { try { setLoadData(true); const response = await apiAdminUserAccessGetById({ id: id as string }); + console.log("[DATA]", JSON.stringify(response.data, null, 2)); setData(response.data); } catch (error) { @@ -48,6 +55,7 @@ export default function AdminUserAccessDetail() { const response = await apiAdminUserAccessUpdateStatus({ id: id as string, active: !data?.active, + category: "access", }); if (!response.success) { @@ -61,6 +69,21 @@ export default function AdminUserAccessDetail() { type: "success", text1: "Update aktifasi berhasil ", }); + + if (data.active === false) { + await apiNotificationsSendById({ + data: { + title: "Akun anda telah diaktifkan", + body: "Selamat menjelajahi HIConnect", + userLoginId: user?.id || "", + kategoriApp: "OTHER", + type: "announcement", + deepLink: routeUser.home, + }, + id: id as string, + }); + } + router.back(); } catch (error) { console.log("[ERROR UPDATE STATUS]", error); diff --git a/context/AuthContext.tsx b/context/AuthContext.tsx index 6f868e2..5d87fdd 100644 --- a/context/AuthContext.tsx +++ b/context/AuthContext.tsx @@ -7,10 +7,10 @@ import { import { apiDeviceTokenDeleted } from "@/service/api-device-token"; import { IUser } from "@/types/User"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import * as Device from "expo-device"; import { router } from "expo-router"; import { createContext, useEffect, useState } from "react"; import Toast from "react-native-toast-message"; -import * as Device from "expo-device"; // --- Types --- type AuthContextType = { @@ -105,18 +105,6 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { } }; - // const loginWithNomor = async (nomor: string) => { - // setIsLoading(true); - // try { - // const response = await apiLogin({ nomor: nomor }); - // await AsyncStorage.setItem("kode_otp", response.kodeId); - // } catch (error: any) { - // throw new Error(error.response?.data?.message || "Gagal kirim OTP"); - // } finally { - // setIsLoading(false); - // } - // }; - // --- 2. Validasi OTP & cek user --- const validateOtp = async (nomor: string) => { try { @@ -209,7 +197,6 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { setIsLoading(true); try { const response = await apiRegister({ data: userData }); - console.log("[REGISTER FETCH]", JSON.stringify(response, null, 2)); if (!response.success) { Toast.show({ @@ -239,42 +226,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { setIsLoading(false); } }; - // const registerUser = async (userData: { - // username: string; - // nomor: string; - // termsOfServiceAccepted: boolean; - // }) => { - // setIsLoading(true); - // try { - // const response = await apiRegister({ data: userData }); - // console.log("response", response); - - // const { token } = response; - // if (!response.success) { - // Toast.show({ - // type: "info", - // text1: "Info", - // text2: response.message, - // }); - - // return; - // } - - // setToken(token); - // await AsyncStorage.setItem("authToken", token); - // Toast.show({ - // type: "success", - // text1: "Sukses", - // text2: "Anda berhasil terdaftar", - // }); - // router.replace("/(application)/(user)/waiting-room"); - // return; - // } catch (error: any) { - // console.log("Error register", error); - // } finally { - // setIsLoading(false); - // } - // }; + // --- 5. Logout --- @@ -284,7 +236,8 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => { setToken(null); setUser(null); - const deviceId = Device.osInternalBuildId || Device.modelName || "unknown"; + const deviceId = + Device.osInternalBuildId || Device.modelName || "unknown"; await AsyncStorage.removeItem("authToken"); await AsyncStorage.removeItem("userData"); diff --git a/lib/routeApp.ts b/lib/routeApp.ts new file mode 100644 index 0000000..8ffa785 --- /dev/null +++ b/lib/routeApp.ts @@ -0,0 +1,9 @@ +export { routeAdmin, routeUser }; + +const routeAdmin = { + userAccess: ({ id }: { id: string }) => `/admin/user-access/${id}`, +}; + +const routeUser = { + home: `/(user)/home`, +}; diff --git a/screens/Home/tabsList.ts b/screens/Home/tabsList.ts index 2c01424..a29f31c 100644 --- a/screens/Home/tabsList.ts +++ b/screens/Home/tabsList.ts @@ -1,6 +1,13 @@ import { ITabs } from "@/components/_Interface/types"; +import { Platform } from "react-native"; -export const tabsHome: any = ({acceptedForumTermsAt, profileId}: {acceptedForumTermsAt: Date, profileId: string}) => [ +export const tabsHome: any = ({ + acceptedForumTermsAt, + profileId, +}: { + acceptedForumTermsAt: Date; + profileId: string; +}) => [ { id: "forum", icon: "chatbubble-ellipses-outline", @@ -25,8 +32,8 @@ export const tabsHome: any = ({acceptedForumTermsAt, profileId}: {acceptedForumT activeIcon: "map", label: "Maps", path: "/maps", - isActive: true, - disabled: false, + isActive: Platform.OS === "ios" ? true : false, + disabled: Platform.OS === "ios" ? false : true, }, { id: "profile", diff --git a/service/api-admin/api-admin-user-access.ts b/service/api-admin/api-admin-user-access.ts index eacf6f2..c47c69b 100644 --- a/service/api-admin/api-admin-user-access.ts +++ b/service/api-admin/api-admin-user-access.ts @@ -28,13 +28,15 @@ export const apiAdminUserAccessUpdateStatus = async ({ id, active, role, + category, }: { id: string; active?: boolean; role?: "user" | "admin" | "super_admin"; + category: "access" | "role"; }) => { try { - const response = await apiConfig.put(`/mobile/admin/user/${id}`, { + const response = await apiConfig.put(`/mobile/admin/user/${id}?category=${category}`, { data: { active, role, diff --git a/service/api-device-token.ts b/service/api-device-token.ts index 7ac50d5..ff42c82 100644 --- a/service/api-device-token.ts +++ b/service/api-device-token.ts @@ -31,7 +31,7 @@ export async function apiDeviceTokenDeleted({ userId, deviceId }: { userId: stri const response = await apiConfig.delete( `/mobile/auth/device-tokens/${userId}?deviceId=${deviceId}` ); - console.log("Device token deleted:", response.data); + return response.data; } catch (error) { console.error("Failed to delete device token:", error); @@ -42,7 +42,7 @@ export async function apiDeviceTokenDeleted({ userId, deviceId }: { userId: stri export async function apiGetAllTokenDevice() { try { const response = await apiConfig.get(`/mobile/auth/device-tokens`); - console.log("Device token deleted:", response.data); + return response.data; } catch (error) { console.error("Failed to delete device token:", error); diff --git a/service/api-notifications.ts b/service/api-notifications.ts index 77810c9..04427db 100644 --- a/service/api-notifications.ts +++ b/service/api-notifications.ts @@ -1,17 +1,9 @@ -import { TypeNotificationCategoryApp, TypeOfTilteCategoryApp } from "@/types/type-notification-category"; +import { + NotificationProp, + TypeNotificationCategoryApp +} from "@/types/type-notification-category"; import { apiConfig } from "./api-config"; -type NotificationProp = { - title: TypeOfTilteCategoryApp; - body: string; - userLoginId: string; - appId?: string; - status?: string; - type?: "announcement" | "trigger"; - deepLink?: string; - kategoriApp?: TypeNotificationCategoryApp -}; - export async function apiNotificationsSend({ data, }: { @@ -28,12 +20,30 @@ export async function apiNotificationsSend({ } } +export async function apiNotificationsSendById({ + data, + id, +}: { + data: NotificationProp; + id: string; +}) { + try { + const response = await apiConfig.post(`/mobile/notification/${id}`, { + data: data, + }); + + return response.data; + } catch (error) { + throw error; + } +} + export async function apiGetNotificationsById({ id, category, }: { id: string; - category: TypeNotificationCategoryApp + category: TypeNotificationCategoryApp; }) { console.log("ID", id); console.log("Category", category); @@ -49,7 +59,13 @@ export async function apiGetNotificationsById({ } } -export async function apiNotificationUnreadCount({ id, role }: { id: string, role: "user" | "admin" }) { +export async function apiNotificationUnreadCount({ + id, + role, +}: { + id: string; + role: "user" | "admin"; +}) { try { const response = await apiConfig.get( `/mobile/notification/${id}/unread-count?role=${role}` @@ -62,12 +78,11 @@ export async function apiNotificationUnreadCount({ id, role }: { id: string, rol } } - -export async function apiNotificationMarkAsRead({id}: {id: string}) { +export async function apiNotificationMarkAsRead({ id }: { id: string }) { try { const response = await apiConfig.put(`/mobile/notification/${id}`); return response.data; } catch (error) { throw error; } -} \ No newline at end of file +} diff --git a/types/type-notification-category.ts b/types/type-notification-category.ts index e8f39a1..ae935d9 100644 --- a/types/type-notification-category.ts +++ b/types/type-notification-category.ts @@ -1,3 +1,16 @@ +export type NotificationProp = { + title: TypeOfTilteCategoryApp | string + body: string; + userLoginId?: string; + appId?: string; + status?: string; + type?: "announcement" | "trigger"; + deepLink?: string; + kategoriApp?: TypeNotificationCategoryApp +}; + + + export type TypeNotificationCategoryApp = | "EVENT" | "JOB" @@ -6,9 +19,9 @@ export type TypeNotificationCategoryApp = | "INVESTASI" | "COLLABORATION" | "FORUM" - | "ACCESS"; + | "OTHER"; -export type TypeOfTilteCategoryApp = "Pendaftaran User Baru" | "Other" | string; +export type TypeOfTilteCategoryApp = "Pendaftaran User Baru" | "Other" export const listOfcategoriesAppNotification = [ { value: "event", label: "Event" }, @@ -18,4 +31,5 @@ export const listOfcategoriesAppNotification = [ { value: "investasi", label: "Investasi" }, { value: "forum", label: "Forum" }, { value: "collaboration", label: "Collaboration" }, + { value: "other", label: "Lainnya" }, ];