Compare commits
44 Commits
join
...
notificati
| Author | SHA1 | Date | |
|---|---|---|---|
| 6e2046467f | |||
| ca33dd83bb | |||
| ea3fbdc541 | |||
| 33cd47aaed | |||
| 57ac1eb45e | |||
| 145ad73616 | |||
| 7c85e35c61 | |||
| d098b8ca16 | |||
| 73a473cdc7 | |||
| 3f85f330d2 | |||
| 7743a2467c | |||
| 54611ef812 | |||
| 1503707eed | |||
| a01a9bd93f | |||
| 05c1cac10f | |||
| d27c01ed56 | |||
| 34680a4c38 | |||
| 43c8c105cf | |||
| 2c0198b1b7 | |||
| 573b525352 | |||
| 6f9481c7c9 | |||
| cccb44a835 | |||
| 0f5862ce70 | |||
| 624bd49f69 | |||
| 2446e9d51a | |||
| ab5733f336 | |||
| f5e30087ed | |||
| a93f97ed6a | |||
| 858b441a8c | |||
| 98aaa126a1 | |||
| 69452ff4e7 | |||
| 33ec892ec8 | |||
| 8a900e9469 | |||
| d471682ae7 | |||
| 00eea71248 | |||
| 41e648d8f3 | |||
| 0c4deac6e2 | |||
| 676b8a38be | |||
| 0a2aa71013 | |||
| 868e96a54a | |||
| 059b4d053a | |||
| 76debfd6a6 | |||
| 8c3aec8e57 | |||
| 1ade69ff2f |
@@ -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,8 +100,8 @@ 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 1
|
versionCode 4
|
||||||
versionName "1.0.0"
|
versionName "1.0.1"
|
||||||
|
|
||||||
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
|
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
|
||||||
}
|
}
|
||||||
@@ -180,3 +188,5 @@ dependencies {
|
|||||||
implementation jscFlavor
|
implementation jscFlavor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply plugin: 'com.google.gms.google-services'
|
||||||
29
android/app/google-services.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"project_info": {
|
||||||
|
"project_number": "608461535079",
|
||||||
|
"project_id": "hipmi-badung-connect",
|
||||||
|
"storage_bucket": "hipmi-badung-connect.firebasestorage.app"
|
||||||
|
},
|
||||||
|
"client": [
|
||||||
|
{
|
||||||
|
"client_info": {
|
||||||
|
"mobilesdk_app_id": "1:608461535079:android:4ff12ddc283fb3746761c2",
|
||||||
|
"android_client_info": {
|
||||||
|
"package_name": "com.bip.hipmimobileapp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth_client": [],
|
||||||
|
"api_key": [
|
||||||
|
{
|
||||||
|
"current_key": "AIzaSyBiDtIk3Q9zffFwIdJ5cjqY7e4390JGSkM"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"services": {
|
||||||
|
"appinvite_service": {
|
||||||
|
"other_platform_oauth_client": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"configuration_version": "1"
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
<uses-permission android:name="android.permission.CAMERA"/>
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
@@ -13,7 +15,11 @@
|
|||||||
<data android:scheme="https"/>
|
<data android:scheme="https"/>
|
||||||
</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" android:fullBackupContent="@xml/secure_store_backup_rules" android:dataExtractionRules="@xml/secure_store_data_extraction_rules">
|
||||||
|
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color" tools:replace="android:resource"/>
|
||||||
|
<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"/>
|
||||||
@@ -29,6 +35,12 @@
|
|||||||
<data android:scheme="hipmimobile"/>
|
<data android:scheme="hipmimobile"/>
|
||||||
<data android:scheme="exp+hipmi-mobile"/>
|
<data android:scheme="exp+hipmi-mobile"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter android:autoVerify="true" data-generated="true">
|
||||||
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
<data android:scheme="https" android:host="cld-dkr-staging-hipmi.wibudev.com" android:pathPrefix="/"/>
|
||||||
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
BIN
android/app/src/main/res/drawable-hdpi/notification_icon.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
android/app/src/main/res/drawable-mdpi/notification_icon.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
android/app/src/main/res/drawable-xhdpi/notification_icon.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
android/app/src/main/res/drawable-xxhdpi/notification_icon.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
android/app/src/main/res/drawable-xxxhdpi/notification_icon.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
@@ -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>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<full-backup-content>
|
||||||
|
<exclude domain="sharedpref" path="SECURESTORE"/>
|
||||||
|
</full-backup-content>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<data-extraction-rules>
|
||||||
|
<cloud-backup>
|
||||||
|
<exclude domain="sharedpref" path="SECURESTORE"/>
|
||||||
|
</cloud-backup>
|
||||||
|
<device-transfer>
|
||||||
|
<exclude domain="sharedpref" path="SECURESTORE"/>
|
||||||
|
</device-transfer>
|
||||||
|
</data-extraction-rules>
|
||||||
@@ -6,6 +6,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
|
classpath 'com.google.gms:google-services:4.4.1'
|
||||||
classpath('com.android.tools.build:gradle')
|
classpath('com.android.tools.build:gradle')
|
||||||
classpath('com.facebook.react:react-native-gradle-plugin')
|
classpath('com.facebook.react:react-native-gradle-plugin')
|
||||||
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
|
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
|
||||||
@@ -22,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
|
||||||
@@ -4,7 +4,7 @@ require("dotenv").config();
|
|||||||
export default {
|
export default {
|
||||||
name: "HIPMI Badung Connect",
|
name: "HIPMI Badung Connect",
|
||||||
slug: "hipmi-mobile",
|
slug: "hipmi-mobile",
|
||||||
version: "1.0.0",
|
version: "1.0.1",
|
||||||
orientation: "portrait",
|
orientation: "portrait",
|
||||||
icon: "./assets/images/icon.png",
|
icon: "./assets/images/icon.png",
|
||||||
scheme: "hipmimobile",
|
scheme: "hipmimobile",
|
||||||
@@ -14,19 +14,25 @@ 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.",
|
||||||
},
|
},
|
||||||
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"],
|
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"],
|
||||||
|
buildNumber: "18",
|
||||||
},
|
},
|
||||||
|
|
||||||
android: {
|
android: {
|
||||||
|
googleServicesFile: "./google-services.json",
|
||||||
adaptiveIcon: {
|
adaptiveIcon: {
|
||||||
foregroundImage: "./assets/images/splash-icon.png",
|
foregroundImage: "./assets/images/splash-icon.png",
|
||||||
backgroundColor: "#ffffff",
|
backgroundColor: "#ffffff",
|
||||||
},
|
},
|
||||||
edgeToEdgeEnabled: true,
|
edgeToEdgeEnabled: true,
|
||||||
package: "com.bip.hipmimobileapp",
|
package: "com.bip.hipmimobileapp",
|
||||||
|
versionCode: 4,
|
||||||
// softwareKeyboardLayoutMode: 'resize', // option: untuk mengatur keyboard pada room chst collaboration
|
// softwareKeyboardLayoutMode: 'resize', // option: untuk mengatur keyboard pada room chst collaboration
|
||||||
intentFilters: [
|
intentFilters: [
|
||||||
{
|
{
|
||||||
@@ -36,7 +42,7 @@ export default {
|
|||||||
{
|
{
|
||||||
scheme: "https",
|
scheme: "https",
|
||||||
host: "cld-dkr-staging-hipmi.wibudev.com",
|
host: "cld-dkr-staging-hipmi.wibudev.com",
|
||||||
pathPrefix: "/app",
|
pathPrefix: "/",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
category: ["BROWSABLE", "DEFAULT"],
|
category: ["BROWSABLE", "DEFAULT"],
|
||||||
@@ -71,6 +77,16 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
"expo-font",
|
"expo-font",
|
||||||
|
"@rnmapbox/maps",
|
||||||
|
"@react-native-firebase/app",
|
||||||
|
[
|
||||||
|
"expo-notifications",
|
||||||
|
{
|
||||||
|
icon: "./assets/images/icon.png",
|
||||||
|
color: "#ffffff",
|
||||||
|
iosDisplayInForeground: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
experiments: {
|
experiments: {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { BackButton } from "@/components";
|
import { BackButton } from "@/components";
|
||||||
|
import { IconPlus } from "@/components/_Icon";
|
||||||
|
import { IconDot } from "@/components/_Icon/IconComponent";
|
||||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
@@ -10,6 +12,13 @@ export default function UserLayout() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack screenOptions={HeaderStyles}>
|
<Stack screenOptions={HeaderStyles}>
|
||||||
|
<Stack.Screen
|
||||||
|
name="delete-account"
|
||||||
|
options={{
|
||||||
|
title: "Hapus Akun",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="waiting-room"
|
name="waiting-room"
|
||||||
options={{
|
options={{
|
||||||
@@ -44,24 +53,35 @@ export default function UserLayout() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* ========== Notification Section ========= */}
|
{/* ========== Notification Section ========= */}
|
||||||
<Stack.Screen
|
|
||||||
|
{/* DIPINDAH DI FILE NOTIFICATION USER */}
|
||||||
|
{/* <Stack.Screen
|
||||||
name="notifications/index"
|
name="notifications/index"
|
||||||
options={{
|
options={{
|
||||||
title: "Notifikasi",
|
title: "Notifikasi",
|
||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
|
// headerRight: () => (
|
||||||
|
// <IconPlus
|
||||||
|
// color={MainColor.yellow}
|
||||||
|
// onPress={() => router.push("/test-notifications")}
|
||||||
|
// />
|
||||||
|
// ),
|
||||||
}}
|
}}
|
||||||
/>
|
/> */}
|
||||||
|
|
||||||
{/* ========== Event Section ========= */}
|
{/* ========== Event Section ========= */}
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="event/(tabs)"
|
name="event/(tabs)"
|
||||||
options={{
|
options={{
|
||||||
title: "Event",
|
title: "Event",
|
||||||
headerLeft: () => (
|
// NOTE: DIPINDAH DI FILE /Event/(Tabs)/_layout.tsx
|
||||||
<LeftButtonCustom path="/(application)/(user)/home" />
|
// headerLeft: () => (
|
||||||
),
|
// <LeftButtonCustom path="/(application)/(user)/home" />
|
||||||
|
// ),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="event/create"
|
name="event/create"
|
||||||
options={{
|
options={{
|
||||||
@@ -504,7 +524,8 @@ export default function UserLayout() {
|
|||||||
name="job/(tabs)"
|
name="job/(tabs)"
|
||||||
options={{
|
options={{
|
||||||
title: "Job Vacancy",
|
title: "Job Vacancy",
|
||||||
headerLeft: () => <BackButton path="/home" />,
|
// headerLeft: () => <BackButton path="/home" />,
|
||||||
|
// NOTE: headerLeft di pindahkan ke Tabs Layout
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -588,6 +609,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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
111
app/(application)/(user)/delete-account.tsx
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import {
|
||||||
|
AlertDefaultSystem,
|
||||||
|
BaseBox,
|
||||||
|
ButtonCustom,
|
||||||
|
CenterCustom,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
TextInputCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { apiDeleteUser } from "@/service/api-client/api-user";
|
||||||
|
import { Image } from "expo-image";
|
||||||
|
import { useLocalSearchParams } from "expo-router/build/hooks";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
|
export default function DeleteAccount() {
|
||||||
|
const { token, logout, user } = useAuth();
|
||||||
|
const { phone } = useLocalSearchParams();
|
||||||
|
const [text, setText] = useState("");
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const deleteAccount = async () => {
|
||||||
|
if (text !== "Delete Account") {
|
||||||
|
return Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Ketik 'Delete Account' untuk menghapus akun",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDefaultSystem({
|
||||||
|
title: "Anda yakin akan menghapus akun ini?",
|
||||||
|
message:
|
||||||
|
"Semua data yang pernah anda buat akan terhapus secara permanen !",
|
||||||
|
textLeft: "Batal",
|
||||||
|
textRight: "Ya",
|
||||||
|
onPressRight: async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiDeleteUser({ id: user?.id as string });
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
console.log("RESPONSE >> ", response);
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Akun berhasil dihapus",
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
logout();
|
||||||
|
setLoading(false);
|
||||||
|
}, 2000);
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal menghapus akun",
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("ERROR >> ", error);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper>
|
||||||
|
<StackCustom>
|
||||||
|
<BaseBox>
|
||||||
|
<StackCustom>
|
||||||
|
<CenterCustom>
|
||||||
|
<Image
|
||||||
|
source={require("@/assets/images/constants/logo-hipmi.png")}
|
||||||
|
style={{
|
||||||
|
width: 150,
|
||||||
|
height: 150,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</CenterCustom>
|
||||||
|
<TextCustom align="center">
|
||||||
|
Anda akan menghapus akun dengan nomor +{phone}
|
||||||
|
</TextCustom>
|
||||||
|
<TextCustom align="center">
|
||||||
|
Ketik 'Delete Account' untuk menghapus akun
|
||||||
|
</TextCustom>
|
||||||
|
<TextInputCustom
|
||||||
|
value={text}
|
||||||
|
onChangeText={setText}
|
||||||
|
placeholder="Ketik 'Delete Account'"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ButtonCustom
|
||||||
|
backgroundColor="red"
|
||||||
|
textColor="white"
|
||||||
|
onPress={deleteAccount}
|
||||||
|
isLoading={isLoading}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</ButtonCustom>
|
||||||
|
</StackCustom>
|
||||||
|
</BaseBox>
|
||||||
|
</StackCustom>
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,10 +4,34 @@ import {
|
|||||||
IconHome,
|
IconHome,
|
||||||
IconStatus,
|
IconStatus,
|
||||||
} from "@/components/_Icon";
|
} from "@/components/_Icon";
|
||||||
|
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
|
||||||
import { TabsStyles } from "@/styles/tabs-styles";
|
import { TabsStyles } from "@/styles/tabs-styles";
|
||||||
import { Tabs } from "expo-router";
|
import { router, Tabs, useLocalSearchParams, useNavigation } from "expo-router";
|
||||||
|
import { useLayoutEffect } from "react";
|
||||||
|
|
||||||
export default function EventTabsLayout() {
|
export default function EventTabsLayout() {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
const { from, category } = useLocalSearchParams<{
|
||||||
|
from?: string;
|
||||||
|
category?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
console.log("from", from);
|
||||||
|
console.log("category", category);
|
||||||
|
|
||||||
|
// Atur header secara dinamis
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
|
headerLeft: () => (
|
||||||
|
<BackButtonFromNotification
|
||||||
|
from={from as string}
|
||||||
|
category={category as string}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}, [from, router, navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs screenOptions={TabsStyles}>
|
<Tabs screenOptions={TabsStyles}>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
|
|||||||
@@ -11,15 +11,17 @@ import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
|||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
||||||
import { apiEventGetByStatus } from "@/service/api-client/api-event";
|
import { apiEventGetByStatus } from "@/service/api-client/api-event";
|
||||||
import { useFocusEffect } from "expo-router";
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function EventStatus() {
|
export default function EventStatus() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const { status } = useLocalSearchParams<{ status?: string }>();
|
||||||
|
|
||||||
const id = user?.id || "";
|
const id = user?.id || "";
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||||
"publish"
|
status || "publish"
|
||||||
);
|
);
|
||||||
const [listData, setListData] = useState([]);
|
const [listData, setListData] = useState([]);
|
||||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
const [loadingGetData, setLoadingGetData] = useState(false);
|
||||||
|
|||||||
@@ -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 })}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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)} />,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { apiEventCreate } from "@/service/api-client/api-event";
|
|||||||
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 } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
interface EventCreateProps {
|
interface EventCreateProps {
|
||||||
@@ -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 })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -1,142 +1,12 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import View_Forumku from "@/screens/Forum/ViewForumku";
|
||||||
AvatarComp,
|
import View_Forumku2 from "@/screens/Forum/ViewForumku2";
|
||||||
ButtonCustom,
|
|
||||||
CenterCustom,
|
|
||||||
DrawerCustom,
|
|
||||||
FloatingButton,
|
|
||||||
Grid,
|
|
||||||
LoaderCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
|
||||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
|
||||||
import { apiForumGetAll } from "@/service/api-client/api-forum";
|
|
||||||
import { apiUser } from "@/service/api-client/api-user";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function Forumku() {
|
export default function Forumku() {
|
||||||
const { id } = useLocalSearchParams();
|
|
||||||
const { user } = useAuth();
|
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
const [status, setStatus] = useState("");
|
|
||||||
const [listData, setListData] = useState<any | null>(null);
|
|
||||||
const [dataUser, setDataUser] = useState<any | null>(null);
|
|
||||||
const [loadingGetList, setLoadingGetList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
onLoadDataProfile(id as string);
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadDataProfile = async (id: string) => {
|
|
||||||
try {
|
|
||||||
const response = await apiUser(id);
|
|
||||||
|
|
||||||
setDataUser(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadingGetList(true);
|
|
||||||
const response = await apiForumGetAll({
|
|
||||||
search: "",
|
|
||||||
authorId: id as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
setListData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadingGetList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper
|
{/* <View_Forumku /> */}
|
||||||
floatingButton={
|
<View_Forumku2 />
|
||||||
user?.id === id && (
|
|
||||||
<FloatingButton
|
|
||||||
onPress={() =>
|
|
||||||
router.navigate("/(application)/(user)/forum/create")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<CenterCustom>
|
|
||||||
<AvatarComp
|
|
||||||
fileId={dataUser?.Profile?.imageId}
|
|
||||||
href={`/(application)/(image)/preview-image/${dataUser?.Profile?.imageId}`}
|
|
||||||
size="xl"
|
|
||||||
/>
|
|
||||||
</CenterCustom>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={6}>
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
@{dataUser?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
<TextCustom>{listData?.length || "0"} postingan</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
|
||||||
<ButtonCustom href={`/profile/${dataUser?.Profile?.id}`}>
|
|
||||||
Kunjungi Profile
|
|
||||||
</ButtonCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
{loadingGetList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom> Tidak ada diskusi</TextCustom>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{listData?.map((item: any, index: number) => (
|
|
||||||
<Forum_BoxDetailSection
|
|
||||||
isRightComponent={false}
|
|
||||||
key={index}
|
|
||||||
data={item}
|
|
||||||
isTruncate={true}
|
|
||||||
href={`/forum/${item.id}`}
|
|
||||||
onSetData={(value) => {
|
|
||||||
setOpenDrawer(value.setOpenDrawer);
|
|
||||||
setStatus(value.setStatus);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
{/* Drawer Komponen Eksternal */}
|
|
||||||
<DrawerCustom
|
|
||||||
height={"auto"}
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
>
|
|
||||||
<Forum_MenuDrawerBerandaSection
|
|
||||||
id={id as string}
|
|
||||||
status={status}
|
|
||||||
setIsDrawerOpen={() => {
|
|
||||||
setOpenDrawer(false);
|
|
||||||
}}
|
|
||||||
authorId={id as string}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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({
|
||||||
@@ -223,6 +230,7 @@ export default function ForumDetail() {
|
|||||||
>
|
>
|
||||||
<Forum_MenuDrawerBerandaSection
|
<Forum_MenuDrawerBerandaSection
|
||||||
id={dataId}
|
id={dataId}
|
||||||
|
authorUsername={data?.Author?.username as string}
|
||||||
status={status}
|
status={status}
|
||||||
setIsDrawerOpen={() => {
|
setIsDrawerOpen={() => {
|
||||||
setOpenDrawer(false);
|
setOpenDrawer(false);
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
|||||||
@@ -1,129 +1,12 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Forum_ViewBeranda from "@/screens/Forum/ViewBeranda";
|
||||||
AvatarComp,
|
import Forum_ViewBeranda2 from "@/screens/Forum/ViewBeranda2";
|
||||||
BackButton,
|
|
||||||
DrawerCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import FloatingButton from "@/components/Button/FloatingButton";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
|
||||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
|
||||||
import { apiForumGetAll } from "@/service/api-client/api-forum";
|
|
||||||
import { apiUser } from "@/service/api-client/api-user";
|
|
||||||
import { router, Stack, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function Forum() {
|
export default function Forum() {
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
const [status, setStatus] = useState("");
|
|
||||||
const { user } = useAuth();
|
|
||||||
const [dataUser, setDataUser] = useState<any>();
|
|
||||||
const [listData, setListData] = useState<any[]>();
|
|
||||||
const [loadingGetList, setLoadingGetList] = useState(false);
|
|
||||||
const [search, setSearch] = useState("");
|
|
||||||
const [dataId, setDataId] = useState("");
|
|
||||||
const [authorId, setAuthorId] = useState("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
onLoadDataProfile(user?.id as string);
|
|
||||||
}, [user?.id, search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadDataProfile = async (id: string) => {
|
|
||||||
const response = await apiUser(id);
|
|
||||||
setDataUser(response.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadingGetList(true);
|
|
||||||
const response = await apiForumGetAll({ search: search });
|
|
||||||
|
|
||||||
setListData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadingGetList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
{/* <Forum_ViewBeranda /> */}
|
||||||
options={{
|
<Forum_ViewBeranda2 />
|
||||||
title: "Forum",
|
|
||||||
headerLeft: () => <BackButton />,
|
|
||||||
headerRight: () => (
|
|
||||||
<AvatarComp
|
|
||||||
fileId={dataUser?.Profile?.imageId}
|
|
||||||
size="base"
|
|
||||||
href={`/forum/${user?.id}/forumku`}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={
|
|
||||||
<SearchInput
|
|
||||||
placeholder="Cari topik diskusi"
|
|
||||||
onChangeText={(e) => setSearch(e)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
floatingButton={
|
|
||||||
<FloatingButton
|
|
||||||
onPress={() =>
|
|
||||||
router.navigate("/(application)/(user)/forum/create")
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{loadingGetList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Tidak ada diskusi
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((e: any, i: number) => (
|
|
||||||
<Forum_BoxDetailSection
|
|
||||||
key={i}
|
|
||||||
data={e}
|
|
||||||
onSetData={() => {
|
|
||||||
setDataId(e.id);
|
|
||||||
setOpenDrawer(true);
|
|
||||||
setStatus(e.ForumMaster_StatusPosting?.status);
|
|
||||||
setAuthorId(e.Author?.id);
|
|
||||||
}}
|
|
||||||
isTruncate={true}
|
|
||||||
href={`/forum/${e.id}`}
|
|
||||||
isRightComponent={false}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
height={"auto"}
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
>
|
|
||||||
<Forum_MenuDrawerBerandaSection
|
|
||||||
id={dataId}
|
|
||||||
authorId={authorId}
|
|
||||||
status={status}
|
|
||||||
setIsDrawerOpen={() => {
|
|
||||||
setOpenDrawer(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
202
app/(application)/(user)/forum/terms.tsx
Normal 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",
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -3,7 +3,9 @@
|
|||||||
import { StackCustom, ViewWrapper } from "@/components";
|
import { StackCustom, ViewWrapper } from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { useNotificationStore } from "@/hooks/use-notification-store";
|
||||||
import Home_BottomFeatureSection from "@/screens/Home/bottomFeatureSection";
|
import Home_BottomFeatureSection from "@/screens/Home/bottomFeatureSection";
|
||||||
|
import HeaderBell from "@/screens/Home/HeaderBell";
|
||||||
import Home_ImageSection from "@/screens/Home/imageSection";
|
import Home_ImageSection from "@/screens/Home/imageSection";
|
||||||
import TabSection from "@/screens/Home/tabSection";
|
import TabSection from "@/screens/Home/tabSection";
|
||||||
import { tabsHome } from "@/screens/Home/tabsList";
|
import { tabsHome } from "@/screens/Home/tabsList";
|
||||||
@@ -11,31 +13,52 @@ 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, 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);
|
||||||
|
const { syncUnreadCount } = useNotificationStore();
|
||||||
|
|
||||||
useEffect(() => {
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
checkVersion();
|
checkVersion();
|
||||||
}, []);
|
userData(token as string);
|
||||||
|
syncUnreadCount();
|
||||||
|
}, [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("Response profile >>", JSON.stringify(response?.data?.Profile, null, 2));
|
console.log(
|
||||||
|
"[Profile ID]>>",
|
||||||
|
JSON.stringify(response?.data?.Profile?.id, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkVersion = async () => {
|
const checkVersion = async () => {
|
||||||
const response = await apiVersion();
|
const response = await apiVersion();
|
||||||
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) {
|
||||||
|
// console.log("User is not accept term service");
|
||||||
|
// return <Redirect href={`/terms-agreement`} />;
|
||||||
|
// }
|
||||||
|
|
||||||
if (data && data?.active === false) {
|
if (data && data?.active === false) {
|
||||||
console.log("User is not active");
|
console.log("User is not active");
|
||||||
return <Redirect href={`/waiting-room`} />;
|
return <Redirect href={`/waiting-room`} />;
|
||||||
@@ -61,24 +84,27 @@ export default function Application() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
headerRight: () => (
|
headerRight: () => <HeaderBell />,
|
||||||
<Ionicons
|
|
||||||
name="notifications"
|
|
||||||
size={20}
|
|
||||||
color={MainColor.yellow}
|
|
||||||
onPress={() => {
|
|
||||||
router.push("/notifications");
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<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>
|
||||||
|
{/* <ButtonCustom onPress={() => router.push("./test-notifications")}>
|
||||||
|
Test Notif
|
||||||
|
</ButtonCustom> */}
|
||||||
|
|
||||||
<Home_ImageSection />
|
<Home_ImageSection />
|
||||||
|
|
||||||
<Home_FeatureSection />
|
<Home_FeatureSection />
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -1,10 +1,36 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import { BackButton } from "@/components";
|
||||||
import { IconHome, IconStatus } from "@/components/_Icon";
|
import { IconHome, IconStatus } from "@/components/_Icon";
|
||||||
|
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
|
||||||
import { TabsStyles } from "@/styles/tabs-styles";
|
import { TabsStyles } from "@/styles/tabs-styles";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { Tabs } from "expo-router";
|
import {
|
||||||
|
router,
|
||||||
|
Tabs,
|
||||||
|
useLocalSearchParams,
|
||||||
|
useNavigation
|
||||||
|
} from "expo-router";
|
||||||
|
import { useLayoutEffect } from "react";
|
||||||
|
|
||||||
export default function JobTabsLayout() {
|
export default function JobTabsLayout() {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
const { from, category } = useLocalSearchParams<{
|
||||||
|
from?: string;
|
||||||
|
category?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// Atur header secara dinamis
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
|
headerLeft: () => (
|
||||||
|
<BackButtonFromNotification from={from as string} category={category as string} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}, [from, router, navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Tabs screenOptions={TabsStyles}>
|
<Tabs screenOptions={TabsStyles}>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="index"
|
name="index"
|
||||||
@@ -30,5 +56,6 @@ export default function JobTabsLayout() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,17 @@ import {
|
|||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
||||||
import { apiJobGetByStatus } from "@/service/api-client/api-job";
|
import { apiJobGetByStatus } from "@/service/api-client/api-job";
|
||||||
import { useFocusEffect } from "expo-router";
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
export default function JobStatus() {
|
export default function JobStatus() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const { status } = useLocalSearchParams<{ status?: string }>();
|
||||||
|
console.log("STATUS", status);
|
||||||
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||||
"publish"
|
status || "publish"
|
||||||
);
|
);
|
||||||
const [listData, setListData] = useState<any[]>([]);
|
const [listData, setListData] = useState<any[]>([]);
|
||||||
const [isLoadList, setIsLoadList] = useState(false);
|
const [isLoadList, setIsLoadList] = useState(false);
|
||||||
@@ -60,11 +63,14 @@ export default function JobStatus() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
||||||
{isLoadList ? (
|
{isLoadList ? (
|
||||||
<LoaderCustom />
|
<LoaderCustom />
|
||||||
) : _.isEmpty(listData) ? (
|
) : _.isEmpty(listData) ? (
|
||||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
<TextCustom align="center">
|
||||||
|
Tidak ada data {activeCategory}
|
||||||
|
</TextCustom>
|
||||||
) : (
|
) : (
|
||||||
listData.map((e, i) => (
|
listData.map((e, i) => (
|
||||||
<BaseBox
|
<BaseBox
|
||||||
@@ -80,5 +86,6 @@ export default function JobStatus() {
|
|||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default function JobDetailStatus() {
|
|||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
{data &&
|
{data &&
|
||||||
data?.catatan &&
|
data?.catatan &&
|
||||||
(status === "draft" || status === "rejected") && (
|
(status === "draft" || status === "reject") && (
|
||||||
<ReportBox text={data?.catatan} />
|
<ReportBox text={data?.catatan} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export default function JobDetail() {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const response = await apiJobGetOne({ id: id as string });
|
const response = await apiJobGetOne({ id: id as string });
|
||||||
|
|
||||||
|
console.log("DATA", JSON.stringify(response.data, null,2));
|
||||||
|
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ERROR]", error);
|
console.log("[ERROR]", error);
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { useState } from "react";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function JobCreate() {
|
export default function JobCreate() {
|
||||||
const nextUrl = "/(application)/(user)/job/(tabs)/status";
|
const nextUrl = "/(application)/(user)/job/(tabs)/status?status=review";
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [image, setImage] = useState<string | null>(null);
|
const [image, setImage] = useState<string | null>(null);
|
||||||
|
|||||||
@@ -1,32 +1,6 @@
|
|||||||
import {
|
import MapsView from "@/screens/Maps/MapsView";
|
||||||
ButtonCustom,
|
import MapsView2 from "@/screens/Maps/MapsView2";
|
||||||
DrawerCustom,
|
import { Text, View } from "react-native";
|
||||||
DummyLandscapeImage,
|
|
||||||
Grid,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
|
||||||
import API_IMAGE from "@/constants/api-storage";
|
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
|
||||||
import { apiMapsGetAll } from "@/service/api-client/api-maps";
|
|
||||||
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
|
|
||||||
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
|
||||||
import { Image } from "expo-image";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import MapView, { Marker } from "react-native-maps";
|
|
||||||
|
|
||||||
const defaultRegion = {
|
|
||||||
latitude: -8.737109,
|
|
||||||
longitude: 115.1756897,
|
|
||||||
latitudeDelta: 0.1,
|
|
||||||
longitudeDelta: 0.1,
|
|
||||||
height: 300,
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface LocationItem {
|
export interface LocationItem {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
@@ -37,198 +11,11 @@ export interface LocationItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Maps() {
|
export default function Maps() {
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
const [selected, setSelected] = useState({
|
|
||||||
id: "",
|
|
||||||
bidangBisnis: "",
|
|
||||||
nomorTelepon: "",
|
|
||||||
alamatBisnis: "",
|
|
||||||
namePin: "",
|
|
||||||
imageId: "",
|
|
||||||
portofolioId: "",
|
|
||||||
latitude: 0,
|
|
||||||
longitude: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
handlerLoadList();
|
|
||||||
}, [])
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlerLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiMapsGetAll();
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setList(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
|
<MapsView />
|
||||||
{/* <MapCustom height={"100%"} /> */}
|
{/* <MapsView2 />, */}
|
||||||
<View style={{ flex: 1 }}>
|
{/* <View style={{ flex: 1, backgroundColor: "gray" }}><Text style={{ color: "white" }}>Map disabled</Text></View> */}
|
||||||
{loadList ? (
|
|
||||||
<MapView
|
|
||||||
initialRegion={defaultRegion}
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<MapView
|
|
||||||
initialRegion={defaultRegion}
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{list?.map((item: any, index: number) => {
|
|
||||||
return (
|
|
||||||
<Marker
|
|
||||||
key={item?.id}
|
|
||||||
coordinate={{
|
|
||||||
latitude: item?.latitude,
|
|
||||||
longitude: item?.longitude,
|
|
||||||
}}
|
|
||||||
title={item?.namePin}
|
|
||||||
onPress={() => {
|
|
||||||
setOpenDrawer(true);
|
|
||||||
setSelected({
|
|
||||||
id: item?.id,
|
|
||||||
bidangBisnis:
|
|
||||||
item?.Portofolio?.MasterBidangBisnis?.name,
|
|
||||||
nomorTelepon: item?.Portofolio?.tlpn,
|
|
||||||
alamatBisnis: item?.Portofolio?.alamatKantor,
|
|
||||||
namePin: item?.namePin,
|
|
||||||
imageId: item?.imageId,
|
|
||||||
portofolioId: item?.Portofolio?.id,
|
|
||||||
latitude: item?.latitude,
|
|
||||||
longitude: item?.longitude,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
// Gunakan gambar kustom jika tersedia
|
|
||||||
>
|
|
||||||
<View style={{}}>
|
|
||||||
<Image
|
|
||||||
source={{
|
|
||||||
uri: API_IMAGE.GET({
|
|
||||||
fileId: item?.Portofolio?.logoId,
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
borderRadius: 100,
|
|
||||||
borderWidth: 1,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</Marker>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</MapView>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<DummyLandscapeImage height={200} imageId={selected.imageId} />
|
|
||||||
<Spacing />
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<GridTwoView
|
|
||||||
spanLeft={2}
|
|
||||||
spanRight={10}
|
|
||||||
leftIcon={
|
|
||||||
<FontAwesome
|
|
||||||
name="building-o"
|
|
||||||
size={ICON_SIZE_SMALL}
|
|
||||||
color="white"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
rightIcon={<TextCustom>{selected.namePin}</TextCustom>}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<GridTwoView
|
|
||||||
spanLeft={2}
|
|
||||||
spanRight={10}
|
|
||||||
leftIcon={
|
|
||||||
<Ionicons
|
|
||||||
name="list-outline"
|
|
||||||
size={ICON_SIZE_SMALL}
|
|
||||||
color="white"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
rightIcon={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<GridTwoView
|
|
||||||
spanLeft={2}
|
|
||||||
spanRight={10}
|
|
||||||
leftIcon={
|
|
||||||
<Ionicons
|
|
||||||
name="call-outline"
|
|
||||||
size={ICON_SIZE_SMALL}
|
|
||||||
color="white"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
rightIcon={<TextCustom>{selected.nomorTelepon}</TextCustom>}
|
|
||||||
/>
|
|
||||||
<GridTwoView
|
|
||||||
spanLeft={2}
|
|
||||||
spanRight={10}
|
|
||||||
leftIcon={
|
|
||||||
<Ionicons
|
|
||||||
name="location-outline"
|
|
||||||
size={ICON_SIZE_SMALL}
|
|
||||||
color="white"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
rightIcon={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={6} style={{ paddingRight: 10 }}>
|
|
||||||
<ButtonCustom
|
|
||||||
onPress={() => {
|
|
||||||
setOpenDrawer(false);
|
|
||||||
router.push(`/portofolio/${selected.portofolioId}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Detail
|
|
||||||
</ButtonCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={6} style={{ paddingLeft: 10 }}>
|
|
||||||
<ButtonCustom
|
|
||||||
onPress={() => {
|
|
||||||
openInDeviceMaps({
|
|
||||||
latitude: selected.latitude,
|
|
||||||
longitude: selected.longitude,
|
|
||||||
title: selected.namePin,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Buka Maps
|
|
||||||
</ButtonCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</StackCustom>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,101 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
|
AlertDefaultSystem,
|
||||||
|
BackButton,
|
||||||
BaseBox,
|
BaseBox,
|
||||||
Grid,
|
DrawerCustom,
|
||||||
|
MenuDrawerDynamicGrid,
|
||||||
|
NewWrapper,
|
||||||
ScrollableCustom,
|
ScrollableCustom,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { IconDot } from "@/components/_Icon/IconComponent";
|
||||||
import { useState } from "react";
|
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||||
import { View } from "react-native";
|
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
||||||
|
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||||
const categories = [
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
{ value: "all", label: "Semua" },
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
{ value: "event", label: "Event" },
|
import { useNotificationStore } from "@/hooks/use-notification-store";
|
||||||
{ value: "job", label: "Job" },
|
import { apiGetNotificationsById } from "@/service/api-notifications";
|
||||||
{ value: "voting", label: "Voting" },
|
import { listOfcategoriesAppNotification } from "@/types/type-notification-category";
|
||||||
{ value: "donasi", label: "Donasi" },
|
import { formatChatTime } from "@/utils/formatChatTime";
|
||||||
{ value: "investasi", label: "Investasi" },
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
{ value: "forum", label: "Forum" },
|
import { router, Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
{ value: "collaboration", label: "Collaboration" },
|
import _ from "lodash";
|
||||||
];
|
import { useCallback, useState } from "react";
|
||||||
|
import { RefreshControl, View } from "react-native";
|
||||||
|
|
||||||
const selectedCategory = (value: string) => {
|
const selectedCategory = (value: string) => {
|
||||||
const category = categories.find((c) => c.value === value);
|
const category = listOfcategoriesAppNotification.find(
|
||||||
|
(c) => c.value === value
|
||||||
|
);
|
||||||
return category?.label;
|
return category?.label;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fixPath = ({
|
||||||
|
deepLink,
|
||||||
|
categoryApp,
|
||||||
|
}: {
|
||||||
|
deepLink: string;
|
||||||
|
categoryApp: string;
|
||||||
|
}) => {
|
||||||
|
if (categoryApp === "OTHER") {
|
||||||
|
return deepLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
const separator = deepLink.includes("?") ? "&" : "?";
|
||||||
|
|
||||||
|
const fixedPath = `${deepLink}${separator}from=notifications&category=${_.lowerCase(
|
||||||
|
categoryApp
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
console.log("Fix Path", fixedPath);
|
||||||
|
|
||||||
|
return fixedPath;
|
||||||
|
};
|
||||||
|
|
||||||
const BoxNotification = ({
|
const BoxNotification = ({
|
||||||
index,
|
data,
|
||||||
activeCategory,
|
activeCategory,
|
||||||
}: {
|
}: {
|
||||||
index: number;
|
data: any;
|
||||||
activeCategory: string | null;
|
activeCategory: string | null;
|
||||||
}) => {
|
}) => {
|
||||||
|
// console.log("DATA NOTIFICATION", JSON.stringify(data, null, 2));
|
||||||
|
const { markAsRead } = useNotificationStore();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BaseBox
|
<BaseBox
|
||||||
onPress={() =>
|
backgroundColor={data.isRead ? AccentColor.darkblue : AccentColor.blue}
|
||||||
console.log(
|
onPress={() => {
|
||||||
"Notification >",
|
// console.log(
|
||||||
selectedCategory(activeCategory as string)
|
// "Notification >",
|
||||||
)
|
// selectedCategory(activeCategory as string)
|
||||||
|
// );
|
||||||
|
const newPath = fixPath({
|
||||||
|
deepLink: data.deepLink,
|
||||||
|
categoryApp: data.kategoriApp,
|
||||||
|
});
|
||||||
|
|
||||||
|
router.replace(newPath as any);
|
||||||
|
selectedCategory(activeCategory as string);
|
||||||
|
|
||||||
|
if (!data.isRead) {
|
||||||
|
markAsRead(data.id);
|
||||||
}
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<TextCustom bold>
|
<TextCustom truncate={2} bold>
|
||||||
# {selectedCategory(activeCategory as string)}
|
{data.title}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
|
|
||||||
<View
|
<TextCustom truncate={2}>{data.pesan}</TextCustom>
|
||||||
style={{
|
|
||||||
borderBottomColor: MainColor.white_gray,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sint odio
|
|
||||||
unde quidem voluptate quam culpa sequi molestias ipsa corrupti id,
|
|
||||||
soluta, nostrum adipisci similique, et illo asperiores deleniti eum
|
|
||||||
labore.
|
|
||||||
</TextCustom>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={6}>
|
|
||||||
<TextCustom size="small" color="gray">
|
<TextCustom size="small" color="gray">
|
||||||
{index + 1} Agustus 2025
|
{formatChatTime(data.createdAt)}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
|
||||||
<TextCustom size="small" color="gray">
|
|
||||||
Belum lihat
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
</>
|
</>
|
||||||
@@ -81,17 +103,74 @@ const BoxNotification = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function Notifications() {
|
export default function Notifications() {
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>("all");
|
const { user } = useAuth();
|
||||||
|
const { category } = useLocalSearchParams<{ category?: string }>();
|
||||||
|
const [activeCategory, setActiveCategory] = useState<string | null>(
|
||||||
|
category || "event"
|
||||||
|
);
|
||||||
|
const [listData, setListData] = useState<any[]>([]);
|
||||||
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
|
||||||
|
const { markAsReadAll } = useNotificationStore();
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
const handlePress = (item: any) => {
|
||||||
setActiveCategory(item.value);
|
setActiveCategory(item.value);
|
||||||
// tambahkan logika lain seperti filter dsb.
|
// tambahkan logika lain seperti filter dsb.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
fecthData();
|
||||||
|
}, [activeCategory])
|
||||||
|
);
|
||||||
|
|
||||||
|
const fecthData = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiGetNotificationsById({
|
||||||
|
id: user?.id as any,
|
||||||
|
category: activeCategory as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setListData(response.data);
|
||||||
|
} else {
|
||||||
|
setListData([]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error Notification", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
setRefreshing(true);
|
||||||
|
fecthData();
|
||||||
|
setRefreshing(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
title: "Notifikasi",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
headerRight: () => (
|
||||||
|
<IconDot
|
||||||
|
color={MainColor.yellow}
|
||||||
|
onPress={() => setOpenDrawer(true)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<NewWrapper
|
||||||
headerComponent={
|
headerComponent={
|
||||||
<ScrollableCustom
|
<ScrollableCustom
|
||||||
data={categories.map((e, i) => ({
|
data={listOfcategoriesAppNotification.map((e, i) => ({
|
||||||
id: i,
|
id: i,
|
||||||
label: e.label,
|
label: e.label,
|
||||||
value: e.value,
|
value: e.value,
|
||||||
@@ -100,12 +179,70 @@ export default function Notifications() {
|
|||||||
activeId={activeCategory as string}
|
activeId={activeCategory as string}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
refreshControl={
|
||||||
|
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{Array.from({ length: 20 }).map((e, i) => (
|
{loading ? (
|
||||||
|
<ListSkeletonComponent />
|
||||||
|
) : _.isEmpty(listData) ? (
|
||||||
|
<NoDataText text="Belum ada notifikasi" />
|
||||||
|
) : (
|
||||||
|
listData.map((e, i) => (
|
||||||
<View key={i}>
|
<View key={i}>
|
||||||
<BoxNotification index={i} activeCategory={activeCategory as any} />
|
<BoxNotification
|
||||||
|
data={e}
|
||||||
|
activeCategory={activeCategory as any}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
))}
|
))
|
||||||
</ViewWrapper>
|
)}
|
||||||
|
</NewWrapper>
|
||||||
|
|
||||||
|
<DrawerCustom
|
||||||
|
isVisible={openDrawer}
|
||||||
|
closeDrawer={() => setOpenDrawer(false)}
|
||||||
|
height={"auto"}
|
||||||
|
>
|
||||||
|
<MenuDrawerDynamicGrid
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
label: "Tandai Semua Dibaca",
|
||||||
|
value: "read-all",
|
||||||
|
icon: (
|
||||||
|
<Ionicons
|
||||||
|
name="reader-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color={MainColor.white}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
path: "",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onPressItem={(item: any) => {
|
||||||
|
console.log("Item", item.value);
|
||||||
|
if (item.value === "read-all") {
|
||||||
|
AlertDefaultSystem({
|
||||||
|
title: "Tandai Semua Dibaca",
|
||||||
|
message:
|
||||||
|
"Apakah Anda yakin ingin menandai semua notifikasi dibaca?",
|
||||||
|
textLeft: "Batal",
|
||||||
|
textRight: "Ya",
|
||||||
|
onPressRight: () => {
|
||||||
|
markAsReadAll(user?.id as any);
|
||||||
|
const data = _.cloneDeep(listData);
|
||||||
|
data.forEach((e) => {
|
||||||
|
e.isRead = true;
|
||||||
|
});
|
||||||
|
setListData(data);
|
||||||
|
onRefresh();
|
||||||
|
setOpenDrawer(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DrawerCustom>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
148
app/(application)/(user)/profile/[id]/blocked-list.tsx
Normal 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 />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
93
app/(application)/(user)/profile/[id]/detail-blocked.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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 (
|
||||||
@@ -135,7 +139,9 @@ const ButtonnDot = ({
|
|||||||
isUserCheck: boolean;
|
isUserCheck: boolean;
|
||||||
logout: () => Promise<void>;
|
logout: () => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
const isId = id === undefined || id === null;
|
console.log("[ID] >>", id);
|
||||||
|
|
||||||
|
const isId = id === undefined || id === "undefined";
|
||||||
|
|
||||||
if (isId) {
|
if (isId) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
75
app/(application)/(user)/test-notifications.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import {
|
||||||
|
ButtonCustom,
|
||||||
|
NewWrapper,
|
||||||
|
StackCustom,
|
||||||
|
TextInputCustom,
|
||||||
|
} from "@/components";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { apiNotificationsSend } from "@/service/api-notifications";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
|
export default function TestNotification() {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const [data, setData] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
console.log("[Data Dikirim]", data);
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiNotificationsSend({
|
||||||
|
data: {
|
||||||
|
title: "Test Notification !!",
|
||||||
|
body: data,
|
||||||
|
userLoginId: user?.id || "",
|
||||||
|
appId: "hipmi",
|
||||||
|
status: "publish",
|
||||||
|
kategoriApp: "JOB",
|
||||||
|
type: "announcement",
|
||||||
|
deepLink: "/job/cmhjz8u3h0005cfaxezyeilrr",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
console.log("[RES SEND NOTIF]", JSON.stringify(response, null, 2));
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Notifikasi berhasil dikirim",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengirim notifikasi",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR SEND NOTIF]", error);
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengirim notifikasi",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NewWrapper>
|
||||||
|
<StackCustom>
|
||||||
|
<TextInputCustom
|
||||||
|
required
|
||||||
|
label="Pesan"
|
||||||
|
placeholder="Masukkan pesan"
|
||||||
|
value={data}
|
||||||
|
onChangeText={(text) => setData(text)}
|
||||||
|
/>
|
||||||
|
<ButtonCustom onPress={handleSubmit} disabled={loading}>
|
||||||
|
Kirim
|
||||||
|
</ButtonCustom>
|
||||||
|
</StackCustom>
|
||||||
|
</NewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import {
|
import {
|
||||||
AlertDefaultSystem,
|
AlertDefaultSystem,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
StackCustom,
|
NewWrapper,
|
||||||
ViewWrapper,
|
StackCustom
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
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";
|
||||||
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 +33,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 +82,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 +101,9 @@ export default function WaitingRoom() {
|
|||||||
icon="refresh-ccw"
|
icon="refresh-ccw"
|
||||||
>
|
>
|
||||||
Check
|
Check
|
||||||
</ButtonCenteredOnly>
|
</ButtonCenteredOnly> */}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,29 @@
|
|||||||
import { BackButton } from "@/components";
|
import { BackButton } from "@/components";
|
||||||
|
import BackgroundNotificationHandler from "@/components/Notification/BackgroundNotificationHandler";
|
||||||
|
import NotificationInitializer from "@/components/Notification/NotificationInitializer";
|
||||||
|
import { NotificationProvider } from "@/hooks/use-notification-store";
|
||||||
import { HeaderStyles } from "@/styles/header-styles";
|
import { HeaderStyles } from "@/styles/header-styles";
|
||||||
import { Stack } from "expo-router";
|
import { Stack } from "expo-router";
|
||||||
|
|
||||||
export default function ApplicationLayout() {
|
export default function ApplicationLayout() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NotificationProvider>
|
||||||
|
<NotificationInitializer />
|
||||||
|
<BackgroundNotificationHandler />
|
||||||
|
<ApplicationStack />
|
||||||
|
</NotificationProvider>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ApplicationStack() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack screenOptions={HeaderStyles}>
|
<Stack screenOptions={HeaderStyles}>
|
||||||
<Stack.Screen name="(user)" options={{ headerShown: false }} />
|
<Stack.Screen name="(user)" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="admin" options={{ headerShown: false }} />
|
<Stack.Screen name="admin" options={{ headerShown: false }} />
|
||||||
|
|
||||||
|
|
||||||
{/* Take Picture */}
|
{/* Take Picture */}
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="(image)/take-picture/[id]/index"
|
name="(image)/take-picture/[id]/index"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
ICON_SIZE_XLARGE,
|
ICON_SIZE_XLARGE,
|
||||||
} from "@/constants/constans-value";
|
} from "@/constants/constans-value";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import AdminNotificationBell from "@/screens/Admin/AdminNotificationBell";
|
||||||
import {
|
import {
|
||||||
adminListMenu,
|
adminListMenu,
|
||||||
superAdminListMenu,
|
superAdminListMenu,
|
||||||
@@ -192,11 +193,12 @@ export default function AdminLayout() {
|
|||||||
label: "Notifikasi",
|
label: "Notifikasi",
|
||||||
value: "notification",
|
value: "notification",
|
||||||
icon: (
|
icon: (
|
||||||
<Ionicons
|
// <Ionicons
|
||||||
name="notifications"
|
// name="notifications"
|
||||||
size={ICON_SIZE_SMALL}
|
// size={ICON_SIZE_SMALL}
|
||||||
color={MainColor.white}
|
// color={MainColor.white}
|
||||||
/>
|
// />
|
||||||
|
<AdminNotificationBell/>
|
||||||
),
|
),
|
||||||
path: "/admin/notification",
|
path: "/admin/notification",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ const listPage = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2",
|
||||||
label: "Bidang Bisnis",
|
label: "Bidang & Sub Bidang",
|
||||||
value: "business",
|
value: "business",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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>}
|
||||||
|
|||||||
@@ -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>}
|
||||||
|
|||||||
@@ -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>}
|
||||||
|
|||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -38,7 +38,12 @@ 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();
|
||||||
@@ -121,6 +126,7 @@ export default function AdminEventDetail() {
|
|||||||
const response = await funUpdateStatusEvent({
|
const response = await funUpdateStatusEvent({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
changeStatus: "publish",
|
changeStatus: "publish",
|
||||||
|
data: {catatan: "", senderId: user?.id as string}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
@@ -156,7 +162,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 +187,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}
|
||||||
@@ -190,6 +196,8 @@ export default function AdminEventDetail() {
|
|||||||
// color="black"
|
// color="black"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<TextCustom align="center">{isDevLink}</TextCustom>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
|
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
|
||||||
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
|
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
@@ -14,9 +15,13 @@ import { useCallback, useState } from "react";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminEventRejectInput() {
|
export default function AdminEventRejectInput() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
|
|
||||||
const [data, setData] = useState<any>("");
|
const [data, setData] = useState<any>({
|
||||||
|
catatan: "",
|
||||||
|
senderId: "",
|
||||||
|
});
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
@@ -45,10 +50,16 @@ export default function AdminEventRejectInput() {
|
|||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const newData = {
|
||||||
|
catatan: data,
|
||||||
|
senderId: user?.id as string,
|
||||||
|
};
|
||||||
|
|
||||||
const response = await funUpdateStatusEvent({
|
const response = await funUpdateStatusEvent({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
changeStatus,
|
changeStatus,
|
||||||
data: data,
|
data: newData,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
|||||||
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
||||||
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 { useAuth } from "@/hooks/use-auth";
|
||||||
import funUpdateStatusJob from "@/screens/Admin/Job/funUpdateStatus";
|
import funUpdateStatusJob from "@/screens/Admin/Job/funUpdateStatus";
|
||||||
import { apiAdminJobGetById } from "@/service/api-admin/api-admin-job";
|
import { apiAdminJobGetById } from "@/service/api-admin/api-admin-job";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
@@ -23,8 +24,10 @@ import { useCallback, useState } from "react";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminJobDetailStatus() {
|
export default function AdminJobDetailStatus() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
const [data, setData] = useState<any | null>(null);
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
@@ -92,6 +95,9 @@ export default function AdminJobDetailStatus() {
|
|||||||
const response = await funUpdateStatusJob({
|
const response = await funUpdateStatusJob({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
changeStatus,
|
changeStatus,
|
||||||
|
data: {
|
||||||
|
senderId: user?.id as string,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
@@ -142,12 +148,15 @@ export default function AdminJobDetailStatus() {
|
|||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
{data && data?.catatan && (status === "reject" || status === "review") && (
|
{data &&
|
||||||
|
data?.catatan &&
|
||||||
|
(status === "reject" || status === "review") && (
|
||||||
<ReportBox text={data?.catatan} />
|
<ReportBox text={data?.catatan} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{status === "review" && (
|
{status === "review" && (
|
||||||
<AdminButtonReview
|
<AdminButtonReview
|
||||||
|
isLoading={isLoading}
|
||||||
onPublish={() => {
|
onPublish={() => {
|
||||||
AlertDefaultSystem({
|
AlertDefaultSystem({
|
||||||
title: "Publish",
|
title: "Publish",
|
||||||
@@ -156,6 +165,7 @@ export default function AdminJobDetailStatus() {
|
|||||||
textRight: "Ya",
|
textRight: "Ya",
|
||||||
onPressRight: () => {
|
onPressRight: () => {
|
||||||
handleUpdate({ changeStatus: "publish" });
|
handleUpdate({ changeStatus: "publish" });
|
||||||
|
setIsLoading(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ import Toast from "react-native-toast-message";
|
|||||||
|
|
||||||
export default function AdminJobRejectInput() {
|
export default function AdminJobRejectInput() {
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
const [data, setData] = useState<any | null>(null);
|
const [data, setData] = useState({
|
||||||
|
catatan: "",
|
||||||
|
senderId: ""
|
||||||
|
});
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
@@ -102,8 +105,8 @@ export default function AdminJobRejectInput() {
|
|||||||
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
|
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
|
||||||
>
|
>
|
||||||
<TextAreaCustom
|
<TextAreaCustom
|
||||||
value={data}
|
value={data?.catatan}
|
||||||
onChangeText={setData}
|
onChangeText={(text) => setData({ ...data, catatan: text })}
|
||||||
placeholder="Masukan alasan"
|
placeholder="Masukan alasan"
|
||||||
required
|
required
|
||||||
showCount
|
showCount
|
||||||
|
|||||||
@@ -1,20 +1,213 @@
|
|||||||
import { BackButton, TextCustom, ViewWrapper } from "@/components";
|
import {
|
||||||
import { Stack } from "expo-router";
|
AlertDefaultSystem,
|
||||||
|
BackButton,
|
||||||
|
BaseBox,
|
||||||
|
DrawerCustom,
|
||||||
|
MenuDrawerDynamicGrid,
|
||||||
|
NewWrapper,
|
||||||
|
ScrollableCustom,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
} from "@/components";
|
||||||
|
import { IconPlus } from "@/components/_Icon";
|
||||||
|
import { IconDot } from "@/components/_Icon/IconComponent";
|
||||||
|
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||||
|
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
||||||
|
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||||
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { useNotificationStore } from "@/hooks/use-notification-store";
|
||||||
|
import { apiGetNotificationsById } from "@/service/api-notifications";
|
||||||
|
import { listOfcategoriesAppNotification } from "@/types/type-notification-category";
|
||||||
|
import { formatChatTime } from "@/utils/formatChatTime";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
import { router, Stack, useFocusEffect } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
import { RefreshControl, View } from "react-native";
|
||||||
|
|
||||||
|
const selectedCategory = (value: string) => {
|
||||||
|
const category = listOfcategoriesAppNotification.find(
|
||||||
|
(c) => c.value === value
|
||||||
|
);
|
||||||
|
return category?.label;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BoxNotification = ({
|
||||||
|
data,
|
||||||
|
activeCategory,
|
||||||
|
}: {
|
||||||
|
data: any;
|
||||||
|
activeCategory: string | null;
|
||||||
|
}) => {
|
||||||
|
const { markAsRead } = useNotificationStore();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BaseBox
|
||||||
|
backgroundColor={data.isRead ? AccentColor.darkblue : AccentColor.blue}
|
||||||
|
onPress={() => {
|
||||||
|
console.log(
|
||||||
|
"Notification >",
|
||||||
|
selectedCategory(activeCategory as string)
|
||||||
|
);
|
||||||
|
router.push(data.deepLink);
|
||||||
|
markAsRead(data.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<StackCustom>
|
||||||
|
<TextCustom truncate={2} bold>
|
||||||
|
{data.title}
|
||||||
|
</TextCustom>
|
||||||
|
|
||||||
|
<TextCustom truncate={2}>{data.pesan}</TextCustom>
|
||||||
|
|
||||||
|
<TextCustom size="small" color="gray">
|
||||||
|
{formatChatTime(data.createdAt)}
|
||||||
|
</TextCustom>
|
||||||
|
</StackCustom>
|
||||||
|
</BaseBox>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default function AdminNotification() {
|
export default function AdminNotification() {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const [activeCategory, setActiveCategory] = useState<string | null>("event");
|
||||||
|
const [listData, setListData] = useState<any[]>([]);
|
||||||
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
|
|
||||||
|
const { markAsReadAll } = useNotificationStore();
|
||||||
|
|
||||||
|
const handlePress = (item: any) => {
|
||||||
|
setActiveCategory(item.value);
|
||||||
|
// tambahkan logika lain seperti filter dsb.
|
||||||
|
};
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
fecthData();
|
||||||
|
}, [activeCategory])
|
||||||
|
);
|
||||||
|
|
||||||
|
const fecthData = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiGetNotificationsById({
|
||||||
|
id: user?.id as any,
|
||||||
|
category: activeCategory as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
setListData(response.data);
|
||||||
|
} else {
|
||||||
|
setListData([]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error Notification", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRefresh = () => {
|
||||||
|
setRefreshing(true);
|
||||||
|
fecthData();
|
||||||
|
setRefreshing(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{
|
options={{
|
||||||
title: "Admin Notifikasi",
|
title: "Admin Notifikasi",
|
||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
headerRight: () => <></>,
|
headerRight: () => (
|
||||||
|
<IconDot
|
||||||
|
color={MainColor.yellow}
|
||||||
|
onPress={() => setOpenDrawer(true)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
<TextCustom>Notification</TextCustom>
|
headerComponent={
|
||||||
</ViewWrapper>
|
<ScrollableCustom
|
||||||
|
data={listOfcategoriesAppNotification.map((e, i) => ({
|
||||||
|
id: i,
|
||||||
|
label: e.label,
|
||||||
|
value: e.value,
|
||||||
|
}))}
|
||||||
|
onButtonPress={handlePress}
|
||||||
|
activeId={activeCategory as string}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
refreshControl={
|
||||||
|
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<ListSkeletonComponent />
|
||||||
|
) : _.isEmpty(listData) ? (
|
||||||
|
<NoDataText text="Belum ada notifikasi" />
|
||||||
|
) : (
|
||||||
|
listData.map((e, i) => (
|
||||||
|
<View key={i}>
|
||||||
|
<BoxNotification
|
||||||
|
data={e}
|
||||||
|
activeCategory={activeCategory as any}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</NewWrapper>
|
||||||
|
|
||||||
|
<DrawerCustom
|
||||||
|
isVisible={openDrawer}
|
||||||
|
closeDrawer={() => setOpenDrawer(false)}
|
||||||
|
height={"auto"}
|
||||||
|
>
|
||||||
|
<MenuDrawerDynamicGrid
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
label: "Tandai Semua Dibaca",
|
||||||
|
value: "read-all",
|
||||||
|
icon: (
|
||||||
|
<Ionicons
|
||||||
|
name="reader-outline"
|
||||||
|
size={ICON_SIZE_SMALL}
|
||||||
|
color={MainColor.white}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
path: "",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onPressItem={(item: any) => {
|
||||||
|
console.log("Item", item.value);
|
||||||
|
if (item.value === "read-all") {
|
||||||
|
AlertDefaultSystem({
|
||||||
|
title: "Tandai Semua Dibaca",
|
||||||
|
message:
|
||||||
|
"Apakah Anda yakin ingin menandai semua notifikasi dibaca?",
|
||||||
|
textLeft: "Batal",
|
||||||
|
textRight: "Ya",
|
||||||
|
onPressRight: () => {
|
||||||
|
markAsReadAll(user?.id as any);
|
||||||
|
const data = _.cloneDeep(listData);
|
||||||
|
data.forEach((e) => {
|
||||||
|
e.isRead = true;
|
||||||
|
});
|
||||||
|
setListData(data);
|
||||||
|
onRefresh();
|
||||||
|
setOpenDrawer(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DrawerCustom>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export default function SuperAdminDetail() {
|
|||||||
const response = await apiAdminUserAccessUpdateStatus({
|
const response = await apiAdminUserAccessUpdateStatus({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
role: data?.masterUserRoleId === "2" ? "user" : "admin",
|
role: data?.masterUserRoleId === "2" ? "user" : "admin",
|
||||||
|
category: "role"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export default function SuperAdmin_ListUser() {
|
|||||||
</TextCustom>
|
</TextCustom>
|
||||||
}
|
}
|
||||||
component2={
|
component2={
|
||||||
<TextCustom align="center" bold>
|
<TextCustom bold>
|
||||||
Username
|
Username
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,21 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { routeUser } from "@/lib/routeApp";
|
||||||
import {
|
import {
|
||||||
apiAdminUserAccessGetById,
|
apiAdminUserAccessGetById,
|
||||||
apiAdminUserAccessUpdateStatus,
|
apiAdminUserAccessUpdateStatus,
|
||||||
} from "@/service/api-admin/api-admin-user-access";
|
} from "@/service/api-admin/api-admin-user-access";
|
||||||
|
import {
|
||||||
|
apiNotificationsSendById
|
||||||
|
} from "@/service/api-notifications";
|
||||||
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 Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminUserAccessDetail() {
|
export default function AdminUserAccessDetail() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [data, setData] = useState<any | null>(null);
|
const [data, setData] = useState<any | null>(null);
|
||||||
const [loadData, setLoadData] = useState(false);
|
const [loadData, setLoadData] = useState(false);
|
||||||
@@ -33,6 +39,7 @@ export default function AdminUserAccessDetail() {
|
|||||||
try {
|
try {
|
||||||
setLoadData(true);
|
setLoadData(true);
|
||||||
const response = await apiAdminUserAccessGetById({ id: id as string });
|
const response = await apiAdminUserAccessGetById({ id: id as string });
|
||||||
|
console.log("[DATA]", JSON.stringify(response.data, null, 2));
|
||||||
|
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -48,6 +55,7 @@ export default function AdminUserAccessDetail() {
|
|||||||
const response = await apiAdminUserAccessUpdateStatus({
|
const response = await apiAdminUserAccessUpdateStatus({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
active: !data?.active,
|
active: !data?.active,
|
||||||
|
category: "access",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
@@ -61,6 +69,21 @@ export default function AdminUserAccessDetail() {
|
|||||||
type: "success",
|
type: "success",
|
||||||
text1: "Update aktifasi berhasil ",
|
text1: "Update aktifasi berhasil ",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (data.active === false) {
|
||||||
|
await apiNotificationsSendById({
|
||||||
|
data: {
|
||||||
|
title: "Akun anda telah diaktifkan",
|
||||||
|
body: "Selamat menjelajahi HIConnect",
|
||||||
|
userLoginId: user?.id || "",
|
||||||
|
kategoriApp: "OTHER",
|
||||||
|
type: "announcement",
|
||||||
|
deepLink: routeUser.home,
|
||||||
|
},
|
||||||
|
id: id as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
router.back();
|
router.back();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ERROR UPDATE STATUS]", error);
|
console.log("[ERROR UPDATE STATUS]", error);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>}
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
|
|||||||
116
app/(application)/terms-agreement.tsx
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
|
ButtonCustom,
|
||||||
|
CheckboxCustom,
|
||||||
|
InformationBox,
|
||||||
|
StackCustom,
|
||||||
|
ViewWrapper
|
||||||
|
} from "@/components";
|
||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { apiAcceptTermService, BASE_URL } from "@/service/api-config";
|
||||||
|
import { GStyles } from "@/styles/global-styles";
|
||||||
|
import { openBrowser } from "@/utils/openBrower";
|
||||||
|
import { Stack } from "expo-router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { Text, View } from "react-native";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
|
export default function TermsAgreement() {
|
||||||
|
const { user, logout } = useAuth();
|
||||||
|
const [term, setTerm] = useState(false);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const url = BASE_URL;
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
setIsLoading(true);
|
||||||
|
const response = await apiAcceptTermService({
|
||||||
|
data: {
|
||||||
|
id: user?.id as string,
|
||||||
|
termsOfServiceAccepted: term,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.success) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal",
|
||||||
|
text2: response.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Anda berhasil menerima syarat & ketentuan",
|
||||||
|
text2: "Silahkan login kembali",
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
logout();
|
||||||
|
}, 2000);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const footerComponent = (
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
onPress={handleSubmit}
|
||||||
|
disabled={!term}
|
||||||
|
isLoading={isLoading}
|
||||||
|
>
|
||||||
|
Setuju
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{
|
||||||
|
title: "Terms & Conditions",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ViewWrapper footerComponent={footerComponent}>
|
||||||
|
<StackCustom>
|
||||||
|
<InformationBox text="Anda dialihkan ke halaman syarat & ketentuan, karena Anda belum menerima persetujuan syarat & ketentuan." />
|
||||||
|
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginTop: 16,
|
||||||
|
marginBottom: 16,
|
||||||
|
paddingInline: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CheckboxCustom value={term} onChange={() => setTerm(!term)} />
|
||||||
|
|
||||||
|
<Text style={GStyles.textLabel}>
|
||||||
|
Saya setuju dengan{" "}
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: MainColor.yellow,
|
||||||
|
textDecorationLine: "underline",
|
||||||
|
}}
|
||||||
|
onPress={() => {
|
||||||
|
const toUrl = `${url}/terms-of-service.html`;
|
||||||
|
openBrowser(toUrl);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Syarat & Ketentuan
|
||||||
|
</Text>{" "}
|
||||||
|
yang melarang konten tidak pantas dan perilaku merugikan.
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</StackCustom>
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,9 +1,18 @@
|
|||||||
import { StackCustom, TextCustom, ViewWrapper } from "@/components";
|
import { BackButton, StackCustom, TextCustom, ViewWrapper } from "@/components";
|
||||||
|
import { Stack } from "expo-router";
|
||||||
|
|
||||||
export default function NotFoundScreen() {
|
export default function NotFoundScreen() {
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<Stack.Screen
|
||||||
|
options={{ headerShown: false, headerLeft: () => <BackButton /> }}
|
||||||
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom align="center" gap={0} style={{justifyContent: "center", alignItems: "center", flex: 1}}>
|
<StackCustom
|
||||||
|
align="center"
|
||||||
|
gap={0}
|
||||||
|
style={{ justifyContent: "center", alignItems: "center", flex: 1 }}
|
||||||
|
>
|
||||||
<TextCustom size="large" bold style={{ fontSize: 100 }}>
|
<TextCustom size="large" bold style={{ fontSize: 100 }}>
|
||||||
404
|
404
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
@@ -12,5 +21,6 @@ export default function NotFoundScreen() {
|
|||||||
</TextCustom>
|
</TextCustom>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
9
app/eula.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import EULAView from "@/screens/Authentication/EULAView";
|
||||||
|
|
||||||
|
export default function EULA() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EULAView />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 509 KiB |
BIN
assets/images/constants/logo-hipmi_back.png
Normal file
|
After Width: | Height: | Size: 1.0 MiB |
16
components/Alert/AlertWarning.ts
Normal 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: () => {},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -12,10 +12,12 @@ const LeftButtonCustom = ({
|
|||||||
path,
|
path,
|
||||||
icon = "arrow-back",
|
icon = "arrow-back",
|
||||||
iconCustom,
|
iconCustom,
|
||||||
|
onPress,
|
||||||
}: {
|
}: {
|
||||||
path?: Href;
|
path?: Href;
|
||||||
icon?: React.ReactNode | any;
|
icon?: React.ReactNode | any;
|
||||||
iconCustom?: React.ReactNode;
|
iconCustom?: React.ReactNode;
|
||||||
|
onPress?: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -26,7 +28,7 @@ const LeftButtonCustom = ({
|
|||||||
name={icon}
|
name={icon}
|
||||||
size={20}
|
size={20}
|
||||||
color={MainColor.yellow}
|
color={MainColor.yellow}
|
||||||
onPress={() => (path ? router.replace(path) : router.back())}
|
onPress={() => (onPress ? onPress() : path ? router.replace(path) : router.back())}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
29
components/Button/BackButtonFromNotification.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { useRouter } from "expo-router";
|
||||||
|
import { BackButton } from "..";
|
||||||
|
|
||||||
|
export default function BackButtonFromNotification({
|
||||||
|
from,
|
||||||
|
category,
|
||||||
|
}: {
|
||||||
|
from: string;
|
||||||
|
category?: string;
|
||||||
|
}) {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BackButton
|
||||||
|
onPress={() => {
|
||||||
|
if (from === "notifications") {
|
||||||
|
router.replace(`/notifications?category=${category}`);
|
||||||
|
} else {
|
||||||
|
if (from) {
|
||||||
|
router.replace(`/${from}` as any);
|
||||||
|
} else {
|
||||||
|
router.navigate("/home");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|
||||||
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
justifyContent: "flex-start",
|
justifyContent: "flex-start",
|
||||||
marginInline: 0.1
|
// marginInline: 0.1
|
||||||
|
margin: 0.1
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
140
components/Notification/BackgroundNotificationHandler.tsx
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
// src/components/BackgroundNotificationHandler.tsx
|
||||||
|
import { useNotificationStore } from "@/hooks/use-notification-store";
|
||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
|
import {
|
||||||
|
FirebaseMessagingTypes,
|
||||||
|
getInitialNotification,
|
||||||
|
getMessaging,
|
||||||
|
onNotificationOpenedApp,
|
||||||
|
} from "@react-native-firebase/messaging";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
const HANDLED_NOTIFICATIONS_KEY = "handled_notifications";
|
||||||
|
|
||||||
|
export default function BackgroundNotificationHandler() {
|
||||||
|
const { addNotification, markAsRead } = useNotificationStore();
|
||||||
|
const messaging = getMessaging();
|
||||||
|
const unsubscribeRef = useRef<(() => void) | null>(null); // 🔑 cegah duplikasi
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const init = async () => {
|
||||||
|
// 1. Handle (cold start)
|
||||||
|
const initialNotification = await getInitialNotification(messaging);
|
||||||
|
if (initialNotification) {
|
||||||
|
handleNotification(initialNotification);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Handle background
|
||||||
|
if (unsubscribeRef.current) {
|
||||||
|
unsubscribeRef.current();
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsubscribe = onNotificationOpenedApp(
|
||||||
|
messaging,
|
||||||
|
(remoteMessage) => {
|
||||||
|
handleNotification(remoteMessage);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
unsubscribeRef.current = unsubscribe;
|
||||||
|
};
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
// Cleanup saat komponen unmount
|
||||||
|
return () => {
|
||||||
|
if (unsubscribeRef.current) {
|
||||||
|
unsubscribeRef.current();
|
||||||
|
unsubscribeRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [addNotification, messaging]);
|
||||||
|
|
||||||
|
const isNotificationHandled = async (
|
||||||
|
notificationId: string
|
||||||
|
): Promise<boolean> => {
|
||||||
|
const handled = await AsyncStorage.getItem(HANDLED_NOTIFICATIONS_KEY);
|
||||||
|
const ids = handled ? JSON.parse(handled) : [];
|
||||||
|
return ids.includes(notificationId);
|
||||||
|
};
|
||||||
|
|
||||||
|
const markNotificationAsHandled = async (notificationId: string) => {
|
||||||
|
const handled = await AsyncStorage.getItem(HANDLED_NOTIFICATIONS_KEY);
|
||||||
|
const ids = handled ? JSON.parse(handled) : [];
|
||||||
|
if (!ids.includes(notificationId)) {
|
||||||
|
ids.push(notificationId);
|
||||||
|
// Simpan maksimal 50 ID terakhir untuk hindari memori bocor
|
||||||
|
await AsyncStorage.setItem(
|
||||||
|
HANDLED_NOTIFICATIONS_KEY,
|
||||||
|
JSON.stringify(ids.slice(-50))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNotification = async (
|
||||||
|
remoteMessage: FirebaseMessagingTypes.RemoteMessage
|
||||||
|
) => {
|
||||||
|
const { notification, data } = remoteMessage;
|
||||||
|
if (!notification?.title) return;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"🚀 Notification received:",
|
||||||
|
JSON.stringify(remoteMessage, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
const notificationId = data?.id;
|
||||||
|
if (!notificationId || typeof notificationId !== "string") {
|
||||||
|
console.warn("Notification missing notificationId, skipping navigation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Cek apakah sudah pernah ditangani
|
||||||
|
if (await isNotificationHandled(notificationId)) {
|
||||||
|
console.log("Notification already handled, skipping:", notificationId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Tandai sebagai ditangani
|
||||||
|
await markNotificationAsHandled(notificationId);
|
||||||
|
|
||||||
|
// ✅ Normalisasi deepLink: pastikan string
|
||||||
|
let deepLink: string | undefined;
|
||||||
|
if (data?.deepLink) {
|
||||||
|
if (typeof data.deepLink === "string") {
|
||||||
|
deepLink = data.deepLink;
|
||||||
|
} else {
|
||||||
|
// Jika object (jarang), coba string-kan
|
||||||
|
deepLink = JSON.stringify(data.deepLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tambahkan ke UI state (agar muncul di daftar notifikasi & badge)
|
||||||
|
addNotification({
|
||||||
|
title: notification.title,
|
||||||
|
body: notification.body || "",
|
||||||
|
type: "announcement",
|
||||||
|
data: data as Record<string, string>, // aman karena di-normalisasi di useNotificationStore
|
||||||
|
});
|
||||||
|
|
||||||
|
markAsRead(data?.id as any);
|
||||||
|
|
||||||
|
// Navigasi
|
||||||
|
if (
|
||||||
|
data?.deepLink &&
|
||||||
|
typeof data.deepLink === "string" &&
|
||||||
|
data.deepLink.startsWith("/")
|
||||||
|
) {
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
router.push(data.deepLink as any);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Navigation failed:", error);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
112
components/Notification/NotificationInitializer.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
// src/components/NotificationInitializer.tsx
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { useForegroundNotifications } from "@/hooks/use-foreground-notifications";
|
||||||
|
import { useNotificationStore } from "@/hooks/use-notification-store";
|
||||||
|
import type { FirebaseMessagingTypes } from "@react-native-firebase/messaging";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { Platform } from "react-native";
|
||||||
|
import * as Device from "expo-device";
|
||||||
|
import * as Application from "expo-application";
|
||||||
|
import { apiDeviceRegisterToken } from "@/service/api-device-token";
|
||||||
|
import messaging, {
|
||||||
|
isSupported,
|
||||||
|
requestPermission,
|
||||||
|
getToken,
|
||||||
|
AuthorizationStatus,
|
||||||
|
} from "@react-native-firebase/messaging";
|
||||||
|
|
||||||
|
// ✅ Modular imports (sesuai v22+)
|
||||||
|
|
||||||
|
export default function NotificationInitializer() {
|
||||||
|
// Setup handler notifikasi
|
||||||
|
const { user, logout } = useAuth(); // dari AuthContext
|
||||||
|
const { addNotification } = useNotificationStore();
|
||||||
|
|
||||||
|
// Ambil token FCM (opsional, hanya untuk log)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!user) {
|
||||||
|
console.log("User not available, skipping token sync");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const registerDeviceToken = async () => {
|
||||||
|
try {
|
||||||
|
// ✅ Dapatkan instance messaging
|
||||||
|
const messagingInstance = messaging();
|
||||||
|
|
||||||
|
// ✅ Gunakan instance sebagai argumen
|
||||||
|
const supported = await isSupported(messagingInstance);
|
||||||
|
if (!supported) {
|
||||||
|
console.log("‼️ FCM tidak didukung");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const authStatus = await requestPermission(messagingInstance);
|
||||||
|
if (authStatus !== AuthorizationStatus.AUTHORIZED) {
|
||||||
|
console.warn("Izin telah ditolak");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fcmToken = await getToken(messagingInstance);
|
||||||
|
if (!fcmToken) {
|
||||||
|
console.warn("Tidak bisa mendapatkan FCM token");
|
||||||
|
// logout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("✅ FCM Token:", fcmToken);
|
||||||
|
|
||||||
|
const platform = Platform.OS; // "ios" | "android"
|
||||||
|
const model = Device.modelName || "unknown";
|
||||||
|
const appVersion =
|
||||||
|
(Application.nativeApplicationVersion || "unknown") +
|
||||||
|
"-" +
|
||||||
|
(Application.nativeBuildVersion || "unknown");
|
||||||
|
const deviceId =
|
||||||
|
Device.osInternalBuildId || Device.modelName || "unknown";
|
||||||
|
|
||||||
|
// Kirim ke backend
|
||||||
|
await apiDeviceRegisterToken({
|
||||||
|
data: {
|
||||||
|
fcmToken,
|
||||||
|
platform,
|
||||||
|
deviceId,
|
||||||
|
model,
|
||||||
|
appVersion,
|
||||||
|
userId: user?.id || "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("✅ Device token berhasil didaftarkan ke backend");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Gagal mendaftarkan device token:", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
registerDeviceToken();
|
||||||
|
}, [user?.id]);
|
||||||
|
|
||||||
|
const handleForegroundNotification = (
|
||||||
|
message: FirebaseMessagingTypes.RemoteMessage
|
||||||
|
) => {
|
||||||
|
const title = message.notification?.title || "Notifikasi";
|
||||||
|
const body = message.notification?.body || "";
|
||||||
|
const rawData = message.data || {};
|
||||||
|
|
||||||
|
const safeData: Record<string, string> = {};
|
||||||
|
for (const key in rawData) {
|
||||||
|
safeData[key] =
|
||||||
|
typeof rawData[key] === "string"
|
||||||
|
? rawData[key]
|
||||||
|
: JSON.stringify(rawData[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("📥 Menambahkan ke store:", { title, body, safeData });
|
||||||
|
addNotification({ title, body, data: safeData, type: "announcement" });
|
||||||
|
console.log("✅ Notifikasi ditambahkan ke state");
|
||||||
|
};
|
||||||
|
|
||||||
|
useForegroundNotifications(handleForegroundNotification);
|
||||||
|
|
||||||
|
return null; // komponen ini tidak merender apa-apa
|
||||||
|
}
|
||||||