API
User search: Fix: - api get all user - searching by username Portofolio: Fix: - dot button hanya muncul jika user yang memiliki portofolio tersebut yang melihat Profile: Fix: - dot button muncul hanya untuk user yang memiliki akunnya ### No Issue
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { DrawerCustom, Spacing, StackCustom } from "@/components";
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { DrawerCustom, LoaderCustom, Spacing, StackCustom } from "@/components";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
@@ -10,18 +11,21 @@ import { drawerItemsPortofolio } from "@/screens/Portofolio/ListPage";
|
||||
import Portofolio_MenuDrawerSection from "@/screens/Portofolio/MenuDrawer";
|
||||
import Portofolio_SocialMediaSection from "@/screens/Portofolio/SocialMediaSection";
|
||||
import { apiGetOnePortofolio } from "@/service/api-client/api-portofolio";
|
||||
import { apiUser } from "@/service/api-client/api-user";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
|
||||
export default function Portofolio() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const { user } = useAuth();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [isLoadingDelete, setIsLoadingDelete] = useState(false);
|
||||
const [data, setData] = useState<any>();
|
||||
const [profileId, setProfileId] = useState<any>();
|
||||
|
||||
const { user } = useAuth();
|
||||
|
||||
const openDrawer = () => {
|
||||
setIsDrawerOpen(true);
|
||||
@@ -33,24 +37,27 @@ export default function Portofolio() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData(id as string);
|
||||
onLoadUserByToken();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
async function onLoadData(id: string) {
|
||||
const response = await apiGetOnePortofolio({ id: id });
|
||||
// console.log(
|
||||
// "Response portofolio >>",
|
||||
// JSON.stringify(response.data, null, 2)
|
||||
// );
|
||||
|
||||
console.log(
|
||||
"[PROFILE ID]>>",
|
||||
JSON.stringify(response.data.Profile.id, null, 2)
|
||||
);
|
||||
setData(response.data);
|
||||
}
|
||||
|
||||
const userId = user?.id;
|
||||
const userLoginId = data?.Profile?.userId
|
||||
|
||||
console.log("User ID >>", userId);
|
||||
console.log("User Login ID >>", userLoginId);
|
||||
const onLoadUserByToken = async () => {
|
||||
const response = await apiUser(user?.id as string);
|
||||
console.log(
|
||||
"[PROFILE LOGIN]>>",
|
||||
JSON.stringify(response.data?.Profile.id, null, 2)
|
||||
);
|
||||
setProfileId(response?.data?.Profile?.id);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -59,7 +66,8 @@ export default function Portofolio() {
|
||||
options={{
|
||||
title: "Portofolio",
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => (
|
||||
headerRight: () =>
|
||||
data?.Profile?.id !== profileId ? null : (
|
||||
<TouchableOpacity onPress={openDrawer}>
|
||||
<Ionicons
|
||||
name="ellipsis-vertical"
|
||||
@@ -73,11 +81,18 @@ export default function Portofolio() {
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
{/* <PorfofolioSection setShowDeleteAlert={setDeleteAlert} data={data} /> */}
|
||||
{!data || !profileId ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<StackCustom>
|
||||
<Portofolio_Data data={data} listSubBidang={data?.Portofolio_BidangDanSubBidangBisnis as any []} />
|
||||
<Portofolio_Data
|
||||
data={data}
|
||||
listSubBidang={data?.Portofolio_BidangDanSubBidangBisnis as any[]}
|
||||
/>
|
||||
<Portofolio_BusinessLocation />
|
||||
<Portofolio_SocialMediaSection data={data?.Portofolio_MediaSosial} />
|
||||
<Portofolio_SocialMediaSection
|
||||
data={data?.Portofolio_MediaSosial}
|
||||
/>
|
||||
<Portofolio_ButtonDelete
|
||||
id={id as string}
|
||||
isLoadingDelete={isLoadingDelete}
|
||||
@@ -85,6 +100,7 @@ export default function Portofolio() {
|
||||
/>
|
||||
<Spacing />
|
||||
</StackCustom>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Komponen Eksternal */}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { LoaderCustom } from "@/components";
|
||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import DrawerCustom from "@/components/Drawer/DrawerCustom";
|
||||
@@ -9,6 +11,7 @@ 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";
|
||||
@@ -20,9 +23,10 @@ export default function Profile() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [data, setData] = useState<IProfile>();
|
||||
const [dataPortofolio, setDataPortofolio] = useState<any[]>();
|
||||
const [dataToken, setDataToken] = useState<IProfile>();
|
||||
const [listPortofolio, setListPortofolio] = useState<any[]>();
|
||||
|
||||
const { logout, isAdmin } = useAuth();
|
||||
const { logout, isAdmin, user } = useAuth();
|
||||
|
||||
const openDrawer = () => {
|
||||
setIsDrawerOpen(true);
|
||||
@@ -36,14 +40,28 @@ export default function Profile() {
|
||||
useCallback(() => {
|
||||
onLoadData(id as string);
|
||||
onLoadPortofolio(id as string);
|
||||
onLoadUserByToken();
|
||||
isUserCheck();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const isUserCheck = () => {
|
||||
const userId = id;
|
||||
const userLoginId = dataToken?.id;
|
||||
|
||||
return userId === userLoginId;
|
||||
};
|
||||
|
||||
const onLoadData = async (id: string) => {
|
||||
const response = await apiProfile({ id: id });
|
||||
setData(response.data);
|
||||
};
|
||||
|
||||
const onLoadUserByToken = async () => {
|
||||
const response = await apiUser(user?.id as string);
|
||||
setDataToken(response?.data?.Profile);
|
||||
};
|
||||
|
||||
const onLoadPortofolio = async (id: string) => {
|
||||
const response = await apiGetPortofolio({ id: id });
|
||||
const lastTwoByDate = response.data
|
||||
@@ -52,7 +70,7 @@ export default function Profile() {
|
||||
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
||||
) // urut desc
|
||||
.slice(0, 2);
|
||||
setDataPortofolio(lastTwoByDate);
|
||||
setListPortofolio(lastTwoByDate);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -61,7 +79,8 @@ export default function Profile() {
|
||||
options={{
|
||||
title: "Profile",
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => (
|
||||
headerRight: () =>
|
||||
isUserCheck() && (
|
||||
<TouchableOpacity onPress={openDrawer}>
|
||||
<Ionicons
|
||||
name="ellipsis-vertical"
|
||||
@@ -76,12 +95,18 @@ export default function Profile() {
|
||||
/>
|
||||
{/* Main View */}
|
||||
<ViewWrapper>
|
||||
{!data || !dataToken ? (
|
||||
<LoaderCustom />
|
||||
) : (
|
||||
<>
|
||||
<ProfileSection data={data as any} />
|
||||
|
||||
<Profile_PortofolioSection
|
||||
data={dataPortofolio as any}
|
||||
data={listPortofolio as any}
|
||||
profileId={id as string}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
|
||||
{/* Drawer Komponen Eksternal */}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
AvatarCustom,
|
||||
AvatarComp,
|
||||
ClickableCustom,
|
||||
Grid,
|
||||
Spacing,
|
||||
@@ -10,39 +10,42 @@ import {
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { apiAllUser } from "@/service/api-client/api-user";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function UserSearch() {
|
||||
function generateRandomPhoneNumber(index: number) {
|
||||
let prefix;
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
// Menentukan prefix berdasarkan index genap atau ganjil
|
||||
if (index % 2 === 0) {
|
||||
const evenPrefixes = ["6288", "6289", "6281"];
|
||||
prefix = evenPrefixes[Math.floor(Math.random() * evenPrefixes.length)];
|
||||
} else {
|
||||
const oddPrefixes = ["6285", "6283"];
|
||||
prefix = oddPrefixes[Math.floor(Math.random() * oddPrefixes.length)];
|
||||
useEffect(() => {
|
||||
onLoadData(search);
|
||||
}, [search]);
|
||||
|
||||
const onLoadData = async (search: string) => {
|
||||
try {
|
||||
const response = await apiAllUser({ search: search });
|
||||
console.log("[DATA USER] >", JSON.stringify(response.data, null, 2));
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
console.log("Error fetching data", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Menghitung panjang sisa nomor acak (antara 10 - 12 digit)
|
||||
const remainingLength = Math.floor(Math.random() * 3) + 10; // 10, 11, atau 12
|
||||
const handleSearch = (search: string) => {
|
||||
setSearch(search);
|
||||
onLoadData(search);
|
||||
};
|
||||
|
||||
// Membuat sisa nomor acak
|
||||
let randomNumber = "";
|
||||
for (let i = 0; i < remainingLength; i++) {
|
||||
randomNumber += Math.floor(Math.random() * 10); // Digit acak antara 0-9
|
||||
}
|
||||
|
||||
// Menggabungkan prefix dan sisa nomor
|
||||
return prefix + randomNumber;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
headerComponent={
|
||||
<TextInputCustom
|
||||
value={search}
|
||||
onChangeText={handleSearch}
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search"
|
||||
@@ -57,17 +60,25 @@ export default function UserSearch() {
|
||||
}
|
||||
>
|
||||
<StackCustom>
|
||||
{Array.from({ length: 20 }).map((e, index) => {
|
||||
{!_.isEmpty(data) ? (
|
||||
data?.map((e, index) => {
|
||||
return (
|
||||
<Grid key={index}>
|
||||
<ClickableCustom
|
||||
key={index}
|
||||
onPress={() => {
|
||||
console.log("Ke Profile");
|
||||
router.push(`/profile/${e?.Profile?.id}`);
|
||||
}}
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarCustom href={`/profile/${index}`}/>
|
||||
<AvatarComp fileId={e?.Profile?.imageId} size="base" />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={9}>
|
||||
<TextCustom size="large">Nama user {index}</TextCustom>
|
||||
<TextCustom size="small">
|
||||
+{generateRandomPhoneNumber(index)}
|
||||
</TextCustom>
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom size="large">{e?.username}</TextCustom>
|
||||
<TextCustom size="small">+{e?.nomor}</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={1}
|
||||
@@ -75,23 +86,20 @@ export default function UserSearch() {
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
console.log("Ke Profile");
|
||||
router.push(`/profile/${index}`);
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.white}
|
||||
/>
|
||||
</ClickableCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</ClickableCustom>
|
||||
);
|
||||
})}
|
||||
})
|
||||
) : (
|
||||
<TextCustom align="center">Tidak ditemukan</TextCustom>
|
||||
)}
|
||||
</StackCustom>
|
||||
<Spacing height={50} />
|
||||
</ViewWrapper>
|
||||
|
||||
8
components/Loader/LoaderCustom.tsx
Normal file
8
components/Loader/LoaderCustom.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ActivityIndicator } from "react-native";
|
||||
|
||||
export default function LoaderCustom({ size }: { size?: "small" | "large" }) {
|
||||
return (
|
||||
<ActivityIndicator size={size ? size : "small"} color={MainColor.yellow} />
|
||||
);
|
||||
}
|
||||
@@ -61,6 +61,8 @@ import DummyLandscapeImage from "./_ShareComponent/DummyLandscapeImage";
|
||||
import GridComponentView from "./_ShareComponent/GridSectionView";
|
||||
// Progress
|
||||
import ProgressCustom from "./Progress/ProgressCustom";
|
||||
// Loader
|
||||
import LoaderCustom from "./Loader/LoaderCustom";
|
||||
|
||||
export {
|
||||
// ActionIcon
|
||||
@@ -128,4 +130,6 @@ export {
|
||||
TextInputCustom,
|
||||
// ViewWrapper
|
||||
ViewWrapper,
|
||||
// Loader
|
||||
LoaderCustom,
|
||||
};
|
||||
|
||||
@@ -92,7 +92,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
await AsyncStorage.setItem("authToken", token);
|
||||
|
||||
const responseUser = await apiConfig.get(
|
||||
`/mobile/user?token=${token}`,
|
||||
`/mobile?token=${token}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { apiConfig } from "../api-config";
|
||||
|
||||
export async function apiUser(id: string) {
|
||||
const response = await apiConfig.get(`/user/${id}`);
|
||||
const response = await apiConfig.get(`/mobile/user/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function apiAllUser({ search }: { search: string }) {
|
||||
const response = await apiConfig.get(`/mobile/user?search=${search}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user