Compare commits

...

32 Commits

Author SHA1 Message Date
60e5c0663c Merge pull request 'Checkpoint 2' (#28) from checkpoint-2/12-dec-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/28
2025-12-12 14:59:49 +08:00
2c0198b1b7 Checkpoint QC Done
Cooming soon: Notification feature

### No Issue
2025-12-12 14:55:30 +08:00
573b525352 Fix QC ( Keano )
Fix:
- modified:   app/(application)/(user)/home.tsx
- modified:   app/(application)/(user)/voting/create.tsx
- modified:   components/DateInput/DataTimeAndroid.tsx
- modified:   components/DateInput/DateTimePickerCustom.tsx
- modified:   screens/Event/BoxPublishSection.tsx

### No Issue
2025-12-11 14:01:05 +08:00
ae0bf4dd60 Merge pull request 'Fix QC ( Ayu )' (#27) from qc/10-dec-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/27
2025-12-10 17:39:06 +08:00
6f9481c7c9 Fix QC ( Ayu )
Fix:
- modified:   app/(application)/(user)/event/[id]/publish.tsx
- modified:   app/(application)/(user)/event/create.tsx
- modified:   app/(application)/(user)/portofolio/[id]/create.tsx
- modified:   app/(application)/(user)/portofolio/[id]/edit.tsx
- modified:   app/(application)/admin/collaboration/[id]/group.tsx
- modified:   app/(application)/admin/collaboration/group.tsx
- modified:   app/(application)/admin/collaboration/publish.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/forum/posting.tsx
- modified:   app/(application)/admin/forum/report-comment.tsx
- modified:   app/(application)/admin/forum/report-posting.tsx
- modified:   app/(application)/admin/voting/[status]/status.tsx
- modified:   app/(application)/admin/voting/history.tsx
- modified:   components/Select/SelectCustom.tsx
- modified:   components/_ShareComponent/GridSpan_4_8.tsx
- modified:   screens/Authentication/LoginView.tsx
- modified:   screens/Collaboration/BoxPublishSection.tsx
- modified:   screens/Event/BoxDetailPublishSection.tsx
- modified:   screens/Home/topFeatureSection.tsx
- modified:   screens/Portofolio/ButtonCreatePortofolio.tsx

Add:
- app/(application)/admin/app-information/business-field/[id]/bidang-update.tsx
- app/(application)/admin/app-information/business-field/[id]/sub-bidang-update.tsx

### No Issue
2025-12-10 17:35:15 +08:00
bb63f7fa9a Merge pull request 'Apple reject and QC: Ayu' (#26) from qc/9-dec-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/26
2025-12-09 17:41:55 +08:00
cccb44a835 Fix QC Ayu
Fix:
- modified:   app/(application)/(user)/event/[id]/publish.tsx
- modified:   app/(application)/(user)/event/create.tsx
- modified:   app/(application)/(user)/portofolio/[id]/create.tsx
- modified:   app/(application)/(user)/portofolio/[id]/edit.tsx
- modified:   app/(application)/admin/collaboration/[id]/group.tsx
- modified:   app/(application)/admin/collaboration/group.tsx
- modified:   app/(application)/admin/collaboration/publish.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/forum/posting.tsx
- modified:   app/(application)/admin/forum/report-comment.tsx
- modified:   app/(application)/admin/forum/report-posting.tsx
- modified:   app/(application)/admin/voting/[status]/status.tsx
- modified:   app/(application)/admin/voting/history.tsx
- modified:   components/Select/SelectCustom.tsx
- modified:   components/_ShareComponent/GridSpan_4_8.tsx
- modified:   screens/Authentication/LoginView.tsx
- modified:   screens/Collaboration/BoxPublishSection.tsx
- modified:   screens/Event/BoxDetailPublishSection.tsx
- modified:   screens/Home/topFeatureSection.tsx
- modified:   screens/Portofolio/ButtonCreatePortofolio.tsx

Add:
- components/_ShareComponent/GridSpan_NewComponent.tsx

### No Issue
2025-12-09 17:36:36 +08:00
0f5862ce70 Fix Apple Reject:
Add:
- app/(application)/(user)/forum/terms.tsx

Fix:
- app/(application)/(user)/_layout.tsx
- app/(application)/(user)/home.tsx
- screens/Home/tabsList.ts
- service/api-client/api-user.ts

### No Issue
2025-12-08 16:34:33 +08:00
9742c1849a Merge pull request 'Fix QC Admin' (#25) from qc/5-dec-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/25
2025-12-05 17:23:16 +08:00
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
2265ef64cc Merge pull request 'QC Hipmi' (#24) from qc/4-dec-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/24
2025-12-04 17:47:01 +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
fbd096af9c Merge pull request 'Fix rejected Apple' (#23) from qc/3-dec-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/23
2025-12-03 17:24:07 +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
5019b00f59 Merge pull request 'Clearing apple rejected & QC' (#22) from qc/2-dec-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/22
2025-12-02 17:50:22 +08:00
858b441a8c Clearing apple rejected
QC: Inno

Fix:
- app.config.js
- app/(application)/(user)/investment/[id]/index.tsx
- app/(application)/(user)/voting/(tabs)/index.tsx
- app/(application)/(user)/waiting-room.tsx
- app/(application)/terms-agreement.tsx
- context/AuthContext.tsx
- ios/HIPMIBadungConnect.xcodeproj/project.pbxproj
- ios/HIPMIBadungConnect/Info.plist
- screens/Authentication/LoginView.tsx
- screens/Authentication/VerificationView.tsx
- screens/Home/topFeatureSection.tsx
- screens/Invesment/BoxBerandaSection.tsx
- screens/Invesment/ButtonInvestasiSection.tsx
- screens/Invesment/DetailDataPublishSection.tsx
- service/api-client/api-voting.ts
- service/api-config.ts

### No Issue
2025-12-02 17:48:24 +08:00
249ada221b Merge pull request 'QC: Inno dan Pak Jun' (#21) from qc/1-dec-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/21
2025-12-01 17:45:26 +08:00
98aaa126a1 QC: Inno dan Pak Jun
Fix:
- app/(application)/(user)/collaboration/create.tsx
- app/(application)/(user)/event/[id]/edit.tsx
- app/(application)/(user)/event/create.tsx
- app/(application)/(user)/profile/[id]/blocked-list.tsx
- app/(application)/(user)/profile/[id]/index.tsx
- app/(application)/(user)/voting/[id]/[status]/detail.tsx
- components/Button/FloatingButton.tsx
- components/TextArea/TextAreaCustom.tsx
- components/TextInput/TextInputCustom.tsx
- constants/color-palet.ts
- screens/Authentication/LoginView.tsx
- screens/Home/topFeatureSection.tsx
- screens/Portofolio/SocialMediaSection.tsx
- screens/Voting/BoxDetailHasilVotingSection.tsx
- styles/global-styles.ts

### No Issue
2025-12-01 17:43:20 +08:00
19814315a4 Merge pull request 'Fix loader dan prebuild' (#20) from apple-reject/28-nov-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/20
2025-11-28 17:32:49 +08:00
69452ff4e7 Fix loader fetch data di Forum dan Forumku
### No Issue
2025-11-28 17:27:59 +08:00
33ec892ec8 Prebuild : untuk Maps box
### No issue
2025-11-28 16:35:18 +08:00
77ef3a055e Merge pull request 'Halaman unblock user' (#19) from apple-reject/28-nov-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/19
2025-11-28 13:58:01 +08:00
8a900e9469 Halaman unblock user
Add:
- app/(application)/(user)/profile/[id]/blocked-list.tsx
app/(application)/(user)/profile/[id]/detail-blocked.tsx
components/_ShareComponent/ListEmptyComponent.tsx
components/_ShareComponent/ListLoaderFooterComponent.tsx
components/_ShareComponent/ListSkeletonComponent.tsx
hooks/use-paginated-api.ts
service/api-client/api-blocked.ts

Fix:
modified:   app/(application)/(user)/profile/_layout.tsx
modified:   components/_ShareComponent/NewWrapper.tsx
modified:   components/index.ts
modified:   screens/Profile/ListPage.tsx
modified:   styles/global-styles.ts

### No Issue
2025-11-28 13:55:48 +08:00
aba3ad8ded Merge pull request 'apple-rejected' (#18) from apple-rejected/26-nov-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/18
2025-11-27 05:57:04 +08:00
7799c7720d Merge pull request 'Fix map android :' (#17) from fix-map/21-nov-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/17
2025-11-21 17:45:18 +08:00
2926b6eac1 Merge pull request 'delete-account' (#16) from delete-account/20-nov-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/16
2025-11-20 15:44:03 +08:00
637d444c5c Merge pull request 'Versi 1.0.1 : Qr Code & Start notifications' (#11) from staging into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/11
2025-11-14 17:46:16 +08:00
a614cfaac9 Merge pull request 'QR Code & Notification' (#10) from qc/14-nov-25 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/10
2025-11-14 17:45:29 +08:00
1a7ad58505 Merge pull request 'QR Code Scan' (#9) from qrcode-access/13-nov-25 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/9
2025-11-13 17:43:08 +08:00
7612be7366 Merge pull request 'UPDATE Build' (#8) from build/12-nov-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi-mobile/pulls/8
2025-11-12 15:03:36 +08:00
115 changed files with 3298 additions and 841 deletions

View File

@@ -82,6 +82,14 @@ def enableMinifyInReleaseBuilds = (findProperty('android.enableMinifyInReleaseBu
def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
android { android {
// @generated begin @rnmapbox/maps-libcpp - expo prebuild (DO NOT MODIFY) sync-e24830a5a3e854b398227dfe9630aabfaa1cadd1
packagingOptions {
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
}
// @generated end @rnmapbox/maps-libcpp
ndkVersion rootProject.ext.ndkVersion ndkVersion rootProject.ext.ndkVersion
buildToolsVersion rootProject.ext.buildToolsVersion buildToolsVersion rootProject.ext.buildToolsVersion
@@ -92,7 +100,7 @@ android {
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

@@ -16,6 +16,10 @@
</intent> </intent>
</queries> </queries>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:enableOnBackInvokedCallback="false"> <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:enableOnBackInvokedCallback="false">
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color"/>
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon"/>
<meta-data android:name="expo.modules.notifications.default_notification_color" android:resource="@color/notification_icon_color"/>
<meta-data android:name="expo.modules.notifications.default_notification_icon" android:resource="@drawable/notification_icon"/>
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/> <meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/> <meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/> <meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@@ -3,4 +3,5 @@
<color name="iconBackground">#ffffff</color> <color name="iconBackground">#ffffff</color>
<color name="colorPrimary">#023c69</color> <color name="colorPrimary">#023c69</color>
<color name="colorPrimaryDark">#ffffff</color> <color name="colorPrimaryDark">#ffffff</color>
<color name="notification_icon_color">#ffffff</color>
</resources> </resources>

View File

@@ -23,3 +23,25 @@ allprojects {
apply plugin: "expo-root-project" apply plugin: "expo-root-project"
apply plugin: "com.facebook.react.rootproject" apply plugin: "com.facebook.react.rootproject"
// @generated begin @rnmapbox/maps-v2-maven - expo prebuild (DO NOT MODIFY) sync-d4ccbfdff48fdba3138b02a8ba41b9722af001d8
allprojects {
repositories {
maven {
url 'https://api.mapbox.com/downloads/v2/releases/maven'
// Authentication is no longer required as per Mapbox's removal of download token requirement
// See: https://github.com/mapbox/mapbox-maps-flutter/issues/775
// Keeping this as optional for backward compatibility
def token = project.properties['MAPBOX_DOWNLOADS_TOKEN'] ?: System.getenv('RNMAPBOX_MAPS_DOWNLOAD_TOKEN')
if (token) {
authentication { basic(BasicAuthentication) }
credentials {
username = 'mapbox'
password = token
}
}
}
}
}
// @generated end @rnmapbox/maps-v2-maven

View File

@@ -14,12 +14,14 @@ export default {
ios: { ios: {
supportsTablet: true, supportsTablet: true,
bundleIdentifier: "com.anonymous.hipmi-mobile", bundleIdentifier: "com.anonymous.hipmi-mobile",
googleServicesFile: "./ios/HIPMIBadungConnect/GoogleService-Info.plist",
infoPlist: { infoPlist: {
ITSAppUsesNonExemptEncryption: false, ITSAppUsesNonExemptEncryption: false,
"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: "8", buildNumber: "15",
}, },
android: { android: {
@@ -30,7 +32,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: [
{ {
@@ -56,7 +58,6 @@ export default {
plugins: [ plugins: [
"expo-router", "expo-router",
"expo-notifications",
"expo-web-browser", "expo-web-browser",
[ [
"expo-splash-screen", "expo-splash-screen",
@@ -77,6 +78,15 @@ export default {
], ],
"expo-font", "expo-font",
"@rnmapbox/maps", "@rnmapbox/maps",
"@react-native-firebase/app",
[
"expo-notifications",
{
icon: "./assets/images/icon.png",
color: "#ffffff",
iosDisplayInForeground: true,
},
],
], ],
experiments: { experiments: {

View File

@@ -595,6 +595,13 @@ export default function UserLayout() {
headerLeft: () => <BackButton />, headerLeft: () => <BackButton />,
}} }}
/> />
<Stack.Screen
name="forum/terms"
options={{
title: "Syarat & Ketentuan Forum",
headerLeft: () => <BackButton />,
}}
/>
{/* ========== Maps Section ========= */} {/* ========== Maps Section ========= */}
<Stack.Screen <Stack.Screen

View File

@@ -155,7 +155,7 @@ export default function CollaborationCreate() {
<TextAreaCustom <TextAreaCustom
required required
label="Keuntungan Proyek" label="Keuntungan Proyek"
placeholder="Masukan keuntungan proyek" placeholder="Masukan keuntungan proyek, contoh: Meningkatkan relasi bisnis , menjamin kualitas produk, meningkatkan kinerja dan lain lain"
showCount showCount
maxLength={1000} maxLength={1000}
value={data?.benefit} value={data?.benefit}

View File

@@ -18,7 +18,7 @@ import {
import { apiMasterEventType } from "@/service/api-client/api-master"; import { apiMasterEventType } from "@/service/api-client/api-master";
import { DateTimePickerEvent } from "@react-native-community/datetimepicker"; import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import React, { useCallback, useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
export default function EventEdit() { export default function EventEdit() {
@@ -55,6 +55,7 @@ export default function EventEdit() {
try { try {
setIsLoadData(true); setIsLoadData(true);
const response = await apiEventGetOne({ id: id as string }); const response = await apiEventGetOne({ id: id as string });
console.log("[DATA BY ID]", JSON.stringify(response, null, 2));
if (response.success) { if (response.success) {
setData(response.data); setData(response.data);
setSelectedDate(new Date(response.data.tanggal)); setSelectedDate(new Date(response.data.tanggal));
@@ -209,7 +210,7 @@ export default function EventEdit() {
minimumDate={new Date(Date.now())} minimumDate={new Date(Date.now())}
label="Tanggal & Waktu Mulai" label="Tanggal & Waktu Mulai"
required required
value={selectedDate as any} value={selectedDate}
onChange={(date: any) => { onChange={(date: any) => {
setSelectedDate(date as any); setSelectedDate(date as any);
}} }}
@@ -254,7 +255,6 @@ export default function EventEdit() {
placeholder="Masukkan deskripsi event" placeholder="Masukkan deskripsi event"
required required
showCount showCount
maxLength={100}
value={data?.deskripsi} value={data?.deskripsi}
onChangeText={(value) => setData({ ...data, deskripsi: value })} onChangeText={(value) => setData({ ...data, deskripsi: value })}
/> />

View File

@@ -71,8 +71,6 @@ export default function EventDetailPublish() {
} }
} }
console.log("[participans]", isParticipant);
const handlePress = (item: IMenuDrawerItem) => { const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH ", item.path); console.log("PATH ", item.path);
router.navigate(item.path as any); router.navigate(item.path as any);
@@ -139,7 +137,7 @@ export default function EventDetailPublish() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: `Event publish`, title: `Event Publish`,
headerLeft: () => <LeftButtonCustom />, headerLeft: () => <LeftButtonCustom />,
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />, headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
}} }}

View File

@@ -110,7 +110,7 @@ export default function EventCreate() {
const response = await apiEventCreate(newData); const response = await apiEventCreate(newData);
console.log("Response", JSON.stringify(response, null, 2)); console.log("Response", JSON.stringify(response, null, 2));
router.navigate("/event/status"); router.replace("/event/status");
} catch (error) { } catch (error) {
console.log(error); console.log(error);
} finally { } finally {
@@ -118,6 +118,7 @@ export default function EventCreate() {
} }
}; };
const buttonSubmit = ( const buttonSubmit = (
<ButtonCustom <ButtonCustom
isLoading={isLoading} isLoading={isLoading}
@@ -144,7 +145,7 @@ export default function EventCreate() {
label: item.name, label: item.name,
value: item.id, value: item.id,
}))} }))}
value={data?.eventMaster_TipeAcaraId || ""} value={data?.eventMaster_TipeAcaraId || null}
onChange={(value: any) => onChange={(value: any) =>
setData({ ...data, eventMaster_TipeAcaraId: value }) setData({ ...data, eventMaster_TipeAcaraId: value })
} }
@@ -191,7 +192,7 @@ export default function EventCreate() {
placeholder="Masukkan deskripsi event" placeholder="Masukkan deskripsi event"
required required
showCount showCount
maxLength={1000} value={data?.deskripsi || ""}
onChangeText={(value: any) => onChangeText={(value: any) =>
setData({ ...data, deskripsi: value }) setData({ ...data, deskripsi: value })
} }

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

@@ -0,0 +1,202 @@
import {
BaseBox,
ButtonCustom,
CheckboxCustom,
NewWrapper,
StackCustom,
TextCustom,
} from "@/components";
import { useAuth } from "@/hooks/use-auth";
import { apiAcceptForumTerms } from "@/service/api-client/api-user";
import { GStyles } from "@/styles/global-styles";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { useState } from "react";
import { View } from "react-native";
import { Text } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function ForumSplash() {
const { user } = useAuth();
const [term, setTerm] = useState(false);
const [loading, setLoading] = useState(false);
const handleSubmit = async () => {
try {
setLoading(true);
const respone = await apiAcceptForumTerms({
category: "Forum",
userId: user?.id as any,
});
if (respone.success) {
Toast.show({
type: "success",
text1: "Berhasil",
text2: "Terima kasih telah menerima syarat & ketentuan forum ini",
});
router.replace("/(application)/forum");
return;
}
Toast.show({
type: "error",
text1: "Gagal",
text2: "Terjadi kesalahan, silahkan coba lagi",
});
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoading(false);
}
};
return (
<NewWrapper>
{/* <TextCustom bold>HIPMI Badung Connect</TextCustom> . */}
<BaseBox>
<StackCustom>
<TextCustom>
Dengan mengakses dan menggunakan Forum HIPMI Badung Connect, Anda
secara sadar menyetujui ketentuan berikut:
</TextCustom>
<TextCustom bold>
1. Dilarang keras memposting konten yang mengandung:
</TextCustom>
<View style={{ paddingInline: 10 }}>
{forumTerms1.map((term, index) => (
<View
key={index}
style={{
flexDirection: "row",
alignItems: "center",
gap: 10,
paddingBottom: 10,
}}
>
<Ionicons name="radio-button-on" color={"white"} />
<TextCustom>{term.text}</TextCustom>
</View>
))}
</View>
<TextCustom bold>
2. Setiap pengguna bertanggung jawab penuh atas konten yang
diunggah. Konten yang melanggar ketentuan ini dapat dihapus kapan
saja tanpa pemberitahuan.
</TextCustom>
<TextCustom bold>
3. Jika Anda menemukan konten tidak pantas, segera:
</TextCustom>
<View style={{ paddingInline: 10 }}>
{forumTerms2.map((term, index) => (
<View
key={index}
style={{
flexDirection: "row",
alignItems: "center",
gap: 10,
paddingBottom: 10,
}}
>
<Ionicons name="radio-button-on" color={"white"} />
<TextCustom>{term.text}</TextCustom>
</View>
))}
</View>
<TextCustom bold>
4. Gunakan fitur Blokir Pengguna di profil pengguna terkait
</TextCustom>
<View style={{ paddingInline: 10 }}>
{forumTerms3.map((term, index) => (
<View
key={index}
style={{
flexDirection: "row",
alignItems: "center",
gap: 10,
paddingBottom: 10,
}}
>
<Ionicons name="radio-button-on" color={"white"} />
<TextCustom>{term.text}</TextCustom>
</View>
))}
</View>
<TextCustom>
Pelanggaran terhadap ketentuan ini berakibat{" "}
<TextCustom bold>pencabutan akses</TextCustom> ke Forum dan/atau{" "}
<TextCustom bold>pemblokiran akun secara permanen</TextCustom> tanpa
pemberitahuan lebih lanjut.
</TextCustom>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginTop: 16,
marginBottom: 16,
}}
>
<CheckboxCustom value={term} onChange={() => setTerm(!term)} />
<Text style={GStyles.textLabel}>
Saya telah membaca dan menyetujui Syarat & Ketentuan Forum ini
</Text>
</View>
<ButtonCustom
disabled={!term || loading}
onPress={() => {
handleSubmit();
}}
>
Lanjut
</ButtonCustom>
</StackCustom>
</BaseBox>
</NewWrapper>
);
}
// Data dalam format JSON (bisa juga diimpor dari file terpisah)
interface Term {
text: string;
}
const forumTerms1: Term[] = [
{
text: "Ujaran kebencian, diskriminasi, atau konten SARA (Suku, Agama, Ras, Antar-golongan)",
},
{ text: "Kata kasar, pelecehan, ancaman, atau bullying" },
{ text: "Pornografi, hoaks, spam, atau informasi menyesatkan" },
{ text: "Promosi aktivitas ilegal seperti perjudian atau narkoba" },
];
const forumTerms2: Term[] = [
{
text: "Gunakan tombol “Laporkan” di setiap postingan, atau",
},
{
text: "Gunakan fitur “Blokir Pengguna” di profil pengguna terkait.",
},
];
const forumTerms3: Term[] = [
{
text: "Meninjau setiap laporan dalam waktu 24 jam",
},
{
text: "Menghapus konten yang melanggar",
},
{
text: "Memblokir akun pelanggar sesuai tingkat pelanggaran",
},
];

View File

@@ -11,25 +11,30 @@ import Home_FeatureSection from "@/screens/Home/topFeatureSection";
import { apiUser } from "@/service/api-client/api-user"; import { apiUser } from "@/service/api-client/api-user";
import { apiVersion } from "@/service/api-config"; import { apiVersion } from "@/service/api-config";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { Redirect, router, Stack } from "expo-router"; import { Redirect, router, Stack, useFocusEffect } from "expo-router";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { RefreshControl } from "react-native";
export default function Application() { export default function Application() {
const { token, user } = useAuth(); const { token, user, userData } = useAuth();
const [data, setData] = useState<any>(); const [data, setData] = useState<any>();
const [refreshing, setRefreshing] = useState(false);
console.log("[User] >>", JSON.stringify(user?.id, null, 2)); console.log("[User] >>", JSON.stringify(user?.id, null, 2));
useEffect(() => { // ‼️ Untuk cek apakah: 1. user ada, 2. user punya profile, 3. accept temrs of forum nya ada atau tidak
useFocusEffect(
useCallback(() => {
onLoadData(); onLoadData();
checkVersion(); checkVersion();
}, []); userData(token as string);
}, [user?.id, token])
);
async function onLoadData() { async function onLoadData() {
const response = await apiUser(user?.id as string); const response = await apiUser(user?.id as string);
console.log( console.log(
"[Profile ID]>>", "[Profile ID]>>",
JSON.stringify(response?.data?.Profile.id, null, 2) JSON.stringify(response?.data?.Profile?.id, null, 2)
); );
setData(response.data); setData(response.data);
@@ -40,6 +45,13 @@ export default function Application() {
console.log("[Version] >>", JSON.stringify(response.data, null, 2)); console.log("[Version] >>", JSON.stringify(response.data, null, 2));
}; };
const onRefresh = useCallback(() => {
setRefreshing(true);
onLoadData();
checkVersion();
setRefreshing(false);
}, []);
if (user && user?.termsOfServiceAccepted === false) { if (user && user?.termsOfServiceAccepted === false) {
console.log("User is not accept term service"); console.log("User is not accept term service");
return <Redirect href={`/terms-agreement`} />; return <Redirect href={`/terms-agreement`} />;
@@ -72,9 +84,10 @@ export default function Application() {
), ),
headerRight: () => ( headerRight: () => (
<Ionicons <Ionicons
disabled={true}
name="notifications" name="notifications"
size={20} size={20}
color={MainColor.yellow} color={MainColor.placeholder}
onPress={() => { onPress={() => {
router.push("/notifications"); router.push("/notifications");
}} }}
@@ -83,8 +96,16 @@ export default function Application() {
}} }}
/> />
<ViewWrapper <ViewWrapper
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
footerComponent={ footerComponent={
<TabSection tabs={tabsHome(data?.Profile?.id as string)} /> <TabSection
tabs={tabsHome({
acceptedForumTermsAt: data?.acceptedForumTermsAt,
profileId: data?.Profile?.id,
})}
/>
} }
> >
<StackCustom> <StackCustom>

View File

@@ -15,6 +15,7 @@ import Investment_ButtonInvestasiSection from "@/screens/Invesment/ButtonInvesta
import Invesment_ComponentBoxOnBottomDetail from "@/screens/Invesment/ComponentBoxOnBottomDetail"; import Invesment_ComponentBoxOnBottomDetail from "@/screens/Invesment/ComponentBoxOnBottomDetail";
import Invesment_DetailDataPublishSection from "@/screens/Invesment/DetailDataPublishSection"; import Invesment_DetailDataPublishSection from "@/screens/Invesment/DetailDataPublishSection";
import { apiInvestmentGetOne } from "@/service/api-client/api-investment"; import { apiInvestmentGetOne } from "@/service/api-client/api-investment";
import { countDownAndCondition } from "@/utils/countDownAndCondition";
import { AntDesign, MaterialIcons } from "@expo/vector-icons"; import { AntDesign, MaterialIcons } from "@expo/vector-icons";
import { import {
router, router,
@@ -23,7 +24,7 @@ import {
useLocalSearchParams, useLocalSearchParams,
} from "expo-router"; } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useCallback, useState } from "react"; import { useCallback, useEffect, useState } from "react";
export default function InvestmentDetail() { export default function InvestmentDetail() {
const { user } = useAuth(); const { user } = useAuth();
@@ -62,6 +63,31 @@ export default function InvestmentDetail() {
setOpenDrawerPublish(false); setOpenDrawerPublish(false);
}; };
const [value, setValue] = useState({
sisa: 0,
reminder: false,
});
useEffect(() => {
updateCountDown();
}, [data]);
console.log("[DATA DETAIL]", JSON.stringify(data, null, 2));
const updateCountDown = () => {
const countDown = countDownAndCondition({
duration: data?.MasterPencarianInvestor.name,
publishTime: data?.countDown,
});
setValue({
sisa: countDown.durationDay,
reminder: countDown.reminder,
});
};
const bottomSection = ( const bottomSection = (
<Invesment_ComponentBoxOnBottomDetail <Invesment_ComponentBoxOnBottomDetail
id={id as string} id={id as string}
@@ -71,7 +97,7 @@ export default function InvestmentDetail() {
); );
const buttonSection = ( const buttonSection = (
<Investment_ButtonInvestasiSection id={id as string} isMine={user?.id === data?.author?.id} /> <Investment_ButtonInvestasiSection id={id as string} isMine={user?.id === data?.author?.id} reminder={value.reminder} />
); );
return ( return (

View File

@@ -31,9 +31,9 @@ import {
import pickImage from "@/utils/pickImage"; import pickImage from "@/utils/pickImage";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { Image } from "expo-image"; import { Image } from "expo-image";
import { useLocalSearchParams } from "expo-router"; import { useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useEffect, useState } from "react"; import { useCallback, useEffect, useState } from "react";
import { Text, TouchableOpacity, View } from "react-native"; import { Text, TouchableOpacity, View } from "react-native";
import PhoneInput, { ICountry } from "react-native-international-phone-number"; import PhoneInput, { ICountry } from "react-native-international-phone-number";
import { Avatar } from "react-native-paper"; import { Avatar } from "react-native-paper";
@@ -76,7 +76,7 @@ export default function PortofolioCreate() {
function handleInputValue(phoneNumber: string) { function handleInputValue(phoneNumber: string) {
setInputValue(phoneNumber); setInputValue(phoneNumber);
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || ""; const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
const fixNumber = inputValue.replace(/\s+/g, ""); let fixNumber = inputValue.replace(/\s+/g, "").replace(/^0+/, "");
const realNumber = callingCode + fixNumber; const realNumber = callingCode + fixNumber;
setData({ ...data, tlpn: realNumber }); setData({ ...data, tlpn: realNumber });
} }
@@ -85,10 +85,12 @@ export default function PortofolioCreate() {
setSelectedCountry(country); setSelectedCountry(country);
} }
useEffect(() => { useFocusEffect(
useCallback(() => {
onLoadMaster(); onLoadMaster();
onLoadMasterSubBidangBisnis(); onLoadMasterSubBidangBisnis();
}, []); }, [])
);
const onLoadMaster = async () => { const onLoadMaster = async () => {
try { try {

View File

@@ -244,7 +244,7 @@ export default function PortofolioEdit() {
const handleSubmitUpdate = async () => { const handleSubmitUpdate = async () => {
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || ""; const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
const fixNumber = data.tlpn.replace(/\s+/g, ""); let fixNumber = data.tlpn.replace(/\s+/g, "").replace(/^0+/, "");
const realNumber = callingCode + fixNumber; const realNumber = callingCode + fixNumber;
const newData: IFormData = { const newData: IFormData = {

View File

@@ -0,0 +1,148 @@
import {
AvatarUsernameAndOtherComponent,
BadgeCustom,
ClickableCustom,
Divider,
SelectCustom,
TextCustom,
} from "@/components";
import ListEmptyComponent from "@/components/_ShareComponent/ListEmptyComponent";
import ListLoaderFooterComponent from "@/components/_ShareComponent/ListLoaderFooterComponent";
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
import { MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth";
import { usePaginatedApi } from "@/hooks/use-paginated-api";
import { apiGetBlocked } from "@/service/api-client/api-blocked";
import { apiMasterAppCategory } from "@/service/api-client/api-master";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useEffect, useRef, useState } from "react";
import { RefreshControl, View } from "react-native";
const PAGE_SIZE = 10;
export default function ProfileBlockedList() {
const { user } = useAuth();
const [masterApp, setMasterApp] = useState<any[]>([]);
const isInitialMount = useRef(true);
const {
data: listData,
loading,
refreshing,
hasMore,
search,
setSearch,
onRefresh,
loadMore,
} = usePaginatedApi({
fetcher: async (params: { page: number; search?: string }) => {
const response = await apiGetBlocked({
id: user?.id as any,
search: search,
page: String(params.page) as any,
});
return response.data;
},
initialSearch: "",
pageSize: PAGE_SIZE,
dependencies: [user?.id],
});
useEffect(() => {
fetchMasterApp();
}, []);
// 🔁 Refresh otomatis saat kembali ke halaman ini
useFocusEffect(
useCallback(() => {
if (isInitialMount.current) {
// Skip saat pertama kali mount
isInitialMount.current = false;
return;
}
// Hanya refresh saat kembali dari screen lain
onRefresh();
}, [onRefresh])
);
const fetchMasterApp = async () => {
const response = await apiMasterAppCategory();
setMasterApp(response.data);
};
const renderHeader = () => (
<SelectCustom
placeholder="Pilih Kategori Fitur"
data={masterApp.map((item) => ({
label: item.name,
value: item.id,
}))}
value={search === "" ? undefined : search}
onChange={(value) => {
setSearch(value as any);
}}
/>
);
const renderItem = ({ item }: { item: any }) => (
<>
<ClickableCustom
onPress={() => {
router.push(`/profile/${item.id}/detail-blocked`);
}}
>
<View
style={{
paddingInline: 8,
}}
>
<AvatarUsernameAndOtherComponent
avatarHref={`/profile/${item?.blocked?.Profile?.id}`}
avatar={item?.blocked?.Profile?.imageId}
name={item?.blocked?.username}
rightComponent={
<View style={{ flexDirection: "row", gap: 4 }}>
<BadgeCustom>
<TextCustom size={"small"} bold truncate>
{item?.menuFeature?.name}
</TextCustom>
</BadgeCustom>
</View>
}
/>
<Divider color="gray" />
</View>
</ClickableCustom>
</>
);
return (
<>
<NewWrapper
// headerComponent={renderHeader()}
listData={listData}
renderItem={renderItem}
onEndReached={loadMore}
refreshControl={
<RefreshControl
progressBackgroundColor={MainColor.yellow}
refreshing={refreshing}
onRefresh={onRefresh}
/>
}
ListFooterComponent={
hasMore && !refreshing ? <ListLoaderFooterComponent /> : null
}
ListEmptyComponent={
!loading && _.isEmpty(listData) ? (
<ListSkeletonComponent />
) : (
<ListEmptyComponent />
)
}
/>
</>
);
}

View File

@@ -0,0 +1,93 @@
import {
AlertDefaultSystem,
AvatarUsernameAndOtherComponent,
BaseBox,
BoxButtonOnFooter,
BoxWithHeaderSection,
ButtonCustom,
NewWrapper,
StackCustom,
TextCustom,
} from "@/components";
import AvatarAndBackground from "@/screens/Profile/AvatarAndBackground";
import {
apiGetBlockedById,
apiUnblock,
} from "@/service/api-client/api-blocked";
import { router, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useEffect, useState } from "react";
export default function ProfileDetailBlocked() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
useEffect(() => {
fetchData();
}, [id]);
const fetchData = async () => {
const response = await apiGetBlockedById({ id: String(id) });
// console.log("[RESPONSE >>]", JSON.stringify(response, null, 2));
setData(response.data);
};
const handleSubmit = async () => {
try {
setIsLoading(true);
await apiUnblock({ id: String(id) });
router.back();
} catch (error) {
console.log("[ERROR >>]", JSON.stringify(error, null, 2));
} finally {
setIsLoading(false);
}
};
return (
<>
<NewWrapper
footerComponent={
<BoxButtonOnFooter>
<ButtonCustom
isLoading={isLoading}
onPress={() => {
AlertDefaultSystem({
title: "Buka Blokir",
message: "Apakah anda yakin ingin membuka blokir ini?",
textLeft: "Tidak",
textRight: "Ya",
onPressRight: () => {
handleSubmit();
},
});
}}
>
Buka Blokir
</ButtonCustom>
</BoxButtonOnFooter>
}
>
<BoxWithHeaderSection>
<StackCustom>
<AvatarUsernameAndOtherComponent
avatarHref={`/profile/${data?.blocked?.Profile?.id}`}
avatar={data?.blocked?.Profile?.imageId}
name={data?.blocked?.username}
/>
<TextCustom align="center">
Jika anda membuka blokir ini maka semua postingan terkait user ini
akan muncul kembali di beranda
<TextCustom bold color="red">
{" "}
{_.upperCase(data?.menuFeature?.name)}
</TextCustom>
</TextCustom>
</StackCustom>
</BoxWithHeaderSection>
</NewWrapper>
</>
);
}

View File

@@ -64,6 +64,7 @@ export default function Profile() {
}; };
const onLoadPortofolio = async (id: string) => { const onLoadPortofolio = async (id: string) => {
try {
const response = await apiGetPortofolio({ id: id }); const response = await apiGetPortofolio({ id: id });
const lastTwoByDate = response.data const lastTwoByDate = response.data
.sort( .sort(
@@ -72,6 +73,9 @@ export default function Profile() {
) // urut desc ) // urut desc
.slice(0, 2); .slice(0, 2);
setListPortofolio(lastTwoByDate); setListPortofolio(lastTwoByDate);
} catch (error) {
console.log("[ERROR]", error);
}
}; };
return ( return (

View File

@@ -33,6 +33,16 @@ export default function ProfileLayout() {
name="create" name="create"
options={{ title: "Buat Profile", headerBackVisible: false }} options={{ title: "Buat Profile", headerBackVisible: false }}
/> />
<Stack.Screen
name="[id]/blocked-list"
options={{ title: "Blocked List", headerLeft: () => <BackButton /> }}
/>
<Stack.Screen
name="[id]/detail-blocked"
options={{ title: "Detail Blokir", headerLeft: () => <BackButton /> }}
/>
</Stack> </Stack>
</> </>
); );

View File

@@ -6,6 +6,7 @@ import {
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import { useAuth } from "@/hooks/use-auth";
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection"; import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
import { apiVotingGetAll } from "@/service/api-client/api-voting"; import { apiVotingGetAll } from "@/service/api-client/api-voting";
import { router, useFocusEffect } from "expo-router"; import { router, useFocusEffect } from "expo-router";
@@ -13,6 +14,7 @@ import _ from "lodash";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
export default function VotingBeranda() { export default function VotingBeranda() {
const { user } = useAuth();
const [listData, setListData] = useState<any>([]); const [listData, setListData] = useState<any>([]);
const [loadingGetData, setLoadingGetData] = useState(false); const [loadingGetData, setLoadingGetData] = useState(false);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
@@ -29,6 +31,7 @@ export default function VotingBeranda() {
const response = await apiVotingGetAll({ const response = await apiVotingGetAll({
search, search,
category: "beranda", category: "beranda",
userLoginId: user?.id,
}); });
if (response.success) { if (response.success) {
setListData(response.data); setListData(response.data);

View File

@@ -134,7 +134,7 @@ export default function VotingDetailStatus() {
{data && {data &&
data?.catatan && data?.catatan &&
(status === "draft" || status === "rejected") && ( (status === "draft" || status === "reject") && (
<ReportBox text={data?.catatan} /> <ReportBox text={data?.catatan} />
)} )}

View File

@@ -4,6 +4,7 @@ import {
ButtonCenteredOnly, ButtonCenteredOnly,
ButtonCustom, ButtonCustom,
InformationBox, InformationBox,
NewWrapper,
StackCustom, StackCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
@@ -12,6 +13,7 @@ import { useAuth } from "@/hooks/use-auth";
import { apiUser } from "@/service/api-client/api-user"; import { apiUser } from "@/service/api-client/api-user";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router"; import { router } from "expo-router";
import { RefreshControl } from "react-native";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
export default function WaitingRoom() { export default function WaitingRoom() {
@@ -33,7 +35,7 @@ export default function WaitingRoom() {
} else { } else {
Toast.show({ Toast.show({
type: "success", type: "success",
text1: "Akun anda telah aktif", // text2: "Anda berhasil login", text1: "Selamat ! Akun anda telah aktif", // text2: "Anda berhasil login",
}); });
router.replace(`/(application)/(user)/profile/create`); router.replace(`/(application)/(user)/profile/create`);
} }
@@ -82,10 +84,18 @@ export default function WaitingRoom() {
return ( return (
<> <>
<ViewWrapper footerComponent={logoutButton()}> <NewWrapper
footerComponent={logoutButton()}
refreshControl={
<RefreshControl refreshing={isLoading} onRefresh={handleCheck} />
}
>
<StackCustom> <StackCustom>
<InformationBox text="Permohonan akses Anda sedang dalam proses verifikasi oleh admin. Harap tunggu, Anda akan menerima pemberitahuan melalui Whatsapp setelah disetujui." /> <InformationBox
<ButtonCenteredOnly text="Akun Anda sedang menunggu aktivasi.
Silakan tunggu beberapa saat. Untuk memperbarui status, tarik layar ke bawah."
/>
{/* <ButtonCenteredOnly
isLoading={isLoading} isLoading={isLoading}
onPress={() => { onPress={() => {
handleCheck(); handleCheck();
@@ -93,9 +103,9 @@ export default function WaitingRoom() {
icon="refresh-ccw" icon="refresh-ccw"
> >
Check Check
</ButtonCenteredOnly> </ButtonCenteredOnly> */}
</StackCustom> </StackCustom>
</ViewWrapper> </NewWrapper>
</> </>
); );
} }

View File

@@ -0,0 +1,129 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { MainColor } from "@/constants/color-palet";
import {
apiAdminMasterBusinessFieldById,
apiAdminMasterBusinessFieldUpdate,
} from "@/service/api-admin/api-master-admin";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import { Switch } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminAppInformation_BusinessFieldDetail() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [isLoading, setIsLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadDetail();
}, [id])
);
const onLoadDetail = async () => {
try {
const response = await apiAdminMasterBusinessFieldById({
id: id as string,
category: "bidang"
});
setData(response.data);
} catch (error) {
console.log("[ERROR]", error);
setData(null);
}
};
const handlerSubmit = async () => {
if (!data.name) {
Toast.show({
type: "error",
text1: "Lengkapi Data",
});
return;
}
try {
setIsLoading(true);
const response = await apiAdminMasterBusinessFieldUpdate({
id: id as string,
data: data,
category: "bidang",
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal update data",
});
return;
}
Toast.show({
type: "success",
text1: "Data berhasil di update",
});
router.back();
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
disabled={!data?.name}
isLoading={isLoading}
onPress={() => handlerSubmit()}
>
Update
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonSubmit}>
<StackCustom>
<AdminBackButtonAntTitle title="Update Bidang Bisnis" />
<TextInputCustom
label="Nama Bidang Bisnis"
placeholder="Masukan Nama Bidang Bisnis"
required
value={data?.name}
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>
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -1,22 +1,22 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { import {
BoxButtonOnFooter, ActionIcon,
ButtonCustom, BaseBox,
CenterCustom,
LoaderCustom,
Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom,
TextInputCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import { IconEdit } from "@/components/_Icon";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle"; import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
import { MainColor } from "@/constants/color-palet"; import { MainColor } from "@/constants/color-palet";
import { import { apiAdminMasterBusinessFieldById } from "@/service/api-admin/api-master-admin";
apiAdminMasterBusinessFieldById,
apiAdminMasterBusinessFieldUpdate,
} from "@/service/api-admin/api-master-admin";
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 { Switch } from "react-native-paper"; import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminAppInformation_BusinessFieldDetail() { export default function AdminAppInformation_BusinessFieldDetail() {
const { id } = useLocalSearchParams(); const { id } = useLocalSearchParams();
@@ -33,8 +33,11 @@ export default function AdminAppInformation_BusinessFieldDetail() {
try { try {
const response = await apiAdminMasterBusinessFieldById({ const response = await apiAdminMasterBusinessFieldById({
id: id as string, id: id as string,
category: "all",
}); });
console.log("Response >>", JSON.stringify(response, null, 2));
setData(response.data); setData(response.data);
} catch (error) { } catch (error) {
console.log("[ERROR]", error); console.log("[ERROR]", error);
@@ -42,73 +45,89 @@ export default function AdminAppInformation_BusinessFieldDetail() {
} }
}; };
const handlerSubmit = async () => {
if (!data.name) {
Toast.show({
type: "error",
text1: "Lengkapi Data",
});
return;
}
try {
setIsLoading(true);
const response = await apiAdminMasterBusinessFieldUpdate({
id: id as string,
data: data,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal update data",
});
return;
}
Toast.show({
type: "success",
text1: "Data berhasil di update",
});
router.back();
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
disabled={!data?.name}
isLoading={isLoading}
onPress={() => handlerSubmit()}
>
Update
</ButtonCustom>
</BoxButtonOnFooter>
);
return ( return (
<> <>
<ViewWrapper footerComponent={buttonSubmit}> <ViewWrapper>
<StackCustom> <StackCustom>
<AdminBackButtonAntTitle title="Update Bidang Bisnis" /> <AdminBackButtonAntTitle title="Detail Bidang & Sub Bidang" />
<TextInputCustom {!data ? (
label="Nama Bidang Bisnis" <LoaderCustom />
placeholder="Masukan Nama Bidang Bisnis" ) : (
required <StackCustom gap={"xs"}>
value={data?.name} <TextCustom bold>Nama Bidang</TextCustom>
onChangeText={(value) => setData({ ...data, name: value })} <Spacing height={5} />
<BaseBox>
<StackCustom gap={"xs"}>
<TextCustom bold>
Status: {data?.bidang?.active ? "Aktif" : "Tidak Aktif"}
</TextCustom>
<GridSpan_NewComponent
span1={10}
span2={2}
text1={
<TextCustom bold size={"large"}>
{data?.bidang?.name}
</TextCustom>
}
text2={
<CenterCustom>
<ActionIcon
icon={<IconEdit size={16} color={MainColor.black} />}
onPress={() =>
router.push(
`/admin/app-information/business-field/${id}/bidang-update`
)
}
/> />
</CenterCustom>
}
/>
</StackCustom>
</BaseBox>
{/* <Divider /> */}
<Spacing height={5} />
<TextCustom>Status Aktivasi</TextCustom> <TextCustom bold>Sub Bidang Bisnis</TextCustom>
<Switch <Spacing height={5} />
color={MainColor.yellow}
value={data?.active} {data?.subBidang?.map((item: any, index: number) => (
onValueChange={(value) => setData({ ...data, active: value })} <BaseBox key={index}>
<StackCustom gap={0}>
<TextCustom bold>
Status: {item?.isActive ? "Aktif" : "Tidak Aktif"}
</TextCustom>
<GridSpan_NewComponent
span1={10}
span2={2}
text1={
<TextCustom bold size={"large"}>
{item.name}
</TextCustom>
}
text2={
<CenterCustom>
<ActionIcon
icon={
<IconEdit size={16} color={MainColor.black} />
}
onPress={() =>
router.push(
`/admin/app-information/business-field/${item?.id}/sub-bidang-update`
)
}
/> />
</CenterCustom>
}
/>
</StackCustom>
</BaseBox>
))}
</StackCustom>
)}
{/* <TextCustom>{JSON.stringify(data, null, 2)}</TextCustom> */}
</StackCustom> </StackCustom>
</ViewWrapper> </ViewWrapper>
</> </>

View File

@@ -0,0 +1,135 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { MainColor } from "@/constants/color-palet";
import {
apiAdminMasterBusinessFieldById,
apiAdminMasterBusinessFieldUpdate,
} from "@/service/api-admin/api-master-admin";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import { Switch } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminAppInformation_BusinessFieldDetail() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [isLoading, setIsLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadDetail();
}, [id])
);
const onLoadDetail = async () => {
try {
const response = await apiAdminMasterBusinessFieldById({
id: id as string,
category: "sub-bidang",
subBidangId: id as string,
});
console.log("Response >>", JSON.stringify(response, null, 2));
setData(response.data);
} catch (error) {
console.log("[ERROR]", error);
setData(null);
}
};
const handlerSubmit = async () => {
if (!data.name) {
Toast.show({
type: "error",
text1: "Lengkapi Data",
});
return;
}
try {
setIsLoading(true);
const response = await apiAdminMasterBusinessFieldUpdate({
id: id as string,
data: data,
category: "sub-bidang",
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal update data",
});
return;
}
Toast.show({
type: "success",
text1: "Data berhasil di update",
});
router.back();
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
disabled={!data?.name}
isLoading={isLoading}
onPress={() => handlerSubmit()}
>
Update
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper footerComponent={buttonSubmit}>
<StackCustom>
<AdminBackButtonAntTitle title="Update Bidang Bisnis" />
<TextInputCustom
label="Nama Bidang Bisnis"
placeholder="Masukan Nama Bidang Bisnis"
required
value={data?.name}
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?.isActive}
onValueChange={(value) => setData({ ...data, isActive: value })}
/>
</StackCustom>
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -1,40 +1,81 @@
import { import {
ActionIcon,
BoxButtonOnFooter, BoxButtonOnFooter,
ButtonCustom, ButtonCustom,
CenterCustom,
Grid,
Spacing,
StackCustom, StackCustom,
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 { ICON_SIZE_XLARGE } from "@/constants/constans-value";
import { apiAdminMasterBusinessFieldCreate } from "@/service/api-admin/api-master-admin"; import { apiAdminMasterBusinessFieldCreate } from "@/service/api-admin/api-master-admin";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router"; import { router } from "expo-router";
import _ from "lodash";
import { useState } from "react"; import { useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
export default function AdminAppInformation_BusinessFieldCreate() { export default function AdminAppInformation_BusinessFieldCreate() {
const [data, setData] = useState<any>({ const [isLoading, setIsLoading] = useState(false);
const [bidang, setBidang] = useState<any>({
name: "", name: "",
}); });
const [isLoading, setIsLoading] = useState(false); const [subBidang, setSubBidang] = useState<any[]>([
{
name: "",
},
]);
const handlerSubmit = async () => { const handlerSubmit = async () => {
if (!data.name) { if (!bidang.name) {
Toast.show({ Toast.show({
type: "error", type: "error",
text1: "Lengkapi Data", text1: "Lengkapi Data",
}); });
return; return;
} }
if (subBidang[0].name === "") {
Toast.show({
type: "error",
text1: "Lengkapi Sub Bidang",
});
return;
}
try { try {
setIsLoading(true); setIsLoading(true);
const response = await apiAdminMasterBusinessFieldCreate({ data: data });
const newData = {
bidang: bidang,
subBidang: subBidang,
};
console.log("[DATA]", newData);
const response = await apiAdminMasterBusinessFieldCreate({
data: newData,
});
console.log("[RESPONSE]", response);
if (response.success) { if (response.success) {
Toast.show({ Toast.show({
type: "success", type: "success",
text1: "Data berhasil di tambah", text1: "Data berhasil di tambah",
}); });
router.back(); // router.back();
} else {
Toast.show({
type: "error",
text1: "Gagal tambah data",
});
} }
} catch (error) { } catch (error) {
console.log("[ERROR]", error); console.log("[ERROR]", error);
@@ -50,6 +91,7 @@ export default function AdminAppInformation_BusinessFieldCreate() {
const buttonSubmit = ( const buttonSubmit = (
<BoxButtonOnFooter> <BoxButtonOnFooter>
<ButtonCustom <ButtonCustom
disabled={subBidang[0].name === ""}
onPress={() => handlerSubmit()} onPress={() => handlerSubmit()}
isLoading={isLoading} isLoading={isLoading}
> >
@@ -60,16 +102,70 @@ export default function AdminAppInformation_BusinessFieldCreate() {
return ( return (
<> <>
<ViewWrapper footerComponent={buttonSubmit}> <ViewWrapper footerComponent={buttonSubmit}>
<StackCustom> <StackCustom gap={"xs"}>
<AdminBackButtonAntTitle title="Tambah Bidang Bisnis" /> <AdminBackButtonAntTitle title="Tambah Bidang Bisnis" />
<TextInputCustom <TextInputCustom
label="Nama Bidang Bisnis" label="Nama Bidang Bisnis"
placeholder="Masukan Nama Bidang Bisnis" placeholder="Masukan Nama Bidang Bisnis"
required required
value={data.name} value={bidang.name}
onChangeText={(value) => setData({ ...data, name: value })} onChangeText={(value) => setBidang({ ...bidang, name: value })}
/> />
<Divider />
<Spacing height={5} />
{subBidang.map((item, index) => (
<TextInputCustom
key={index}
label="Nama Sub Bidang"
placeholder="Masukan Nama Sub Bidang"
required
value={item.name}
onChangeText={(value) => {
const list = _.clone(subBidang);
list[index].name = value;
setSubBidang(list);
}}
/>
))}
<CenterCustom>
<View
style={{ flexDirection: "row", alignItems: "center", gap: 10 }}
>
<ActionIcon
onPress={() => {
setSubBidang([...subBidang, { name: "" }]);
}}
icon={
<Ionicons
name="add-circle-outline"
size={ICON_SIZE_XLARGE}
color={MainColor.black}
/>
}
size="xl"
/>
<ActionIcon
disabled={subBidang.length <= 1}
onPress={() => {
const list = _.clone(subBidang);
list.pop();
setSubBidang(list);
}}
icon={
<Ionicons
name="remove-circle-outline"
size={ICON_SIZE_XLARGE}
color={MainColor.black}
/>
}
size="xl"
/>
</View>
</CenterCustom>
</StackCustom> </StackCustom>
</ViewWrapper> </ViewWrapper>
</> </>

View File

@@ -75,7 +75,7 @@ const listPage = [
}, },
{ {
id: "2", id: "2",
label: "Bidang Bisnis", label: "Bidang & Sub Bidang",
value: "business", value: "business",
}, },
{ {

View File

@@ -2,11 +2,13 @@
import { import {
BaseBox, BaseBox,
Grid, Grid,
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 { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import { apiAdminCollaborationGetById } from "@/service/api-admin/api-admin-collaboration"; import { apiAdminCollaborationGetById } from "@/service/api-admin/api-admin-collaboration";
import { useFocusEffect, useLocalSearchParams } from "expo-router"; import { useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
@@ -28,6 +30,8 @@ export default function AdminCollaborationGroup() {
category: "group", category: "group",
}); });
console.log("[DATA]", JSON.stringify(response.data, null, 2));
if (response.success) { if (response.success) {
setData(response.data); setData(response.data);
} }
@@ -59,38 +63,33 @@ export default function AdminCollaborationGroup() {
))} ))}
</StackCustom> </StackCustom>
</BaseBox> </BaseBox>
<TextCustom bold>Anggota</TextCustom>
<Spacing height={5}/>
<BaseBox> <BaseBox>
<StackCustom> <StackCustom>
<TextCustom align="center">Anggota</TextCustom>
<Grid>
<Grid.Col span={6} style={{ justifyContent: "center", paddingRight: 10 }}>
<TextCustom bold>Nomor</TextCustom>
</Grid.Col>
<Grid.Col span={6} style={{ justifyContent: "center" }}>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
</Grid>
{data?.ProjectCollaboration_AnggotaRoomChat?.map( {data?.ProjectCollaboration_AnggotaRoomChat?.map(
(item: any, index: number) => ( (item: any, index: number) => (
<StackCustom key={index} gap={0}> <Grid key={index}>
<Grid> <Grid.Col span={6} style={{ justifyContent: "center", paddingRight: 10 }}>
<Grid.Col <TextCustom bold truncate>+{item?.User?.nomor || "-"}</TextCustom>
span={4}
style={{ justifyContent: "center", paddingRight: 10 }}
>
<TextCustom bold>Nama</TextCustom>
</Grid.Col> </Grid.Col>
<Grid.Col span={8} style={{ justifyContent: "center" }}> <Grid.Col span={6} style={{ justifyContent: "center" }}>
<TextCustom> <TextCustom bold>
{item?.User?.Profile?.name || "-"} {item?.User?.username || "-"}
</TextCustom> </TextCustom>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
<Grid>
<Grid.Col
span={4}
style={{ justifyContent: "center", paddingRight: 10 }}
>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
<Grid.Col span={8} style={{ justifyContent: "center" }}>
<TextCustom>{item?.User?.username || "-"}</TextCustom>
</Grid.Col>
</Grid>
</StackCustom>
) )
)} )}
</StackCustom> </StackCustom>

View File

@@ -1,17 +1,14 @@
import { import {
ActionIcon, ClickableCustom,
LoaderCustom, LoaderCustom,
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 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 { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration"; import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
import { Octicons } from "@expo/vector-icons";
import { router, useFocusEffect } from "expo-router"; import { router, useFocusEffect } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
@@ -51,10 +48,19 @@ export default function AdminCollaborationGroup() {
<StackCustom> <StackCustom>
<AdminComp_BoxTitle title="Group" /> <AdminComp_BoxTitle title="Group" />
<> <>
<AdminTitleTable <GridSpan_NewComponent
title1="Aksi" span1={6}
title2="Jumlah peserta" span2={6}
title3="Nama group" text1={
<TextCustom bold truncate align="center">
Jumlah Anggota
</TextCustom>
}
text2={
<TextCustom bold truncate>
Nama Group
</TextCustom>
}
/> />
<Divider /> <Divider />
@@ -67,31 +73,27 @@ export default function AdminCollaborationGroup() {
) : ( ) : (
list?.map((item: any, index: number) => ( list?.map((item: any, index: number) => (
<View key={index}> <View key={index}>
<AdminTableValue <ClickableCustom
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => { onPress={() => {
router.push(`/admin/collaboration/${item.id}/group`); router.push(`/admin/collaboration/${item.id}/group`);
}} }}
/> >
} <GridSpan_NewComponent
value2={ span1={6}
<TextCustom truncate={1}> span2={6}
text1={
<TextCustom truncate={1} align="center">
{item?.ProjectCollaboration_AnggotaRoomChat?.length || {item?.ProjectCollaboration_AnggotaRoomChat?.length ||
"-"} "-"}
</TextCustom> </TextCustom>
} }
value3={ text2={
<TextCustom truncate={2}>{item?.name || "-"}</TextCustom> <TextCustom truncate={2}>
{item?.name || "-"}
</TextCustom>
} }
/> />
</ClickableCustom>
</View> </View>
)) ))
)} )}

View File

@@ -1,6 +1,8 @@
import { import {
ActionIcon, ActionIcon,
ClickableCustom,
LoaderCustom, LoaderCustom,
Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
@@ -9,6 +11,7 @@ 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 AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage"; import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value"; import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration"; import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
import { Octicons } from "@expo/vector-icons"; import { Octicons } from "@expo/vector-icons";
@@ -51,11 +54,7 @@ export default function AdminCollaborationPublish() {
<StackCustom> <StackCustom>
<AdminComp_BoxTitle title="Publish" /> <AdminComp_BoxTitle title="Publish" />
<AdminTitleTable <GridSpan_NewComponent text1={<TextCustom bold>Username</TextCustom>} text2={<TextCustom bold>Judul Proyek</TextCustom>} />
title1="Aksi"
title2="Username"
title3="Judul Proyek"
/>
{/* <Spacing height={10} /> */} {/* <Spacing height={10} /> */}
<Divider /> <Divider />
@@ -68,32 +67,26 @@ export default function AdminCollaborationPublish() {
) : ( ) : (
list?.map((item: any, index: number) => ( list?.map((item: any, index: number) => (
<View key={index}> <View key={index}>
<AdminTableValue <ClickableCustom
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => { onPress={() => {
router.push(`/admin/collaboration/${item?.id}/publish`); router.push(`/admin/collaboration/${item?.id}/publish`);
}} }}
/> >
} <GridSpan_NewComponent
value2={ text1={
<TextCustom align="center" truncate={1}> <TextCustom truncate={1}>
{item?.Author?.username || "-"}{" "} {item?.Author?.username || "-"}{" "}
</TextCustom> </TextCustom>
} }
value3={ text2={
<TextCustom align="center" truncate={2}> <TextCustom truncate={2}>
{item?.title || "-"} {item?.title || "-"}
</TextCustom> </TextCustom>
} }
/> />
</ClickableCustom>
<Spacing height={8}/>
<Divider/>
</View> </View>
)) ))
)} )}

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
component1={
<CenterCustom>
<ActionIcon
icon={
<IconEdit size={ICON_SIZE_BUTTON} color="black" />
}
onPress={() => { onPress={() => {
router.push(`/admin/donation/category-update?id=${index}`); router.push(`/admin/donation/category-update?id=${item.id}`);
}} }}
/> key={index}
>
<GridSpan_4_8
label={
<CenterCustom>
<BadgeCustom
color={colorActivationForBadge({
status: item.active,
})}
>
{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}
/> />
} {/* <Grid containerStyle={{ paddingBottom: 10 }}>
component3={<TextCustom bold>{item.label}</TextCustom>} <Grid.Col span={4} style={{ paddingLeft: 10 }}>
/> <CenterCustom>
<Spacing height={10} /> <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

@@ -16,7 +16,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 } 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" }}>
{startDate &&
startDate.subtract(1, "hour").diff(dayjs()) < 0 ? (
<BadgeCustom <BadgeCustom
style={{ alignSelf: "flex-end" }} style={{ alignSelf: "flex-end" }}
color={item?.isPresent ? "green" : "red"} color={item?.isPresent ? "green" : "red"}
> >
{item?.isPresent ? "Hadir" : "Tidak Hadir"} {item?.isPresent ? "Hadir" : "Tidak Hadir"}
</BadgeCustom> </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={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => { onPress={() => {
router.push(`/admin/event/${item.id}/${status}`); router.push(`/admin/event/${item.id}/${status}`);
}} }}
/> >
} <AdminTableValue
value2={ key={index}
value1={
<TextCustom truncate={1}> <TextCustom truncate={1}>
{item?.Author?.username || "-"} {item?.Author?.username || "-"}
</TextCustom> </TextCustom>
// <ActionIcon
// icon={
// <Octicons
// name="eye"
// size={ICON_SIZE_BUTTON}
// color="black"
// />
// }
// onPress={() => {
// router.push(`/admin/event/${item.id}/${status}`);
// }}
// />
} }
value3={ value2={
<TextCustom align="center" truncate={2}> <TextCustom truncate={1}>
{item?.title || "-"} {dateTimeView({ date: item?.tanggal })}
</TextCustom> </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

@@ -3,6 +3,7 @@ import {
ActionIcon, ActionIcon,
AlertDefaultSystem, AlertDefaultSystem,
BaseBox, BaseBox,
CenterCustom,
DrawerCustom, DrawerCustom,
LoaderCustom, LoaderCustom,
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
@@ -16,7 +17,8 @@ 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 { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
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 {
@@ -27,6 +29,7 @@ import {
import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper"; import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
@@ -95,24 +98,24 @@ export default function AdminForumReportComment() {
> >
<BaseBox> <BaseBox>
<StackCustom gap={"sm"}> <StackCustom gap={"sm"}>
<GridDetail_4_8 <GridSpan_NewComponent
label={<TextCustom bold>Username</TextCustom>} text1={<TextCustom bold>Username</TextCustom>}
value={<TextCustom>{data?.Author?.username || "-"}</TextCustom>} text2={<TextCustom>{data?.Author?.username || "-"}</TextCustom>}
/> />
<GridDetail_4_8 <GridSpan_NewComponent
label={<TextCustom bold>Komentar</TextCustom>} text1={<TextCustom bold>Komentar</TextCustom>}
value={<TextCustom>{data?.komentar || "-"}</TextCustom>} text2={<TextCustom>{data?.komentar || "-"}</TextCustom>}
/> />
</StackCustom> </StackCustom>
</BaseBox> </BaseBox>
<AdminComp_BoxTitle title="Daftar Report Komentar" /> <AdminComp_BoxTitle title="Daftar Report Komentar" />
<StackCustom> <StackCustom gap={"sm"}>
<AdminTitleTable <GridSpan_NewComponent
title1="Aksi" text1={<TextCustom bold align="center">Aksi</TextCustom>}
title2="Pelapor" text2={<TextCustom bold>Pelapor</TextCustom>}
title3="Kategori Report" text3={<TextCustom bold>Kategori Report</TextCustom>}
/> />
<Divider /> <Divider />
{loadList ? ( {loadList ? (
@@ -123,9 +126,10 @@ export default function AdminForumReportComment() {
</TextCustom> </TextCustom>
) : ( ) : (
listReport?.map((item: any, index: number) => ( listReport?.map((item: any, index: number) => (
<AdminTableValue <View key={index}>
key={index} <GridSpan_NewComponent
value1={ text1={
<CenterCustom>
<ActionIcon <ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />} icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => { onPress={() => {
@@ -134,23 +138,27 @@ export default function AdminForumReportComment() {
id: item.id, id: item.id,
username: item.User?.username, username: item.User?.username,
kategori: item.ForumMaster_KategoriReport?.title, kategori: item.ForumMaster_KategoriReport?.title,
keterangan: item.ForumMaster_KategoriReport?.deskripsi, keterangan:
item.ForumMaster_KategoriReport?.deskripsi,
deskripsi: item.deskripsi, deskripsi: item.deskripsi,
}); });
}} }}
/> />
</CenterCustom>
} }
value2={ text2={
<TextCustom truncate={1}> <TextCustom truncate={1}>
{item?.User?.username || "-"} {item?.User?.username || "-"}
</TextCustom> </TextCustom>
} }
value3={ text3={
<TextCustom truncate={2} align="center"> <TextCustom truncate={2}>
{item?.ForumMaster_KategoriReport?.title || "-"} {item?.ForumMaster_KategoriReport?.title || "-"}
</TextCustom> </TextCustom>
} }
/> />
<Divider />
</View>
)) ))
)} )}
</StackCustom> </StackCustom>
@@ -208,20 +216,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 +239,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

@@ -4,6 +4,7 @@ import {
AlertDefaultSystem, AlertDefaultSystem,
BadgeCustom, BadgeCustom,
BaseBox, BaseBox,
CenterCustom,
DrawerCustom, DrawerCustom,
LoaderCustom, LoaderCustom,
MenuDrawerDynamicGrid, MenuDrawerDynamicGrid,
@@ -17,7 +18,8 @@ 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 { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
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 {
@@ -28,6 +30,7 @@ import {
import { router, useFocusEffect, useLocalSearchParams } from "expo-router"; import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper"; import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
@@ -95,14 +98,14 @@ export default function AdminForumReportPosting() {
> >
<BaseBox> <BaseBox>
<StackCustom gap={"sm"}> <StackCustom gap={"sm"}>
<GridDetail_4_8 <GridSpan_NewComponent
label={<TextCustom bold>Username</TextCustom>} text1={<TextCustom bold>Username</TextCustom>}
value={<TextCustom>{data?.Author?.username || "-"}</TextCustom>} text2={<TextCustom>{data?.Author?.username || "-"}</TextCustom>}
/> />
<GridDetail_4_8 <GridSpan_NewComponent
label={<TextCustom bold>Status</TextCustom>} text1={<TextCustom bold>Status</TextCustom>}
value={ text2={
data && data?.ForumMaster_StatusPosting?.status ? ( data && data?.ForumMaster_StatusPosting?.status ? (
<BadgeCustom <BadgeCustom
color={ color={
@@ -121,19 +124,23 @@ export default function AdminForumReportPosting() {
} }
/> />
<GridDetail_4_8 <GridSpan_NewComponent
label={<TextCustom bold>Postingan</TextCustom>} text1={<TextCustom bold>Postingan</TextCustom>}
value={<TextCustom>{data?.diskusi || "-"}</TextCustom>} text2={<TextCustom>{data?.diskusi || "-"}</TextCustom>}
/> />
</StackCustom> </StackCustom>
</BaseBox> </BaseBox>
<AdminComp_BoxTitle title="Daftar Report Posting" /> <AdminComp_BoxTitle title="Daftar Report Posting" />
<StackCustom gap={"sm"}> <StackCustom gap={"sm"}>
<AdminTitleTable <GridSpan_NewComponent
title1="Aksi" text1={
title2="Pelapor" <TextCustom bold align="center">
title3="Kategori Report" Aksi
</TextCustom>
}
text2={<TextCustom bold>Pelapor</TextCustom>}
text3={<TextCustom bold>Kategori Report</TextCustom>}
/> />
<Divider /> <Divider />
{loadListReport ? ( {loadListReport ? (
@@ -144,34 +151,41 @@ export default function AdminForumReportPosting() {
</TextCustom> </TextCustom>
) : ( ) : (
listReport?.map((item: any, index: number) => ( listReport?.map((item: any, index: number) => (
<AdminTableValue <View key={index}>
key={index} <GridSpan_NewComponent
value1={ text1={
<CenterCustom>
<ActionIcon <ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />} icon={
<IconView size={ICON_SIZE_BUTTON} color="black" />
}
onPress={() => { onPress={() => {
setOpenDrawerAction(true); setOpenDrawerAction(true);
setSelectedReport({ setSelectedReport({
id: item?.id, id: item?.id,
username: item?.User?.username, username: item?.User?.username,
kategori: item?.ForumMaster_KategoriReport?.title, kategori: item?.ForumMaster_KategoriReport?.title,
keterangan: item?.ForumMaster_KategoriReport?.deskripsi, keterangan:
item?.ForumMaster_KategoriReport?.deskripsi,
deskripsi: item?.deskripsi, deskripsi: item?.deskripsi,
}); });
}} }}
/> />
</CenterCustom>
} }
value2={ text2={
<TextCustom truncate={1}> <TextCustom truncate>
{item?.User?.username || "-"} {item?.User?.username || "-"}
</TextCustom> </TextCustom>
} }
value3={ text3={
<TextCustom truncate={2} align="center"> <TextCustom truncate={2}>
{item?.ForumMaster_KategoriReport?.title || "-"} {item?.ForumMaster_KategoriReport?.title || "-"}
</TextCustom> </TextCustom>
} }
/> />
<Divider />
</View>
)) ))
)} )}
</StackCustom> </StackCustom>
@@ -229,20 +243,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 +266,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

@@ -1,22 +1,22 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { import {
ActionIcon, ClickableCustom,
LoaderCustom, LoaderCustom,
SearchInput, SearchInput,
Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
} from "@/components"; } from "@/components";
import { IconView } from "@/components/_Icon/IconComponent";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage"; import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
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 { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
import { apiAdminForum } from "@/service/api-admin/api-admin-forum"; import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect } from "expo-router"; import { router, useFocusEffect } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import React, { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper"; import { Divider } from "react-native-paper";
export default function AdminForumPosting() { export default function AdminForumPosting() {
@@ -38,6 +38,8 @@ export default function AdminForumPosting() {
search: search, search: search,
}); });
console.log("DATA", JSON.stringify(response, null, 2));
if (response.success) { if (response.success) {
setList(response.data); setList(response.data);
} }
@@ -51,7 +53,7 @@ export default function AdminForumPosting() {
const rightComponent = ( const rightComponent = (
<SearchInput <SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }} containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari" placeholder="Cari postingan"
value={search} value={search}
onChangeText={setSearch} onChangeText={setSearch}
/> />
@@ -61,9 +63,15 @@ export default function AdminForumPosting() {
<> <>
<ViewWrapper headerComponent={<AdminTitlePage title="Forum" />}> <ViewWrapper headerComponent={<AdminTitlePage title="Forum" />}>
<AdminComp_BoxTitle title={"Posting"} rightComponent={rightComponent} /> <AdminComp_BoxTitle title={"Posting"} rightComponent={rightComponent} />
<StackCustom> <GridSpan_NewComponent
<AdminTitleTable title1="Aksi" title2="Username" title3="Postingan" /> text1={<TextCustom bold truncate>Username</TextCustom>}
text2={<TextCustom bold truncate> Postingan</TextCustom>}
text3={<TextCustom bold align="center" truncate> Report Posting</TextCustom>}
text4={<TextCustom bold align="center" truncate> Komentar</TextCustom>}
/>
<Divider /> <Divider />
<Spacing />
<StackCustom>
{loadList ? ( {loadList ? (
<LoaderCustom /> <LoaderCustom />
) : _.isEmpty(list) ? ( ) : _.isEmpty(list) ? (
@@ -72,25 +80,38 @@ export default function AdminForumPosting() {
</TextCustom> </TextCustom>
) : ( ) : (
list?.map((item: any, index: number) => ( list?.map((item: any, index: number) => (
<AdminTableValue <View key={index}>
key={index} <ClickableCustom
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => { onPress={() => {
router.push(`/admin/forum/${item?.id}`); router.push(`/admin/forum/${item.id}`);
}} }}
/> >
} <GridSpan_NewComponent
value2={ text1={
<TextCustom truncate={1}> <TextCustom truncate={1}>
{item?.Author?.username || "-"} {item?.Author?.username || "-"}
</TextCustom> </TextCustom>
} }
value3={ text2={
<TextCustom truncate={2}>{item?.diskusi || "-"}</TextCustom> <TextCustom truncate>
{item?.diskusi || "-"}
</TextCustom>
}
text3={
<TextCustom align="center" truncate={2}>
{item?.reportPosting || "-"}
</TextCustom>
}
text4={
<TextCustom align="center" truncate={2}>
{item?.komentar || "-"}
</TextCustom>
} }
/> />
</ClickableCustom>
<Divider />
</View>
)) ))
)} )}
</StackCustom> </StackCustom>

View File

@@ -1,8 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { import {
ActionIcon, ActionIcon,
ClickableCustom,
LoaderCustom, LoaderCustom,
SearchInput, SearchInput,
Spacing,
StackCustom, StackCustom,
TextCustom, TextCustom,
ViewWrapper, ViewWrapper,
@@ -12,12 +14,14 @@ 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 AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage"; import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
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 { apiAdminForum } from "@/service/api-admin/api-admin-forum"; import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect } from "expo-router"; import { router, useFocusEffect } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper"; import { Divider } from "react-native-paper";
export default function AdminForumReportComment() { export default function AdminForumReportComment() {
@@ -67,13 +71,26 @@ export default function AdminForumReportComment() {
rightComponent={rightComponent} rightComponent={rightComponent}
/> />
<StackCustom gap={"sm"}> <GridSpan_NewComponent
<AdminTitleTable text1={
title1="Aksi" <TextCustom bold truncate>
title2="Pelapor" Pelapor
title3="Jenis Laporan" </TextCustom>
}
text2={
<TextCustom bold truncate>
Komentar
</TextCustom>
}
text3={
<TextCustom bold truncate>
Jenis Laporan
</TextCustom>
}
/> />
<Divider /> <Divider />
<Spacing />
<StackCustom gap={"lg"}>
{loadList ? ( {loadList ? (
<LoaderCustom /> <LoaderCustom />
) : _.isEmpty(listData) ? ( ) : _.isEmpty(listData) ? (
@@ -82,34 +99,35 @@ export default function AdminForumReportComment() {
</TextCustom> </TextCustom>
) : ( ) : (
listData?.map((item: any, index: number) => ( listData?.map((item: any, index: number) => (
<AdminTableValue <View key={index}>
key={index} <ClickableCustom
value1={
<ActionIcon
icon={
<IconView
size={ICON_SIZE_BUTTON}
color={MainColor.black}
/>
}
onPress={() => { onPress={() => {
router.push( router.push(
`/admin/forum/${item?.Forum_Komentar?.id}/list-report-comment` `/admin/forum/${item?.Forum_Komentar?.id}/list-report-comment`
); );
}} }}
/> >
} <GridSpan_NewComponent
value2={ text1={
<TextCustom truncate={1}> <TextCustom truncate={1}>
{item?.User?.username || "-"} {item?.User?.username || "-"}
</TextCustom> </TextCustom>
} }
value3={ text2={
<TextCustom truncate={2} align="center"> <TextCustom truncate={2}>
{item?.Forum_Komentar?.komentar || "-"}
</TextCustom>
}
text3={
<TextCustom truncate={2}>
{item?.ForumMaster_KategoriReport?.title || "-"} {item?.ForumMaster_KategoriReport?.title || "-"}
</TextCustom> </TextCustom>
} }
/> />
</ClickableCustom>
<Spacing />
<Divider />
</View>
)) ))
)} )}
</StackCustom> </StackCustom>

View File

@@ -1,24 +1,27 @@
/* eslint-disable react-hooks/exhaustive-deps */ /* eslint-disable react-hooks/exhaustive-deps */
import { import {
ActionIcon, ActionIcon,
ClickableCustom,
Divider, Divider,
LoaderCustom, LoaderCustom,
SearchInput, SearchInput,
StackCustom, StackCustom,
TextCustom, TextCustom,
ViewWrapper ViewWrapper,
} from "@/components"; } from "@/components";
import { IconView } from "@/components/_Icon/IconComponent"; import { IconView } from "@/components/_Icon/IconComponent";
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 AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage"; import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
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 { apiAdminForum } from "@/service/api-admin/api-admin-forum"; import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect } from "expo-router"; import { router, useFocusEffect } from "expo-router";
import _ from "lodash"; import _ from "lodash";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { View } from "react-native";
export default function AdminForumReportPosting() { export default function AdminForumReportPosting() {
const [listData, setListData] = useState<any[] | null>(null); const [listData, setListData] = useState<any[] | null>(null);
@@ -67,10 +70,20 @@ export default function AdminForumReportPosting() {
rightComponent={rightComponent} rightComponent={rightComponent}
/> />
<StackCustom gap={"sm"}> <GridSpan_NewComponent
<AdminTitleTable title1="Aksi" title2="Pelapor" title3="Postingan" /> text1={
<TextCustom bold truncate>
Username
</TextCustom>
}
text2={
<TextCustom bold truncate>
Postingan
</TextCustom>
}
/>
<Divider /> <Divider />
<StackCustom>
{loadList ? ( {loadList ? (
<LoaderCustom /> <LoaderCustom />
) : _.isEmpty(listData) ? ( ) : _.isEmpty(listData) ? (
@@ -79,34 +92,29 @@ export default function AdminForumReportPosting() {
</TextCustom> </TextCustom>
) : ( ) : (
listData?.map((item: any, index: number) => ( listData?.map((item: any, index: number) => (
<AdminTableValue <View key={index}>
key={index} <ClickableCustom
value1={
<ActionIcon
icon={
<IconView
size={ICON_SIZE_BUTTON}
color={MainColor.black}
/>
}
onPress={() => { onPress={() => {
router.push( router.push(
`/admin/forum/${item?.Forum_Posting?.id}/list-report-posting` `/admin/forum/${item?.Forum_Posting?.id}/list-report-posting`
); );
}} }}
/> >
} <GridSpan_NewComponent
value2={ text1={
<TextCustom truncate={1}> <TextCustom truncate={1}>
{item?.User?.username || "-"} {item?.User?.username || "-"}
</TextCustom> </TextCustom>
} }
value3={ text2={
<TextCustom truncate={2} align="center"> <TextCustom truncate={1}>
{item?.Forum_Posting?.diskusi || "-"} {item?.Forum_Posting?.diskusi || "-"}
</TextCustom> </TextCustom>
} }
/> />
</ClickableCustom>
<Divider />
</View>
)) ))
)} )}
</StackCustom> </StackCustom>

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

@@ -76,7 +76,7 @@ export default function SuperAdmin_ListUser() {
</TextCustom> </TextCustom>
} }
component2={ component2={
<TextCustom align="center" bold> <TextCustom bold>
Username Username
</TextCustom> </TextCustom>
} }

View File

@@ -73,11 +73,7 @@ export default function AdminUserAccess() {
Aksi Aksi
</TextCustom> </TextCustom>
} }
component2={ component2={<TextCustom bold>Username</TextCustom>}
<TextCustom align="center" bold>
Username
</TextCustom>
}
component3={ component3={
<TextCustom align="center" bold> <TextCustom align="center" bold>
Status Akses Status Akses

View File

@@ -13,7 +13,7 @@ import {
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

@@ -103,7 +103,7 @@ export default function AdminVotingStatus() {
</TextCustom> </TextCustom>
} }
value3={ value3={
<TextCustom align="center" truncate={2}> <TextCustom truncate={2}>
{item?.title || "-"} {item?.title || "-"}
</TextCustom> </TextCustom>
} }

View File

@@ -91,7 +91,7 @@ export default function AdminVotingHistory() {
} }
value2={<TextCustom truncate={1}>{item?.Author?.username || "-"}</TextCustom>} value2={<TextCustom truncate={1}>{item?.Author?.username || "-"}</TextCustom>}
value3={ value3={
<TextCustom align="center" truncate={2}> <TextCustom truncate={2}>
{item?.title || "-"} {item?.title || "-"}
</TextCustom> </TextCustom>
} }

View File

@@ -74,7 +74,7 @@ export default function TermsAgreement() {
<> <>
<Stack.Screen <Stack.Screen
options={{ options={{
title: "Terms Agreement", title: "Terms & Conditions",
}} }}
/> />
<ViewWrapper footerComponent={footerComponent}> <ViewWrapper footerComponent={footerComponent}>
@@ -87,6 +87,7 @@ export default function TermsAgreement() {
alignItems: "center", alignItems: "center",
marginTop: 16, marginTop: 16,
marginBottom: 16, marginBottom: 16,
paddingInline: 10,
}} }}
> >
<CheckboxCustom value={term} onChange={() => setTerm(!term)} /> <CheckboxCustom value={term} onChange={() => setTerm(!term)} />

459
bun.lock

File diff suppressed because it is too large Load Diff

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

@@ -32,9 +32,10 @@ const FloatingButton: React.FC<FloatingButtonProps> = ({
const styles = StyleSheet.create({ const styles = StyleSheet.create({
fab: { fab: {
position: "absolute", position: "absolute",
margin: 16, margin: "auto",
right: 0, right: 0,
bottom: 0, // bottom: 10,
top: -20,
backgroundColor: AccentColor.softblue, // Warna Twitter biru backgroundColor: AccentColor.softblue, // Warna Twitter biru
borderRadius: 50, borderRadius: 50,
borderColor: AccentColor.blue, borderColor: AccentColor.blue,

View File

@@ -53,6 +53,8 @@ const DateTimeInput_Android: React.FC<DateTimeInputProps> = ({
const [selectedDate, setSelectedDate] = useState<Date>(value as any); const [selectedDate, setSelectedDate] = useState<Date>(value as any);
const [selectedTime, setSelectedTime] = useState<Date>(value as any); const [selectedTime, setSelectedTime] = useState<Date>(value as any);
console.log("Date Android", value)
// Fungsi untuk menggabungkan tanggal dan waktu // Fungsi untuk menggabungkan tanggal dan waktu
const combineDateAndTime = useCallback((date: Date, time: Date): Date => { const combineDateAndTime = useCallback((date: Date, time: Date): Date => {
const combined = new Date(date); const combined = new Date(date);

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

@@ -23,6 +23,9 @@ const DateTimePickerCustom: React.FC<Props> = ({
minimumDate, minimumDate,
disabled = false, disabled = false,
}) => { }) => {
console.log("Date Android Comp", value)
return ( return (
<> <>
{Platform.OS === "ios" ? ( {Platform.OS === "ios" ? (
@@ -47,6 +50,9 @@ const DateTimePickerCustom: React.FC<Props> = ({
maximumDate={maximumDate} maximumDate={maximumDate}
minimumDate={minimumDate} minimumDate={minimumDate}
disabled={disabled} disabled={disabled}
value={value as DateTimePickerEvent | Date | null | any}
/> />
)} )}
</> </>

View File

@@ -87,7 +87,7 @@ const SelectCustom: React.FC<SelectProps> = ({
borderRadius, borderRadius,
flexDirection: "row", flexDirection: "row",
alignItems: "center", alignItems: "center",
paddingHorizontal: 10, // paddingHorizontal: 0,
height: 50, height: 50,
}, },

View File

@@ -1,3 +1,4 @@
import { AccentColor, MainColor } from "@/constants/color-palet";
import { GStyles } from "@/styles/global-styles"; import { GStyles } from "@/styles/global-styles";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { import {
@@ -6,7 +7,9 @@ import {
Text, Text,
View, View,
ViewStyle, ViewStyle,
useColorScheme,
} from "react-native"; } from "react-native";
import { PlaceholderColor } from "@/constants/color-palet";
type IconType = React.ReactNode | string; type IconType = React.ReactNode | string;
@@ -48,7 +51,7 @@ const TextAreaCustom: React.FC<TextAreaCustomProps> = ({
minRows = 4, minRows = 4,
maxRows = 6, maxRows = 6,
showCount = false, showCount = false,
maxLength, maxLength = 1000,
value, value,
onChangeText, onChangeText,
height = 100, height = 100,
@@ -78,6 +81,9 @@ const TextAreaCustom: React.FC<TextAreaCustomProps> = ({
); );
}; };
const colorScheme = useColorScheme();
const theme = PlaceholderColor[colorScheme || "light"];
return ( return (
<View style={[GStyles.inputContainerArea]}> <View style={[GStyles.inputContainerArea]}>
{label && ( {label && (
@@ -109,6 +115,7 @@ const TextAreaCustom: React.FC<TextAreaCustomProps> = ({
GStyles.textAreaInput, GStyles.textAreaInput,
{ color: fontColor }, { color: fontColor },
]} ]}
placeholderTextColor={theme.placeholder}
editable={!disabled} editable={!disabled}
value={value as string} value={value as string}
onChangeText={onChangeText} onChangeText={onChangeText}

View File

@@ -1,3 +1,4 @@
import { PlaceholderColor } from "@/constants/color-palet";
import { GStyles } from "@/styles/global-styles"; import { GStyles } from "@/styles/global-styles";
import Ionicons from "@expo/vector-icons/Ionicons"; import Ionicons from "@expo/vector-icons/Ionicons";
import React, { useState } from "react"; import React, { useState } from "react";
@@ -8,8 +9,10 @@ import {
TouchableOpacity, TouchableOpacity,
View, View,
ViewStyle, ViewStyle,
useColorScheme
} from "react-native"; } from "react-native";
type IconType = React.ReactNode | string; type IconType = React.ReactNode | string;
type Props = { type Props = {
@@ -74,6 +77,9 @@ const TextInputCustom = ({
} }
}; };
const colorScheme = useColorScheme();
const theme = PlaceholderColor[colorScheme || "light"];
return ( return (
<View style={[GStyles.inputContainerArea, containerStyle]}> <View style={[GStyles.inputContainerArea, containerStyle]}>
{label && ( {label && (
@@ -100,12 +106,14 @@ const TextInputCustom = ({
{ color: fontColor }, { color: fontColor },
disabled && GStyles.inputPlaceholderDisabled, // <-- placeholder saat disabled disabled && GStyles.inputPlaceholderDisabled, // <-- placeholder saat disabled
]} ]}
placeholderTextColor={theme.placeholder}
editable={!disabled} editable={!disabled}
secureTextEntry={secureTextEntry && !isPasswordVisible} secureTextEntry={secureTextEntry && !isPasswordVisible}
keyboardType={keyboardType} keyboardType={keyboardType}
onChangeText={handleTextChange} onChangeText={handleTextChange}
maxLength={maxLength} maxLength={maxLength}
{...rest} {...rest}
/> />
{secureTextEntry && ( {secureTextEntry && (
<TouchableOpacity <TouchableOpacity

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: text1,
value: text2,
}: {
label: React.ReactNode;
value: React.ReactNode;
}) => {
return (
<Grid>
<Grid.Col
span={4}
style={{
justifyContent: "flex-start",
paddingRight: 8,
paddingLeft: 8,
}}
>
{text1}
</Grid.Col>
<Grid.Col span={8} style={{ justifyContent: "center", paddingRight: 8 }}>
{text2}
</Grid.Col>
</Grid>
);
};

View File

@@ -0,0 +1,54 @@
import { Grid } from "@/components";
export const GridSpan_NewComponent = ({
text1,
text2,
text3,
text4,
span1,
span2,
}: {
text1: React.ReactNode;
text2: React.ReactNode;
text3?: React.ReactNode;
text4?: React.ReactNode;
span1?: number;
span2?: number;
}) => {
return (
<Grid>
<Grid.Col
span={span1 ? span1 : text4 ? 3 : 4}
style={{
justifyContent: "flex-start",
paddingRight: 5,
paddingLeft: 3,
}}
>
{text1}
</Grid.Col>
<Grid.Col
span={span2 ? span2 : text4 ? 3 : text3 ? 4 : 8}
style={{ justifyContent: "flex-start", paddingRight: 5 }}
>
{text2}
</Grid.Col>
{text3 && (
<Grid.Col
span={text4 ? 3 : 4}
style={{ justifyContent: "flex-start", paddingRight: 5 }}
>
{text3}
</Grid.Col>
)}
{text4 && (
<Grid.Col
span={3}
style={{ justifyContent: "flex-start", paddingRight: 5 }}
>
{text4}
</Grid.Col>
)}
</Grid>
);
};

View File

@@ -0,0 +1,20 @@
import { View } from "react-native";
import TextCustom from "../Text/TextCustom";
// Komponen Empty
const ListEmptyComponent = ({ search }: { search?: string }) => (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
padding: 20,
}}
>
<TextCustom align="center" color="gray">
{search ? "Tidak ada hasil pencarian" : "Tidak ada data"}
</TextCustom>
</View>
);
export default ListEmptyComponent;

View File

@@ -0,0 +1,11 @@
import { View } from "react-native";
import LoaderCustom from "../Loader/LoaderCustom";
const ListLoaderFooterComponent = () =>(
<View style={{ paddingVertical: 16, alignItems: "center" }}>
<LoaderCustom />
</View>
)
export default ListLoaderFooterComponent;

View File

@@ -0,0 +1,21 @@
import { View } from "react-native";
import StackCustom from "../Stack/StackCustom";
import SkeletonCustom from "./SkeletonCustom";
const ListSkeletonComponent = ({
length = 5,
height = 100,
}: {
length?: number;
height?: number;
}) => (
<View style={{ flex: 1 }}>
<StackCustom>
{Array.from({ length }).map((_, i) => (
<SkeletonCustom height={height} key={i} />
))}
</StackCustom>
</View>
);
export default ListSkeletonComponent;

View File

@@ -40,8 +40,8 @@ interface StaticModeProps extends BaseProps {
interface ListModeProps extends BaseProps { interface ListModeProps extends BaseProps {
children?: never; children?: never;
listData: any[]; listData?: any[];
renderItem: FlatListProps<any>["renderItem"]; renderItem?: FlatListProps<any>["renderItem"];
onEndReached?: () => void; onEndReached?: () => void;
// ✅ Gunakan tipe yang kompatibel dengan FlatList // ✅ Gunakan tipe yang kompatibel dengan FlatList
ListHeaderComponent?: React.ReactElement | null; ListHeaderComponent?: React.ReactElement | null;

View File

@@ -59,6 +59,7 @@ import ViewWrapper from "./_ShareComponent/ViewWrapper";
import SearchInput from "./_ShareComponent/SearchInput"; import SearchInput from "./_ShareComponent/SearchInput";
import DummyLandscapeImage from "./_ShareComponent/DummyLandscapeImage"; import DummyLandscapeImage from "./_ShareComponent/DummyLandscapeImage";
import GridComponentView from "./_ShareComponent/GridSectionView"; import GridComponentView from "./_ShareComponent/GridSectionView";
import NewWrapper from "./_ShareComponent/NewWrapper";
// Progress // Progress
import ProgressCustom from "./Progress/ProgressCustom"; import ProgressCustom from "./Progress/ProgressCustom";
// Loader // Loader
@@ -119,6 +120,7 @@ export {
DummyLandscapeImage, DummyLandscapeImage,
GridComponentView, GridComponentView,
Spacing, Spacing,
NewWrapper,
// Stack // Stack
StackCustom, StackCustom,
TabBarBackground, TabBarBackground,

View File

@@ -45,3 +45,23 @@ export const AdminColor = {
// Warna Asli: #002e59 // Warna Asli: #002e59
// Warna Lebih Gelap: #001f3b // Warna Lebih Gelap: #001f3b
// Warna Tergelap: #001323 // Warna Tergelap: #001323
export const PlaceholderColor = {
light: {
text: "#000",
placeholder: "#666",
border: "#ccc",
background: "#fff",
error: "#d00",
icon: "#555",
},
dark: {
text: "#fff",
placeholder: "#aaa",
border: "#444",
background: "#1a1a1a",
error: "#ff4d4d",
icon: "#ccc",
},
};

View File

@@ -72,8 +72,31 @@ 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));
if (response.success) {
console.log("[Keluar provider]", nomor);
Toast.show({
type: "success",
text1: "Sukses",
text2: "Kode OTP berhasil dikirim",
});
await AsyncStorage.setItem("kode_otp", response.kodeId); await AsyncStorage.setItem("kode_otp", response.kodeId);
router.push(`/verification?nomor=${nomor}`);
return;
} else {
router.push(`/register?nomor=${nomor}`);
Toast.show({
type: "info",
text1: "Info",
text2: "Silahkan mendaftar",
});
return;
}
} 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 {
@@ -81,13 +104,26 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
} }
}; };
// const loginWithNomor = async (nomor: string) => {
// setIsLoading(true);
// try {
// const response = await apiLogin({ nomor: nomor });
// await AsyncStorage.setItem("kode_otp", response.kodeId);
// } catch (error: any) {
// throw new Error(error.response?.data?.message || "Gagal kirim OTP");
// } finally {
// setIsLoading(false);
// }
// };
// --- 2. Validasi OTP & cek user --- // --- 2. Validasi OTP & cek user ---
const validateOtp = async (nomor: string) => { const validateOtp = async (nomor: string) => {
try { try {
setIsLoading(true); setIsLoading(true);
const response = await apiValidationCode({ nomor: nomor }); const response = await apiValidationCode({ nomor: nomor });
const { token } = response; const { token } = response;
console.log("[RESPONSE VALIDASI OTP]", JSON.stringify(response, null, 2));
if (response.success) { if (response.success) {
setToken(token); setToken(token);
await AsyncStorage.setItem("authToken", token); await AsyncStorage.setItem("authToken", token);
@@ -104,20 +140,23 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
if (response.active) { if (response.active) {
if (response.roleId === "1") { if (response.roleId === "1") {
return "/(application)/(user)/home"; router.replace("/(application)/(user)/home");
return;
} else { } else {
return "/(application)/admin/dashboard"; router.replace("/(application)/admin/dashboard");
return;
} }
} else { } else {
return "/(application)/(user)/waiting-room"; router.replace("/(application)/(user)/waiting-room");
return;
} }
} else { } else {
Toast.show({ Toast.show({
type: "info", type: "info",
text1: "Anda belum terdaftar", text1: "Terjadi kesalahan",
text2: "Silahkan daftar terlebih dahulu", text2: "Silahkan coba lagi",
}); });
return `/register?nomor=${nomor}`; return;
} }
} 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);
@@ -132,6 +171,10 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
// --- 3. Ambil data user --- // --- 3. Ambil data user ---
const userData = async (token: string) => { const userData = async (token: string) => {
try { try {
if (!token) {
throw new Error("Token tidak ditemukan");
}
setIsLoading(true); setIsLoading(true);
const response = await apiConfig.get(`/mobile?token=${token}`, { const response = await apiConfig.get(`/mobile?token=${token}`, {
headers: { headers: {
@@ -145,7 +188,10 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
await AsyncStorage.setItem("userData", JSON.stringify(dataUser)); await AsyncStorage.setItem("userData", JSON.stringify(dataUser));
return dataUser; return dataUser;
} catch (error: any) { } catch (error: any) {
console.log(error.response?.data?.message + "user" || "Gagal mengambil data user"); console.log(
"[LOAD USER DATA]",
error.response?.data?.message + "user" || "Gagal mengambil data user"
);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
@@ -160,9 +206,8 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
setIsLoading(true); setIsLoading(true);
try { try {
const response = await apiRegister({ data: userData }); const response = await apiRegister({ data: userData });
console.log("response", response); console.log("[REGISTER FETCH]", JSON.stringify(response, null, 2));
const { token } = response;
if (!response.success) { if (!response.success) {
Toast.show({ Toast.show({
type: "info", type: "info",
@@ -173,23 +218,63 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
return; return;
} }
setToken(token);
await AsyncStorage.setItem("authToken", token);
Toast.show({ Toast.show({
type: "success", type: "success",
text1: "Sukses", text1: "Sukses",
text2: "Anda berhasil terdaftar", text2: "Anda berhasil terdaftar",
}); });
router.replace("/(application)/(user)/waiting-room"); router.replace(`/verification?nomor=${userData.nomor}`);
return; return;
} catch (error: any) { } catch (error: any) {
Toast.show({
type: "error",
text1: "Error",
text2: error.response?.data?.message || "Gagal mendaftar",
});
console.log("Error register", error); console.log("Error register", error);
} finally { } finally {
setIsLoading(false); setIsLoading(false);
} }
}; };
// const registerUser = async (userData: {
// username: string;
// nomor: string;
// termsOfServiceAccepted: boolean;
// }) => {
// setIsLoading(true);
// try {
// const response = await apiRegister({ data: userData });
// console.log("response", response);
// const { token } = response;
// if (!response.success) {
// Toast.show({
// type: "info",
// text1: "Info",
// text2: response.message,
// });
// return;
// }
// setToken(token);
// await AsyncStorage.setItem("authToken", token);
// Toast.show({
// type: "success",
// text1: "Sukses",
// text2: "Anda berhasil terdaftar",
// });
// router.replace("/(application)/(user)/waiting-room");
// return;
// } catch (error: any) {
// console.log("Error register", error);
// } finally {
// setIsLoading(false);
// }
// };
// --- 5. Logout --- // --- 5. Logout ---
const logout = async () => { const logout = async () => {
try { try {
setIsLoading(true); setIsLoading(true);

View File

@@ -0,0 +1,97 @@
// @/hooks/use-paginated-api.ts
import { useCallback, useState, useEffect, useRef } from "react";
interface UsePaginatedApiProps {
fetcher: (params: { page: number; search?: string }) => Promise<any[]>;
initialSearch?: string; // mengatur nilai awal dari state
pageSize?: number;
dependencies?: any[]; // untuk refresh saat deps berubah (misal: user.id)
}
export const usePaginatedApi = ({
fetcher,
initialSearch = "",
pageSize = 5,
dependencies = [],
}: UsePaginatedApiProps) => {
const [data, setData] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [page, setPage] = useState(1);
const [search, setSearch] = useState(initialSearch);
const refreshingRef = useRef<boolean>(false);
const loadingRef = useRef<boolean>(false);
const fetchRef = useRef(false);
const fetchData = useCallback(
async (pageNumber: number, clear: boolean) => {
const isRefresh = clear;
// 🔒 Proteksi: jangan jalankan jika sedang refreshing (untuk refresh)
if (isRefresh && refreshingRef.current) return;
// 🔒 Proteksi: jangan jalankan loadMore jika sedang loading
if (!isRefresh && loadingRef.current) return;
const setLoadingState = (isLoading: boolean) => {
if (isRefresh) {
setRefreshing(isLoading);
refreshingRef.current = isLoading;
} else {
setLoading(isLoading);
loadingRef.current = isLoading;
}
};
setLoadingState(true);
try {
const newData = await fetcher({ page: pageNumber, search });
setData((prev) => {
const current = Array.isArray(prev) ? prev : [];
return clear ? newData : [...current, ...newData];
});
setHasMore(newData.length === pageSize);
setPage(pageNumber);
} catch (error) {
console.error("[usePaginatedApi] Error:", error);
setHasMore(false);
} finally {
setLoadingState(false);
}
},
[search, hasMore, pageSize, ...dependencies]
);
const onRefresh = useCallback(() => {
fetchData(1, true);
}, [fetchData]);
const loadMore = useCallback(() => {
if (hasMore && !loading && !refreshing) {
fetchData(page + 1, false);
}
}, [hasMore, loading, refreshing, page, fetchData]);
// Reset & fetch ulang saat search atau deps berubah
useEffect(() => {
if (fetchRef.current) return; // hindari double initial
fetchRef.current = true;
setPage(1);
setData([]);
setHasMore(true);
fetchData(1, true);
}, [search, ...dependencies]);
return {
data,
loading,
refreshing,
hasMore,
search,
setSearch,
onRefresh,
loadMore,
};
};

View File

@@ -10,7 +10,8 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
241CFEB1CBB2124E18A09822 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 51B6792712498367CD1EEA5D /* PrivacyInfo.xcprivacy */; }; 241CFEB1CBB2124E18A09822 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 51B6792712498367CD1EEA5D /* PrivacyInfo.xcprivacy */; };
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
7833AA2741E7B2CCBEBEB1C7 /* libPods-HIPMIBadungConnect.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B3A38F3F981E3720B61AC51 /* libPods-HIPMIBadungConnect.a */; }; 5FFACD268BA648EFA3A59CDC /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = E8EB1D2D7338425899CD9FA1 /* GoogleService-Info.plist */; };
74742C163A8F5202D2925B40 /* libPods-HIPMIBadungConnect.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BE614D12468C9A25709B0317 /* libPods-HIPMIBadungConnect.a */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
D138B3E4BC618ADDE62DCDCC /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F39CC335B3942749573F05 /* ExpoModulesProvider.swift */; }; D138B3E4BC618ADDE62DCDCC /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F39CC335B3942749573F05 /* ExpoModulesProvider.swift */; };
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; }; F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
@@ -20,12 +21,13 @@
13B07F961A680F5B00A75B9A /* HIPMIBadungConnect.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HIPMIBadungConnect.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* HIPMIBadungConnect.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HIPMIBadungConnect.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = HIPMIBadungConnect/Images.xcassets; sourceTree = "<group>"; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = HIPMIBadungConnect/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = HIPMIBadungConnect/Info.plist; sourceTree = "<group>"; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = HIPMIBadungConnect/Info.plist; sourceTree = "<group>"; };
1E94102500A8768C71C74AF6 /* Pods-HIPMIBadungConnect.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HIPMIBadungConnect.debug.xcconfig"; path = "Target Support Files/Pods-HIPMIBadungConnect/Pods-HIPMIBadungConnect.debug.xcconfig"; sourceTree = "<group>"; }; 40E847A832DE042BE51AD9E9 /* Pods-HIPMIBadungConnect.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HIPMIBadungConnect.release.xcconfig"; path = "Target Support Files/Pods-HIPMIBadungConnect/Pods-HIPMIBadungConnect.release.xcconfig"; sourceTree = "<group>"; };
51B6792712498367CD1EEA5D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = HIPMIBadungConnect/PrivacyInfo.xcprivacy; sourceTree = "<group>"; }; 51B6792712498367CD1EEA5D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = HIPMIBadungConnect/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
9B3A38F3F981E3720B61AC51 /* libPods-HIPMIBadungConnect.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HIPMIBadungConnect.a"; sourceTree = BUILT_PRODUCTS_DIR; };
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = HIPMIBadungConnect/SplashScreen.storyboard; sourceTree = "<group>"; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = HIPMIBadungConnect/SplashScreen.storyboard; sourceTree = "<group>"; };
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
C19B4954908DD0D4CD2965DF /* Pods-HIPMIBadungConnect.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HIPMIBadungConnect.release.xcconfig"; path = "Target Support Files/Pods-HIPMIBadungConnect/Pods-HIPMIBadungConnect.release.xcconfig"; sourceTree = "<group>"; }; BE614D12468C9A25709B0317 /* libPods-HIPMIBadungConnect.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HIPMIBadungConnect.a"; sourceTree = BUILT_PRODUCTS_DIR; };
C273E4769DE3DC0FEF6A377C /* Pods-HIPMIBadungConnect.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HIPMIBadungConnect.debug.xcconfig"; path = "Target Support Files/Pods-HIPMIBadungConnect/Pods-HIPMIBadungConnect.debug.xcconfig"; sourceTree = "<group>"; };
E8EB1D2D7338425899CD9FA1 /* GoogleService-Info.plist */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "HIPMIBadungConnect/GoogleService-Info.plist"; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = HIPMIBadungConnect/AppDelegate.swift; sourceTree = "<group>"; }; F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = HIPMIBadungConnect/AppDelegate.swift; sourceTree = "<group>"; };
F11748442D0722820044C1D9 /* HIPMIBadungConnect-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "HIPMIBadungConnect-Bridging-Header.h"; path = "HIPMIBadungConnect/HIPMIBadungConnect-Bridging-Header.h"; sourceTree = "<group>"; }; F11748442D0722820044C1D9 /* HIPMIBadungConnect-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "HIPMIBadungConnect-Bridging-Header.h"; path = "HIPMIBadungConnect/HIPMIBadungConnect-Bridging-Header.h"; sourceTree = "<group>"; };
@@ -37,7 +39,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7833AA2741E7B2CCBEBEB1C7 /* libPods-HIPMIBadungConnect.a in Frameworks */, 74742C163A8F5202D2925B40 /* libPods-HIPMIBadungConnect.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -54,6 +56,7 @@
13B07FB61A68108700A75B9A /* Info.plist */, 13B07FB61A68108700A75B9A /* Info.plist */,
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */, AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
51B6792712498367CD1EEA5D /* PrivacyInfo.xcprivacy */, 51B6792712498367CD1EEA5D /* PrivacyInfo.xcprivacy */,
E8EB1D2D7338425899CD9FA1 /* GoogleService-Info.plist */,
); );
name = HIPMIBadungConnect; name = HIPMIBadungConnect;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -62,7 +65,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
9B3A38F3F981E3720B61AC51 /* libPods-HIPMIBadungConnect.a */, BE614D12468C9A25709B0317 /* libPods-HIPMIBadungConnect.a */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -108,8 +111,8 @@
A30E580F1E4E26A6BD0E3B24 /* Pods */ = { A30E580F1E4E26A6BD0E3B24 /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1E94102500A8768C71C74AF6 /* Pods-HIPMIBadungConnect.debug.xcconfig */, C273E4769DE3DC0FEF6A377C /* Pods-HIPMIBadungConnect.debug.xcconfig */,
C19B4954908DD0D4CD2965DF /* Pods-HIPMIBadungConnect.release.xcconfig */, 40E847A832DE042BE51AD9E9 /* Pods-HIPMIBadungConnect.release.xcconfig */,
); );
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -138,14 +141,14 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "HIPMIBadungConnect" */; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "HIPMIBadungConnect" */;
buildPhases = ( buildPhases = (
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */, AA173A81855B88A7082A949D /* [CP] Check Pods Manifest.lock */,
0B90913696EEE0E819CAF510 /* [Expo] Configure project */, 0B90913696EEE0E819CAF510 /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */, 13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */, 13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */, 1A2B5C8B77E56B3325ADC6E7 /* [CP] Embed Pods Frameworks */,
2DA25245F64078F29C0B38E4 /* [CP] Embed Pods Frameworks */, AECA4AD2A3DFA75F552CA1F1 /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
@@ -196,6 +199,7 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
241CFEB1CBB2124E18A09822 /* PrivacyInfo.xcprivacy in Resources */, 241CFEB1CBB2124E18A09822 /* PrivacyInfo.xcprivacy in Resources */,
5FFACD268BA648EFA3A59CDC /* GoogleService-Info.plist in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -219,28 +223,6 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n";
}; };
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-HIPMIBadungConnect-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
0B90913696EEE0E819CAF510 /* [Expo] Configure project */ = { 0B90913696EEE0E819CAF510 /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
@@ -265,7 +247,7 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-HIPMIBadungConnect/expo-configure-project.sh\"\n"; shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-HIPMIBadungConnect/expo-configure-project.sh\"\n";
}; };
2DA25245F64078F29C0B38E4 /* [CP] Embed Pods Frameworks */ = { 1A2B5C8B77E56B3325ADC6E7 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@@ -295,7 +277,29 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HIPMIBadungConnect/Pods-HIPMIBadungConnect-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HIPMIBadungConnect/Pods-HIPMIBadungConnect-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = { AA173A81855B88A7082A949D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-HIPMIBadungConnect-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
AECA4AD2A3DFA75F552CA1F1 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
@@ -309,6 +313,13 @@
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCore/FirebaseCore_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseCoreInternal/FirebaseCoreInternal_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseInstallations/FirebaseInstallations_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/FirebaseMessaging/FirebaseMessaging_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleDataTransport/GoogleDataTransport_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/GoogleUtilities/GoogleUtilities_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/PromisesObjC/FBLPromises_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf", "${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
@@ -335,6 +346,7 @@
"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/nanopb/nanopb_Privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/react-native-maps/ReactNativeMapsPrivacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/react-native-maps/ReactNativeMapsPrivacy.bundle",
); );
name = "[CP] Copy Pods Resources"; name = "[CP] Copy Pods Resources";
@@ -346,6 +358,13 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCore_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseCoreInternal_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseInstallations_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FirebaseMessaging_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleDataTransport_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleUtilities_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FBLPromises_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
@@ -372,6 +391,7 @@
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/nanopb_Privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ReactNativeMapsPrivacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ReactNativeMapsPrivacy.bundle",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -396,12 +416,12 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = { 13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 1E94102500A8768C71C74AF6 /* Pods-HIPMIBadungConnect.debug.xcconfig */; baseConfigurationReference = C273E4769DE3DC0FEF6A377C /* Pods-HIPMIBadungConnect.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = HIPMIBadungConnect/HIPMIBadungConnect.entitlements; CODE_SIGN_ENTITLEMENTS = HIPMIBadungConnect/HIPMIBadungConnect.entitlements;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = BMY6GT6W3D; DEVELOPMENT_TEAM = BMY6GT6W3D;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
@@ -422,7 +442,7 @@
); );
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = "com.anonymous.hipmi-mobile"; PRODUCT_BUNDLE_IDENTIFIER = "com.anonymous.hipmi-mobile";
PRODUCT_NAME = HIPMIBadungConnect; PRODUCT_NAME = "HIPMIBadungConnect";
SWIFT_OBJC_BRIDGING_HEADER = "HIPMIBadungConnect/HIPMIBadungConnect-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "HIPMIBadungConnect/HIPMIBadungConnect-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -433,12 +453,12 @@
}; };
13B07F951A680F5B00A75B9A /* Release */ = { 13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = C19B4954908DD0D4CD2965DF /* Pods-HIPMIBadungConnect.release.xcconfig */; baseConfigurationReference = 40E847A832DE042BE51AD9E9 /* Pods-HIPMIBadungConnect.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = HIPMIBadungConnect/HIPMIBadungConnect.entitlements; CODE_SIGN_ENTITLEMENTS = HIPMIBadungConnect/HIPMIBadungConnect.entitlements;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = BMY6GT6W3D; DEVELOPMENT_TEAM = BMY6GT6W3D;
INFOPLIST_FILE = HIPMIBadungConnect/Info.plist; INFOPLIST_FILE = HIPMIBadungConnect/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1; IPHONEOS_DEPLOYMENT_TARGET = 15.1;
@@ -454,7 +474,7 @@
); );
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = "com.anonymous.hipmi-mobile"; PRODUCT_BUNDLE_IDENTIFIER = "com.anonymous.hipmi-mobile";
PRODUCT_NAME = HIPMIBadungConnect; PRODUCT_NAME = "HIPMIBadungConnect";
SWIFT_OBJC_BRIDGING_HEADER = "HIPMIBadungConnect/HIPMIBadungConnect-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "HIPMIBadungConnect/HIPMIBadungConnect-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";

View File

@@ -1,4 +1,5 @@
import Expo import Expo
import FirebaseCore
import React import React
import ReactAppDependencyProvider import ReactAppDependencyProvider
@@ -23,6 +24,9 @@ public class AppDelegate: ExpoAppDelegate {
#if os(iOS) || os(tvOS) #if os(iOS) || os(tvOS)
window = UIWindow(frame: UIScreen.main.bounds) window = UIWindow(frame: UIScreen.main.bounds)
// @generated begin @react-native-firebase/app-didFinishLaunchingWithOptions - expo prebuild (DO NOT MODIFY) sync-10e8520570672fd76b2403b7e1e27f5198a6349a
FirebaseApp.configure()
// @generated end @react-native-firebase/app-didFinishLaunchingWithOptions
factory.startReactNative( factory.startReactNative(
withModuleName: "main", withModuleName: "main",
in: window, in: window,

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>AIzaSyAAvM0yBp5kCXa0hLAaNFZNqwkpBGYAWYs</string>
<key>GCM_SENDER_ID</key>
<string>608461535079</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.anonymous.hipmi-mobile</string>
<key>PROJECT_ID</key>
<string>hipmi-badung-connect</string>
<key>STORAGE_BUCKET</key>
<string>hipmi-badung-connect.firebasestorage.app</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:608461535079:ios:589f31b2b6b1068b6761c2</string>
</dict>
</plist>

View File

@@ -39,7 +39,7 @@
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>8</string> <string>15</string>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>
<false/> <false/>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
@@ -53,15 +53,18 @@
<key>NSAllowsLocalNetworking</key> <key>NSAllowsLocalNetworking</key>
<true/> <true/>
</dict> </dict>
<key>NSCameraUsageDescription</key>
<!-- Photo Library --> <string>Allow $(PRODUCT_NAME) to access your camera</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your location</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your location</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Aplikasi membutuhkan akses lokasi untuk menampilkan peta.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow $(PRODUCT_NAME) to access your microphone</string>
<key>NSPhotoLibraryUsageDescription</key> <key>NSPhotoLibraryUsageDescription</key>
<string>Untuk mengunggah dokumen dan media bisnis seperti foto profil, logo usaha, poster lowongan, atau bukti transaksi di berbagai fitur aplikasi: Profile, Portofolio, Job Vacancy, Investasi, dan Donasi.</string> <string>Untuk mengunggah dokumen dan media bisnis seperti foto profil, logo usaha, poster lowongan, atau bukti transaksi di berbagai fitur aplikasi: Profile, Portofolio, Job Vacancy, Investasi, dan Donasi.</string>
<!-- Camera -->
<key>NSCameraUsageDescription</key>
<string>Untuk mengambil foto langsung saat mengunggah dokumen bisnis seperti foto profil, logo usaha, poster, atau bukti pembayaran di fitur Profile, Portofolio, Job Vacancy, Investasi, dan Donasi.</string>
<key>NSUserActivityTypes</key> <key>NSUserActivityTypes</key>
<array> <array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route</string>

View File

@@ -20,6 +20,8 @@
<key>NSPrivacyAccessedAPITypeReasons</key> <key>NSPrivacyAccessedAPITypeReasons</key>
<array> <array>
<string>CA92.1</string> <string>CA92.1</string>
<string>1C8F.1</string>
<string>C56D.1</string>
</array> </array>
</dict> </dict>
<dict> <dict>

View File

@@ -1,3 +1,5 @@
use_modular_headers!
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods") require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
@@ -33,6 +35,14 @@ target 'HIPMIBadungConnect' do
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
# @generated begin pre_installer - expo prebuild (DO NOT MODIFY) sync-c8812095000d6054b846ce74840f0ffb540c2757
pre_install do |installer|
# @generated begin @rnmapbox/maps-pre_installer - expo prebuild (DO NOT MODIFY) sync-ea4905840bf9fcea0acc62e92aa2e784f9d760f8
$RNMapboxMaps.pre_install(installer)
# @generated end @rnmapbox/maps-pre_installer
end
# @generated end pre_installer
use_react_native!( use_react_native!(
:path => config[:reactNativePath], :path => config[:reactNativePath],
:hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
@@ -41,7 +51,31 @@ target 'HIPMIBadungConnect' do
:privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false',
) )
pod 'Firebase/Messaging'
# @generated begin post_installer - expo prebuild (DO NOT MODIFY) sync-4092f82b887b5b9edb84642c2a56984d69b9a403
post_install do |installer| post_install do |installer|
# @generated begin @rnmapbox/maps-post_installer - expo prebuild (DO NOT MODIFY) sync-c4e8f90e96f6b6c6ea9241dd7b52ab5f57f7bf36
$RNMapboxMaps.post_install(installer)
# @generated end @rnmapbox/maps-post_installer
# Fix all script phases with incorrect paths
installer.pods_project.targets.each do |target|
target.build_phases.each do |phase|
next unless phase.respond_to?(:shell_script)
# Fix duplicated path issue
if phase.shell_script.include?('with-environment.sh')
# Remove any existing path and use proper relative path
phase.shell_script = phase.shell_script.gsub(
%r{(/.*?/node_modules/react-native)+/scripts/xcode/with-environment.sh},
'${PODS_ROOT}/../../node_modules/react-native/scripts/xcode/with-environment.sh'
)
end
end
end
# Standard React Native post install
react_native_post_install( react_native_post_install(
installer, installer,
config[:reactNativePath], config[:reactNativePath],
@@ -49,4 +83,6 @@ target 'HIPMIBadungConnect' do
:ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true',
) )
end end
# @generated end post_installer
end end

View File

@@ -239,6 +239,8 @@ PODS:
- ExpoModulesCore - ExpoModulesCore
- ExpoKeepAwake (15.0.7): - ExpoKeepAwake (15.0.7):
- ExpoModulesCore - ExpoModulesCore
- ExpoLinearGradient (15.0.7):
- ExpoModulesCore
- ExpoLinking (8.0.8): - ExpoLinking (8.0.8):
- ExpoModulesCore - ExpoModulesCore
- ExpoLocation (19.0.7): - ExpoLocation (19.0.7):
@@ -277,6 +279,58 @@ PODS:
- EXUpdatesInterface (2.0.0): - EXUpdatesInterface (2.0.0):
- ExpoModulesCore - ExpoModulesCore
- FBLazyVector (0.81.4) - FBLazyVector (0.81.4)
- Firebase/CoreOnly (12.7.0):
- FirebaseCore (~> 12.7.0)
- Firebase/Messaging (12.7.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 12.7.0)
- FirebaseCore (12.7.0):
- FirebaseCoreInternal (~> 12.7.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1)
- FirebaseCoreInternal (12.7.0):
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- FirebaseInstallations (12.7.0):
- FirebaseCore (~> 12.7.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- PromisesObjC (~> 2.4)
- FirebaseMessaging (12.7.0):
- FirebaseCore (~> 12.7.0)
- FirebaseInstallations (~> 12.7.0)
- GoogleDataTransport (~> 10.1)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Reachability (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- nanopb (~> 3.30910.0)
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- GoogleUtilities/AppDelegateSwizzler (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Logger
- GoogleUtilities/Network
- GoogleUtilities/Privacy
- GoogleUtilities/Environment (8.1.0):
- GoogleUtilities/Privacy
- GoogleUtilities/Logger (8.1.0):
- GoogleUtilities/Environment
- GoogleUtilities/Privacy
- GoogleUtilities/Network (8.1.0):
- GoogleUtilities/Logger
- "GoogleUtilities/NSData+zlib"
- GoogleUtilities/Privacy
- GoogleUtilities/Reachability
- "GoogleUtilities/NSData+zlib (8.1.0)":
- GoogleUtilities/Privacy
- GoogleUtilities/Privacy (8.1.0)
- GoogleUtilities/Reachability (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- GoogleUtilities/UserDefaults (8.1.0):
- GoogleUtilities/Logger
- GoogleUtilities/Privacy
- hermes-engine (0.81.4): - hermes-engine (0.81.4):
- hermes-engine/Pre-built (= 0.81.4) - hermes-engine/Pre-built (= 0.81.4)
- hermes-engine/Pre-built (0.81.4) - hermes-engine/Pre-built (0.81.4)
@@ -305,6 +359,12 @@ PODS:
- MapboxCommon (= 24.15.4) - MapboxCommon (= 24.15.4)
- MapboxCoreMaps (= 11.15.4) - MapboxCoreMaps (= 11.15.4)
- Turf (= 4.0.0) - Turf (= 4.0.0)
- nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0)
- nanopb/encode (= 3.30910.0)
- nanopb/decode (3.30910.0)
- nanopb/encode (3.30910.0)
- PromisesObjC (2.4.0)
- RCTDeprecation (0.81.4) - RCTDeprecation (0.81.4)
- RCTRequired (0.81.4) - RCTRequired (0.81.4)
- RCTTypeSafety (0.81.4): - RCTTypeSafety (0.81.4):
@@ -2446,10 +2506,10 @@ PODS:
- ReactCommon/turbomodule/core - ReactCommon/turbomodule/core
- ReactNativeDependencies - ReactNativeDependencies
- Yoga - Yoga
- SDWebImage (5.21.1): - SDWebImage (5.21.3):
- SDWebImage/Core (= 5.21.1) - SDWebImage/Core (= 5.21.3)
- SDWebImage/Core (5.21.1) - SDWebImage/Core (5.21.3)
- SDWebImageAVIFCoder (0.11.0): - SDWebImageAVIFCoder (0.11.1):
- libavif/core (>= 0.11.0) - libavif/core (>= 0.11.0)
- SDWebImage (~> 5.10) - SDWebImage (~> 5.10)
- SDWebImageSVGCoder (1.7.0): - SDWebImageSVGCoder (1.7.0):
@@ -2489,6 +2549,7 @@ DEPENDENCIES:
- ExpoImage (from `../node_modules/expo-image/ios`) - ExpoImage (from `../node_modules/expo-image/ios`)
- ExpoImagePicker (from `../node_modules/expo-image-picker/ios`) - ExpoImagePicker (from `../node_modules/expo-image-picker/ios`)
- ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`) - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
- ExpoLinearGradient (from `../node_modules/expo-linear-gradient/ios`)
- ExpoLinking (from `../node_modules/expo-linking/ios`) - ExpoLinking (from `../node_modules/expo-linking/ios`)
- ExpoLocation (from `../node_modules/expo-location/ios`) - ExpoLocation (from `../node_modules/expo-location/ios`)
- ExpoModulesCore (from `../node_modules/expo-modules-core`) - ExpoModulesCore (from `../node_modules/expo-modules-core`)
@@ -2498,6 +2559,7 @@ DEPENDENCIES:
- ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`) - ExpoWebBrowser (from `../node_modules/expo-web-browser/ios`)
- EXUpdatesInterface (from `../node_modules/expo-updates-interface/ios`) - EXUpdatesInterface (from `../node_modules/expo-updates-interface/ios`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- Firebase/Messaging
- hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
- RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
- RCTRequired (from `../node_modules/react-native/Libraries/Required`) - RCTRequired (from `../node_modules/react-native/Libraries/Required`)
@@ -2582,12 +2644,21 @@ DEPENDENCIES:
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- Firebase
- FirebaseCore
- FirebaseCoreInternal
- FirebaseInstallations
- FirebaseMessaging
- GoogleDataTransport
- GoogleUtilities
- libavif - libavif
- libdav1d - libdav1d
- libwebp - libwebp
- MapboxCommon - MapboxCommon
- MapboxCoreMaps - MapboxCoreMaps
- MapboxMaps - MapboxMaps
- nanopb
- PromisesObjC
- SDWebImage - SDWebImage
- SDWebImageAVIFCoder - SDWebImageAVIFCoder
- SDWebImageSVGCoder - SDWebImageSVGCoder
@@ -2642,6 +2713,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/expo-image-picker/ios" :path: "../node_modules/expo-image-picker/ios"
ExpoKeepAwake: ExpoKeepAwake:
:path: "../node_modules/expo-keep-awake/ios" :path: "../node_modules/expo-keep-awake/ios"
ExpoLinearGradient:
:path: "../node_modules/expo-linear-gradient/ios"
ExpoLinking: ExpoLinking:
:path: "../node_modules/expo-linking/ios" :path: "../node_modules/expo-linking/ios"
ExpoLocation: ExpoLocation:
@@ -2846,6 +2919,7 @@ SPEC CHECKSUMS:
ExpoImage: e88f500585913969b930e13a4be47277eb7c6de8 ExpoImage: e88f500585913969b930e13a4be47277eb7c6de8
ExpoImagePicker: d251aab45a1b1857e4156fed88511b278b4eee1c ExpoImagePicker: d251aab45a1b1857e4156fed88511b278b4eee1c
ExpoKeepAwake: 1a2e820692e933c94a565ec3fbbe38ac31658ffe ExpoKeepAwake: 1a2e820692e933c94a565ec3fbbe38ac31658ffe
ExpoLinearGradient: a464898cb95153125e3b81894fd479bcb1c7dd27
ExpoLinking: f051f28e50ea9269ff539317c166adec81d9342d ExpoLinking: f051f28e50ea9269ff539317c166adec81d9342d
ExpoLocation: 93d7faa0c2adbd5a04686af0c1a61bc6ed3ee2f7 ExpoLocation: 93d7faa0c2adbd5a04686af0c1a61bc6ed3ee2f7
ExpoModulesCore: 9281d8f1cda9d0c37dbce34c26014212b22eb8c0 ExpoModulesCore: 9281d8f1cda9d0c37dbce34c26014212b22eb8c0
@@ -2855,6 +2929,13 @@ SPEC CHECKSUMS:
ExpoWebBrowser: b973e1351fdcf5fec0c400997b1851f5a8219ec3 ExpoWebBrowser: b973e1351fdcf5fec0c400997b1851f5a8219ec3
EXUpdatesInterface: 5adf50cb41e079c861da6d9b4b954c3db9a50734 EXUpdatesInterface: 5adf50cb41e079c861da6d9b4b954c3db9a50734
FBLazyVector: 9e0cd874afd81d9a4d36679daca991b58b260d42 FBLazyVector: 9e0cd874afd81d9a4d36679daca991b58b260d42
Firebase: 2d19a10c9a2e48ac532a4303115d3fc9b2798396
FirebaseCore: c7b57863ce0859281a66d16ca36d665c45d332b5
FirebaseCoreInternal: 571a2dd8c975410966199623351db3a3265c874d
FirebaseInstallations: 6d05424a046b68ca146b4de4376f05b4e9262fc3
FirebaseMessaging: b5f7bdc62b91b6102015991fb7bc6fa75f643908
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
hermes-engine: 35c763d57c9832d0eef764316ca1c4d043581394 hermes-engine: 35c763d57c9832d0eef764316ca1c4d043581394
libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7 libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7
libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f
@@ -2862,6 +2943,8 @@ SPEC CHECKSUMS:
MapboxCommon: 975faa94b893bb64a1d28b09d9d6d820e1030a26 MapboxCommon: 975faa94b893bb64a1d28b09d9d6d820e1030a26
MapboxCoreMaps: 105af9894d850290fbb466e9f9a133f5d175abf1 MapboxCoreMaps: 105af9894d850290fbb466e9f9a133f5d175abf1
MapboxMaps: e97e59d6ba48bb6f695a4c1dc2f174cb24743cd4 MapboxMaps: e97e59d6ba48bb6f695a4c1dc2f174cb24743cd4
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
RCTDeprecation: 7487d6dda857ccd4cb3dd6ecfccdc3170e85dcbc RCTDeprecation: 7487d6dda857ccd4cb3dd6ecfccdc3170e85dcbc
RCTRequired: 54128b7df8be566881d48c7234724a78cb9b6157 RCTRequired: 54128b7df8be566881d48c7234724a78cb9b6157
RCTTypeSafety: d2b07797a79e45d7b19e1cd2f53c79ab419fe217 RCTTypeSafety: d2b07797a79e45d7b19e1cd2f53c79ab419fe217
@@ -2940,14 +3023,14 @@ SPEC CHECKSUMS:
RNSVG: 31d6639663c249b7d5abc9728dde2041eb2a3c34 RNSVG: 31d6639663c249b7d5abc9728dde2041eb2a3c34
RNVectorIcons: 704d89d2f11886824e3bd5cbea34dd00aeade200 RNVectorIcons: 704d89d2f11886824e3bd5cbea34dd00aeade200
RNWorklets: 76fce72926e28e304afb44f0da23b2d24f2c1fa0 RNWorklets: 76fce72926e28e304afb44f0da23b2d24f2c1fa0
SDWebImage: f29024626962457f3470184232766516dee8dfea SDWebImage: 16309af6d214ba3f77a7c6f6fdda888cb313a50a
SDWebImageAVIFCoder: 00310d246aab3232ce77f1d8f0076f8c4b021d90 SDWebImageAVIFCoder: afe194a084e851f70228e4be35ef651df0fc5c57
SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c SDWebImageSVGCoder: 15a300a97ec1c8ac958f009c02220ac0402e936c
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
Turf: c9eb11a65d96af58cac523460fd40fec5061b081 Turf: c9eb11a65d96af58cac523460fd40fec5061b081
Yoga: 051f086b5ccf465ff2ed38a2cf5a558ae01aaaa1 Yoga: 051f086b5ccf465ff2ed38a2cf5a558ae01aaaa1
ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5 ZXingObjC: 8898711ab495761b2dbbdec76d90164a6d7e14c5
PODFILE CHECKSUM: 961e5122c387eef49538723a9e3e7a469ca4144f PODFILE CHECKSUM: 61c4a9d35618e5f2a420f47e5a24c89e86706f00
COCOAPODS: 1.16.2 COCOAPODS: 1.16.2

View File

@@ -14,6 +14,8 @@
"@expo/vector-icons": "^15.0.2", "@expo/vector-icons": "^15.0.2",
"@react-native-async-storage/async-storage": "2.2.0", "@react-native-async-storage/async-storage": "2.2.0",
"@react-native-community/datetimepicker": "8.4.4", "@react-native-community/datetimepicker": "8.4.4",
"@react-native-firebase/app": "^23.7.0",
"@react-native-firebase/messaging": "^23.7.0",
"@react-navigation/bottom-tabs": "^7.3.10", "@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/drawer": "^7.3.9", "@react-navigation/drawer": "^7.3.9",
"@react-navigation/elements": "^2.3.8", "@react-navigation/elements": "^2.3.8",
@@ -72,6 +74,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.25.2", "@babel/core": "^7.25.2",
"@react-native-community/cli": "^20.0.2",
"@types/react": "~19.1.10", "@types/react": "~19.1.10",
"eslint": "^9.25.0", "eslint": "^9.25.0",
"eslint-config-expo": "~10.0.0", "eslint-config-expo": "~10.0.0",

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) {
@@ -66,21 +81,13 @@ export default function LoginView() {
if (!isValid) return; if (!isValid) return;
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || ""; const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
const fixNumber = inputValue.replace(/\s+/g, ""); let fixNumber = inputValue.replace(/\s+/g, "").replace(/^0+/, "");
const realNumber = callingCode + fixNumber; const realNumber = callingCode + fixNumber;
try { try {
setLoading(true); setLoading(true);
// const response = await apiLogin({ nomor: realNumber });
await loginWithNomor(realNumber); await loginWithNomor(realNumber);
Toast.show({
type: "success",
text1: "Sukses",
text2: "Kode OTP berhasil dikirim",
});
router.navigate(`/verification?nomor=${realNumber}`);
} catch (error) { } catch (error) {
console.log("Error login", error); console.log("Error login", error);
Toast.show({ Toast.show({
@@ -91,6 +98,30 @@ 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) {
@@ -110,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}>
@@ -154,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
@@ -89,8 +91,9 @@ export default function VerificationView() {
// ✅ VERIFIKASI OTOMATIS UNTUK APPLE REVIEW // ✅ VERIFIKASI OTOMATIS UNTUK APPLE REVIEW
if (inputOtp === "1234") { if (inputOtp === "1234") {
try { try {
const response = await validateOtp(nomor as string); await validateOtp(nomor as string);
router.replace(response);
return;
} catch (error) { } catch (error) {
console.log("Error verification", error); console.log("Error verification", error);
Toast.show({ type: "error", text1: "Gagal verifikasi" }); Toast.show({ type: "error", text1: "Gagal verifikasi" });
@@ -103,16 +106,8 @@ export default function VerificationView() {
// 🔁 VERIFIKASI NORMAL (untuk pengguna sungguhan) // 🔁 VERIFIKASI NORMAL (untuk pengguna sungguhan)
try { try {
const response = await validateOtp(nomor as string); await validateOtp(nomor as string);
// registerForPushNotificationsAsync().then((token) => { return
// if (token) {
// console.log("Expo Push Token:", token);
// // TODO: Kirim token ke backend kamu
// } else {
// console.log("Failed to get Expo Push Token");
// }
// });
router.replace(response);
} catch (error) { } catch (error) {
console.log("Error verification", error); console.log("Error verification", error);
Toast.show({ type: "error", text1: "Gagal verifikasi" }); Toast.show({ type: "error", text1: "Gagal verifikasi" });

View File

@@ -25,7 +25,7 @@ function Collaboration_BoxPublishSection({
name={data?.Author?.username || "Username"} name={data?.Author?.username || "Username"}
rightComponent={rightComponentAvatar} rightComponent={rightComponentAvatar}
avatar={data?.Author?.Profile?.imageId} avatar={data?.Author?.Profile?.imageId}
withBottomLine // withBottomLine
/> />
<StackCustom style={{paddingBlock: 10}}> <StackCustom style={{paddingBlock: 10}}>

View File

@@ -50,7 +50,7 @@ export default function Event_BoxDetailPublishSection({
<Grid.Col span={4}> <Grid.Col span={4}>
<TextCustom bold>{item.title}</TextCustom> <TextCustom bold>{item.title}</TextCustom>
</Grid.Col> </Grid.Col>
<Grid.Col span={8}> <Grid.Col span={8} style={{ paddingLeft: 10 }}>
<TextCustom>{item.value}</TextCustom> <TextCustom>{item.value}</TextCustom>
</Grid.Col> </Grid.Col>
</Grid> </Grid>

View File

@@ -28,7 +28,7 @@ export default function Event_BoxPublishSection({
<AvatarUsernameAndOtherComponent <AvatarUsernameAndOtherComponent
avatarHref={`/profile/${data?.Author?.Profile?.id}`} avatarHref={`/profile/${data?.Author?.Profile?.id}`}
name={data?.Author?.username || "-"} name={data?.Author?.username || "-"}
rightComponent={rightComponentAvatar} // rightComponent={rightComponentAvatar}
avatar={data?.Author?.Profile?.imageId || ""} avatar={data?.Author?.Profile?.imageId || ""}
/> />
<TextCustom truncate bold> <TextCustom truncate bold>

View File

@@ -203,10 +203,9 @@ export default function Forum_ViewBeranda2() {
/> />
} }
onEndReached={loadMore} onEndReached={loadMore}
// ListHeaderComponent={ListHeaderComponent}
ListFooterComponent={ListFooterComponent} ListFooterComponent={ListFooterComponent}
ListEmptyComponent={ ListEmptyComponent={
_.isEmpty(listData) ? <SkeletonListComponent /> : <EmptyComponent /> loading && _.isEmpty(listData) ? <SkeletonListComponent /> : <EmptyComponent />
} }
// ------------------------ // ------------------------
/> />

View File

@@ -207,7 +207,7 @@ export default function View_Forumku2() {
ListHeaderComponent={randerHeaderComponent()} ListHeaderComponent={randerHeaderComponent()}
ListFooterComponent={ListFooterComponent} ListFooterComponent={ListFooterComponent}
ListEmptyComponent={ ListEmptyComponent={
_.isEmpty(listData) ? <SkeletonListComponent /> : <EmptyComponent /> loading && _.isEmpty(listData) ? <SkeletonListComponent /> : <EmptyComponent />
} }
/> />
</> </>

View File

@@ -1,12 +1,12 @@
import { ITabs } from "@/components/_Interface/types"; import { ITabs } from "@/components/_Interface/types";
export const tabsHome: any = (profileId: string) => [ export const tabsHome: any = ({acceptedForumTermsAt, profileId}: {acceptedForumTermsAt: Date, profileId: string}) => [
{ {
id: "forum", id: "forum",
icon: "chatbubble-ellipses-outline", icon: "chatbubble-ellipses-outline",
activeIcon: "chatbubble-ellipses", activeIcon: "chatbubble-ellipses",
label: "Forum", label: "Forum",
path: "/forum", path: acceptedForumTermsAt ? "/forum" : "/forum/terms",
isActive: true, isActive: true,
disabled: false, disabled: false,
}, },

View File

@@ -7,6 +7,7 @@ import {
TextCustom, TextCustom,
} from "@/components"; } from "@/components";
import API_STRORAGE from "@/constants/base-url-api-strorage"; import API_STRORAGE from "@/constants/base-url-api-strorage";
import { MainColor } from "@/constants/color-palet";
import DUMMY_IMAGE from "@/constants/dummy-image-value"; import DUMMY_IMAGE from "@/constants/dummy-image-value";
import { countDownAndCondition } from "@/utils/countDownAndCondition"; import { countDownAndCondition } from "@/utils/countDownAndCondition";
import { Ionicons } from "@expo/vector-icons"; import { Ionicons } from "@expo/vector-icons";
@@ -32,6 +33,8 @@ export default function Investment_BoxBerandaSection({
updateCountDown(); updateCountDown();
}, [data]); }, [data]);
console.log("[DATA BERANDA]", JSON.stringify(data, null, 2));
const updateCountDown = () => { const updateCountDown = () => {
const countDown = countDownAndCondition({ const countDown = countDownAndCondition({
duration: data?.pencarianInvestor, duration: data?.pencarianInvestor,
@@ -66,8 +69,10 @@ export default function Investment_BoxBerandaSection({
<TextCustom truncate={2}>{data.title}</TextCustom> <TextCustom truncate={2}>{data.title}</TextCustom>
<ProgressCustom <ProgressCustom
label={`${data.progress}%`} label={`${data.progress}%`}
value={data.progress} value={Number(data.progress)}
size="lg" size="lg"
animated
color="primary"
/> />
{value.reminder ? ( {value.reminder ? (
<View <View
@@ -79,13 +84,11 @@ export default function Investment_BoxBerandaSection({
> >
<Ionicons name="alert-circle-outline" size={16} color="red" /> <Ionicons name="alert-circle-outline" size={16} color="red" />
<TextCustom truncate color="red" size="small"> <TextCustom truncate color="red" size="small">
Periode Investasi Berakhir Periode Berakhir
</TextCustom> </TextCustom>
</View> </View>
) : ( ) : (
<TextCustom> <TextCustom>Sisa waktu: {value.sisa} hari</TextCustom>
Sisa waktu: {value.sisa} hari
</TextCustom>
)} )}
</StackCustom> </StackCustom>
</Grid.Col> </Grid.Col>

Some files were not shown because too many files have changed in this diff Show More