Investment UI (Admin & User)

- app/(application)/(user)/investment/[id]/(transaction-flow)/invoice.tsx
- app/(application)/admin/investment/[id]/[status]/index.tsx
- app/(application)/admin/investment/[id]/[status]/transaction-detail.tsx
- app/(application)/admin/investment/[status]/status.tsx

API Service (Admin)
- service/api-admin/api-admin-investment.ts

### No issue
This commit is contained in:
2026-01-21 15:39:08 +08:00
parent 14c0f0e499
commit da82a02a45
5 changed files with 131 additions and 104 deletions

View File

@@ -27,7 +27,6 @@ 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 [data, setData] = useState<any>({});
const [image, setImage] = useState<IFileData>({ const [image, setImage] = useState<IFileData>({
name: "", name: "",
@@ -49,7 +48,6 @@ export default function InvestmentInvoice() {
category: "invoice", category: "invoice",
}); });
console.log("[RES INVOICE]", JSON.stringify(response.data, null, 2));
setData(response.data); setData(response.data);
} catch (error) { } catch (error) {
console.log("[ERROR]", error); console.log("[ERROR]", error);
@@ -64,8 +62,6 @@ export default function InvestmentInvoice() {
imageUri: image?.uri, imageUri: image?.uri,
}); });
console.log("[RESPONSE UPLOAD IMAGE]", responseUploadImage);
if (!responseUploadImage?.data?.id) { if (!responseUploadImage?.data?.id) {
Toast.show({ Toast.show({
type: "error", type: "error",
@@ -83,10 +79,6 @@ export default function InvestmentInvoice() {
}); });
if (response.success) { if (response.success) {
console.log(
"[RESPONSE UPDATE]",
JSON.stringify(response.data, null, 2)
);
Toast.show({ Toast.show({
type: "success", type: "success",
text1: "Berhasil mengunggah bukti transfer", text1: "Berhasil mengunggah bukti transfer",
@@ -210,7 +202,6 @@ export default function InvestmentInvoice() {
pickFile({ pickFile({
allowedType: "image", allowedType: "image",
setImageUri(file: any) { setImageUri(file: any) {
console.log("[IMAGE]", file);
setImage(file); setImage(file);
}, },
}); });
@@ -224,7 +215,7 @@ export default function InvestmentInvoice() {
<ButtonCustom <ButtonCustom
isLoading={isLoading} isLoading={isLoading}
disabled={!image} disabled={!image || isLoading}
onPress={() => { onPress={() => {
handlerSubmitUpdate(); handlerSubmitUpdate();
}} }}

View File

@@ -20,6 +20,7 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject"; import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview"; import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import ReportBox from "@/components/Box/ReportBox"; import ReportBox from "@/components/Box/ReportBox";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
@@ -28,6 +29,7 @@ import {
apiAdminInvestmentDetailById, apiAdminInvestmentDetailById,
} from "@/service/api-admin/api-admin-investment"; } from "@/service/api-admin/api-admin-investment";
import { colorBadgeStatus } from "@/utils/colorBadge"; import { colorBadgeStatus } from "@/utils/colorBadge";
import { countDownAndCondition } from "@/utils/countDownAndCondition";
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay"; import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
@@ -40,91 +42,41 @@ export default function AdminInvestmentDetail() {
const [data, setData] = React.useState<any | null>(null); const [data, setData] = React.useState<any | null>(null);
const [isLoading, setLoading] = React.useState(false); const [isLoading, setLoading] = React.useState(false);
const [remind, setRemind] = React.useState({
sisa: 0,
reminder: false,
});
useFocusEffect( useFocusEffect(
React.useCallback(() => { React.useCallback(() => {
onLoadData(); onLoadData();
}, [id]) }, [id]),
); );
const onLoadData = async () => { const onLoadData = async () => {
try { try {
const response = await apiAdminInvestmentDetailById({ id: id as string }); const response = await apiAdminInvestmentDetailById({ id: id as string });
// console.log("[GETONE INVEST]", JSON.stringify(response, null, 2));
if (response.success) { if (response.success) {
setData(response.data); 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) { } 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 ? (
<BadgeCustom
color={colorBadgeStatus({
status: data?.MasterStatusInvestasi?.name as string,
})}
>
{_.startCase(data?.MasterStatusInvestasi?.name as string)}
</BadgeCustom>
) : (
"-"
),
},
{
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 () => { const handlerSubmitPublish = async () => {
try { try {
setLoading(true); setLoading(true);
@@ -134,7 +86,6 @@ export default function AdminInvestmentDetail() {
data: data, data: data,
}); });
// console.log("[GET ON INVEST]", JSON.stringify(response, null, 2));
if (!response.success) { if (!response.success) {
Toast.show({ Toast.show({
type: "error", type: "error",
@@ -164,6 +115,16 @@ export default function AdminInvestmentDetail() {
/> />
); );
if (!data) {
return (
<>
<ViewWrapper>
<CustomSkeleton height={200} />
</ViewWrapper>
</>
);
}
return ( return (
<> <>
<ViewWrapper <ViewWrapper
@@ -177,8 +138,8 @@ export default function AdminInvestmentDetail() {
{status === "publish" && ( {status === "publish" && (
<BaseBox> <BaseBox>
<ProgressCustom <ProgressCustom
label={data && `${data.progress}%` || "0%"} label={(data && `${data.progress}%`) || "0%"}
value={data && data.progress || 0} value={(data && data.progress) || 0}
size="lg" size="lg"
/> />
<Spacing /> <Spacing />
@@ -187,7 +148,8 @@ export default function AdminInvestmentDetail() {
label={<TextCustom bold>Sisa Saham</TextCustom>} label={<TextCustom bold>Sisa Saham</TextCustom>}
value={ value={
<TextCustom> <TextCustom>
{data && formatCurrencyDisplay(data && data?.sisaLembar)} lembar {data && formatCurrencyDisplay(data && data?.sisaLembar)}{" "}
lembar
</TextCustom> </TextCustom>
} }
/> />
@@ -206,13 +168,15 @@ export default function AdminInvestmentDetail() {
<BaseBox> <BaseBox>
<StackCustom> <StackCustom>
<DummyLandscapeImage imageId={data?.imageId} /> <DummyLandscapeImage imageId={data?.imageId} />
{listData.map((item, i) => ( {listData({ data: data, reminder: remind.reminder })?.map(
(item, i) => (
<GridSpan_4_8 <GridSpan_4_8
key={i} key={i}
label={<TextCustom bold>{item.label}</TextCustom>} label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>} value={<TextCustom>{item.value}</TextCustom>}
/> />
))} ),
)}
</StackCustom> </StackCustom>
</BaseBox> </BaseBox>
@@ -230,7 +194,7 @@ export default function AdminInvestmentDetail() {
} }
onPress={() => { onPress={() => {
router.push( router.push(
`/(application)/(file)/${data?.prospektusFileId}` `/(application)/(file)/${data?.prospektusFileId}`,
); );
}} }}
> >
@@ -259,7 +223,7 @@ export default function AdminInvestmentDetail() {
} }
onPress={() => { onPress={() => {
router.push( router.push(
`/(application)/(file)/${item?.fileId}` `/(application)/(file)/${item?.fileId}`,
); );
}} }}
> >
@@ -299,8 +263,8 @@ export default function AdminInvestmentDetail() {
onReject={() => { onReject={() => {
router.push( router.push(
`/admin/investment/${id}/reject-input?status=${_.lowerCase( `/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 ? (
<BadgeCustom
color={colorBadgeStatus({
status: reminder ? "periode berakhir" : "publish",
})}
>
{reminder
? "Periode Berakhir"
: _.startCase(data?.MasterStatusInvestasi?.name as string)}
</BadgeCustom>
) : (
"-"
),
},
{
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" || "-",
},
];

View File

@@ -13,6 +13,7 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import GridTwoView from "@/components/_ShareComponent/GridTwoView"; import GridTwoView from "@/components/_ShareComponent/GridTwoView";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth";
import { import {
apiAdminInvestmentGetOneInvoiceById, apiAdminInvestmentGetOneInvoiceById,
apiAdminInvestmentUpdateInvoice, apiAdminInvestmentUpdateInvoice,
@@ -25,6 +26,7 @@ import { useCallback, useState } from "react";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
export default function AdminInvestmentTransactionDetail() { export default function AdminInvestmentTransactionDetail() {
const { user } = useAuth();
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null); const [data, setData] = useState<any | null>(null);
const [isLoading, setLoading] = useState<boolean>(false); const [isLoading, setLoading] = useState<boolean>(false);
@@ -32,7 +34,7 @@ export default function AdminInvestmentTransactionDetail() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
onLoadData(); onLoadData();
}, [id]) }, [id]),
); );
const onLoadData = async () => { const onLoadData = async () => {
@@ -40,7 +42,6 @@ export default function AdminInvestmentTransactionDetail() {
const response = await apiAdminInvestmentGetOneInvoiceById({ const response = await apiAdminInvestmentGetOneInvoiceById({
id: id as string, id: id as string,
}); });
// console.log("[RESPONSE]", JSON.stringify(response, null, 2));
if (response.success) { if (response.success) {
setData(response.data); setData(response.data);
} }
@@ -92,7 +93,7 @@ export default function AdminInvestmentTransactionDetail() {
<ButtonCustom <ButtonCustom
onPress={() => onPress={() =>
router.push( 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"; category: "accept" | "deny";
}) => { }) => {
if (!user?.id) {
Toast.show({
type: "error",
text1: "Gagal update status transaksi",
});
return;
}
try { try {
setLoading(true); setLoading(true);
const response = await apiAdminInvestmentUpdateInvoice({ const response = await apiAdminInvestmentUpdateInvoice({
@@ -117,11 +125,10 @@ export default function AdminInvestmentTransactionDetail() {
data: { data: {
investasiId: data?.investasiId, investasiId: data?.investasiId,
lembarTerbeli: data?.lembarTerbeli, lembarTerbeli: data?.lembarTerbeli,
senderId: user?.id as any,
}, },
}); });
// console.log("[RESPONSE SUBMIT]", JSON.stringify(response, null, 2));
if (!response.success) { if (!response.success) {
Toast.show({ Toast.show({
type: "error", type: "error",
@@ -153,6 +160,7 @@ export default function AdminInvestmentTransactionDetail() {
styleRight={{ paddingLeft: 10 }} styleRight={{ paddingLeft: 10 }}
leftIcon={ leftIcon={
<ButtonCustom <ButtonCustom
disabled={isLoading}
isLoading={isLoading} isLoading={isLoading}
backgroundColor={MainColor.red} backgroundColor={MainColor.red}
textColor="white" textColor="white"
@@ -175,6 +183,7 @@ export default function AdminInvestmentTransactionDetail() {
} }
rightIcon={ rightIcon={
<ButtonCustom <ButtonCustom
disabled={isLoading}
isLoading={isLoading} isLoading={isLoading}
onPress={() => { onPress={() => {
AlertDefaultSystem({ AlertDefaultSystem({
@@ -198,8 +207,8 @@ export default function AdminInvestmentTransactionDetail() {
} else if (data?.StatusInvoice?.name === "Gagal") { } else if (data?.StatusInvoice?.name === "Gagal") {
return ( return (
<> <>
<ButtonCustom textColor="red" onPress={() => router.back()}> <ButtonCustom disabled onPress={() => router.back()}>
Gagal Transaksi telah gagal
</ButtonCustom> </ButtonCustom>
</> </>
); );

View File

@@ -22,8 +22,6 @@ import { Divider } from "react-native-paper";
export default function AdminInvestmentStatus() { export default function AdminInvestmentStatus() {
const { status } = useLocalSearchParams(); const { status } = useLocalSearchParams();
console.log("[STATUS]", status);
const [listData, setListData] = React.useState<any[] | null>(null); const [listData, setListData] = React.useState<any[] | null>(null);
const [loadData, setLoadingData] = React.useState(false); const [loadData, setLoadingData] = React.useState(false);
const [search, setSearch] = React.useState(""); const [search, setSearch] = React.useState("");
@@ -41,7 +39,7 @@ export default function AdminInvestmentStatus() {
category: status as "publish" | "review" | "reject", category: status as "publish" | "review" | "reject",
search, search,
}); });
console.log("[LIST DATA]", JSON.stringify(response, null, 2));
if (response.success) { if (response.success) {
setListData(response.data); setListData(response.data);
} }

View File

@@ -98,6 +98,7 @@ export async function apiAdminInvestmentUpdateInvoice({
data: { data: {
investasiId: string; investasiId: string;
lembarTerbeli: number; lembarTerbeli: number;
senderId: string
}; };
}) { }) {
try { try {