diff --git a/app/(application)/(user)/investment/(tabs)/_layout.tsx b/app/(application)/(user)/investment/(tabs)/_layout.tsx index 0e49df0..9272ec9 100644 --- a/app/(application)/(user)/investment/(tabs)/_layout.tsx +++ b/app/(application)/(user)/investment/(tabs)/_layout.tsx @@ -1,9 +1,33 @@ +import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification"; import { ICON_SIZE_SMALL } from "@/constants/constans-value"; import { TabsStyles } from "@/styles/tabs-styles"; import { Feather, FontAwesome6, Ionicons } from "@expo/vector-icons"; -import { Tabs } from "expo-router"; +import { router, Tabs, useLocalSearchParams, useNavigation } from "expo-router"; +import { useLayoutEffect } from "react"; export default function InvestmentTabsLayout() { + // const navigation = useNavigation(); + + // const { from, category } = useLocalSearchParams<{ + // from?: string; + // category?: string; + // }>(); + + // console.log("from", from); + // console.log("category", category); + + // // Atur header secara dinamis + // useLayoutEffect(() => { + // navigation.setOptions({ + // headerLeft: () => ( + // + // ), + // }); + // }, [from, router, navigation]); + return ( (); + const [activeCategory, setActiveCategory] = useState( - "publish" + status || "publish" ); const [listData, setListData] = useState([]); diff --git a/app/(application)/(user)/investment/[id]/(transaction-flow)/invoice.tsx b/app/(application)/(user)/investment/[id]/(transaction-flow)/invoice.tsx index e76a5a1..fc9c00d 100644 --- a/app/(application)/(user)/investment/[id]/(transaction-flow)/invoice.tsx +++ b/app/(application)/(user)/investment/[id]/(transaction-flow)/invoice.tsx @@ -27,7 +27,6 @@ import Toast from "react-native-toast-message"; export default function InvestmentInvoice() { const { id } = useLocalSearchParams(); - console.log("[ID]", id); const [data, setData] = useState({}); const [image, setImage] = useState({ name: "", @@ -49,7 +48,6 @@ export default function InvestmentInvoice() { category: "invoice", }); - console.log("[RES INVOICE]", JSON.stringify(response.data, null, 2)); setData(response.data); } catch (error) { console.log("[ERROR]", error); @@ -64,8 +62,6 @@ export default function InvestmentInvoice() { imageUri: image?.uri, }); - console.log("[RESPONSE UPLOAD IMAGE]", responseUploadImage); - if (!responseUploadImage?.data?.id) { Toast.show({ type: "error", @@ -83,10 +79,6 @@ export default function InvestmentInvoice() { }); if (response.success) { - console.log( - "[RESPONSE UPDATE]", - JSON.stringify(response.data, null, 2) - ); Toast.show({ type: "success", text1: "Berhasil mengunggah bukti transfer", @@ -210,7 +202,6 @@ export default function InvestmentInvoice() { pickFile({ allowedType: "image", setImageUri(file: any) { - console.log("[IMAGE]", file); setImage(file); }, }); @@ -224,7 +215,7 @@ export default function InvestmentInvoice() { { handlerSubmitUpdate(); }} diff --git a/app/(application)/(user)/investment/create.tsx b/app/(application)/(user)/investment/create.tsx index ca64b6b..d7a9442 100644 --- a/app/(application)/(user)/investment/create.tsx +++ b/app/(application)/(user)/investment/create.tsx @@ -167,7 +167,7 @@ export default function InvestmentCreate() { text1: "Berhasil", text2: response.message, }); - router.replace("/investment/portofolio"); + router.replace("/investment/portofolio?status=review"); } else { Toast.show({ type: "error", @@ -224,7 +224,6 @@ export default function InvestmentCreate() { onPress={() => { pickFile({ setPdfUri: ({ uri, name, size }) => { - setPdf({ uri, name, size }); }, allowedType: "pdf", @@ -357,7 +356,11 @@ export default function InvestmentCreate() { )} - handleSubmit()}> + handleSubmit()} + > Simpan diff --git a/app/(application)/admin/investment/[id]/[status]/index.tsx b/app/(application)/admin/investment/[id]/[status]/index.tsx index a3dff44..f0ce822 100644 --- a/app/(application)/admin/investment/[id]/[status]/index.tsx +++ b/app/(application)/admin/investment/[id]/[status]/index.tsx @@ -20,6 +20,7 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject"; import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8"; +import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom"; import ReportBox from "@/components/Box/ReportBox"; import { MainColor } from "@/constants/color-palet"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; @@ -28,6 +29,7 @@ import { apiAdminInvestmentDetailById, } from "@/service/api-admin/api-admin-investment"; import { colorBadgeStatus } from "@/utils/colorBadge"; +import { countDownAndCondition } from "@/utils/countDownAndCondition"; import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import _ from "lodash"; @@ -40,91 +42,41 @@ export default function AdminInvestmentDetail() { const [data, setData] = React.useState(null); const [isLoading, setLoading] = React.useState(false); + const [remind, setRemind] = React.useState({ + sisa: 0, + reminder: false, + }); useFocusEffect( React.useCallback(() => { onLoadData(); - }, [id]) + }, [id]), ); const onLoadData = async () => { try { const response = await apiAdminInvestmentDetailById({ id: id as string }); - // console.log("[GETONE INVEST]", JSON.stringify(response, null, 2)); if (response.success) { setData(response.data); + + const duration = response?.data?.MasterPencarianInvestor?.name; + const publishTime = response?.data?.countDown; + + const countDown = countDownAndCondition({ + duration: duration, + publishTime: publishTime + }); + + setRemind({ + sisa: countDown.durationDay, + reminder: countDown.reminder, + }); } } catch (error) { - console.log(error); + console.log("Error", error); } }; - const listData = [ - { - label: "Username", - value: (data && data?.author?.username) || "-", - }, - { - label: "Judul", - value: (data && data?.title) || "-", - }, - { - label: "Status", - value: - data && data?.MasterStatusInvestasi?.name ? ( - - {_.startCase(data?.MasterStatusInvestasi?.name as string)} - - ) : ( - "-" - ), - }, - { - label: "Dana Dibutuhkan", - value: `Rp. ${ - (data && data?.targetDana && formatCurrencyDisplay(data?.targetDana)) || - "-" - }`, - }, - { - label: "Harga Perlembar", - value: `Rp. ${ - (data && - data?.hargaLembar && - formatCurrencyDisplay(data?.hargaLembar)) || - "-" - }`, - }, - { - label: "Total Lembar", - value: - (data && - data?.totalLembar && - formatCurrencyDisplay(data?.totalLembar)) || - "-", - }, - { - label: "ROI", - value: `${(data && data?.roi && data?.roi) || 0} %`, - }, - { - label: "Pembagian Deviden", - value: (data && data?.MasterPembagianDeviden?.name) + " bulan" || "-", - }, - { - label: "Jadwal Pembagian", - value: (data && data?.MasterPeriodeDeviden?.name) || "-", - }, - { - label: "Pencarian Investor", - value: (data && data?.MasterPencarianInvestor?.name) + " hari" || "-", - }, - ]; - const handlerSubmitPublish = async () => { try { setLoading(true); @@ -134,7 +86,6 @@ export default function AdminInvestmentDetail() { data: data, }); - // console.log("[GET ON INVEST]", JSON.stringify(response, null, 2)); if (!response.success) { Toast.show({ type: "error", @@ -164,6 +115,16 @@ export default function AdminInvestmentDetail() { /> ); + if (!data) { + return ( + <> + + + + + ); + } + return ( <> @@ -187,7 +148,8 @@ export default function AdminInvestmentDetail() { label={Sisa Saham} value={ - {data && formatCurrencyDisplay(data && data?.sisaLembar)} lembar + {data && formatCurrencyDisplay(data && data?.sisaLembar)}{" "} + lembar } /> @@ -206,13 +168,15 @@ export default function AdminInvestmentDetail() { - {listData.map((item, i) => ( - {item.label}} - value={{item.value}} - /> - ))} + {listData({ data: data, reminder: remind.reminder })?.map( + (item, i) => ( + {item.label}} + value={{item.value}} + /> + ), + )} @@ -230,7 +194,7 @@ export default function AdminInvestmentDetail() { } onPress={() => { router.push( - `/(application)/(file)/${data?.prospektusFileId}` + `/(application)/(file)/${data?.prospektusFileId}`, ); }} > @@ -259,7 +223,7 @@ export default function AdminInvestmentDetail() { } onPress={() => { router.push( - `/(application)/(file)/${item?.fileId}` + `/(application)/(file)/${item?.fileId}`, ); }} > @@ -299,8 +263,8 @@ export default function AdminInvestmentDetail() { onReject={() => { router.push( `/admin/investment/${id}/reject-input?status=${_.lowerCase( - data?.MasterStatusInvestasi?.name - )}` + data?.MasterStatusInvestasi?.name, + )}`, ); }} /> @@ -343,3 +307,67 @@ export default function AdminInvestmentDetail() { ); } + +const listData = ({ data, reminder }: { data: any; reminder: boolean }) => [ + { + label: "Username", + value: (data && data?.author?.username) || "-", + }, + { + label: "Judul", + value: (data && data?.title) || "-", + }, + { + label: "Status", + value: + data && data?.MasterStatusInvestasi?.name ? ( + + {reminder + ? "Periode Berakhir" + : _.startCase(data?.MasterStatusInvestasi?.name as string)} + + ) : ( + "-" + ), + }, + { + label: "Dana Dibutuhkan", + value: `Rp. ${ + (data && data?.targetDana && formatCurrencyDisplay(data?.targetDana)) || + "-" + }`, + }, + { + label: "Harga Perlembar", + value: `Rp. ${ + (data && data?.hargaLembar && formatCurrencyDisplay(data?.hargaLembar)) || + "-" + }`, + }, + { + label: "Total Lembar", + value: + (data && data?.totalLembar && formatCurrencyDisplay(data?.totalLembar)) || + "-", + }, + { + label: "ROI", + value: `${(data && data?.roi && data?.roi) || 0} %`, + }, + { + label: "Pembagian Deviden", + value: (data && data?.MasterPembagianDeviden?.name) + " bulan" || "-", + }, + { + label: "Jadwal Pembagian", + value: (data && data?.MasterPeriodeDeviden?.name) || "-", + }, + { + label: "Pencarian Investor", + value: (data && data?.MasterPencarianInvestor?.name) + " hari" || "-", + }, +]; diff --git a/app/(application)/admin/investment/[id]/[status]/transaction-detail.tsx b/app/(application)/admin/investment/[id]/[status]/transaction-detail.tsx index 6bab71c..583c248 100644 --- a/app/(application)/admin/investment/[id]/[status]/transaction-detail.tsx +++ b/app/(application)/admin/investment/[id]/[status]/transaction-detail.tsx @@ -13,6 +13,7 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8"; import GridTwoView from "@/components/_ShareComponent/GridTwoView"; import { MainColor } from "@/constants/color-palet"; +import { useAuth } from "@/hooks/use-auth"; import { apiAdminInvestmentGetOneInvoiceById, apiAdminInvestmentUpdateInvoice, @@ -25,6 +26,7 @@ import { useCallback, useState } from "react"; import Toast from "react-native-toast-message"; export default function AdminInvestmentTransactionDetail() { + const { user } = useAuth(); const { id } = useLocalSearchParams(); const [data, setData] = useState(null); const [isLoading, setLoading] = useState(false); @@ -32,7 +34,7 @@ export default function AdminInvestmentTransactionDetail() { useFocusEffect( useCallback(() => { onLoadData(); - }, [id]) + }, [id]), ); const onLoadData = async () => { @@ -40,7 +42,6 @@ export default function AdminInvestmentTransactionDetail() { const response = await apiAdminInvestmentGetOneInvoiceById({ id: id as string, }); - // console.log("[RESPONSE]", JSON.stringify(response, null, 2)); if (response.success) { setData(response.data); } @@ -92,7 +93,7 @@ export default function AdminInvestmentTransactionDetail() { router.push( - `/(application)/(image)/preview-image/${data?.imageId}` + `/(application)/(image)/preview-image/${data?.imageId}`, ) } > @@ -109,6 +110,13 @@ export default function AdminInvestmentTransactionDetail() { }: { category: "accept" | "deny"; }) => { + if (!user?.id) { + Toast.show({ + type: "error", + text1: "Gagal update status transaksi", + }); + return; + } try { setLoading(true); const response = await apiAdminInvestmentUpdateInvoice({ @@ -117,11 +125,10 @@ export default function AdminInvestmentTransactionDetail() { data: { investasiId: data?.investasiId, lembarTerbeli: data?.lembarTerbeli, + senderId: user?.id as any, }, }); - // console.log("[RESPONSE SUBMIT]", JSON.stringify(response, null, 2)); - if (!response.success) { Toast.show({ type: "error", @@ -153,6 +160,7 @@ export default function AdminInvestmentTransactionDetail() { styleRight={{ paddingLeft: 10 }} leftIcon={ { AlertDefaultSystem({ @@ -198,8 +207,8 @@ export default function AdminInvestmentTransactionDetail() { } else if (data?.StatusInvoice?.name === "Gagal") { return ( <> - router.back()}> - Gagal + router.back()}> + Transaksi telah gagal ); diff --git a/app/(application)/admin/investment/[id]/reject-input.tsx b/app/(application)/admin/investment/[id]/reject-input.tsx index c7361e0..444a973 100644 --- a/app/(application)/admin/investment/[id]/reject-input.tsx +++ b/app/(application)/admin/investment/[id]/reject-input.tsx @@ -7,34 +7,39 @@ import { } from "@/components"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject"; -import { apiAdminInvestasiUpdateByStatus, apiAdminInvestmentDetailById } from "@/service/api-admin/api-admin-investment"; +import { useAuth } from "@/hooks/use-auth"; +import { + apiAdminInvestasiUpdateByStatus, + apiAdminInvestmentDetailById, +} from "@/service/api-admin/api-admin-investment"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import { useCallback, useState } from "react"; import Toast from "react-native-toast-message"; export default function AdminInvestmentRejectInput() { + const { user } = useAuth(); const { id, status } = useLocalSearchParams(); console.log("[STATUS]", status); const [value, setValue] = useState(null); - const [isLoading , setLoading] = useState(false) + const [isLoading, setLoading] = useState(false); - useFocusEffect( - useCallback(() => { - onLoadData(); - }, [id]) - ); - - const onLoadData = async () => { - try { - const response = await apiAdminInvestmentDetailById({ id: id as string }); - console.log("[DATA]", JSON.stringify(response, null, 2)); - if (response.success) { - setValue(response.data?.catatan); - } - } catch (error) { - console.log(error); + useFocusEffect( + useCallback(() => { + onLoadData(); + }, [id]) + ); + + const onLoadData = async () => { + try { + const response = await apiAdminInvestmentDetailById({ id: id as string }); + console.log("[DATA]", JSON.stringify(response, null, 2)); + if (response.success) { + setValue(response.data?.catatan); } - }; + } catch (error) { + console.log(error); + } + }; const handlerSubmit = async () => { if (!value) { @@ -45,12 +50,23 @@ export default function AdminInvestmentRejectInput() { return; } + if (!user?.id) { + Toast.show({ + type: "error", + text1: "User tidak ditemukan", + }); + return; + } + try { - setLoading(true) + setLoading(true); const response = await apiAdminInvestasiUpdateByStatus({ id: id as string, status: "reject", - data: value, + data: { + catatan: value, + senderId: user?.id as string, + }, }); console.log("[RESPONSE]", JSON.stringify(response, null, 2)); @@ -76,7 +92,7 @@ export default function AdminInvestmentRejectInput() { } catch (error) { console.error(["ERROR"], error); } finally { - setLoading(false) + setLoading(false); } }; diff --git a/app/(application)/admin/investment/[status]/status.tsx b/app/(application)/admin/investment/[status]/status.tsx index d3c644c..c84c8ad 100644 --- a/app/(application)/admin/investment/[status]/status.tsx +++ b/app/(application)/admin/investment/[status]/status.tsx @@ -22,8 +22,6 @@ import { Divider } from "react-native-paper"; export default function AdminInvestmentStatus() { const { status } = useLocalSearchParams(); - console.log("[STATUS]", status); - const [listData, setListData] = React.useState(null); const [loadData, setLoadingData] = React.useState(false); const [search, setSearch] = React.useState(""); @@ -41,7 +39,7 @@ export default function AdminInvestmentStatus() { category: status as "publish" | "review" | "reject", search, }); - console.log("[LIST DATA]", JSON.stringify(response, null, 2)); + if (response.success) { setListData(response.data); } diff --git a/screens/Invesment/BoxBerandaSection.tsx b/screens/Invesment/BoxBerandaSection.tsx index 5e6292a..4113bb3 100644 --- a/screens/Invesment/BoxBerandaSection.tsx +++ b/screens/Invesment/BoxBerandaSection.tsx @@ -7,7 +7,6 @@ import { TextCustom, } from "@/components"; import API_STRORAGE from "@/constants/base-url-api-strorage"; -import { MainColor } from "@/constants/color-palet"; import DUMMY_IMAGE from "@/constants/dummy-image-value"; import { countDownAndCondition } from "@/utils/countDownAndCondition"; import { Ionicons } from "@expo/vector-icons"; @@ -22,8 +21,6 @@ export default function Investment_BoxBerandaSection({ id: string; data: any; }) { - // console.log("[DATA By one]", JSON.stringify(data, null, 2)); - const [value, setValue] = useState({ sisa: 0, reminder: false, @@ -33,8 +30,6 @@ export default function Investment_BoxBerandaSection({ updateCountDown(); }, [data]); - console.log("[DATA BERANDA]", JSON.stringify(data, null, 2)); - const updateCountDown = () => { const countDown = countDownAndCondition({ duration: data?.pencarianInvestor, diff --git a/service/api-admin/api-admin-investment.ts b/service/api-admin/api-admin-investment.ts index 5bb57d3..510bd9c 100644 --- a/service/api-admin/api-admin-investment.ts +++ b/service/api-admin/api-admin-investment.ts @@ -1,3 +1,4 @@ +import { typeRejectedData } from "@/types/type-collect-other"; import { apiConfig } from "../api-config"; export async function apiAdminInvestment({ @@ -38,7 +39,7 @@ export async function apiAdminInvestasiUpdateByStatus({ }: { id: string; status: "publish" | "review" | "reject"; - data: any; + data: typeRejectedData; }) { try { const response = await apiConfig.put( @@ -97,6 +98,7 @@ export async function apiAdminInvestmentUpdateInvoice({ data: { investasiId: string; lembarTerbeli: number; + senderId: string }; }) { try { diff --git a/utils/pickFile.ts b/utils/pickFile.ts index 613a7d7..68f279c 100644 --- a/utils/pickFile.ts +++ b/utils/pickFile.ts @@ -1,6 +1,6 @@ import * as ImagePicker from "expo-image-picker"; import * as DocumentPicker from "expo-document-picker"; -import { Alert } from "react-native"; +import { Alert, Platform } from "react-native"; const ALLOWED_IMAGE_EXTENSIONS = ["jpg", "jpeg", "png"]; const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB @@ -33,22 +33,52 @@ export default async function pickFile({ await pickImage(setImageUri, aspectRatio); } else { // Jika tidak, tawarkan pilihan rasio (default [4,3]) - showAspectRatioChoice(setImageUri); + // 🚀 Hanya tampilkan pilihan rasio di ANDROID + if (Platform.OS === "android") { + showAspectRatioChoice(setImageUri); + } else { + // iOS: langsung buka galeri dengan default [4, 3] + await pickImage(setImageUri, [4, 3]); + } } } else if (allowedType === "pdf") { await pickPdf(setPdfUri); } else { // Mode fleksibel: tampilkan pilihan - Alert.alert( - "Pilih Jenis File", - "Pilih sumber file yang ingin diunggah:", - [ - { text: "Batal", style: "cancel" }, - { text: "Dokumen (PDF)", onPress: () => pickPdf(setPdfUri) }, - { text: "Gambar", onPress: () => pickImage(setImageUri, aspectRatio) }, - ], - { cancelable: true } - ); + // Alert.alert( + // "Pilih Jenis File", + // "Pilih sumber file yang ingin diunggah:", + // [ + // { text: "Batal", style: "cancel" }, + // { text: "Dokumen (PDF)", onPress: () => pickPdf(setPdfUri) }, + // { text: "Gambar", onPress: () => pickImage(setImageUri, aspectRatio) }, + // ], + // { cancelable: true } + // ); + if (Platform.OS === "android") { + Alert.alert( + "Pilih Jenis File", + "Pilih sumber file yang ingin diunggah:", + [ + { text: "Batal", style: "cancel" }, + { text: "Dokumen (PDF)", onPress: () => pickPdf(setPdfUri) }, + { text: "Gambar", onPress: () => showAspectRatioChoice(setImageUri) }, + ], + { cancelable: true } + ); + } else { + // iOS: Langsung pakai default [4,3] untuk gambar + Alert.alert( + "Pilih Jenis File", + "Pilih sumber file yang ingin diunggah:", + [ + { text: "Batal", style: "cancel" }, + { text: "Dokumen (PDF)", onPress: () => pickPdf(setPdfUri) }, + { text: "Gambar", onPress: () => pickImage(setImageUri, [4, 3]) }, + ], + { cancelable: true } + ); + } } }