│ │ │ 1. Device Token Registration Error (HTTP 500) │ │ - File: service/api-device-token.ts │ │ - Fix: Hapus nested data wrapper pada payload │ │ - Improvement: Tambahkan error logging detail │ │ │ │ 2. Uncaught Promise Errors │ │ - File: components/Notification/NotificationInitializer.tsx │ │ - Fix: Better error handling untuk device token registration │ │ - File: app/(application)/(user)/home.tsx │ │ - Fix: Add .catch() untuk userData() dan error handling apiUser() │ │ - File: app/(application)/(user)/profile/[id]/index.tsx │ │ - Fix: Add error handling untuk apiProfile(), apiUser(), userData() │ │ │ │ 3. UI Improvements │ │ - File: app/(application)/(user)/home.tsx │ │ - Feature: 4 skeleton lingkaran untuk loading state grid features │ │ │ │ 4. Maps Migration │ │ - File: app/(application)/admin/maps.tsx │ │ - Change: Replace react-native-maps dengan MapsV2Custom (Maplibre) │ │ - Cleanup: Hapus unused imports dan interfaces │ │ │ │ Files Modified (7) │ │ - app/(application)/(user)/home.tsx │ │ - app/(application)/(user)/profile/[id]/index.tsx │ │ - app/(application)/admin/maps.tsx │ │ - components/Notification/NotificationInitializer.tsx │ │ - service/api-device-token.ts │ │ - constants/constans-value.ts │ │ - screens/Home/bottomFeatureSection.tsx │ │ - screens/UserSeach/MainView_V2.tsx ### No Issue
201 lines
5.5 KiB
TypeScript
201 lines
5.5 KiB
TypeScript
/* eslint-disable react-hooks/exhaustive-deps */
|
|
import { NewWrapper, StackCustom } from "@/components";
|
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
|
import LeftButtonCustom from "@/components/Button/BackButton";
|
|
import DrawerCustom from "@/components/Drawer/DrawerCustom";
|
|
import { MainColor } from "@/constants/color-palet";
|
|
import { useAuth } from "@/hooks/use-auth";
|
|
import { drawerItemsProfile } from "@/screens/Profile/ListPage";
|
|
import Profile_MenuDrawerSection from "@/screens/Profile/menuDrawerSection";
|
|
import Profile_PortofolioSection from "@/screens/Profile/PortofolioSection";
|
|
import ProfileSection from "@/screens/Profile/ProfileSection";
|
|
import { apiGetPortofolio } from "@/service/api-client/api-portofolio";
|
|
import { apiProfile } from "@/service/api-client/api-profile";
|
|
import { apiUser } from "@/service/api-client/api-user";
|
|
import { GStyles } from "@/styles/global-styles";
|
|
import { IProfile } from "@/types/Type-Profile";
|
|
import { Ionicons } from "@expo/vector-icons";
|
|
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
import { useCallback, useState } from "react";
|
|
import { RefreshControl, TouchableOpacity } from "react-native";
|
|
|
|
export default function Profile() {
|
|
const { id } = useLocalSearchParams();
|
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
|
const [data, setData] = useState<IProfile>();
|
|
const [dataToken, setDataToken] = useState<IProfile>();
|
|
const [listPortofolio, setListPortofolio] = useState<any[]>();
|
|
const [refreshing, setRefreshing] = useState(false);
|
|
|
|
const { token, logout, isAdmin, user, userData } = useAuth();
|
|
|
|
const openDrawer = () => {
|
|
setIsDrawerOpen(true);
|
|
};
|
|
|
|
const closeDrawer = () => {
|
|
setIsDrawerOpen(false);
|
|
};
|
|
|
|
useFocusEffect(
|
|
useCallback(() => {
|
|
onLoadData(id as string);
|
|
onLoadPortofolio(id as string);
|
|
onLoadUserByToken();
|
|
isUserCheck();
|
|
userData(token as string);
|
|
}, [id, token]),
|
|
);
|
|
|
|
const isUserCheck = () => {
|
|
const userId = id;
|
|
const userLoginId = dataToken?.id;
|
|
|
|
return userId === userLoginId;
|
|
};
|
|
|
|
const onLoadData = async (id: string) => {
|
|
try {
|
|
const response = await apiProfile({ id: id });
|
|
setData(response.data);
|
|
} catch (error) {
|
|
console.log("[ERROR onLoadData]", error);
|
|
}
|
|
};
|
|
|
|
const onLoadUserByToken = async () => {
|
|
try {
|
|
const response = await apiUser(user?.id as string);
|
|
setDataToken(response?.data?.Profile);
|
|
} catch (error) {
|
|
console.log("[ERROR onLoadUserByToken]", error);
|
|
}
|
|
};
|
|
|
|
const onLoadPortofolio = async (id: string) => {
|
|
try {
|
|
const response = await apiGetPortofolio({ id: id });
|
|
const lastTwoByDate = response.data
|
|
.sort(
|
|
(a: any, b: any) =>
|
|
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
|
) // urut desc
|
|
.slice(0, 2);
|
|
setListPortofolio(lastTwoByDate);
|
|
} catch (error) {
|
|
console.log("[ERROR onLoadPortofolio]", error);
|
|
}
|
|
};
|
|
|
|
const onRefresh = useCallback(() => {
|
|
setRefreshing(true);
|
|
onLoadData(id as string);
|
|
onLoadPortofolio(id as string);
|
|
onLoadUserByToken();
|
|
isUserCheck();
|
|
userData(token as string);
|
|
setRefreshing(false);
|
|
}, [id, token]);
|
|
|
|
return (
|
|
<>
|
|
<Stack.Screen
|
|
options={{
|
|
title: `Profile`,
|
|
headerLeft: () => <LeftButtonCustom />,
|
|
headerRight: () => (
|
|
<ButtonnDot
|
|
id={id as string}
|
|
openDrawer={openDrawer}
|
|
isUserCheck={isUserCheck()}
|
|
logout={logout}
|
|
/>
|
|
),
|
|
headerStyle: GStyles.headerStyle,
|
|
headerTitleStyle: GStyles.headerTitleStyle,
|
|
}}
|
|
/>
|
|
{/* Main View */}
|
|
<NewWrapper
|
|
refreshControl={
|
|
<RefreshControl
|
|
refreshing={refreshing}
|
|
onRefresh={onRefresh}
|
|
tintColor={MainColor.yellow}
|
|
colors={[MainColor.yellow]}
|
|
/>
|
|
}
|
|
>
|
|
{!data || !dataToken ? (
|
|
<StackCustom>
|
|
<CustomSkeleton height={400} />
|
|
<CustomSkeleton height={200} />
|
|
</StackCustom>
|
|
) : (
|
|
<>
|
|
<ProfileSection data={data as any} />
|
|
|
|
<Profile_PortofolioSection
|
|
data={listPortofolio as any}
|
|
profileId={id as string}
|
|
/>
|
|
</>
|
|
)}
|
|
</NewWrapper>
|
|
|
|
{/* Drawer Komponen Eksternal */}
|
|
<DrawerCustom
|
|
height={"auto"}
|
|
isVisible={isDrawerOpen}
|
|
closeDrawer={closeDrawer}
|
|
>
|
|
<Profile_MenuDrawerSection
|
|
drawerItems={drawerItemsProfile({ id: id as string, isAdmin })}
|
|
setIsDrawerOpen={setIsDrawerOpen}
|
|
logout={logout}
|
|
/>
|
|
</DrawerCustom>
|
|
</>
|
|
);
|
|
}
|
|
|
|
const ButtonnDot = ({
|
|
id,
|
|
openDrawer,
|
|
isUserCheck,
|
|
logout,
|
|
}: {
|
|
id: string;
|
|
openDrawer: () => void;
|
|
isUserCheck: boolean;
|
|
logout: () => Promise<void>;
|
|
}) => {
|
|
console.log("[ID] >>", id);
|
|
|
|
const isId = id === undefined || id === "undefined";
|
|
|
|
if (isId) {
|
|
return (
|
|
<>
|
|
<TouchableOpacity onPress={logout}>
|
|
<Ionicons name="log-out" size={20} color={MainColor.red} />
|
|
</TouchableOpacity>
|
|
</>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{isUserCheck && (
|
|
<TouchableOpacity onPress={openDrawer}>
|
|
<Ionicons
|
|
name="ellipsis-vertical"
|
|
size={20}
|
|
color={MainColor.yellow}
|
|
/>
|
|
</TouchableOpacity>
|
|
)}
|
|
</>
|
|
);
|
|
};
|