Compare commits

...

5 Commits

Author SHA1 Message Date
624bd49f69 QC Admin ( Inno )
Fix:
   modified:   android/app/build.gradle
        modified:   app.config.js
        modified:   app/(application)/admin/donation/[id]/[status]/index.tsx
        modified:   app/(application)/admin/donation/[id]/[status]/transaction-detail.tsx
        modified:   app/(application)/admin/donation/[id]/detail-disbursement-of-funds.tsx
        modified:   app/(application)/admin/donation/category.tsx
        modified:   app/(application)/admin/event/[id]/[status]/index.tsx
        modified:   app/(application)/admin/event/[id]/list-of-participants.tsx
        modified:   app/(application)/admin/event/[status]/status.tsx
        modified:   app/(application)/admin/forum/[id]/index.tsx
        modified:   app/(application)/admin/forum/[id]/list-report-comment.tsx
        modified:   app/(application)/admin/forum/[id]/list-report-posting.tsx
        modified:   app/(application)/admin/investment/[id]/[status]/index.tsx
        modified:   app/(application)/admin/investment/[id]/[status]/transaction-detail.tsx
        modified:   app/(application)/admin/voting/[id]/[status]/index.tsx
        modified:   components/DateInput/DateTimeIOS.tsx
        modified:   components/_ShareComponent/Admin/ButtonReject.tsx
        deleted:    components/_ShareComponent/GridDetail_4_8.tsx

Add:/
components/_ShareComponent/GridSpan_4_8.tsx

### No Issue
2025-12-05 17:20:39 +08:00
2446e9d51a Fix apple reject EULA
Add:
components/Alert/AlertWarning.ts
        utils/badWordsIndonesia.ts

Fix:
- app.config.js
- app/(application)/(user)/forum/[id]/edit.tsx
- app/(application)/(user)/forum/[id]/index.tsx
- app/(application)/(user)/forum/create.tsx
- ios/HIPMIBadungConnect/Info.plist

### No Issue
2025-12-05 11:46:36 +08:00
ab5733f336 Fix redirect admin 2025-12-04 17:41:19 +08:00
f5e30087ed Fix QC Inno
Fix:
- app/(application)/admin/donation/category-create.tsx
- app/(application)/admin/donation/category-update.tsx
- app/(application)/admin/donation/category.tsx
- components/_ShareComponent/Admin/TableValue.tsx
- screens/Authentication/LoginView.tsx
- service/api-admin/api-master-admin.ts

### No Issue
2025-12-04 16:59:39 +08:00
a93f97ed6a Fix rejected Apple
Add:
-  utils/viersionBadge.ts

Fix:
- app.config.js
- context/AuthContext.tsx
- ios/HIPMIBadungConnect/Info.plist
- screens/Authentication/LoginView.tsx
- screens/Authentication/VerificationView.tsx
- service/api-config.ts

### No Issue
2025-12-03 17:23:12 +08:00
34 changed files with 725 additions and 232 deletions

View File

@@ -100,7 +100,7 @@ packagingOptions {
applicationId 'com.bip.hipmimobileapp' applicationId 'com.bip.hipmimobileapp'
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 2 versionCode 3
versionName "1.0.1" versionName "1.0.1"
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\"" buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""

View File

@@ -19,7 +19,7 @@ export default {
"NSLocationWhenInUseUsageDescription": "Aplikasi membutuhkan akses lokasi untuk menampilkan peta.", "NSLocationWhenInUseUsageDescription": "Aplikasi membutuhkan akses lokasi untuk menampilkan peta.",
}, },
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"], associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"],
buildNumber: "10", buildNumber: "13",
}, },
android: { android: {
@@ -30,7 +30,7 @@ export default {
}, },
edgeToEdgeEnabled: true, edgeToEdgeEnabled: true,
package: "com.bip.hipmimobileapp", package: "com.bip.hipmimobileapp",
versionCode: 2, versionCode: 3,
// softwareKeyboardLayoutMode: 'resize', // option: untuk mengatur keyboard pada room chst collaboration // softwareKeyboardLayoutMode: 'resize', // option: untuk mengatur keyboard pada room chst collaboration
intentFilters: [ intentFilters: [
{ {

View File

@@ -5,9 +5,12 @@ import {
TextAreaCustom, TextAreaCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AlertWarning from "@/components/Alert/AlertWarning";
import { apiForumGetOne, apiForumUpdate } from "@/service/api-client/api-forum"; import { apiForumGetOne, apiForumUpdate } from "@/service/api-client/api-forum";
import { isBadContent } from "@/utils/badWordsIndonesia";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { Alert } from "react-native";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
export default function ForumEdit() { export default function ForumEdit() {
@@ -43,6 +46,12 @@ export default function ForumEdit() {
}); });
return; return;
} }
if (isBadContent(text)) {
AlertWarning({});
return;
}
try { try {
setIsLoading(true); setIsLoading(true);
const response = await apiForumUpdate({ const response = await apiForumUpdate({

View File

@@ -7,6 +7,7 @@ import {
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AlertWarning from "@/components/Alert/AlertWarning";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import Forum_CommentarBoxSection from "@/screens/Forum/CommentarBoxSection"; import Forum_CommentarBoxSection from "@/screens/Forum/CommentarBoxSection";
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection"; import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
@@ -18,9 +19,11 @@ import {
apiForumGetOne, apiForumGetOne,
apiForumUpdateStatus, apiForumUpdateStatus,
} from "@/service/api-client/api-forum"; } from "@/service/api-client/api-forum";
import { isBadContent } from "@/utils/badWordsIndonesia";
import { useFocusEffect, useLocalSearchParams } from "expo-router"; import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { Alert } from "react-native";
interface CommentProps { interface CommentProps {
id: string; id: string;
@@ -110,11 +113,15 @@ export default function ForumDetail() {
// Create Commentar // Create Commentar
const handlerCreateCommentar = async () => { const handlerCreateCommentar = async () => {
if (isBadContent(text)) {
AlertWarning({});
return;
}
const newData = { const newData = {
comment: text, comment: text,
authorId: user?.id, authorId: user?.id,
}; };
try { try {
setLoadingComment(true); setLoadingComment(true);
const response = await apiForumCreateComment({ const response = await apiForumCreateComment({

View File

@@ -2,12 +2,15 @@ import {
BoxButtonOnFooter, BoxButtonOnFooter,
ButtonCustom, ButtonCustom,
TextAreaCustom, TextAreaCustom,
ViewWrapper, ViewWrapper
} from "@/components"; } from "@/components";
import AlertWarning from "@/components/Alert/AlertWarning";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { apiForumCreate } from "@/service/api-client/api-forum"; import { apiForumCreate } from "@/service/api-client/api-forum";
import { isBadContent } from "@/utils/badWordsIndonesia";
import { router } from "expo-router"; import { router } from "expo-router";
import { useState } from "react"; import { useState } from "react";
import { Alert } from "react-native";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
export default function ForumCreate() { export default function ForumCreate() {
@@ -16,11 +19,16 @@ export default function ForumCreate() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const handlerSubmit = async () => { const handlerSubmit = async () => {
if (isBadContent(text)) {
AlertWarning({})
return;
}
const newData = { const newData = {
diskusi: text, diskusi: text,
authorId: user?.id, authorId: user?.id,
}; };
try { try {
setIsLoading(true); setIsLoading(true);
const response = await apiForumCreate({ data: newData }); const response = await apiForumCreate({ data: newData });

View File

@@ -18,7 +18,7 @@ import { IconDot, IconList } from "@/components/_Icon/IconComponent";
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 AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview"; import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import ReportBox from "@/components/Box/ReportBox"; import ReportBox from "@/components/Box/ReportBox";
import { ICON_SIZE_BUTTON, TEXT_SIZE_LARGE } from "@/constants/constans-value"; import { ICON_SIZE_BUTTON, TEXT_SIZE_LARGE } from "@/constants/constans-value";
import AdminDonation_BoxOfDonationStory from "@/screens/Admin/Donation/BoxOfDonationStory"; import AdminDonation_BoxOfDonationStory from "@/screens/Admin/Donation/BoxOfDonationStory";
@@ -195,7 +195,7 @@ export default function AdminDonationDetail() {
<StackCustom gap={5}> <StackCustom gap={5}>
{listPencarianDana.map((item, i) => ( {listPencarianDana.map((item, i) => (
<GridDetail_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>}
@@ -236,7 +236,7 @@ export default function AdminDonationDetail() {
<Spacing /> <Spacing />
<StackCustom gap={"xs"}> <StackCustom gap={"xs"}>
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Jumlah Donatur</TextCustom>} label={<TextCustom bold>Jumlah Donatur</TextCustom>}
value={ value={
<TextCustom> <TextCustom>
@@ -244,7 +244,7 @@ export default function AdminDonationDetail() {
</TextCustom> </TextCustom>
} }
/> />
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Dana Terkumpul</TextCustom>} label={<TextCustom bold>Dana Terkumpul</TextCustom>}
value={ value={
<TextCustom> <TextCustom>
@@ -261,7 +261,7 @@ export default function AdminDonationDetail() {
<StackCustom> <StackCustom>
<DummyLandscapeImage imageId={data?.imageId || ""} /> <DummyLandscapeImage imageId={data?.imageId || ""} />
{listData.map((item, i) => ( {listData.map((item, i) => (
<GridDetail_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>}

View File

@@ -8,7 +8,7 @@ import {
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import { import {
apiAdminDonationInvoiceDetailById, apiAdminDonationInvoiceDetailById,
apiAdminDonationInvoiceUpdateById, apiAdminDonationInvoiceUpdateById,
@@ -177,7 +177,7 @@ export default function AdminDonasiTransactionDetail() {
<BaseBox> <BaseBox>
<StackCustom> <StackCustom>
{listData.map((item, index) => ( {listData.map((item, index) => (
<GridDetail_4_8 <GridSpan_4_8
key={index} key={index}
label={<TextCustom bold>{item.label}</TextCustom>} label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>} value={<TextCustom>{item.value}</TextCustom>}

View File

@@ -7,7 +7,7 @@ import {
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import { apiAdminDonationDisbursementOfFundsListById } from "@/service/api-admin/api-admin-donation"; import { apiAdminDonationDisbursementOfFundsListById } from "@/service/api-admin/api-admin-donation";
import { dateTimeView } from "@/utils/dateTimeView"; import { dateTimeView } from "@/utils/dateTimeView";
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay"; import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
@@ -67,7 +67,7 @@ export default function AdminDonationDetailDisbursementOfFunds() {
<BaseBox> <BaseBox>
<StackCustom> <StackCustom>
{listData?.map((item, index) => ( {listData?.map((item, index) => (
<GridDetail_4_8 <GridSpan_4_8
key={index} key={index}
label={<TextCustom bold>{item.label}</TextCustom>} label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>} value={<TextCustom>{item.value}</TextCustom>}

View File

@@ -1,17 +1,56 @@
import { import {
BoxButtonOnFooter, BoxButtonOnFooter,
ButtonCustom, ButtonCustom,
StackCustom,
TextCustom,
TextInputCustom, TextInputCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { MainColor } from "@/constants/color-palet";
import { apiAdminMasterDonationCategoryCreate } from "@/service/api-admin/api-master-admin";
import { useRouter } from "expo-router"; import { useRouter } from "expo-router";
import { useState } from "react";
import { Switch } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminDonationCategoryCreate() { export default function AdminDonationCategoryCreate() {
const router = useRouter(); const router = useRouter();
const [loading, setLoading] = useState(false);
const [data, setData] = useState({
name: "",
active: false,
});
const onSubmit = async () => {
try {
setLoading(true);
const response = await apiAdminMasterDonationCategoryCreate({ data });
if (response.success) {
Toast.show({
type: "success",
text2: "Data berhasil disimpan",
});
router.back();
return;
}
Toast.show({
type: "error",
text1: "Gagal menyimpan data",
});
} catch (error) {
console.log("[Error]", error);
} finally {
setLoading(false);
}
};
const buttonSubmit = ( const buttonSubmit = (
<BoxButtonOnFooter> <BoxButtonOnFooter>
<ButtonCustom onPress={() => router.back()}>Simpan</ButtonCustom> <ButtonCustom isLoading={loading} onPress={onSubmit}>
Simpan
</ButtonCustom>
</BoxButtonOnFooter> </BoxButtonOnFooter>
); );
return ( return (
@@ -20,7 +59,23 @@ export default function AdminDonationCategoryCreate() {
headerComponent={<AdminBackButtonAntTitle title="Tambah Kategori" />} headerComponent={<AdminBackButtonAntTitle title="Tambah Kategori" />}
footerComponent={buttonSubmit} footerComponent={buttonSubmit}
> >
<TextInputCustom placeholder="Masukkan Kategori" /> <TextInputCustom
label=""
placeholder="Masukkan Kategori"
value={data.name}
onChangeText={(text) => setData({ ...data, name: text })}
/>
<StackCustom gap={"sm"}>
<TextCustom>Status</TextCustom>
<Switch
style={{
alignSelf: "flex-start",
}}
color={MainColor.yellow}
value={data.active}
onValueChange={(value) => setData({ ...data, active: value })}
/>
</StackCustom>
</ViewWrapper> </ViewWrapper>
</> </>
); );

View File

@@ -1,21 +1,76 @@
import { import {
AlertDefaultSystem,
BoxButtonOnFooter, BoxButtonOnFooter,
ButtonCustom, ButtonCustom,
StackCustom,
TextCustom,
TextInputCustom, TextInputCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { MainColor } from "@/constants/color-palet";
import {
apiAdminMasterDonationCategoryById,
apiAdminMasterDonationCategoryUpdate,
} from "@/service/api-admin/api-master-admin";
import { useLocalSearchParams, useRouter } from "expo-router"; import { useLocalSearchParams, useRouter } from "expo-router";
import { useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { Switch } from "react-native-paper";
export default function AdminDonationCategoryUpdate() { export default function AdminDonationCategoryUpdate() {
const router = useRouter();
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const [value, setValue] = useState(id); const [value, setValue] = useState(id);
const router = useRouter(); const [data, setData] = useState<any>(null);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const fetchData = async () => {
const response = await apiAdminMasterDonationCategoryById({
id: id as any,
});
console.log(JSON.stringify(response.data, null, 2));
setData(response.data);
};
fetchData();
}, [id]);
const handlerSubmit = async () => {
try {
setIsLoading(true);
const response = await apiAdminMasterDonationCategoryUpdate({
id: id as any,
data: data,
});
console.log(JSON.stringify(response.data, null, 2));
router.back();
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
const buttonSubmit = ( const buttonSubmit = (
<BoxButtonOnFooter> <BoxButtonOnFooter>
<ButtonCustom onPress={() => router.back()}>Update</ButtonCustom> <ButtonCustom
disabled={isLoading || data?.name === ""}
isLoading={isLoading}
onPress={() => {
AlertDefaultSystem({
title: "Update Data",
message: "Apakah anda yakin ingin mengupdate data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressLeft: () => {},
onPressRight: () => handlerSubmit(),
});
}}
>
Update
</ButtonCustom>
</BoxButtonOnFooter> </BoxButtonOnFooter>
); );
return ( return (
@@ -25,10 +80,28 @@ export default function AdminDonationCategoryUpdate() {
footerComponent={buttonSubmit} footerComponent={buttonSubmit}
> >
<TextInputCustom <TextInputCustom
label="Nama Kategori"
placeholder="Masukkan Kategori" placeholder="Masukkan Kategori"
value={value as any} value={data?.name}
onChangeText={setValue} onChangeText={(value) => setData({ ...data, name: value })}
/> />
<StackCustom
gap={"sm"}
style={{
alignContent: "flex-start",
}}
>
<TextCustom>Status</TextCustom>
<Switch
style={{
alignSelf: "flex-start",
}}
color={MainColor.yellow}
value={data?.active}
onValueChange={(value) => setData({ ...data, active: value })}
/>
</StackCustom>
</ViewWrapper> </ViewWrapper>
</> </>
); );

View File

@@ -1,27 +1,65 @@
import { import {
ActionIcon, BadgeCustom,
BaseBox, CenterCustom,
CenterCustom, ClickableCustom,
Spacing, Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom,
ViewWrapper, ViewWrapper
} from "@/components"; } from "@/components";
import { IconEdit } from "@/components/_Icon";
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus"; import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage"; import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage"; import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { GridView_3_3_6 } from "@/components/_ShareComponent/GridView_3_3_6"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import { MainColor } from "@/constants/color-palet"; import { apiAdminMasterDonationCategory } from "@/service/api-admin/api-master-admin";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; import { colorActivationForBadge } from "@/utils/colorActivationForBadge";
import { View } from "react-native"; import { router, useFocusEffect } from "expo-router";
import { Divider, Switch } from "react-native-paper"; import { useCallback, useState } from "react";
import { router } from "expo-router"; import { RefreshControl, View } from "react-native";
import { Divider } from "react-native-paper";
export default function AdminDonationCategory() { export default function AdminDonationCategory() {
const [listData, setListData] = useState<any[]>([]);
const [refreshing, setRefreshing] = useState(false);
const [loading, setLoading] = useState(false);
useFocusEffect(
useCallback(() => {
fetchMaster();
}, [])
);
const fetchMaster = async () => {
try {
setLoading(true);
const response = await apiAdminMasterDonationCategory();
if (response.success) {
console.log(JSON.stringify(response.data, null, 2));
setListData(response.data);
} else {
setListData([]);
}
} catch (error) {
console.log("[Error]", error);
} finally {
setLoading(false);
}
};
const onRefresh = async () => {
setRefreshing(true);
await fetchMaster();
setRefreshing(false);
};
return ( return (
<> <>
<ViewWrapper headerComponent={<AdminTitlePage title="Donasi" />}> <ViewWrapper
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
headerComponent={<AdminTitlePage title="Donasi" />}
>
<AdminComp_BoxTitle <AdminComp_BoxTitle
title="Kategori" title="Kategori"
rightComponent={ rightComponent={
@@ -33,81 +71,65 @@ export default function AdminDonationCategory() {
} }
/> />
<BaseBox> <View>
<GridView_3_3_6 <GridSpan_4_8
component1={ label={<TextCustom bold>Status</TextCustom>}
<TextCustom bold align="center"> value={<TextCustom bold>Kategori</TextCustom>}
Aksi
</TextCustom>
}
component2={<TextCustom bold>Status</TextCustom>}
component3={<TextCustom bold>Kategori</TextCustom>}
/> />
{/* <Grid>
<Grid.Col style={{ paddingLeft: 10 }} span={4}>
<TextCustom bold>Status</TextCustom>
</Grid.Col>
<Grid.Col span={8}>
<TextCustom bold>Kategori</TextCustom>
</Grid.Col>
</Grid> */}
<Divider /> <Divider />
<Spacing /> <Spacing />
<StackCustom> <StackCustom>
{listData.map((item, index) => ( {listData.map((item, index) => (
<View key={index}> <ClickableCustom
<GridView_3_3_6 onPress={() => {
component1={ router.push(`/admin/donation/category-update?id=${item.id}`);
}}
key={index}
>
<GridSpan_4_8
label={
<CenterCustom> <CenterCustom>
<ActionIcon <BadgeCustom
icon={ color={colorActivationForBadge({
<IconEdit size={ICON_SIZE_BUTTON} color="black" /> status: item.active,
} })}
onPress={() => { >
router.push(`/admin/donation/category-update?id=${index}`); {item.active ? "Aktif" : "Tidak Aktif"}
}} </BadgeCustom>
/>
</CenterCustom> </CenterCustom>
} }
component2={ value={<TextCustom>{item.name}</TextCustom>}
<Switch
value={true}
onValueChange={(item) => {
console.log(item);
}}
color={MainColor.yellow}
/>
}
component3={<TextCustom bold>{item.label}</TextCustom>}
/> />
<Spacing height={10} /> {/* <Grid containerStyle={{ paddingBottom: 10 }}>
<Grid.Col span={4} style={{ paddingLeft: 10 }}>
<CenterCustom>
<BadgeCustom
color={item.active ? MainColor.green : MainColor.red}
>
{item.active ? "Aktif" : "Tidak Aktif"}
</BadgeCustom>
</CenterCustom>
</Grid.Col>
<Grid.Col span={8}>
<TextCustom bold>{item.name}</TextCustom>
</Grid.Col>
</Grid> */}
<Divider /> <Divider />
</View> </ClickableCustom>
))} ))}
</StackCustom> </StackCustom>
</BaseBox> </View>
</ViewWrapper> </ViewWrapper>
</> </>
); );
} }
const listData = [
{
label: "Kegiatan Sosial",
value: "kegiatan_sosial",
},
{
label: "Pendidikan",
value: "pendidikan",
},
{
label: "Kesehatan",
value: "kesehatan",
},
{
label: "Kebudayaan",
value: "kebudayaan",
},
{
label: "Bencana Alami",
value: "bencana_alami",
},
{
label: "Lainnya",
value: "lainnya",
},
];

View File

@@ -1,22 +1,22 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { import {
ActionIcon, ActionIcon,
AlertDefaultSystem, AlertDefaultSystem,
BadgeCustom, BadgeCustom,
BaseBox, BaseBox,
DrawerCustom, DrawerCustom,
LoaderCustom, LoaderCustom,
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
Spacing, Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import { IconDot, IconList } from "@/components/_Icon/IconComponent"; import { IconDot, IconList } from "@/components/_Icon/IconComponent";
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 AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview"; import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import ReportBox from "@/components/Box/ReportBox"; import ReportBox from "@/components/Box/ReportBox";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
@@ -39,6 +39,11 @@ export default function AdminEventDetail() {
const [data, setData] = React.useState<any | null>(null); const [data, setData] = React.useState<any | null>(null);
const [loadData, setLoadData] = React.useState(false); const [loadData, setLoadData] = React.useState(false);
const deepLinkURL = `${DEEP_LINK_URL}/event/${id}/confirmation?userId=${user?.id}`; const deepLinkURL = `${DEEP_LINK_URL}/event/${id}/confirmation?userId=${user?.id}`;
const deepLinkURLDEV = `${DEEP_LINK_URL}/--/event/${id}/confirmation?userId=${user?.id}`;
const isDevLink = process.env.NODE_ENV === "development" ? deepLinkURLDEV : deepLinkURL;
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
onLoadData(); onLoadData();
@@ -156,7 +161,7 @@ export default function AdminEventDetail() {
<BaseBox> <BaseBox>
<StackCustom> <StackCustom>
{listData.map((item, i) => ( {listData.map((item, i) => (
<GridDetail_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>}
@@ -181,7 +186,7 @@ export default function AdminEventDetail() {
<LoaderCustom /> <LoaderCustom />
) : ( ) : (
<QRCode <QRCode
value={deepLinkURL} value={isDevLink}
size={200} size={200}
// logo={require("@/assets/images/logo-hipmi.png")} // logo={require("@/assets/images/logo-hipmi.png")}
// logoSize={70} // logoSize={70}
@@ -191,7 +196,7 @@ export default function AdminEventDetail() {
/> />
)} )}
<TextCustom align="center">{deepLinkURL}</TextCustom> <TextCustom align="center">{isDevLink}</TextCustom>
</StackCustom> </StackCustom>
</BaseBox> </BaseBox>
)} )}

View File

@@ -10,14 +10,17 @@ import {
} from "@/components"; } from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { apiAdminEventListOfParticipants } from "@/service/api-admin/api-admin-event"; import { apiAdminEventListOfParticipants } from "@/service/api-admin/api-admin-event";
import dayjs, { Dayjs } from "dayjs";
import { useFocusEffect, useLocalSearchParams } from "expo-router"; import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { View } from "moti";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
export default function AdminEventListOfParticipants() { export default function AdminEventListOfParticipants() {
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
const [listData, setListData] = useState<any[] | null>(null); const [listData, setListData] = useState<any[] | null>(null);
const [loadData, setLoadData] = useState(false); const [loadData, setLoadData] = useState(false);
const [startDate, setStartDate] = useState<Dayjs | undefined>();
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
@@ -32,8 +35,11 @@ export default function AdminEventListOfParticipants() {
id: id as string, id: id as string,
}); });
console.log("[DATA]", JSON.stringify(response, null, 2));
if (response.success) { if (response.success) {
setListData(response.data); setListData(response.data);
setStartDate(dayjs(response.data.Event.tanggal));
} }
} catch (error) { } catch (error) {
console.log("[ERROR]", error); console.log("[ERROR]", error);
@@ -42,7 +48,6 @@ export default function AdminEventListOfParticipants() {
} }
}; };
return ( return (
<> <>
<ViewWrapper <ViewWrapper
@@ -60,17 +65,35 @@ export default function AdminEventListOfParticipants() {
<Grid> <Grid>
<Grid.Col span={6}> <Grid.Col span={6}>
<StackCustom gap={"sm"}> <StackCustom gap={"sm"}>
<TextCustom bold truncate>{item?.User?.username}</TextCustom> <TextCustom bold truncate>
{item?.User?.username}
</TextCustom>
<TextCustom>+{item?.User?.nomor}</TextCustom> <TextCustom>+{item?.User?.nomor}</TextCustom>
</StackCustom> </StackCustom>
</Grid.Col> </Grid.Col>
<Grid.Col span={6} style={{ justifyContent: "center" }}> <Grid.Col span={6} style={{ justifyContent: "center" }}>
<BadgeCustom {startDate &&
style={{ alignSelf: "flex-end" }} startDate.subtract(1, "hour").diff(dayjs()) < 0 ? (
color={item?.isPresent ? "green" : "red"} <BadgeCustom
> style={{ alignSelf: "flex-end" }}
{item?.isPresent ? "Hadir" : "Tidak Hadir"} color={item?.isPresent ? "green" : "red"}
</BadgeCustom> >
{item?.isPresent ? "Hadir" : "Tidak Hadir"}
</BadgeCustom>
) : (
<View
style={{
justifyContent: "flex-end",
}}
>
<BadgeCustom
style={{ alignSelf: "flex-end" }}
color="gray"
>
-
</BadgeCustom>
</View>
)}
</Grid.Col> </Grid.Col>
</Grid> </Grid>
</BaseBox> </BaseBox>

View File

@@ -1,11 +1,12 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { import {
ActionIcon, ActionIcon,
ClickableCustom,
LoaderCustom, LoaderCustom,
SearchInput, SearchInput,
StackCustom, StackCustom,
TextCustom, TextCustom,
ViewWrapper ViewWrapper,
} from "@/components"; } from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage"; import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle"; import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
@@ -13,6 +14,7 @@ import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage"; import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminEvent } from "@/service/api-admin/api-admin-event"; import { apiAdminEvent } from "@/service/api-admin/api-admin-event";
import { dateTimeView } from "@/utils/dateTimeView";
import { Octicons } from "@expo/vector-icons"; import { Octicons } from "@expo/vector-icons";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
@@ -74,8 +76,8 @@ export default function AdminEventStatus() {
<StackCustom gap={"sm"}> <StackCustom gap={"sm"}>
<AdminTitleTable <AdminTitleTable
title1="Aksi" title1="Username"
title2="Username" title2="Tanggal"
title3="Judul Event" title3="Judul Event"
/> />
<Divider /> <Divider />
@@ -83,36 +85,47 @@ export default function AdminEventStatus() {
{loadData ? ( {loadData ? (
<LoaderCustom /> <LoaderCustom />
) : _.isEmpty(listData) ? ( ) : _.isEmpty(listData) ? (
<TextCustom align="center" size="small" color="gray">Belum ada data</TextCustom> <TextCustom align="center" size="small" color="gray">
Belum ada data
</TextCustom>
) : ( ) : (
listData?.map((item, index) => ( listData?.map((item, index) => (
<AdminTableValue <ClickableCustom
key={index} key={index}
value1={ onPress={() => {
<ActionIcon router.push(`/admin/event/${item.id}/${status}`);
icon={ }}
<Octicons >
name="eye" <AdminTableValue
size={ICON_SIZE_BUTTON} key={index}
color="black" value1={
/> <TextCustom truncate={1}>
} {item?.Author?.username || "-"}
onPress={() => { </TextCustom>
router.push(`/admin/event/${item.id}/${status}`); // <ActionIcon
}} // icon={
/> // <Octicons
} // name="eye"
value2={ // size={ICON_SIZE_BUTTON}
<TextCustom truncate={1}> // color="black"
{item?.Author?.username || "-"} // />
</TextCustom> // }
} // onPress={() => {
value3={ // router.push(`/admin/event/${item.id}/${status}`);
<TextCustom align="center" truncate={2}> // }}
{item?.title || "-"} // />
</TextCustom> }
} value2={
/> <TextCustom truncate={1}>
{dateTimeView({ date: item?.tanggal })}
</TextCustom>
}
value3={
<TextCustom truncate={2}>{item?.title || "-"}</TextCustom>
}
/>
<Divider/>
</ClickableCustom>
)) ))
)} )}
</StackCustom> </StackCustom>

View File

@@ -11,7 +11,7 @@ import {
} from "@/components"; } from "@/components";
import { IconDot } from "@/components/_Icon/IconComponent"; import { IconDot } from "@/components/_Icon/IconComponent";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_XLARGE } from "@/constants/constans-value"; import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
import { apiAdminForumPostingById } from "@/service/api-admin/api-admin-forum"; import { apiAdminForumPostingById } from "@/service/api-admin/api-admin-forum";
@@ -103,7 +103,7 @@ export default function AdminForumDetailPosting() {
<BaseBox> <BaseBox>
<StackCustom gap={"sm"}> <StackCustom gap={"sm"}>
{listDataAction.map((item, i) => ( {listDataAction.map((item, i) => (
<GridDetail_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>}

View File

@@ -16,7 +16,7 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage"; import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle"; import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue"; import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
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";
import { import {
@@ -95,11 +95,11 @@ export default function AdminForumReportComment() {
> >
<BaseBox> <BaseBox>
<StackCustom gap={"sm"}> <StackCustom gap={"sm"}>
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Username</TextCustom>} label={<TextCustom bold>Username</TextCustom>}
value={<TextCustom>{data?.Author?.username || "-"}</TextCustom>} value={<TextCustom>{data?.Author?.username || "-"}</TextCustom>}
/> />
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Komentar</TextCustom>} label={<TextCustom bold>Komentar</TextCustom>}
value={<TextCustom>{data?.komentar || "-"}</TextCustom>} value={<TextCustom>{data?.komentar || "-"}</TextCustom>}
/> />
@@ -208,20 +208,20 @@ export default function AdminForumReportComment() {
height={"auto"} height={"auto"}
> >
<StackCustom> <StackCustom>
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Pelapor</TextCustom>} label={<TextCustom bold>Pelapor</TextCustom>}
value={<TextCustom>{selectedReport?.username || "-"}</TextCustom>} value={<TextCustom>{selectedReport?.username || "-"}</TextCustom>}
/> />
{selectedReport?.kategori && ( {selectedReport?.kategori && (
<> <>
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Kategori Report</TextCustom>} label={<TextCustom bold>Kategori Report</TextCustom>}
value={ value={
<TextCustom>{selectedReport?.kategori || "-"}</TextCustom> <TextCustom>{selectedReport?.kategori || "-"}</TextCustom>
} }
/> />
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Keterangan</TextCustom>} label={<TextCustom bold>Keterangan</TextCustom>}
value={ value={
<TextCustom>{selectedReport?.keterangan || "-"}</TextCustom> <TextCustom>{selectedReport?.keterangan || "-"}</TextCustom>
@@ -231,7 +231,7 @@ export default function AdminForumReportComment() {
)} )}
{selectedReport?.deskripsi && ( {selectedReport?.deskripsi && (
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Deskripsi</TextCustom>} label={<TextCustom bold>Deskripsi</TextCustom>}
value={ value={
<TextCustom>{selectedReport?.deskripsi || "-"}</TextCustom> <TextCustom>{selectedReport?.deskripsi || "-"}</TextCustom>

View File

@@ -17,7 +17,7 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage"; import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle"; import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue"; import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
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";
import { import {
@@ -95,12 +95,12 @@ export default function AdminForumReportPosting() {
> >
<BaseBox> <BaseBox>
<StackCustom gap={"sm"}> <StackCustom gap={"sm"}>
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Username</TextCustom>} label={<TextCustom bold>Username</TextCustom>}
value={<TextCustom>{data?.Author?.username || "-"}</TextCustom>} value={<TextCustom>{data?.Author?.username || "-"}</TextCustom>}
/> />
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Status</TextCustom>} label={<TextCustom bold>Status</TextCustom>}
value={ value={
data && data?.ForumMaster_StatusPosting?.status ? ( data && data?.ForumMaster_StatusPosting?.status ? (
@@ -121,7 +121,7 @@ export default function AdminForumReportPosting() {
} }
/> />
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Postingan</TextCustom>} label={<TextCustom bold>Postingan</TextCustom>}
value={<TextCustom>{data?.diskusi || "-"}</TextCustom>} value={<TextCustom>{data?.diskusi || "-"}</TextCustom>}
/> />
@@ -229,20 +229,20 @@ export default function AdminForumReportPosting() {
height={"auto"} height={"auto"}
> >
<StackCustom> <StackCustom>
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Pelapor</TextCustom>} label={<TextCustom bold>Pelapor</TextCustom>}
value={<TextCustom>{selectedReport?.username || "-"}</TextCustom>} value={<TextCustom>{selectedReport?.username || "-"}</TextCustom>}
/> />
{selectedReport?.kategori && ( {selectedReport?.kategori && (
<> <>
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Kategori Report</TextCustom>} label={<TextCustom bold>Kategori Report</TextCustom>}
value={ value={
<TextCustom>{selectedReport?.kategori || "-"}</TextCustom> <TextCustom>{selectedReport?.kategori || "-"}</TextCustom>
} }
/> />
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Keterangan</TextCustom>} label={<TextCustom bold>Keterangan</TextCustom>}
value={ value={
<TextCustom>{selectedReport?.keterangan || "-"}</TextCustom> <TextCustom>{selectedReport?.keterangan || "-"}</TextCustom>
@@ -252,7 +252,7 @@ export default function AdminForumReportPosting() {
)} )}
{selectedReport?.deskripsi && ( {selectedReport?.deskripsi && (
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Deskripsi</TextCustom>} label={<TextCustom bold>Deskripsi</TextCustom>}
value={ value={
<TextCustom>{selectedReport?.deskripsi || "-"}</TextCustom> <TextCustom>{selectedReport?.deskripsi || "-"}</TextCustom>

View File

@@ -19,7 +19,7 @@ import { IconDot, IconList } from "@/components/_Icon/IconComponent";
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 AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview"; import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
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";
@@ -183,7 +183,7 @@ export default function AdminInvestmentDetail() {
/> />
<Spacing /> <Spacing />
<StackCustom gap={"xs"}> <StackCustom gap={"xs"}>
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Sisa Saham</TextCustom>} label={<TextCustom bold>Sisa Saham</TextCustom>}
value={ value={
<TextCustom> <TextCustom>
@@ -191,7 +191,7 @@ export default function AdminInvestmentDetail() {
</TextCustom> </TextCustom>
} }
/> />
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>Validasi Transaksi</TextCustom>} label={<TextCustom bold>Validasi Transaksi</TextCustom>}
value={ value={
<TextCustom> <TextCustom>
@@ -207,7 +207,7 @@ export default function AdminInvestmentDetail() {
<StackCustom> <StackCustom>
<DummyLandscapeImage imageId={data?.imageId} /> <DummyLandscapeImage imageId={data?.imageId} />
{listData.map((item, i) => ( {listData.map((item, i) => (
<GridDetail_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>}
@@ -218,7 +218,7 @@ export default function AdminInvestmentDetail() {
<BaseBox> <BaseBox>
<StackCustom> <StackCustom>
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>File Prospektus</TextCustom>} label={<TextCustom bold>File Prospektus</TextCustom>}
value={ value={
<ButtonCustom <ButtonCustom
@@ -238,7 +238,7 @@ export default function AdminInvestmentDetail() {
</ButtonCustom> </ButtonCustom>
} }
/> />
<GridDetail_4_8 <GridSpan_4_8
label={<TextCustom bold>File Dokumen</TextCustom>} label={<TextCustom bold>File Dokumen</TextCustom>}
value={ value={
<StackCustom> <StackCustom>

View File

@@ -10,7 +10,7 @@ import {
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_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 { import {
@@ -225,7 +225,7 @@ export default function AdminInvestmentTransactionDetail() {
<BaseBox> <BaseBox>
<StackCustom> <StackCustom>
{listData.map((item, index) => ( {listData.map((item, index) => (
<GridDetail_4_8 <GridSpan_4_8
key={index} key={index}
label={<TextCustom bold>{item.label}</TextCustom>} label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>} value={<TextCustom>{item.value}</TextCustom>}

View File

@@ -1,19 +1,19 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { import {
AlertDefaultSystem, AlertDefaultSystem,
BadgeCustom, BadgeCustom,
BaseBox, BaseBox,
CircleContainer, CircleContainer,
Grid, Grid,
Spacing, Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} 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 AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview"; import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8"; import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
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 funUpdateStatusVoting from "@/screens/Admin/Voting/funUpdateStatus"; import funUpdateStatusVoting from "@/screens/Admin/Voting/funUpdateStatus";
@@ -169,7 +169,7 @@ export default function AdminVotingDetail() {
<BaseBox> <BaseBox>
<StackCustom> <StackCustom>
{listData.map((item, i) => ( {listData.map((item, i) => (
<GridDetail_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>}

View File

@@ -0,0 +1,16 @@
import { Alert } from "react-native";
export default function AlertWarning({
title = "Peringatan Bagi Pengguna !",
description = "Konten yang Anda masukkan mengandung kata-kata yang tidak sesuai dengan pedoman komunitas kami. Mohon gunakan bahasa yang sopan dan menghargai sesama pengguna. Jika kata tersebut sebenarnya lumrah, mohon maaf—kemungkinan sistem kami belum mengenalnya sebagai wajar.",
}: {
title?: string
description?: string;
}) {
return Alert.alert(title, description, [
{
text: "Tutup",
onPress: () => {},
},
]);
}

View File

@@ -7,7 +7,7 @@ import DateTimePicker, {
} from "@react-native-community/datetimepicker"; } from "@react-native-community/datetimepicker";
import dayjs from "dayjs"; import dayjs from "dayjs";
import React, { useState } from "react"; import React, { useState } from "react";
import { StyleProp, Text, View, ViewStyle } from "react-native"; import { Button, StyleProp, Text, View, ViewStyle } from "react-native";
import ClickableCustom from "../Clickable/ClickableCustom"; import ClickableCustom from "../Clickable/ClickableCustom";
import TextCustom from "../Text/TextCustom"; import TextCustom from "../Text/TextCustom";
@@ -129,24 +129,64 @@ const DateTimeInput_IOS: React.FC<DateTimeInputProps> = ({
borderWidth: 1, borderWidth: 1,
}} }}
> >
<View style={{ alignItems: "flex-end" }}> {/* <View style={{ alignItems: "flex-start" }}>
<Ionicons <Ionicons
name="close" name="close"
size={20} size={20}
color="black" color="black"
onPress={() => setShow(false)} onPress={() => {
setShow(false);
setSelectedDate(undefined);
}}
/> />
</View> </View> */}
<DateTimePicker <DateTimePicker
value={selectedDate || new Date()} value={selectedDate || new Date()}
mode={"datetime"} mode={"datetime"}
display="inline" display="spinner"
onChange={handleConfirm} onChange={handleConfirm}
minimumDate={minimumDate} minimumDate={minimumDate}
maximumDate={maximumDate} maximumDate={maximumDate}
themeVariant="light" themeVariant="light"
/> />
<View style={{ flexDirection: "row", gap: 10 }}>
<ClickableCustom
onPress={() => {
setShow(false)
setSelectedDate(undefined)
}}
style={{
alignItems: "center",
justifyContent: "center",
padding: 12,
borderRadius: 10,
backgroundColor: MainColor.placeholder,
marginTop: 10,
width: "48%",
}}
>
<TextCustom color="black">Batal</TextCustom>
</ClickableCustom>
<ClickableCustom
onPress={() => {
setShow(false)
onChange(selectedDate as any)
}}
style={{
alignItems: "center",
justifyContent: "center",
padding: 12,
borderRadius: 10,
backgroundColor: MainColor.darkblue,
marginTop: 10,
width: "48%",
}}
>
<TextCustom>OK</TextCustom>
</ClickableCustom>
</View>
</View> </View>
</> </>
)} )}

View File

@@ -15,7 +15,7 @@ export default function AdminButtonReject({
<> <>
<ButtonCustom <ButtonCustom
iconLeft={<IconReject size={16} />} iconLeft={<IconReject size={16} />}
backgroundColor={MainColor.red} backgroundColor={MainColor.orange}
textColor="white" textColor="white"
onPress={onReject} onPress={onReject}
isLoading={isLoading} isLoading={isLoading}

View File

@@ -1,17 +1,23 @@
import Grid from "@/components/Grid/GridCustom"; import Grid from "@/components/Grid/GridCustom";
import React from "react"; import React from "react";
import { View } from "react-native"; import { StyleProp, View, ViewStyle } from "react-native";
import { Divider } from "react-native-paper"; import { Divider } from "react-native-paper";
export default function AdminTableValue({ export default function AdminTableValue({
value1, value1,
value2, value2,
value3, value3,
style1,
style2,
style3,
bottomLine = false, bottomLine = false,
}: { }: {
value1: React.ReactNode; value1: React.ReactNode;
value2: React.ReactNode; value2: React.ReactNode;
value3: React.ReactNode; value3: React.ReactNode;
style1?: ViewStyle;
style2?: ViewStyle;
style3?: ViewStyle;
bottomLine?: boolean; bottomLine?: boolean;
}) { }) {
return ( return (
@@ -25,6 +31,7 @@ export default function AdminTableValue({
justifyContent: "center", justifyContent: "center",
paddingLeft: 10, paddingLeft: 10,
paddingRight: 10, paddingRight: 10,
...style1,
}} }}
> >
{value1} {value1}
@@ -36,6 +43,7 @@ export default function AdminTableValue({
justifyContent: "center", justifyContent: "center",
paddingLeft: 10, paddingLeft: 10,
paddingRight: 10, paddingRight: 10,
...style2,
}} }}
> >
{value2} {value2}
@@ -44,9 +52,10 @@ export default function AdminTableValue({
span={6} span={6}
style={{ style={{
justifyContent: "center", justifyContent: "center",
alignItems: "center", alignItems: "flex-start",
paddingLeft: 10, paddingLeft: 10,
paddingRight: 10, paddingRight: 10,
...style3,
}} }}
> >
{value3} {value3}

View File

@@ -1,20 +0,0 @@
import { Grid } from "@/components";
export const GridDetail_4_8 = ({
label,
value,
}: {
label: React.ReactNode;
value: React.ReactNode;
}) => {
return (
<Grid>
<Grid.Col span={4} style={{ justifyContent: "center", paddingRight: 10 }}>
{label}
</Grid.Col>
<Grid.Col span={8} style={{ justifyContent: "center" }}>
{value}
</Grid.Col>
</Grid>
);
};

View File

@@ -0,0 +1,27 @@
import { Grid } from "@/components";
export const GridSpan_4_8 = ({
label,
value,
}: {
label: React.ReactNode;
value: React.ReactNode;
}) => {
return (
<Grid>
<Grid.Col
span={4}
style={{
justifyContent: "flex-start",
paddingRight: 8,
paddingLeft: 8,
}}
>
{label}
</Grid.Col>
<Grid.Col span={8} style={{ justifyContent: "center", paddingRight: 8 }}>
{value}
</Grid.Col>
</Grid>
);
};

View File

@@ -72,10 +72,13 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const loginWithNomor = async (nomor: string) => { const loginWithNomor = async (nomor: string) => {
setIsLoading(true); setIsLoading(true);
try { try {
console.log("[Masuk provider]", nomor);
const response = await apiLogin({ nomor: nomor }); const response = await apiLogin({ nomor: nomor });
console.log("[RESPONSE AUTH]", JSON.stringify(response)); console.log("[RESPONSE AUTH]", JSON.stringify(response));
if (response.success) { if (response.success) {
console.log("[Keluar provider]", nomor);
Toast.show({ Toast.show({
type: "success", type: "success",
text1: "Sukses", text1: "Sukses",
@@ -83,10 +86,15 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
}); });
await AsyncStorage.setItem("kode_otp", response.kodeId); await AsyncStorage.setItem("kode_otp", response.kodeId);
router.replace(`/verification?nomor=${nomor}`); router.push(`/verification?nomor=${nomor}`);
return; return;
} else { } else {
router.replace(`/register?nomor=${nomor}`); router.push(`/register?nomor=${nomor}`);
Toast.show({
type: "info",
text1: "Info",
text2: "Silahkan mendaftar",
});
return; return;
} }
} catch (error: any) { } catch (error: any) {

View File

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

View File

@@ -1,3 +1,4 @@
import { NewWrapper } from "@/components";
import ButtonCustom from "@/components/Button/ButtonCustom"; import ButtonCustom from "@/components/Button/ButtonCustom";
import Spacing from "@/components/_ShareComponent/Spacing"; import Spacing from "@/components/_ShareComponent/Spacing";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper"; import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
@@ -5,9 +6,11 @@ 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 } from "@/service/api-config";
import { GStyles } from "@/styles/global-styles"; import { GStyles } from "@/styles/global-styles";
import { Redirect, router } from "expo-router"; import versionBadge from "@/utils/viersionBadge";
import VersionBadge from "@/utils/viersionBadge";
import { Redirect } from "expo-router";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Text, View } from "react-native"; import { 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";
@@ -16,6 +19,7 @@ export default function LoginView() {
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 { loginWithNomor, token, isAdmin, isUserActive } = useAuth(); const { loginWithNomor, token, isAdmin, isUserActive } = useAuth();
@@ -25,7 +29,18 @@ export default function LoginView() {
async function onLoadVersion() { async function onLoadVersion() {
const res = await apiVersion(); const res = await apiVersion();
setVersion(res.data);
if (res.success) {
setVersion(versionBadge());
}
}
async function handleRefresh() {
setRefreshing(true);
await onLoadVersion();
setInputValue("");
setLoading(false);
setRefreshing(false);
} }
function handleInputValue(phoneNumber: string) { function handleInputValue(phoneNumber: string) {
@@ -65,8 +80,6 @@ export default function LoginView() {
const isValid = await validateData(); const isValid = await validateData();
if (!isValid) return; if (!isValid) return;
// const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
// const fixNumber = inputValue.replace(/\s+/g, "");
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || ""; const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
let fixNumber = inputValue.replace(/\s+/g, "").replace(/^0+/, ""); let fixNumber = inputValue.replace(/\s+/g, "").replace(/^0+/, "");
@@ -74,9 +87,7 @@ export default function LoginView() {
try { try {
setLoading(true); setLoading(true);
const response = await loginWithNomor(realNumber); await loginWithNomor(realNumber);
console.log("[RESPONSE UI]", response);
} catch (error) { } catch (error) {
console.log("Error login", error); console.log("Error login", error);
Toast.show({ Toast.show({
@@ -130,7 +141,12 @@ export default function LoginView() {
} }
return ( return (
<ViewWrapper withBackground> <NewWrapper
withBackground
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
}
>
<View style={GStyles.authContainer}> <View style={GStyles.authContainer}>
<View> <View>
<View style={GStyles.authContainerTitle}> <View style={GStyles.authContainerTitle}>
@@ -174,6 +190,6 @@ export default function LoginView() {
Coba Coba
</ButtonCustom> */} </ButtonCustom> */}
</View> </View>
</ViewWrapper> </NewWrapper>
); );
} }

View File

@@ -17,6 +17,8 @@ import Toast from "react-native-toast-message";
export default function VerificationView() { export default function VerificationView() {
const { nomor } = useLocalSearchParams<{ nomor: string }>(); const { nomor } = useLocalSearchParams<{ nomor: string }>();
console.log("[NOMOR]", nomor);
const [inputOtp, setInputOtp] = useState<string>(""); const [inputOtp, setInputOtp] = useState<string>("");
const [userNumber, setUserNumber] = useState<string>(""); const [userNumber, setUserNumber] = useState<string>("");
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
@@ -52,7 +54,7 @@ export default function VerificationView() {
try { try {
const response = await apiCheckCodeOtp({ kodeId }); const response = await apiCheckCodeOtp({ kodeId });
console.log( console.log(
"Response check code otp >>", "[OTP] >>",
JSON.stringify(response.otp, null, 2) JSON.stringify(response.otp, null, 2)
); );
// Kita tidak perlu simpan codeOtp di state karena verifikasi dilakukan di backend // Kita tidak perlu simpan codeOtp di state karena verifikasi dilakukan di backend

View File

@@ -167,3 +167,56 @@ export async function apiAdminMasterTypeOfEventUpdate({
} }
// ================== END EVENT ================== // // ================== END EVENT ================== //
// ================== START DONATION ================== //
export async function apiAdminMasterDonationCategory() {
try {
const response = await apiConfig.get(`/mobile/admin/master/donation`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminMasterDonationCategoryById({ id }: { id: string }) {
try {
const response = await apiConfig.get(`/mobile/admin/master/donation/${id}`);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminMasterDonationCategoryUpdate({
id,
data,
}: {
id: string;
data: any;
}) {
try {
const response = await apiConfig.put(
`/mobile/admin/master/donation/${id}`,
{
data: data,
}
);
return response.data;
} catch (error) {
throw error;
}
}
export async function apiAdminMasterDonationCategoryCreate({ data }: { data: any }) {
try {
const response = await apiConfig.post(`/mobile/admin/master/donation`, {
data: data,
});
return response.data;
} catch (error) {
throw error;
}
}
// ================== END DONATION ================== //

View File

@@ -14,6 +14,7 @@ apiConfig.interceptors.request.use(
async (config) => { async (config) => {
console.log("API_BASE_URL >>", API_BASE_URL); console.log("API_BASE_URL >>", API_BASE_URL);
const token = await AsyncStorage.getItem("authToken"); const token = await AsyncStorage.getItem("authToken");
// console.log("[TOKEN] >>", token);
if (token) { if (token) {
// config.timeout = 10000; // config.timeout = 10000;
config.headers["Content-Type"] = "application/json"; config.headers["Content-Type"] = "application/json";
@@ -33,7 +34,7 @@ export async function apiVersion() {
} }
export async function apiLogin({ nomor }: { nomor: string }) { export async function apiLogin({ nomor }: { nomor: string }) {
const response = await apiConfig.post("/mobile/auth/login", { const response = await apiConfig.post("/auth/mobile-login", {
nomor: nomor, nomor: nomor,
}); });
return response.data;; return response.data;;
@@ -45,7 +46,7 @@ export async function apiCheckCodeOtp({ kodeId }: { kodeId: string }) {
} }
export async function apiValidationCode({ nomor }: { nomor: string }) { export async function apiValidationCode({ nomor }: { nomor: string }) {
const response = await apiConfig.post(`/auth/validasi`, { const response = await apiConfig.post(`/auth/mobile-validasi`, {
nomor: nomor, nomor: nomor,
}); });
return response.data; return response.data;
@@ -56,7 +57,7 @@ export async function apiRegister({
}: { }: {
data: { nomor: string; username: string; termsOfServiceAccepted: boolean }; data: { nomor: string; username: string; termsOfServiceAccepted: boolean };
}) { }) {
const response = await apiConfig.post(`/mobile/auth/register`, { const response = await apiConfig.post(`/auth/mobile-register`, {
data: data, data: data,
}); });
return response.data; return response.data;

108
utils/badWordsIndonesia.ts Normal file
View File

@@ -0,0 +1,108 @@
// badWordsIndonesia.js (Versi Lengkap)
const badWordsIndonesia = [
// 🐶 Kata Kasar & Vulgar
'anjing', 'babi', 'bangsat', 'bodoh', 'goblok', 'idiot', 'jancok', 'jembut', 'kampret',
'kontol', 'memek', 'ngentot', 'peler', 'puki', 'sialan', 'tai', 'tolol', 'wibu',
'anjingg', 'babbii', 'bangsaat', 'gobllokk', 'jancokk', 'kontoll', 'memekk', 'ngentott',
'pelerr', 'puuki', 'sialann', 'taii', 'tololl', 'wibuu',
// 🔥 Kata Sindiran & Penghinaan
'bego', 'dungu', 'edan', 'gila', 'goblog', 'kampang', 'kampret', 'keparat', 'lonte',
'main mata', 'monyet', 'najis', 'ngeyel', 'ngibul', 'ngomong seenaknya', 'ngurangin',
'ngutang', 'ngurusin urusan orang', 'pemalas', 'pengecut', 'penipu', 'sinting',
'begoo', 'dunguu', 'goblogg', 'kampangg', 'keparatt', 'lontee', 'monyyet', 'najiss',
'ngeyell', 'ngibull', 'ngomongg seenaknya', 'nguranginn', 'ngutangg', 'pemalass',
'pengecutt', 'penipuu', 'sintting',
// ⚖️ Kata SARA & Diskriminasi
'cina', 'kafir', 'kampungan', 'kacung', 'mampus', 'menghina', 'racist', 'sara',
'setan', 'syiah', 'waria', 'wong jowo', 'wong sunda', 'wong madura',
'chinna', 'kafiir', 'kampungann', 'kacungg', 'mampuss', 'menghinna', 'racisst',
'saraa', 'setann', 'syiahh', 'wariia', 'wong jowoo', 'wong sundaa', 'wong maduraa',
// 💸 Kata Spam / Promosi Ilegal
'judi', 'togel', 'slot', 'casino', 'poker', 'qq', 'bandar', 'agen', 'link', 'wa',
'whatsapp', 'telepon', 'nomor', 'hp', 'sms', 'grup', 'join', 'daftar', 'bonus',
'deposit', 'withdraw', 'uang', 'duit', 'rp', 'ratusan', 'juta', 'milyar',
'judii', 'togell', 'slotss', 'casinoo', 'pokerr', 'qqq', 'bandarr', 'agenn', 'linkk',
'waa', 'whatsappp', 'teleponn', 'nomorr', 'hpp', 'smss', 'grupp', 'jooin', 'daftarr',
'bonuss', 'depositt', 'withdraww', 'uangs', 'duitt', 'rpp', 'ratusann', 'jutaa', 'milyarr',
// 🧩 Variasi Penulisan (Bypass Filter)
'a*njing', 'b*b*i', 'b*ngsat', 'g*blok', 'k*nt*l', 'm*m*k', 'n*g*nt*t', 'p*l*r',
't*i', 't*l*l', 'j*n*c*k', 'j*m*b*t', 'k*m*p*r*t', 's*i*l*a*n', 'w*b*u',
'a.n.j.i.n.g', 'b.a.b.i', 'b.a.n.g.s.a.t', 'g.o.b.l.o.k', 'k.o.n.t.o.l', 'm.e.m.e.k',
'n.g.e.n.t.o.t', 'p.e.l.e.r', 't.a.i', 't.o.l.o.l', 'j.a.n.c.o.k', 'j.e.m.b.u.t',
'k.a.m.p.r.e.t', 's.i.a.l.a.n', 'w.i.b.u',
// 📱 Variasi dengan Angka & Simbol
'4nj1ng', 'b4b1', 'b4ngs4t', 'g0bl0k', 'k0nt0l', 'm3m3k', 'ng3nt0t', 'p3l3r',
't4i', 't0l0l', 'j4nc0k', 'j3mbut', 'k4mpr3t', 's14l4n', 'w1bu',
'4nj1ngg', 'b4b11', 'b4ngs4tt', 'g0bl0kk', 'k0nt0ll', 'm3m3kk', 'ng3nt0tt',
'p3l3rr', 't4ii', 't0l0ll', 'j4nc0kk', 'j3mbutt', 'k4mpr3tt', 's14l4nn', 'w1buu',
// 🗣️ Kata yang Sering Digunakan dalam Konteks Negatif
'dasar', 'kamu', 'kau', 'lu', 'lo', 'gue', 'gua', 'kita', 'kami', 'mereka',
'dasarr', 'kamuu', 'kauu', 'luu', 'loo', 'guee', 'guua', 'kitaa', 'kamii', 'merekaa',
'dasar bodoh', 'dasar goblok', 'dasar bangsat', 'dasar idiot', 'dasar sialan',
'dasar bego', 'dasar dungu', 'dasar edan', 'dasar gila', 'dasar sinting',
'dasar pemalas', 'dasar pengecut', 'dasar penipu', 'dasar najis', 'dasar kampret',
// 🚫 Kata yang Mengandung Unsur Seksual
'porno', 'seks', 'mesum', 'bugil', 'telanjang', 'payudara', 'pantat', 'vagina', 'penis',
'pornoo', 'sekss', 'mesumm', 'bugill', 'telanjangg', 'payudaraa', 'pantatt', 'vaginna',
'peniss', 'pornoografi', 'pornografi', 'porno graf i', 'seksual', 'seksualitas',
'pornoograffii', 'pornografffii', 'porno graf ii', 'seksuall', 'seksualitass',
// 🤬 Kata Kasar dari Bahasa Daerah (Sunda, Jawa, dll)
'kampret', 'kacung', 'mampus', 'sialan', 'bangsat', 'goblok', 'bodoh', 'tolol',
'kamprett', 'kacungg', 'mampuss', 'sialann', 'bangsaatt', 'gobllokk', 'bodooh', 'tololl',
'kampret sunda', 'kacung jawa', 'mampus batak', 'sialan minang', 'bangsat lampung',
'goblok palembang', 'bodoh medan', 'tolol makassar',
// 📉 Kata yang Sering Digunakan untuk Menjelekkan Orang
// 'jelek', 'buruk', 'tidak pantas', 'tidak sopan', 'tidak beretika', 'tidak beradab',
// 'jelekk', 'burukk', 'tidak pantass', 'tidak sopann', 'tidak beretikaa', 'tidak beradabb',
// 'jelek banget', 'buruk banget', 'tidak pantas banget', 'tidak sopan banget',
// 'tidak beretika banget', 'tidak beradab banget',
// 🛑 Kata yang Sering Digunakan untuk Menyebarkan Hoax
// 'hoax', 'bohong', 'palsu', 'tipu', 'menipu', 'menyesatkan', 'menjerumuskan',
// 'hoaxx', 'bohongg', 'palsuu', 'tipuu', 'menipuu', 'menyesatkanng', 'menjerumuskanng',
// 'hoax besar', 'bohong besar', 'palsu besar', 'tipu besar', 'menipu besar',
// 'menyesatkan besar', 'menjerumuskan besar'
];
// Normalisasi teks
const normalizeText = (text: string) => {
return text.toLowerCase().replace(/\s+/g, ' ').trim();
};
// Cek apakah teks mengandung kata buruk
const isBadContent = (text: string) => {
const normalized = normalizeText(text);
for (let word of badWordsIndonesia) {
if (normalized.includes(word)) {
return true;
}
}
return false;
};
// Saring teks (ganti dengan asterisk)
const censorText = (text: string) => {
let result = text;
for (let word of badWordsIndonesia) {
const regex = new RegExp(word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
result = result.replace(regex, '*'.repeat(word.length));
}
return result;
};
export {
badWordsIndonesia,
isBadContent,
censorText,
normalizeText
};

18
utils/viersionBadge.ts Normal file
View File

@@ -0,0 +1,18 @@
// VersionBadge.tsx
import Constants from "expo-constants";
import { Platform } from "react-native";
export default function versionBadge() {
const expoConfig = Constants.expoConfig;
const version = expoConfig?.version; // "1.0.1"
const iosBuild = expoConfig?.ios?.buildNumber; // "10"
const androidBuild = expoConfig?.android?.versionCode; // 2
const build =
Platform.OS === "ios" ? iosBuild : androidBuild;
const result = `${version} ( ${build} )`;
return result
}