- 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
281 lines
7.3 KiB
TypeScript
281 lines
7.3 KiB
TypeScript
import {
|
|
apiConfig,
|
|
apiLogin,
|
|
apiRegister,
|
|
apiValidationCode,
|
|
} from "@/service/api-config";
|
|
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";
|
|
|
|
// --- Types ---
|
|
type AuthContextType = {
|
|
user: IUser | null;
|
|
token: string | null;
|
|
isLoading: boolean;
|
|
isAuthenticated: boolean;
|
|
isAdmin: boolean;
|
|
isUserActive: boolean;
|
|
loginWithNomor: (nomor: string) => Promise<void>;
|
|
validateOtp: (nomor: string) => Promise<any>;
|
|
logout: () => Promise<void>;
|
|
registerUser: (userData: {
|
|
username: string;
|
|
nomor: string;
|
|
termsOfServiceAccepted: boolean;
|
|
}) => Promise<void>;
|
|
userData: (token: string) => Promise<any>;
|
|
};
|
|
|
|
// --- Create Context ---
|
|
export const AuthContext = createContext<AuthContextType | undefined>(
|
|
undefined
|
|
);
|
|
|
|
export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
|
const [user, setUser] = useState<IUser | null>(null);
|
|
const [token, setToken] = useState<string | null>(null);
|
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
|
|
const isAuthenticated = !!user;
|
|
const isAdmin = user?.masterUserRoleId !== "1";
|
|
const isUserActive = user?.active === true;
|
|
|
|
// --- Load session from AsyncStorage on app start ---
|
|
useEffect(() => {
|
|
const loadSession = async () => {
|
|
try {
|
|
const storedToken = await AsyncStorage.getItem("authToken");
|
|
const storedUser = await AsyncStorage.getItem("userData");
|
|
|
|
if (storedToken) {
|
|
setToken(storedToken);
|
|
}
|
|
|
|
if (storedToken && storedUser) {
|
|
setToken(storedToken);
|
|
setUser(JSON.parse(storedUser));
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to load session", error);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
loadSession();
|
|
}, []);
|
|
|
|
// --- 1. Kirim nomor → dapat OTP ---
|
|
const loginWithNomor = async (nomor: string) => {
|
|
setIsLoading(true);
|
|
try {
|
|
console.log("[Masuk provider]", nomor);
|
|
const response = await apiLogin({ nomor: nomor });
|
|
console.log("[RESPONSE AUTH]", JSON.stringify(response));
|
|
|
|
if (response.success) {
|
|
console.log("[Keluar provider]", nomor);
|
|
Toast.show({
|
|
type: "success",
|
|
text1: "Sukses",
|
|
text2: "Kode OTP berhasil dikirim",
|
|
});
|
|
|
|
await AsyncStorage.setItem("kode_otp", response.kodeId);
|
|
router.push(`/verification?nomor=${nomor}`);
|
|
return;
|
|
} else {
|
|
router.push(`/register?nomor=${nomor}`);
|
|
Toast.show({
|
|
type: "info",
|
|
text1: "Info",
|
|
text2: "Silahkan mendaftar",
|
|
});
|
|
return;
|
|
}
|
|
} 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 {
|
|
setIsLoading(true);
|
|
const response = await apiValidationCode({ nomor: nomor });
|
|
const { token } = response;
|
|
console.log("[RESPONSE VALIDASI OTP]", JSON.stringify(response, null, 2));
|
|
|
|
if (response.success) {
|
|
setToken(token);
|
|
await AsyncStorage.setItem("authToken", token);
|
|
|
|
const responseUser = await apiConfig.get(`/mobile?token=${token}`, {
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
});
|
|
const dataUser = responseUser.data.data;
|
|
|
|
setUser(dataUser);
|
|
await AsyncStorage.setItem("userData", JSON.stringify(dataUser));
|
|
|
|
if (response.active) {
|
|
// if (response.roleId === "1") {
|
|
// router.replace("/(application)/(user)/home");
|
|
// return;
|
|
// } else {
|
|
// router.replace("/(application)/admin/dashboard");
|
|
// return;
|
|
// }
|
|
router.replace("/(application)/(user)/home");
|
|
return;
|
|
} else {
|
|
router.replace("/(application)/(user)/waiting-room");
|
|
return;
|
|
}
|
|
} else {
|
|
Toast.show({
|
|
type: "info",
|
|
text1: "Terjadi kesalahan",
|
|
text2: "Silahkan coba lagi",
|
|
});
|
|
return;
|
|
}
|
|
} catch (error: any) {
|
|
console.log("Error validasi otp >>", (error as Error).message || error);
|
|
throw new Error(
|
|
error.response?.data?.message || "OTP salah atau user tidak ditemukan"
|
|
);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
// --- 3. Ambil data user ---
|
|
const userData = async (token: string) => {
|
|
try {
|
|
if (!token) {
|
|
throw new Error("Token tidak ditemukan");
|
|
}
|
|
|
|
setIsLoading(true);
|
|
const response = await apiConfig.get(`/mobile?token=${token}`, {
|
|
headers: {
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
});
|
|
|
|
const dataUser = response.data.data;
|
|
|
|
setUser(dataUser);
|
|
await AsyncStorage.setItem("userData", JSON.stringify(dataUser));
|
|
return dataUser;
|
|
} catch (error: any) {
|
|
console.log(
|
|
"[LOAD USER DATA]",
|
|
error.response?.data?.message + "user" || "Gagal mengambil data user"
|
|
);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
// --- 4. Register jika user belum ada ---
|
|
const registerUser = async (userData: {
|
|
username: string;
|
|
nomor: string;
|
|
termsOfServiceAccepted: boolean;
|
|
}) => {
|
|
setIsLoading(true);
|
|
try {
|
|
const response = await apiRegister({ data: userData });
|
|
|
|
if (!response.success) {
|
|
Toast.show({
|
|
type: "info",
|
|
text1: "Info",
|
|
text2: response.message,
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
Toast.show({
|
|
type: "success",
|
|
text1: "Sukses",
|
|
text2: "Anda berhasil terdaftar",
|
|
});
|
|
router.replace(`/verification?nomor=${userData.nomor}`);
|
|
return;
|
|
} catch (error: any) {
|
|
Toast.show({
|
|
type: "error",
|
|
text1: "Error",
|
|
text2: error.response?.data?.message || "Gagal mendaftar",
|
|
});
|
|
console.log("Error register", error);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
|
|
// --- 5. Logout ---
|
|
|
|
const logout = async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
setToken(null);
|
|
setUser(null);
|
|
|
|
const deviceId =
|
|
Device.osInternalBuildId || Device.modelName || "unknown";
|
|
|
|
await AsyncStorage.removeItem("authToken");
|
|
await AsyncStorage.removeItem("userData");
|
|
await apiDeviceTokenDeleted({ userId: user?.id as any, deviceId });
|
|
|
|
Toast.show({
|
|
type: "success",
|
|
text1: "Logout berhasil",
|
|
text2: "Anda berhasil keluar dari akun.",
|
|
});
|
|
router.replace("/");
|
|
} catch (error) {
|
|
console.log("Logout error (optional):", error);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<AuthContext.Provider
|
|
value={{
|
|
user,
|
|
token,
|
|
isLoading,
|
|
isAuthenticated,
|
|
isAdmin,
|
|
isUserActive,
|
|
loginWithNomor,
|
|
validateOtp,
|
|
logout,
|
|
registerUser,
|
|
userData,
|
|
}}
|
|
>
|
|
{children}
|
|
</AuthContext.Provider>
|
|
</>
|
|
);
|
|
};
|