Compare commits
21 Commits
api/29-sep
...
api-admin/
| Author | SHA1 | Date | |
|---|---|---|---|
| 0770237fe5 | |||
| 6f4dd79568 | |||
| 9faa0b0f64 | |||
| e05a7c8701 | |||
| f50c5099d8 | |||
| 5f36620988 | |||
| f750d158be | |||
| 0e7b29bb15 | |||
| b293310969 | |||
| a980397640 | |||
| 7c82e8b588 | |||
| 53cdca21fc | |||
| ba878d4d08 | |||
| f3a3acc747 | |||
| a6389174d7 | |||
| 2be4afdcb1 | |||
| aa85e05f79 | |||
| c2acb97a37 | |||
| 250b216a54 | |||
| 5f05d1f7f0 | |||
| 3d8d8568a3 |
@@ -1,9 +1,13 @@
|
|||||||
import { BackButton, ViewWrapper } from "@/components";
|
import { BackButton } from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import PdfViewer from "@/components/_ShareComponent/PdfViewer";
|
||||||
import { FontAwesome } from "@expo/vector-icons";
|
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||||
import { Stack } from "expo-router";
|
import { Stack, useLocalSearchParams } from "expo-router";
|
||||||
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
|
||||||
export default function FileScreen() {
|
export default function FileScreen() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const url = API_STRORAGE.GET({ fileId: id as string });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -12,14 +16,9 @@ export default function FileScreen() {
|
|||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<SafeAreaView style={{ flex: 1 }} edges={["bottom"]}>
|
||||||
<FontAwesome
|
<PdfViewer uri={url} />
|
||||||
name="file-pdf-o"
|
</SafeAreaView>
|
||||||
size={300}
|
|
||||||
style={{ alignSelf: "center" }}
|
|
||||||
color={MainColor.white}
|
|
||||||
/>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ export default function UserLayout() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack screenOptions={HeaderStyles}>
|
<Stack screenOptions={HeaderStyles}>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="waiting-room"
|
name="waiting-room"
|
||||||
options={{
|
options={{
|
||||||
@@ -186,7 +185,7 @@ export default function UserLayout() {
|
|||||||
name="crowdfunding/index"
|
name="crowdfunding/index"
|
||||||
options={{
|
options={{
|
||||||
title: "Crowdfunding",
|
title: "Crowdfunding",
|
||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton path="/home" />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -449,7 +448,7 @@ export default function UserLayout() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="donation/[id]/(transaction-flow)/[transaction]/invoice"
|
name="donation/[id]/(transaction-flow)/[invoiceId]/invoice"
|
||||||
options={{
|
options={{
|
||||||
title: "Invoice",
|
title: "Invoice",
|
||||||
headerLeft: () => (
|
headerLeft: () => (
|
||||||
@@ -463,7 +462,7 @@ export default function UserLayout() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="donation/[id]/(transaction-flow)/[transaction]/process"
|
name="donation/[id]/(transaction-flow)/[invoiceId]/process"
|
||||||
options={{
|
options={{
|
||||||
title: "Proses",
|
title: "Proses",
|
||||||
headerLeft: () => (
|
headerLeft: () => (
|
||||||
@@ -477,14 +476,14 @@ export default function UserLayout() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="donation/[id]/(transaction-flow)/[transaction]/success"
|
name="donation/[id]/(transaction-flow)/[invoiceId]/success"
|
||||||
options={{
|
options={{
|
||||||
title: "Donasi Berhasil",
|
title: "Donasi Berhasil",
|
||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="donation/[id]/(transaction-flow)/[transaction]/failed"
|
name="donation/[id]/(transaction-flow)/[invoiceId]/failed"
|
||||||
options={{
|
options={{
|
||||||
title: "Donasi Gagal",
|
title: "Donasi Gagal",
|
||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import Toast from "react-native-toast-message";
|
|||||||
|
|
||||||
export default function CollaborationEdit() {
|
export default function CollaborationEdit() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
console.log("id :", id);
|
|
||||||
const [data, setData] = useState<any>();
|
const [data, setData] = useState<any>();
|
||||||
const [listMaster, setListMaster] = useState<any[]>([]);
|
const [listMaster, setListMaster] = useState<any[]>([]);
|
||||||
const [loadingData, setLoadingData] = useState(false);
|
const [loadingData, setLoadingData] = useState(false);
|
||||||
|
|||||||
@@ -1,11 +1,41 @@
|
|||||||
import {
|
import {
|
||||||
FloatingButton,
|
FloatingButton,
|
||||||
ViewWrapper
|
LoaderCustom,
|
||||||
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import Donation_BoxPublish from "@/screens/Donation/BoxPublish";
|
import Donation_BoxPublish from "@/screens/Donation/BoxPublish";
|
||||||
import { router } from "expo-router";
|
import { apiDonationGetAll } from "@/service/api-client/api-donation";
|
||||||
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function DonationBeranda() {
|
export default function DonationBeranda() {
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiDonationGetAll({
|
||||||
|
category: "beranda"
|
||||||
|
});
|
||||||
|
console.log("[RES GET ALL]", JSON.stringify(response.data, null, 2));
|
||||||
|
|
||||||
|
setList(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<ViewWrapper
|
||||||
hideFooter
|
hideFooter
|
||||||
@@ -13,9 +43,15 @@ export default function DonationBeranda() {
|
|||||||
<FloatingButton onPress={() => router.push("/donation/create")} />
|
<FloatingButton onPress={() => router.push("/donation/create")} />
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{loadList ? (
|
||||||
<Donation_BoxPublish key={index} id={index.toString()}/>
|
<LoaderCustom />
|
||||||
))}
|
) : _.isEmpty(list) ? (
|
||||||
|
<TextCustom align="center" color="gray">Belum ada donasi</TextCustom>
|
||||||
|
) : (
|
||||||
|
list?.map((item: any, index: number) => (
|
||||||
|
<Donation_BoxPublish data={item} key={index} id={item.id} />
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,76 +1,142 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BadgeCustom,
|
BadgeCustom,
|
||||||
BaseBox,
|
BaseBox,
|
||||||
DummyLandscapeImage,
|
DummyLandscapeImage,
|
||||||
Grid,
|
Grid,
|
||||||
|
LoaderCustom,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { dummyMasterStatusTransaction } from "@/lib/dummy-data/_master/status-transaction";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { router } from "expo-router";
|
import { apiDonationGetAll } from "@/service/api-client/api-donation";
|
||||||
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
|
import { Href, router, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
|
|
||||||
export default function DonationMyDonation() {
|
export default function DonationMyDonation() {
|
||||||
const randomStatusData = Array.from({ length: 10 }, () => {
|
const { user } = useAuth();
|
||||||
const randomIndex = Math.floor(
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
Math.random() * dummyMasterStatusTransaction.length
|
const [loadList, setLoadList] = useState(false);
|
||||||
);
|
|
||||||
return dummyMasterStatusTransaction[randomIndex];
|
|
||||||
});
|
|
||||||
|
|
||||||
const handlePress = (value: string) => {
|
useFocusEffect(
|
||||||
if (value === "menunggu") {
|
useCallback(() => {
|
||||||
router.push(`/donation/${value}/(transaction-flow)/123/invoice`);
|
onLoadData();
|
||||||
} else if (value === "proses") {
|
}, [user?.id])
|
||||||
router.push(`/donation/${value}/(transaction-flow)/123/process`);
|
);
|
||||||
} else if (value === "berhasil") {
|
|
||||||
router.push(`/donation/${value}/(transaction-flow)/123/success`);
|
const onLoadData = async () => {
|
||||||
} else if (value === "gagal") {
|
try {
|
||||||
router.push(`/donation/${value}/(transaction-flow)/123/failed`);
|
setLoadList(true);
|
||||||
|
const response = await apiDonationGetAll({
|
||||||
|
category: "my-donation",
|
||||||
|
authorId: user?.id,
|
||||||
|
});
|
||||||
|
console.log(
|
||||||
|
"[RES GET MY DONATION]",
|
||||||
|
JSON.stringify(response.data, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
setList(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerColor = (status: string) => {
|
||||||
|
if (status === "menunggu") {
|
||||||
|
return "orange";
|
||||||
|
} else if (status === "proses") {
|
||||||
|
return "white";
|
||||||
|
} else if (status === "berhasil") {
|
||||||
|
return "green";
|
||||||
|
} else if (status === "gagal") {
|
||||||
|
return "red";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePress = ({
|
||||||
|
invoiceId,
|
||||||
|
donationId,
|
||||||
|
status,
|
||||||
|
}: {
|
||||||
|
invoiceId: string;
|
||||||
|
donationId: string;
|
||||||
|
status: string;
|
||||||
|
}) => {
|
||||||
|
const url: Href = `../${donationId}/(transaction-flow)/${invoiceId}`;
|
||||||
|
if (status === "menunggu") {
|
||||||
|
router.push(`${url}/invoice`);
|
||||||
|
} else if (status === "proses") {
|
||||||
|
router.push(`${url}/process`);
|
||||||
|
} else if (status === "berhasil") {
|
||||||
|
router.push(`${url}/success`);
|
||||||
|
} else if (status === "gagal") {
|
||||||
|
router.push(`${url}/failed`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper hideFooter>
|
<ViewWrapper hideFooter>
|
||||||
{randomStatusData.map((item, index) => (
|
{loadList ? (
|
||||||
<BaseBox
|
<LoaderCustom />
|
||||||
key={index}
|
) : _.isEmpty(list) ? (
|
||||||
paddingTop={7}
|
<TextCustom align="center" color="gray">
|
||||||
paddingBottom={7}
|
Belum ada transaksi
|
||||||
onPress={() => {
|
</TextCustom>
|
||||||
handlePress(item.value);
|
) : (
|
||||||
}}
|
list?.map((item, index) => (
|
||||||
>
|
<BaseBox
|
||||||
<Grid>
|
key={index}
|
||||||
<Grid.Col span={5}>
|
paddingTop={7}
|
||||||
<DummyLandscapeImage height={100} unClickPath />
|
paddingBottom={7}
|
||||||
</Grid.Col>
|
onPress={() => {
|
||||||
<Grid.Col span={1}>
|
handlePress({
|
||||||
<View />
|
status: _.lowerCase(item.statusInvoice),
|
||||||
</Grid.Col>
|
invoiceId: item.id,
|
||||||
<Grid.Col span={6}>
|
donationId: item.donasiId,
|
||||||
<StackCustom gap={"sm"}>
|
});
|
||||||
<View>
|
}}
|
||||||
<TextCustom truncate>
|
>
|
||||||
Judul Donasi: Lorem ipsum dolor sit amet consectetur
|
<Grid>
|
||||||
adipisicing elit.
|
<Grid.Col span={5}>
|
||||||
|
<DummyLandscapeImage
|
||||||
|
height={100}
|
||||||
|
unClickPath
|
||||||
|
imageId={item.imageId}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={1}>
|
||||||
|
<View />
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={6}>
|
||||||
|
<StackCustom>
|
||||||
|
<TextCustom truncate={2} bold>
|
||||||
|
{item.title || "-"}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
</View>
|
|
||||||
<View>
|
|
||||||
<TextCustom>Donasi Saya</TextCustom>
|
|
||||||
<TextCustom bold color="yellow">
|
<TextCustom bold color="yellow">
|
||||||
Rp. 7.500.000
|
Rp. {formatCurrencyDisplay(item.nominal)}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
</View>
|
|
||||||
<BadgeCustom variant="light" color={item.color} fullWidth>
|
<BadgeCustom
|
||||||
{item.label}
|
variant="light"
|
||||||
</BadgeCustom>
|
color={handlerColor(_.lowerCase(item.statusInvoice))}
|
||||||
</StackCustom>
|
fullWidth
|
||||||
</Grid.Col>
|
>
|
||||||
</Grid>
|
{item.statusInvoice}
|
||||||
</BaseBox>
|
</BadgeCustom>
|
||||||
))}
|
</StackCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</BaseBox>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,48 @@
|
|||||||
import { ScrollableCustom, ViewWrapper } from "@/components";
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import {
|
||||||
|
LoaderCustom,
|
||||||
|
ScrollableCustom,
|
||||||
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
||||||
import Donasi_BoxStatus from "@/screens/Donation/BoxStatus";
|
import Donasi_BoxStatus from "@/screens/Donation/BoxStatus";
|
||||||
import { useState } from "react";
|
import { apiDonationGetByStatus } from "@/service/api-client/api-donation";
|
||||||
|
import { useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function DonationStatus() {
|
export default function DonationStatus() {
|
||||||
|
const { user } = useAuth();
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||||
"publish"
|
"publish"
|
||||||
);
|
);
|
||||||
|
const [listData, setListData] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadList();
|
||||||
|
}, [activeCategory])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiDonationGetByStatus({
|
||||||
|
authorId: user?.id as string,
|
||||||
|
status: activeCategory as string,
|
||||||
|
});
|
||||||
|
|
||||||
|
setListData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
setListData(null);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
const handlePress = (item: any) => {
|
||||||
setActiveCategory(item.value);
|
setActiveCategory(item.value);
|
||||||
@@ -26,13 +62,19 @@ export default function DonationStatus() {
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<ViewWrapper hideFooter headerComponent={scrollComponent}>
|
<ViewWrapper hideFooter headerComponent={scrollComponent}>
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{loadList ? (
|
||||||
<Donasi_BoxStatus
|
<LoaderCustom />
|
||||||
key={index}
|
) : _.isEmpty(listData) ? (
|
||||||
id={index.toString()}
|
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
||||||
status={activeCategory as string}
|
) : (
|
||||||
/>
|
listData?.map((item: any, index: number) => (
|
||||||
))}
|
<Donasi_BoxStatus
|
||||||
|
key={index}
|
||||||
|
data={item}
|
||||||
|
status={activeCategory as string}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
@@ -9,17 +10,120 @@ import {
|
|||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { router } from "expo-router";
|
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||||
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
|
import {
|
||||||
|
apiDonationGetNewsById,
|
||||||
|
apiDonationUpdateNews,
|
||||||
|
} from "@/service/api-client/api-donation";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function DonationEditNews() {
|
export default function DonationEditNews() {
|
||||||
|
const { news } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any>(null);
|
||||||
|
const [image, setImage] = useState<IFileData | null>(null);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [news])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiDonationGetNewsById({
|
||||||
|
id: news as string,
|
||||||
|
category: "get-one",
|
||||||
|
});
|
||||||
|
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmitUpdate = async () => {
|
||||||
|
let newData;
|
||||||
|
if (!data.title || !data.deskripsi) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Judul dan deskripsi harus diisi",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
newData = {
|
||||||
|
title: data?.title,
|
||||||
|
deskripsi: data?.deskripsi,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (image && image?.uri) {
|
||||||
|
const uploadNewImage = await uploadFileService({
|
||||||
|
dirId: DIRECTORY_ID.donasi_kabar,
|
||||||
|
imageUri: image?.uri,
|
||||||
|
});
|
||||||
|
|
||||||
|
newData = {
|
||||||
|
title: data?.title,
|
||||||
|
deskripsi: data?.deskripsi,
|
||||||
|
newImageId: uploadNewImage.data.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await apiDonationUpdateNews({
|
||||||
|
id: news as string,
|
||||||
|
data: newData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengupdate berita",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Berita berhasil diperbarui",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="Upload gambar bersifat opsional untuk melengkapi kabar terkait donasi Anda." />
|
<InformationBox text="Upload gambar bersifat opsional untuk melengkapi kabar terkait donasi Anda." />
|
||||||
<LandscapeFrameUploaded />
|
<LandscapeFrameUploaded
|
||||||
|
image={
|
||||||
|
image
|
||||||
|
? image.uri
|
||||||
|
: data && data.imageId
|
||||||
|
? API_STRORAGE.GET({ fileId: data.imageId })
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push("/(application)/(image)/take-picture/123");
|
pickFile({
|
||||||
|
allowedType: "image",
|
||||||
|
setImageUri(file) {
|
||||||
|
setImage(file);
|
||||||
|
},
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
icon="upload"
|
icon="upload"
|
||||||
>
|
>
|
||||||
@@ -30,6 +134,8 @@ export default function DonationEditNews() {
|
|||||||
label="Judul Berita"
|
label="Judul Berita"
|
||||||
placeholder="Masukan judul berita"
|
placeholder="Masukan judul berita"
|
||||||
required
|
required
|
||||||
|
value={data?.title}
|
||||||
|
onChangeText={(value) => setData({ ...data, title: value })}
|
||||||
/>
|
/>
|
||||||
<TextAreaCustom
|
<TextAreaCustom
|
||||||
label="Deskripsi Berita"
|
label="Deskripsi Berita"
|
||||||
@@ -37,12 +143,16 @@ export default function DonationEditNews() {
|
|||||||
required
|
required
|
||||||
showCount
|
showCount
|
||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
|
value={data?.deskripsi}
|
||||||
|
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
disabled={!data?.title || !data?.deskripsi}
|
||||||
|
isLoading={isLoading}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.back();
|
handlerSubmitUpdate();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
AlertDefaultSystem,
|
AlertDefaultSystem,
|
||||||
BackButton,
|
BackButton,
|
||||||
@@ -12,13 +13,45 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconEdit } from "@/components/_Icon";
|
import { IconEdit } from "@/components/_Icon";
|
||||||
import { IconTrash } from "@/components/_Icon/IconTrash";
|
import { IconTrash } from "@/components/_Icon/IconTrash";
|
||||||
import dayjs from "dayjs";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import {
|
||||||
import { useState } from "react";
|
apiDonationDeleteNews,
|
||||||
|
apiDonationGetNewsById,
|
||||||
|
} from "@/service/api-client/api-donation";
|
||||||
|
import { formatChatTime } from "@/utils/formatChatTime";
|
||||||
|
import {
|
||||||
|
router,
|
||||||
|
Stack,
|
||||||
|
useFocusEffect,
|
||||||
|
useLocalSearchParams,
|
||||||
|
} from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function DonationNews() {
|
export default function DonationNews() {
|
||||||
const { id, news } = useLocalSearchParams();
|
const { user } = useAuth();
|
||||||
|
const { news } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
const [data, setData] = useState<any>(null);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [news])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiDonationGetNewsById({
|
||||||
|
id: news as string,
|
||||||
|
category: "get-one",
|
||||||
|
});
|
||||||
|
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -26,28 +59,28 @@ export default function DonationNews() {
|
|||||||
options={{
|
options={{
|
||||||
title: "Detail Kabar",
|
title: "Detail Kabar",
|
||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
headerRight: () =>
|
||||||
|
user?.id === data?.authorId && (
|
||||||
|
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<TextCustom style={{ alignSelf: "flex-end" }}>
|
<TextCustom style={{ alignSelf: "flex-end" }}>
|
||||||
{dayjs().format("DD MMM YYYY")}
|
{formatChatTime(data?.createdAt)}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
|
|
||||||
<DummyLandscapeImage />
|
{data && data.imageId && (
|
||||||
|
<DummyLandscapeImage imageId={data.imageId} />
|
||||||
|
)}
|
||||||
|
|
||||||
<TextCustom bold size="large" align="center">
|
<TextCustom bold size="large" align="center">
|
||||||
Judul Berita
|
{data?.title || "-"}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
|
|
||||||
<TextCustom>
|
<TextCustom>{data?.deskripsi || "-"}</TextCustom>
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sapiente
|
|
||||||
est id temporibus perferendis eos reiciendis reprehenderit tempora
|
|
||||||
ut quibusdam dolores facilis rerum exercitationem recusandae quis
|
|
||||||
neque, adipisci dolorum, aspernatur labore?
|
|
||||||
</TextCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
@@ -62,12 +95,12 @@ export default function DonationNews() {
|
|||||||
{
|
{
|
||||||
icon: <IconEdit />,
|
icon: <IconEdit />,
|
||||||
label: "Edit Berita",
|
label: "Edit Berita",
|
||||||
path: `/donation/${id}/(news)/${news}/edit-news` as any,
|
path: `/donation/[id]/(news)/${news}/edit-news` as any,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <IconTrash />,
|
icon: <IconTrash />,
|
||||||
label: "Hapus Berita",
|
label: "Hapus Berita",
|
||||||
path: `` as any,
|
path: "",
|
||||||
color: "red",
|
color: "red",
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
@@ -79,7 +112,23 @@ export default function DonationNews() {
|
|||||||
message: "Apakah Anda yakin ingin menghapus berita ini?",
|
message: "Apakah Anda yakin ingin menghapus berita ini?",
|
||||||
textLeft: "Batal",
|
textLeft: "Batal",
|
||||||
textRight: "Hapus",
|
textRight: "Hapus",
|
||||||
onPressRight: () => {
|
onPressRight: async () => {
|
||||||
|
const response = await apiDonationDeleteNews({
|
||||||
|
id: news as string,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal menghapus berita",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Berita berhasil dihapus",
|
||||||
|
});
|
||||||
router.back();
|
router.back();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,17 +9,79 @@ import {
|
|||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { router } from "expo-router";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
|
import { apiDonationCreateNews } from "@/service/api-client/api-donation";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||||
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function DonationAddNews() {
|
export default function DonationAddNews() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState({
|
||||||
|
title: "",
|
||||||
|
deskripsi: "",
|
||||||
|
});
|
||||||
|
const [image, setImage] = useState<IFileData | null>(null);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
let newData: any = { ...data };
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
if (image) {
|
||||||
|
const responseUploadImage = await uploadFileService({
|
||||||
|
dirId: DIRECTORY_ID.donasi_kabar,
|
||||||
|
imageUri: image?.uri,
|
||||||
|
});
|
||||||
|
|
||||||
|
newData = {
|
||||||
|
...newData,
|
||||||
|
imageId: responseUploadImage.data.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await apiDonationCreateNews({
|
||||||
|
id: id as string,
|
||||||
|
data: newData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal menambah berita",
|
||||||
|
});
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Berita berhasil ditambahkan",
|
||||||
|
});
|
||||||
|
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="Upload gambar bersifat opsional untuk melengkapi kabar terkait donasi Anda." />
|
<InformationBox text="Upload gambar bersifat opsional untuk melengkapi kabar terkait donasi Anda." />
|
||||||
<LandscapeFrameUploaded />
|
<LandscapeFrameUploaded image={image?.uri} />
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push("/(application)/(image)/take-picture/123");
|
pickFile({
|
||||||
|
allowedType: "image",
|
||||||
|
setImageUri(file) {
|
||||||
|
setImage(file);
|
||||||
|
},
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
icon="upload"
|
icon="upload"
|
||||||
>
|
>
|
||||||
@@ -30,6 +92,13 @@ export default function DonationAddNews() {
|
|||||||
label="Judul Berita"
|
label="Judul Berita"
|
||||||
placeholder="Masukan judul berita"
|
placeholder="Masukan judul berita"
|
||||||
required
|
required
|
||||||
|
value={data.title}
|
||||||
|
onChangeText={(value) => {
|
||||||
|
setData({
|
||||||
|
...data,
|
||||||
|
title: value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<TextAreaCustom
|
<TextAreaCustom
|
||||||
label="Deskripsi Berita"
|
label="Deskripsi Berita"
|
||||||
@@ -37,12 +106,21 @@ export default function DonationAddNews() {
|
|||||||
required
|
required
|
||||||
showCount
|
showCount
|
||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
|
value={data.deskripsi}
|
||||||
|
onChangeText={(value) => {
|
||||||
|
setData({
|
||||||
|
...data,
|
||||||
|
deskripsi: value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
disabled={!data.title || !data.deskripsi}
|
||||||
|
isLoading={isLoading}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.back();
|
handlerSubmit();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Simpan
|
Simpan
|
||||||
|
|||||||
@@ -1,45 +1,88 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BackButton,
|
BackButton,
|
||||||
BaseBox,
|
BaseBox,
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
Grid,
|
Grid,
|
||||||
MenuDrawerDynamicGrid,
|
LoaderCustom,
|
||||||
TextCustom,
|
MenuDrawerDynamicGrid,
|
||||||
ViewWrapper
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconPlus } from "@/components/_Icon";
|
import { IconPlus } from "@/components/_Icon";
|
||||||
import dayjs from "dayjs";
|
import { apiDonationGetNewsById } from "@/service/api-client/api-donation";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import { formatChatTime } from "@/utils/formatChatTime";
|
||||||
import { useState } from "react";
|
import {
|
||||||
|
router,
|
||||||
|
Stack,
|
||||||
|
useFocusEffect,
|
||||||
|
useLocalSearchParams,
|
||||||
|
} from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function DonationRecapOfNews() {
|
export default function DonationRecapOfNews() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadList();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiDonationGetNewsById({
|
||||||
|
id: id as string,
|
||||||
|
category: "get-all",
|
||||||
|
});
|
||||||
|
|
||||||
|
setList(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
setList([]);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{
|
options={{
|
||||||
title: "Daftar Kabar",
|
title: "Daftar Kabar",
|
||||||
headerLeft: () => <BackButton />, }}
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
{Array.from({ length: 15 }).map((_, index) => (
|
{loadList ? (
|
||||||
<BaseBox key={index} href={`/donation/${id}/(news)/${index}`}>
|
<LoaderCustom />
|
||||||
<Grid>
|
) : _.isEmpty(list) ? (
|
||||||
<Grid.Col span={8}>
|
<TextCustom align="center" color="gray">
|
||||||
<TextCustom truncate bold>
|
Tidak ada kabar
|
||||||
Lorem ipsum dolor, sit amet consectetur adipisicing elit.
|
</TextCustom>
|
||||||
</TextCustom>
|
) : (
|
||||||
</Grid.Col>
|
list?.map((item: any, index: number) => (
|
||||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
<BaseBox key={index} href={`/donation/[id]/(news)/${item.id}`}>
|
||||||
<TextCustom size="small">
|
<Grid>
|
||||||
{dayjs().format("DD MMM YYYY")}
|
<Grid.Col span={8}>
|
||||||
</TextCustom>
|
<TextCustom truncate bold>
|
||||||
</Grid.Col>
|
{item?.title || "-"}
|
||||||
</Grid>
|
</TextCustom>
|
||||||
</BaseBox>
|
</Grid.Col>
|
||||||
))}
|
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
||||||
|
<TextCustom size="small">
|
||||||
|
{formatChatTime(item?.createdAt)}
|
||||||
|
</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</BaseBox>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
<DrawerCustom
|
<DrawerCustom
|
||||||
|
|||||||
@@ -1,21 +1,55 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BackButton,
|
BackButton,
|
||||||
BaseBox,
|
BaseBox,
|
||||||
DotButton,
|
DotButton,
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
Grid,
|
Grid,
|
||||||
|
LoaderCustom,
|
||||||
MenuDrawerDynamicGrid,
|
MenuDrawerDynamicGrid,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconPlus } from "@/components/_Icon";
|
import { IconPlus } from "@/components/_Icon";
|
||||||
import dayjs from "dayjs";
|
import { apiDonationGetNewsById } from "@/service/api-client/api-donation";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import { formatChatTime } from "@/utils/formatChatTime";
|
||||||
import { useState } from "react";
|
import {
|
||||||
|
router,
|
||||||
|
Stack,
|
||||||
|
useFocusEffect,
|
||||||
|
useLocalSearchParams,
|
||||||
|
} from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function DonationRecapOfNews() {
|
export default function DonationRecapOfNews() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadList();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiDonationGetNewsById({
|
||||||
|
id: id as string,
|
||||||
|
category: "get-all",
|
||||||
|
});
|
||||||
|
|
||||||
|
setList(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
setList([]);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -27,20 +61,30 @@ export default function DonationRecapOfNews() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
{Array.from({ length: 15 }).map((_, index) => (
|
{loadList ? (
|
||||||
<BaseBox key={index} href={`/donation/${id}/(news)/${index}`}>
|
<LoaderCustom />
|
||||||
<Grid>
|
) : _.isEmpty(list) ? (
|
||||||
<Grid.Col span={8}>
|
<TextCustom align="center" color="gray">
|
||||||
<TextCustom truncate bold>
|
Tidak ada kabar
|
||||||
Lorem ipsum dolor, sit amet consectetur adipisicing elit.
|
</TextCustom>
|
||||||
</TextCustom>
|
) : (
|
||||||
</Grid.Col>
|
list?.map((item: any, index: number) => (
|
||||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
<BaseBox key={index} href={`/donation/[id]/(news)/${item.id}`}>
|
||||||
<TextCustom size="small">{dayjs().format("DD MMM YYYY")}</TextCustom>
|
<Grid>
|
||||||
</Grid.Col>
|
<Grid.Col span={8}>
|
||||||
</Grid>
|
<TextCustom truncate bold>
|
||||||
</BaseBox>
|
{item?.title || "-"}
|
||||||
))}
|
</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
||||||
|
<TextCustom size="small">
|
||||||
|
{formatChatTime(item?.createdAt)}
|
||||||
|
</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</BaseBox>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
<DrawerCustom
|
<DrawerCustom
|
||||||
|
|||||||
@@ -0,0 +1,222 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import {
|
||||||
|
BaseBox,
|
||||||
|
ButtonCenteredOnly,
|
||||||
|
ButtonCustom,
|
||||||
|
Grid,
|
||||||
|
InformationBox,
|
||||||
|
Spacing,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import CopyButton from "@/components/Button/CoyButton";
|
||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
|
import {
|
||||||
|
apiDonationGetInvoiceById,
|
||||||
|
apiDonationUpdateInvoice,
|
||||||
|
} from "@/service/api-client/api-donation";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
|
import pickFile from "@/utils/pickFile";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
|
export default function DonationInvoice() {
|
||||||
|
const { invoiceId } = useLocalSearchParams();
|
||||||
|
console.log("invoiceId", invoiceId);
|
||||||
|
const [data, setData] = useState<any>(null);
|
||||||
|
const [image, setImage] = useState<any>(null);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [invoiceId])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiDonationGetInvoiceById({
|
||||||
|
id: invoiceId as string,
|
||||||
|
});
|
||||||
|
console.log("[RESPONSE]", JSON.stringify(response, null, 2));
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerUpdateInvoice = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const responseUploadImage = await uploadFileService({
|
||||||
|
dirId: DIRECTORY_ID.donasi_bukti_transfer,
|
||||||
|
imageUri: image?.uri,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("[RESPONSE UPLOAD IMAGE]", responseUploadImage);
|
||||||
|
|
||||||
|
if (!responseUploadImage?.data?.id) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengunggah bukti transfer",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileId = responseUploadImage?.data?.id;
|
||||||
|
|
||||||
|
const response = await apiDonationUpdateInvoice({
|
||||||
|
id: invoiceId as string,
|
||||||
|
fileId: fileId,
|
||||||
|
status: "proses",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("[RESPONSE UPDATE]", JSON.stringify(response, null, 2));
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengunggah bukti transfer",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Berhasil mengunggah bukti transfer",
|
||||||
|
});
|
||||||
|
router.replace(`/donation/[id]/(transaction-flow)/${invoiceId}/process`);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper>
|
||||||
|
<StackCustom>
|
||||||
|
<InformationBox
|
||||||
|
text={`Mohon transfer donasi anda ke rekening dibawah`}
|
||||||
|
/>
|
||||||
|
<BaseBox>
|
||||||
|
<StackCustom gap={"xs"}>
|
||||||
|
<TextCustom bold>
|
||||||
|
BANK: {data?.DonasiMaster_Bank?.name}
|
||||||
|
</TextCustom>
|
||||||
|
{/* <TextCustom>{data?.DonasiMaster_Bank?.accountName}</TextCustom> */}
|
||||||
|
<Spacing height={10} />
|
||||||
|
|
||||||
|
<BaseBox backgroundColor={MainColor.soft_darkblue}>
|
||||||
|
<Grid containerStyle={{ justifyContent: "center" }}>
|
||||||
|
<Grid.Col
|
||||||
|
span={8}
|
||||||
|
style={{
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextCustom size="xlarge" bold color="yellow">
|
||||||
|
{data?.DonasiMaster_Bank?.norek}
|
||||||
|
</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col
|
||||||
|
span={4}
|
||||||
|
style={{
|
||||||
|
alignItems: "flex-end",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CopyButton textToCopy={data?.DonasiMaster_Bank?.norek} />
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</BaseBox>
|
||||||
|
</StackCustom>
|
||||||
|
</BaseBox>
|
||||||
|
|
||||||
|
<BaseBox>
|
||||||
|
<StackCustom gap={"xs"}>
|
||||||
|
<TextCustom>Jumlah Transaksi</TextCustom>
|
||||||
|
|
||||||
|
<Spacing height={10} />
|
||||||
|
|
||||||
|
<BaseBox backgroundColor={MainColor.soft_darkblue}>
|
||||||
|
<Grid containerStyle={{ justifyContent: "center" }}>
|
||||||
|
<Grid.Col
|
||||||
|
span={8}
|
||||||
|
style={{
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextCustom size="xlarge" bold color="yellow">
|
||||||
|
Rp. {formatCurrencyDisplay(data?.nominal) || "-"}
|
||||||
|
</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col
|
||||||
|
span={4}
|
||||||
|
style={{
|
||||||
|
alignItems: "flex-end",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CopyButton textToCopy={data?.nominal} />
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</BaseBox>
|
||||||
|
</StackCustom>
|
||||||
|
</BaseBox>
|
||||||
|
|
||||||
|
<BaseBox>
|
||||||
|
<StackCustom>
|
||||||
|
<TextCustom bold align="center" size={"small"} color="gray">
|
||||||
|
Upload bukti transfer anda.
|
||||||
|
</TextCustom>
|
||||||
|
{image ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
gap: 10,
|
||||||
|
paddingInline: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextCustom bold align="center" truncate>
|
||||||
|
{image?.name}
|
||||||
|
</TextCustom>
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
<ButtonCenteredOnly
|
||||||
|
onPress={() => {
|
||||||
|
pickFile({
|
||||||
|
allowedType: "image",
|
||||||
|
setImageUri(file) {
|
||||||
|
setImage(file);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
icon="upload"
|
||||||
|
>
|
||||||
|
Upload
|
||||||
|
</ButtonCenteredOnly>
|
||||||
|
</StackCustom>
|
||||||
|
</BaseBox>
|
||||||
|
|
||||||
|
<ButtonCustom
|
||||||
|
disabled={!image}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={() => {
|
||||||
|
handlerUpdateInvoice();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</ButtonCustom>
|
||||||
|
</StackCustom>
|
||||||
|
<Spacing />
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,13 +1,6 @@
|
|||||||
import {
|
import { BaseBox, StackCustom, TextCustom, ViewWrapper } from "@/components";
|
||||||
BaseBox,
|
import MoneyTransferAnimation from "@/components/_ShareComponent/MoneyTransferAnimation";
|
||||||
Grid,
|
import { View } from "react-native";
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
import { ActivityIndicator } from "react-native";
|
|
||||||
|
|
||||||
export default function DonationProcess() {
|
export default function DonationProcess() {
|
||||||
return (
|
return (
|
||||||
@@ -16,13 +9,16 @@ export default function DonationProcess() {
|
|||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<TextCustom align="center" bold>
|
<TextCustom align="center" bold>
|
||||||
Admin sedang memproses transaksi donasimu
|
Admin sedang memvalidasi data dan bukti transfer anda. Mohon
|
||||||
|
tunggu proses ini selesai.
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
<ActivityIndicator size="large" color={MainColor.yellow} />
|
<View style={{ alignItems: "center", justifyContent: "center" }}>
|
||||||
|
<MoneyTransferAnimation />
|
||||||
|
</View>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
<BaseBox>
|
{/* <BaseBox>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col span={10} style={{ justifyContent: "center" }}>
|
<Grid.Col span={10} style={{ justifyContent: "center" }}>
|
||||||
<TextCustom size="small">
|
<TextCustom size="small">
|
||||||
@@ -38,7 +34,7 @@ export default function DonationProcess() {
|
|||||||
/>
|
/>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
</BaseBox>
|
</BaseBox> */}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
import {
|
|
||||||
BaseBox,
|
|
||||||
ButtonCenteredOnly,
|
|
||||||
ButtonCustom,
|
|
||||||
Grid,
|
|
||||||
InformationBox,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
|
||||||
|
|
||||||
export default function DonationInvoice() {
|
|
||||||
const { id, transaction } = useLocalSearchParams();
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper>
|
|
||||||
<StackCustom>
|
|
||||||
<InformationBox text={`Mohon transfer donasi anda ke rekening dibawah dengan Id: ${transaction}`} />
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<TextCustom>Nama BANK</TextCustom>
|
|
||||||
<TextCustom>Nama Penerima</TextCustom>
|
|
||||||
<Spacing height={10} />
|
|
||||||
|
|
||||||
<BaseBox backgroundColor={MainColor.soft_darkblue}>
|
|
||||||
<Grid containerStyle={{ justifyContent: "center" }}>
|
|
||||||
<Grid.Col
|
|
||||||
span={8}
|
|
||||||
style={{
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextCustom size="xlarge" bold color="yellow">
|
|
||||||
4567898765433567
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col
|
|
||||||
span={4}
|
|
||||||
style={{
|
|
||||||
alignItems: "flex-end",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ButtonCustom>Salin</ButtonCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<TextCustom>Jumlah Transaksi</TextCustom>
|
|
||||||
|
|
||||||
<Spacing height={10} />
|
|
||||||
|
|
||||||
<BaseBox backgroundColor={MainColor.soft_darkblue}>
|
|
||||||
<Grid containerStyle={{ justifyContent: "center" }}>
|
|
||||||
<Grid.Col
|
|
||||||
span={8}
|
|
||||||
style={{
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextCustom size="xlarge" bold color="yellow">
|
|
||||||
Rp. 1.000.000
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col
|
|
||||||
span={4}
|
|
||||||
style={{
|
|
||||||
alignItems: "flex-end",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ButtonCustom>Salin</ButtonCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom>
|
|
||||||
<TextCustom>Upload bukti transfer anda.</TextCustom>
|
|
||||||
<ButtonCenteredOnly
|
|
||||||
onPress={() => {
|
|
||||||
router.push("/(application)/(image)/take-picture/123");
|
|
||||||
}}
|
|
||||||
icon="upload"
|
|
||||||
>
|
|
||||||
Upload
|
|
||||||
</ButtonCenteredOnly>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
<ButtonCustom
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/donation/${id}/(transaction-flow)/process`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Saya Sudah Transfer
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
|
||||||
<Spacing />
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -9,15 +9,43 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
|
import { LOCAL_STORAGE_KEY } from "@/constants/local-storage-key";
|
||||||
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
export default function InvestmentInputDonation() {
|
export default function InvestmentInputDonation() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
const [nominal, setNominal] = useState<number>(0);
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
try {
|
||||||
|
await AsyncStorage.setItem(
|
||||||
|
LOCAL_STORAGE_KEY.transactionDonation,
|
||||||
|
JSON.stringify({ nominal: nominal.toString() })
|
||||||
|
);
|
||||||
|
router.replace(`/donation/${id}/select-bank`);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayJumlah = formatCurrencyDisplay(nominal);
|
||||||
|
|
||||||
|
const handleChangeCurrency = (text: string) => {
|
||||||
|
const numeric = text.replace(/\D/g, "");
|
||||||
|
setNominal(Number(numeric));
|
||||||
|
};
|
||||||
|
|
||||||
const bottomComponent = (
|
const bottomComponent = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
onPress={() => router.replace(`/donation/${id}/select-bank`)}
|
disabled={nominal < 10000 || nominal === 0}
|
||||||
|
onPress={() => {
|
||||||
|
handlerSubmit();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Lanjutan
|
Lanjutan
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
@@ -27,7 +55,7 @@ export default function InvestmentInputDonation() {
|
|||||||
<>
|
<>
|
||||||
<ViewWrapper footerComponent={bottomComponent}>
|
<ViewWrapper footerComponent={bottomComponent}>
|
||||||
{listData.map((item, i) => (
|
{listData.map((item, i) => (
|
||||||
<BaseBox key={i}>
|
<BaseBox key={i} onPress={() => setNominal(item.value)}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col span={8}>
|
<Grid.Col span={8}>
|
||||||
<TextCustom bold size="large">
|
<TextCustom bold size="large">
|
||||||
@@ -48,9 +76,12 @@ export default function InvestmentInputDonation() {
|
|||||||
|
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
|
keyboardType="numeric"
|
||||||
label="Nominal lainnya"
|
label="Nominal lainnya"
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
iconLeft="Rp."
|
iconLeft="Rp."
|
||||||
|
value={displayJumlah}
|
||||||
|
onChangeText={(value) => handleChangeCurrency(value)}
|
||||||
/>
|
/>
|
||||||
<TextCustom size="small" color="gray">
|
<TextCustom size="small" color="gray">
|
||||||
Minimal donasi Rp. 10.000
|
Minimal donasi Rp. 10.000
|
||||||
|
|||||||
@@ -5,24 +5,84 @@ import {
|
|||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom";
|
import { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom";
|
||||||
import { dummyMasterBank } from "@/lib/dummy-data/_master/bank";
|
import { LOCAL_STORAGE_KEY } from "@/constants/local-storage-key";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { apiDonationCreateInvoice } from "@/service/api-client/api-donation";
|
||||||
|
import { apiMasterBank } from "@/service/api-client/api-master";
|
||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
import { useState } from "react";
|
import _ from "lodash";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function DonationSelectBank() {
|
export default function DonationSelectBank() {
|
||||||
const { id, transaction } = useLocalSearchParams();
|
const { user } = useAuth();
|
||||||
const [value, setValue] = useState<any | number>("");
|
const { id } = useLocalSearchParams();
|
||||||
|
const [select, setSelect] = useState<any | number>("");
|
||||||
|
const [listBank, setListBank] = useState<any>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadListBank();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadListBank = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiMasterBank();
|
||||||
|
|
||||||
|
setListBank(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
setListBank([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const dataStorage = await AsyncStorage.getItem(
|
||||||
|
LOCAL_STORAGE_KEY.transactionDonation
|
||||||
|
);
|
||||||
|
|
||||||
|
if (dataStorage) {
|
||||||
|
const storage = JSON.parse(dataStorage);
|
||||||
|
const newData = {
|
||||||
|
...storage,
|
||||||
|
bankId: select,
|
||||||
|
authorId: user?.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await apiDonationCreateInvoice({
|
||||||
|
id: id as string,
|
||||||
|
data: newData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
const invoiceId = response.data.id;
|
||||||
|
|
||||||
|
await AsyncStorage.removeItem(LOCAL_STORAGE_KEY.transactionDonation);
|
||||||
|
|
||||||
|
router.replace(
|
||||||
|
`/(application)/(user)/donation/[id]/(transaction-flow)/${invoiceId}/invoice`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log("[FAILED]", response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonSubmit = () => {
|
const buttonSubmit = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
onPress={() =>
|
isLoading={isLoading}
|
||||||
router.replace(
|
disabled={!select}
|
||||||
`/(application)/(user)/donation/${id}/(transaction-flow)/${transaction}/invoice`
|
onPress={() => handlerSubmit()}
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
Pilih
|
Pilih
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
@@ -32,12 +92,14 @@ export default function DonationSelectBank() {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<ViewWrapper footerComponent={buttonSubmit()}>
|
<ViewWrapper footerComponent={buttonSubmit()}>
|
||||||
<RadioGroup value={value} onChange={setValue}>
|
<RadioGroup value={select} onChange={setSelect}>
|
||||||
{dummyMasterBank.map((item) => (
|
{_.isEmpty(listBank)
|
||||||
<BaseBox key={item.name}>
|
? []
|
||||||
<RadioCustom label={item.name} value={item.code} />
|
: listBank?.map((item: any, index: number) => (
|
||||||
</BaseBox>
|
<BaseBox key={index}>
|
||||||
))}
|
<RadioCustom label={item.namaBank} value={item.id} />
|
||||||
|
</BaseBox>
|
||||||
|
))}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BackButton,
|
BackButton,
|
||||||
DotButton,
|
DotButton,
|
||||||
@@ -14,16 +15,43 @@ import Donation_ButtonStatusSection from "@/screens/Donation/ButtonStatusSection
|
|||||||
import Donation_ComponentBoxDetailData from "@/screens/Donation/ComponentBoxDetailData";
|
import Donation_ComponentBoxDetailData from "@/screens/Donation/ComponentBoxDetailData";
|
||||||
import Donation_ComponentStoryFunrising from "@/screens/Donation/ComponentStoryFunrising";
|
import Donation_ComponentStoryFunrising from "@/screens/Donation/ComponentStoryFunrising";
|
||||||
import Donation_ProgressSection from "@/screens/Donation/ProgressSection";
|
import Donation_ProgressSection from "@/screens/Donation/ProgressSection";
|
||||||
|
import { apiDonationGetOne } from "@/service/api-client/api-donation";
|
||||||
import { FontAwesome6 } from "@expo/vector-icons";
|
import { FontAwesome6 } from "@expo/vector-icons";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import {
|
||||||
|
router,
|
||||||
|
Stack,
|
||||||
|
useFocusEffect,
|
||||||
|
useLocalSearchParams,
|
||||||
|
} from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function DonasiDetailStatus() {
|
export default function DonasiDetailStatus() {
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||||
|
|
||||||
|
const [data, setData] = useState<any>();
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiDonationGetOne({
|
||||||
|
id: id as string,
|
||||||
|
category: "permanent",
|
||||||
|
});
|
||||||
|
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handlePress = (item: IMenuDrawerItem) => {
|
const handlePress = (item: IMenuDrawerItem) => {
|
||||||
console.log("PATH ", item.path);
|
console.log("PATH ", item.path);
|
||||||
router.navigate(item.path as any);
|
router.navigate(item.path as any);
|
||||||
@@ -46,13 +74,22 @@ export default function DonasiDetailStatus() {
|
|||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<Donation_ComponentBoxDetailData
|
<Donation_ComponentBoxDetailData
|
||||||
|
data={data}
|
||||||
bottomSection={
|
bottomSection={
|
||||||
status === "publish" && <Donation_ProgressSection id={id as string} />
|
status === "publish" && (
|
||||||
|
<Donation_ProgressSection id={id as string} />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Donation_ComponentStoryFunrising id={id as string} />
|
<Donation_ComponentStoryFunrising
|
||||||
|
id={id as string}
|
||||||
|
dataStory={data?.CeritaDonasi}
|
||||||
|
/>
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<Donation_ButtonStatusSection status={status as string} />
|
<Donation_ButtonStatusSection
|
||||||
|
id={id as string}
|
||||||
|
status={status as string}
|
||||||
|
/>
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,42 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
DummyLandscapeImage,
|
DummyLandscapeImage,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { useLocalSearchParams } from "expo-router";
|
import { apiDonationGetOne } from "@/service/api-client/api-donation";
|
||||||
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function DonationDetailStory() {
|
export default function DonationDetailStory() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any>();
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiDonationGetOne({
|
||||||
|
id: id as string,
|
||||||
|
category: "permanent",
|
||||||
|
});
|
||||||
|
|
||||||
|
setData(response.data.CeritaDonasi);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<TextCustom>
|
<TextCustom>{data?.pembukaan || "-"}</TextCustom>
|
||||||
Lorem {id} ipsum dolor, sit amet consectetur adipisicing elit. Fuga
|
<DummyLandscapeImage imageId={data?.imageId} />
|
||||||
quasi nam nesciunt nisi corporis alias modi, pariatur sit totam rem
|
<TextCustom>{data?.cerita || "-"}</TextCustom>
|
||||||
fugiat ex similique magni, aliquam maiores officiis iure at adipisci.
|
|
||||||
</TextCustom>
|
|
||||||
<DummyLandscapeImage />
|
|
||||||
<TextCustom>
|
|
||||||
Lorem {id} ipsum dolor, sit amet consectetur adipisicing elit. Fuga
|
|
||||||
quasi nam nesciunt nisi corporis alias modi, pariatur sit totam rem
|
|
||||||
fugiat ex similique magni, aliquam maiores officiis iure at adipisci.
|
|
||||||
</TextCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,80 @@
|
|||||||
import { ViewWrapper, StackCustom, InformationBox, TextInputCustom, Spacing, ButtonCustom } from "@/components";
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { router } from "expo-router";
|
import {
|
||||||
|
ViewWrapper,
|
||||||
|
StackCustom,
|
||||||
|
InformationBox,
|
||||||
|
TextInputCustom,
|
||||||
|
Spacing,
|
||||||
|
ButtonCustom,
|
||||||
|
} from "@/components";
|
||||||
|
import {
|
||||||
|
apiDonationGetOne,
|
||||||
|
apiDonationUpdateData,
|
||||||
|
} from "@/service/api-client/api-donation";
|
||||||
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function DonationEditRekening() {
|
export default function DonationEditRekening() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState({
|
||||||
|
namaBank: "",
|
||||||
|
rekening: "",
|
||||||
|
});
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiDonationGetOne({
|
||||||
|
id: id as string,
|
||||||
|
category: "permanent",
|
||||||
|
});
|
||||||
|
|
||||||
|
const resData = response.data;
|
||||||
|
console.log("[RESPONSE]", JSON.stringify(resData, null, 2));
|
||||||
|
|
||||||
|
setData({
|
||||||
|
namaBank: resData.namaBank,
|
||||||
|
rekening: resData.rekening,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmitUpdate = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
const response = await apiDonationUpdateData({
|
||||||
|
id: id as string,
|
||||||
|
data: data,
|
||||||
|
category: "edit-bank-account",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengupdate data bank",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Data bank berhasil diupdate",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
@@ -10,17 +83,22 @@ export default function DonationEditRekening() {
|
|||||||
label="Nama Bank"
|
label="Nama Bank"
|
||||||
placeholder="Masukkan nama bank"
|
placeholder="Masukkan nama bank"
|
||||||
required
|
required
|
||||||
|
value={data.namaBank}
|
||||||
|
onChangeText={(value) => setData({ ...data, namaBank: value })}
|
||||||
/>
|
/>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
label="Nomor Rekening"
|
label="Nomor Rekening"
|
||||||
placeholder="Masukkan nomor rekening"
|
placeholder="Masukkan nomor rekening"
|
||||||
required
|
required
|
||||||
|
value={data.rekening}
|
||||||
|
onChangeText={(value) => setData({ ...data, rekening: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.back();
|
handlerSubmitUpdate();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
|
|||||||
@@ -1,16 +1,97 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
ViewWrapper
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { router } from "expo-router";
|
import API_IMAGE from "@/constants/api-storage";
|
||||||
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
|
import {
|
||||||
|
apiDonationGetOne,
|
||||||
|
apiDonationUpdateData,
|
||||||
|
} from "@/service/api-client/api-donation";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import pickFile from "@/utils/pickFile";
|
||||||
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function DonationEditStory() {
|
export default function DonationEditStory() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any>();
|
||||||
|
const [imageStory, setImageStory] = useState<string | null>(null);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiDonationGetOne({
|
||||||
|
id: id as string,
|
||||||
|
category: "permanent",
|
||||||
|
});
|
||||||
|
|
||||||
|
setData(response.data.CeritaDonasi);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmitUpdate = async () => {
|
||||||
|
let newData;
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
newData = {
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (imageStory) {
|
||||||
|
const responseUploadImageDonasi = await uploadFileService({
|
||||||
|
imageUri: imageStory,
|
||||||
|
dirId: DIRECTORY_ID.donasi_cerita_image,
|
||||||
|
});
|
||||||
|
|
||||||
|
newData = {
|
||||||
|
...data,
|
||||||
|
newImageId: responseUploadImageDonasi.data.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await apiDonationUpdateData({
|
||||||
|
id: id as string,
|
||||||
|
data: newData,
|
||||||
|
category: "edit-story",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("[RESPONSE]", JSON.stringify(response, null, 2));
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal membuat donasi",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Donasi berhasil disimpan",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
@@ -21,12 +102,23 @@ export default function DonationEditStory() {
|
|||||||
required
|
required
|
||||||
showCount
|
showCount
|
||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
|
value={data?.pembukaan}
|
||||||
|
onChangeText={(value) => setData({ ...data, pembukaan: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LandscapeFrameUploaded />
|
<LandscapeFrameUploaded
|
||||||
|
image={
|
||||||
|
imageStory ? imageStory : API_IMAGE.GET({ fileId: data?.imageId })
|
||||||
|
}
|
||||||
|
/>
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push("/(application)/(image)/take-picture/123");
|
pickFile({
|
||||||
|
allowedType: "image",
|
||||||
|
setImageUri: ({ uri }) => {
|
||||||
|
setImageStory(uri);
|
||||||
|
},
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
icon="upload"
|
icon="upload"
|
||||||
>
|
>
|
||||||
@@ -39,12 +131,15 @@ export default function DonationEditStory() {
|
|||||||
required
|
required
|
||||||
showCount
|
showCount
|
||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
|
value={data?.cerita}
|
||||||
|
onChangeText={(value) => setData({ ...data, cerita: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing height={40} />
|
<Spacing height={40} />
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.back();
|
handlerSubmitUpdate();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
|
|||||||
@@ -1,78 +1,275 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
SelectCustom,
|
LoaderCustom,
|
||||||
Spacing,
|
SelectCustom,
|
||||||
StackCustom,
|
Spacing,
|
||||||
TextInputCustom,
|
StackCustom,
|
||||||
ViewWrapper,
|
TextInputCustom,
|
||||||
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { dummyDonasiDurasi } from "@/lib/dummy-data/donasi/durasi";
|
import API_IMAGE from "@/constants/api-storage";
|
||||||
import { dummyDonasiKategori } from "@/lib/dummy-data/donasi/kategori";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { router } from "expo-router";
|
import {
|
||||||
|
apiDonationGetOne,
|
||||||
|
apiDonationUpdateData,
|
||||||
|
} from "@/service/api-client/api-donation";
|
||||||
|
import { apiMasterDonation } from "@/service/api-client/api-master";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
|
import pickFile from "@/utils/pickFile";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
|
interface IEditDonation {
|
||||||
|
donasiMaster_KategoriId: string;
|
||||||
|
donasiMaster_DurasiId: string;
|
||||||
|
title: string;
|
||||||
|
target: string;
|
||||||
|
imageId: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function DonationEdit() {
|
export default function DonationEdit() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<IEditDonation>({
|
||||||
|
donasiMaster_DurasiId: "",
|
||||||
|
donasiMaster_KategoriId: "",
|
||||||
|
title: "",
|
||||||
|
target: "",
|
||||||
|
imageId: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const [image, setImage] = useState<string | null>(null);
|
||||||
|
const [listCategory, setListCategory] = useState<any[]>([]);
|
||||||
|
const [listDuration, setListDuration] = useState<any[]>([]);
|
||||||
|
const [loadList, setLoadList] = useState<boolean>(false);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const displayTarget = formatCurrencyDisplay(data?.target);
|
||||||
|
const handleChangeCurrency = (field: keyof typeof data) => (text: string) => {
|
||||||
|
const numeric = text.replace(/\D/g, "");
|
||||||
|
setData((prev: any) => ({ ...prev, [field]: numeric }));
|
||||||
|
};
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
onLoadList();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiDonationGetOne({
|
||||||
|
id: id as string,
|
||||||
|
category: "permanent",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setData({
|
||||||
|
donasiMaster_DurasiId: response.data.donasiMaster_DurasiId,
|
||||||
|
donasiMaster_KategoriId: response.data.donasiMaster_KategoriId,
|
||||||
|
title: response.data.title,
|
||||||
|
target: response.data.target,
|
||||||
|
imageId: response.data.imageId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiMasterDonation({ category: "" });
|
||||||
|
|
||||||
|
setListCategory(response.data.category);
|
||||||
|
setListDuration(response.data.duration);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(["ERROR"], error);
|
||||||
|
setListCategory([]);
|
||||||
|
setListDuration([]);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateData = async () => {
|
||||||
|
if (
|
||||||
|
!data.donasiMaster_DurasiId ||
|
||||||
|
!data.donasiMaster_KategoriId ||
|
||||||
|
!data.title ||
|
||||||
|
!data.target ||
|
||||||
|
!data.imageId
|
||||||
|
) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Harap lengkapi data",
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmitUpdate = async () => {
|
||||||
|
const isValid = await validateData();
|
||||||
|
if (!isValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let newData;
|
||||||
|
|
||||||
|
newData = {
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
if (image && image) {
|
||||||
|
const uploadNewImage = await uploadFileService({
|
||||||
|
dirId: DIRECTORY_ID.donasi_image,
|
||||||
|
imageUri: image,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!uploadFileService) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengunggah gambar",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
newData = {
|
||||||
|
...data,
|
||||||
|
newImageId: uploadNewImage.data.id,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await apiDonationUpdateData({
|
||||||
|
id: id as string,
|
||||||
|
data: newData,
|
||||||
|
category: "edit-donation",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: response.message,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Donasi berhasil diperbarui",
|
||||||
|
});
|
||||||
|
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR UPDATE DONASI]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom gap={"xs"}>
|
<InformationBox text="Lengkapi semua data di bawah untuk selanjutnya mengisi cerita penggalangan dana." />
|
||||||
<InformationBox text="Lengkapi semua data di bawah untuk selanjutnya mengisi cerita penggalangan dana." />
|
{!data || loadList ? (
|
||||||
|
<LoaderCustom />
|
||||||
|
) : (
|
||||||
|
<StackCustom gap={"xs"}>
|
||||||
|
<TextInputCustom
|
||||||
|
label="Judul Donasi"
|
||||||
|
placeholder="Masukkan Judul Donasi"
|
||||||
|
required
|
||||||
|
value={data?.title}
|
||||||
|
onChangeText={(value) => setData({ ...data, title: value })}
|
||||||
|
/>
|
||||||
|
<TextInputCustom
|
||||||
|
iconLeft="Rp."
|
||||||
|
label="Target Donasi"
|
||||||
|
placeholder="Masukkan Target Donasi"
|
||||||
|
required
|
||||||
|
keyboardType="numeric"
|
||||||
|
value={displayTarget}
|
||||||
|
onChangeText={handleChangeCurrency("target")}
|
||||||
|
/>
|
||||||
|
|
||||||
<TextInputCustom
|
<LandscapeFrameUploaded
|
||||||
label="Judul Donasi"
|
image={image ? image : API_IMAGE.GET({ fileId: data?.imageId })}
|
||||||
placeholder="Masukkan Judul Donasi"
|
/>
|
||||||
required
|
<ButtonCenteredOnly
|
||||||
/>
|
onPress={() => {
|
||||||
<TextInputCustom
|
pickFile({
|
||||||
label="Target Donasi"
|
setImageUri: ({ uri }) => {
|
||||||
placeholder="Masukkan Target Donasi"
|
setImage(uri);
|
||||||
required
|
},
|
||||||
keyboardType="numeric"
|
allowedType: "image",
|
||||||
/>
|
});
|
||||||
|
}}
|
||||||
|
icon="upload"
|
||||||
|
>
|
||||||
|
Upload
|
||||||
|
</ButtonCenteredOnly>
|
||||||
|
<Spacing />
|
||||||
|
|
||||||
<LandscapeFrameUploaded />
|
<SelectCustom
|
||||||
<ButtonCenteredOnly
|
data={
|
||||||
onPress={() => {
|
_.isEmpty(listCategory)
|
||||||
router.push("/(application)/(image)/take-picture/123");
|
? []
|
||||||
}}
|
: listCategory?.map((item) => ({
|
||||||
icon="upload"
|
label: item.name,
|
||||||
>
|
value: item.id,
|
||||||
Upload
|
}))
|
||||||
</ButtonCenteredOnly>
|
}
|
||||||
<Spacing />
|
label="Pilih Kategori Donasi"
|
||||||
|
placeholder="Pilih Kategori Donasi"
|
||||||
|
required
|
||||||
|
value={data?.donasiMaster_KategoriId}
|
||||||
|
onChange={(value: any) =>
|
||||||
|
setData({ ...data, donasiMaster_KategoriId: value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<SelectCustom
|
<SelectCustom
|
||||||
data={dummyDonasiKategori.map((item) => ({
|
data={
|
||||||
label: item.label,
|
_.isEmpty(listDuration)
|
||||||
value: item.value,
|
? []
|
||||||
}))}
|
: listDuration?.map((item) => ({
|
||||||
onChange={(value) => console.log(value)}
|
label: item.name + " hari",
|
||||||
label="Pilih Kategori Donasi"
|
value: item.id,
|
||||||
placeholder="Pilih Kategori Donasi"
|
}))
|
||||||
required
|
}
|
||||||
/>
|
label="Pilih Durasi Donasi"
|
||||||
|
placeholder="Pilih Durasi Donasi"
|
||||||
|
required
|
||||||
|
value={data?.donasiMaster_DurasiId}
|
||||||
|
onChange={(value: any) =>
|
||||||
|
setData({ ...data, donasiMaster_DurasiId: value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<SelectCustom
|
<Spacing />
|
||||||
data={dummyDonasiDurasi.map((item) => ({
|
<ButtonCustom
|
||||||
label: item.label,
|
isLoading={isLoading}
|
||||||
value: item.value,
|
onPress={() => {
|
||||||
}))}
|
handlerSubmitUpdate();
|
||||||
onChange={(value) => console.log(value)}
|
}}
|
||||||
label="Pilih Durasi Donasi"
|
>
|
||||||
placeholder="Pilih Durasi Donasi"
|
Update
|
||||||
required
|
</ButtonCustom>
|
||||||
/>
|
</StackCustom>
|
||||||
|
)}
|
||||||
<Spacing />
|
|
||||||
<ButtonCustom
|
|
||||||
onPress={() => {
|
|
||||||
router.back();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BackButton,
|
BackButton,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
@@ -9,24 +10,54 @@ import {
|
|||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconNews } from "@/components/_Icon";
|
import { IconNews } from "@/components/_Icon";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import Donation_ComponentBoxDetailData from "@/screens/Donation/ComponentBoxDetailData";
|
import Donation_ComponentBoxDetailData from "@/screens/Donation/ComponentBoxDetailData";
|
||||||
import Donation_ComponentInfoFundrising from "@/screens/Donation/ComponentInfoFundrising";
|
import Donation_ComponentInfoFundrising from "@/screens/Donation/ComponentInfoFundrising";
|
||||||
import Donation_ComponentStoryFunrising from "@/screens/Donation/ComponentStoryFunrising";
|
import Donation_ComponentStoryFunrising from "@/screens/Donation/ComponentStoryFunrising";
|
||||||
import Donation_ProgressSection from "@/screens/Donation/ProgressSection";
|
import Donation_ProgressSection from "@/screens/Donation/ProgressSection";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import { apiDonationGetOne } from "@/service/api-client/api-donation";
|
||||||
import { useState } from "react";
|
import {
|
||||||
|
router,
|
||||||
|
Stack,
|
||||||
|
useFocusEffect,
|
||||||
|
useLocalSearchParams,
|
||||||
|
} from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function DonasiDetailBeranda() {
|
export default function DonasiDetailBeranda() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
console.log("ID ", id);
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
|
||||||
|
const [data, setData] = useState<any>();
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiDonationGetOne({
|
||||||
|
id: id as string,
|
||||||
|
category: "permanent",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("[RES GET ONE]", JSON.stringify(response.data, null, 2));
|
||||||
|
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonSection = (
|
const buttonSection = (
|
||||||
<>
|
<>
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
onPress={() =>
|
onPress={() => router.navigate(`/donation/${id}/(transaction-flow)`)}
|
||||||
router.navigate(`/donation/${id}/(transaction-flow)`)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
Donasi
|
Donasi
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
@@ -40,16 +71,23 @@ export default function DonasiDetailBeranda() {
|
|||||||
options={{
|
options={{
|
||||||
title: `Detail Donasi`,
|
title: `Detail Donasi`,
|
||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
headerRight: () =>
|
||||||
|
user?.id === data?.Author?.id ? (
|
||||||
|
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||||
|
) : null,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper footerComponent={buttonSection}>
|
<ViewWrapper footerComponent={buttonSection}>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<Donation_ComponentBoxDetailData
|
<Donation_ComponentBoxDetailData
|
||||||
|
data={data}
|
||||||
bottomSection={<Donation_ProgressSection id={id as string} />}
|
bottomSection={<Donation_ProgressSection id={id as string} />}
|
||||||
/>
|
/>
|
||||||
<Donation_ComponentInfoFundrising id={id as string} />
|
<Donation_ComponentInfoFundrising dataAuthor={data?.Author} />
|
||||||
<Donation_ComponentStoryFunrising id={id as string} />
|
<Donation_ComponentStoryFunrising
|
||||||
|
id={id as string}
|
||||||
|
dataStory={data?.CeritaDonasi}
|
||||||
|
/>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,68 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
AvatarCustom,
|
AvatarComp,
|
||||||
BaseBox,
|
BaseBox,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
CenterCustom,
|
|
||||||
Grid,
|
Grid,
|
||||||
|
LoaderCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper
|
ViewWrapper
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import Donation_BoxPublish from "@/screens/Donation/BoxPublish";
|
import Donation_BoxPublish from "@/screens/Donation/BoxPublish";
|
||||||
import React from "react";
|
import { apiDonationFundrising } from "@/service/api-client/api-donation";
|
||||||
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import React, { useCallback, useState } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
|
||||||
export default function DonationInformationFunrising() {
|
export default function DonationInformationFunrising() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any>();
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiDonationFundrising({ id: id as string });
|
||||||
|
|
||||||
|
setData(response?.data?.user);
|
||||||
|
setList(response?.data?.donasi);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
||||||
<CenterCustom>
|
<View
|
||||||
<AvatarCustom size="lg" />
|
style={{
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AvatarComp size="lg" fileId={data?.Profile?.imageId} />
|
||||||
<TextCustom bold size="large" truncate>
|
<TextCustom bold size="large" truncate>
|
||||||
@Username
|
@{data?.username}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
</CenterCustom>
|
</View>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
||||||
<ButtonCustom href={`/profile/1234`}>
|
<ButtonCustom href={`/profile/${data?.Profile?.id}`}>
|
||||||
Kunjungi Profile
|
Kunjungi Profile
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
@@ -35,9 +71,15 @@ export default function DonationInformationFunrising() {
|
|||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
|
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{loadList ? (
|
||||||
<Donation_BoxPublish key={index} id={index.toString()} />
|
<LoaderCustom />
|
||||||
))}
|
) : _.isEmpty(list) ? (
|
||||||
|
<TextCustom align="center" color="gray" size="small">Belum ada data</TextCustom>
|
||||||
|
) : (
|
||||||
|
list?.map((item: any, index: number) => (
|
||||||
|
<Donation_BoxPublish key={index} id={item?.id} data={item} />
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
@@ -9,9 +10,107 @@ import {
|
|||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { router } from "expo-router";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import {
|
||||||
|
apiDonationCreate,
|
||||||
|
apiDonationGetOne,
|
||||||
|
} from "@/service/api-client/api-donation";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import pickFile from "@/utils/pickFile";
|
||||||
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function DonationCreateStory() {
|
export default function DonationCreateStory() {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [temporary, setTemporary] = useState<any>();
|
||||||
|
const [data, setData] = useState({
|
||||||
|
pembukaan: "",
|
||||||
|
cerita: "",
|
||||||
|
namaBank: "",
|
||||||
|
rekening: "",
|
||||||
|
});
|
||||||
|
const [imageStory, setImageStory] = useState<string | null>(null);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiDonationGetOne({
|
||||||
|
id: id as string,
|
||||||
|
category: "temporary",
|
||||||
|
});
|
||||||
|
|
||||||
|
setTemporary(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
if (_.values(data).includes("")) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Harap isi semua data",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const responseUploadImageDonasi = await uploadFileService({
|
||||||
|
imageUri: imageStory,
|
||||||
|
dirId: DIRECTORY_ID.donasi_cerita_image,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newData = {
|
||||||
|
// Data Donasi
|
||||||
|
temporaryId: temporary?.id,
|
||||||
|
authorId: user?.id,
|
||||||
|
title: temporary?.title,
|
||||||
|
target: temporary?.target,
|
||||||
|
donasiMaster_KategoriId: temporary?.donasiMaster_KategoriId,
|
||||||
|
donasiMaster_DurasiId: temporary?.donasiMaster_DurasiId,
|
||||||
|
imageId: temporary?.imageId,
|
||||||
|
// Data Bank
|
||||||
|
namaBank: data.namaBank,
|
||||||
|
rekening: data.rekening,
|
||||||
|
// Data Cerita
|
||||||
|
imageCeritaId: responseUploadImageDonasi.data.id,
|
||||||
|
pembukaan: data.pembukaan,
|
||||||
|
cerita: data.cerita,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await apiDonationCreate({
|
||||||
|
data: newData,
|
||||||
|
category: "permanent",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal membuat donasi",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Donasi berhasil disimpan",
|
||||||
|
});
|
||||||
|
router.replace("/donation/status");
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
@@ -22,6 +121,8 @@ export default function DonationCreateStory() {
|
|||||||
required
|
required
|
||||||
showCount
|
showCount
|
||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
|
value={data.pembukaan}
|
||||||
|
onChangeText={(value) => setData({ ...data, pembukaan: value })}
|
||||||
/>
|
/>
|
||||||
<TextAreaCustom
|
<TextAreaCustom
|
||||||
label="Tujuan Donasi"
|
label="Tujuan Donasi"
|
||||||
@@ -29,12 +130,19 @@ export default function DonationCreateStory() {
|
|||||||
required
|
required
|
||||||
showCount
|
showCount
|
||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
|
value={data.cerita}
|
||||||
|
onChangeText={(value) => setData({ ...data, cerita: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LandscapeFrameUploaded />
|
<LandscapeFrameUploaded image={imageStory || ""} />
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push("/(application)/(image)/take-picture/123");
|
pickFile({
|
||||||
|
allowedType: "image",
|
||||||
|
setImageUri: ({ uri }) => {
|
||||||
|
setImageStory(uri);
|
||||||
|
},
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
icon="upload"
|
icon="upload"
|
||||||
>
|
>
|
||||||
@@ -47,17 +155,23 @@ export default function DonationCreateStory() {
|
|||||||
label="Nama Bank"
|
label="Nama Bank"
|
||||||
placeholder="Masukkan nama bank"
|
placeholder="Masukkan nama bank"
|
||||||
required
|
required
|
||||||
|
value={data.namaBank}
|
||||||
|
onChangeText={(value) => setData({ ...data, namaBank: value })}
|
||||||
/>
|
/>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
label="Nomor Rekening"
|
label="Nomor Rekening"
|
||||||
placeholder="Masukkan nomor rekening"
|
placeholder="Masukkan nomor rekening"
|
||||||
required
|
required
|
||||||
|
keyboardType="numeric"
|
||||||
|
value={data.rekening}
|
||||||
|
onChangeText={(value) => setData({ ...data, rekening: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.replace(`/donation/(tabs)/status`);
|
handlerSubmit();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Simpan
|
Simpan
|
||||||
|
|||||||
@@ -3,17 +3,127 @@ import {
|
|||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
|
LoaderCustom,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { dummyDonasiDurasi } from "@/lib/dummy-data/donasi/durasi";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { dummyDonasiKategori } from "@/lib/dummy-data/donasi/kategori";
|
import { apiDonationCreate } from "@/service/api-client/api-donation";
|
||||||
import { router } from "expo-router";
|
import { apiMasterDonation } from "@/service/api-client/api-master";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
|
import pickFile from "@/utils/pickFile";
|
||||||
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function DonationCreate() {
|
export default function DonationCreate() {
|
||||||
|
const [listCategory, setListCategory] = useState<any[]>([]);
|
||||||
|
const [listDuration, setListDuration] = useState<any[]>([]);
|
||||||
|
const [loadList, setLoadList] = useState<boolean>(false);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
const [image, setImage] = useState<string | null>(null);
|
||||||
|
const [data, setData] = useState({
|
||||||
|
kategoriId: "",
|
||||||
|
title: "",
|
||||||
|
target: "",
|
||||||
|
durasiId: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const displayTarget = formatCurrencyDisplay(data.target);
|
||||||
|
const handleChangeCurrency = (field: keyof typeof data) => (text: string) => {
|
||||||
|
const numeric = text.replace(/\D/g, "");
|
||||||
|
setData((prev) => ({ ...prev, [field]: numeric }));
|
||||||
|
};
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadList();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiMasterDonation({ category: "" });
|
||||||
|
|
||||||
|
setListCategory(response.data.category);
|
||||||
|
setListDuration(response.data.duration);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(["ERROR"], error);
|
||||||
|
setListCategory([]);
|
||||||
|
setListDuration([]);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateData = () => {
|
||||||
|
if (!data.title || !data.target || !data.durasiId || !data.kategoriId) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Harap isi semua data",
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
if (!validateData()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const responseUploadImage = await uploadFileService({
|
||||||
|
imageUri: image,
|
||||||
|
dirId: DIRECTORY_ID.donasi_image,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!responseUploadImage.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengunggah gambar",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageId = responseUploadImage.data.id;
|
||||||
|
|
||||||
|
const newData = {
|
||||||
|
title: data.title,
|
||||||
|
target: data.target,
|
||||||
|
durasiId: data.durasiId,
|
||||||
|
kategoriId: data.kategoriId,
|
||||||
|
imageId: imageId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await apiDonationCreate({
|
||||||
|
data: newData,
|
||||||
|
category: "temporary",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal membuat donasi",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
router.push(`/donation/create-story?id=${response.data.id}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
@@ -23,18 +133,28 @@ export default function DonationCreate() {
|
|||||||
label="Judul Donasi"
|
label="Judul Donasi"
|
||||||
placeholder="Masukkan Judul Donasi"
|
placeholder="Masukkan Judul Donasi"
|
||||||
required
|
required
|
||||||
|
value={data.title}
|
||||||
|
onChangeText={(value) => setData({ ...data, title: value })}
|
||||||
/>
|
/>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
|
iconLeft="Rp."
|
||||||
label="Target Donasi"
|
label="Target Donasi"
|
||||||
placeholder="Masukkan Target Donasi"
|
placeholder="Masukkan Target Donasi"
|
||||||
required
|
required
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
|
value={displayTarget}
|
||||||
|
onChangeText={handleChangeCurrency("target")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<LandscapeFrameUploaded />
|
<LandscapeFrameUploaded image={image || ""} />
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push("/(application)/(image)/take-picture/123");
|
pickFile({
|
||||||
|
allowedType: "image",
|
||||||
|
setImageUri: ({ uri }) => {
|
||||||
|
setImage(uri);
|
||||||
|
},
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
icon="upload"
|
icon="upload"
|
||||||
>
|
>
|
||||||
@@ -42,31 +162,52 @@ export default function DonationCreate() {
|
|||||||
</ButtonCenteredOnly>
|
</ButtonCenteredOnly>
|
||||||
<Spacing />
|
<Spacing />
|
||||||
|
|
||||||
<SelectCustom
|
{loadList ? (
|
||||||
data={dummyDonasiKategori.map((item) => ({
|
<LoaderCustom />
|
||||||
label: item.label,
|
) : (
|
||||||
value: item.value,
|
<SelectCustom
|
||||||
}))}
|
data={
|
||||||
onChange={(value) => console.log(value)}
|
_.isEmpty(listCategory)
|
||||||
label="Pilih Kategori Donasi"
|
? []
|
||||||
placeholder="Pilih Kategori Donasi"
|
: listCategory?.map((item: any) => ({
|
||||||
required
|
label: item.name,
|
||||||
/>
|
value: item.id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
label="Pilih Kategori Donasi"
|
||||||
|
placeholder="Pilih Kategori Donasi"
|
||||||
|
required
|
||||||
|
value={data.kategoriId}
|
||||||
|
onChange={(value: any) => setData({ ...data, kategoriId: value })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{loadList ? (
|
||||||
|
<LoaderCustom />
|
||||||
|
) : (
|
||||||
|
<SelectCustom
|
||||||
|
data={
|
||||||
|
_.isEmpty(listDuration)
|
||||||
|
? []
|
||||||
|
: listDuration?.map((item: any) => ({
|
||||||
|
label: item.name + `${" hari"}`,
|
||||||
|
value: item.id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
label="Pilih Durasi Donasi"
|
||||||
|
placeholder="Pilih Durasi Donasi"
|
||||||
|
required
|
||||||
|
value={data.durasiId}
|
||||||
|
onChange={(value: any) => setData({ ...data, durasiId: value })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<SelectCustom
|
|
||||||
data={dummyDonasiDurasi.map((item) => ({
|
|
||||||
label: item.label,
|
|
||||||
value: item.value,
|
|
||||||
}))}
|
|
||||||
onChange={(value) => console.log(value)}
|
|
||||||
label="Pilih Durasi Donasi"
|
|
||||||
placeholder="Pilih Durasi Donasi"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.replace("/donation/create-story");
|
handlerSubmit();
|
||||||
|
// router.push(`/donation/create-story?id=${"dasdsadsa"}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Selanjutnya
|
Selanjutnya
|
||||||
|
|||||||
@@ -2,18 +2,46 @@ import {
|
|||||||
BaseBox,
|
BaseBox,
|
||||||
FloatingButton,
|
FloatingButton,
|
||||||
Grid,
|
Grid,
|
||||||
|
LoaderCustom,
|
||||||
ProgressCustom,
|
ProgressCustom,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
|
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||||
|
import { apiInvestmentGetAll } from "@/service/api-client/api-investment";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { router } from "expo-router";
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
|
|
||||||
export default function InvestmentBursa() {
|
export default function InvestmentBursa() {
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadingList, setLoadingList] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadList();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadingList(true);
|
||||||
|
const response = await apiInvestmentGetAll();
|
||||||
|
console.log("[DATA LIST]", JSON.stringify(response.data, null, 2));
|
||||||
|
setList(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadingList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<ViewWrapper
|
||||||
hideFooter
|
hideFooter
|
||||||
@@ -21,40 +49,77 @@ export default function InvestmentBursa() {
|
|||||||
<FloatingButton onPress={() => router.push("/investment/create")} />
|
<FloatingButton onPress={() => router.push("/investment/create")} />
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{loadingList ? (
|
||||||
<BaseBox key={index} paddingTop={7} paddingBottom={7} href={`/investment/${index}`}>
|
<LoaderCustom />
|
||||||
<Grid>
|
) : _.isEmpty(list) ? (
|
||||||
<Grid.Col span={5}>
|
<TextCustom>Belum ada data</TextCustom>
|
||||||
<Image
|
) : (
|
||||||
source={DUMMY_IMAGE.background}
|
list?.map((item: any, index: number) => (
|
||||||
style={{ width: "auto", height: 100, borderRadius: 10 }}
|
<BaseBox
|
||||||
/>
|
key={index}
|
||||||
</Grid.Col>
|
paddingTop={7}
|
||||||
<Grid.Col span={1}>
|
paddingBottom={7}
|
||||||
<View />
|
href={`/investment/${item.id}`}
|
||||||
</Grid.Col>
|
>
|
||||||
<Grid.Col span={6}>
|
<Grid>
|
||||||
<StackCustom>
|
<Grid.Col span={5}>
|
||||||
<TextCustom truncate={2}>
|
<Image
|
||||||
Title here : Lorem ipsum dolor sit amet consectetur
|
source={
|
||||||
adipisicing elit. Omnis, exercitationem, sequi enim quod
|
item && item.imageId
|
||||||
distinctio maiores laudantium amet, quidem atque repellat sit
|
? API_STRORAGE.GET({ fileId: item.imageId })
|
||||||
vitae qui aliquam est veritatis laborum eum voluptatum totam!
|
: DUMMY_IMAGE.background
|
||||||
</TextCustom>
|
}
|
||||||
<ProgressCustom value={index % 5 * 20} size="lg" />
|
style={{ width: "auto", height: 100, borderRadius: 10 }}
|
||||||
<TextCustom>
|
/>
|
||||||
Sisa waktu: {dayjs().diff(dayjs(), "day")} hari
|
</Grid.Col>
|
||||||
</TextCustom>
|
<Grid.Col span={1}>
|
||||||
</StackCustom>
|
<View />
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
<Grid.Col span={6}>
|
||||||
</BaseBox>
|
<StackCustom>
|
||||||
))}
|
<TextCustom truncate={2}>{item.title}</TextCustom>
|
||||||
|
<ProgressCustom
|
||||||
|
label={`${item.progress}%`}
|
||||||
|
value={item.progress}
|
||||||
|
size="lg"
|
||||||
|
/>
|
||||||
|
{Number(item?.pencarianInvestor) -
|
||||||
|
dayjs().diff(dayjs(item.countDown), "days") <=
|
||||||
|
0 ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Ionicons
|
||||||
|
name="alert-circle-outline"
|
||||||
|
size={16}
|
||||||
|
color="red"
|
||||||
|
/>
|
||||||
|
<TextCustom color="red" size="small">
|
||||||
|
Periode Investasi Selesai
|
||||||
|
</TextCustom>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<TextCustom>
|
||||||
|
Sisa waktu:{" "}
|
||||||
|
{Number(item?.pencarianInvestor) -
|
||||||
|
dayjs().diff(dayjs(item.countDown), "days")}{" "}
|
||||||
|
hari
|
||||||
|
</TextCustom>
|
||||||
|
)}
|
||||||
|
</StackCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</BaseBox>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// <View style={{ padding: 20, gap: 16 }}>
|
// <View style={{ padding: 20, gap: 16 }}>
|
||||||
// <TextCustom>Progress 70%</TextCustom>
|
// <TextCustom>Progress 70%</TextCustom>
|
||||||
// <ProgressCustom value={70} color="primary" size="md" />
|
// <ProgressCustom value={70} color="primary" size="md" />
|
||||||
|
|||||||
@@ -1,81 +1,123 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BadgeCustom,
|
BadgeCustom,
|
||||||
BaseBox,
|
BaseBox,
|
||||||
Grid,
|
Grid,
|
||||||
|
LoaderCustom,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { dummyMasterStatusTransaction } from "@/lib/dummy-data/_master/status-transaction";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { apiInvestmentGetInvoice } from "@/service/api-client/api-investment";
|
||||||
import { GStyles } from "@/styles/global-styles";
|
import { GStyles } from "@/styles/global-styles";
|
||||||
import dayjs from "dayjs";
|
import { formatChatTime } from "@/utils/formatChatTime";
|
||||||
import { router } from "expo-router";
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
|
|
||||||
export default function InvestmentTransaction() {
|
export default function InvestmentTransaction() {
|
||||||
const randomStatusData = Array.from({ length: 10 }, () => {
|
const { user } = useAuth();
|
||||||
const randomIndex = Math.floor(
|
const [list, setList] = useState<any>([]);
|
||||||
Math.random() * dummyMasterStatusTransaction.length
|
const [loadList, setLoadList] = useState<boolean>(false);
|
||||||
);
|
|
||||||
return dummyMasterStatusTransaction[randomIndex];
|
|
||||||
});
|
|
||||||
|
|
||||||
const handlePress = (value: string) => {
|
useFocusEffect(
|
||||||
if (value === "menunggu") {
|
useCallback(() => {
|
||||||
router.push(`/investment/${value}/(transaction-flow)/invoice`);
|
onLoadList();
|
||||||
} else if (value === "proses") {
|
}, [user?.id])
|
||||||
router.push(`/investment/${value}/(transaction-flow)/process`);
|
);
|
||||||
} else if (value === "berhasil") {
|
|
||||||
router.push(`/investment/${value}/(transaction-flow)/success`);
|
const onLoadList = async () => {
|
||||||
} else if (value === "gagal") {
|
try {
|
||||||
router.push(`/investment/${value}/(transaction-flow)/failed`);
|
setLoadList(true);
|
||||||
|
const response = await apiInvestmentGetInvoice({
|
||||||
|
authorId: user?.id as string,
|
||||||
|
category: "transaction",
|
||||||
|
});
|
||||||
|
console.log("[RESPONSE LIST]", JSON.stringify(response.data, null, 2));
|
||||||
|
setList(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerColor = (status: string) => {
|
||||||
|
if (status === "menunggu") {
|
||||||
|
return "orange";
|
||||||
|
} else if (status === "proses") {
|
||||||
|
return "white";
|
||||||
|
} else if (status === "berhasil") {
|
||||||
|
return "green";
|
||||||
|
} else if (status === "gagal") {
|
||||||
|
return "red";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePress = ({ id, status }: { id: string; status: string }) => {
|
||||||
|
if (status === "menunggu") {
|
||||||
|
router.push(`/investment/${id}/(transaction-flow)/invoice`);
|
||||||
|
} else if (status === "proses") {
|
||||||
|
router.push(`/investment/${id}/(transaction-flow)/process`);
|
||||||
|
} else if (status === "berhasil") {
|
||||||
|
router.push(`/investment/${id}/(transaction-flow)/success`);
|
||||||
|
} else if (status === "gagal") {
|
||||||
|
router.push(`/investment/${id}/(transaction-flow)/failed`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper hideFooter>
|
<ViewWrapper hideFooter>
|
||||||
{randomStatusData.map((item, i) => (
|
{loadList ? (
|
||||||
<BaseBox
|
<LoaderCustom />
|
||||||
key={i}
|
) : _.isEmpty(list) ? (
|
||||||
paddingTop={7}
|
<TextCustom>Tidak ada data</TextCustom>
|
||||||
paddingBottom={7}
|
) : (
|
||||||
onPress={() => {
|
list.map((item: any, i: number) => (
|
||||||
handlePress(item.value);
|
<BaseBox
|
||||||
}}
|
key={i}
|
||||||
>
|
paddingTop={7}
|
||||||
<Grid>
|
paddingBottom={7}
|
||||||
<Grid.Col span={6}>
|
onPress={() => {
|
||||||
<StackCustom gap={"xs"}>
|
handlePress({
|
||||||
<TextCustom truncate>
|
id: item.id,
|
||||||
Title Investment: Lorem ipsum dolor sit amet consectetur
|
status: _.lowerCase(item.statusInvoice),
|
||||||
adipisicing elit. Am culpa excepturi deleniti soluta animi
|
});
|
||||||
porro amet ducimus.
|
}}
|
||||||
</TextCustom>
|
>
|
||||||
<TextCustom color="gray" size="small">
|
<Grid>
|
||||||
{dayjs().format("DD/MM/YYYY")}
|
<Grid.Col span={6}>
|
||||||
</TextCustom>
|
<StackCustom gap={"xs"}>
|
||||||
</StackCustom>
|
<TextCustom truncate>{item?.title || "-"}</TextCustom>
|
||||||
</Grid.Col>
|
<TextCustom color="gray" size="small">
|
||||||
<Grid.Col span={1}>
|
{formatChatTime(item?.createdAt)}
|
||||||
<View />
|
</TextCustom>
|
||||||
</Grid.Col>
|
</StackCustom>
|
||||||
<Grid.Col span={5} style={{ alignItems: "flex-end" }}>
|
</Grid.Col>
|
||||||
<StackCustom gap={"xs"}>
|
<Grid.Col span={1}>
|
||||||
<TextCustom bold truncate>
|
<View />
|
||||||
Rp. 7.500.000
|
</Grid.Col>
|
||||||
</TextCustom>
|
<Grid.Col span={5} style={{ alignItems: "flex-end" }}>
|
||||||
<BadgeCustom
|
<StackCustom gap={"xs"}>
|
||||||
variant="light"
|
<TextCustom bold truncate>
|
||||||
color={item.color}
|
Rp. {formatCurrencyDisplay(item?.nominal) || "-"}
|
||||||
style={GStyles.alignSelfFlexEnd}
|
</TextCustom>
|
||||||
>
|
<BadgeCustom
|
||||||
{item.label}
|
variant="light"
|
||||||
</BadgeCustom>
|
color={handlerColor(_.lowerCase(item.statusInvoice))}
|
||||||
</StackCustom>
|
style={GStyles.alignSelfFlexEnd}
|
||||||
</Grid.Col>
|
>
|
||||||
</Grid>
|
{item?.statusInvoice || "-"}
|
||||||
</BaseBox>
|
</BadgeCustom>
|
||||||
))}
|
</StackCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</BaseBox>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,17 +7,78 @@ import {
|
|||||||
InformationBox,
|
InformationBox,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
|
import { apiInvestmentUpsertDocument } from "@/service/api-client/api-investment";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import pickFile from "@/utils/pickFile";
|
||||||
import { FontAwesome5 } from "@expo/vector-icons";
|
import { FontAwesome5 } from "@expo/vector-icons";
|
||||||
import { router } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function InvestmentAddDocument() {
|
export default function InvestmentAddDocument() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState({
|
||||||
|
title: "",
|
||||||
|
});
|
||||||
|
const [pdf, setPdf] = useState<any>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const responseUploadFile = await uploadFileService({
|
||||||
|
dirId: DIRECTORY_ID.investasi_dokumen,
|
||||||
|
imageUri: pdf.uri,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!responseUploadFile.success) {
|
||||||
|
throw new Error(responseUploadFile.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newData = {
|
||||||
|
title: data.title,
|
||||||
|
fileId: responseUploadFile.data.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await apiInvestmentUpsertDocument({
|
||||||
|
id: id as string,
|
||||||
|
data: newData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Data berhasil disimpan",
|
||||||
|
});
|
||||||
|
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal menyimpan data",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonFooter = (
|
const buttonFooter = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom onPress={() => router.back()}>Simpan</ButtonCustom>
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
|
disabled={!pdf || data.title.length <= 0}
|
||||||
|
onPress={handlerSubmit}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</ButtonCustom>
|
||||||
</BoxButtonOnFooter>
|
</BoxButtonOnFooter>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -31,22 +92,33 @@ export default function InvestmentAddDocument() {
|
|||||||
label="Judul Dokumen"
|
label="Judul Dokumen"
|
||||||
placeholder="Masukan judul dokumen"
|
placeholder="Masukan judul dokumen"
|
||||||
required
|
required
|
||||||
|
value={data.title}
|
||||||
|
onChangeText={(value) => setData({ ...data, title: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<CenterCustom>
|
<CenterCustom>
|
||||||
<FontAwesome5
|
{pdf ? (
|
||||||
name="file-pdf"
|
<TextCustom truncate>{pdf.name}</TextCustom>
|
||||||
size={30}
|
) : (
|
||||||
color={MainColor.disabled}
|
<FontAwesome5
|
||||||
/>
|
name="file-pdf"
|
||||||
|
size={30}
|
||||||
|
color={MainColor.disabled}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</CenterCustom>
|
</CenterCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
icon="upload"
|
icon="upload"
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
router.push("/(application)/(image)/take-picture/123")
|
pickFile({
|
||||||
|
allowedType: "pdf",
|
||||||
|
setPdfUri(file) {
|
||||||
|
setPdf(file);
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Upload
|
Upload
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
@@ -7,17 +8,99 @@ import {
|
|||||||
InformationBox,
|
InformationBox,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { FontAwesome5 } from "@expo/vector-icons";
|
import {
|
||||||
import { router } from "expo-router";
|
apiInvestmentGetDocument,
|
||||||
|
apiInvestmentUpsertDocument,
|
||||||
|
} from "@/service/api-client/api-investment";
|
||||||
|
import { deleteFileService, uploadFileService } from "@/service/upload-service";
|
||||||
|
import pickFile from "@/utils/pickFile";
|
||||||
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function InvestmentEditDocument() {
|
export default function InvestmentEditDocument() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any>(null);
|
||||||
|
const [pdf, setPdf] = useState<any>(null);
|
||||||
|
const [titleFile, setTitleFile] = useState<string>("");
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiInvestmentGetDocument({
|
||||||
|
id: id as string,
|
||||||
|
category: "one-document",
|
||||||
|
});
|
||||||
|
setData(response.data);
|
||||||
|
setTitleFile(response.data.title);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerUpdate = async () => {
|
||||||
|
const prevFileId = data.fileId;
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const responseUploadFile = await uploadFileService({
|
||||||
|
dirId: DIRECTORY_ID.investasi_dokumen,
|
||||||
|
imageUri: pdf.uri,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!responseUploadFile.success) {
|
||||||
|
throw new Error(responseUploadFile.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newData = {
|
||||||
|
title: data.title,
|
||||||
|
fileId: responseUploadFile.data.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await apiInvestmentUpsertDocument({
|
||||||
|
id: id as string,
|
||||||
|
data: newData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
const delPrevFile = await deleteFileService({
|
||||||
|
id: prevFileId,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("[DEL PREV FILE]", delPrevFile);
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Data berhasil diupdate",
|
||||||
|
});
|
||||||
|
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengupdate data",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonFooter = (
|
const buttonFooter = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
|
<ButtonCustom isLoading={isLoading} disabled={!pdf} onPress={handlerUpdate}>
|
||||||
|
Update
|
||||||
|
</ButtonCustom>
|
||||||
</BoxButtonOnFooter>
|
</BoxButtonOnFooter>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -31,22 +114,29 @@ export default function InvestmentEditDocument() {
|
|||||||
label="Judul Dokumen"
|
label="Judul Dokumen"
|
||||||
placeholder="Masukan judul dokumen"
|
placeholder="Masukan judul dokumen"
|
||||||
required
|
required
|
||||||
|
value={data?.title}
|
||||||
|
onChangeText={(value) => setData({ ...data, title: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<CenterCustom>
|
<CenterCustom>
|
||||||
<FontAwesome5
|
{pdf ? (
|
||||||
name="file-pdf"
|
<TextCustom truncate>{pdf.name}</TextCustom>
|
||||||
size={30}
|
) : (
|
||||||
color={MainColor.disabled}
|
<TextCustom truncate>{_.snakeCase(titleFile || "")}.pdf</TextCustom>
|
||||||
/>
|
)}
|
||||||
</CenterCustom>
|
</CenterCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
icon="upload"
|
icon="upload"
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
router.push("/(application)/(image)/take-picture/123")
|
pickFile({
|
||||||
|
allowedType: "pdf",
|
||||||
|
setPdfUri(file) {
|
||||||
|
setPdf(file);
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Upload
|
Upload
|
||||||
|
|||||||
@@ -1,16 +1,58 @@
|
|||||||
import { ViewWrapper } from "@/components";
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import { LoaderCustom, TextCustom, ViewWrapper } from "@/components";
|
||||||
import Investment_BoxDetailDocument from "@/screens/Invesment/Document/RecapBoxDetail";
|
import Investment_BoxDetailDocument from "@/screens/Invesment/Document/RecapBoxDetail";
|
||||||
|
import { apiInvestmentGetDocument } from "@/service/api-client/api-investment";
|
||||||
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function InvestmentListOfDocument() {
|
export default function InvestmentListOfDocument() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
console.log("ID >> ", id);
|
||||||
|
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadListDocument();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadListDocument = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiInvestmentGetDocument({
|
||||||
|
id: id as string,
|
||||||
|
category: "all-document",
|
||||||
|
});
|
||||||
|
|
||||||
|
setList(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
setList([]);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{loadList ? (
|
||||||
<Investment_BoxDetailDocument
|
<LoaderCustom />
|
||||||
key={index}
|
) : _.isEmpty(list) ? (
|
||||||
title={`Judul Dokumen ${index + 1}`}
|
<TextCustom align="center" color="gray">
|
||||||
href={`/(file)/${index + 1}`}
|
Tidak ada data
|
||||||
/>
|
</TextCustom>
|
||||||
))}
|
) : (
|
||||||
|
list?.map((item: any, index: number) => (
|
||||||
|
<Investment_BoxDetailDocument
|
||||||
|
key={index}
|
||||||
|
title={item.title}
|
||||||
|
href={`/(file)/${item.fileId}`}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,90 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
AlertDefaultSystem,
|
AlertDefaultSystem,
|
||||||
BackButton,
|
BackButton,
|
||||||
DotButton,
|
DotButton,
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
|
LoaderCustom,
|
||||||
MenuDrawerDynamicGrid,
|
MenuDrawerDynamicGrid,
|
||||||
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconEdit } from "@/components/_Icon";
|
import { IconEdit } from "@/components/_Icon";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
import Investment_BoxDetailDocument from "@/screens/Invesment/Document/RecapBoxDetail";
|
import Investment_BoxDetailDocument from "@/screens/Invesment/Document/RecapBoxDetail";
|
||||||
|
import {
|
||||||
|
apiInvestmentDeleteDocument,
|
||||||
|
apiInvestmentGetDocument,
|
||||||
|
} from "@/service/api-client/api-investment";
|
||||||
import { AntDesign, Ionicons } from "@expo/vector-icons";
|
import { AntDesign, Ionicons } from "@expo/vector-icons";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import {
|
||||||
import { useState } from "react";
|
router,
|
||||||
|
Stack,
|
||||||
|
useFocusEffect,
|
||||||
|
useLocalSearchParams,
|
||||||
|
} from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function InvestmentRecapOfDocument() {
|
export default function InvestmentRecapOfDocument() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
const [openDrawerBox, setOpenDrawerBox] = useState(false);
|
const [openDrawerBox, setOpenDrawerBox] = useState(false);
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
const [selectId, setSelectId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadListDocument();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadListDocument = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiInvestmentGetDocument({
|
||||||
|
id: id as string,
|
||||||
|
category: "all-document",
|
||||||
|
});
|
||||||
|
|
||||||
|
setList(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
setList([]);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerDeleteDocument = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiInvestmentDeleteDocument({
|
||||||
|
id: selectId as string,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Data berhasil dihapus",
|
||||||
|
});
|
||||||
|
setList((prev: any[] | null) => {
|
||||||
|
if (!prev) return null;
|
||||||
|
return prev.filter((item: any) => item.id !== selectId);
|
||||||
|
});
|
||||||
|
setOpenDrawerBox(false);
|
||||||
|
setSelectId(null);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal menghapus data",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -37,25 +104,36 @@ export default function InvestmentRecapOfDocument() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{loadList ? (
|
||||||
<Investment_BoxDetailDocument
|
<LoaderCustom />
|
||||||
key={index}
|
) : _.isEmpty(list) ? (
|
||||||
title={`Judul Dokumen ${index + 1}`}
|
<TextCustom align="center" color="gray">
|
||||||
leftIcon={
|
Tidak ada data
|
||||||
<Ionicons
|
</TextCustom>
|
||||||
name="ellipsis-horizontal-outline"
|
) : (
|
||||||
size={ICON_SIZE_SMALL}
|
list?.map((item: any, index: number) => (
|
||||||
color={MainColor.white}
|
<Investment_BoxDetailDocument
|
||||||
style={{
|
key={index}
|
||||||
zIndex: 10,
|
title={item.title}
|
||||||
alignSelf: "flex-end",
|
leftIcon={
|
||||||
}}
|
<Ionicons
|
||||||
onPress={() => setOpenDrawerBox(true)}
|
name="ellipsis-horizontal-outline"
|
||||||
/>
|
size={ICON_SIZE_SMALL}
|
||||||
}
|
color={MainColor.white}
|
||||||
href={`/(file)/${id}`}
|
style={{
|
||||||
/>
|
zIndex: 10,
|
||||||
))}
|
alignSelf: "flex-end",
|
||||||
|
}}
|
||||||
|
onPress={() => {
|
||||||
|
setSelectId(item.id);
|
||||||
|
setOpenDrawerBox(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
href={`/(file)/${item.fileId}`}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
{/* Drawer On Header */}
|
{/* Drawer On Header */}
|
||||||
@@ -69,7 +147,7 @@ export default function InvestmentRecapOfDocument() {
|
|||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
<AntDesign
|
<AntDesign
|
||||||
name="pluscircle"
|
name="plus-circle"
|
||||||
size={ICON_SIZE_SMALL}
|
size={ICON_SIZE_SMALL}
|
||||||
color={MainColor.white}
|
color={MainColor.white}
|
||||||
/>
|
/>
|
||||||
@@ -96,7 +174,7 @@ export default function InvestmentRecapOfDocument() {
|
|||||||
{
|
{
|
||||||
icon: <IconEdit />,
|
icon: <IconEdit />,
|
||||||
label: "Edit Dokumen",
|
label: "Edit Dokumen",
|
||||||
path: `/investment/${id}/(document)/edit-document`,
|
path: `/investment/${selectId}/(document)/edit-document`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: (
|
icon: (
|
||||||
@@ -119,12 +197,14 @@ export default function InvestmentRecapOfDocument() {
|
|||||||
textLeft: "Batal",
|
textLeft: "Batal",
|
||||||
textRight: "Hapus",
|
textRight: "Hapus",
|
||||||
onPressRight: () => {
|
onPressRight: () => {
|
||||||
setOpenDrawerBox(false);
|
handlerDeleteDocument();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
router.push(item.path as any);
|
||||||
}
|
}
|
||||||
router.push(item.path as any);
|
|
||||||
setOpenDrawer(false);
|
setOpenDrawerBox(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DrawerCustom>
|
</DrawerCustom>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
AlertDefaultSystem,
|
AlertDefaultSystem,
|
||||||
BackButton,
|
BackButton,
|
||||||
@@ -8,37 +9,71 @@ import {
|
|||||||
MenuDrawerDynamicGrid,
|
MenuDrawerDynamicGrid,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconTrash } from "@/components/_Icon/IconTrash";
|
import { IconTrash } from "@/components/_Icon/IconTrash";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { useState } from "react";
|
import {
|
||||||
|
apiInvestmentDeleteNews,
|
||||||
|
apiInvestmentGetNews,
|
||||||
|
} from "@/service/api-client/api-investment";
|
||||||
|
import {
|
||||||
|
router,
|
||||||
|
Stack,
|
||||||
|
useFocusEffect,
|
||||||
|
useLocalSearchParams,
|
||||||
|
} from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function InvestmentNews() {
|
export default function InvestmentNews() {
|
||||||
const { id, news } = useLocalSearchParams();
|
const { user } = useAuth();
|
||||||
|
const { news } = useLocalSearchParams();
|
||||||
|
const id = news as string;
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiInvestmentGetNews({
|
||||||
|
id: id,
|
||||||
|
category: "one-news",
|
||||||
|
});
|
||||||
|
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{
|
options={{
|
||||||
title: "Detail Berita",
|
title: "Detail Berita",
|
||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
headerRight: () =>
|
||||||
|
user?.id === data?.authorId && (
|
||||||
|
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<DummyLandscapeImage />
|
{data && data?.imageId && (
|
||||||
|
<DummyLandscapeImage imageId={data?.imageId || ""} />
|
||||||
|
)}
|
||||||
<TextCustom bold align="center" size="large">
|
<TextCustom bold align="center" size="large">
|
||||||
Judul Berita {news} Terbaru
|
{(data && data?.title) || "-"}
|
||||||
</TextCustom>
|
|
||||||
<TextCustom>
|
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Laborum
|
|
||||||
fuga mollitia laboriosam voluptatibus quos molestias, illo fugiat
|
|
||||||
esse repellat, ad officia earum numquam? Aliquid corrupti quam
|
|
||||||
tempora cum harum est!
|
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
|
<TextCustom>{(data && data?.deskripsi) || "-"}</TextCustom>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
@@ -52,7 +87,7 @@ export default function InvestmentNews() {
|
|||||||
data={[
|
data={[
|
||||||
{
|
{
|
||||||
label: "Hapus Berita",
|
label: "Hapus Berita",
|
||||||
path: `/investment/${id}/add-news`,
|
path: ``,
|
||||||
icon: <IconTrash />,
|
icon: <IconTrash />,
|
||||||
color: "red",
|
color: "red",
|
||||||
},
|
},
|
||||||
@@ -63,9 +98,26 @@ export default function InvestmentNews() {
|
|||||||
message: "Apakah Anda yakin ingin menghapus berita ini?",
|
message: "Apakah Anda yakin ingin menghapus berita ini?",
|
||||||
textLeft: "Batal",
|
textLeft: "Batal",
|
||||||
textRight: "Hapus",
|
textRight: "Hapus",
|
||||||
onPressRight: () => {
|
onPressRight: async () => {
|
||||||
router.back();
|
try {
|
||||||
setOpenDrawer(false);
|
const response = await apiInvestmentDeleteNews({ id });
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Berita berhasil dihapus",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
setOpenDrawer(false);
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal menghapus berita",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -9,17 +9,89 @@ import {
|
|||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { router } from "expo-router";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
|
import { apiInvestmentCreateNews } from "@/service/api-client/api-investment";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||||
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function InvestmentAddNews() {
|
export default function InvestmentAddNews() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [image, setImage] = useState<IFileData | null>(null);
|
||||||
|
const [data, setData] = useState({
|
||||||
|
title: "",
|
||||||
|
deskripsi: "",
|
||||||
|
});
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
let imageId = "";
|
||||||
|
|
||||||
|
if (!data.title || !data.deskripsi) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Judul dan deskripsi harus diisi",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
if (image) {
|
||||||
|
const uploadImage = await uploadFileService({
|
||||||
|
dirId: DIRECTORY_ID.investasi_berita,
|
||||||
|
imageUri: image.uri,
|
||||||
|
});
|
||||||
|
|
||||||
|
imageId = uploadImage.data.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newData = {
|
||||||
|
id: id as string,
|
||||||
|
title: data.title,
|
||||||
|
deskripsi: data.deskripsi,
|
||||||
|
imageId: imageId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await apiInvestmentCreateNews({
|
||||||
|
id: id as string,
|
||||||
|
data: newData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Berita berhasil disimpan",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal menyimpan berita",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="Pengunggahan foto ke aplikasi bersifat opsional dan tidak diwajibkan, Anda dapat menyimpan berita tanpa mengunggah foto." />
|
<InformationBox text="Pengunggahan foto ke aplikasi bersifat opsional dan tidak diwajibkan, Anda dapat menyimpan berita tanpa mengunggah foto." />
|
||||||
<LandscapeFrameUploaded />
|
<LandscapeFrameUploaded image={image?.uri} />
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push("/(application)/(image)/take-picture/123");
|
pickFile({
|
||||||
|
allowedType: "image",
|
||||||
|
setImageUri(file) {
|
||||||
|
setImage(file);
|
||||||
|
},
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
icon="upload"
|
icon="upload"
|
||||||
>
|
>
|
||||||
@@ -30,6 +102,8 @@ export default function InvestmentAddNews() {
|
|||||||
label="Judul Berita"
|
label="Judul Berita"
|
||||||
placeholder="Masukan judul berita"
|
placeholder="Masukan judul berita"
|
||||||
required
|
required
|
||||||
|
value={data.title}
|
||||||
|
onChangeText={(value) => setData({ ...data, title: value })}
|
||||||
/>
|
/>
|
||||||
<TextAreaCustom
|
<TextAreaCustom
|
||||||
label="Deskripsi Berita"
|
label="Deskripsi Berita"
|
||||||
@@ -37,13 +111,11 @@ export default function InvestmentAddNews() {
|
|||||||
required
|
required
|
||||||
showCount
|
showCount
|
||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
|
value={data.deskripsi}
|
||||||
|
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ButtonCustom
|
<ButtonCustom isLoading={isLoading} onPress={handlerSubmit}>
|
||||||
onPress={() => {
|
|
||||||
router.back();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Simpan
|
Simpan
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|||||||
@@ -1,18 +1,51 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BackButton,
|
BackButton,
|
||||||
BaseBox,
|
BaseBox,
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
MenuDrawerDynamicGrid,
|
LoaderCustom,
|
||||||
TextCustom,
|
MenuDrawerDynamicGrid,
|
||||||
ViewWrapper
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconPlus } from "@/components/_Icon";
|
import { IconPlus } from "@/components/_Icon";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import { apiInvestmentGetNews } from "@/service/api-client/api-investment";
|
||||||
import { useState } from "react";
|
import {
|
||||||
|
router,
|
||||||
|
Stack,
|
||||||
|
useFocusEffect,
|
||||||
|
useLocalSearchParams,
|
||||||
|
} from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function InvestmentListOfNews() {
|
export default function InvestmentListOfNews() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadList();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiInvestmentGetNews({
|
||||||
|
id: id as string,
|
||||||
|
category: "all-news",
|
||||||
|
});
|
||||||
|
|
||||||
|
setList(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -22,16 +55,25 @@ export default function InvestmentListOfNews() {
|
|||||||
// headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
// headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
{Array.from({ length: 15 }).map((_, index) => (
|
{loadList ? (
|
||||||
<BaseBox
|
<LoaderCustom />
|
||||||
key={index}
|
) : _.isEmpty(list) ? (
|
||||||
paddingBlock={5}
|
<TextCustom align="center" color="gray">
|
||||||
href={`/investment/${id}/(news)/${index + 1}`}
|
Tidak ada data
|
||||||
>
|
</TextCustom>
|
||||||
<TextCustom bold>Berita Terbaru {index + 1}</TextCustom>
|
) : (
|
||||||
</BaseBox>
|
list?.map((item: any, index: number) => (
|
||||||
))}
|
<BaseBox
|
||||||
|
key={index}
|
||||||
|
paddingBlock={5}
|
||||||
|
href={`/investment/[id]/(news)/${item.id}`}
|
||||||
|
>
|
||||||
|
<TextCustom bold>{item.title}</TextCustom>
|
||||||
|
</BaseBox>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
<DrawerCustom
|
<DrawerCustom
|
||||||
|
|||||||
@@ -1,19 +1,53 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BackButton,
|
BackButton,
|
||||||
BaseBox,
|
BaseBox,
|
||||||
DotButton,
|
DotButton,
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
|
LoaderCustom,
|
||||||
MenuDrawerDynamicGrid,
|
MenuDrawerDynamicGrid,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconPlus } from "@/components/_Icon";
|
import { IconPlus } from "@/components/_Icon";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import { apiInvestmentGetNews } from "@/service/api-client/api-investment";
|
||||||
import { useState } from "react";
|
import {
|
||||||
|
router,
|
||||||
|
Stack,
|
||||||
|
useFocusEffect,
|
||||||
|
useLocalSearchParams,
|
||||||
|
} from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function InvestmentRecapOfNews() {
|
export default function InvestmentRecapOfNews() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadList();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiInvestmentGetNews({
|
||||||
|
id: id as string,
|
||||||
|
category: "all-news",
|
||||||
|
});
|
||||||
|
|
||||||
|
setList(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -24,15 +58,23 @@ export default function InvestmentRecapOfNews() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
{Array.from({ length: 15 }).map((_, index) => (
|
{loadList ? (
|
||||||
<BaseBox
|
<LoaderCustom />
|
||||||
key={index}
|
) : _.isEmpty(list) ? (
|
||||||
paddingBlock={5}
|
<TextCustom align="center" color="gray">
|
||||||
href={`/investment/${id}/(news)/${index + 1}`}
|
Tidak ada data
|
||||||
>
|
</TextCustom>
|
||||||
<TextCustom bold>Berita Terbaru {index + 1}</TextCustom>
|
) : (
|
||||||
</BaseBox>
|
list?.map((item: any, index: number) => (
|
||||||
))}
|
<BaseBox
|
||||||
|
key={index}
|
||||||
|
paddingBlock={5}
|
||||||
|
href={`/investment/[id]/(news)/${item.id}`}
|
||||||
|
>
|
||||||
|
<TextCustom bold>{item.title}</TextCustom>
|
||||||
|
</BaseBox>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
<DrawerCustom
|
<DrawerCustom
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
@@ -9,16 +10,89 @@ import {
|
|||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { LOCAL_STORAGE_KEY } from "@/constants/local-storage-key";
|
||||||
|
import { apiInvestmentGetOne } from "@/service/api-client/api-investment";
|
||||||
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function InvestmentInvest() {
|
export default function InvestmentInvest() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
// console.log("[ID]", id);
|
||||||
|
const [data, setData] = useState<any>(null);
|
||||||
|
const [jumlah, setJumlah] = useState<number>(0);
|
||||||
|
const [total, setTotal] = useState<number>(0);
|
||||||
|
const [sisaLembar, setSisaLembar] = useState<number>(0);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiInvestmentGetOne({
|
||||||
|
id: id as string,
|
||||||
|
});
|
||||||
|
|
||||||
|
setData(response.data);
|
||||||
|
setSisaLembar(response.data?.sisaLembar);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTextChange = (text: string) => {
|
||||||
|
// Izinkan input kosong → anggap sebagai 0 (atau abaikan, tergantung UX)
|
||||||
|
if (text === "") {
|
||||||
|
setJumlah(0);
|
||||||
|
setTotal(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regex: hanya digit (angka bulat positif)
|
||||||
|
const integerRegex = /^\d+$/;
|
||||||
|
|
||||||
|
if (integerRegex.test(text)) {
|
||||||
|
const numValue = Number(text);
|
||||||
|
// Karena regex sudah pastikan hanya angka, isNaN biasanya false
|
||||||
|
// Tapi tetap aman untuk cek
|
||||||
|
if (!isNaN(numValue)) {
|
||||||
|
setJumlah(numValue);
|
||||||
|
setTotal(numValue * Number(data?.hargaLembar));
|
||||||
|
console.log("[VALUE]", numValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Jika input tidak valid (misal: "12a", "12."), abaikan → state tidak berubah
|
||||||
|
};
|
||||||
|
|
||||||
const buttonSubmit = () => {
|
const buttonSubmit = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom onPress={() => router.push(`/investment/${id}/select-bank`)}>Beli</ButtonCustom>
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
|
disabled={jumlah < 10 || jumlah > sisaLembar}
|
||||||
|
onPress={async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
await AsyncStorage.setItem(
|
||||||
|
LOCAL_STORAGE_KEY.transactionInvestment,
|
||||||
|
JSON.stringify({ jumlah, total })
|
||||||
|
);
|
||||||
|
router.push(`/investment/${id}/select-bank`);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Beli
|
||||||
|
</ButtonCustom>
|
||||||
</BoxButtonOnFooter>
|
</BoxButtonOnFooter>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -34,7 +108,7 @@ export default function InvestmentInvest() {
|
|||||||
<TextCustom>Sisa Lembar Saham</TextCustom>
|
<TextCustom>Sisa Lembar Saham</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||||
<TextCustom>3.000</TextCustom>
|
<TextCustom>{data?.sisaLembar || "-"}</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -42,7 +116,7 @@ export default function InvestmentInvest() {
|
|||||||
<TextCustom>Harga Per Lembar</TextCustom>
|
<TextCustom>Harga Per Lembar</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||||
<TextCustom>Rp. 1.000</TextCustom>
|
<TextCustom>{data?.hargaLembar || "-"}</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -69,6 +143,10 @@ export default function InvestmentInvest() {
|
|||||||
}}
|
}}
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
|
value={jumlah.toString()}
|
||||||
|
onChangeText={(value) => {
|
||||||
|
handleTextChange(value);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -78,7 +156,7 @@ export default function InvestmentInvest() {
|
|||||||
<TextCustom>Total Harga</TextCustom>
|
<TextCustom>Total Harga</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
||||||
<TextCustom>Rp. 1.000</TextCustom>
|
<TextCustom> Rp. {formatCurrencyDisplay(total)}</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
@@ -9,11 +10,96 @@ import {
|
|||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
|
import CopyButton from "@/components/Button/CoyButton";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
|
import {
|
||||||
|
apiInvestmentGetInvoice,
|
||||||
|
apiInvestmentUpdateInvoice,
|
||||||
|
} from "@/service/api-client/api-investment";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
|
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function InvestmentInvoice() {
|
export default function InvestmentInvoice() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
console.log("[ID]", id);
|
||||||
|
const [data, setData] = useState<any>({});
|
||||||
|
const [image, setImage] = useState<IFileData>({
|
||||||
|
name: "",
|
||||||
|
uri: "",
|
||||||
|
size: 0,
|
||||||
|
});
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiInvestmentGetInvoice({
|
||||||
|
id: id as string,
|
||||||
|
category: "invoice",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("[RES INVOICE]", JSON.stringify(response.data, null, 2));
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmitUpdate = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const responseUploadImage = await uploadFileService({
|
||||||
|
dirId: DIRECTORY_ID.investasi_bukti_transfer,
|
||||||
|
imageUri: image?.uri,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("[RESPONSE UPLOAD IMAGE]", responseUploadImage);
|
||||||
|
|
||||||
|
if (!responseUploadImage?.data?.id) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengunggah bukti transfer",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await apiInvestmentUpdateInvoice({
|
||||||
|
id: id as string,
|
||||||
|
data: {
|
||||||
|
imageId: responseUploadImage?.data?.id,
|
||||||
|
},
|
||||||
|
status: "proses",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
console.log(
|
||||||
|
"[RESPONSE UPDATE]",
|
||||||
|
JSON.stringify(response.data, null, 2)
|
||||||
|
);
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Berhasil mengunggah bukti transfer",
|
||||||
|
});
|
||||||
|
router.push(`/investment/${id}/(transaction-flow)/process`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
@@ -21,9 +107,23 @@ export default function InvestmentInvoice() {
|
|||||||
<InformationBox text="Mohon transfer ke rekening dibawah" />
|
<InformationBox text="Mohon transfer ke rekening dibawah" />
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<TextCustom>Nama BANK</TextCustom>
|
<Grid>
|
||||||
<TextCustom>Nama Penerima</TextCustom>
|
<Grid.Col span={4}>
|
||||||
|
<TextCustom>Bank</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={8}>
|
||||||
|
<TextCustom>{data?.MasterBank?.namaBank}</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
<Spacing height={10} />
|
<Spacing height={10} />
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={4}>
|
||||||
|
<TextCustom>Nama Akun</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={8}>
|
||||||
|
<TextCustom>{data?.MasterBank?.namaAkun}</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<BaseBox backgroundColor={MainColor.soft_darkblue}>
|
<BaseBox backgroundColor={MainColor.soft_darkblue}>
|
||||||
<Grid containerStyle={{ justifyContent: "center" }}>
|
<Grid containerStyle={{ justifyContent: "center" }}>
|
||||||
@@ -34,7 +134,7 @@ export default function InvestmentInvoice() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TextCustom size="xlarge" bold color="yellow">
|
<TextCustom size="xlarge" bold color="yellow">
|
||||||
4567898765433567
|
{data?.MasterBank?.norek}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col
|
<Grid.Col
|
||||||
@@ -43,7 +143,7 @@ export default function InvestmentInvoice() {
|
|||||||
alignItems: "flex-end",
|
alignItems: "flex-end",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ButtonCustom>Salin</ButtonCustom>
|
<CopyButton textToCopy={data?.MasterBank?.norek} />
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
@@ -65,7 +165,7 @@ export default function InvestmentInvoice() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TextCustom size="xlarge" bold color="yellow">
|
<TextCustom size="xlarge" bold color="yellow">
|
||||||
Rp. 1.000.000
|
Rp. {formatCurrencyDisplay(data?.nominal)}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col
|
<Grid.Col
|
||||||
@@ -74,7 +174,7 @@ export default function InvestmentInvoice() {
|
|||||||
alignItems: "flex-end",
|
alignItems: "flex-end",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ButtonCustom>Salin</ButtonCustom>
|
<CopyButton textToCopy={data?.nominal} />
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
@@ -83,10 +183,37 @@ export default function InvestmentInvoice() {
|
|||||||
|
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<TextCustom>Upload bukti transfer anda.</TextCustom>
|
<TextCustom align="center">
|
||||||
|
Upload bukti transfer anda.
|
||||||
|
</TextCustom>
|
||||||
|
{image ? (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
gap: 10,
|
||||||
|
paddingInline: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextCustom bold align="center" truncate>
|
||||||
|
{image?.name}
|
||||||
|
</TextCustom>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<TextCustom align="center">
|
||||||
|
Tidak ada gambar yang diunggah
|
||||||
|
</TextCustom>
|
||||||
|
)}
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push("/(application)/(image)/take-picture/123");
|
pickFile({
|
||||||
|
allowedType: "image",
|
||||||
|
setImageUri(file: any) {
|
||||||
|
console.log("[IMAGE]", file);
|
||||||
|
setImage(file);
|
||||||
|
},
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
icon="upload"
|
icon="upload"
|
||||||
>
|
>
|
||||||
@@ -96,14 +223,16 @@ export default function InvestmentInvoice() {
|
|||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
|
disabled={!image}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push(`/investment/${id}/(transaction-flow)/process`);
|
handlerSubmitUpdate();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Saya Sudah Transfer
|
Saya Sudah Transfer
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
<Spacing/>
|
<Spacing />
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
import {
|
import { BaseBox, StackCustom, TextCustom, ViewWrapper } from "@/components";
|
||||||
BaseBox,
|
import MoneyTransferAnimation from "@/components/_ShareComponent/MoneyTransferAnimation";
|
||||||
Grid,
|
import { View } from "react-native";
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
import { ActivityIndicator } from "react-native";
|
|
||||||
|
|
||||||
export default function InvestmentProcess() {
|
export default function InvestmentProcess() {
|
||||||
return (
|
return (
|
||||||
@@ -16,21 +9,24 @@ export default function InvestmentProcess() {
|
|||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<TextCustom align="center" bold>
|
<TextCustom align="center" bold>
|
||||||
Admin sedang memproses transaksi investasimu
|
Admin sedang memvalidasi data dan bukti transfer anda. Mohon
|
||||||
|
tunggu proses ini selesai.
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
<ActivityIndicator size="large" color={MainColor.yellow} />
|
<View style={{ alignItems: "center", justifyContent: "center" }}>
|
||||||
|
<MoneyTransferAnimation />
|
||||||
|
</View>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
<BaseBox>
|
{/* <BaseBox>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col span={10} style={{justifyContent: 'center'}}>
|
<Grid.Col span={10} style={{ justifyContent: "center" }}>
|
||||||
<TextCustom size="small">
|
<TextCustom size="small">
|
||||||
Hubungi admin jika tidak kunjung di proses! Klik pada logo
|
Hubungi admin jika tidak kunjung di proses! Klik pada logo
|
||||||
Whatsapp ini.
|
Whatsapp ini.
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={2} style={{alignItems: "flex-end"}}>
|
<Grid.Col span={2} style={{ alignItems: "flex-end" }}>
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name="logo-whatsapp"
|
name="logo-whatsapp"
|
||||||
size={50}
|
size={50}
|
||||||
@@ -38,7 +34,7 @@ export default function InvestmentProcess() {
|
|||||||
/>
|
/>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
</BaseBox>
|
</BaseBox> */}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,35 +5,97 @@ import {
|
|||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom";
|
import { RadioCustom, RadioGroup } from "@/components/Radio/RadioCustom";
|
||||||
import { dummyMasterBank } from "@/lib/dummy-data/_master/bank";
|
import { LOCAL_STORAGE_KEY } from "@/constants/local-storage-key";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { apiInvestmentCreateInvoice } from "@/service/api-client/api-investment";
|
||||||
|
import { apiMasterBank } from "@/service/api-client/api-master";
|
||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
import { useState } from "react";
|
import _ from "lodash";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function InvestmentSelectBank() {
|
export default function InvestmentSelectBank() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [value, setValue] = useState<any | number>("");
|
const [select, setSelect] = useState<any | number>("");
|
||||||
|
const [listBank, setListBank] = useState<any>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadListBank();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const loadListBank = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiMasterBank();
|
||||||
|
|
||||||
|
setListBank(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
setListBank([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const dataCheckout = await AsyncStorage.getItem(
|
||||||
|
LOCAL_STORAGE_KEY.transactionInvestment
|
||||||
|
);
|
||||||
|
if (dataCheckout) {
|
||||||
|
const storage = JSON.parse(dataCheckout);
|
||||||
|
const newData = {
|
||||||
|
...storage,
|
||||||
|
bankId: select,
|
||||||
|
authorId: user?.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await apiInvestmentCreateInvoice({
|
||||||
|
id: id as string,
|
||||||
|
data: newData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
console.log("[RESPONSE >>]", response);
|
||||||
|
const invoiceId = response.data.id;
|
||||||
|
|
||||||
|
const delStorage = await AsyncStorage.removeItem(
|
||||||
|
LOCAL_STORAGE_KEY.transactionInvestment
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("[DEL STORAGE]", delStorage);
|
||||||
|
router.replace(`/investment/${invoiceId}/invoice`);
|
||||||
|
} else {
|
||||||
|
console.log("[FAILED]", response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonSubmit = () => {
|
const buttonSubmit = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom isLoading={isLoading} onPress={() => handlerSubmit()}>Pilih</ButtonCustom>
|
||||||
onPress={() => router.replace(`/investment/${id}/invoice`)}
|
|
||||||
>
|
|
||||||
Pilih
|
|
||||||
</ButtonCustom>
|
|
||||||
</BoxButtonOnFooter>
|
</BoxButtonOnFooter>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper footerComponent={buttonSubmit()}>
|
<ViewWrapper footerComponent={buttonSubmit()}>
|
||||||
<RadioGroup value={value} onChange={setValue}>
|
<RadioGroup value={select} onChange={setSelect}>
|
||||||
{dummyMasterBank.map((item) => (
|
{_.isEmpty(listBank)
|
||||||
<BaseBox key={item.name}>
|
? []
|
||||||
<RadioCustom label={item.name} value={item.code} />
|
: listBank?.map((item: any) => (
|
||||||
</BaseBox>
|
<BaseBox key={item.id}>
|
||||||
))}
|
<RadioCustom label={item.namaBank} value={item.id} />
|
||||||
|
</BaseBox>
|
||||||
|
))}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ import { IconDocument, IconEdit, IconNews } from "@/components/_Icon";
|
|||||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import Investment_ButtonInvestasiSection from "@/screens/Invesment/ButtonInvestasiSection";
|
import Investment_ButtonInvestasiSection from "@/screens/Invesment/ButtonInvestasiSection";
|
||||||
import Invesment_ComponentBoxOnBottomDetail from "@/screens/Invesment/ComponentBoxOnBottomDetail";
|
import Invesment_ComponentBoxOnBottomDetail from "@/screens/Invesment/ComponentBoxOnBottomDetail";
|
||||||
import Invesment_DetailDataPublishSection from "@/screens/Invesment/DetailDataPublishSection";
|
import Invesment_DetailDataPublishSection from "@/screens/Invesment/DetailDataPublishSection";
|
||||||
import { apiInvestmentGetById } from "@/service/api-client/api-investment";
|
import { apiInvestmentGetOne } from "@/service/api-client/api-investment";
|
||||||
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
|
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
|
||||||
import {
|
import {
|
||||||
router,
|
router,
|
||||||
@@ -25,6 +26,7 @@ import _ from "lodash";
|
|||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function InvestmentDetailStatus() {
|
export default function InvestmentDetailStatus() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
|
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
|
||||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||||
@@ -39,10 +41,10 @@ export default function InvestmentDetailStatus() {
|
|||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await apiInvestmentGetById({
|
const response = await apiInvestmentGetOne({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
});
|
});
|
||||||
// console.log("[DATA]", JSON.stringify(response.data, null, 2));
|
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ERROR]", error);
|
console.log("[ERROR]", error);
|
||||||
@@ -63,13 +65,14 @@ export default function InvestmentDetailStatus() {
|
|||||||
|
|
||||||
const bottomSection = (
|
const bottomSection = (
|
||||||
<Invesment_ComponentBoxOnBottomDetail
|
<Invesment_ComponentBoxOnBottomDetail
|
||||||
id={id as string}
|
id={data?.id}
|
||||||
|
prospectusId={data?.prospektusFileId}
|
||||||
status={status as string}
|
status={status as string}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const buttonSection = (
|
const buttonSection = (
|
||||||
<Investment_ButtonInvestasiSection id={id as string} isMine={false} />
|
<Investment_ButtonInvestasiSection id={id as string} isMine={user?.id === data?.author?.id} />
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
@@ -5,38 +6,149 @@ import {
|
|||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
CenterCustom,
|
CenterCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
|
LoaderCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { FontAwesome5 } from "@expo/vector-icons";
|
import {
|
||||||
import { router } from "expo-router";
|
apiInvestmentGetOne,
|
||||||
|
apiInvestmentUpdateData,
|
||||||
|
} from "@/service/api-client/api-investment";
|
||||||
|
import { deleteFileService, uploadFileService } from "@/service/upload-service";
|
||||||
|
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function InvestmentEditProspectus() {
|
export default function InvestmentEditProspectus() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any>(null);
|
||||||
|
const [loadingGet, setLoadingGet] = useState(false);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [pdf, setPdf] = useState<IFileData | null>(null);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
setLoadingGet(true);
|
||||||
|
const response = await apiInvestmentGetOne({
|
||||||
|
id: id as string,
|
||||||
|
});
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadingGet(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmitUpdate = async () => {
|
||||||
|
const prevProspectusFileId = data?.prospektusFileId;
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const responseUploadImage = await uploadFileService({
|
||||||
|
imageUri: pdf?.uri as any,
|
||||||
|
dirId: DIRECTORY_ID.investasi_prospektus,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!responseUploadImage.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengunggah gambar",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const prospektusFileId = responseUploadImage.data.id;
|
||||||
|
|
||||||
|
const responseUpdate = await apiInvestmentUpdateData({
|
||||||
|
id: id as string,
|
||||||
|
data: prospektusFileId,
|
||||||
|
category: "prospectus",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (responseUpdate.success) {
|
||||||
|
const deletePrevImage = await deleteFileService({
|
||||||
|
id: prevProspectusFileId as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!deletePrevImage.success) {
|
||||||
|
console.log("[ERROR DELETE PREV IMAGE]", deletePrevImage.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Data berhasil diupdate",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: responseUpdate.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonFooter = (
|
const buttonFooter = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom>
|
<ButtonCustom
|
||||||
|
disabled={pdf === null}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={handleSubmitUpdate}
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</ButtonCustom>
|
||||||
</BoxButtonOnFooter>
|
</BoxButtonOnFooter>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<ViewWrapper footerComponent={buttonFooter}>
|
<ViewWrapper footerComponent={!loadingGet && buttonFooter}>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="File prospektus wajib untuk diupload, agar calon investor paham dengan prospek investasi yang akan anda jalankan kedepan." />
|
<InformationBox text="File prospektus wajib untuk diupload, agar calon investor paham dengan prospek investasi yang akan anda jalankan kedepan." />
|
||||||
<Spacing />
|
<Spacing />
|
||||||
|
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<CenterCustom>
|
<CenterCustom>
|
||||||
<FontAwesome5
|
{loadingGet ? (
|
||||||
name="file-pdf"
|
<LoaderCustom />
|
||||||
size={30}
|
) : pdf ? (
|
||||||
color={MainColor.disabled}
|
<TextCustom truncate>{pdf.name}</TextCustom>
|
||||||
/>
|
) : (
|
||||||
|
<TextCustom truncate>
|
||||||
|
{_.snakeCase(data?.title || "").replace(/_/g, "-")}.pdf
|
||||||
|
</TextCustom>
|
||||||
|
)}
|
||||||
</CenterCustom>
|
</CenterCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
|
disabled={loadingGet}
|
||||||
icon="upload"
|
icon="upload"
|
||||||
onPress={() => router.push("/(application)/(image)/take-picture/123")}
|
onPress={() => {
|
||||||
|
pickFile({
|
||||||
|
allowedType: "pdf",
|
||||||
|
setPdfUri(file: any) {
|
||||||
|
setPdf({
|
||||||
|
uri: file.uri,
|
||||||
|
name: file.name,
|
||||||
|
size: file.size,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Upload
|
Upload
|
||||||
</ButtonCenteredOnly>
|
</ButtonCenteredOnly>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
|
LoaderCustom,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
@@ -12,20 +13,19 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import dummyPembagianDeviden from "@/lib/dummy-data/investment/pembagian-deviden";
|
|
||||||
import dummyListPencarianInvestor from "@/lib/dummy-data/investment/pencarian-investor";
|
|
||||||
import dummyPeriodeDeviden from "@/lib/dummy-data/investment/periode-deviden";
|
|
||||||
import {
|
import {
|
||||||
apiInvestmentGetById,
|
apiInvestmentGetOne,
|
||||||
apiInvestmentUpdateData,
|
apiInvestmentUpdateData,
|
||||||
} from "@/service/api-client/api-investment";
|
} from "@/service/api-client/api-investment";
|
||||||
|
import { apiMasterInvestment } from "@/service/api-client/api-master";
|
||||||
import {
|
import {
|
||||||
deleteImageService,
|
deleteFileService,
|
||||||
uploadImageService,
|
uploadFileService,
|
||||||
} from "@/service/upload-service";
|
} from "@/service/upload-service";
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
import pickFile from "@/utils/pickFile";
|
import pickFile from "@/utils/pickFile";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
@@ -61,55 +61,82 @@ export default function InvestmentEdit() {
|
|||||||
|
|
||||||
const [image, setImage] = useState<string | null>(null);
|
const [image, setImage] = useState<string | null>(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [loadingMaster, setLoadingMaster] = useState(false);
|
||||||
const displayTargetDana = formatCurrencyDisplay(data?.targetDana);
|
const [listPencarianInvestor, setListPencarianInvestor] = useState<any[]>([]);
|
||||||
const displayHargaPerLembar = formatCurrencyDisplay(data?.hargaLembar);
|
const [listPeriodeDeviden, setListPeriodeDeviden] = useState<any[]>([]);
|
||||||
const displayTotalLembar = formatCurrencyDisplay(
|
const [listPembagianDeviden, setListPembagianDeviden] = useState<any[]>([]);
|
||||||
Number(data?.targetDana) / Number(data?.hargaLembar)
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleChangeCurrency = (field: keyof typeof data) => (text: string) => {
|
|
||||||
const numeric = text.replace(/\D/g, "");
|
|
||||||
setData((prev) => ({ ...prev, [field]: numeric }));
|
|
||||||
};
|
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
|
onLoadMaster();
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onLoadMaster = async () => {
|
||||||
|
try {
|
||||||
|
setLoadingMaster(true);
|
||||||
|
const response = await apiMasterInvestment({ category: "" });
|
||||||
|
|
||||||
|
setListPencarianInvestor(response.data.pencarianInvestor);
|
||||||
|
setListPeriodeDeviden(response.data.periodeDeviden);
|
||||||
|
setListPembagianDeviden(response.data.pembagianDeviden);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadingMaster(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await apiInvestmentGetById({
|
const response = await apiInvestmentGetOne({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
});
|
});
|
||||||
// console.log("[DATA]", JSON.stringify(response.data, null, 2));
|
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ERROR]", error);
|
console.log("[ERROR]", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmitUpdate = async () => {
|
const displayTargetDana = formatCurrencyDisplay(data?.targetDana);
|
||||||
let newData = {
|
const displayHargaPerLembar = formatCurrencyDisplay(data?.hargaLembar);
|
||||||
...data,
|
const realTotalLembar = Number(data?.targetDana) / Number(data?.hargaLembar);
|
||||||
};
|
const displayTotalLembar = formatCurrencyDisplay(realTotalLembar);
|
||||||
|
|
||||||
|
const handleChangeCurrency = (field: keyof typeof data) => (text: string) => {
|
||||||
|
const numeric = text.replace(/\D/g, "");
|
||||||
|
setData((prev) => ({ ...prev, [field]: numeric }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateData = () => {
|
||||||
if (
|
if (
|
||||||
newData?.title === "" ||
|
!data.title ||
|
||||||
newData?.targetDana === "" ||
|
!data.targetDana ||
|
||||||
newData?.hargaLembar === "" ||
|
!data.hargaLembar ||
|
||||||
newData?.totalLembar === "" ||
|
!data.totalLembar ||
|
||||||
newData?.roi === "" ||
|
!data.roi ||
|
||||||
newData?.masterPencarianInvestorId === "" ||
|
!data.masterPencarianInvestorId ||
|
||||||
newData?.masterPeriodeDevidenId === "" ||
|
!data.masterPeriodeDevidenId ||
|
||||||
newData?.masterPembagianDevidenId === ""
|
!data.masterPembagianDevidenId
|
||||||
) {
|
) {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "info",
|
type: "info",
|
||||||
text1: "Harap isi semua data",
|
text1: "Harap isi semua data",
|
||||||
});
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmitUpdate = async () => {
|
||||||
|
let newData = {
|
||||||
|
...data,
|
||||||
|
totalLembar: realTotalLembar.toString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!validateData()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +144,7 @@ export default function InvestmentEdit() {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
if (image) {
|
if (image) {
|
||||||
const responseUploadImage = await uploadImageService({
|
const responseUploadImage = await uploadFileService({
|
||||||
imageUri: image,
|
imageUri: image,
|
||||||
dirId: DIRECTORY_ID.investasi_image,
|
dirId: DIRECTORY_ID.investasi_image,
|
||||||
});
|
});
|
||||||
@@ -130,7 +157,7 @@ export default function InvestmentEdit() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deletePrevImage = await deleteImageService({
|
const deletePrevImage = await deleteFileService({
|
||||||
id: data?.imageId as any,
|
id: data?.imageId as any,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -151,10 +178,9 @@ export default function InvestmentEdit() {
|
|||||||
const responseUpdate = await apiInvestmentUpdateData({
|
const responseUpdate = await apiInvestmentUpdateData({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
data: newData,
|
data: newData,
|
||||||
|
category: "data"
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[RESPONSE UPDATE]", JSON.parse(JSON.stringify(responseUpdate)));
|
|
||||||
|
|
||||||
if (responseUpdate.success) {
|
if (responseUpdate.success) {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "success",
|
type: "success",
|
||||||
@@ -188,7 +214,6 @@ export default function InvestmentEdit() {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
pickFile({
|
pickFile({
|
||||||
setImageUri: ({ uri }) => {
|
setImageUri: ({ uri }) => {
|
||||||
console.log("URI IMAGE", uri);
|
|
||||||
setImage(uri);
|
setImage(uri);
|
||||||
},
|
},
|
||||||
allowedType: "image",
|
allowedType: "image",
|
||||||
@@ -249,47 +274,72 @@ export default function InvestmentEdit() {
|
|||||||
value={data?.roi === "" ? "" : data?.roi}
|
value={data?.roi === "" ? "" : data?.roi}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectCustom
|
{loadingMaster ? (
|
||||||
required
|
<LoaderCustom />
|
||||||
placeholder="Pilih batas waktu"
|
) : (
|
||||||
label="Pencarian Investor"
|
<SelectCustom
|
||||||
data={dummyListPencarianInvestor.map((item) => ({
|
required
|
||||||
label: item.name + `${" "}hari`,
|
placeholder="Pilih batas waktu"
|
||||||
value: item.id,
|
label="Pencarian Investor"
|
||||||
}))}
|
data={
|
||||||
onChange={(value) =>
|
_.isEmpty(listPencarianInvestor)
|
||||||
setData({ ...data, masterPencarianInvestorId: value as any })
|
? []
|
||||||
}
|
: listPencarianInvestor.map((item) => ({
|
||||||
value={data.masterPencarianInvestorId}
|
label: item.name + `${" "}hari`,
|
||||||
/>
|
value: item.id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
onChange={(value) =>
|
||||||
|
setData({ ...data, masterPencarianInvestorId: value as any })
|
||||||
|
}
|
||||||
|
value={data.masterPencarianInvestorId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<SelectCustom
|
{loadingMaster ? (
|
||||||
required
|
<LoaderCustom />
|
||||||
placeholder="Pilih batas waktu"
|
) : (
|
||||||
label="Pilih Periode Deviden"
|
<SelectCustom
|
||||||
data={dummyPeriodeDeviden.map((item) => ({
|
required
|
||||||
label: item.name,
|
placeholder="Pilih batas waktu"
|
||||||
value: item.id,
|
label="Pilih Periode Deviden"
|
||||||
}))}
|
data={
|
||||||
onChange={(value) =>
|
_.isEmpty(listPeriodeDeviden)
|
||||||
setData({ ...data, masterPeriodeDevidenId: value as any })
|
? []
|
||||||
}
|
: listPeriodeDeviden.map((item) => ({
|
||||||
value={data.masterPeriodeDevidenId}
|
label: item.name,
|
||||||
/>
|
value: item.id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
onChange={(value) =>
|
||||||
|
setData({ ...data, masterPeriodeDevidenId: value as any })
|
||||||
|
}
|
||||||
|
value={data.masterPeriodeDevidenId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{loadingMaster ? (
|
||||||
|
<LoaderCustom />
|
||||||
|
) : (
|
||||||
|
<SelectCustom
|
||||||
|
required
|
||||||
|
placeholder="Pilih batas waktu"
|
||||||
|
label="Pilih Pembagian Deviden"
|
||||||
|
data={
|
||||||
|
_.isEmpty(listPembagianDeviden)
|
||||||
|
? []
|
||||||
|
: listPembagianDeviden.map((item) => ({
|
||||||
|
label: item.name + `${" "}bulan`,
|
||||||
|
value: item.id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
onChange={(value) =>
|
||||||
|
setData({ ...data, masterPembagianDevidenId: value as any })
|
||||||
|
}
|
||||||
|
value={data.masterPembagianDevidenId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<SelectCustom
|
|
||||||
required
|
|
||||||
placeholder="Pilih batas waktu"
|
|
||||||
label="Pilih Pembagian Deviden"
|
|
||||||
data={dummyPembagianDeviden.map((item) => ({
|
|
||||||
label: item.name + `${" "}bulan`,
|
|
||||||
value: item.id,
|
|
||||||
}))}
|
|
||||||
onChange={(value) =>
|
|
||||||
setData({ ...data, masterPembagianDevidenId: value as any })
|
|
||||||
}
|
|
||||||
value={data.masterPembagianDevidenId}
|
|
||||||
/>
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom isLoading={isLoading} onPress={handleSubmitUpdate}>
|
<ButtonCustom isLoading={isLoading} onPress={handleSubmitUpdate}>
|
||||||
Simpan
|
Simpan
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BackButton,
|
BackButton,
|
||||||
DotButton,
|
DotButton,
|
||||||
@@ -9,18 +10,45 @@ import { IconDocument, IconEdit, IconNews } from "@/components/_Icon";
|
|||||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import Investment_ButtonInvestasiSection from "@/screens/Invesment/ButtonInvestasiSection";
|
import Investment_ButtonInvestasiSection from "@/screens/Invesment/ButtonInvestasiSection";
|
||||||
import Invesment_ComponentBoxOnBottomDetail from "@/screens/Invesment/ComponentBoxOnBottomDetail";
|
import Invesment_ComponentBoxOnBottomDetail from "@/screens/Invesment/ComponentBoxOnBottomDetail";
|
||||||
import Invesment_DetailDataPublishSection from "@/screens/Invesment/DetailDataPublishSection";
|
import Invesment_DetailDataPublishSection from "@/screens/Invesment/DetailDataPublishSection";
|
||||||
|
import { apiInvestmentGetOne } from "@/service/api-client/api-investment";
|
||||||
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
|
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
|
||||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
import {
|
||||||
|
router,
|
||||||
|
Stack,
|
||||||
|
useFocusEffect,
|
||||||
|
useLocalSearchParams,
|
||||||
|
} from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function InvestmentDetail() {
|
export default function InvestmentDetail() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
|
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
|
||||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||||
|
const [data, setData] = useState<any>(null);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id, status])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiInvestmentGetOne({
|
||||||
|
id: id as string,
|
||||||
|
});
|
||||||
|
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handlePressDraft = (item: IMenuDrawerItem) => {
|
const handlePressDraft = (item: IMenuDrawerItem) => {
|
||||||
console.log("PATH >> ", item.path);
|
console.log("PATH >> ", item.path);
|
||||||
@@ -37,11 +65,14 @@ export default function InvestmentDetail() {
|
|||||||
const bottomSection = (
|
const bottomSection = (
|
||||||
<Invesment_ComponentBoxOnBottomDetail
|
<Invesment_ComponentBoxOnBottomDetail
|
||||||
id={id as string}
|
id={id as string}
|
||||||
status={'publish'}
|
prospectusId={data?.prospektusFileId}
|
||||||
|
status={"publish"}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const buttonSection = <Investment_ButtonInvestasiSection id={id as string} isMine={true} />;
|
const buttonSection = (
|
||||||
|
<Investment_ButtonInvestasiSection id={id as string} isMine={user?.id === data?.author?.id} />
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -61,6 +92,7 @@ export default function InvestmentDetail() {
|
|||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<Invesment_DetailDataPublishSection
|
<Invesment_DetailDataPublishSection
|
||||||
status={"publish"}
|
status={"publish"}
|
||||||
|
data={data}
|
||||||
bottomSection={bottomSection}
|
bottomSection={bottomSection}
|
||||||
buttonSection={buttonSection}
|
buttonSection={buttonSection}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
CenterCustom,
|
CenterCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
|
LoaderCustom,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
@@ -15,16 +16,15 @@ import {
|
|||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import dummyPembagianDeviden from "@/lib/dummy-data/investment/pembagian-deviden";
|
|
||||||
import dummyListPencarianInvestor from "@/lib/dummy-data/investment/pencarian-investor";
|
|
||||||
import dummyPeriodeDeviden from "@/lib/dummy-data/investment/periode-deviden";
|
|
||||||
import { apiInvestmentCreate } from "@/service/api-client/api-investment";
|
import { apiInvestmentCreate } from "@/service/api-client/api-investment";
|
||||||
import { uploadImageService } from "@/service/upload-service";
|
import { apiMasterInvestment } from "@/service/api-client/api-master";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
import pickFile, { IFileData } from "@/utils/pickFile";
|
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||||
import { FontAwesome5 } from "@expo/vector-icons";
|
import { FontAwesome5 } from "@expo/vector-icons";
|
||||||
import { router } from "expo-router";
|
import { router, useFocusEffect } from "expo-router";
|
||||||
import { useState } from "react";
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function InvestmentCreate() {
|
export default function InvestmentCreate() {
|
||||||
@@ -46,26 +46,43 @@ export default function InvestmentCreate() {
|
|||||||
const [pdf, setPdf] = useState<IFileData | null>(null);
|
const [pdf, setPdf] = useState<IFileData | null>(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const [loadingMaster, setLoadingMaster] = useState(false);
|
||||||
|
const [listPencarianInvestor, setListPencarianInvestor] = useState<any[]>([]);
|
||||||
|
const [listPeriodeDeviden, setListPeriodeDeviden] = useState<any[]>([]);
|
||||||
|
const [listPembagianDeviden, setListPembagianDeviden] = useState<any[]>([]);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadMaster();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadMaster = async () => {
|
||||||
|
try {
|
||||||
|
setLoadingMaster(true);
|
||||||
|
const response = await apiMasterInvestment({ category: "" });
|
||||||
|
|
||||||
|
setListPencarianInvestor(response.data.pencarianInvestor);
|
||||||
|
setListPeriodeDeviden(response.data.periodeDeviden);
|
||||||
|
setListPembagianDeviden(response.data.pembagianDeviden);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadingMaster(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const displayTargetDana = formatCurrencyDisplay(data.targetDana);
|
const displayTargetDana = formatCurrencyDisplay(data.targetDana);
|
||||||
const displayHargaPerLembar = formatCurrencyDisplay(data.hargaPerLembar);
|
const displayHargaPerLembar = formatCurrencyDisplay(data.hargaPerLembar);
|
||||||
const displayTotalLembar = formatCurrencyDisplay(
|
const realTotalLembar = Number(data.targetDana) / Number(data.hargaPerLembar);
|
||||||
Number(data.targetDana) / Number(data.hargaPerLembar)
|
const displayTotalLembar = formatCurrencyDisplay(realTotalLembar);
|
||||||
);
|
|
||||||
|
|
||||||
const handleChangeCurrency = (field: keyof typeof data) => (text: string) => {
|
const handleChangeCurrency = (field: keyof typeof data) => (text: string) => {
|
||||||
const numeric = text.replace(/\D/g, "");
|
const numeric = text.replace(/\D/g, "");
|
||||||
setData((prev) => ({ ...prev, [field]: numeric }));
|
setData((prev) => ({ ...prev, [field]: numeric }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const validateData = () => {
|
||||||
if (!image || !pdf) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Harap pilih gambar dan file PDF",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!data.title ||
|
!data.title ||
|
||||||
!data.targetDana ||
|
!data.targetDana ||
|
||||||
@@ -79,12 +96,28 @@ export default function InvestmentCreate() {
|
|||||||
type: "error",
|
type: "error",
|
||||||
text1: "Harap isi semua data",
|
text1: "Harap isi semua data",
|
||||||
});
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!validateData()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!image || !pdf) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Harap upload gambar dan file PDF",
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const responseUploadImage = await uploadImageService({
|
const responseUploadImage = await uploadFileService({
|
||||||
imageUri: image,
|
imageUri: image,
|
||||||
dirId: DIRECTORY_ID.investasi_image,
|
dirId: DIRECTORY_ID.investasi_image,
|
||||||
});
|
});
|
||||||
@@ -98,7 +131,7 @@ export default function InvestmentCreate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const imageId = responseUploadImage.data.id;
|
const imageId = responseUploadImage.data.id;
|
||||||
const responseUploadPdf = await uploadImageService({
|
const responseUploadPdf = await uploadFileService({
|
||||||
imageUri: pdf.uri,
|
imageUri: pdf.uri,
|
||||||
dirId: DIRECTORY_ID.investasi_prospektus,
|
dirId: DIRECTORY_ID.investasi_prospektus,
|
||||||
});
|
});
|
||||||
@@ -116,7 +149,7 @@ export default function InvestmentCreate() {
|
|||||||
title: data.title,
|
title: data.title,
|
||||||
targetDana: data.targetDana,
|
targetDana: data.targetDana,
|
||||||
hargaLembar: data.hargaPerLembar,
|
hargaLembar: data.hargaPerLembar,
|
||||||
totalLembar: displayTotalLembar,
|
totalLembar: realTotalLembar.toString(),
|
||||||
roi: data.rasioKeuntungan,
|
roi: data.rasioKeuntungan,
|
||||||
masterPencarianInvestorId: data.pencarianInvestor,
|
masterPencarianInvestorId: data.pencarianInvestor,
|
||||||
masterPembagianDevidenId: data.pembagianDeviden,
|
masterPembagianDevidenId: data.pembagianDeviden,
|
||||||
@@ -127,7 +160,7 @@ export default function InvestmentCreate() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const response = await apiInvestmentCreate({ data: newData });
|
const response = await apiInvestmentCreate({ data: newData });
|
||||||
console.log("[RESPONSE]", JSON.stringify(response, null, 2));
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "success",
|
type: "success",
|
||||||
@@ -160,7 +193,6 @@ export default function InvestmentCreate() {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
pickFile({
|
pickFile({
|
||||||
setImageUri: ({ uri }) => {
|
setImageUri: ({ uri }) => {
|
||||||
console.log("URI IMAGE", uri);
|
|
||||||
setImage(uri);
|
setImage(uri);
|
||||||
},
|
},
|
||||||
allowedType: "image",
|
allowedType: "image",
|
||||||
@@ -192,7 +224,7 @@ export default function InvestmentCreate() {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
pickFile({
|
pickFile({
|
||||||
setPdfUri: ({ uri, name, size }) => {
|
setPdfUri: ({ uri, name, size }) => {
|
||||||
console.log("URI PDF", JSON.stringify(uri, null, 2));
|
|
||||||
setPdf({ uri, name, size });
|
setPdf({ uri, name, size });
|
||||||
},
|
},
|
||||||
allowedType: "pdf",
|
allowedType: "pdf",
|
||||||
@@ -258,47 +290,72 @@ export default function InvestmentCreate() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectCustom
|
{loadingMaster ? (
|
||||||
required
|
<LoaderCustom />
|
||||||
placeholder="Pilih batas waktu"
|
) : (
|
||||||
label="Pencarian Investor"
|
<SelectCustom
|
||||||
data={dummyListPencarianInvestor.map((item) => ({
|
required
|
||||||
label: item.name + `${" "}hari`,
|
placeholder="Pilih batas waktu"
|
||||||
value: item.id,
|
label="Pencarian Investor"
|
||||||
}))}
|
data={
|
||||||
onChange={(value) =>
|
_.isEmpty(listPencarianInvestor)
|
||||||
setData({ ...data, pencarianInvestor: value as any })
|
? []
|
||||||
}
|
: listPencarianInvestor.map((item) => ({
|
||||||
value={data.pencarianInvestor}
|
label: item.name + `${" "}hari`,
|
||||||
/>
|
value: item.id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
onChange={(value) =>
|
||||||
|
setData({ ...data, pencarianInvestor: value as any })
|
||||||
|
}
|
||||||
|
value={data.pencarianInvestor}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<SelectCustom
|
{loadingMaster ? (
|
||||||
required
|
<LoaderCustom />
|
||||||
placeholder="Pilih batas waktu"
|
) : (
|
||||||
label="Pilih Periode Deviden"
|
<SelectCustom
|
||||||
data={dummyPeriodeDeviden.map((item) => ({
|
required
|
||||||
label: item.name,
|
placeholder="Pilih batas waktu"
|
||||||
value: item.id,
|
label="Pilih Periode Deviden"
|
||||||
}))}
|
data={
|
||||||
onChange={(value) =>
|
_.isEmpty(listPeriodeDeviden)
|
||||||
setData({ ...data, periodeDeviden: value as any })
|
? []
|
||||||
}
|
: listPeriodeDeviden.map((item) => ({
|
||||||
value={data.periodeDeviden}
|
label: item.name,
|
||||||
/>
|
value: item.id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
onChange={(value) =>
|
||||||
|
setData({ ...data, periodeDeviden: value as any })
|
||||||
|
}
|
||||||
|
value={data.periodeDeviden}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{loadingMaster ? (
|
||||||
|
<LoaderCustom />
|
||||||
|
) : (
|
||||||
|
<SelectCustom
|
||||||
|
required
|
||||||
|
placeholder="Pilih batas waktu"
|
||||||
|
label="Pilih Pembagian Deviden"
|
||||||
|
data={
|
||||||
|
_.isEmpty(listPembagianDeviden)
|
||||||
|
? []
|
||||||
|
: listPembagianDeviden.map((item) => ({
|
||||||
|
label: item.name + `${" "}bulan`,
|
||||||
|
value: item.id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
onChange={(value) =>
|
||||||
|
setData({ ...data, pembagianDeviden: value as any })
|
||||||
|
}
|
||||||
|
value={data.pembagianDeviden}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<SelectCustom
|
|
||||||
required
|
|
||||||
placeholder="Pilih batas waktu"
|
|
||||||
label="Pilih Pembagian Deviden"
|
|
||||||
data={dummyPembagianDeviden.map((item) => ({
|
|
||||||
label: item.name + `${" "}bulan`,
|
|
||||||
value: item.id,
|
|
||||||
}))}
|
|
||||||
onChange={(value) =>
|
|
||||||
setData({ ...data, pembagianDeviden: value as any })
|
|
||||||
}
|
|
||||||
value={data.pembagianDeviden}
|
|
||||||
/>
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom isLoading={isLoading} onPress={() => handleSubmit()}>
|
<ButtonCustom isLoading={isLoading} onPress={() => handleSubmit()}>
|
||||||
Simpan
|
Simpan
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
DummyLandscapeImage,
|
DummyLandscapeImage,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
LoaderCustom,
|
LoaderCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { apiJobGetOne, apiJobUpdateData } from "@/service/api-client/api-job";
|
import { apiJobGetOne, apiJobUpdateData } from "@/service/api-client/api-job";
|
||||||
import {
|
import {
|
||||||
deleteImageService,
|
deleteFileService,
|
||||||
uploadImageService,
|
uploadFileService,
|
||||||
} from "@/service/upload-service";
|
} from "@/service/upload-service";
|
||||||
import pickImage from "@/utils/pickImage";
|
import pickImage from "@/utils/pickImage";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
@@ -69,7 +69,7 @@ export default function JobEdit() {
|
|||||||
let newImageId = "";
|
let newImageId = "";
|
||||||
|
|
||||||
if (imageUri) {
|
if (imageUri) {
|
||||||
const responseUploadImage = await uploadImageService({
|
const responseUploadImage = await uploadFileService({
|
||||||
imageUri: imageUri,
|
imageUri: imageUri,
|
||||||
dirId: DIRECTORY_ID.job_image,
|
dirId: DIRECTORY_ID.job_image,
|
||||||
});
|
});
|
||||||
@@ -80,7 +80,7 @@ export default function JobEdit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (data?.imageId) {
|
if (data?.imageId) {
|
||||||
const responseDeleteImage = await deleteImageService({
|
const responseDeleteImage = await deleteFileService({
|
||||||
id: data.imageId,
|
id: data.imageId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { apiJobCreate } from "@/service/api-client/api-job";
|
import { apiJobCreate } from "@/service/api-client/api-job";
|
||||||
import { uploadImageService } from "@/service/upload-service";
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
import pickImage from "@/utils/pickImage";
|
import pickImage from "@/utils/pickImage";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@@ -66,7 +66,7 @@ export default function JobCreate() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseUploadImage = await uploadImageService({
|
const responseUploadImage = await uploadFileService({
|
||||||
imageUri: image,
|
imageUri: image,
|
||||||
dirId: DIRECTORY_ID.job_image,
|
dirId: DIRECTORY_ID.job_image,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,54 +1,226 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BaseBox,
|
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
MapCustom,
|
|
||||||
Spacing,
|
Spacing,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import API_IMAGE from "@/constants/api-storage";
|
||||||
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
|
import { apiMapsGetOne, apiMapsUpdate } from "@/service/api-client/api-maps";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { StyleSheet, View } from "react-native";
|
||||||
|
import MapView, { LatLng, Marker } from "react-native-maps";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
|
const defaultRegion = {
|
||||||
|
latitude: -8.737109,
|
||||||
|
longitude: 115.1756897,
|
||||||
|
latitudeDelta: 0.1,
|
||||||
|
longitudeDelta: 0.1,
|
||||||
|
};
|
||||||
export default function MapsEdit() {
|
export default function MapsEdit() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any | null>({
|
||||||
|
id: "",
|
||||||
|
namePin: "",
|
||||||
|
latitude: "",
|
||||||
|
longitude: "",
|
||||||
|
imageId: "",
|
||||||
|
});
|
||||||
|
const [selectedLocation, setSelectedLocation] = useState<LatLng | null>(null);
|
||||||
|
const [image, setImage] = useState<IFileData | null>(null);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiMapsGetOne({ id: id as string });
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setData({
|
||||||
|
id: response.data.id,
|
||||||
|
namePin: response.data.namePin,
|
||||||
|
latitude: response.data.latitude,
|
||||||
|
longitude: response.data.longitude,
|
||||||
|
imageId: response.data.imageId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMapPress = (event: any) => {
|
||||||
|
const { latitude, longitude } = event.nativeEvent.coordinate;
|
||||||
|
const location = { latitude, longitude };
|
||||||
|
setSelectedLocation(location);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
let newData: any;
|
||||||
|
if (!data.namePin) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Nama pin harus diisi",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
newData = {
|
||||||
|
namePin: data?.namePin,
|
||||||
|
latitude: selectedLocation?.latitude || data?.latitude,
|
||||||
|
longitude: selectedLocation?.longitude || data?.longitude,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
if (image) {
|
||||||
|
const responseUpload = await uploadFileService({
|
||||||
|
dirId: DIRECTORY_ID.map_image,
|
||||||
|
imageUri: image?.uri,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!responseUpload?.data?.id) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengunggah gambar",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageId = responseUpload?.data?.id;
|
||||||
|
|
||||||
|
newData = {
|
||||||
|
namePin: data?.namePin,
|
||||||
|
latitude: selectedLocation?.latitude,
|
||||||
|
longitude: selectedLocation?.longitude,
|
||||||
|
newImageId: imageId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseUpdate = await apiMapsUpdate({
|
||||||
|
id: data?.id,
|
||||||
|
data: newData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!responseUpdate.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengupdate map",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Map berhasil diupdate",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonFooter = (
|
const buttonFooter = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
onPress={() => {
|
disabled={!data.namePin}
|
||||||
console.log(`Simpan maps ${id}`);
|
onPress={handleSubmit}
|
||||||
router.back()
|
isLoading={isLoading}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Simpan
|
Update
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
</BoxButtonOnFooter>
|
</BoxButtonOnFooter>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper footerComponent={buttonFooter}>
|
<ViewWrapper footerComponent={buttonFooter}>
|
||||||
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
|
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
|
||||||
|
|
||||||
<BaseBox>
|
<View style={[styles.container, { height: 400 }]}>
|
||||||
<MapCustom />
|
<MapView
|
||||||
</BaseBox>
|
style={styles.map}
|
||||||
|
initialRegion={
|
||||||
|
data?.latitude && data?.longitude
|
||||||
|
? {
|
||||||
|
latitude: data?.latitude,
|
||||||
|
longitude: data?.longitude,
|
||||||
|
latitudeDelta: 0.1,
|
||||||
|
longitudeDelta: 0.1,
|
||||||
|
}
|
||||||
|
: defaultRegion
|
||||||
|
}
|
||||||
|
onPress={handleMapPress}
|
||||||
|
showsUserLocation={true}
|
||||||
|
showsMyLocationButton={true}
|
||||||
|
loadingEnabled={true}
|
||||||
|
loadingIndicatorColor="#666"
|
||||||
|
loadingBackgroundColor="#f0f0f0"
|
||||||
|
>
|
||||||
|
{selectedLocation ? (
|
||||||
|
<Marker
|
||||||
|
coordinate={selectedLocation}
|
||||||
|
title="Lokasi Dipilih"
|
||||||
|
description={`Lat: ${selectedLocation.latitude.toFixed(
|
||||||
|
6
|
||||||
|
)}, Lng: ${selectedLocation.longitude.toFixed(6)}`}
|
||||||
|
pinColor="red"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Marker
|
||||||
|
coordinate={defaultRegion}
|
||||||
|
title="Lokasi Dipilih"
|
||||||
|
description={`Lat: ${defaultRegion.latitude.toFixed(
|
||||||
|
6
|
||||||
|
)}, Lng: ${defaultRegion.longitude.toFixed(6)}`}
|
||||||
|
pinColor="red"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</MapView>
|
||||||
|
</View>
|
||||||
|
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
required
|
required
|
||||||
label="Nama Pin"
|
label="Nama Pin"
|
||||||
placeholder="Masukkan nama pin maps"
|
placeholder="Masukkan nama pin maps"
|
||||||
|
value={data?.namePin}
|
||||||
|
onChangeText={(value) => setData({ ...data, namePin: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
|
|
||||||
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
|
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
|
||||||
<LandscapeFrameUploaded />
|
<LandscapeFrameUploaded
|
||||||
|
image={
|
||||||
|
image
|
||||||
|
? image?.uri
|
||||||
|
: API_IMAGE.GET({ fileId: data?.imageId as string })
|
||||||
|
}
|
||||||
|
/>
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
icon="upload"
|
icon="upload"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
console.log("Upload foto ");
|
pickFile({
|
||||||
router.navigate(`/take-picture/${id}`);
|
allowedType: "image",
|
||||||
|
setImageUri(file) {
|
||||||
|
setImage(file);
|
||||||
|
},
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Upload
|
Upload
|
||||||
@@ -57,3 +229,16 @@ export default function MapsEdit() {
|
|||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
width: "100%",
|
||||||
|
backgroundColor: "#f5f5f5",
|
||||||
|
overflow: "hidden",
|
||||||
|
borderRadius: 8,
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
map: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -6,21 +6,96 @@ import {
|
|||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
Spacing,
|
Spacing,
|
||||||
TextCustom,
|
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
|
import MapSelected from "@/components/Map/MapSelected";
|
||||||
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { apiMapsCreate } from "@/service/api-client/api-maps";
|
||||||
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
|
import pickFile, { IFileData } from "@/utils/pickFile";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { LatLng } from "react-native-maps";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function MapsCreate() {
|
export default function MapsCreate() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
const [selectedLocation, setSelectedLocation] = useState<LatLng | null>(null);
|
||||||
|
const [name, setName] = useState<string>("");
|
||||||
|
const [image, setImage] = useState<IFileData | null>(null);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
let newData: any;
|
||||||
|
newData = {
|
||||||
|
authorId: user?.id,
|
||||||
|
portofolioId: id,
|
||||||
|
namePin: name,
|
||||||
|
latitude: selectedLocation?.latitude,
|
||||||
|
longitude: selectedLocation?.longitude,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (image) {
|
||||||
|
const responseUpload = await uploadFileService({
|
||||||
|
dirId: DIRECTORY_ID.map_image,
|
||||||
|
imageUri: image?.uri,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!responseUpload?.data?.id) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengunggah gambar",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imageId = responseUpload?.data?.id;
|
||||||
|
|
||||||
|
newData = {
|
||||||
|
authorId: user?.id,
|
||||||
|
portofolioId: id,
|
||||||
|
namePin: name,
|
||||||
|
latitude: selectedLocation?.latitude,
|
||||||
|
longitude: selectedLocation?.longitude,
|
||||||
|
imageId: imageId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await apiMapsCreate({
|
||||||
|
data: newData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal menambahkan map",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Map berhasil ditambahkan",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonFooter = (
|
const buttonFooter = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
onPress={() => {
|
isLoading={isLoading}
|
||||||
console.log(`Simpan maps ${id}`);
|
disabled={!selectedLocation || name === ""}
|
||||||
router.replace(`/portofolio/${id}`);
|
onPress={handleSubmit}
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
Simpan
|
Simpan
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
@@ -30,25 +105,34 @@ export default function MapsCreate() {
|
|||||||
<ViewWrapper footerComponent={buttonFooter}>
|
<ViewWrapper footerComponent={buttonFooter}>
|
||||||
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
|
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
|
||||||
|
|
||||||
<BaseBox style={{ height: 400 }}>
|
<BaseBox>
|
||||||
<TextCustom>Maps Her</TextCustom>
|
<MapSelected
|
||||||
|
selectedLocation={selectedLocation as any}
|
||||||
|
setSelectedLocation={setSelectedLocation}
|
||||||
|
/>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
required
|
required
|
||||||
label="Nama Pin"
|
label="Nama Pin"
|
||||||
placeholder="Masukkan nama pin maps"
|
placeholder="Masukkan nama pin maps"
|
||||||
|
value={name}
|
||||||
|
onChangeText={setName}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing height={50} />
|
<Spacing height={50} />
|
||||||
|
|
||||||
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
|
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
|
||||||
<LandscapeFrameUploaded />
|
<LandscapeFrameUploaded image={image?.uri} />
|
||||||
<ButtonCenteredOnly
|
<ButtonCenteredOnly
|
||||||
icon="upload"
|
icon="upload"
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
console.log("Upload foto ");
|
pickFile({
|
||||||
router.navigate(`/take-picture/${id}`);
|
allowedType: "image",
|
||||||
|
setImageUri(file) {
|
||||||
|
setImage(file);
|
||||||
|
},
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Upload
|
Upload
|
||||||
|
|||||||
@@ -1,19 +1,234 @@
|
|||||||
import { MapCustom, ViewWrapper } from "@/components";
|
import {
|
||||||
|
ButtonCustom,
|
||||||
|
DrawerCustom,
|
||||||
|
DummyLandscapeImage,
|
||||||
|
Grid,
|
||||||
|
Spacing,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
|
import API_IMAGE from "@/constants/api-storage";
|
||||||
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
|
import { apiMapsGetAll } from "@/service/api-client/api-maps";
|
||||||
|
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
|
||||||
|
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
||||||
|
import { Image } from "expo-image";
|
||||||
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import MapView from "react-native-maps";
|
import MapView, { Marker } from "react-native-maps";
|
||||||
|
|
||||||
|
const defaultRegion = {
|
||||||
|
latitude: -8.737109,
|
||||||
|
longitude: 115.1756897,
|
||||||
|
latitudeDelta: 0.1,
|
||||||
|
longitudeDelta: 0.1,
|
||||||
|
height: 300,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface LocationItem {
|
||||||
|
id: string | number;
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
name: string;
|
||||||
|
imageId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function Maps() {
|
export default function Maps() {
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
const [selected, setSelected] = useState({
|
||||||
|
id: "",
|
||||||
|
bidangBisnis: "",
|
||||||
|
nomorTelepon: "",
|
||||||
|
alamatBisnis: "",
|
||||||
|
namePin: "",
|
||||||
|
imageId: "",
|
||||||
|
portofolioId: "",
|
||||||
|
latitude: 0,
|
||||||
|
longitude: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
handlerLoadList();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlerLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiMapsGetAll();
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setList(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
|
<>
|
||||||
{/* <MapCustom height={"100%"} /> */}
|
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
|
||||||
<View style={{ flex: 1 }}>
|
{/* <MapCustom height={"100%"} /> */}
|
||||||
<MapView
|
<View style={{ flex: 1 }}>
|
||||||
style={{
|
{loadList ? (
|
||||||
width: "100%",
|
<MapView
|
||||||
height: "100%",
|
initialRegion={defaultRegion}
|
||||||
}}
|
style={{
|
||||||
/>
|
width: "100%",
|
||||||
</View>
|
height: "100%",
|
||||||
</ViewWrapper>
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<MapView
|
||||||
|
initialRegion={defaultRegion}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{list?.map((item: any, index: number) => {
|
||||||
|
return (
|
||||||
|
<Marker
|
||||||
|
key={item?.id}
|
||||||
|
coordinate={{
|
||||||
|
latitude: item?.latitude,
|
||||||
|
longitude: item?.longitude,
|
||||||
|
}}
|
||||||
|
title={item?.namePin}
|
||||||
|
onPress={() => {
|
||||||
|
setOpenDrawer(true);
|
||||||
|
setSelected({
|
||||||
|
id: item?.id,
|
||||||
|
bidangBisnis:
|
||||||
|
item?.Portofolio?.MasterBidangBisnis?.name,
|
||||||
|
nomorTelepon: item?.Portofolio?.tlpn,
|
||||||
|
alamatBisnis: item?.Portofolio?.alamatKantor,
|
||||||
|
namePin: item?.namePin,
|
||||||
|
imageId: item?.imageId,
|
||||||
|
portofolioId: item?.Portofolio?.id,
|
||||||
|
latitude: item?.latitude,
|
||||||
|
longitude: item?.longitude,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
// Gunakan gambar kustom jika tersedia
|
||||||
|
>
|
||||||
|
<View style={{}}>
|
||||||
|
<Image
|
||||||
|
source={{
|
||||||
|
uri: API_IMAGE.GET({
|
||||||
|
fileId: item?.Portofolio?.logoId,
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
borderRadius: 100,
|
||||||
|
borderWidth: 1,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</Marker>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</MapView>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</ViewWrapper>
|
||||||
|
|
||||||
|
<DrawerCustom
|
||||||
|
isVisible={openDrawer}
|
||||||
|
closeDrawer={() => setOpenDrawer(false)}
|
||||||
|
height={"auto"}
|
||||||
|
>
|
||||||
|
<DummyLandscapeImage height={200} imageId={selected.imageId} />
|
||||||
|
<Spacing />
|
||||||
|
<StackCustom gap={"xs"}>
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<FontAwesome
|
||||||
|
name="building-o"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>{selected.namePin}</TextCustom>}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<Ionicons
|
||||||
|
name="list-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<Ionicons
|
||||||
|
name="call-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>{selected.nomorTelepon}</TextCustom>}
|
||||||
|
/>
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<Ionicons
|
||||||
|
name="location-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={6} style={{ paddingRight: 10 }}>
|
||||||
|
<ButtonCustom
|
||||||
|
onPress={() => {
|
||||||
|
setOpenDrawer(false);
|
||||||
|
router.push(`/portofolio/${selected.portofolioId}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</ButtonCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={6} style={{ paddingLeft: 10 }}>
|
||||||
|
<ButtonCustom
|
||||||
|
onPress={() => {
|
||||||
|
openInDeviceMaps({
|
||||||
|
latitude: selected.latitude,
|
||||||
|
longitude: selected.longitude,
|
||||||
|
title: selected.namePin,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Buka Maps
|
||||||
|
</ButtonCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</StackCustom>
|
||||||
|
</DrawerCustom>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
ViewWrapper
|
ViewWrapper
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
@@ -11,10 +11,10 @@ import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
|||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { apiFileDelete } from "@/service/api-client/api-file";
|
import { apiFileDelete } from "@/service/api-client/api-file";
|
||||||
import {
|
import {
|
||||||
apiGetOnePortofolio,
|
apiGetOnePortofolio,
|
||||||
apiUpdatePortofolio,
|
apiUpdatePortofolio,
|
||||||
} from "@/service/api-client/api-portofolio";
|
} from "@/service/api-client/api-portofolio";
|
||||||
import { uploadImageService } from "@/service/upload-service";
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
import pickImage from "@/utils/pickImage";
|
import pickImage from "@/utils/pickImage";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
@@ -45,7 +45,7 @@ export default function PortofolioEditLogo() {
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const response = await uploadImageService({
|
const response = await uploadFileService({
|
||||||
imageUri,
|
imageUri,
|
||||||
dirId: DIRECTORY_ID.portofolio_logo,
|
dirId: DIRECTORY_ID.portofolio_logo,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { DrawerCustom, LoaderCustom, Spacing, StackCustom } from "@/components";
|
import {
|
||||||
|
ButtonCustom,
|
||||||
|
DrawerCustom,
|
||||||
|
DummyLandscapeImage,
|
||||||
|
LoaderCustom,
|
||||||
|
Spacing,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
} from "@/components";
|
||||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||||
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import Portofolio_BusinessLocation from "@/screens/Portofolio/BusinessLocationSection";
|
import Portofolio_BusinessLocation from "@/screens/Portofolio/BusinessLocationSection";
|
||||||
import Portofolio_ButtonDelete from "@/screens/Portofolio/ButtonDelete";
|
import Portofolio_ButtonDelete from "@/screens/Portofolio/ButtonDelete";
|
||||||
@@ -13,19 +23,20 @@ import Portofolio_SocialMediaSection from "@/screens/Portofolio/SocialMediaSecti
|
|||||||
import { apiGetOnePortofolio } from "@/service/api-client/api-portofolio";
|
import { apiGetOnePortofolio } from "@/service/api-client/api-portofolio";
|
||||||
import { apiUser } from "@/service/api-client/api-user";
|
import { apiUser } from "@/service/api-client/api-user";
|
||||||
import { GStyles } from "@/styles/global-styles";
|
import { GStyles } from "@/styles/global-styles";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
|
||||||
|
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
||||||
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { TouchableOpacity } from "react-native";
|
import { TouchableOpacity } from "react-native";
|
||||||
|
|
||||||
export default function Portofolio() {
|
export default function Portofolio() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||||
const [isLoadingDelete, setIsLoadingDelete] = useState(false);
|
const [isLoadingDelete, setIsLoadingDelete] = useState(false);
|
||||||
const [data, setData] = useState<any>();
|
const [data, setData] = useState<any>();
|
||||||
const [profileId, setProfileId] = useState<any>();
|
const [profileId, setProfileId] = useState<any>();
|
||||||
|
const [openDrawerLocation, setOpenDrawerLocation] = useState(false);
|
||||||
const { user } = useAuth();
|
|
||||||
|
|
||||||
const openDrawer = () => {
|
const openDrawer = () => {
|
||||||
setIsDrawerOpen(true);
|
setIsDrawerOpen(true);
|
||||||
@@ -43,19 +54,13 @@ export default function Portofolio() {
|
|||||||
|
|
||||||
async function onLoadData(id: string) {
|
async function onLoadData(id: string) {
|
||||||
const response = await apiGetOnePortofolio({ id: id });
|
const response = await apiGetOnePortofolio({ id: id });
|
||||||
console.log(
|
|
||||||
"[PROFILE ID]>>",
|
|
||||||
JSON.stringify(response.data.Profile.id, null, 2)
|
|
||||||
);
|
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const onLoadUserByToken = async () => {
|
const onLoadUserByToken = async () => {
|
||||||
const response = await apiUser(user?.id as string);
|
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);
|
setProfileId(response?.data?.Profile?.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -89,15 +94,21 @@ export default function Portofolio() {
|
|||||||
data={data}
|
data={data}
|
||||||
listSubBidang={data?.Portofolio_BidangDanSubBidangBisnis as any[]}
|
listSubBidang={data?.Portofolio_BidangDanSubBidangBisnis as any[]}
|
||||||
/>
|
/>
|
||||||
<Portofolio_BusinessLocation />
|
<Portofolio_BusinessLocation
|
||||||
|
data={data?.BusinessMaps}
|
||||||
|
imageId={data?.logoId}
|
||||||
|
setOpenDrawerLocation={setOpenDrawerLocation}
|
||||||
|
/>
|
||||||
<Portofolio_SocialMediaSection
|
<Portofolio_SocialMediaSection
|
||||||
data={data?.Portofolio_MediaSosial}
|
data={data?.Portofolio_MediaSosial}
|
||||||
/>
|
/>
|
||||||
<Portofolio_ButtonDelete
|
{data?.Profile?.id !== profileId ? null : (
|
||||||
id={id as string}
|
<Portofolio_ButtonDelete
|
||||||
isLoadingDelete={isLoadingDelete}
|
id={id as string}
|
||||||
setIsLoadingDelete={setIsLoadingDelete}
|
isLoadingDelete={isLoadingDelete}
|
||||||
/>
|
setIsLoadingDelete={setIsLoadingDelete}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
)}
|
)}
|
||||||
@@ -110,10 +121,93 @@ export default function Portofolio() {
|
|||||||
height={"auto"}
|
height={"auto"}
|
||||||
>
|
>
|
||||||
<Portofolio_MenuDrawerSection
|
<Portofolio_MenuDrawerSection
|
||||||
drawerItems={drawerItemsPortofolio({ id: id as string })}
|
drawerItems={drawerItemsPortofolio({
|
||||||
|
id: id as string,
|
||||||
|
maps: data?.BusinessMaps,
|
||||||
|
})}
|
||||||
setIsDrawerOpen={setIsDrawerOpen}
|
setIsDrawerOpen={setIsDrawerOpen}
|
||||||
/>
|
/>
|
||||||
</DrawerCustom>
|
</DrawerCustom>
|
||||||
|
|
||||||
|
{/* Drawer Lokasi */}
|
||||||
|
<DrawerCustom
|
||||||
|
isVisible={openDrawerLocation}
|
||||||
|
closeDrawer={() => setOpenDrawerLocation(false)}
|
||||||
|
height={"auto"}
|
||||||
|
>
|
||||||
|
<DummyLandscapeImage
|
||||||
|
height={200}
|
||||||
|
imageId={data?.BusinessMaps?.imageId}
|
||||||
|
/>
|
||||||
|
<Spacing />
|
||||||
|
<StackCustom gap={"xs"}>
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<FontAwesome
|
||||||
|
name="building-o"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>{data?.BusinessMaps?.namePin}</TextCustom>}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<Ionicons
|
||||||
|
name="list-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={
|
||||||
|
<TextCustom>{data?.MasterBidangBisnis?.name}</TextCustom>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<Ionicons
|
||||||
|
name="call-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>{data?.tlpn}</TextCustom>}
|
||||||
|
/>
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<Ionicons
|
||||||
|
name="location-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>{data?.alamatKantor}</TextCustom>}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Spacing />
|
||||||
|
<ButtonCustom
|
||||||
|
onPress={() => {
|
||||||
|
openInDeviceMaps({
|
||||||
|
latitude: data?.BusinessMaps?.latitude,
|
||||||
|
longitude: data?.BusinessMaps?.longitude,
|
||||||
|
title: data?.BusinessMaps?.namePin,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Buka Maps
|
||||||
|
</ButtonCustom>
|
||||||
|
</StackCustom>
|
||||||
|
</DrawerCustom>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function Profile() {
|
|||||||
const [dataToken, setDataToken] = useState<IProfile>();
|
const [dataToken, setDataToken] = useState<IProfile>();
|
||||||
const [listPortofolio, setListPortofolio] = useState<any[]>();
|
const [listPortofolio, setListPortofolio] = useState<any[]>();
|
||||||
|
|
||||||
const { logout, isAdmin, user } = useAuth();
|
const { token, logout, isAdmin, user, userData } = useAuth();
|
||||||
|
|
||||||
const openDrawer = () => {
|
const openDrawer = () => {
|
||||||
setIsDrawerOpen(true);
|
setIsDrawerOpen(true);
|
||||||
@@ -42,7 +42,8 @@ export default function Profile() {
|
|||||||
onLoadPortofolio(id as string);
|
onLoadPortofolio(id as string);
|
||||||
onLoadUserByToken();
|
onLoadUserByToken();
|
||||||
isUserCheck();
|
isUserCheck();
|
||||||
}, [id])
|
userData(token as string);
|
||||||
|
}, [id, token])
|
||||||
);
|
);
|
||||||
|
|
||||||
const isUserCheck = () => {
|
const isUserCheck = () => {
|
||||||
@@ -136,10 +137,7 @@ const ButtonnDot = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const isId = id === undefined || id === null;
|
const isId = id === undefined || id === null;
|
||||||
|
|
||||||
console.log("ID CHECK", id);
|
|
||||||
|
|
||||||
if (isId) {
|
if (isId) {
|
||||||
console.log("ID UNDEFINED", id);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TouchableOpacity onPress={logout}>
|
<TouchableOpacity onPress={logout}>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||||
@@ -11,7 +11,7 @@ import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
|||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { apiFileDelete } from "@/service/api-client/api-file";
|
import { apiFileDelete } from "@/service/api-client/api-file";
|
||||||
import { apiProfile, apiUpdateProfile } from "@/service/api-client/api-profile";
|
import { apiProfile, apiUpdateProfile } from "@/service/api-client/api-profile";
|
||||||
import { uploadImageService } from "@/service/upload-service";
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
import { IProfile } from "@/types/Type-Profile";
|
import { IProfile } from "@/types/Type-Profile";
|
||||||
import pickImage from "@/utils/pickImage";
|
import pickImage from "@/utils/pickImage";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
@@ -45,7 +45,7 @@ export default function UpdateBackgroundProfile() {
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const response = await uploadImageService({
|
const response = await uploadFileService({
|
||||||
imageUri,
|
imageUri,
|
||||||
dirId: DIRECTORY_ID.profile_background,
|
dirId: DIRECTORY_ID.profile_background,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,16 +8,16 @@ import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
|||||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { apiFileDelete } from "@/service/api-client/api-file";
|
||||||
import { apiProfile, apiUpdateProfile } from "@/service/api-client/api-profile";
|
import { apiProfile, apiUpdateProfile } from "@/service/api-client/api-profile";
|
||||||
import { uploadImageService } from "@/service/upload-service";
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
import { IProfile } from "@/types/Type-Profile";
|
import { IProfile } from "@/types/Type-Profile";
|
||||||
import pickImage from "@/utils/pickImage";
|
import pickImage from "@/utils/pickImage";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { Image } from "react-native";
|
import { Image } from "react-native";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { apiFileDelete } from "@/service/api-client/api-file";
|
|
||||||
|
|
||||||
export default function UpdatePhotoProfile() {
|
export default function UpdatePhotoProfile() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
@@ -46,7 +46,7 @@ export default function UpdatePhotoProfile() {
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
const response = await uploadImageService({
|
const response = await uploadFileService({
|
||||||
imageUri,
|
imageUri,
|
||||||
dirId: DIRECTORY_ID.profile_foto,
|
dirId: DIRECTORY_ID.profile_foto,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import BoxButtonOnFooter from "@/components/Box/BoxButtonOnFooter";
|
import BoxButtonOnFooter from "@/components/Box/BoxButtonOnFooter";
|
||||||
import InformationBox from "@/components/Box/InformationBox";
|
import InformationBox from "@/components/Box/InformationBox";
|
||||||
@@ -15,7 +15,7 @@ import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
|||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { apiCreateProfile } from "@/service/api-client/api-profile";
|
import { apiCreateProfile } from "@/service/api-client/api-profile";
|
||||||
import { apiValidationEmail } from "@/service/api-client/api-validation";
|
import { apiValidationEmail } from "@/service/api-client/api-validation";
|
||||||
import { uploadImageService } from "@/service/upload-service";
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
import pickImage from "@/utils/pickImage";
|
import pickImage from "@/utils/pickImage";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@@ -69,7 +69,7 @@ export default function CreateProfile() {
|
|||||||
|
|
||||||
if (imagePhoto) {
|
if (imagePhoto) {
|
||||||
try {
|
try {
|
||||||
const responseUploadPhoto = await uploadImageService({
|
const responseUploadPhoto = await uploadFileService({
|
||||||
imageUri: imagePhoto,
|
imageUri: imagePhoto,
|
||||||
dirId: DIRECTORY_ID.profile_foto,
|
dirId: DIRECTORY_ID.profile_foto,
|
||||||
});
|
});
|
||||||
@@ -90,7 +90,7 @@ export default function CreateProfile() {
|
|||||||
|
|
||||||
if (imageBackground) {
|
if (imageBackground) {
|
||||||
try {
|
try {
|
||||||
const responseUploadBackground = await uploadImageService({
|
const responseUploadBackground = await uploadFileService({
|
||||||
imageUri: imageBackground,
|
imageUri: imageBackground,
|
||||||
dirId: DIRECTORY_ID.profile_background,
|
dirId: DIRECTORY_ID.profile_background,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { apiUser } from "@/service/api-client/api-user";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
@@ -19,12 +20,25 @@ export default function WaitingRoom() {
|
|||||||
async function handleCheck() {
|
async function handleCheck() {
|
||||||
try {
|
try {
|
||||||
const response = await userData(token as string);
|
const response = await userData(token as string);
|
||||||
|
|
||||||
if (response.active) {
|
if (response.active) {
|
||||||
Toast.show({
|
const checkProfile = await apiUser(response.id);
|
||||||
type: "success",
|
|
||||||
text1: "Akun anda telah aktif", // text2: "Anda berhasil login",
|
if (checkProfile?.data?.Profile) {
|
||||||
});
|
Toast.show({
|
||||||
router.replace(`/(application)/(user)/profile/create`);
|
type: "success",
|
||||||
|
text1: "Akun anda telah aktif kembali", // text2: "Anda berhasil login",
|
||||||
|
});
|
||||||
|
router.replace(`/(application)/(user)/home`);
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Akun anda telah aktif", // text2: "Anda berhasil login",
|
||||||
|
});
|
||||||
|
router.replace(`/(application)/(user)/profile/create`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// router.replace(`/(application)/(user)/profile/create`);
|
||||||
} else {
|
} else {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "error",
|
type: "error",
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ import {
|
|||||||
ICON_SIZE_XLARGE,
|
ICON_SIZE_XLARGE,
|
||||||
} from "@/constants/constans-value";
|
} from "@/constants/constans-value";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { adminListMenu } from "@/screens/Admin/listPageAdmin";
|
import {
|
||||||
|
adminListMenu,
|
||||||
|
superAdminListMenu,
|
||||||
|
} from "@/screens/Admin/listPageAdmin";
|
||||||
import { GStyles } from "@/styles/global-styles";
|
import { GStyles } from "@/styles/global-styles";
|
||||||
import { FontAwesome6, Ionicons } from "@expo/vector-icons";
|
import { FontAwesome6, Ionicons } from "@expo/vector-icons";
|
||||||
import { router, Stack } from "expo-router";
|
import { router, Stack } from "expo-router";
|
||||||
@@ -24,8 +27,11 @@ import { useState } from "react";
|
|||||||
export default function AdminLayout() {
|
export default function AdminLayout() {
|
||||||
const [openDrawerNavbar, setOpenDrawerNavbar] = useState(false);
|
const [openDrawerNavbar, setOpenDrawerNavbar] = useState(false);
|
||||||
const [openDrawerUser, setOpenDrawerUser] = useState(false);
|
const [openDrawerUser, setOpenDrawerUser] = useState(false);
|
||||||
|
// const [user, setUser] = useState(null);
|
||||||
|
|
||||||
const { logout } = useAuth();
|
const { logout, user } = useAuth();
|
||||||
|
|
||||||
|
console.log("[USER LAYOUT]", JSON.stringify(user, null, 2));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -56,58 +62,58 @@ export default function AdminLayout() {
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack.Screen name="dashboard" />
|
{/* <Stack.Screen name="dashboard" /> */}
|
||||||
{/* ================== Investment Start ================== */}
|
{/* ================== Investment Start ================== */}
|
||||||
<Stack.Screen name="investment/index" />
|
{/* <Stack.Screen name="investment/index" /> */}
|
||||||
{/* ================== Investment End ================== */}
|
{/* ================== Investment End ================== */}
|
||||||
|
|
||||||
{/* ================== Maps Start ================== */}
|
{/* ================== Maps Start ================== */}
|
||||||
<Stack.Screen name="maps" />
|
{/* <Stack.Screen name="maps" /> */}
|
||||||
{/* ================== Maps End ================== */}
|
{/* ================== Maps End ================== */}
|
||||||
|
|
||||||
{/* ================== App Information Start ================== */}
|
{/* ================== App Information Start ================== */}
|
||||||
<Stack.Screen name="app-information/index" />
|
{/* <Stack.Screen name="app-information/index" /> */}
|
||||||
{/* ================== App Information End ================== */}
|
{/* ================== App Information End ================== */}
|
||||||
|
|
||||||
{/* ================== Job Start ================== */}
|
{/* ================== Job Start ================== */}
|
||||||
<Stack.Screen name="job/index" />
|
{/* <Stack.Screen name="job/index" /> */}
|
||||||
{/* <Stack.Screen name="job/publish" />
|
{/* <Stack.Screen name="job/publish" />
|
||||||
<Stack.Screen name="job/review" />
|
<Stack.Screen name="job/review" />
|
||||||
<Stack.Screen name="job/reject" /> */}
|
<Stack.Screen name="job/reject" /> */}
|
||||||
<Stack.Screen name="job/[status]/status" />
|
{/* <Stack.Screen name="job/[status]/status" />
|
||||||
<Stack.Screen name="job/[id]/[status]/index" />
|
<Stack.Screen name="job/[id]/[status]/index" /> */}
|
||||||
|
|
||||||
{/* ================== Collaboration Start ================== */}
|
{/* ================== Collaboration Start ================== */}
|
||||||
<Stack.Screen name="collaboration/index" />
|
{/* <Stack.Screen name="collaboration/index" /> */}
|
||||||
<Stack.Screen name="collaboration/publish" />
|
{/* <Stack.Screen name="collaboration/publish" />
|
||||||
<Stack.Screen name="collaboration/group" />
|
<Stack.Screen name="collaboration/group" />
|
||||||
<Stack.Screen name="collaboration/reject" />
|
<Stack.Screen name="collaboration/reject" />
|
||||||
<Stack.Screen name="collaboration/[id]/[status]" />
|
<Stack.Screen name="collaboration/[id]/[status]" />
|
||||||
<Stack.Screen name="collaboration/[id]/group" />
|
<Stack.Screen name="collaboration/[id]/group" /> */}
|
||||||
{/* ================== Collaboration End ================== */}
|
{/* ================== Collaboration End ================== */}
|
||||||
|
|
||||||
{/* ================== Forum Start ================== */}
|
{/* ================== Forum Start ================== */}
|
||||||
<Stack.Screen name="forum/index" />
|
{/* <Stack.Screen name="forum/index" /> */}
|
||||||
<Stack.Screen name="forum/[id]/index" />
|
{/* <Stack.Screen name="forum/[id]/index" />
|
||||||
<Stack.Screen name="forum/report-comment" />
|
<Stack.Screen name="forum/report-comment" />
|
||||||
<Stack.Screen name="forum/report-posting" />
|
<Stack.Screen name="forum/report-posting" />
|
||||||
<Stack.Screen name="forum/[id]/list-report-posting" />
|
<Stack.Screen name="forum/[id]/list-report-posting" />
|
||||||
<Stack.Screen name="forum/[id]/list-report-comment" />
|
<Stack.Screen name="forum/[id]/list-report-comment" /> */}
|
||||||
{/* ================== Forum End ================== */}
|
{/* ================== Forum End ================== */}
|
||||||
|
|
||||||
{/* ================== Voting Start ================== */}
|
{/* ================== Voting Start ================== */}
|
||||||
<Stack.Screen name="voting/index" />
|
{/* <Stack.Screen name="voting/index" />
|
||||||
<Stack.Screen name="voting/[status]/status" />
|
<Stack.Screen name="voting/[status]/status" />
|
||||||
<Stack.Screen name="voting/[id]/[status]/index" />
|
<Stack.Screen name="voting/[id]/[status]/index" />
|
||||||
<Stack.Screen name="voting/[id]/reject-input" />
|
<Stack.Screen name="voting/[id]/reject-input" /> */}
|
||||||
{/* ================== Voting End ================== */}
|
{/* ================== Voting End ================== */}
|
||||||
|
|
||||||
{/* ================== Event Start ================== */}
|
{/* ================== Event Start ================== */}
|
||||||
<Stack.Screen name="event/index" />
|
{/* <Stack.Screen name="event/index" />
|
||||||
<Stack.Screen name="event/[status]/status" />
|
<Stack.Screen name="event/[status]/status" />
|
||||||
<Stack.Screen name="event/type-of-event" />
|
<Stack.Screen name="event/type-of-event" />
|
||||||
<Stack.Screen name="event/type-create" />
|
<Stack.Screen name="event/type-create" />
|
||||||
<Stack.Screen name="event/type-update" />
|
<Stack.Screen name="event/type-update" /> */}
|
||||||
{/* <Stack.Screen name="event/[id]/[status]/index" />
|
{/* <Stack.Screen name="event/[id]/[status]/index" />
|
||||||
<Stack.Screen name="event/[id]/reject-input"/> */}
|
<Stack.Screen name="event/[id]/reject-input"/> */}
|
||||||
{/* ================== Event End ================== */}
|
{/* ================== Event End ================== */}
|
||||||
@@ -134,7 +140,11 @@ export default function AdminLayout() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<NavbarMenu
|
<NavbarMenu
|
||||||
items={adminListMenu}
|
items={
|
||||||
|
user?.masterUserRoleId === "2"
|
||||||
|
? adminListMenu
|
||||||
|
: superAdminListMenu
|
||||||
|
}
|
||||||
onClose={() => setOpenDrawerNavbar(false)}
|
onClose={() => setOpenDrawerNavbar(false)}
|
||||||
/>
|
/>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
@@ -155,7 +165,7 @@ export default function AdminLayout() {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<TextCustom>Username</TextCustom>
|
<TextCustom>{user?.username || "-"}</TextCustom>
|
||||||
</GridComponentView>
|
</GridComponentView>
|
||||||
<GridComponentView
|
<GridComponentView
|
||||||
leftIcon={
|
leftIcon={
|
||||||
@@ -166,7 +176,13 @@ export default function AdminLayout() {
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<TextCustom>User Role</TextCustom>
|
<TextCustom>
|
||||||
|
{user
|
||||||
|
? user?.masterUserRoleId === "2"
|
||||||
|
? "Admin"
|
||||||
|
: "Super Admin"
|
||||||
|
: "-"}
|
||||||
|
</TextCustom>
|
||||||
</GridComponentView>
|
</GridComponentView>
|
||||||
|
|
||||||
<MenuDrawerDynamicGrid
|
<MenuDrawerDynamicGrid
|
||||||
|
|||||||
@@ -1,18 +1,89 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import { router } from "expo-router";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import {
|
||||||
|
apiAdminMasterBusinessFieldById,
|
||||||
|
apiAdminMasterBusinessFieldUpdate,
|
||||||
|
} from "@/service/api-admin/api-master-admin";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { Switch } from "react-native-paper";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminAppInformation_BusinessFieldDetail() {
|
export default function AdminAppInformation_BusinessFieldDetail() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadDetail();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadDetail = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiAdminMasterBusinessFieldById({
|
||||||
|
id: id as string,
|
||||||
|
});
|
||||||
|
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
setData(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
if (!data.name) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Lengkapi Data",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const response = await apiAdminMasterBusinessFieldUpdate({
|
||||||
|
id: id as string,
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal update data",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Data berhasil di update",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonSubmit = (
|
const buttonSubmit = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
onPress={() => router.back()}
|
disabled={!data?.name}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={() => handlerSubmit()}
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
@@ -28,6 +99,15 @@ export default function AdminAppInformation_BusinessFieldDetail() {
|
|||||||
label="Nama Bidang Bisnis"
|
label="Nama Bidang Bisnis"
|
||||||
placeholder="Masukan Nama Bidang Bisnis"
|
placeholder="Masukan Nama Bidang Bisnis"
|
||||||
required
|
required
|
||||||
|
value={data?.name}
|
||||||
|
onChangeText={(value) => setData({ ...data, name: value })}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextCustom>Status Aktivasi</TextCustom>
|
||||||
|
<Switch
|
||||||
|
color={MainColor.yellow}
|
||||||
|
value={data?.active}
|
||||||
|
onValueChange={(value) => setData({ ...data, active: value })}
|
||||||
/>
|
/>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|||||||
@@ -6,13 +6,52 @@ import {
|
|||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
|
import { apiAdminMasterBusinessFieldCreate } from "@/service/api-admin/api-master-admin";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminAppInformation_BusinessFieldCreate() {
|
export default function AdminAppInformation_BusinessFieldCreate() {
|
||||||
|
const [data, setData] = useState<any>({
|
||||||
|
name: "",
|
||||||
|
});
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
if (!data.name) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Lengkapi Data",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const response = await apiAdminMasterBusinessFieldCreate({ data: data });
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Data berhasil di tambah",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal tambah data",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonSubmit = (
|
const buttonSubmit = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
onPress={() => router.back()}
|
onPress={() => handlerSubmit()}
|
||||||
|
isLoading={isLoading}
|
||||||
>
|
>
|
||||||
Tambah
|
Tambah
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
@@ -28,6 +67,8 @@ export default function AdminAppInformation_BusinessFieldCreate() {
|
|||||||
label="Nama Bidang Bisnis"
|
label="Nama Bidang Bisnis"
|
||||||
placeholder="Masukan Nama Bidang Bisnis"
|
placeholder="Masukan Nama Bidang Bisnis"
|
||||||
required
|
required
|
||||||
|
value={data.name}
|
||||||
|
onChangeText={(value) => setData({ ...data, name: value })}
|
||||||
/>
|
/>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
import {
|
import { ScrollableCustom, StackCustom, ViewWrapper } from "@/components";
|
||||||
ScrollableCustom,
|
|
||||||
StackCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
|
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||||
import AdminAppInformation_BusinessFieldSection from "@/screens/Admin/App-Information/BusinessFieldSection";
|
import AdminAppInformation_BusinessFieldSection from "@/screens/Admin/App-Information/BusinessFieldSection";
|
||||||
@@ -10,6 +6,7 @@ import AdminAppInformation_Bank from "@/screens/Admin/App-Information/Informatio
|
|||||||
import AdminAppInformation_StickerSection from "@/screens/Admin/App-Information/StickerSection";
|
import AdminAppInformation_StickerSection from "@/screens/Admin/App-Information/StickerSection";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { Alert } from "react-native";
|
||||||
|
|
||||||
export default function AdminInformation() {
|
export default function AdminInformation() {
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>("bank");
|
const [activeCategory, setActiveCategory] = useState<string | null>("bank");
|
||||||
@@ -57,7 +54,8 @@ export default function AdminInformation() {
|
|||||||
} else if (activeCategory === "business") {
|
} else if (activeCategory === "business") {
|
||||||
router.push("/admin/app-information/business-field/create");
|
router.push("/admin/app-information/business-field/create");
|
||||||
} else if (activeCategory === "sticker") {
|
} else if (activeCategory === "sticker") {
|
||||||
router.push("/admin/app-information/sticker/create");
|
Alert.alert("Coming Soon", "Next Update");
|
||||||
|
// router.push("/admin/app-information/sticker/create");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,18 +1,90 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
StackCustom,
|
CenterCustom,
|
||||||
TextInputCustom,
|
Grid,
|
||||||
ViewWrapper,
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
TextInputCustom,
|
||||||
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import { router } from "expo-router";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import {
|
||||||
|
apiAdminMasterBankById,
|
||||||
|
apiAdminMasterBankUpdate,
|
||||||
|
} from "@/service/api-admin/api-master-admin";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { Switch } from "react-native-paper";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminAppInformation_BankDetail() {
|
export default function AdminAppInformation_BankDetail() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadList();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadList = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiAdminMasterBankById({ id: id as string });
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setData(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerUpdate = async () => {
|
||||||
|
if (!data.namaBank || !data.namaAkun || !data.norek) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Lengkapi Data",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiAdminMasterBankUpdate({
|
||||||
|
id: id as string,
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal update data",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Success",
|
||||||
|
text2: "Data berhasil di update",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonSubmit = (
|
const buttonSubmit = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
onPress={() => router.back()}
|
disabled={!data?.namaBank || !data?.namaAkun || !data?.norek}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={() => handlerUpdate()}
|
||||||
>
|
>
|
||||||
Update
|
Update
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
@@ -25,22 +97,46 @@ export default function AdminAppInformation_BankDetail() {
|
|||||||
<AdminBackButtonAntTitle title="Update Bank" />
|
<AdminBackButtonAntTitle title="Update Bank" />
|
||||||
|
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<TextInputCustom
|
<Grid>
|
||||||
label="Nama Bank"
|
<Grid.Col span={6}>
|
||||||
placeholder="Masukan Nama Bank"
|
<TextInputCustom
|
||||||
required
|
label="Nama Bank"
|
||||||
/>
|
placeholder="Masukan Nama Bank"
|
||||||
|
required
|
||||||
|
value={data?.namaBank}
|
||||||
|
onChangeText={(value) =>
|
||||||
|
setData({ ...data, namaBank: value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={6} style={{ alignItems: "center" }}>
|
||||||
|
<TextCustom>Status Aktivasi</TextCustom>
|
||||||
|
<CenterCustom>
|
||||||
|
<Switch
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setData({ ...data, isActive: value })
|
||||||
|
}
|
||||||
|
color={MainColor.yellow}
|
||||||
|
value={data?.isActive}
|
||||||
|
/>
|
||||||
|
</CenterCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
label="Nama Rekening"
|
label="Nama Rekening"
|
||||||
placeholder="Masukan Nama Rekening"
|
placeholder="Masukan Nama Rekening"
|
||||||
required
|
required
|
||||||
|
value={data?.namaAkun}
|
||||||
|
onChangeText={(value) => setData({ ...data, namaAkun: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
label="Nomor Rekening"
|
label="Nomor Rekening"
|
||||||
placeholder="Masukan Nomor Rekening"
|
placeholder="Masukan Nomor Rekening"
|
||||||
required
|
required
|
||||||
|
value={data?.norek}
|
||||||
|
onChangeText={(value) => setData({ ...data, norek: value })}
|
||||||
/>
|
/>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|||||||
@@ -1,19 +1,51 @@
|
|||||||
import {
|
import {
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
|
import { apiAdminMasterBankCreate } from "@/service/api-admin/api-master-admin";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminAppInformation_BankCreate() {
|
export default function AdminAppInformation_BankCreate() {
|
||||||
|
const [data, setData] = useState<any>({
|
||||||
|
namaBank: "",
|
||||||
|
namaAkun: "",
|
||||||
|
norek: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiAdminMasterBankCreate({ data: data });
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Data berhasil di tambah",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal tambah data",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const buttonSubmit = (
|
const buttonSubmit = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom isLoading={isLoading} onPress={handlerSubmit}>
|
||||||
onPress={() => router.back()}
|
|
||||||
>
|
|
||||||
Tambah
|
Tambah
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
</BoxButtonOnFooter>
|
</BoxButtonOnFooter>
|
||||||
@@ -29,18 +61,25 @@ export default function AdminAppInformation_BankCreate() {
|
|||||||
label="Nama Bank"
|
label="Nama Bank"
|
||||||
placeholder="Masukan Nama Bank"
|
placeholder="Masukan Nama Bank"
|
||||||
required
|
required
|
||||||
|
value={data.namaBank}
|
||||||
|
onChangeText={(value) => setData({ ...data, namaBank: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
label="Nama Rekening"
|
label="Nama Rekening"
|
||||||
placeholder="Masukan Nama Rekening"
|
placeholder="Masukan Nama Rekening"
|
||||||
required
|
required
|
||||||
|
value={data.namaAkun}
|
||||||
|
onChangeText={(value) => setData({ ...data, namaAkun: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
|
keyboardType="numeric"
|
||||||
label="Nomor Rekening"
|
label="Nomor Rekening"
|
||||||
placeholder="Masukan Nomor Rekening"
|
placeholder="Masukan Nomor Rekening"
|
||||||
required
|
required
|
||||||
|
value={data.norek}
|
||||||
|
onChangeText={(value) => setData({ ...data, norek: value })}
|
||||||
/>
|
/>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
@@ -8,18 +9,45 @@ import {
|
|||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { useLocalSearchParams } from "expo-router";
|
import { apiAdminCollaborationGetById } from "@/service/api-admin/api-admin-collaboration";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function AdminCollaborationPublish() {
|
export default function AdminCollaborationPublish() {
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
console.log("params:", id, status);
|
const [data, setData] = useState<any | null>(null);
|
||||||
const bottomFooter = (
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
handlerLoadData();
|
||||||
|
}, [status])
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlerLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiAdminCollaborationGetById({
|
||||||
|
id: id as string,
|
||||||
|
category: status as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setData(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const bottomFooter = status === "publish" && (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
backgroundColor={MainColor.red}
|
backgroundColor={MainColor.red}
|
||||||
textColor="white"
|
textColor="white"
|
||||||
onPress={() => {}}
|
onPress={() => {
|
||||||
|
router.push(`/admin/collaboration/${id}/reject-input`);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Reject
|
Reject
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
@@ -29,12 +57,12 @@ export default function AdminCollaborationPublish() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper
|
<ViewWrapper
|
||||||
headerComponent={<AdminBackButtonAntTitle title={`Detail ${status}`} />}
|
headerComponent={<AdminBackButtonAntTitle title={`Detail`} />}
|
||||||
footerComponent={bottomFooter}
|
footerComponent={bottomFooter}
|
||||||
>
|
>
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
{listData.map((item, i) => (
|
{listData(data)?.map((item, i) => (
|
||||||
<Grid key={i}>
|
<Grid key={i}>
|
||||||
<Grid.Col
|
<Grid.Col
|
||||||
span={4}
|
span={4}
|
||||||
@@ -49,41 +77,49 @@ export default function AdminCollaborationPublish() {
|
|||||||
))}
|
))}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
|
{data?.report && (
|
||||||
|
<BaseBox>
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={4}
|
||||||
|
spanRight={8}
|
||||||
|
leftIcon={<TextCustom bold>Catatan report</TextCustom>}
|
||||||
|
rightIcon={<TextCustom>{data?.report}</TextCustom>}
|
||||||
|
/>
|
||||||
|
</BaseBox>
|
||||||
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const listData = [
|
const listData = (data: any) => [
|
||||||
{
|
{
|
||||||
label: "Username",
|
label: "Username",
|
||||||
value: "Bagas Banuna",
|
value: (data && data?.Author?.username) || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Judul Proyek",
|
label: "Judul Proyek",
|
||||||
value:
|
value: (data && data?.title) || "-",
|
||||||
"Judul Proyek: Lorem ipsum dolor sit amet consectetur adipisicing elit.",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Industri",
|
label: "Industri",
|
||||||
value: "Kesehatan",
|
value: (data && data?.ProjectCollaborationMaster_Industri?.name) || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Jumlah Partisipan ",
|
label: "Jumlah Partisipan ",
|
||||||
value: "0",
|
value: (data && data?.ProjectCollaboration_Partisipasi.length) || "0",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Lokasi",
|
label: "Lokasi",
|
||||||
value: "Kuta Selatan, Bali",
|
value: (data && data?.lokasi) || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Tujuan Proyek",
|
label: "Tujuan Proyek",
|
||||||
value:
|
value: (data && data?.purpose) || "-",
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Keuntungan",
|
label: "Keuntungan",
|
||||||
value:
|
value: (data && data?.benefit) || "-",
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
Grid,
|
Grid,
|
||||||
@@ -6,23 +7,45 @@ import {
|
|||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import { useLocalSearchParams } from "expo-router";
|
import { apiAdminCollaborationGetById } from "@/service/api-admin/api-admin-collaboration";
|
||||||
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function AdminCollaborationGroup() {
|
export default function AdminCollaborationGroup() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
console.log("params:", id);
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiAdminCollaborationGetById({
|
||||||
|
id: id as string,
|
||||||
|
category: "group",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setData(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper
|
<ViewWrapper
|
||||||
headerComponent={
|
headerComponent={<AdminBackButtonAntTitle title={`Detail Group`} />}
|
||||||
<AdminBackButtonAntTitle title={`Detail Group ${id}`} />
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
{listData.map((item, i) => (
|
{listData(data).map((item: any, index: number) => (
|
||||||
<Grid key={i}>
|
<Grid key={index}>
|
||||||
<Grid.Col
|
<Grid.Col
|
||||||
span={4}
|
span={4}
|
||||||
style={{ justifyContent: "center", paddingRight: 10 }}
|
style={{ justifyContent: "center", paddingRight: 10 }}
|
||||||
@@ -40,21 +63,36 @@ export default function AdminCollaborationGroup() {
|
|||||||
<StackCustom>
|
<StackCustom>
|
||||||
<TextCustom align="center">Anggota</TextCustom>
|
<TextCustom align="center">Anggota</TextCustom>
|
||||||
|
|
||||||
{Array.from({ length: 10 }).map((_, i) => (
|
{data?.ProjectCollaboration_AnggotaRoomChat?.map(
|
||||||
<Grid key={i}>
|
(item: any, index: number) => (
|
||||||
<Grid.Col
|
<StackCustom key={index} gap={0}>
|
||||||
span={4}
|
<Grid>
|
||||||
style={{ justifyContent: "center", paddingRight: 10 }}
|
<Grid.Col
|
||||||
>
|
span={4}
|
||||||
<TextCustom bold>Username</TextCustom>
|
style={{ justifyContent: "center", paddingRight: 10 }}
|
||||||
</Grid.Col>
|
>
|
||||||
<Grid.Col span={8} style={{ justifyContent: "center" }}>
|
<TextCustom bold>Nama</TextCustom>
|
||||||
<TextCustom>
|
</Grid.Col>
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit.
|
<Grid.Col span={8} style={{ justifyContent: "center" }}>
|
||||||
</TextCustom>
|
<TextCustom>
|
||||||
</Grid.Col>
|
{item?.User?.Profile?.name || "-"}
|
||||||
</Grid>
|
</TextCustom>
|
||||||
))}
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col
|
||||||
|
span={4}
|
||||||
|
style={{ justifyContent: "center", paddingRight: 10 }}
|
||||||
|
>
|
||||||
|
<TextCustom bold>Username</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={8} style={{ justifyContent: "center" }}>
|
||||||
|
<TextCustom>{item?.User?.username || "-"}</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</StackCustom>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
@@ -63,35 +101,35 @@ export default function AdminCollaborationGroup() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const listData = [
|
const listData = (data: any) => [
|
||||||
{
|
{
|
||||||
label: "Admin Group",
|
label: "Admin Group",
|
||||||
value: "Bagas Banuna",
|
value: data?.ProjectCollaboration?.Author?.username || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Nama Group",
|
label: "Nama Group",
|
||||||
value: "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
|
value: data?.name || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Industri",
|
label: "Industri",
|
||||||
value: "Kesehatan",
|
value:
|
||||||
|
data?.ProjectCollaboration?.ProjectCollaborationMaster_Industri?.name ||
|
||||||
|
"-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Jumlah Partisipan ",
|
label: "Jumlah Partisipan ",
|
||||||
value: "0",
|
value: data?.ProjectCollaboration_AnggotaRoomChat?.length || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Lokasi",
|
label: "Lokasi",
|
||||||
value: "Kuta Selatan, Bali",
|
value: data?.ProjectCollaboration?.lokasi || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Tujuan Proyek",
|
label: "Tujuan Proyek",
|
||||||
value:
|
value: data?.ProjectCollaboration?.purpose || "-",
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Keuntungan",
|
label: "Keuntungan",
|
||||||
value:
|
value: data?.ProjectCollaboration?.benefit || "-",
|
||||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
83
app/(application)/admin/collaboration/[id]/reject-input.tsx
Normal file
83
app/(application)/admin/collaboration/[id]/reject-input.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import {
|
||||||
|
AlertDefaultSystem,
|
||||||
|
BoxButtonOnFooter,
|
||||||
|
TextAreaCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
|
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||||
|
import { apiAdminCollaborationReject } from "@/service/api-admin/api-admin-collaboration";
|
||||||
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
|
export default function AdminCollaborationRejectInput() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [value, setValue] = useState("");
|
||||||
|
|
||||||
|
const handlerReject = async () => {
|
||||||
|
console.log("value:", value);
|
||||||
|
// router.replace(`/admin/job/reject/status`);
|
||||||
|
try {
|
||||||
|
const response = await apiAdminCollaborationReject({
|
||||||
|
id: id as string,
|
||||||
|
data: value,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Report gagal",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Report berhasil",
|
||||||
|
});
|
||||||
|
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttonSubmit = (
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<AdminButtonReject
|
||||||
|
title="Reject"
|
||||||
|
onReject={() =>
|
||||||
|
AlertDefaultSystem({
|
||||||
|
title: "Reject",
|
||||||
|
message: "Apakah anda yakin ingin menolak data ini?",
|
||||||
|
textLeft: "Batal",
|
||||||
|
textRight: "Ya",
|
||||||
|
onPressRight: () => {
|
||||||
|
handlerReject();
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper
|
||||||
|
footerComponent={buttonSubmit}
|
||||||
|
headerComponent={
|
||||||
|
<AdminBackButtonAntTitle title="Penolakan Collaboration" />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TextAreaCustom
|
||||||
|
value={value}
|
||||||
|
onChangeText={setValue}
|
||||||
|
placeholder="Masukan alasan"
|
||||||
|
required
|
||||||
|
showCount
|
||||||
|
maxLength={1000}
|
||||||
|
/>
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,67 +1,101 @@
|
|||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
BaseBox,
|
LoaderCustom,
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
|
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
|
||||||
import { Octicons } from "@expo/vector-icons";
|
import { Octicons } from "@expo/vector-icons";
|
||||||
import { router } from "expo-router";
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
import { Divider } from "react-native-paper";
|
import { Divider } from "react-native-paper";
|
||||||
|
|
||||||
export default function AdminCollaborationGroup() {
|
export default function AdminCollaborationGroup() {
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
handlerLoadList();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlerLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiAdminCollaboration({
|
||||||
|
category: "group",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setList(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
|
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom>
|
||||||
<AdminComp_BoxTitle title="Group" />
|
<AdminComp_BoxTitle title="Group" />
|
||||||
<BaseBox>
|
<>
|
||||||
<AdminTitleTable
|
<AdminTitleTable
|
||||||
title1="Aksi"
|
title1="Aksi"
|
||||||
title2="Admin Group"
|
title2="Jumlah peserta"
|
||||||
title3="Nama Group"
|
title3="Nama group"
|
||||||
/>
|
/>
|
||||||
<Spacing height={10} />
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{loadList ? (
|
||||||
<AdminTableValue
|
<LoaderCustom />
|
||||||
key={index}
|
) : _.isEmpty(list) ? (
|
||||||
value1={
|
<TextCustom align="center" color="gray">
|
||||||
<ActionIcon
|
Belum ada data
|
||||||
icon={
|
</TextCustom>
|
||||||
<Octicons
|
) : (
|
||||||
name="eye"
|
list?.map((item: any, index: number) => (
|
||||||
size={ICON_SIZE_BUTTON}
|
<View key={index}>
|
||||||
color="black"
|
<AdminTableValue
|
||||||
|
value1={
|
||||||
|
<ActionIcon
|
||||||
|
icon={
|
||||||
|
<Octicons
|
||||||
|
name="eye"
|
||||||
|
size={ICON_SIZE_BUTTON}
|
||||||
|
color="black"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onPress={() => {
|
||||||
|
router.push(`/admin/collaboration/${item.id}/group`);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
onPress={() => {
|
value2={
|
||||||
router.push(`/admin/collaboration/${index}/group`);
|
<TextCustom truncate={1}>
|
||||||
}}
|
{item?.ProjectCollaboration_AnggotaRoomChat?.length ||
|
||||||
|
"-"}
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
value3={
|
||||||
|
<TextCustom truncate={2}>{item?.name || "-"}</TextCustom>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
}
|
</View>
|
||||||
value2={
|
))
|
||||||
<TextCustom truncate={1}>Username username </TextCustom>
|
)}
|
||||||
}
|
</>
|
||||||
value3={
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit.
|
|
||||||
Blanditiis asperiores quidem deleniti architecto eaque et
|
|
||||||
nostrum, ad consequuntur eveniet quisquam quae voluptatum
|
|
||||||
ducimus! Dolorem nobis modi officia debitis, beatae
|
|
||||||
mollitia.
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</BaseBox>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -2,14 +2,41 @@ import { StackCustom, ViewWrapper } from "@/components";
|
|||||||
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
|
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
|
||||||
import { Entypo, FontAwesome } from "@expo/vector-icons";
|
import { Entypo, FontAwesome } from "@expo/vector-icons";
|
||||||
|
import { useFocusEffect } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function AdminCollaboration() {
|
export default function AdminCollaboration() {
|
||||||
|
const [list, setList] = useState<any | null>(null);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
handlerLoadList();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlerLoadList = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiAdminCollaboration({
|
||||||
|
category: "dashboard",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("[RESPONSE]", JSON.stringify(response, null, 2));
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setList(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
|
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
{listData.map((item, i) => (
|
{listData(list as any).map((item, i) => (
|
||||||
<AdminComp_BoxDashboard key={i} item={item} />
|
<AdminComp_BoxDashboard key={i} item={item} />
|
||||||
))}
|
))}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
@@ -18,20 +45,23 @@ export default function AdminCollaboration() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const listData = [
|
const listData = (list: any) => {
|
||||||
{
|
console.log("[LIST masuk]", JSON.stringify(list, null, 2));
|
||||||
label: "Publish",
|
return [
|
||||||
value: 4,
|
{
|
||||||
icon: <Entypo name="publish" size={25} color={MainColor.green} />,
|
label: "Publish",
|
||||||
},
|
value: (list && list?.publish) || "0",
|
||||||
{
|
icon: <Entypo name="publish" size={25} color={MainColor.green} />,
|
||||||
label: "Group",
|
},
|
||||||
value: 7,
|
{
|
||||||
icon: <FontAwesome name="group" size={25} color={MainColor.yellow} />,
|
label: "Group",
|
||||||
},
|
value: (list && list?.group) || "0",
|
||||||
{
|
icon: <FontAwesome name="group" size={25} color={MainColor.yellow} />,
|
||||||
label: "Reject",
|
},
|
||||||
value: 7,
|
{
|
||||||
icon: <FontAwesome name="warning" size={25} color={MainColor.red} />,
|
label: "Reject",
|
||||||
},
|
value: (list && list?.reject) || "0",
|
||||||
];
|
icon: <FontAwesome name="warning" size={25} color={MainColor.red} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
BaseBox,
|
LoaderCustom,
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
@@ -11,60 +10,95 @@ import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
|
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
|
||||||
import { Octicons } from "@expo/vector-icons";
|
import { Octicons } from "@expo/vector-icons";
|
||||||
import { router } from "expo-router";
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
import { Divider } from "react-native-paper";
|
import { Divider } from "react-native-paper";
|
||||||
|
|
||||||
export default function AdminCollaborationPublish() {
|
export default function AdminCollaborationPublish() {
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
handlerLoadList();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlerLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiAdminCollaboration({
|
||||||
|
category: "publish",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setList(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
|
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom>
|
||||||
<AdminComp_BoxTitle title="Publish" />
|
<AdminComp_BoxTitle title="Publish" />
|
||||||
<BaseBox>
|
|
||||||
<AdminTitleTable
|
|
||||||
title1="Aksi"
|
|
||||||
title2="Username"
|
|
||||||
title3="Judul Proyek"
|
|
||||||
/>
|
|
||||||
<Spacing height={10} />
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
<AdminTitleTable
|
||||||
<AdminTableValue
|
title1="Aksi"
|
||||||
key={index}
|
title2="Username"
|
||||||
value1={
|
title3="Judul Proyek"
|
||||||
<ActionIcon
|
/>
|
||||||
icon={
|
{/* <Spacing height={10} /> */}
|
||||||
<Octicons
|
<Divider />
|
||||||
name="eye"
|
|
||||||
size={ICON_SIZE_BUTTON}
|
{loadList ? (
|
||||||
color="black"
|
<LoaderCustom />
|
||||||
/>
|
) : _.isEmpty(list) ? (
|
||||||
}
|
<TextCustom align="center" color="gray">
|
||||||
onPress={() => {
|
Belum ada data
|
||||||
router.push(`/admin/collaboration/${index}/publish`);
|
</TextCustom>
|
||||||
}}
|
) : (
|
||||||
/>
|
list?.map((item: any, index: number) => (
|
||||||
}
|
<View key={index}>
|
||||||
value2={
|
<AdminTableValue
|
||||||
<TextCustom truncate={1}>Username username </TextCustom>
|
value1={
|
||||||
}
|
<ActionIcon
|
||||||
value3={
|
icon={
|
||||||
<TextCustom truncate={2}>
|
<Octicons
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit.
|
name="eye"
|
||||||
Blanditiis asperiores quidem deleniti architecto eaque et
|
size={ICON_SIZE_BUTTON}
|
||||||
nostrum, ad consequuntur eveniet quisquam quae voluptatum
|
color="black"
|
||||||
ducimus! Dolorem nobis modi officia debitis, beatae
|
/>
|
||||||
mollitia.
|
}
|
||||||
</TextCustom>
|
onPress={() => {
|
||||||
}
|
router.push(`/admin/collaboration/${item?.id}/publish`);
|
||||||
/>
|
}}
|
||||||
))}
|
/>
|
||||||
</BaseBox>
|
}
|
||||||
|
value2={
|
||||||
|
<TextCustom align="center" truncate={1}>
|
||||||
|
{item?.Author?.username || "-"}{" "}
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
value3={
|
||||||
|
<TextCustom align="center" truncate={2}>
|
||||||
|
{item?.title || "-"}
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +1,99 @@
|
|||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
BaseBox,
|
LoaderCustom,
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
|
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
|
||||||
import { Octicons } from "@expo/vector-icons";
|
import { Octicons } from "@expo/vector-icons";
|
||||||
import { router } from "expo-router";
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
import { Divider } from "react-native-paper";
|
import { Divider } from "react-native-paper";
|
||||||
|
|
||||||
export default function AdminCollaborationReject() {
|
export default function AdminCollaborationReject() {
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
handlerLoadList();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlerLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiAdminCollaboration({
|
||||||
|
category: "reject",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setList(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
|
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom>
|
||||||
<AdminComp_BoxTitle title="Reject" />
|
<AdminComp_BoxTitle title="Reject" />
|
||||||
<BaseBox>
|
<AdminTitleTable
|
||||||
<AdminTitleTable
|
title1="Aksi"
|
||||||
title1="Aksi"
|
title2="Username"
|
||||||
title2="Username"
|
title3="Judul Proyek"
|
||||||
title3="Judul Proyek"
|
/>
|
||||||
/>
|
<Divider />
|
||||||
<Spacing height={10} />
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{loadList ? (
|
||||||
<AdminTableValue
|
<LoaderCustom />
|
||||||
key={index}
|
) : _.isEmpty(list) ? (
|
||||||
value1={
|
<TextCustom>Belum ada data</TextCustom>
|
||||||
<ActionIcon
|
) : (
|
||||||
icon={
|
list?.map((item: any) => (
|
||||||
<Octicons
|
<View key={item.id}>
|
||||||
name="eye"
|
<AdminTableValue
|
||||||
size={ICON_SIZE_BUTTON}
|
key={item?.id}
|
||||||
color="black"
|
value1={
|
||||||
/>
|
<ActionIcon
|
||||||
}
|
icon={
|
||||||
onPress={() => {
|
<Octicons
|
||||||
router.push(`/admin/collaboration/${index}/reject`);
|
name="eye"
|
||||||
}}
|
size={ICON_SIZE_BUTTON}
|
||||||
/>
|
color="black"
|
||||||
}
|
/>
|
||||||
value2={
|
}
|
||||||
<TextCustom truncate={1}>Username username </TextCustom>
|
onPress={() => {
|
||||||
}
|
router.push(`/admin/collaboration/${item?.id}/reject`);
|
||||||
value3={
|
}}
|
||||||
<TextCustom truncate={2}>
|
/>
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit.
|
}
|
||||||
Blanditiis asperiores quidem deleniti architecto eaque et
|
value2={
|
||||||
nostrum, ad consequuntur eveniet quisquam quae voluptatum
|
<TextCustom align="center" truncate={1}>
|
||||||
ducimus! Dolorem nobis modi officia debitis, beatae
|
{item?.Author?.username || "-"}
|
||||||
mollitia.
|
</TextCustom>
|
||||||
</TextCustom>
|
}
|
||||||
}
|
value3={
|
||||||
/>
|
<TextCustom align="center" truncate={2}>
|
||||||
))}
|
{item?.title || "-"}
|
||||||
</BaseBox>
|
</TextCustom>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,13 +1,31 @@
|
|||||||
import {
|
import { StackCustom, TextCustom, ViewWrapper } from "@/components";
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
|
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { apiAdminMainDashboardGetAll } from "@/service/api-admin/api-admin-main-dashboard";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function AdminDashboard() {
|
export default function AdminDashboard() {
|
||||||
|
const [countUser, setCountUser] = useState(0);
|
||||||
|
const [countPortofolio, setCountPortofolio] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiAdminMainDashboardGetAll();
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setCountUser(response.data.user);
|
||||||
|
setCountPortofolio(response.data.portofolio);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR LOAD DATA]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
@@ -15,7 +33,7 @@ export default function AdminDashboard() {
|
|||||||
<TextCustom bold size={30}>
|
<TextCustom bold size={30}>
|
||||||
Main Dashboard
|
Main Dashboard
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
{listData.map((item, i) => (
|
{listData(countUser, countPortofolio).map((item, i) => (
|
||||||
<AdminComp_BoxDashboard key={i} item={item} />
|
<AdminComp_BoxDashboard key={i} item={item} />
|
||||||
))}
|
))}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
@@ -24,15 +42,15 @@ export default function AdminDashboard() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const listData = [
|
const listData = (countUser: number, countPortofolio: number) => [
|
||||||
{
|
{
|
||||||
label: "User",
|
label: "User",
|
||||||
value: 4,
|
value: countUser,
|
||||||
icon: <Ionicons name="people" size={30} color={MainColor.yellow} />,
|
icon: <Ionicons name="people" size={30} color={MainColor.yellow} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Portofolio",
|
label: "Portofolio",
|
||||||
value: 7,
|
value: countPortofolio,
|
||||||
icon: (
|
icon: (
|
||||||
<Ionicons name="id-card-outline" size={30} color={MainColor.yellow} />
|
<Ionicons name="id-card-outline" size={30} color={MainColor.yellow} />
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
BaseBox,
|
LoaderCustom,
|
||||||
SearchInput,
|
SearchInput,
|
||||||
Spacing,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper
|
ViewWrapper
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
@@ -11,17 +12,52 @@ import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
|
import { apiAdminJob } from "@/service/api-admin/api-admin-job";
|
||||||
import { Octicons } from "@expo/vector-icons";
|
import { Octicons } from "@expo/vector-icons";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
import { Divider } from "react-native-paper";
|
import { Divider } from "react-native-paper";
|
||||||
|
|
||||||
export default function AdminJobStatus() {
|
export default function AdminJobStatus() {
|
||||||
const { status } = useLocalSearchParams();
|
const { status } = useLocalSearchParams();
|
||||||
|
console.log("[STATUS]", status);
|
||||||
|
|
||||||
|
const [list, setList] = useState<any | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
handlerLoadList();
|
||||||
|
}, [status, search])
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlerLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiAdminJob({
|
||||||
|
category: status as "publish" | "review" | "reject",
|
||||||
|
search,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("[RESPONSE >>]", JSON.stringify(response, null, 2));
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setList(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const rightComponent = (
|
const rightComponent = (
|
||||||
<SearchInput
|
<SearchInput
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari"
|
placeholder="Cari"
|
||||||
|
onChangeText={setSearch}
|
||||||
|
value={search}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
@@ -32,44 +68,53 @@ export default function AdminJobStatus() {
|
|||||||
rightComponent={rightComponent}
|
rightComponent={rightComponent}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<BaseBox>
|
<StackCustom>
|
||||||
<AdminTitleTable
|
<AdminTitleTable
|
||||||
title1="Aksi"
|
title1="Aksi"
|
||||||
title2="Username"
|
title2="Username"
|
||||||
title3="Judul Pekerjaan"
|
title3="Judul Pekerjaan"
|
||||||
/>
|
/>
|
||||||
<Spacing />
|
{/* <Spacing /> */}
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
{loadList ? (
|
||||||
<AdminTableValue
|
<LoaderCustom />
|
||||||
key={index}
|
) : _.isEmpty(list) ? (
|
||||||
value1={
|
<TextCustom align="center" color="gray">
|
||||||
<ActionIcon
|
Tidak ada data
|
||||||
icon={
|
</TextCustom>
|
||||||
<Octicons
|
) : (
|
||||||
name="eye"
|
list?.map((item: any, index: number) => (
|
||||||
size={ICON_SIZE_BUTTON}
|
<AdminTableValue
|
||||||
color="black"
|
key={index}
|
||||||
/>
|
value1={
|
||||||
}
|
<ActionIcon
|
||||||
onPress={() => {
|
icon={
|
||||||
router.push(`/admin/job/${index}/${status}`);
|
<Octicons
|
||||||
}}
|
name="eye"
|
||||||
/>
|
size={ICON_SIZE_BUTTON}
|
||||||
}
|
color="black"
|
||||||
value2={<TextCustom truncate={1}>Username username</TextCustom>}
|
/>
|
||||||
value3={
|
}
|
||||||
<TextCustom truncate={2}>
|
onPress={() => {
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit.
|
router.push(`/admin/job/${item.id}/${status}`);
|
||||||
Blanditiis asperiores quidem deleniti architecto eaque et
|
}}
|
||||||
nostrum, ad consequuntur eveniet quisquam quae voluptatum
|
/>
|
||||||
ducimus! Dolorem nobis modi officia debitis, beatae mollitia.
|
}
|
||||||
</TextCustom>
|
value2={
|
||||||
}
|
<TextCustom align="center" truncate={1}>
|
||||||
/>
|
{item?.Author?.username || "-"}
|
||||||
))}
|
</TextCustom>
|
||||||
</BaseBox>
|
}
|
||||||
|
value3={
|
||||||
|
<TextCustom truncate={2} align="center">
|
||||||
|
{item?.title || "-"}
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
import { Spacing, StackCustom, ViewWrapper } from "@/components";
|
import { Spacing, StackCustom, ViewWrapper } from "@/components";
|
||||||
import {
|
import {
|
||||||
IconPublish,
|
IconPublish,
|
||||||
@@ -7,15 +8,45 @@ import {
|
|||||||
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
|
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { apiAdminJob } from "@/service/api-admin/api-admin-job";
|
||||||
|
import { useFocusEffect } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function AdminJob() {
|
export default function AdminJob() {
|
||||||
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
handlerLoadList();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlerLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiAdminJob({
|
||||||
|
category: "dashboard",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setData(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<AdminTitlePage title="Job Vacancy" />
|
<AdminTitlePage title="Job Vacancy" />
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
{listData.map((item, i) => (
|
{listData(data).map((item: any, i: number) => (
|
||||||
<AdminComp_BoxDashboard key={i} item={item} />
|
<AdminComp_BoxDashboard key={i} item={item} />
|
||||||
))}
|
))}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
@@ -24,20 +55,20 @@ export default function AdminJob() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const listData = [
|
const listData = (data: any) => [
|
||||||
{
|
{
|
||||||
label: "Publish",
|
label: "Publish",
|
||||||
value: 4,
|
value: (data && data?.publish) || 0,
|
||||||
icon: <IconPublish size={25} color={MainColor.green} />,
|
icon: <IconPublish size={25} color={MainColor.green} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Review",
|
label: "Review",
|
||||||
value: 7,
|
value: (data && data?.review) || 0,
|
||||||
icon: <IconReview size={25} color={MainColor.orange} />,
|
icon: <IconReview size={25} color={MainColor.orange} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Reject",
|
label: "Reject",
|
||||||
value: 5,
|
value: (data && data?.reject) || 0,
|
||||||
icon: <IconReject size={25} color={MainColor.red} />,
|
icon: <IconReject size={25} color={MainColor.red} />,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,11 +1,224 @@
|
|||||||
import { MapCustom, ViewWrapper } from "@/components";
|
import { ButtonCustom, DrawerCustom, DummyLandscapeImage, Grid, Spacing, StackCustom, TextCustom, ViewWrapper } from "@/components";
|
||||||
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
|
import API_IMAGE from "@/constants/api-storage";
|
||||||
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
|
import { apiMapsGetAll } from "@/service/api-client/api-maps";
|
||||||
|
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
|
||||||
|
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
||||||
|
import { Image } from "expo-image";
|
||||||
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import MapView, { Marker } from "react-native-maps";
|
||||||
|
|
||||||
|
const defaultRegion = {
|
||||||
|
latitude: -8.737109,
|
||||||
|
longitude: 115.1756897,
|
||||||
|
latitudeDelta: 0.1,
|
||||||
|
longitudeDelta: 0.1,
|
||||||
|
height: 300,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface LocationItem {
|
||||||
|
id: string | number;
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
name: string;
|
||||||
|
imageId?: string;
|
||||||
|
}
|
||||||
export default function AdminMaps() {
|
export default function AdminMaps() {
|
||||||
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
|
const [loadList, setLoadList] = useState(false);
|
||||||
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
const [selected, setSelected] = useState({
|
||||||
|
id: "",
|
||||||
|
bidangBisnis: "",
|
||||||
|
nomorTelepon: "",
|
||||||
|
alamatBisnis: "",
|
||||||
|
namePin: "",
|
||||||
|
imageId: "",
|
||||||
|
portofolioId: "",
|
||||||
|
latitude: 0,
|
||||||
|
longitude: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
handlerLoadList();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const handlerLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadList(true);
|
||||||
|
const response = await apiMapsGetAll();
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setList(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadList(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
|
||||||
<MapCustom height={"100%"} />
|
{/* <MapCustom height={"100%"} /> */}
|
||||||
|
<View style={{ flex: 1 }}>
|
||||||
|
{loadList ? (
|
||||||
|
<MapView
|
||||||
|
initialRegion={defaultRegion}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<MapView
|
||||||
|
initialRegion={defaultRegion}
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{list?.map((item: any, index: number) => {
|
||||||
|
return (
|
||||||
|
<Marker
|
||||||
|
key={item?.id}
|
||||||
|
coordinate={{
|
||||||
|
latitude: item?.latitude,
|
||||||
|
longitude: item?.longitude,
|
||||||
|
}}
|
||||||
|
title={item?.namePin}
|
||||||
|
onPress={() => {
|
||||||
|
setOpenDrawer(true);
|
||||||
|
setSelected({
|
||||||
|
id: item?.id,
|
||||||
|
bidangBisnis:
|
||||||
|
item?.Portofolio?.MasterBidangBisnis?.name,
|
||||||
|
nomorTelepon: item?.Portofolio?.tlpn,
|
||||||
|
alamatBisnis: item?.Portofolio?.alamatKantor,
|
||||||
|
namePin: item?.namePin,
|
||||||
|
imageId: item?.imageId,
|
||||||
|
portofolioId: item?.Portofolio?.id,
|
||||||
|
latitude: item?.latitude,
|
||||||
|
longitude: item?.longitude,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
// Gunakan gambar kustom jika tersedia
|
||||||
|
>
|
||||||
|
<View>
|
||||||
|
<Image
|
||||||
|
source={{
|
||||||
|
uri: API_IMAGE.GET({
|
||||||
|
fileId: item?.Portofolio?.logoId,
|
||||||
|
}),
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
borderRadius: 100,
|
||||||
|
borderWidth: 1,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</Marker>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</MapView>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
|
<DrawerCustom
|
||||||
|
isVisible={openDrawer}
|
||||||
|
closeDrawer={() => setOpenDrawer(false)}
|
||||||
|
height={"auto"}
|
||||||
|
>
|
||||||
|
<DummyLandscapeImage height={200} imageId={selected.imageId} />
|
||||||
|
<Spacing />
|
||||||
|
<StackCustom gap={"xs"}>
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<FontAwesome
|
||||||
|
name="building-o"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>{selected.namePin}</TextCustom>}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<Ionicons
|
||||||
|
name="list-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<Ionicons
|
||||||
|
name="call-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>+{selected.nomorTelepon}</TextCustom>}
|
||||||
|
/>
|
||||||
|
<GridTwoView
|
||||||
|
spanLeft={2}
|
||||||
|
spanRight={10}
|
||||||
|
leftIcon={
|
||||||
|
<Ionicons
|
||||||
|
name="location-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col span={6} style={{ paddingRight: 10 }}>
|
||||||
|
<ButtonCustom
|
||||||
|
onPress={() => {
|
||||||
|
setOpenDrawer(false);
|
||||||
|
router.push(`/portofolio/${selected.portofolioId}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Detail
|
||||||
|
</ButtonCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={6} style={{ paddingLeft: 10 }}>
|
||||||
|
<ButtonCustom
|
||||||
|
onPress={() => {
|
||||||
|
openInDeviceMaps({
|
||||||
|
latitude: selected.latitude,
|
||||||
|
longitude: selected.longitude,
|
||||||
|
title: selected.namePin,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Buka Maps
|
||||||
|
</ButtonCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</StackCustom>
|
||||||
|
</DrawerCustom>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
128
app/(application)/admin/super-admin/[id]/index.tsx
Normal file
128
app/(application)/admin/super-admin/[id]/index.tsx
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
|
ButtonCustom,
|
||||||
|
LoaderCustom,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
|
import {
|
||||||
|
apiAdminUserAccessGetById,
|
||||||
|
apiAdminUserAccessUpdateStatus,
|
||||||
|
} from "@/service/api-admin/api-admin-user-access";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
|
export default function SuperAdminDetail() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
const [loadData, setLoadData] = useState(false);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
setLoadData(true);
|
||||||
|
const response = await apiAdminUserAccessGetById({ id: id as string });
|
||||||
|
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR LOAD DATA]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadData(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiAdminUserAccessUpdateStatus({
|
||||||
|
id: id as string,
|
||||||
|
role: data?.masterUserRoleId === "2" ? "user" : "admin",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Update role gagal",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Update role berhasil ",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR UPDATE STATUS]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper
|
||||||
|
headerComponent={<AdminBackButtonAntTitle title={`Detail User`} />}
|
||||||
|
footerComponent={
|
||||||
|
data && (
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
|
backgroundColor={
|
||||||
|
data?.masterUserRoleId === "2" ? "red" : "green"
|
||||||
|
}
|
||||||
|
textColor={"white"}
|
||||||
|
onPress={handlerSubmit}
|
||||||
|
>
|
||||||
|
{data?.masterUserRoleId === "2"
|
||||||
|
? "Hapus akses admin"
|
||||||
|
: "Tambah sebagai admin"}
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{loadData ? (
|
||||||
|
<LoaderCustom />
|
||||||
|
) : (
|
||||||
|
<StackCustom>
|
||||||
|
{listData(data && data)?.map((item: any, index: number) => (
|
||||||
|
<GridTwoView
|
||||||
|
key={index}
|
||||||
|
spanLeft={4}
|
||||||
|
spanRight={8}
|
||||||
|
leftIcon={<TextCustom bold>{item?.label}</TextCustom>}
|
||||||
|
rightIcon={<TextCustom>{item?.value}</TextCustom>}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</StackCustom>
|
||||||
|
)}
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const listData = (data: any) => [
|
||||||
|
{
|
||||||
|
label: "Username",
|
||||||
|
value: (data && data?.username) || "-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Role",
|
||||||
|
value: data && data?.masterUserRoleId === "2" ? "Admin" : "User",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Nomor",
|
||||||
|
value: (data && `+${data?.nomor}`) || "-",
|
||||||
|
},
|
||||||
|
];
|
||||||
148
app/(application)/admin/super-admin/index.tsx
Normal file
148
app/(application)/admin/super-admin/index.tsx
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import {
|
||||||
|
BadgeCustom,
|
||||||
|
CenterCustom,
|
||||||
|
Divider,
|
||||||
|
SearchInput,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||||
|
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
|
||||||
|
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||||
|
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||||
|
import { apiAdminUserAccessGetAll } from "@/service/api-admin/api-admin-user-access";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
|
||||||
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
|
export default function SuperAdmin_ListUser() {
|
||||||
|
const [listData, setListData] = useState<any[] | null>(null);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [search])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiAdminUserAccessGetAll({
|
||||||
|
search: search,
|
||||||
|
category: "all-role",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setListData(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR LOAD DATA]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const rightComponent = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SearchInput
|
||||||
|
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||||
|
placeholder="Cari Username"
|
||||||
|
onChangeText={(text) => setSearch(text)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper
|
||||||
|
headerComponent={
|
||||||
|
<AdminComp_BoxTitle
|
||||||
|
title={"Super Admin"}
|
||||||
|
rightComponent={rightComponent()}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<GridViewCustomSpan
|
||||||
|
span1={2}
|
||||||
|
span2={5}
|
||||||
|
span3={5}
|
||||||
|
component1={
|
||||||
|
<TextCustom align="center" bold>
|
||||||
|
Aksi
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
component2={
|
||||||
|
<TextCustom align="center" bold>
|
||||||
|
Username
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
component3={
|
||||||
|
<TextCustom align="center" bold>
|
||||||
|
Role
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<StackCustom>
|
||||||
|
{_.isEmpty(listData) ? (
|
||||||
|
<TextCustom align="center" color="gray" size={"small"}>
|
||||||
|
Tidak ada data
|
||||||
|
</TextCustom>
|
||||||
|
) : (
|
||||||
|
listData?.map((item: any, index: number) => (
|
||||||
|
<GridViewCustomSpan
|
||||||
|
key={index}
|
||||||
|
span1={2}
|
||||||
|
span2={5}
|
||||||
|
span3={5}
|
||||||
|
component1={
|
||||||
|
<CenterCustom>
|
||||||
|
<Ionicons
|
||||||
|
onPress={() =>
|
||||||
|
router.push(`/admin/super-admin/${item?.id}`)
|
||||||
|
}
|
||||||
|
name="open"
|
||||||
|
size={ICON_SIZE_XLARGE}
|
||||||
|
color={MainColor.yellow}
|
||||||
|
/>
|
||||||
|
</CenterCustom>
|
||||||
|
|
||||||
|
// <ButtonCustom
|
||||||
|
// onPress={() =>
|
||||||
|
// router.push(`/admin/super-admin/${item?.id}`)
|
||||||
|
// }
|
||||||
|
// >
|
||||||
|
// Detail
|
||||||
|
// </ButtonCustom>
|
||||||
|
}
|
||||||
|
component2={
|
||||||
|
<TextCustom bold truncate>
|
||||||
|
{item?.username || "-"}
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
component3={
|
||||||
|
<CenterCustom>
|
||||||
|
{item?.masterUserRoleId === "2" ? (
|
||||||
|
<BadgeCustom color={AccentColor.blue}>Admin</BadgeCustom>
|
||||||
|
) : (
|
||||||
|
<BadgeCustom color={AccentColor.softblue}>
|
||||||
|
User
|
||||||
|
</BadgeCustom>
|
||||||
|
)}
|
||||||
|
</CenterCustom>
|
||||||
|
}
|
||||||
|
style3={{ alignItems: "center", justifyContent: "center" }}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</StackCustom>
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
import {
|
|
||||||
ButtonCustom,
|
|
||||||
Divider,
|
|
||||||
Grid,
|
|
||||||
SearchInput,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
|
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
|
|
||||||
export default function AdminUserAccess() {
|
|
||||||
const rightComponent = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SearchInput
|
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari User"
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title="User Access"
|
|
||||||
rightComponent={rightComponent()}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={4} style={{ alignItems: "center" }}>
|
|
||||||
<TextCustom bold>Aksi</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={4} style={{ alignItems: "center" }}>
|
|
||||||
<TextCustom bold>Username</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={4} style={{ alignItems: "center" }}>
|
|
||||||
<TextCustom bold>Nomor</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<StackCustom>
|
|
||||||
{Array.from({ length: 10 }).map((_, index) => (
|
|
||||||
<Grid key={index}>
|
|
||||||
<Grid.Col
|
|
||||||
span={4}
|
|
||||||
style={{
|
|
||||||
alignItems: "center",
|
|
||||||
paddingLeft: 5,
|
|
||||||
paddingRight: 5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextCustom bold>
|
|
||||||
<ButtonCustom
|
|
||||||
iconLeft={
|
|
||||||
<Ionicons
|
|
||||||
name={
|
|
||||||
index % 2 === 0
|
|
||||||
? "checkmark-outline"
|
|
||||||
: "close-circle-outline"
|
|
||||||
}
|
|
||||||
size={ICON_SIZE_MEDIUM}
|
|
||||||
color="black"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onPress={() => {}}
|
|
||||||
backgroundColor={
|
|
||||||
index % 2 === 0 ? MainColor.green : MainColor.red
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TextCustom size={"small"} color={"black"}>
|
|
||||||
{index % 2 === 0 ? "Berikan Akses" : "Hapus Akses"}
|
|
||||||
</TextCustom>
|
|
||||||
</ButtonCustom>
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col
|
|
||||||
span={4}
|
|
||||||
style={{
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
paddingLeft: 5,
|
|
||||||
paddingRight: 5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
Useraname
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col
|
|
||||||
span={4}
|
|
||||||
style={{
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
paddingLeft: 5,
|
|
||||||
paddingRight: 5,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
08123456789
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
))}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
124
app/(application)/admin/user-access/[id]/index.tsx
Normal file
124
app/(application)/admin/user-access/[id]/index.tsx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
|
ButtonCustom,
|
||||||
|
LoaderCustom,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
|
import {
|
||||||
|
apiAdminUserAccessGetById,
|
||||||
|
apiAdminUserAccessUpdateStatus,
|
||||||
|
} from "@/service/api-admin/api-admin-user-access";
|
||||||
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
|
export default function AdminUserAccessDetail() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
const [loadData, setLoadData] = useState(false);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
setLoadData(true);
|
||||||
|
const response = await apiAdminUserAccessGetById({ id: id as string });
|
||||||
|
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR LOAD DATA]", error);
|
||||||
|
} finally {
|
||||||
|
setLoadData(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlerSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiAdminUserAccessUpdateStatus({
|
||||||
|
id: id as string,
|
||||||
|
active: !data?.active,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Update aktifasi gagal",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Update aktifasi berhasil ",
|
||||||
|
});
|
||||||
|
router.back();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR UPDATE STATUS]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper
|
||||||
|
headerComponent={<AdminBackButtonAntTitle title={`Detail User`} />}
|
||||||
|
footerComponent={
|
||||||
|
data && (
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
|
backgroundColor={data?.active ? "red" : "green"}
|
||||||
|
textColor={"white"}
|
||||||
|
onPress={handlerSubmit}
|
||||||
|
>
|
||||||
|
{data?.active ? "Hapus Akses" : "Berikan Akses"}
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{loadData ? (
|
||||||
|
<LoaderCustom />
|
||||||
|
) : (
|
||||||
|
<StackCustom>
|
||||||
|
{listData(data && data)?.map((item: any, index: number) => (
|
||||||
|
<GridTwoView
|
||||||
|
key={index}
|
||||||
|
spanLeft={4}
|
||||||
|
spanRight={8}
|
||||||
|
leftIcon={<TextCustom bold>{item?.label}</TextCustom>}
|
||||||
|
rightIcon={<TextCustom>{item?.value}</TextCustom>}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</StackCustom>
|
||||||
|
)}
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const listData = (data: any) => [
|
||||||
|
{
|
||||||
|
label: "Username",
|
||||||
|
value: (data && data?.username) || "-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Aktivasi",
|
||||||
|
value: data && data?.active ? "Aktif" : "Tidak Aktif",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Nomor",
|
||||||
|
value: (data && `+${data?.nomor}`) || "-",
|
||||||
|
},
|
||||||
|
];
|
||||||
143
app/(application)/admin/user-access/index.tsx
Normal file
143
app/(application)/admin/user-access/index.tsx
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import {
|
||||||
|
BadgeCustom,
|
||||||
|
CenterCustom,
|
||||||
|
Divider,
|
||||||
|
SearchInput,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||||
|
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
|
||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||||
|
import { apiAdminUserAccessGetAll } from "@/service/api-admin/api-admin-user-access";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
import { router, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
|
export default function AdminUserAccess() {
|
||||||
|
const [listData, setListData] = useState<any[] | null>(null);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData();
|
||||||
|
}, [search])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiAdminUserAccessGetAll({
|
||||||
|
search: search,
|
||||||
|
category: "only-user",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setListData(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR LOAD DATA]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const rightComponent = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SearchInput
|
||||||
|
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||||
|
placeholder="Cari User"
|
||||||
|
onChangeText={(text) => setSearch(text)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper
|
||||||
|
headerComponent={
|
||||||
|
<AdminComp_BoxTitle
|
||||||
|
title="User Access"
|
||||||
|
rightComponent={rightComponent()}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<GridViewCustomSpan
|
||||||
|
span1={2}
|
||||||
|
span2={5}
|
||||||
|
span3={5}
|
||||||
|
component1={
|
||||||
|
<TextCustom align="center" bold>
|
||||||
|
Aksi
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
component2={
|
||||||
|
<TextCustom align="center" bold>
|
||||||
|
Username
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
component3={
|
||||||
|
<TextCustom align="center" bold>
|
||||||
|
Status Akses
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<StackCustom>
|
||||||
|
{_.isEmpty(listData) ? (
|
||||||
|
<TextCustom align="center" color="gray" size={"small"}>
|
||||||
|
Tidak ada data
|
||||||
|
</TextCustom>
|
||||||
|
) : (
|
||||||
|
listData?.map((item: any, index: number) => (
|
||||||
|
<GridViewCustomSpan
|
||||||
|
key={index}
|
||||||
|
span1={2}
|
||||||
|
span2={5}
|
||||||
|
span3={5}
|
||||||
|
component1={
|
||||||
|
<CenterCustom>
|
||||||
|
<Ionicons
|
||||||
|
onPress={() =>
|
||||||
|
router.push(`/admin/user-access/${item?.id}`)
|
||||||
|
}
|
||||||
|
name="open"
|
||||||
|
size={ICON_SIZE_XLARGE}
|
||||||
|
color={MainColor.yellow}
|
||||||
|
/>
|
||||||
|
</CenterCustom>
|
||||||
|
// <ButtonCustom
|
||||||
|
// onPress={() =>
|
||||||
|
// router.push(`/admin/user-access/${item?.id}`)
|
||||||
|
// }
|
||||||
|
// >
|
||||||
|
// Detail
|
||||||
|
// </ButtonCustom>
|
||||||
|
}
|
||||||
|
component2={
|
||||||
|
<TextCustom bold truncate>
|
||||||
|
{item?.username || "-"}
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
|
component3={
|
||||||
|
<CenterCustom>
|
||||||
|
{item?.active ? (
|
||||||
|
<BadgeCustom color="green">Aktif</BadgeCustom>
|
||||||
|
) : (
|
||||||
|
<BadgeCustom color="red">Tidak Aktif</BadgeCustom>
|
||||||
|
)}
|
||||||
|
</CenterCustom>
|
||||||
|
}
|
||||||
|
style3={{ alignItems: "center", justifyContent: "center" }}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</StackCustom>
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
AvatarCustom,
|
AvatarCustom,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||||
import { uploadImageService } from "@/service/upload-service";
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@@ -21,7 +21,7 @@ export default function ScreenUpload() {
|
|||||||
async function onUpload() {
|
async function onUpload() {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await uploadImageService({
|
const response = await uploadFileService({
|
||||||
imageUri,
|
imageUri,
|
||||||
dirId: DIRECTORY_ID.profile_foto,
|
dirId: DIRECTORY_ID.profile_foto,
|
||||||
});
|
});
|
||||||
|
|||||||
6
bun.lock
6
bun.lock
@@ -23,7 +23,7 @@
|
|||||||
"expo-constants": "~18.0.8",
|
"expo-constants": "~18.0.8",
|
||||||
"expo-dev-client": "~6.0.12",
|
"expo-dev-client": "~6.0.12",
|
||||||
"expo-document-picker": "~14.0.7",
|
"expo-document-picker": "~14.0.7",
|
||||||
"expo-file-system": "~19.0.12",
|
"expo-file-system": "^19.0.15",
|
||||||
"expo-font": "~14.0.8",
|
"expo-font": "~14.0.8",
|
||||||
"expo-haptics": "~15.0.7",
|
"expo-haptics": "~15.0.7",
|
||||||
"expo-image": "~3.0.8",
|
"expo-image": "~3.0.8",
|
||||||
@@ -1171,7 +1171,7 @@
|
|||||||
|
|
||||||
"expo-document-picker": ["expo-document-picker@14.0.7", "", { "peerDependencies": { "expo": "*" } }, "sha512-81Jh8RDD0GYBUoSTmIBq30hXXjmkDV1ZY2BNIp1+3HR5PDSh2WmdhD/Ezz5YFsv46hIXHsQc+Kh1q8vn6OLT9Q=="],
|
"expo-document-picker": ["expo-document-picker@14.0.7", "", { "peerDependencies": { "expo": "*" } }, "sha512-81Jh8RDD0GYBUoSTmIBq30hXXjmkDV1ZY2BNIp1+3HR5PDSh2WmdhD/Ezz5YFsv46hIXHsQc+Kh1q8vn6OLT9Q=="],
|
||||||
|
|
||||||
"expo-file-system": ["expo-file-system@19.0.12", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-gqpxpnjfhzXLcqMOi49isB5S1Af49P9410fsaFfnLZWN3X6Dwc8EplDwbaolOI/wnGwP81P+/nDn5RNmU6m7mQ=="],
|
"expo-file-system": ["expo-file-system@19.0.15", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-sRLW+3PVJDiuoCE2LuteHhC7OxPjh1cfqLylf1YG1TDEbbQXnzwjfsKeRm6dslEPZLkMWfSLYIrVbnuq5mF7kQ=="],
|
||||||
|
|
||||||
"expo-font": ["expo-font@14.0.8", "", { "dependencies": { "fontfaceobserver": "^2.1.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-bTUHaJWRZ7ywP8dg3f+wfOwv6RwMV3mWT2CDUIhsK70GjNGlCtiWOCoHsA5Od/esPaVxqc37cCBvQGQRFStRlA=="],
|
"expo-font": ["expo-font@14.0.8", "", { "dependencies": { "fontfaceobserver": "^2.1.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-bTUHaJWRZ7ywP8dg3f+wfOwv6RwMV3mWT2CDUIhsK70GjNGlCtiWOCoHsA5Od/esPaVxqc37cCBvQGQRFStRlA=="],
|
||||||
|
|
||||||
@@ -2567,6 +2567,8 @@
|
|||||||
|
|
||||||
"expo/babel-preset-expo": ["babel-preset-expo@54.0.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/plugin-proposal-decorators": "^7.12.9", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-transform-class-static-block": "^7.27.1", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.23.0", "@react-native/babel-preset": "0.81.4", "babel-plugin-react-compiler": "^19.1.0-rc.2", "babel-plugin-react-native-web": "~0.21.0", "babel-plugin-syntax-hermes-parser": "^0.25.1", "babel-plugin-transform-flow-enums": "^0.0.2", "debug": "^4.3.4", "resolve-from": "^5.0.0" }, "peerDependencies": { "@babel/runtime": "^7.20.0", "expo": "*", "react-refresh": ">=0.14.0 <1.0.0" }, "optionalPeers": ["@babel/runtime", "expo"] }, "sha512-a0Ej4ik6xzvtrA4Ivblov3XVvfntIoqnXOy2jG2k/3hzWqzrJxKyY2gUW9ZCMAicGevj2ju28q+TsK29uTe0eQ=="],
|
"expo/babel-preset-expo": ["babel-preset-expo@54.0.0", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/plugin-proposal-decorators": "^7.12.9", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/plugin-syntax-export-default-from": "^7.24.7", "@babel/plugin-transform-class-static-block": "^7.27.1", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-flow-strip-types": "^7.25.2", "@babel/plugin-transform-modules-commonjs": "^7.24.8", "@babel/plugin-transform-object-rest-spread": "^7.24.7", "@babel/plugin-transform-parameters": "^7.24.7", "@babel/plugin-transform-private-methods": "^7.24.7", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-runtime": "^7.24.7", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.23.0", "@react-native/babel-preset": "0.81.4", "babel-plugin-react-compiler": "^19.1.0-rc.2", "babel-plugin-react-native-web": "~0.21.0", "babel-plugin-syntax-hermes-parser": "^0.25.1", "babel-plugin-transform-flow-enums": "^0.0.2", "debug": "^4.3.4", "resolve-from": "^5.0.0" }, "peerDependencies": { "@babel/runtime": "^7.20.0", "expo": "*", "react-refresh": ">=0.14.0 <1.0.0" }, "optionalPeers": ["@babel/runtime", "expo"] }, "sha512-a0Ej4ik6xzvtrA4Ivblov3XVvfntIoqnXOy2jG2k/3hzWqzrJxKyY2gUW9ZCMAicGevj2ju28q+TsK29uTe0eQ=="],
|
||||||
|
|
||||||
|
"expo/expo-file-system": ["expo-file-system@19.0.12", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-gqpxpnjfhzXLcqMOi49isB5S1Af49P9410fsaFfnLZWN3X6Dwc8EplDwbaolOI/wnGwP81P+/nDn5RNmU6m7mQ=="],
|
||||||
|
|
||||||
"expo-module-scripts/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
"expo-module-scripts/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||||
|
|
||||||
"expo-modules-autolinking/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
|
"expo-modules-autolinking/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { Href, router } from "expo-router";
|
import { Href, router } from "expo-router";
|
||||||
import { DimensionValue, TouchableOpacity } from "react-native";
|
import { DimensionValue, StyleProp, TouchableOpacity, ViewStyle } from "react-native";
|
||||||
|
|
||||||
type SizeType = "xs" | "sm" | "md" | "lg" | "xl" | number | string | undefined;
|
type SizeType = "xs" | "sm" | "md" | "lg" | "xl" | number | string | undefined;
|
||||||
|
|
||||||
@@ -10,12 +10,14 @@ export default function ActionIcon({
|
|||||||
icon,
|
icon,
|
||||||
size = "md",
|
size = "md",
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
style,
|
||||||
}: {
|
}: {
|
||||||
href?: Href;
|
href?: Href;
|
||||||
onPress?: () => void;
|
onPress?: () => void;
|
||||||
icon: React.ReactNode;
|
icon: React.ReactNode;
|
||||||
size?: SizeType;
|
size?: SizeType;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
style?: StyleProp<ViewStyle>;
|
||||||
}) {
|
}) {
|
||||||
const sizeMap = {
|
const sizeMap = {
|
||||||
xs: 22,
|
xs: 22,
|
||||||
@@ -39,7 +41,7 @@ export default function ActionIcon({
|
|||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
activeOpacity={0.7}
|
activeOpacity={0.7}
|
||||||
style={{
|
style={[{
|
||||||
backgroundColor: disabled ? MainColor.disabled : MainColor.yellow,
|
backgroundColor: disabled ? MainColor.disabled : MainColor.yellow,
|
||||||
padding: 5,
|
padding: 5,
|
||||||
borderRadius: 50,
|
borderRadius: 50,
|
||||||
@@ -47,7 +49,7 @@ export default function ActionIcon({
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
width: iconSize,
|
width: iconSize,
|
||||||
height: iconSize,
|
height: iconSize,
|
||||||
}}
|
}, style]}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
if (disabled) return;
|
if (disabled) return;
|
||||||
if (href) {
|
if (href) {
|
||||||
|
|||||||
@@ -10,15 +10,18 @@ interface ButtonCenteredOnlyProps {
|
|||||||
icon?: "plus" | "upload" | string;
|
icon?: "plus" | "upload" | string;
|
||||||
onPress: () => void;
|
onPress: () => void;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
export default function ButtonCenteredOnly({
|
export default function ButtonCenteredOnly({
|
||||||
onPress,
|
onPress,
|
||||||
children,
|
children,
|
||||||
icon = "plus",
|
icon = "plus",
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
|
disabled = false,
|
||||||
}: ButtonCenteredOnlyProps) {
|
}: ButtonCenteredOnlyProps) {
|
||||||
return (
|
return (
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
disabled={disabled}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
iconLeft={
|
iconLeft={
|
||||||
|
|||||||
72
components/Button/CoyButton.tsx
Normal file
72
components/Button/CoyButton.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import { TouchableOpacity, Text, StyleSheet } from "react-native";
|
||||||
|
import * as Clipboard from "expo-clipboard";
|
||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
|
||||||
|
interface CopyButtonProps {
|
||||||
|
textToCopy: string;
|
||||||
|
copyLabel?: string; // Teks tombol saat normal, default: "Copy"
|
||||||
|
copiedLabel?: string; // Teks tombol saat berhasil, default: "Copied!"
|
||||||
|
onCopySuccess?: () => void; // Callback opsional saat berhasil menyalin
|
||||||
|
duration?: number; // Durasi (ms) menampilkan "Copied!", default: 2000
|
||||||
|
style?: any; // Gaya tambahan untuk tombol
|
||||||
|
textStyle?: any; // Gaya tambahan untuk teks
|
||||||
|
}
|
||||||
|
|
||||||
|
const CopyButton: React.FC<CopyButtonProps> = ({
|
||||||
|
textToCopy,
|
||||||
|
copyLabel = "Copy",
|
||||||
|
copiedLabel = "Copied!",
|
||||||
|
onCopySuccess,
|
||||||
|
duration = 2000,
|
||||||
|
style,
|
||||||
|
textStyle,
|
||||||
|
}) => {
|
||||||
|
const [isCopied, setIsCopied] = useState(false);
|
||||||
|
|
||||||
|
const handleCopy = async () => {
|
||||||
|
try {
|
||||||
|
await Clipboard.setStringAsync(textToCopy);
|
||||||
|
setIsCopied(true);
|
||||||
|
onCopySuccess?.();
|
||||||
|
|
||||||
|
// Reset kembali ke label "Copy" setelah durasi tertentu
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsCopied(false);
|
||||||
|
}, duration);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to copy text:", error);
|
||||||
|
// Opsional: tampilkan error toast jika diperlukan
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.button, style]}
|
||||||
|
onPress={handleCopy}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
>
|
||||||
|
<Text style={[styles.text, textStyle]}>
|
||||||
|
{isCopied ? copiedLabel : copyLabel}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
button: {
|
||||||
|
backgroundColor: MainColor.yellow,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 8,
|
||||||
|
borderRadius: 50,
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
color: MainColor.darkblue,
|
||||||
|
fontWeight: "600",
|
||||||
|
fontSize: 14,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CopyButton;
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
// components/MapComponent.js
|
// components/MapComponent.js
|
||||||
|
|
||||||
|
import API_IMAGE from "@/constants/api-storage";
|
||||||
|
import { Image } from "expo-image";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { DimensionValue, StyleSheet, View } from "react-native";
|
import { DimensionValue, StyleSheet, View } from "react-native";
|
||||||
import MapView, { Marker } from "react-native-maps";
|
import MapView, { Marker } from "react-native-maps";
|
||||||
@@ -10,6 +12,9 @@ interface MapComponentProps {
|
|||||||
latitudeDelta?: number;
|
latitudeDelta?: number;
|
||||||
longitudeDelta?: number;
|
longitudeDelta?: number;
|
||||||
height?: DimensionValue;
|
height?: DimensionValue;
|
||||||
|
namePin?: string;
|
||||||
|
imageId?: string;
|
||||||
|
onPress?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MapCustom = ({
|
const MapCustom = ({
|
||||||
@@ -18,6 +23,9 @@ const MapCustom = ({
|
|||||||
latitudeDelta = 0.1,
|
latitudeDelta = 0.1,
|
||||||
longitudeDelta = 0.1,
|
longitudeDelta = 0.1,
|
||||||
height = 300,
|
height = 300,
|
||||||
|
namePin = "Bali",
|
||||||
|
imageId,
|
||||||
|
onPress,
|
||||||
}: MapComponentProps) => {
|
}: MapComponentProps) => {
|
||||||
const initialRegion = {
|
const initialRegion = {
|
||||||
latitude,
|
latitude,
|
||||||
@@ -40,9 +48,22 @@ const MapCustom = ({
|
|||||||
latitude,
|
latitude,
|
||||||
longitude,
|
longitude,
|
||||||
}}
|
}}
|
||||||
title="Bali"
|
title={namePin}
|
||||||
description="Badung, Bali, Indonesia"
|
onPress={onPress}
|
||||||
/>
|
// Gunakan gambar kustom jika tersedia
|
||||||
|
>
|
||||||
|
<View style={{}}>
|
||||||
|
<Image
|
||||||
|
source={{ uri: API_IMAGE.GET({ fileId: imageId as string }) }}
|
||||||
|
style={{
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
borderRadius: 100,
|
||||||
|
borderWidth: 1,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</Marker>
|
||||||
</MapView>
|
</MapView>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|||||||
79
components/Map/MapSelected.tsx
Normal file
79
components/Map/MapSelected.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { StyleSheet, View } from "react-native";
|
||||||
|
import MapView, { LatLng, Marker } from "react-native-maps";
|
||||||
|
|
||||||
|
interface MapSelectedProps {
|
||||||
|
initialRegion?: {
|
||||||
|
latitude?: number;
|
||||||
|
longitude?: number;
|
||||||
|
latitudeDelta?: number;
|
||||||
|
longitudeDelta?: number;
|
||||||
|
};
|
||||||
|
onLocationSelect?: (location: LatLng) => void;
|
||||||
|
height?: number; // Opsional: tinggi peta dalam piksel
|
||||||
|
selectedLocation: LatLng;
|
||||||
|
setSelectedLocation: (location: LatLng) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MapSelected: React.FC<MapSelectedProps> = ({
|
||||||
|
initialRegion,
|
||||||
|
onLocationSelect,
|
||||||
|
selectedLocation,
|
||||||
|
setSelectedLocation,
|
||||||
|
height = 400, // Default height: 400
|
||||||
|
}) => {
|
||||||
|
const handleMapPress = (event: any) => {
|
||||||
|
const { latitude, longitude } = event.nativeEvent.coordinate;
|
||||||
|
const location = { latitude, longitude };
|
||||||
|
setSelectedLocation(location);
|
||||||
|
onLocationSelect?.(location);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default region sesuai permintaan
|
||||||
|
const defaultRegion = {
|
||||||
|
latitude: -8.737109,
|
||||||
|
longitude: 115.1756897,
|
||||||
|
latitudeDelta: 0.1,
|
||||||
|
longitudeDelta: 0.1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { height }]}>
|
||||||
|
<MapView
|
||||||
|
style={styles.map}
|
||||||
|
initialRegion={(initialRegion as any) || defaultRegion}
|
||||||
|
onPress={handleMapPress}
|
||||||
|
showsUserLocation={true}
|
||||||
|
showsMyLocationButton={true}
|
||||||
|
loadingEnabled={true}
|
||||||
|
loadingIndicatorColor="#666"
|
||||||
|
loadingBackgroundColor="#f0f0f0"
|
||||||
|
>
|
||||||
|
{selectedLocation && (
|
||||||
|
<Marker
|
||||||
|
coordinate={selectedLocation}
|
||||||
|
title="Lokasi Dipilih"
|
||||||
|
description={`Lat: ${selectedLocation.latitude.toFixed(
|
||||||
|
6
|
||||||
|
)}, Lng: ${selectedLocation.longitude.toFixed(6)}`}
|
||||||
|
pinColor="red"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</MapView>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
width: "100%",
|
||||||
|
backgroundColor: "#f5f5f5",
|
||||||
|
overflow: "hidden",
|
||||||
|
borderRadius: 8,
|
||||||
|
},
|
||||||
|
map: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default MapSelected;
|
||||||
@@ -13,14 +13,32 @@ export default function AdminTitleTable({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col span={3} style={{ alignItems: "center", justifyContent: "center" }}>
|
<Grid.Col
|
||||||
<TextCustom bold align="center">{title1}</TextCustom>
|
span={3}
|
||||||
|
style={{
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
paddingLeft: 5,
|
||||||
|
paddingRight: 5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextCustom truncate bold align="center">
|
||||||
|
{title1}
|
||||||
|
</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={3} style={{ alignItems: "center", justifyContent: "center" }}>
|
<Grid.Col
|
||||||
<TextCustom bold align="center">{title2}</TextCustom>
|
span={3}
|
||||||
|
style={{ alignItems: "center", justifyContent: "center", paddingLeft: 5, paddingRight: 5 }}
|
||||||
|
>
|
||||||
|
<TextCustom truncate bold align="center">
|
||||||
|
{title2}
|
||||||
|
</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={6} style={{ alignItems: "center", justifyContent: "center" }}>
|
<Grid.Col
|
||||||
<TextCustom bold align="center">{title3}</TextCustom>
|
span={6}
|
||||||
|
style={{ alignItems: "center", justifyContent: "center", paddingLeft: 5, paddingRight: 5 }}
|
||||||
|
>
|
||||||
|
<TextCustom truncate bold align="center">{title3}</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ export default function AdminTableValue({
|
|||||||
value1,
|
value1,
|
||||||
value2,
|
value2,
|
||||||
value3,
|
value3,
|
||||||
|
bottomLine = false,
|
||||||
}: {
|
}: {
|
||||||
value1: React.ReactNode;
|
value1: React.ReactNode;
|
||||||
value2: React.ReactNode;
|
value2: React.ReactNode;
|
||||||
value3: React.ReactNode;
|
value3: React.ReactNode;
|
||||||
|
bottomLine?: boolean;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -21,8 +23,8 @@ export default function AdminTableValue({
|
|||||||
style={{
|
style={{
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
paddingLeft: 5,
|
paddingLeft: 10,
|
||||||
paddingRight: 5,
|
paddingRight: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{value1}
|
{value1}
|
||||||
@@ -32,8 +34,8 @@ export default function AdminTableValue({
|
|||||||
style={{
|
style={{
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
paddingLeft: 5,
|
paddingLeft: 10,
|
||||||
paddingRight: 5,
|
paddingRight: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{value2}
|
{value2}
|
||||||
@@ -42,14 +44,15 @@ export default function AdminTableValue({
|
|||||||
span={6}
|
span={6}
|
||||||
style={{
|
style={{
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
paddingLeft: 5,
|
alignItems: "center",
|
||||||
paddingRight: 5,
|
paddingLeft: 10,
|
||||||
|
paddingRight: 10,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{value3}
|
{value3}
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Divider />
|
{bottomLine && <Divider />}
|
||||||
</View>
|
</View>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
37
components/_ShareComponent/GridTwoView.tsx
Normal file
37
components/_ShareComponent/GridTwoView.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { ViewStyle } from "react-native";
|
||||||
|
import Grid from "../Grid/GridCustom";
|
||||||
|
|
||||||
|
export default function GridTwoView({
|
||||||
|
spanLeft = 6,
|
||||||
|
spanRight = 6,
|
||||||
|
leftIcon,
|
||||||
|
rightIcon,
|
||||||
|
styleLeft,
|
||||||
|
styleRight,
|
||||||
|
}: {
|
||||||
|
spanLeft?: number;
|
||||||
|
spanRight?: number;
|
||||||
|
leftIcon?: React.ReactNode;
|
||||||
|
rightIcon?: React.ReactNode;
|
||||||
|
styleLeft?: ViewStyle;
|
||||||
|
styleRight?: ViewStyle;
|
||||||
|
}) {
|
||||||
|
const baseStyle: ViewStyle = { justifyContent: "center" };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid containerStyle={{ marginBottom: 0 }}>
|
||||||
|
<Grid.Col
|
||||||
|
span={spanLeft}
|
||||||
|
style={styleLeft ? { ...baseStyle, ...styleLeft } : baseStyle}
|
||||||
|
>
|
||||||
|
{leftIcon}
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col
|
||||||
|
span={spanRight}
|
||||||
|
style={styleRight ? { ...baseStyle, ...styleRight } : baseStyle}
|
||||||
|
>
|
||||||
|
{rightIcon}
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Grid } from "@/components";
|
import { Grid } from "@/components";
|
||||||
|
import { StyleProp, ViewStyle } from "react-native";
|
||||||
|
|
||||||
export const GridViewCustomSpan = ({
|
export const GridViewCustomSpan = ({
|
||||||
span1,
|
span1,
|
||||||
@@ -7,31 +8,52 @@ export const GridViewCustomSpan = ({
|
|||||||
component1,
|
component1,
|
||||||
component2,
|
component2,
|
||||||
component3,
|
component3,
|
||||||
|
style1,
|
||||||
|
style2,
|
||||||
|
style3,
|
||||||
}: {
|
}: {
|
||||||
span1: number;
|
span1?: number;
|
||||||
span2: number;
|
span2?: number;
|
||||||
span3: number;
|
span3?: number;
|
||||||
component1: React.ReactNode;
|
component1: React.ReactNode;
|
||||||
component2: React.ReactNode;
|
component2: React.ReactNode;
|
||||||
component3: React.ReactNode;
|
component3: React.ReactNode;
|
||||||
|
style1?: StyleProp<ViewStyle>;
|
||||||
|
style2?: StyleProp<ViewStyle>;
|
||||||
|
style3?: StyleProp<ViewStyle>;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col
|
<Grid.Col
|
||||||
span={span1 || 4}
|
span={span1 || 4}
|
||||||
style={{ justifyContent: "center", paddingRight: 5, paddingLeft: 5 }}
|
style={{
|
||||||
|
justifyContent: "center",
|
||||||
|
paddingRight: 10,
|
||||||
|
paddingLeft: 10,
|
||||||
|
...(style1 as object)
|
||||||
|
} as ViewStyle}
|
||||||
>
|
>
|
||||||
{component1}
|
{component1}
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col
|
<Grid.Col
|
||||||
span={span2 || 4}
|
span={span2 || 4}
|
||||||
style={{ justifyContent: "center", paddingRight: 5, paddingLeft: 5 }}
|
style={{
|
||||||
|
justifyContent: "center",
|
||||||
|
paddingRight: 10,
|
||||||
|
paddingLeft: 10,
|
||||||
|
...(style2 as object)
|
||||||
|
} as ViewStyle}
|
||||||
>
|
>
|
||||||
{component2}
|
{component2}
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col
|
<Grid.Col
|
||||||
span={span3 || 4}
|
span={span3 || 4}
|
||||||
style={{ justifyContent: "center", paddingRight: 5, paddingLeft: 5 }}
|
style={{
|
||||||
|
justifyContent: "center",
|
||||||
|
paddingRight: 10,
|
||||||
|
paddingLeft: 10,
|
||||||
|
...(style3 as object)
|
||||||
|
} as ViewStyle}
|
||||||
>
|
>
|
||||||
{component3}
|
{component3}
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
|
|||||||
118
components/_ShareComponent/MoneyTransferAnimation.tsx
Normal file
118
components/_ShareComponent/MoneyTransferAnimation.tsx
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
// MoneyTransferAnimation.tsx
|
||||||
|
import React from "react";
|
||||||
|
import { View, StyleSheet } from "react-native";
|
||||||
|
import { FontAwesome } from "@expo/vector-icons";
|
||||||
|
import Animated, {
|
||||||
|
useSharedValue,
|
||||||
|
useAnimatedStyle,
|
||||||
|
withTiming,
|
||||||
|
Easing,
|
||||||
|
runOnJS,
|
||||||
|
} from "react-native-reanimated";
|
||||||
|
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||||
|
import TextCustom from "../Text/TextCustom";
|
||||||
|
|
||||||
|
const SCREEN_WIDTH = 300; // Lebar area animasi
|
||||||
|
const DURATION = 2500; // Durasi sekali jalan (kiri → kanan)
|
||||||
|
|
||||||
|
const MoneyTransferAnimation: React.FC = () => {
|
||||||
|
const progress = useSharedValue(0);
|
||||||
|
const opacity = useSharedValue(0);
|
||||||
|
|
||||||
|
// Fungsi untuk mereset dan mulai ulang animasi
|
||||||
|
const startAnimation = () => {
|
||||||
|
progress.value = 0;
|
||||||
|
opacity.value = 0;
|
||||||
|
|
||||||
|
// Fade in di awal
|
||||||
|
opacity.value = withTiming(1, { duration: 300 });
|
||||||
|
|
||||||
|
// Gerak kiri → kanan
|
||||||
|
progress.value = withTiming(
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
duration: DURATION,
|
||||||
|
easing: Easing.linear,
|
||||||
|
},
|
||||||
|
(finished) => {
|
||||||
|
if (finished) {
|
||||||
|
// Fade out di akhir
|
||||||
|
opacity.value = withTiming(0, { duration: 300 }, () => {
|
||||||
|
// Setelah fade out, ulangi
|
||||||
|
runOnJS(startAnimation)();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
startAnimation();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const animatedStyle = useAnimatedStyle(() => {
|
||||||
|
return {
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
translateX: progress.value * SCREEN_WIDTH,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
opacity: opacity.value,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{/* Area animasi (track tidak wajib, tapi bisa ditambahkan) */}
|
||||||
|
<View style={styles.track} />
|
||||||
|
|
||||||
|
{/* Ikon uang animasi */}
|
||||||
|
<Animated.View style={[styles.iconContainer, animatedStyle]}>
|
||||||
|
<FontAwesome name="money" size={28} color="#2ecc71" />
|
||||||
|
</Animated.View>
|
||||||
|
|
||||||
|
{/* Teks status */}
|
||||||
|
<View style={styles.textContainer}>
|
||||||
|
<TextCustom bold>Transaksi Sedang Diproses</TextCustom>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
paddingVertical: 30,
|
||||||
|
position: "relative",
|
||||||
|
width: SCREEN_WIDTH,
|
||||||
|
},
|
||||||
|
track: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 20,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
height: 2,
|
||||||
|
backgroundColor: AccentColor.darkblue,
|
||||||
|
borderRadius: 1,
|
||||||
|
},
|
||||||
|
iconContainer: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 4,
|
||||||
|
left: -30, // Mulai di luar kiri
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
textContainer: {
|
||||||
|
marginTop: 40,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "600",
|
||||||
|
color: MainColor.white,
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default MoneyTransferAnimation;
|
||||||
72
components/_ShareComponent/PdfViewer.tsx
Normal file
72
components/_ShareComponent/PdfViewer.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
// PdfViewer.tsx
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { ActivityIndicator, Platform, StyleSheet, View } from "react-native";
|
||||||
|
import WebView from "react-native-webview";
|
||||||
|
|
||||||
|
interface PdfViewerProps {
|
||||||
|
uri: string; // URL PDF dari API
|
||||||
|
}
|
||||||
|
|
||||||
|
const PdfViewer: React.FC<PdfViewerProps> = ({ uri }) => {
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
// ✅ Bungkus dengan Google Docs Viewer
|
||||||
|
const viewerUrl = `https://docs.google.com/gview?embedded=true&url=${encodeURIComponent(
|
||||||
|
uri
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
const selectedDivice = Platform.OS === "ios" ? uri : viewerUrl;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{loading && (
|
||||||
|
<View style={styles.loadingContainer}>
|
||||||
|
<ActivityIndicator size="large" color="#007AFF" />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
<WebView
|
||||||
|
source={{
|
||||||
|
uri: selectedDivice,
|
||||||
|
}}
|
||||||
|
style={styles.webView}
|
||||||
|
onLoadEnd={() => setLoading(false)}
|
||||||
|
onError={(syntheticEvent) => {
|
||||||
|
const { nativeEvent } = syntheticEvent;
|
||||||
|
console.warn("WebView error:", nativeEvent);
|
||||||
|
setLoading(false);
|
||||||
|
}}
|
||||||
|
// scalesPageToFit={true}
|
||||||
|
// javaScriptEnabled={true}
|
||||||
|
// domStorageEnabled={true}
|
||||||
|
// originWhitelist={["*"]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// const { width, height } = Dimensions.get("window");
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "#f0f0f0",
|
||||||
|
},
|
||||||
|
loadingContainer: {
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
backgroundColor: "rgba(255,255,255,0.8)",
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
webView: {
|
||||||
|
// width: width,
|
||||||
|
// height: height,
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default PdfViewer;
|
||||||
@@ -2,7 +2,7 @@ import { MainColor } from "@/constants/color-palet";
|
|||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
import TextInputCustom from "../TextInput/TextInputCustom";
|
import TextInputCustom from "../TextInput/TextInputCustom";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { StyleProp, ViewStyle, TextStyle } from "react-native";
|
import { StyleProp, ViewStyle, TextStyle, StyleSheet } from "react-native";
|
||||||
|
|
||||||
interface SearchInputProps {
|
interface SearchInputProps {
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
@@ -12,6 +12,8 @@ interface SearchInputProps {
|
|||||||
containerStyle?: StyleProp<ViewStyle>;
|
containerStyle?: StyleProp<ViewStyle>;
|
||||||
style?: StyleProp<TextStyle>;
|
style?: StyleProp<TextStyle>;
|
||||||
onChangeText?: (value: string) => void;
|
onChangeText?: (value: string) => void;
|
||||||
|
value?: string;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
export default function SearchInput({
|
export default function SearchInput({
|
||||||
placeholder,
|
placeholder,
|
||||||
@@ -21,6 +23,8 @@ export default function SearchInput({
|
|||||||
containerStyle,
|
containerStyle,
|
||||||
style,
|
style,
|
||||||
onChangeText,
|
onChangeText,
|
||||||
|
value,
|
||||||
|
disabled,
|
||||||
...props
|
...props
|
||||||
}: SearchInputProps) {
|
}: SearchInputProps) {
|
||||||
return (
|
return (
|
||||||
@@ -29,14 +33,21 @@ export default function SearchInput({
|
|||||||
<Ionicons
|
<Ionicons
|
||||||
name="search-outline"
|
name="search-outline"
|
||||||
size={ICON_SIZE_SMALL}
|
size={ICON_SIZE_SMALL}
|
||||||
color={MainColor.placeholder}
|
color={disabled ? MainColor.white_gray : MainColor.placeholder}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
value={value}
|
||||||
onChangeText={onChangeText}
|
onChangeText={onChangeText}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
borderRadius={50}
|
borderRadius={50}
|
||||||
containerStyle={[containerStyle, { marginBottom: 0 }]}
|
containerStyle={[disabled ? styleses.disabled : styleses.containerStyle]}
|
||||||
|
disabled={disabled}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const styleses = StyleSheet.create({
|
||||||
|
containerStyle: { width: "100%", marginBottom: 0 },
|
||||||
|
disabled: { width: "100%", marginBottom: 0, color: MainColor.white_gray },
|
||||||
|
});
|
||||||
|
|||||||
4
constants/local-storage-key.ts
Normal file
4
constants/local-storage-key.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const LOCAL_STORAGE_KEY = {
|
||||||
|
transactionInvestment: "transactionInvestment",
|
||||||
|
transactionDonation: "transactionDonation",
|
||||||
|
};
|
||||||
@@ -135,7 +135,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
const userData = async (token: string) => {
|
const userData = async (token: string) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const response = await apiConfig.get(`/mobile/user?token=${token}`, {
|
const response = await apiConfig.get(`/mobile?token=${token}`, {
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -17,15 +17,15 @@ const listDataNotPublishInvesment = ({ data }: { data: any }) => [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Total Lembar",
|
label: "Total Lembar",
|
||||||
value: data?.totalLembar || "-",
|
value: formatCurrencyDisplay(data?.totalLembar) || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Pencarian Investor",
|
label: "Pencarian Investor",
|
||||||
value: data && data?.MasterPencarianInvestor?.name + " hari" || "-",
|
value: (data && data?.MasterPencarianInvestor?.name + " hari") || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Jadwal Pembagian",
|
label: "Jadwal Pembagian",
|
||||||
value: data && data?.MasterPembagianDeviden?.name + " bulan" || "-",
|
value: (data && data?.MasterPembagianDeviden?.name + " bulan") || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Pembagian Deviden",
|
label: "Pembagian Deviden",
|
||||||
@@ -40,34 +40,34 @@ const listDataPublishInvesment = ({ data }: { data: any }) => [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Target Dana",
|
label: "Target Dana",
|
||||||
value: data?.targetDana,
|
value: `Rp. ${formatCurrencyDisplay(data?.targetDana) || "-"}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Harga Per Lembar",
|
label: "Harga Per Lembar",
|
||||||
value: data?.hargaPerLembar,
|
value: `Rp. ${formatCurrencyDisplay(data?.hargaLembar) || "-"}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Return Of Investment (ROI)",
|
label: "Return Of Investment (ROI)",
|
||||||
value: data?.roi + " %",
|
value: `${data?.roi || "-"} %`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Total Lembar",
|
label: "Total Lembar",
|
||||||
value: data?.totalLembar,
|
value: formatCurrencyDisplay(data?.totalLembar) || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Sisa Lembar",
|
label: "Sisa Lembar",
|
||||||
value: data?.sisaLembar,
|
value: formatCurrencyDisplay(data?.sisaLembar) || "-",
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Jadwal Pembagian",
|
|
||||||
value: data?.jadwalPembagian,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Pembagian Deviden",
|
|
||||||
value: data?.pembagianDeviden,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Pencarian Investor",
|
label: "Pencarian Investor",
|
||||||
value: data?.pencarianInvestor,
|
value: (data && data?.MasterPencarianInvestor?.name + " hari") || "-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Jadwal Pembagian",
|
||||||
|
value: (data && data?.MasterPembagianDeviden?.name + " bulan") || "-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Pembagian Deviden",
|
||||||
|
value: data?.MasterPeriodeDeviden?.name || "-",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
"expo-constants": "~18.0.8",
|
"expo-constants": "~18.0.8",
|
||||||
"expo-dev-client": "~6.0.12",
|
"expo-dev-client": "~6.0.12",
|
||||||
"expo-document-picker": "~14.0.7",
|
"expo-document-picker": "~14.0.7",
|
||||||
"expo-file-system": "~19.0.12",
|
"expo-file-system": "^19.0.15",
|
||||||
"expo-font": "~14.0.8",
|
"expo-font": "~14.0.8",
|
||||||
"expo-haptics": "~15.0.7",
|
"expo-haptics": "~15.0.7",
|
||||||
"expo-image": "~3.0.8",
|
"expo-image": "~3.0.8",
|
||||||
|
|||||||
@@ -1,29 +1,57 @@
|
|||||||
import {
|
import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
|
BadgeCustom,
|
||||||
|
CenterCustom,
|
||||||
Grid,
|
Grid,
|
||||||
|
LoaderCustom,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom
|
TextCustom,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { AccentColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
import dummyMasterBidangBisnis from "@/lib/dummy-data/master-bidang-bisnis";
|
import { apiAdminMasterBusinessField } from "@/service/api-admin/api-master-admin";
|
||||||
import { FontAwesome5 } from "@expo/vector-icons";
|
import { FontAwesome5 } from "@expo/vector-icons";
|
||||||
import { router } from "expo-router";
|
import { router, useFocusEffect } from "expo-router";
|
||||||
import { useState } from "react";
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { Divider, Switch } from "react-native-paper";
|
import { Divider } from "react-native-paper";
|
||||||
|
|
||||||
export default function AdminAppInformation_BusinessFieldSection() {
|
export default function AdminAppInformation_BusinessFieldSection() {
|
||||||
const [value, setValue] = useState(false);
|
const [listData, setListData] = useState<any[] | null>(null);
|
||||||
const [selectedBusinessField, setSelectedBusinessField] = useState<any>(null);
|
const [loadData, setLoadData] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadList();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadList = async () => {
|
||||||
|
try {
|
||||||
|
setLoadData(true);
|
||||||
|
const response = await apiAdminMasterBusinessField();
|
||||||
|
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setListData(response.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR LIST BUSINESS FIELD]", error);
|
||||||
|
setListData([]);
|
||||||
|
} finally {
|
||||||
|
setLoadData(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<>
|
<StackCustom>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||||
<TextCustom bold>Aksi</TextCustom>
|
<TextCustom bold>Aksi</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
<Grid.Col span={4} style={{ alignItems: "center" }}>
|
||||||
<TextCustom bold>Status</TextCustom>
|
<TextCustom bold>Status</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={6}>
|
<Grid.Col span={6}>
|
||||||
@@ -33,51 +61,54 @@ export default function AdminAppInformation_BusinessFieldSection() {
|
|||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
|
{loadData ? (
|
||||||
<StackCustom>
|
<LoaderCustom />
|
||||||
{dummyMasterBidangBisnis.map((e, i) => (
|
) : _.isEmpty(listData) ? (
|
||||||
<View key={i}>
|
<TextCustom align="center">Tidak ada data</TextCustom>
|
||||||
<Grid>
|
) : (
|
||||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
<StackCustom>
|
||||||
<ActionIcon
|
{listData?.map((item: any, index: number) => (
|
||||||
icon={
|
<View key={index}>
|
||||||
<FontAwesome5
|
<Grid>
|
||||||
name="edit"
|
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||||
size={ICON_SIZE_BUTTON}
|
<ActionIcon
|
||||||
color="black"
|
icon={
|
||||||
/>
|
<FontAwesome5
|
||||||
}
|
name="edit"
|
||||||
onPress={() => {
|
size={ICON_SIZE_BUTTON}
|
||||||
router.push(`/admin/app-information/business-field/${i}`);
|
color="black"
|
||||||
}}
|
/>
|
||||||
/>
|
}
|
||||||
</Grid.Col>
|
onPress={() => {
|
||||||
<Grid.Col
|
router.push(
|
||||||
span={3}
|
`/admin/app-information/business-field/${item.id}`
|
||||||
style={{ alignItems: "center", justifyContent: "center" }}
|
);
|
||||||
>
|
}}
|
||||||
<Switch
|
/>
|
||||||
value={i === selectedBusinessField}
|
</Grid.Col>
|
||||||
onValueChange={() => {
|
<Grid.Col
|
||||||
setValue(!value);
|
span={4}
|
||||||
setSelectedBusinessField(i);
|
style={{ alignItems: "center", justifyContent: "center" }}
|
||||||
}}
|
>
|
||||||
theme={{
|
<CenterCustom>
|
||||||
colors: {
|
<BadgeCustom
|
||||||
primary: MainColor.yellow,
|
color={
|
||||||
},
|
item.active ? AccentColor.blue : AccentColor.blackgray
|
||||||
}}
|
}
|
||||||
/>
|
>
|
||||||
</Grid.Col>
|
{item.active ? "Aktif" : "Tidak Aktif"}
|
||||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
</BadgeCustom>
|
||||||
<TextCustom>{e.name}</TextCustom>
|
</CenterCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
||||||
<Divider />
|
<TextCustom>{item.name}</TextCustom>
|
||||||
</View>
|
</Grid.Col>
|
||||||
))}
|
</Grid>
|
||||||
</StackCustom>
|
</View>
|
||||||
</>
|
))}
|
||||||
|
</StackCustom>
|
||||||
|
)}
|
||||||
|
</StackCustom>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,119 @@
|
|||||||
import { ActionIcon, Grid, StackCustom, TextCustom } from "@/components";
|
import {
|
||||||
import { MainColor } from "@/constants/color-palet";
|
ActionIcon,
|
||||||
|
BadgeCustom,
|
||||||
|
CenterCustom,
|
||||||
|
Grid,
|
||||||
|
LoaderCustom,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
} from "@/components";
|
||||||
|
import { AccentColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
import { dummyMasterBank } from "@/lib/dummy-data/_master/bank";
|
import { apiAdminMasterBank } from "@/service/api-admin/api-master-admin";
|
||||||
import { FontAwesome5 } from "@expo/vector-icons";
|
import { FontAwesome5 } from "@expo/vector-icons";
|
||||||
import { router } from "expo-router";
|
import { router, useFocusEffect } from "expo-router";
|
||||||
import { useState } from "react";
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
import { Divider, Switch } from "react-native-paper";
|
import { Divider } from "react-native-paper";
|
||||||
|
|
||||||
export default function AdminAppInformation_Bank() {
|
export default function AdminAppInformation_Bank() {
|
||||||
const [value, setValue] = useState(false);
|
const [listData, setListData] = useState<any | null>(null);
|
||||||
|
const [loadData, setLoadData] = useState(false);
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
loadMasterBank();
|
||||||
|
}, [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const loadMasterBank = async () => {
|
||||||
|
try {
|
||||||
|
setLoadData(true);
|
||||||
|
const response = await apiAdminMasterBank();
|
||||||
|
|
||||||
|
setListData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR LIST BANK]", error);
|
||||||
|
setListData([]);
|
||||||
|
} finally {
|
||||||
|
setLoadData(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<>
|
<StackCustom>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
<Grid.Col span={3}>
|
||||||
<TextCustom bold>Aksi</TextCustom>
|
<TextCustom bold align="center">
|
||||||
|
Aksi
|
||||||
|
</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
<Grid.Col span={3}>
|
||||||
<TextCustom bold>Status</TextCustom>
|
<TextCustom bold align="center">
|
||||||
|
Status
|
||||||
|
</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
<Grid.Col span={6}>
|
<Grid.Col span={6}>
|
||||||
<TextCustom bold>Nama Bank</TextCustom>
|
<TextCustom bold align="center">
|
||||||
|
Nama Bank
|
||||||
|
</TextCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
<StackCustom>
|
{loadData ? (
|
||||||
{dummyMasterBank.map((e, i) => (
|
<LoaderCustom />
|
||||||
<View key={i}>
|
) : _.isEmpty(listData) ? (
|
||||||
<Grid>
|
<TextCustom align="center">Tidak ada data</TextCustom>
|
||||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
) : (
|
||||||
<ActionIcon
|
<StackCustom>
|
||||||
icon={
|
{listData?.map((item: any, index: number) => (
|
||||||
<FontAwesome5
|
<View key={index}>
|
||||||
name="edit"
|
<Grid>
|
||||||
size={ICON_SIZE_BUTTON}
|
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
||||||
color="black"
|
<ActionIcon
|
||||||
/>
|
icon={
|
||||||
}
|
<FontAwesome5
|
||||||
onPress={() => {
|
name="edit"
|
||||||
router.push(
|
size={ICON_SIZE_BUTTON}
|
||||||
`/admin/app-information/information-bank/${i}`
|
color="black"
|
||||||
);
|
/>
|
||||||
}}
|
}
|
||||||
/>
|
onPress={() => {
|
||||||
</Grid.Col>
|
router.push(
|
||||||
<Grid.Col
|
`/admin/app-information/information-bank/${item.id}`
|
||||||
span={3}
|
);
|
||||||
style={{ alignItems: "center", justifyContent: "center" }}
|
}}
|
||||||
>
|
/>
|
||||||
<Switch
|
</Grid.Col>
|
||||||
value={value}
|
<Grid.Col
|
||||||
onValueChange={() => {
|
span={3}
|
||||||
setValue(!value);
|
style={{ alignItems: "center", justifyContent: "center" }}
|
||||||
}}
|
>
|
||||||
theme={{
|
<CenterCustom>
|
||||||
colors: {
|
<BadgeCustom
|
||||||
primary: MainColor.yellow,
|
color={
|
||||||
},
|
item.isActive
|
||||||
}}
|
? AccentColor.blue
|
||||||
/>
|
: AccentColor.blackgray
|
||||||
</Grid.Col>
|
}
|
||||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
>
|
||||||
<TextCustom>{e.code}</TextCustom>
|
{item.isActive ? "Aktif" : "Tidak Aktif"}
|
||||||
</Grid.Col>
|
</BadgeCustom>
|
||||||
</Grid>
|
</CenterCustom>
|
||||||
<Divider />
|
</Grid.Col>
|
||||||
</View>
|
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
||||||
))}
|
<TextCustom align="center">{item.namaBank}</TextCustom>
|
||||||
</StackCustom>
|
</Grid.Col>
|
||||||
</>
|
</Grid>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</StackCustom>
|
||||||
|
)}
|
||||||
|
</StackCustom>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user