Compare commits

...

4 Commits

Author SHA1 Message Date
07e64c335e Donation – App & Admin
- app/(application)/(user)/donation/(tabs)/status.tsx
- app/(application)/(user)/donation/create-story.tsx
- app/(application)/admin/donation/[id]/[status]/transaction-detail.tsx
- app/(application)/admin/donation/[id]/reject-input.tsx
- screens/Admin/Donation/funDonationUpdateStatus.ts
- service/api-admin/api-admin-donation.ts

Config
- app.config.js
- ios/HIPMIBadungConnect/Info.plist

### No Issue
2026-01-23 17:13:06 +08:00
1aebc9b4e8 Logika EULA
Dipindah ke halaman login dan cek dengan modal

Fix:
Authentication & EULA
- context/AuthContext.tsx
- screens/Authentication/EULAView.tsx
- screens/Authentication/LoginView.tsx

Add:
- components/Modal/ModalReactNative.tsx
- screens/Authentication/EULASection.tsx

### No Issue
2026-01-23 14:45:44 +08:00
5665dc88ba Notification investasi done
### No Isssue
2026-01-22 17:54:33 +08:00
da82a02a45 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
2026-01-21 15:39:08 +08:00
24 changed files with 576 additions and 214 deletions

View File

@@ -21,7 +21,7 @@ export default {
"Aplikasi membutuhkan akses lokasi untuk menampilkan peta.", "Aplikasi membutuhkan akses lokasi untuk menampilkan peta.",
}, },
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"], associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"],
buildNumber: "19", buildNumber: "20",
}, },
android: { android: {

View File

@@ -9,14 +9,16 @@ 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 { apiDonationGetByStatus } from "@/service/api-client/api-donation"; import { apiDonationGetByStatus } from "@/service/api-client/api-donation";
import { useFocusEffect } from "expo-router"; import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
export default function DonationStatus() { export default function DonationStatus() {
const { user } = useAuth(); const { user } = useAuth();
const { status } = useLocalSearchParams<{ status?: string }>();
const [activeCategory, setActiveCategory] = useState<string | null>( const [activeCategory, setActiveCategory] = useState<string | null>(
"publish" status || "publish",
); );
const [listData, setListData] = useState<any[] | null>(null); const [listData, setListData] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false); const [loadList, setLoadList] = useState(false);
@@ -24,7 +26,7 @@ export default function DonationStatus() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
onLoadList(); onLoadList();
}, [activeCategory]) }, [activeCategory]),
); );
const onLoadList = async () => { const onLoadList = async () => {

View File

@@ -103,7 +103,7 @@ export default function DonationCreateStory() {
type: "success", type: "success",
text1: "Donasi berhasil disimpan", text1: "Donasi berhasil disimpan",
}); });
router.replace("/donation/status"); router.replace("/donation/status?status=review");
} catch (error) { } catch (error) {
console.log("[ERROR]", error); console.log("[ERROR]", error);
} finally { } finally {

View File

@@ -115,7 +115,11 @@ export default function InvestmentAddNews() {
onChangeText={(value) => setData({ ...data, deskripsi: value })} onChangeText={(value) => setData({ ...data, deskripsi: value })}
/> />
<ButtonCustom isLoading={isLoading} onPress={handlerSubmit}> <ButtonCustom
disabled={!data.title || !data.deskripsi || isLoading}
isLoading={isLoading}
onPress={handlerSubmit}
>
Simpan Simpan
</ButtonCustom> </ButtonCustom>
</StackCustom> </StackCustom>

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

@@ -18,10 +18,7 @@ import {
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";
import { import { deleteFileService, uploadFileService } from "@/service/upload-service";
deleteFileService,
uploadFileService,
} 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";
@@ -70,7 +67,7 @@ export default function InvestmentEdit() {
useCallback(() => { useCallback(() => {
onLoadMaster(); onLoadMaster();
onLoadData(); onLoadData();
}, [id]) }, [id]),
); );
const onLoadMaster = async () => { const onLoadMaster = async () => {
@@ -178,7 +175,7 @@ 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" category: "data",
}); });
if (responseUpdate.success) { if (responseUpdate.success) {
@@ -256,6 +253,7 @@ export default function InvestmentEdit() {
/> />
<TextInputCustom <TextInputCustom
disabled
required required
placeholder="0" placeholder="0"
label="Total Lembar" label="Total Lembar"

View File

@@ -54,7 +54,7 @@ export default function InvestmentCreate() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
onLoadMaster(); onLoadMaster();
}, []) }, []),
); );
const onLoadMaster = async () => { const onLoadMaster = async () => {
@@ -264,6 +264,7 @@ export default function InvestmentCreate() {
<StackCustom gap={0}> <StackCustom gap={0}>
<TextInputCustom <TextInputCustom
disabled
required required
placeholder="0" placeholder="0"
label="Total Lembar" label="Total Lembar"

View File

@@ -36,7 +36,7 @@ export default function ProfileLayout() {
<Stack.Screen <Stack.Screen
name="[id]/blocked-list" name="[id]/blocked-list"
options={{ title: "Blocked List", headerLeft: () => <BackButton /> }} options={{ title: "Daftar Blokir", headerLeft: () => <BackButton /> }}
/> />
<Stack.Screen <Stack.Screen

View File

@@ -1,4 +1,5 @@
import { import {
AlertDefaultSystem,
BadgeCustom, BadgeCustom,
BaseBox, BaseBox,
BoxButtonOnFooter, BoxButtonOnFooter,
@@ -9,6 +10,7 @@ import {
} from "@/components"; } from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import { useAuth } from "@/hooks/use-auth";
import { import {
apiAdminDonationInvoiceDetailById, apiAdminDonationInvoiceDetailById,
apiAdminDonationInvoiceUpdateById, apiAdminDonationInvoiceUpdateById,
@@ -22,6 +24,7 @@ import { useCallback, useState } from "react";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
export default function AdminDonasiTransactionDetail() { export default function AdminDonasiTransactionDetail() {
const { user } = useAuth();
const { id, status } = useLocalSearchParams(); const { id, status } = useLocalSearchParams();
console.log("[STATUS]", id, status); console.log("[STATUS]", id, status);
@@ -33,7 +36,7 @@ export default function AdminDonasiTransactionDetail() {
onLoadData(); onLoadData();
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [id]) }, [id]),
); );
const onLoadData = async () => { const onLoadData = async () => {
@@ -57,6 +60,7 @@ export default function AdminDonasiTransactionDetail() {
const newData = { const newData = {
donationId: data?.donasiId, donationId: data?.donasiId,
nominal: data?.nominal, nominal: data?.nominal,
senderId: user?.id,
}; };
const response = await apiAdminDonationInvoiceUpdateById({ const response = await apiAdminDonationInvoiceUpdateById({
@@ -97,7 +101,15 @@ export default function AdminDonasiTransactionDetail() {
<ButtonCustom <ButtonCustom
isLoading={isLoading} isLoading={isLoading}
onPress={() => { onPress={() => {
handlerSubmit(); AlertDefaultSystem({
title: "Konfirmasi transaksi",
message: "Apakah anda yakin ingin menyetujui transaksi ini?",
textLeft: "Tidak",
textRight: "Ya",
onPressRight: () => {
handlerSubmit();
},
});
}} }}
> >
Terima donasi Terima donasi
@@ -109,7 +121,7 @@ export default function AdminDonasiTransactionDetail() {
return ( return (
<BoxButtonOnFooter> <BoxButtonOnFooter>
<ButtonCustom disabled> <ButtonCustom disabled>
{data?.DonasiMaster_StatusInvoice?.name} {data?.DonasiMaster_StatusInvoice?.name}
</ButtonCustom> </ButtonCustom>
</BoxButtonOnFooter> </BoxButtonOnFooter>
); );
@@ -140,7 +152,7 @@ export default function AdminDonasiTransactionDetail() {
})} })}
> >
{_.startCase( {_.startCase(
(data?.DonasiMaster_StatusInvoice?.name as any) || "-" (data?.DonasiMaster_StatusInvoice?.name as any) || "-",
)} )}
</BadgeCustom> </BadgeCustom>
)) || )) ||
@@ -157,7 +169,7 @@ export default function AdminDonasiTransactionDetail() {
<ButtonCustom <ButtonCustom
onPress={() => onPress={() =>
router.push( router.push(
`/(application)/(image)/preview-image/${data?.imageId}` `/(application)/(image)/preview-image/${data?.imageId}`,
) )
} }
> >

View File

@@ -7,15 +7,15 @@ import {
} from "@/components"; } from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject"; import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import { useAuth } from "@/hooks/use-auth";
import { funUpdateStatusDonation } from "@/screens/Admin/Donation/funDonationUpdateStatus"; import { funUpdateStatusDonation } from "@/screens/Admin/Donation/funDonationUpdateStatus";
import { import { apiAdminDonationDetailById } from "@/service/api-admin/api-admin-donation";
apiAdminDonationDetailById
} from "@/service/api-admin/api-admin-donation";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import React from "react"; import React from "react";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
export default function AdminDonationRejectInput() { export default function AdminDonationRejectInput() {
const { user } = useAuth();
const { id, status } = useLocalSearchParams(); const { id, status } = useLocalSearchParams();
const [data, setData] = React.useState<any | null>(null); const [data, setData] = React.useState<any | null>(null);
@@ -24,7 +24,7 @@ export default function AdminDonationRejectInput() {
useFocusEffect( useFocusEffect(
React.useCallback(() => { React.useCallback(() => {
onLoadData(); onLoadData();
}, [id]) }, [id]),
); );
const onLoadData = async () => { const onLoadData = async () => {
@@ -48,11 +48,23 @@ export default function AdminDonationRejectInput() {
changeStatus: "publish" | "review" | "reject"; changeStatus: "publish" | "review" | "reject";
}) => { }) => {
try { try {
if (!user?.id) {
Toast.show({
type: "error",
text1: "User tidak ditemukan",
});
return;
}
setIsLoading(true); setIsLoading(true);
const response = await funUpdateStatusDonation({ const response = await funUpdateStatusDonation({
id: id as string, id: id as string,
changeStatus, changeStatus,
data: data, data: {
senderId: user?.id as string,
catatan: data,
},
}); });
if (!response.success) { if (!response.success) {
@@ -61,7 +73,7 @@ export default function AdminDonationRejectInput() {
text1: "Report gagal", text1: "Report gagal",
}); });
return return;
} }
Toast.show({ Toast.show({

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(
<GridSpan_4_8 (item, i) => (
key={i} <GridSpan_4_8
label={<TextCustom bold>{item.label}</TextCustom>} key={i}
value={<TextCustom>{item.value}</TextCustom>} label={<TextCustom bold>{item.label}</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

@@ -0,0 +1,33 @@
import { Modal, View } from "react-native";
export default function ModalReactNative({
children,
isVisible,
}: {
children: React.ReactNode;
isVisible: boolean;
}) {
return (
<Modal
animationType="slide"
backdropColor={"rgba(0, 0, 0, 0.5)"}
visible={isVisible}
>
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.5)",
// margin: 10,
marginBlock: 30,
padding: 10,
borderRadius: 10,
paddingTop: 30
}}
>
{children}
</View>
</Modal>
);
}

View File

@@ -21,7 +21,7 @@ type AuthContextType = {
isAuthenticated: boolean; isAuthenticated: boolean;
isAdmin: boolean; isAdmin: boolean;
isUserActive: boolean; isUserActive: boolean;
loginWithNomor: (nomor: string) => Promise<void>; loginWithNomor: (nomor: string) => Promise<boolean>;
validateOtp: (nomor: string) => Promise<any>; validateOtp: (nomor: string) => Promise<any>;
logout: () => Promise<void>; logout: () => Promise<void>;
registerUser: (userData: { registerUser: (userData: {
@@ -30,12 +30,12 @@ type AuthContextType = {
termsOfServiceAccepted: boolean; termsOfServiceAccepted: boolean;
}) => Promise<void>; }) => Promise<void>;
userData: (token: string) => Promise<any>; userData: (token: string) => Promise<any>;
acceptedTerms: (nomor: string) => Promise<any>; acceptedTerms: (nomor: string, onSetModalVisible: (visible: boolean) => void) => Promise<any>;
}; };
// --- Create Context --- // --- Create Context ---
export const AuthContext = createContext<AuthContextType | undefined>( export const AuthContext = createContext<AuthContextType | undefined>(
undefined undefined,
); );
export const AuthProvider = ({ children }: { children: React.ReactNode }) => { export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
@@ -79,31 +79,35 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const response = await apiLogin({ nomor: nomor }); const response = await apiLogin({ nomor: nomor });
console.log("[RESPONSE AUTH]", JSON.stringify(response, null, 2)); console.log("[RESPONSE AUTH]", JSON.stringify(response, null, 2));
if (response.success) { if (response.success && response.isAcceptTerms) {
if (response.isAcceptTerms) {
Toast.show({ await AsyncStorage.setItem("kode_otp", response.kodeId);
type: "success", router.push(`/verification?nomor=${nomor}`);
text1: "Sukses", return true;
text2: "Kode OTP berhasil dikirim",
});
await AsyncStorage.setItem("kode_otp", response.kodeId);
router.push(`/verification?nomor=${nomor}`);
return;
} else {
router.push(`/eula?nomor=${nomor}`);
return;
}
} else { } else {
router.push(`/eula?nomor=${nomor}`); // router.push(`/eula?nomor=${nomor}`);
return false;
// Toast.show({
// type: "info",
// text1: "Info",
// text2: "Silahkan mendaftar",
// });
return;
} }
// if (response.success) {
// if (response.isAcceptTerms) {
// Toast.show({
// type: "success",
// text1: "Sukses",
// text2: "Kode OTP berhasil dikirim",
// });
// await AsyncStorage.setItem("kode_otp", response.kodeId);
// router.push(`/verification?nomor=${nomor}`);
// return false
// } else {
// // router.push(`/eula?nomor=${nomor}`);
// return true
// }
// } else {
// router.push(`/eula?nomor=${nomor}`);
// return true;
// }
} catch (error: any) { } catch (error: any) {
throw new Error(error.response?.data?.message || "Gagal kirim OTP"); throw new Error(error.response?.data?.message || "Gagal kirim OTP");
} finally { } finally {
@@ -158,7 +162,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
} catch (error: any) { } catch (error: any) {
console.log("Error validasi otp >>", (error as Error).message || error); console.log("Error validasi otp >>", (error as Error).message || error);
throw new Error( throw new Error(
error.response?.data?.message || "OTP salah atau user tidak ditemukan" error.response?.data?.message || "OTP salah atau user tidak ditemukan",
); );
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -187,7 +191,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
} catch (error: any) { } catch (error: any) {
console.log( console.log(
"[LOAD USER DATA]", "[LOAD USER DATA]",
error.response?.data?.message + "user" || "Gagal mengambil data user" error.response?.data?.message + "user" || "Gagal mengambil data user",
); );
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@@ -261,7 +265,8 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
} }
}; };
const acceptedTerms = async (nomor: string) => { // --- 6. Accept Terms ---
const acceptedTerms = async (nomor: string, onSetModalVisible: (visible: boolean) => void) => {
try { try {
setIsLoading(true); setIsLoading(true);
const response = await apiUpdatedTermCondition({ nomor: nomor }); const response = await apiUpdatedTermCondition({ nomor: nomor });
@@ -283,6 +288,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
console.log("Error accept terms", error); console.log("Error accept terms", error);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
// onSetModalVisible(false);
} }
}; };

View File

@@ -18,7 +18,7 @@ android: bunx expo prebuild --platform android
adb devices : cek device yang terhubung adb devices : cek device yang terhubung
Note: izinkan perangkat dulu agar statusnya tidak unauthorized Note: izinkan perangkat dulu agar statusnya tidak unauthorized
adb install android/app/build/outputs/apk/debug/app-debug.apk : install apk ke device adb install android/app/build/outputs/apk/debug/app-debug.apk : install apk ke device / emulator
Note: Note:
Gunakan flag -s (serial) di perintah adb untuk menentukan target Gunakan flag -s (serial) di perintah adb untuk menentukan target
adb -s <0G52319V261040B2 ini adalah id nya> install android/app/build/outputs/apk/debug/app-debug.apk adb -s <0G52319V261040B2 ini adalah id nya> install android/app/build/outputs/apk/debug/app-debug.apk

View File

@@ -39,7 +39,7 @@
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>19</string> <string>20</string>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>
<false/> <false/>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>

View File

@@ -1,4 +1,5 @@
import { apiAdminDonationUpdateStatus } from "@/service/api-admin/api-admin-donation"; import { apiAdminDonationUpdateStatus } from "@/service/api-admin/api-admin-donation";
import { typeRejectedData } from "@/types/type-collect-other";
export const funUpdateStatusDonation = async ({ export const funUpdateStatusDonation = async ({
id, id,
@@ -7,7 +8,7 @@ export const funUpdateStatusDonation = async ({
}: { }: {
id: string; id: string;
changeStatus: "publish" | "review" | "reject"; changeStatus: "publish" | "review" | "reject";
data?: string; data?: typeRejectedData;
}) => { }) => {
try { try {
const response = await apiAdminDonationUpdateStatus({ const response = await apiAdminDonationUpdateStatus({

View File

@@ -0,0 +1,264 @@
// app/syarat-dan-ketentuan.tsx
import {
View,
Text,
ScrollView,
TouchableOpacity,
StyleSheet,
} from "react-native";
import { useState, useRef } from "react";
import { useLocalSearchParams, useRouter } from "expo-router";
import { SafeAreaView } from "react-native-safe-area-context";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth";
export default function EULASection({ nomor, onSetModalVisible }: { nomor: string, onSetModalVisible: (visible: boolean) => void }) {
const { acceptedTerms } = useAuth();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isAtBottom, setIsAtBottom] = useState(false);
const scrollViewRef = useRef<ScrollView>(null);
const handleScroll = (event: any) => {
const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent;
const paddingToBottom = 20;
const isCloseToBottom =
layoutMeasurement.height + contentOffset.y >=
contentSize.height - paddingToBottom;
setIsAtBottom(isCloseToBottom);
};
const handleAccept = async () => {
// console.log("Accept terms", nomor);
// onSetModalVisible(false);
try {
if (!isAtBottom) return;
setIsLoading(true);
await acceptedTerms(nomor as string, onSetModalVisible);
} catch (error) {
console.log("Error accept terms", error);
} finally {
setIsLoading(false);
}
};
return (
<SafeAreaView edges={["bottom"]} style={styles.container}>
<Text style={styles.title}>
Syarat & Ketentuan Penggunaan HIPMI Badung Connect
</Text>
<ScrollView
ref={scrollViewRef}
onScroll={handleScroll}
scrollEventThrottle={16}
style={styles.scrollView}
contentContainerStyle={styles.scrollContent}
>
<Text style={styles.paragraph}>
Dengan menggunakan aplikasi{" "}
<Text style={styles.bold}>HIPMI Badung Connect</Text> (Aplikasi),
Anda setuju untuk mematuhi dan terikat oleh syarat dan ketentuan
berikut. Jika Anda tidak setuju dengan ketentuan ini, harap jangan
gunakan Aplikasi.
</Text>
<Text style={styles.heading}>1. Definisi</Text>
<Text style={styles.paragraph}>
<Text style={styles.bold}>HIPMI Badung Connect</Text> adalah platform
digital resmi untuk anggota Himpunan Pengusaha Muda Indonesia (HIPMI)
Kabupaten Badung, yang bertujuan memfasilitasi jaringan, kolaborasi,
dan pertumbuhan bisnis para pengusaha muda.
</Text>
<Text style={styles.heading}>2. Larangan Konten Tidak Pantas</Text>
<Text style={styles.paragraph}>
Anda <Text style={styles.bold}>dilarang keras</Text> memposting,
mengirim, membagikan, atau mengunggah konten apa pun yang mengandung:
</Text>
<View style={styles.list}>
<Text style={styles.listItem}>
Ujaran kebencian, diskriminasi, atau konten SARA (Suku, Agama,
Ras, Antar-golongan)
</Text>
<Text style={styles.listItem}>
Pornografi, konten seksual eksplisit, atau gambar tidak senonoh
</Text>
<Text style={styles.listItem}>
Ancaman, pelecehan, bullying, atau perilaku melecehkan
</Text>
<Text style={styles.listItem}>
Informasi palsu, hoaks, spam, atau konten menyesatkan
</Text>
<Text style={styles.listItem}>
Konten ilegal, melanggar hukum, atau melanggar hak kekayaan
intelektual pihak lain
</Text>
<Text style={styles.listItem}>
Promosi narkoba, perjudian, atau aktivitas ilegal lainnya
</Text>
</View>
<Text style={styles.heading}>3. Tanggung Jawab Pengguna</Text>
<Text style={styles.paragraph}>
Anda bertanggung jawab penuh atas setiap konten yang Anda unggah atau
bagikan melalui fitur-fitur berikut:
</Text>
<View style={styles.list}>
<Text style={styles.listItem}> Profil (bio, foto, portofolio)</Text>
<Text style={styles.listItem}> Forum diskusi</Text>
<Text style={styles.listItem}> Chat pribadi atau grup</Text>
<Text style={styles.listItem}>
Lowongan kerja, investasi, dan donasi
</Text>
</View>
<Text style={styles.paragraph}>
Konten yang melanggar ketentuan ini dapat dihapus kapan saja tanpa
pemberitahuan.
</Text>
<Text style={styles.heading}>4. Tindakan terhadap Pelanggaran</Text>
<Text style={styles.paragraph}>
Jika kami menerima laporan atau menemukan konten yang melanggar
ketentuan ini, kami akan:
</Text>
<View style={styles.list}>
<Text style={styles.listItem}>
Segera menghapus konten tersebut
</Text>
<Text style={styles.listItem}>
Memberikan peringatan atau memblokir akun pengguna
</Text>
<Text style={styles.listItem}>
Dalam kasus berat, melaporkan ke pihak berwajib sesuai hukum yang
berlaku
</Text>
</View>
<Text style={styles.paragraph}>
Tim kami berkomitmen untuk menanggapi laporan konten tidak pantas{" "}
<Text style={styles.bold}>dalam waktu 24 jam</Text>.
</Text>
<Text style={styles.heading}>5. Mekanisme Pelaporan</Text>
<Text style={styles.paragraph}>
Anda dapat melaporkan konten atau pengguna yang mencurigakan melalui:
</Text>
<View style={styles.list}>
<Text style={styles.listItem}>
Tombol <Text style={styles.bold}>Laporkan</Text> di setiap
posting forum atau pesan chat
</Text>
<Text style={styles.listItem}>
Tombol <Text style={styles.bold}>Blokir Pengguna</Text> di
profil pengguna
</Text>
</View>
<Text style={styles.paragraph}>
Setiap laporan akan ditangani secara rahasia dan segera.
</Text>
<Text style={styles.heading}>6. Perubahan Ketentuan</Text>
<Text style={styles.paragraph}>
Kami berhak memperbarui Syarat & Ketentuan ini sewaktu-waktu. Versi
terbaru akan dipublikasikan di halaman ini dengan tanggal revisi yang
diperbarui.
</Text>
<Text style={styles.heading}>7. Kontak</Text>
<Text style={styles.paragraph}>
Jika Anda memiliki pertanyaan tentang ketentuan ini, silakan hubungi
kami di:{"\n"}
<Text style={[styles.bold, { color: "#1E90FF" }]}>
bip.baliinteraktifperkasa@gmail.com
</Text>
</Text>
<Text style={styles.footer}>
© 2026 Bali Interaktif Perkasa. All rights reserved.
</Text>
</ScrollView>
<TouchableOpacity
onPress={handleAccept}
disabled={!isAtBottom || isLoading}
style={[styles.button, { opacity: !isAtBottom || isLoading ? 0.6 : 1 }]}
>
<Text style={styles.buttonText}>
{isLoading ? "Menyimpan..." : "Saya Setuju"}
</Text>
</TouchableOpacity>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: MainColor.darkblue,
padding: 16,
},
title: {
fontSize: 20,
fontWeight: "bold",
textAlign: "center",
marginBottom: 16,
color: MainColor.white,
},
scrollView: {
flex: 1,
marginBottom: 20,
},
scrollContent: {
paddingBottom: 30,
},
heading: {
fontSize: 16,
fontWeight: "600",
marginTop: 16,
marginBottom: 8,
color: MainColor.white,
},
paragraph: {
fontSize: 14,
lineHeight: 22,
color: MainColor.white,
marginBottom: 12,
},
bold: {
fontWeight: "600",
},
list: {
marginLeft: 8,
marginBottom: 12,
},
listItem: {
fontSize: 14,
lineHeight: 22,
color: MainColor.white,
marginBottom: 6,
},
footer: {
fontSize: 12,
color: MainColor.white,
textAlign: "center",
marginTop: 20,
paddingTop: 10,
borderTopWidth: 2,
borderTopColor: AccentColor.blue,
},
button: {
backgroundColor: MainColor.yellow,
paddingVertical: 14,
borderRadius: 8,
alignItems: "center",
width: 200,
alignSelf: "center",
},
buttonText: {
color: "#fff",
fontSize: 16,
fontWeight: "600",
},
});

View File

@@ -12,25 +12,6 @@ import { SafeAreaView } from "react-native-safe-area-context";
import { AccentColor, MainColor } from "@/constants/color-palet"; import { AccentColor, MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
// Ganti dengan API call ke backend Anda
// const acceptEula = async (): Promise<boolean> => {
// try {
// const response = await fetch("/api/user/update-eula", {
// method: "PATCH",
// headers: { "Content-Type": "application/json" },
// credentials: "include",
// body: JSON.stringify({
// eulaAcceptedAt: new Date().toISOString(),
// eulaVersion: "2026-01-v1", // sesuaikan versi Anda
// }),
// });
// return response.ok;
// } catch (error) {
// console.error("Gagal menyimpan persetujuan EULA:", error);
// return false;
// }
// };
export default function EULAView() { export default function EULAView() {
const { acceptedTerms } = useAuth(); const { acceptedTerms } = useAuth();
const { nomor } = useLocalSearchParams(); const { nomor } = useLocalSearchParams();
@@ -52,7 +33,7 @@ export default function EULAView() {
if (!isAtBottom) return; if (!isAtBottom) return;
setIsLoading(true); setIsLoading(true);
await acceptedTerms(nomor as string); // await acceptedTerms(nomor as string);
} catch (error) { } catch (error) {
console.log("Error accept terms", error); console.log("Error accept terms", error);
} finally { } finally {

View File

@@ -1,25 +1,31 @@
import { NewWrapper } from "@/components"; import { NewWrapper, TextCustom } from "@/components";
import ButtonCustom from "@/components/Button/ButtonCustom"; import ButtonCustom from "@/components/Button/ButtonCustom";
import ModalReactNative from "@/components/Modal/ModalReactNative";
import Spacing from "@/components/_ShareComponent/Spacing"; import Spacing from "@/components/_ShareComponent/Spacing";
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 { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { apiVersion } from "@/service/api-config"; import { apiVersion, BASE_URL } from "@/service/api-config";
import { GStyles } from "@/styles/global-styles"; import { GStyles } from "@/styles/global-styles";
import { openBrowser } from "@/utils/openBrower";
import versionBadge from "@/utils/viersionBadge"; import versionBadge from "@/utils/viersionBadge";
import VersionBadge from "@/utils/viersionBadge"; import VersionBadge from "@/utils/viersionBadge";
import { Redirect } from "expo-router"; import { Redirect } from "expo-router";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { RefreshControl, Text, View } from "react-native"; import { Modal, RefreshControl, Text, View } from "react-native";
import PhoneInput, { ICountry } from "react-native-international-phone-number"; import PhoneInput, { ICountry } from "react-native-international-phone-number";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
import EULASection from "./EULASection";
export default function LoginView() { export default function LoginView() {
const url = BASE_URL;
const [version, setVersion] = useState<string>(""); const [version, setVersion] = useState<string>("");
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null); const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
const [inputValue, setInputValue] = useState<string>(""); const [inputValue, setInputValue] = useState<string>("");
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
const [refreshing, setRefreshing] = useState<boolean>(false); const [refreshing, setRefreshing] = useState<boolean>(false);
const [modalVisible, setModalVisible] = useState(false);
const [numberToEULA, setNumberToEULA] = useState<string>("");
const { loginWithNomor, token, isAdmin, isUserActive } = useAuth(); const { loginWithNomor, token, isAdmin, isUserActive } = useAuth();
@@ -84,10 +90,17 @@ export default function LoginView() {
let fixNumber = inputValue.replace(/\s+/g, "").replace(/^0+/, ""); let fixNumber = inputValue.replace(/\s+/g, "").replace(/^0+/, "");
const realNumber = callingCode + fixNumber; const realNumber = callingCode + fixNumber;
try { try {
setLoading(true); setLoading(true);
await loginWithNomor(realNumber); const loginRes = await loginWithNomor(realNumber);
if (!loginRes) {
setModalVisible(true);
}
setNumberToEULA(realNumber);
} catch (error) { } catch (error) {
console.log("Error login", error); console.log("Error login", error);
Toast.show({ Toast.show({
@@ -98,30 +111,6 @@ export default function LoginView() {
} finally { } finally {
setLoading(false); setLoading(false);
} }
// try {
// setLoading(true);
// // const response = await apiLogin({ nomor: realNumber });
// const response = await loginWithNomor(realNumber);
// console.log("[RESPONSE]", response);
// Toast.show({
// type: "success",
// text1: "Sukses",
// text2: "Kode OTP berhasil dikirim",
// });
// // router.navigate(`/verification?nomor=${realNumber}`);
// } catch (error) {
// console.log("Error login", error);
// Toast.show({
// type: "error",
// text1: "Error",
// text2: error as string,
// });
// } finally {
// setLoading(false);
// }
} }
if (token && token !== "" && !isUserActive) { if (token && token !== "" && !isUserActive) {
@@ -148,6 +137,7 @@ export default function LoginView() {
} }
> >
<View style={GStyles.authContainer}> <View style={GStyles.authContainer}>
<View> <View>
<View style={GStyles.authContainerTitle}> <View style={GStyles.authContainerTitle}>
<Text style={GStyles.authSubTitle}>WELCOME TO</Text> <Text style={GStyles.authSubTitle}>WELCOME TO</Text>
@@ -185,11 +175,41 @@ export default function LoginView() {
<ButtonCustom onPress={handleLogin} isLoading={loading}> <ButtonCustom onPress={handleLogin} isLoading={loading}>
Login Login
</ButtonCustom> </ButtonCustom>
<Spacing /> <Spacing height={50} />
{/* <ButtonCustom onPress={() => router.navigate("/(application)/coba")}>
Coba {/* <ButtonCustom
onPress={() => {
setModalVisible(true);
console.log("Show modal", modalVisible);
}}
>
Show Modal
</ButtonCustom> */} </ButtonCustom> */}
{/* <CheckboxCustom value={term} onChange={() => setTerm(!term)} /> */}
<Text
style={{ ...GStyles.textLabel, textAlign: "center", fontSize: 12 }}
>
Dengan menggunakan aplikasi ini, Anda telah menyetujui{" "}
<Text
style={{
color: MainColor.yellow,
textDecorationLine: "underline",
}}
onPress={() => {
const toUrl = `${url}/terms-of-service.html`;
openBrowser(toUrl);
}}
>
Syarat & Ketentuan
</Text>{" "}
dan seluruh kebijakan privasi yang berlaku.
</Text>
</View> </View>
<ModalReactNative isVisible={modalVisible}>
<EULASection nomor={numberToEULA || ""} onSetModalVisible={setModalVisible} />
</ModalReactNative>
</NewWrapper> </NewWrapper>
); );
} }

View File

@@ -70,7 +70,7 @@ export const drawerItemsProfile = ({
color={AccentColor.white} color={AccentColor.white}
/> />
), ),
label: "Blocked List", label: "Daftar Blokir",
path: `/(application)/profile/${id}/blocked-list`, path: `/(application)/profile/${id}/blocked-list`,
value: "blocked-list", value: "blocked-list",
}, },

View File

@@ -1,3 +1,4 @@
import { typeRejectedData } from "@/types/type-collect-other";
import { apiConfig } from "../api-config"; import { apiConfig } from "../api-config";
export async function apiAdminDonation({ export async function apiAdminDonation({
@@ -33,7 +34,7 @@ export async function apiAdminDonationUpdateStatus({
}: { }: {
id: string; id: string;
changeStatus: "publish" | "review" | "reject"; changeStatus: "publish" | "review" | "reject";
data?: string; data?: typeRejectedData;
}) { }) {
try { try {
const response = await apiConfig.put( const response = await apiConfig.put(

View File

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