Poortofoloio

Fix:
- service/api-client/api-portofolio.ts : api edit detail. logo, medsos

### No Issue
This commit is contained in:
2025-09-01 17:10:25 +08:00
parent bb95e8ccbd
commit 6887f85e6a
7 changed files with 381 additions and 67 deletions

View File

@@ -1,9 +1,19 @@
import { MapCustom, ViewWrapper } from "@/components";
import { View } from "react-native";
import MapView from "react-native-maps";
export default function Maps() {
return (
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
<MapCustom height={"100%"} />
{/* <MapCustom height={"100%"} /> */}
<View style={{ flex: 1 }}>
<MapView
style={{
width: "100%",
height: "100%",
}}
/>
</View>
</ViewWrapper>
);
}

View File

@@ -1,25 +1,125 @@
import {
AvatarCustom,
BaseBox,
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
ViewWrapper,
ViewWrapper
} from "@/components";
import API_STRORAGE from "@/constants/base-url-api-strorage";
import DIRECTORY_ID from "@/constants/directory-id";
import DUMMY_IMAGE from "@/constants/dummy-image-value";
import { useAuth } from "@/hooks/use-auth";
import { apiFileDelete } from "@/service/api-client/api-file";
import {
apiGetOnePortofolio,
apiUpdatePortofolio,
} from "@/service/api-client/api-portofolio";
import { uploadImageService } from "@/service/upload-service";
import pickImage from "@/utils/pickImage";
import { Image } from "expo-image";
import { router, useLocalSearchParams } from "expo-router";
import { useEffect, useState } from "react";
import Toast from "react-native-toast-message";
export default function PortofolioEditLogo() {
const { id } = useLocalSearchParams();
const [logoId, setLogoId] = useState<any>();
const [imageUri, setImageUri] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const { token } = useAuth();
useEffect(() => {
onLoadData(id as string);
}, [id]);
const onLoadData = async (id: string) => {
const response = await apiGetOnePortofolio({ id: id });
console.log(
"Response portofolio >>",
JSON.stringify(response.data.logoId, null, 2)
);
setLogoId(response.data.logoId);
};
async function onUpload() {
try {
setIsLoading(true);
const response = await uploadImageService({
imageUri,
dirId: DIRECTORY_ID.portofolio_logo,
});
if (response.success) {
const fileId = response.data.id;
const responseUpdate = await apiUpdatePortofolio({
id: id as string,
data: { fileId },
category: "logo",
});
if (!responseUpdate.success) {
Toast.show({
type: "error",
text1: "Info",
text2: responseUpdate.message,
});
return;
}
if (logoId) {
const deletePrevFile = await apiFileDelete({
token: token as string,
id: logoId as string,
});
if (!deletePrevFile.success) {
console.log("error delete prev file >>", deletePrevFile.message);
}
}
Toast.show({
type: "success",
text1: "Sukses",
text2: "Logo berhasil diupdate",
});
router.back();
}
} catch (error) {
Toast.show({
type: "error",
text1: "Gagal",
text2: error as string,
});
} finally {
setIsLoading(false);
}
}
const image = imageUri ? (
<Image source={{ uri: imageUri }} style={{ width: 200, height: 200 }} />
) : (
<Image
source={
logoId
? { uri: API_STRORAGE.GET({ fileId: logoId }) }
: DUMMY_IMAGE.avatar
}
style={{ width: 200, height: 200 }}
/>
);
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
isLoading={isLoading}
disabled={isLoading}
onPress={() => {
console.log("Simpan logo ");
router.back();
onUpload();
}}
>
Simpan
Update
</ButtonCustom>
</BoxButtonOnFooter>
);
@@ -34,13 +134,17 @@ export default function PortofolioEditLogo() {
height: 250,
}}
>
<AvatarCustom size="xl" />
{image}
</BaseBox>
<ButtonCenteredOnly
icon="upload"
onPress={() => router.navigate(`/take-picture/${id}`)}
onPress={() => {
pickImage({
setImageUri,
});
}}
>
Update
Upload
</ButtonCenteredOnly>
</ViewWrapper>
</>

View File

@@ -4,20 +4,87 @@ import {
TextInputCustom,
ViewWrapper,
} from "@/components";
import {
apiGetOnePortofolio,
apiUpdatePortofolio,
} from "@/service/api-client/api-portofolio";
import { useLocalSearchParams, router } from "expo-router";
import { useEffect, useState } from "react";
import Toast from "react-native-toast-message";
export default function PortofolioEditSocialMedia() {
const { id } = useLocalSearchParams();
console.log("ID >>", id);
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState<any>({
facebook: "",
twitter: "",
instagram: "",
tiktok: "",
youtube: "",
});
useEffect(() => {
onLoadData(id as string);
}, [id]);
const onLoadData = async (id: string) => {
const response = await apiGetOnePortofolio({ id: id });
console.log(
"Response portofolio >>",
JSON.stringify(response.data.Portofolio_MediaSosial, null, 2)
);
const data = response.data.Portofolio_MediaSosial;
setData({
facebook: data.facebook,
twitter: data.twitter,
instagram: data.instagram,
tiktok: data.tiktok,
youtube: data.youtube,
});
};
const onSubmitUpdate = async () => {
try {
setIsLoading(true);
const response = await apiUpdatePortofolio({
id: id as string,
data: data,
category: "medsos",
});
if (!response.success) {
Toast.show({
type: "info",
text1: "Info",
text2: response.message,
});
return;
}
Toast.show({
type: "success",
text1: "Sukses",
text2: "Data media terupdate",
});
router.back();
} catch (error) {
console.log("Error onSubmitUpdate", error);
} finally {
setIsLoading(false);
}
};
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log(`Simpan sosmed ${id}`);
router.back();
}}
isLoading={isLoading}
disabled={isLoading}
onPress={onSubmitUpdate}
>
Simpan
Update
</ButtonCustom>
</BoxButtonOnFooter>
);
@@ -25,11 +92,36 @@ export default function PortofolioEditSocialMedia() {
return (
<>
<ViewWrapper footerComponent={buttonFooter}>
<TextInputCustom label="Tiktok" placeholder="Masukkan tiktok" />
<TextInputCustom label="Instagram" placeholder="Masukkan instagram" />
<TextInputCustom label="Facebook" placeholder="Masukkan facebook" />
<TextInputCustom label="Twitter" placeholder="Masukkan twitter" />
<TextInputCustom label="Youtube" placeholder="Masukkan youtube" />
<TextInputCustom
value={data.tiktok}
onChangeText={(value) => setData({ ...data, tiktok: value })}
label="Tiktok"
placeholder="Masukkan tiktok"
/>
<TextInputCustom
value={data.instagram}
onChangeText={(value) => setData({ ...data, instagram: value })}
label="Instagram"
placeholder="Masukkan instagram"
/>
<TextInputCustom
value={data.facebook}
onChangeText={(value) => setData({ ...data, facebook: value })}
label="Facebook"
placeholder="Masukkan facebook"
/>
<TextInputCustom
value={data.twitter}
onChangeText={(value) => setData({ ...data, twitter: value })}
label="Twitter"
placeholder="Masukkan twitter"
/>
<TextInputCustom
value={data.youtube}
onChangeText={(value) => setData({ ...data, youtube: value })}
label="Youtube"
placeholder="Masukkan youtube"
/>
</ViewWrapper>
</>
);

View File

@@ -1,57 +1,134 @@
import {
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
Grid,
SelectCustom,
Spacing,
StackCustom,
TextAreaCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
BoxButtonOnFooter,
ButtonCustom,
SelectCustom,
Spacing,
StackCustom,
TextAreaCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import dummyMasterBidangBisnis from "@/lib/dummy-data/master-bidang-bisnis";
import dummyMasterSubBidangBisnis from "@/lib/dummy-data/master-sub-bidang-bisnis";
import { Ionicons } from "@expo/vector-icons";
import { apiMasterBidangBisnis } from "@/service/api-client/api-master";
import {
apiGetOnePortofolio,
apiUpdatePortofolio,
} from "@/service/api-client/api-portofolio";
import { IMasterBidangBisnis } from "@/types/Type-Master";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";
import { useEffect, useState } from "react";
import { Text, View } from "react-native";
import PhoneInput, { ICountry } from "react-native-international-phone-number";
import Toast from "react-native-toast-message";
interface IFormData {
id_Portofolio: string;
namaBisnis: string;
alamatKantor: string;
tlpn: string;
deskripsi: string;
masterBidangBisnisId: string;
}
export default function PortofolioEdit() {
const { id } = useLocalSearchParams();
const [isLoading, setIsLoading] = useState(false);
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
const [inputValue, setInputValue] = useState<string>("");
const [data, setData] = useState({
namaBisnis: "",
bidang_usaha: "",
sub_bidang_usaha: "",
alamat: "",
nomor_telepon: "",
deskripsi: "",
});
const [bidangBisnis, setBidangBisnis] = useState<IMasterBidangBisnis[]>([]);
const [data, setData] = useState<any>({});
function handleInputValue(phoneNumber: string) {
setInputValue(phoneNumber);
setData({ ...data, tlpn: phoneNumber });
}
function handleSelectedCountry(country: ICountry) {
setSelectedCountry(country);
}
function handleSave() {
console.log(`Update portofolio berhasil ${id}`);
router.back();
}
useEffect(() => {
onLoadData(id as string);
onLoadMaster();
}, [id]);
const onLoadData = async (id: string) => {
const response = await apiGetOnePortofolio({ id: id });
if (response.data.tlpn && response.data.tlpn.includes("62")) {
const fixNumber = response.data.tlpn.replace("62", "");
console.log("Fix Number >>", fixNumber);
setData({ ...response.data, tlpn: fixNumber });
}
};
const onLoadMaster = async () => {
try {
const response = await apiMasterBidangBisnis();
setBidangBisnis(response.data);
} catch (error) {
setBidangBisnis([]);
console.log("Error onLoadMasterBidangBisnis", error);
}
};
const handleSubmitUpdate = async () => {
try {
setIsLoading(true);
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
const fixNumber = data.tlpn.replace(/\s+/g, "");
const realNumber = callingCode + fixNumber;
const newData: IFormData = {
id_Portofolio: data.id_Portofolio,
namaBisnis: data.namaBisnis,
alamatKantor: data.alamatKantor,
tlpn: realNumber,
deskripsi: data.deskripsi,
masterBidangBisnisId: data.masterBidangBisnisId,
};
const response = await apiUpdatePortofolio({
id: id as string,
data: newData,
category: "detail",
});
if (!response.success) {
Toast.show({
type: "info",
text1: "Info",
text2: response.message,
});
return
}
Toast.show({
type: "success",
text1: "Sukses",
text2: "Data terupdate",
});
router.back();
} catch (error) {
console.log("Error handleSubmitUpdate", error);
} finally {
setIsLoading(false);
}
};
const buttonUpdate = (
<BoxButtonOnFooter>
<ButtonCustom onPress={handleSave}>Simpan</ButtonCustom>
<ButtonCustom
isLoading={isLoading}
disabled={isLoading}
onPress={handleSubmitUpdate}
>
Update
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonUpdate}>
@@ -60,22 +137,26 @@ export default function PortofolioEdit() {
required
label="Nama Bisnis"
placeholder="Masukkan nama bisnis"
value={data.namaBisnis}
onChangeText={(value: any) =>
setData({ ...data, namaBisnis: value })
}
/>
<SelectCustom
label="Bidang Usaha"
required
data={dummyMasterBidangBisnis.map((item) => ({
data={bidangBisnis?.map((item) => ({
label: item.name,
value: item.id,
}))}
value={data.bidang_usaha}
value={data.masterBidangBisnisId}
onChange={(value) => {
setData({ ...(data as any), bidang_usaha: value });
setData({ ...(data as any), masterBidangBisnisId: value });
}}
/>
<Grid>
{/* <Grid>
<Grid.Col span={10}>
<SelectCustom
// disabled
@@ -85,9 +166,9 @@ export default function PortofolioEdit() {
label: item.name,
value: item.id,
}))}
value={data.sub_bidang_usaha}
value={data.masterSubBidangBisnisId}
onChange={(value) => {
setData({ ...(data as any), sub_bidang_usaha: value });
setData({ ...(data as any), masterSubBidangBisnisId: value });
}}
/>
</Grid.Col>
@@ -99,11 +180,12 @@ export default function PortofolioEdit() {
<Ionicons name="trash" size={24} color={MainColor.red} />
</TouchableOpacity>
</Grid.Col>
</Grid>
<ButtonCenteredOnly onPress={() => console.log("add")}>
</Grid> */}
{/* <ButtonCenteredOnly onPress={() => console.log("add")}>
Tambah Pilihan
</ButtonCenteredOnly>
<Spacing />
<Spacing /> */}
<View>
<View style={{ flexDirection: "row", alignItems: "center" }}>
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
@@ -113,7 +195,7 @@ export default function PortofolioEdit() {
</View>
<Spacing height={5} />
<PhoneInput
value={inputValue}
value={data.tlpn}
onChangePhoneNumber={handleInputValue}
selectedCountry={selectedCountry}
onChangeSelectedCountry={handleSelectedCountry}
@@ -127,6 +209,10 @@ export default function PortofolioEdit() {
required
label="Alamat Bisnis"
placeholder="Masukkan alamat bisnis"
value={data.alamatKantor}
onChangeText={(value: any) =>
setData({ ...data, alamatKantor: value })
}
/>
<TextAreaCustom
@@ -141,7 +227,7 @@ export default function PortofolioEdit() {
maxRows={5}
required
showCount
maxLength={100}
maxLength={1000}
/>
<Spacing />
</StackCustom>

View File

@@ -91,7 +91,7 @@ export default function Portofolio() {
<DrawerCustom
isVisible={isDrawerOpen}
closeDrawer={closeDrawer}
height={350}
height={"auto"}
>
<Portofolio_MenuDrawerSection
drawerItems={drawerItemsPortofolio({ id: id as string })}

View File

@@ -21,7 +21,7 @@ export default function Portofolio_MenuDrawerSection({
<MenuDrawerDynamicGrid
data={drawerItems}
columns={4} // Ubah ke 2 jika ingin 2 kolom per baris
onPressItem={handlePress}
onPressItem={handlePress as any}
/>
</>
);

View File

@@ -25,7 +25,7 @@ export async function apiGetPortofolio({ id }: { id: string }) {
export async function apiGetOnePortofolio({ id }: { id: string }) {
try {
const response = await apiConfig.get(`/mobile/portofolio/${id}`);
return response.data;
} catch (error) {
throw error;
@@ -35,10 +35,32 @@ export async function apiGetOnePortofolio({ id }: { id: string }) {
export async function apiDeletePortofolio({ id }: { id: string }) {
try {
const response = await apiConfig.delete(`/mobile/portofolio/${id}`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiUpdatePortofolio({
id,
data,
category,
}: {
id: string;
data: any;
category: "detail" | "medsos" | "logo";
}) {
try {
const response = await apiConfig.put(
`/mobile/portofolio/${id}?category=${category}`,
{
data: data,
}
);
return response.data;
} catch (error) {
throw error;
}
}