Invesment

Fix:
- tampilan list data bada beranda dan detail data main

### No Issue
This commit is contained in:
2025-10-01 16:45:23 +08:00
parent 250b216a54
commit c2acb97a37
12 changed files with 190 additions and 72 deletions

View File

@@ -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" />

View File

@@ -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,7 +41,7 @@ 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,
}); });
@@ -70,7 +72,7 @@ export default function InvestmentDetailStatus() {
); );
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 (

View File

@@ -14,7 +14,7 @@ import {
} from "@/components"; } from "@/components";
import DIRECTORY_ID from "@/constants/directory-id"; import DIRECTORY_ID from "@/constants/directory-id";
import { import {
apiInvestmentGetById, apiInvestmentGetOne,
apiInvestmentUpdateData, apiInvestmentUpdateData,
} from "@/service/api-client/api-investment"; } from "@/service/api-client/api-investment";
import { deleteFileService, uploadFileService } from "@/service/upload-service"; import { deleteFileService, uploadFileService } from "@/service/upload-service";
@@ -40,7 +40,7 @@ export default function InvestmentEditProspectus() {
const onLoadData = async () => { const onLoadData = async () => {
try { try {
setLoadingGet(true); setLoadingGet(true);
const response = await apiInvestmentGetById({ const response = await apiInvestmentGetOne({
id: id as string, id: id as string,
}); });
setData(response.data); setData(response.data);

View File

@@ -14,7 +14,7 @@ import {
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 { 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 { apiMasterInvestment } from "@/service/api-client/api-master";
@@ -90,7 +90,7 @@ export default function InvestmentEdit() {
const onLoadData = async () => { const onLoadData = async () => {
try { try {
const response = await apiInvestmentGetById({ const response = await apiInvestmentGetOne({
id: id as string, id: id as string,
}); });
setData(response.data); setData(response.data);

View File

@@ -9,18 +9,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 +64,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 +91,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}
/> />

View File

@@ -44,30 +44,30 @@ const listDataPublishInvesment = ({ data }: { data: any }) => [
}, },
{ {
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: data?.totalLembar || "-",
}, },
{ {
label: "Sisa Lembar", label: "Sisa Lembar",
value: data?.sisaLembar, value: 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 || "-",
}, },
]; ];

View File

@@ -1,5 +1,7 @@
import { import {
AvatarUsernameAndOtherComponent,
BaseBox, BaseBox,
BoxWithHeaderSection,
DummyLandscapeImage, DummyLandscapeImage,
Grid, Grid,
Spacing, Spacing,
@@ -10,19 +12,30 @@ import { View } from "react-native";
export default function Invesment_BoxDetailDataSection({ export default function Invesment_BoxDetailDataSection({
title, title,
author,
imageId, imageId,
data, data,
bottomSection, bottomSection,
}: { }: {
title?: string; title?: string;
author?: any;
imageId?: string; imageId?: string;
data: any; data: any;
bottomSection?: React.ReactNode; bottomSection?: React.ReactNode;
}) { }) {
return ( return (
<> <>
<BaseBox paddingBottom={0}> <BoxWithHeaderSection>
<StackCustom gap={"xs"}> <StackCustom gap={"xs"}>
{author && (
<AvatarUsernameAndOtherComponent
avatar={author?.Profile?.imageId}
name={author?.username}
rightComponent={""}
withBottomLine={true}
/>
)}
<DummyLandscapeImage imageId={imageId} /> <DummyLandscapeImage imageId={imageId} />
<Spacing /> <Spacing />
<TextCustom align="center" size="xlarge" bold> <TextCustom align="center" size="xlarge" bold>
@@ -46,7 +59,7 @@ export default function Invesment_BoxDetailDataSection({
<Spacing /> <Spacing />
{bottomSection} {bottomSection}
</StackCustom> </StackCustom>
</BaseBox> </BoxWithHeaderSection>
</> </>
); );
} }

View File

@@ -1,13 +1,13 @@
import { BaseBox, StackCustom, TextCustom, ProgressCustom } from "@/components"; import { BaseBox, StackCustom, TextCustom, ProgressCustom } from "@/components";
export default function Invesment_BoxProgressSection({status}: {status: string}) { export default function Invesment_BoxProgressSection({progress, status}: {progress: number, status: string}) {
return ( return (
<> <>
{status === "publish" && ( {status === "publish" && (
<BaseBox> <BaseBox>
<StackCustom> <StackCustom>
<TextCustom bold>Progress Saham</TextCustom> <TextCustom bold>Progress Saham</TextCustom>
<ProgressCustom value={70} size="lg" /> <ProgressCustom label={progress + "%"} value={progress} size="lg" />
</StackCustom> </StackCustom>
</BaseBox> </BaseBox>
)} )}

View File

@@ -8,9 +8,12 @@ export default function Investment_ButtonInvestasiSection({
id: string; id: string;
isMine: boolean; isMine: boolean;
}) { }) {
console.log("[IS MINE]", isMine);
return ( return (
<> <>
{isMine ? ( {isMine ? (
<ButtonCustom disabled>Investasi Ini Milik Anda</ButtonCustom>
) : (
<ButtonCustom <ButtonCustom
onPress={() => { onPress={() => {
router.navigate(`/investment/${id}/(transaction-flow)`); router.navigate(`/investment/${id}/(transaction-flow)`);
@@ -18,8 +21,6 @@ export default function Investment_ButtonInvestasiSection({
> >
Beli Saham Beli Saham
</ButtonCustom> </ButtonCustom>
) : (
<ButtonCustom disabled>Investasi Ini Milik Anda</ButtonCustom>
)} )}
</> </>
); );

View File

@@ -18,7 +18,6 @@ export default function Invesment_ComponentBoxOnBottomDetail({
prospectusId: string; prospectusId: string;
status: string; status: string;
}) { }) {
return ( return (
<> <>
{status === "publish" ? ( {status === "publish" ? (
@@ -28,7 +27,7 @@ export default function Invesment_ComponentBoxOnBottomDetail({
<BaseBox <BaseBox
backgroundColor={AccentColor.blue} backgroundColor={AccentColor.blue}
style={{ borderColor: AccentColor.softblue, borderWidth: 1 }} style={{ borderColor: AccentColor.softblue, borderWidth: 1 }}
href={`/(file)/${id}`} href={`/(file)/${prospectusId}`}
> >
<StackCustom> <StackCustom>
<TextCustom align="center">Prospektus</TextCustom> <TextCustom align="center">Prospektus</TextCustom>

View File

@@ -19,13 +19,14 @@ export default function Invesment_DetailDataPublishSection({
bottomSection?: React.ReactNode; bottomSection?: React.ReactNode;
buttonSection?: React.ReactNode; buttonSection?: React.ReactNode;
}) { }) {
// console.log("[DATA DETAIL]", JSON.stringify(data, null, 2));
return ( return (
<> <>
<StackCustom gap={"sm"}> <StackCustom gap={"sm"}>
<Invesment_BoxProgressSection status={status as string} /> <Invesment_BoxProgressSection progress={data?.progress} status={status as string} />
<Invesment_BoxDetailDataSection <Invesment_BoxDetailDataSection
title={data?.title} title={data?.title}
author={data?.author}
imageId={data?.imageId} imageId={data?.imageId}
data={ data={
status === "publish" status === "publish"

View File

@@ -28,7 +28,7 @@ export async function apiInvestmentGetByStatus({
} }
} }
export async function apiInvestmentGetById({ id }: { id: string }) { export async function apiInvestmentGetOne({ id }: { id: string }) {
try { try {
const response = await apiConfig.get(`/mobile/investment/${id}`); const response = await apiConfig.get(`/mobile/investment/${id}`);
return response.data; return response.data;
@@ -117,11 +117,7 @@ export async function apiInvestmentGetDocument({
} }
} }
export async function apiInvestmentDeleteDocument({ export async function apiInvestmentDeleteDocument({ id }: { id: string }) {
id,
}: {
id: string;
}) {
try { try {
const response = await apiConfig.delete( const response = await apiConfig.delete(
`/mobile/investment/${id}/document` `/mobile/investment/${id}/document`
@@ -131,3 +127,13 @@ export async function apiInvestmentDeleteDocument({
throw error; throw error;
} }
} }
export async function apiInvestmentGetAll() {
try {
const response = await apiConfig.get(`/mobile/investment`);
return response.data;
} catch (error) {
throw error;
}
}