From 59482ca712059508653f371009f5479489f6eec4 Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Mon, 25 Aug 2025 17:59:07 +0800 Subject: [PATCH] API Profile: Fix: - api create, get , edit Types Add: - Type-Profile ### No Issue --- app/(application)/(user)/home.tsx | 13 +- .../(user)/profile/[id]/edit.tsx | 96 ++++---- .../(user)/profile/[id]/index.tsx | 82 ++++--- app/(application)/(user)/profile/create.tsx | 18 +- app/(application)/admin/_layout.tsx | 2 +- components/_Interface/types.ts | 1 + screens/Authentication/VerificationView.tsx | 1 + screens/Home/tabsList.ts | 6 +- screens/Profile/AvatarAndBackground.tsx | 2 + screens/Profile/ListPage.tsx | 214 +++++++++++------- screens/Profile/ProfileSection.tsx | 28 ++- screens/Profile/menuDrawerSection.tsx | 2 - service/api-client/api-profile.ts | 20 +- service/api-client/api-user.ts | 3 +- service/api-config.ts | 1 + types/Type-Profile.ts | 16 ++ types/User.ts | 16 +- 17 files changed, 323 insertions(+), 198 deletions(-) create mode 100644 types/Type-Profile.ts diff --git a/app/(application)/(user)/home.tsx b/app/(application)/(user)/home.tsx index 668c804..c5ca91a 100644 --- a/app/(application)/(user)/home.tsx +++ b/app/(application)/(user)/home.tsx @@ -22,14 +22,17 @@ export default function Application() { async function onLoadData() { const response = await apiUser(user?.id as string); - console.log("data user >>", JSON.stringify(response.active, null, 2)); setData(response.data); } - if (data?.active === false) { + if (data && data?.active === false) { return ; } + if (data && data?.Profile === null) { + return ; + } + return ( <> - }> + + } + > diff --git a/app/(application)/(user)/profile/[id]/edit.tsx b/app/(application)/(user)/profile/[id]/edit.tsx index d0ae6c9..7cdf615 100644 --- a/app/(application)/(user)/profile/[id]/edit.tsx +++ b/app/(application)/(user)/profile/[id]/edit.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ import { ButtonCustom, SelectCustom, @@ -7,46 +6,69 @@ import { ViewWrapper, } from "@/components"; import BoxButtonOnFooter from "@/components/Box/BoxButtonOnFooter"; +import { apiProfile, apiUpdateProfile } from "@/service/api-client/api-profile"; +import { IProfile } from "@/types/Type-Profile"; import { router, useLocalSearchParams } from "expo-router"; -import { useState } from "react"; -import { StyleSheet } from "react-native"; +import { useEffect, useState } from "react"; +import Toast from "react-native-toast-message"; export default function ProfileEdit() { const { id } = useLocalSearchParams(); - const [data, setData] = useState({ - nama: "Bagas Banuna", - email: "bagasbanuna@gmail.com", - alamat: "Jember", - selectedValue: "", - }); + const [data, setData] = useState(); + const [isLoading, setIsLoading] = useState(false); const options = [ { label: "Laki-laki", value: "laki-laki" }, { label: "Perempuan", value: "perempuan" }, ]; - const handleSave = () => { - console.log({ - nama: data.nama, - email: data.email, - alamat: data.alamat, - selectedValue: data.selectedValue, - }); - router.back(); + useEffect(() => { + onLoadData(id as string); + }, [id]); + + async function onLoadData(id: string) { + try { + const response = await apiProfile({ id }); + setData(response.data); + } catch (error) { + console.log("error get profile >>", error); + } + } + + const handleUpdate = async () => { + try { + setIsLoading(true); + const response = await apiUpdateProfile({ id: id as string, data }); + if (!response.success) { + Toast.show({ + type: "info", + text1: "Info", + text2: response.message, + }); + + return; + } + + Toast.show({ + type: "success", + text1: "Sukses", + text2: "Profile berhasil diupdate", + }); + return router.back(); + } catch (error) { + console.log("error update profile >>", error); + } finally { + setIsLoading(false); + } }; return ( - - Simpan + + Update } @@ -55,16 +77,17 @@ export default function ProfileEdit() { { - setData({ ...data, nama: text }); + setData({ ...data, name: text }); }} required /> { setData({ ...data, email: text }); }} @@ -73,7 +96,7 @@ export default function ProfileEdit() { { setData({ ...data, alamat: text }); }} @@ -84,25 +107,12 @@ export default function ProfileEdit() { label="Jenis Kelamin" placeholder="Pilih jenis kelamin" data={options} - value={data.selectedValue} + value={data?.jenisKelamin} onChange={(value) => { - setData({ ...(data as any), selectedValue: value }); + setData({ ...(data as any), jenisKelamin: value }); }} /> ); } - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: "center", - padding: 20, - }, - result: { - marginTop: 20, - fontSize: 16, - fontWeight: "bold", - }, -}); diff --git a/app/(application)/(user)/profile/[id]/index.tsx b/app/(application)/(user)/profile/[id]/index.tsx index ab423ad..49ac5be 100644 --- a/app/(application)/(user)/profile/[id]/index.tsx +++ b/app/(application)/(user)/profile/[id]/index.tsx @@ -1,5 +1,4 @@ import ViewWrapper from "@/components/_ShareComponent/ViewWrapper"; -import AlertCustom from "@/components/Alert/AlertCustom"; import LeftButtonCustom from "@/components/Button/BackButton"; import DrawerCustom from "@/components/Drawer/DrawerCustom"; import { MainColor } from "@/constants/color-palet"; @@ -7,18 +6,24 @@ import { useAuth } from "@/hooks/use-auth"; import { drawerItemsProfile } from "@/screens/Profile/ListPage"; import Profile_MenuDrawerSection from "@/screens/Profile/menuDrawerSection"; import ProfileSection from "@/screens/Profile/ProfileSection"; +import { apiProfile } from "@/service/api-client/api-profile"; import { GStyles } from "@/styles/global-styles"; +import { IProfile } from "@/types/Type-Profile"; import { Ionicons } from "@expo/vector-icons"; -import { router, Stack, useLocalSearchParams } from "expo-router"; -import React, { useState } from "react"; +import { + Stack, + useFocusEffect, + useLocalSearchParams +} from "expo-router"; +import React, { useCallback, useState } from "react"; import { TouchableOpacity } from "react-native"; export default function Profile() { const { id } = useLocalSearchParams(); const [isDrawerOpen, setIsDrawerOpen] = useState(false); - const [showLogoutAlert, setShowLogoutAlert] = useState(false); + const [data, setData] = useState(); - const { logout } = useAuth(); + const { logout, isAdmin } = useAuth(); const openDrawer = () => { setIsDrawerOpen(true); @@ -28,60 +33,53 @@ export default function Profile() { setIsDrawerOpen(false); }; - const handleLogout = () => { - console.log("User logout"); - router.replace("/"); - setShowLogoutAlert(false); - }; + useFocusEffect( + useCallback(() => { + onLoadData(id as string); + }, [id]) + ); + + async function onLoadData(id: string) { + const response = await apiProfile({ id: id }); + setData(response.data); + } return ( <> + , + headerRight: () => ( + + + + ), + headerStyle: GStyles.headerStyle, + headerTitleStyle: GStyles.headerTitleStyle, + }} + /> {/* Header */} - , - headerRight: () => ( - - - - ), - headerStyle: GStyles.headerStyle, - headerTitleStyle: GStyles.headerTitleStyle, - }} - /> - + {/* Drawer Komponen Eksternal */} - - {/* Alert Komponen Eksternal */} - setShowLogoutAlert(false)} - onRightPress={handleLogout} - title="Apakah anda yakin ingin keluar?" - textLeft="Batal" - textRight="Keluar" - colorRight={MainColor.red} - /> ); } diff --git a/app/(application)/(user)/profile/create.tsx b/app/(application)/(user)/profile/create.tsx index 7833bfe..b23af22 100644 --- a/app/(application)/(user)/profile/create.tsx +++ b/app/(application)/(user)/profile/create.tsx @@ -13,8 +13,8 @@ import InformationBox from "@/components/Box/InformationBox"; import LandscapeFrameUploaded from "@/components/Image/LandscapeFrameUploaded"; import { useAuth } from "@/hooks/use-auth"; import { apiCreateProfile } from "@/service/api-client/api-profile"; -import { router } from "expo-router"; -import { useState } from "react"; +import { router, useFocusEffect } from "expo-router"; +import { useCallback, useState } from "react"; import { View } from "react-native"; import Toast from "react-native-toast-message"; @@ -30,6 +30,16 @@ export default function CreateProfile() { jenisKelamin: "", }); + useFocusEffect( + useCallback(() => { + Toast.show({ + type: "info", + text1: "Lengkapi Profile Anda", + text2: "Untuk menjelajahi fitur-fitur yang ada", + }); + }, []) + ); + const handlerSave = async () => { if (!data.name || !data.email || !data.alamat || !data.jenisKelamin) { Toast.show({ @@ -43,8 +53,6 @@ export default function CreateProfile() { try { setIsLoading(true); const response = await apiCreateProfile(data); - console.log("data create profile >>", JSON.stringify(response, null, 2)); - if (response.status === 400) { Toast.show({ type: "error", @@ -56,7 +64,7 @@ export default function CreateProfile() { Toast.show({ type: "success", - text1: "Success", + text1: "Sukses", text2: "Profile berhasil dibuat", }); router.push("/(application)/(user)/home"); diff --git a/app/(application)/admin/_layout.tsx b/app/(application)/admin/_layout.tsx index 4709c5d..282eb4c 100644 --- a/app/(application)/admin/_layout.tsx +++ b/app/(application)/admin/_layout.tsx @@ -221,7 +221,7 @@ export default function AdminLayout() { textLeft: "Batal", textRight: "Ya", onPressRight: () => { - router.replace(`/(application)/(user)/profile/${123}`); + router.replace(`/(application)/(user)/home`); }, }); } else if (item.value === "logout") { diff --git a/components/_Interface/types.ts b/components/_Interface/types.ts index 5f2cfe0..790a3a1 100644 --- a/components/_Interface/types.ts +++ b/components/_Interface/types.ts @@ -24,4 +24,5 @@ interface IMenuDrawerItem { label: string; path?: string; color?: string; + value?: string; } diff --git a/screens/Authentication/VerificationView.tsx b/screens/Authentication/VerificationView.tsx index 1d5ed94..f531a7a 100644 --- a/screens/Authentication/VerificationView.tsx +++ b/screens/Authentication/VerificationView.tsx @@ -29,6 +29,7 @@ export default function VerificationView() { async function onLoadCheckCodeOtp() { const kodeId = await AsyncStorage.getItem("kode_otp"); const response = await apiCheckCodeOtp({ kodeId: kodeId as string }); + console.log("Response check code otp >>", JSON.stringify(response.otp, null, 2)); setCodeOtp(response.otp); setUserNumber(response.nomor); } diff --git a/screens/Home/tabsList.ts b/screens/Home/tabsList.ts index 227d06c..f5fc139 100644 --- a/screens/Home/tabsList.ts +++ b/screens/Home/tabsList.ts @@ -1,6 +1,6 @@ import { ITabs } from "@/components/_Interface/types"; -export const tabsHome: ITabs[] = [ +export const tabsHome: any = (profileId: string) => [ { id: "forum", icon: "chatbubble-ellipses-outline", @@ -33,8 +33,8 @@ export const tabsHome: ITabs[] = [ icon: "person-outline", activeIcon: "person", label: "Profile", - path: "/profile/id-percoban-123456", + path: `/profile/${profileId}`, isActive: true, disabled: false, }, -]; \ No newline at end of file +]; diff --git a/screens/Profile/AvatarAndBackground.tsx b/screens/Profile/AvatarAndBackground.tsx index 885518a..aabcd85 100644 --- a/screens/Profile/AvatarAndBackground.tsx +++ b/screens/Profile/AvatarAndBackground.tsx @@ -12,6 +12,8 @@ const AvatarAndBackground = ({ imageId: string; }) => { return ( + // console.log("backgroundId", backgroundId), + // console.log("imageId", imageId), {/* Background Image */} [ - { - icon: ( - - ), - label: "Edit profile", - path: `/(application)/profile/${id}/edit`, - }, - { - icon: ( - - ), - label: "Ubah foto profile", - path: `/(application)/profile/${id}/update-photo`, - }, - { - icon: ( - - ), - label: "Ubah latar belakang", - path: `/(application)/profile/${id}/update-background`, - }, - { - icon: ( - - ), - label: "Tambah portofolio", - path: `/(application)/portofolio/${id}/create`, - }, - { - icon: ( - - ), - label: "Dashboard Admin", - path: `/(application)/admin/dashboard`, - }, - { - icon: ( - - ), - label: "Keluar", - color: MainColor.red, - path: "", - }, - { - icon: ( - - ), - label: "Create profile", - path: `/(application)/profile/${id}/create`, - }, -]; + isAdmin: boolean; +}) => { + const adminItems: IMenuDrawerItem[] = [ + { + icon: ( + + ), + label: "Edit profile", + path: `/(application)/profile/${id}/edit`, + }, + { + icon: ( + + ), + label: "Ubah foto profile", + path: `/(application)/profile/${id}/update-photo`, + }, + { + icon: ( + + ), + label: "Ubah latar belakang", + path: `/(application)/profile/${id}/update-background`, + }, + { + icon: ( + + ), + label: "Tambah portofolio", + path: `/(application)/portofolio/${id}/create`, + }, + { + icon: ( + + ), + label: "Dashboard Admin", + path: `/(application)/admin/dashboard`, + }, + { + icon: ( + + ), + label: "Keluar", + color: MainColor.red, + path: "", + }, + ]; + + const userItems: IMenuDrawerItem[] = [ + { + icon: ( + + ), + label: "Edit profile", + path: `/(application)/profile/${id}/edit`, + }, + { + icon: ( + + ), + label: "Ubah foto profile", + path: `/(application)/profile/${id}/update-photo`, + }, + { + icon: ( + + ), + label: "Ubah latar belakang", + path: `/(application)/profile/${id}/update-background`, + }, + { + icon: ( + + ), + label: "Tambah portofolio", + path: `/(application)/portofolio/${id}/create`, + }, + { + icon: ( + + ), + label: "Keluar", + color: MainColor.red, + path: "", + }, + ]; + + return isAdmin ? adminItems : userItems; +}; diff --git a/screens/Profile/ProfileSection.tsx b/screens/Profile/ProfileSection.tsx index 945f5c1..6824426 100644 --- a/screens/Profile/ProfileSection.tsx +++ b/screens/Profile/ProfileSection.tsx @@ -5,8 +5,10 @@ import { FontAwesome5, Ionicons } from "@expo/vector-icons"; import { router, useLocalSearchParams } from "expo-router"; import { View } from "react-native"; import AvatarAndBackground from "./AvatarAndBackground"; +import { IProfile } from "@/types/Type-Profile"; +import _ from "lodash"; -export default function ProfileSection() { +export default function ProfileSection({ data }: { data: IProfile }) { const { id } = useLocalSearchParams(); const listData = [ @@ -14,13 +16,13 @@ export default function ProfileSection() { icon: ( ), - label: "+6282340374412", + label: `+${data && data.User.nomor ? data.User.nomor : "-"}`, }, { icon: ( ), - label: "bagasbanuna@gmail.com", + label: `${data && data.email ? data.email : "-"}`, }, { icon: ( @@ -30,28 +32,38 @@ export default function ProfileSection() { color="white" /> ), - label: "Jalan Raya Sesetan No. 123, Bandung, Indonesia", + label: `${data && data.alamat ? data.alamat : "-"}`, }, { icon: ( ), - label: "Laki-laki", + label: `${ + data && data.jenisKelamin ? _.startCase(data.jenisKelamin) : "-" + }`, }, ]; return ( <> - + {/* + {JSON.stringify(data.imageBackgroundId, null, 2)} + */} + - Nama User + {data && data.name ? data.name : "-"} - @Username + + @{data && data.User.username ? data.User.username : "-"} + diff --git a/screens/Profile/menuDrawerSection.tsx b/screens/Profile/menuDrawerSection.tsx index ff30938..58d08bb 100644 --- a/screens/Profile/menuDrawerSection.tsx +++ b/screens/Profile/menuDrawerSection.tsx @@ -5,12 +5,10 @@ import { router } from "expo-router"; export default function Profile_MenuDrawerSection({ drawerItems, - setShowLogoutAlert, setIsDrawerOpen, logout, }: { drawerItems: IMenuDrawerItem[]; - setShowLogoutAlert: (value: boolean) => void; setIsDrawerOpen: (value: boolean) => void; logout: () => Promise; }) { diff --git a/service/api-client/api-profile.ts b/service/api-client/api-profile.ts index 051ac64..1fe049f 100644 --- a/service/api-client/api-profile.ts +++ b/service/api-client/api-profile.ts @@ -4,5 +4,23 @@ export async function apiCreateProfile(data: any) { const response = await apiConfig.post(`/mobile/profile`, { data: data, }); - return response; + return response.data; +} + +export async function apiProfile({ id }: { id: string }) { + const response = await apiConfig.get(`/mobile/profile/${id}`); + return response.data; +} + +export async function apiUpdateProfile({ + id, + data, +}: { + id: string; + data: any; +}) { + const response = await apiConfig.put(`/mobile/profile/${id}`, { + data: data, + }); + return response.data; } diff --git a/service/api-client/api-user.ts b/service/api-client/api-user.ts index 201a0c1..defce65 100644 --- a/service/api-client/api-user.ts +++ b/service/api-client/api-user.ts @@ -1,7 +1,6 @@ import { apiConfig } from "../api-config"; - export async function apiUser(id: string) { const response = await apiConfig.get(`/user/${id}`); return response.data; -} \ No newline at end of file +} diff --git a/service/api-config.ts b/service/api-config.ts index 92e88c3..cab9e68 100644 --- a/service/api-config.ts +++ b/service/api-config.ts @@ -10,6 +10,7 @@ export const apiConfig: AxiosInstance = axios.create({ apiConfig.interceptors.request.use( async (config) => { + console.log("API_BASE_URL >>", API_BASE_URL); const token = await AsyncStorage.getItem("authToken"); if (token) { // config.timeout = 10000; diff --git a/types/Type-Profile.ts b/types/Type-Profile.ts new file mode 100644 index 0000000..7acd765 --- /dev/null +++ b/types/Type-Profile.ts @@ -0,0 +1,16 @@ +import { IUser } from "./User"; + +export interface IProfile { + id?: string; + name?: string; + email?: string; + alamat?: string; + jenisKelamin?: string; + active?: boolean; + createdAt?: string; + updatedAt?: string; + userId?: string; + imageId?: string | null; + imageBackgroundId?: string | null; + User: IUser +} \ No newline at end of file diff --git a/types/User.ts b/types/User.ts index b3f564f..46fee52 100644 --- a/types/User.ts +++ b/types/User.ts @@ -6,12 +6,12 @@ export interface IMasterUserRole { } export interface IUser { - id: string; - username: string; - nomor: string; - active: boolean; - createdAt: string | null; - updatedAt: string | null; - masterUserRoleId: string; - MasterUserRole: IMasterUserRole; + id?: string; + username?: string; + nomor?: string; + active?: boolean; + createdAt?: string | null; + updatedAt?: string | null; + masterUserRoleId?: string; + MasterUserRole?: IMasterUserRole; }