Compare commits

...

17 Commits

Author SHA1 Message Date
36dbfa3296 Integrasi API: Admin Event & Event user ui
Fix:
Metode konfirmasi pada event menggunakan QR Code
Perbaikan file pada:
- app/(application)/(user)/event/(tabs)/index.tsx
- app/(application)/(user)/event/[id]/confirmation.tsx
- app/(application)/(user)/event/[id]/publish.tsx
- app/(application)/admin/event/[id]/[status]/index.tsx
- app/(application)/admin/event/[id]/reject-input.tsx
- app/(application)/admin/event/[status]/status.tsx
- service/api-admin/api-admin-event.ts
- service/api-client/api-event.ts

### Issue: Belum menyertakan fungsi konfrimasu kehadiran
2025-10-23 17:47:36 +08:00
966e55597c Integrasi API: Admin event & QR Code event
Add:
- app/(application)/(user)/event/[id]/confirmation.tsx
- screens/Admin/Event/
- service/api-admin/api-admin-event.ts

Fix:
- app.config.js : penambahan DEEP_LINK_URL untuk url QR Code
- app/(application)/(user)/event/create.tsx
- app/(application)/admin/event/[id]/[status]/index.tsx
- app/(application)/admin/event/[id]/reject-input.tsx
- app/(application)/admin/event/[status]/status.tsx
- app/(application)/admin/event/index.tsx
- app/(application)/admin/job/[id]/[status]/index.tsx
- screens/Admin/listPageAdmin.tsx
- service/api-config.ts
-

### No Issue
2025-10-22 15:45:58 +08:00
4da55a5a8a Integrasi API: Voting admin
Add:
- app/(application)/admin/voting/[id]/[status]/reject-input.tsx
- app/(application)/admin/voting/history.tsx
- components/Box/ReportBox.tsx
- screens/Admin/Voting/
- utils/colorBadge.ts

Fix:
- app/(application)/(user)/job/[id]/[status]/detail.tsx
- app/(application)/(user)/voting/[id]/[status]/detail.tsx
- app/(application)/admin/job/[id]/[status]/index.tsx
- app/(application)/admin/job/[id]/[status]/reject-input.tsx
- app/(application)/admin/voting/[id]/[status]/index.tsx
- app/(application)/admin/voting/[id]/reject-input.tsx
- app/(application)/admin/voting/[status]/status.tsx
- components/Container/CircleContainer.tsx
- components/Text/TextCustom.tsx
- components/_ShareComponent/Admin/ButtonReview.tsx
- screens/Admin/Job/funUpdateStatus.ts
- screens/Admin/listPageAdmin.tsx
- service/api-admin/api-admin-voting.ts

### No Issue
2025-10-21 16:52:17 +08:00
faf0f36e53 UI Fix: Pada tampilan ios bagian button ada yang ta terlihat dan sudah di perbaiki
### No Issue
2025-10-21 11:17:37 +08:00
57285e5697 Integrasi API: Admin Voting
Add:
-  service/api-admin/api-admin-voting.ts

Fix:
- app/(application)/admin/voting/[id]/[status]/index.tsx
- app/(application)/admin/voting/[status]/status.tsx
- app/(application)/admin/voting/index.tsx

### No Issue
2025-10-20 17:39:12 +08:00
1fd9694ebf Integrasi API: Admin Forum
Fix:
app/(application)/admin/forum/[id]/index.tsx
app/(application)/admin/forum/[id]/list-comment.tsx
app/(application)/admin/forum/[id]/list-report-comment.tsx
app/(application)/admin/forum/[id]/list-report-posting.tsx
app/(application)/admin/forum/index.tsx
app/(application)/admin/forum/posting.tsx
app/(application)/admin/forum/report-comment.tsx
app/(application)/admin/forum/report-posting.tsx
screens/Admin/listPageAdmin.tsx
service/api-admin/api-admin-forum.ts

### No Issue
2025-10-20 16:34:38 +08:00
d31df8c390 Integrasi API: Admin forum
Add:
app/(application)/admin/forum/[id]/list-comment.tsx
        components/_Icon/IconOpenTo.tsx
        service/api-admin/api-admin-forum.ts

Fix:
app/(application)/admin/collaboration/index.tsx
app/(application)/admin/forum/[id]/index.tsx
app/(application)/admin/forum/[id]/list-report-comment.tsx
app/(application)/admin/forum/index.tsx
app/(application)/admin/forum/posting.tsx

### Issue: Report komentar masih belum berfungsi
2025-10-17 17:41:25 +08:00
90cfb042d8 Integrasi API: Admin Job
Add:
app/(application)/admin/job/[id]/[status]/reject-input.tsx
screens/Admin/Job/

Fix:
Tampilan ke UI sudah terintegrasi

### No Issue
2025-10-17 14:15:21 +08:00
b9f93ff46a Menjalankan perintah: eas build --profile preview
### Issue: Harus ada SDK nya jika local
2025-10-17 10:24:04 +08:00
0770237fe5 Integrasi API: Admin Job
Add:
-  service/api-admin/api-admin-job.ts

Fix:
 modified:   app/(application)/admin/job/[status]/status.tsx
        modified:   app/(application)/admin/job/index.tsx
        modified:   components/_ShareComponent/SearchInput.tsx

### No issue
2025-10-16 16:44:43 +08:00
6f4dd79568 Integrasi API: Admin Collaboration
Fix: Tampilan yang terintegrasi API
 - app/(application)/(user)/collaboration/[id]/edit.tsx
 - app/(application)/admin/collaboration/[id]/[status].tsx
 - app/(application)/admin/collaboration/[id]/group.tsx
 - app/(application)/admin/collaboration/[id]/reject-input.tsx
 - app/(application)/admin/collaboration/group.tsx
 - app/(application)/admin/collaboration/index.tsx
 - app/(application)/admin/collaboration/publish.tsx
 - app/(application)/admin/collaboration/reject.tsx
 - components/_ShareComponent/Admin/TableValue.tsx
 - screens/Collaboration/BoxPublishSection.tsx
 - service/api-admin/api-admin-collaboration.ts

### No Issue
2025-10-16 14:48:31 +08:00
9faa0b0f64 Collaboration Mobile API
Add:
- /admin/collaboration/[id]/reject-input.tsx
- /api-admin/api-admin-collaboration.ts

Fix:
- Integrasi ke tampilan UI
app/(application)/admin/collaboration/[id]/[status].tsx
app/(application)/admin/collaboration/index.tsx
app/(application)/admin/collaboration/publish.tsx
components/_ShareComponent/Admin/TableTitle.tsx
components/_ShareComponent/Admin/TableValue.tsx

### No Issue
2025-10-15 17:30:25 +08:00
e05a7c8701 Portofolio
Fix:
(application)/(user)/portofolio/[id]/index.
Button hapus hanya muncul jika user login adalah author

### No Issue
2025-10-15 15:45:01 +08:00
f50c5099d8 Integrasi API: App Information & Maps
Add:
- service/api-admin/api-master-admin.ts

Fix:
app/(application)/admin/app-information/business-field/[id]/index.tsx
app/(application)/admin/app-information/business-field/create.tsx
app/(application)/admin/app-information/index.tsx
app/(application)/admin/app-information/information-bank/[id]/index.tsx
app/(application)/admin/app-information/information-bank/create.tsx
app/(application)/admin/maps.tsx
screens/Admin/App-Information/BusinessFieldSection.tsx
screens/Admin/App-Information/InformationBankSection.tsx
screens/Admin/App-Information/StickerSection.tsx
screens/Authentication/LoginView.tsx
service/api-client/api-master.ts

- Perbaikan berupa integrasi API

### No Issue
2025-10-15 15:09:18 +08:00
5f36620988 Integrasi Admin: User Acces & Super Admin
Add:
- admin/super-admin/
- admin/user-access/
- service/api-admin/

Fix:
- (user)/profile/[id]/index: penambahan useData dari useAuthuntuk merestart value masterRole
- integrasi pada tampilan admin

### No Issue
2025-10-14 17:28:40 +08:00
f750d158be Integrasi Map Business
Add:
 components/Map/MapSelected.tsx
        components/_ShareComponent/GridTwoView.tsx
        service/api-client/api-maps.ts
utils/openInDeviceMaps.ts

Fix:
 modified:   app/(application)/(user)/maps/[id]/edit.tsx
        modified:   app/(application)/(user)/maps/create.tsx
        modified:   app/(application)/(user)/maps/index.tsx
        modified:   app/(application)/(user)/portofolio/[id]/index.tsx
        modified:   components/Map/MapCustom.tsx
        modified:   screens/Portofolio/BusinessLocationSection.tsx
        modified:   screens/Portofolio/DataPortofolio.tsx
        modified:   screens/Portofolio/ListPage.tsx

### No issue
2025-10-13 17:46:47 +08:00
0e7b29bb15 Integrasi API Donation
Fix:
- (application)/(user)/donation/[id]/(news)/[news]/edit-news
- (application)/(user)/donation/[id]/(news)/[news]/index
- (application)/(user)/donation/[id]/(news)/add-news
- (application)/(user)/donation/[id]/(news)/list-of-news
- (application)/(user)/donation/[id]/(news)/recap-of-news
- (application)/(user)/donation/[id]/infromation-fundrising
- service/api-client/api-donation

### No Issue
2025-10-09 17:00:04 +08:00
128 changed files with 6291 additions and 4750 deletions

42
.gitignore vendored
View File

@@ -40,3 +40,45 @@ app-example
.qodo
.env
# @generated expo-cli sync-8d4afeec25ea8a192358fae2f8e2fc766bdce4ec
# The following patterns were generated by expo-cli
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
# dependencies
node_modules/
# Expo
.expo/
dist/
web-build/
expo-env.d.ts
# Native
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
# Metro
.metro-health-check*
# debug
npm-debug.*
yarn-debug.*
yarn-error.*
# macOS
.DS_Store
*.pem
# local env files
.env*.local
# typescript
*.tsbuildinfo
# @end expo-cli

View File

@@ -64,9 +64,9 @@ react {
}
/**
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
* Set this to true in release builds to optimize the app using [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization).
*/
def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean()
def enableMinifyInReleaseBuilds = (findProperty('android.enableMinifyInReleaseBuilds') ?: false).toBoolean()
/**
* The preferred build flavor of JavaScriptCore (JSC)
@@ -94,6 +94,8 @@ android {
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0.0"
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
}
signingConfigs {
debug {
@@ -111,15 +113,18 @@ android {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false)
minifyEnabled enableProguardInReleaseBuilds
def enableShrinkResources = findProperty('android.enableShrinkResourcesInReleaseBuilds') ?: 'false'
shrinkResources enableShrinkResources.toBoolean()
minifyEnabled enableMinifyInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true)
def enablePngCrunchInRelease = findProperty('android.enablePngCrunchInReleaseBuilds') ?: 'true'
crunchPngs enablePngCrunchInRelease.toBoolean()
}
}
packagingOptions {
jniLibs {
useLegacyPackaging (findProperty('expo.useLegacyPackaging')?.toBoolean() ?: false)
def enableLegacyPackaging = findProperty('expo.useLegacyPackaging') ?: 'false'
useLegacyPackaging enableLegacyPackaging.toBoolean()
}
}
androidResources {

View File

@@ -13,7 +13,7 @@
<data android:scheme="https"/>
</intent>
</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">
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:enableOnBackInvokedCallback="false">
<meta-data android:name="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_LAUNCH_WAIT_MS" android:value="0"/>

View File

@@ -5,13 +5,13 @@ import android.content.res.Configuration
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.ReactHost
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.common.ReleaseLevel
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader
import expo.modules.ApplicationLifecycleDispatcher
import expo.modules.ReactNativeHostWrapper
@@ -19,21 +19,19 @@ import expo.modules.ReactNativeHostWrapper
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
this,
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> {
val packages = PackageList(this).packages
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(MyReactNativePackage())
return packages
}
this,
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
}
override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}
)
@@ -42,11 +40,12 @@ class MainApplication : Application(), ReactApplication {
override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()
DefaultNewArchitectureEntryPoint.releaseLevel = try {
ReleaseLevel.valueOf(BuildConfig.REACT_NATIVE_RELEASE_LEVEL.uppercase())
} catch (e: IllegalArgumentException) {
ReleaseLevel.STABLE
}
loadReactNative(this)
ApplicationLifecycleDispatcher.onApplicationCreate(this)
}

View File

@@ -1,5 +1,6 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="AppTheme" parent="Theme.EdgeToEdge">
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:enforceNavigationBarContrast" tools:targetApi="29">true</item>
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="android:statusBarColor">#ffffff</item>
@@ -8,5 +9,6 @@
<item name="windowSplashScreenBackground">@color/splashscreen_background</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splashscreen_logo</item>
<item name="postSplashScreenTheme">@style/AppTheme</item>
<item name="android:windowSplashScreenBehavior">icon_preferred</item>
</style>
</resources>

View File

@@ -12,21 +12,8 @@ buildscript {
}
}
def reactNativeAndroidDir = new File(
providers.exec {
workingDir(rootDir)
commandLine("node", "--print", "require.resolve('react-native/package.json')")
}.standardOutput.asText.get().trim(),
"../android"
)
allprojects {
repositories {
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url(reactNativeAndroidDir)
}
google()
mavenCentral()
maven { url 'https://www.jitpack.io' }

View File

@@ -15,7 +15,7 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
@@ -41,6 +41,11 @@ newArchEnabled=true
# If set to false, you will be using JSC instead.
hermesEnabled=true
# Use this property to enable edge-to-edge display support.
# This allows your app to draw behind system bars for an immersive UI.
# Note: Only works with ReactActivity and should not be used with custom Activity.
edgeToEdgeEnabled=true
# Enable GIF support in React Native images (~200 B increase)
expo.gif.enabled=true
# Enable webp support in React Native images (~85 KB increase)
@@ -55,5 +60,6 @@ EX_DEV_CLIENT_NETWORK_INSPECTOR=true
# Use legacy packaging to compress native libraries in the resulting APK.
expo.useLegacyPackaging=false
# Whether the app is configured to use edge-to-edge via the app config or `react-native-edge-to-edge` plugin
expo.edgeToEdgeEnabled=true
# Specifies whether the app is configured to use edge-to-edge via the app config or plugin
# WARNING: This property has been deprecated and will be removed in Expo SDK 55. Use `edgeToEdgeEnabled` or `react.edgeToEdgeEnabled` to determine whether the project is using edge-to-edge.
expo.edgeToEdgeEnabled=true

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

4
android/gradlew vendored
View File

@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.

4
android/gradlew.bat vendored
View File

@@ -70,11 +70,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
set CLASSPATH=
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell

View File

@@ -70,5 +70,6 @@ export default {
// Tambahkan environment variables ke sini
API_BASE_URL: process.env.API_BASE_URL,
BASE_URL: process.env.BASE_URL,
DEEP_LINK_URL: process.env.DEEP_LINK_URL,
},
};

View File

@@ -19,7 +19,6 @@ import Toast from "react-native-toast-message";
export default function CollaborationEdit() {
const { id } = useLocalSearchParams();
console.log("id :", id);
const [data, setData] = useState<any>();
const [listMaster, setListMaster] = useState<any[]>([]);
const [loadingData, setLoadingData] = useState(false);

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ButtonCenteredOnly,
ButtonCustom,
@@ -9,17 +10,120 @@ import {
TextInputCustom,
ViewWrapper,
} from "@/components";
import { router } from "expo-router";
import API_STRORAGE from "@/constants/base-url-api-strorage";
import DIRECTORY_ID from "@/constants/directory-id";
import {
apiDonationGetNewsById,
apiDonationUpdateNews,
} from "@/service/api-client/api-donation";
import { uploadFileService } from "@/service/upload-service";
import pickFile, { IFileData } from "@/utils/pickFile";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function DonationEditNews() {
const { news } = useLocalSearchParams();
const [data, setData] = useState<any>(null);
const [image, setImage] = useState<IFileData | null>(null);
const [isLoading, setLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [news])
);
const onLoadData = async () => {
try {
const response = await apiDonationGetNewsById({
id: news as string,
category: "get-one",
});
setData(response.data);
} catch (error) {
console.log("[ERROR]", error);
}
};
const handlerSubmitUpdate = async () => {
let newData;
if (!data.title || !data.deskripsi) {
Toast.show({
type: "error",
text1: "Judul dan deskripsi harus diisi",
});
return;
}
try {
setLoading(true);
newData = {
title: data?.title,
deskripsi: data?.deskripsi,
};
if (image && image?.uri) {
const uploadNewImage = await uploadFileService({
dirId: DIRECTORY_ID.donasi_kabar,
imageUri: image?.uri,
});
newData = {
title: data?.title,
deskripsi: data?.deskripsi,
newImageId: uploadNewImage.data.id,
};
}
const response = await apiDonationUpdateNews({
id: news as string,
data: newData,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal mengupdate berita",
});
return;
}
Toast.show({
type: "success",
text1: "Berita berhasil diperbarui",
});
router.back();
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoading(false);
}
};
return (
<ViewWrapper>
<StackCustom gap={"xs"}>
<InformationBox text="Upload gambar bersifat opsional untuk melengkapi kabar terkait donasi Anda." />
<LandscapeFrameUploaded />
<LandscapeFrameUploaded
image={
image
? image.uri
: data && data.imageId
? API_STRORAGE.GET({ fileId: data.imageId })
: undefined
}
/>
<ButtonCenteredOnly
onPress={() => {
router.push("/(application)/(image)/take-picture/123");
pickFile({
allowedType: "image",
setImageUri(file) {
setImage(file);
},
});
}}
icon="upload"
>
@@ -30,6 +134,8 @@ export default function DonationEditNews() {
label="Judul Berita"
placeholder="Masukan judul berita"
required
value={data?.title}
onChangeText={(value) => setData({ ...data, title: value })}
/>
<TextAreaCustom
label="Deskripsi Berita"
@@ -37,12 +143,16 @@ export default function DonationEditNews() {
required
showCount
maxLength={1000}
value={data?.deskripsi}
onChangeText={(value) => setData({ ...data, deskripsi: value })}
/>
<Spacing />
<ButtonCustom
disabled={!data?.title || !data?.deskripsi}
isLoading={isLoading}
onPress={() => {
router.back();
handlerSubmitUpdate();
}}
>
Update

View File

@@ -14,9 +14,11 @@ import {
import { IconEdit } from "@/components/_Icon";
import { IconTrash } from "@/components/_Icon/IconTrash";
import { useAuth } from "@/hooks/use-auth";
import { apiDonationGetNewsById } from "@/service/api-client/api-donation";
import {
apiDonationDeleteNews,
apiDonationGetNewsById,
} from "@/service/api-client/api-donation";
import { formatChatTime } from "@/utils/formatChatTime";
import dayjs from "dayjs";
import {
router,
Stack,
@@ -24,6 +26,7 @@ import {
useLocalSearchParams,
} from "expo-router";
import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function DonationNews() {
const { user } = useAuth();
@@ -57,7 +60,7 @@ export default function DonationNews() {
title: "Detail Kabar",
headerLeft: () => <BackButton />,
headerRight: () =>
user?.id === data && data?.authorId && (
user?.id === data?.authorId && (
<DotButton onPress={() => setOpenDrawer(true)} />
),
}}
@@ -97,7 +100,7 @@ export default function DonationNews() {
{
icon: <IconTrash />,
label: "Hapus Berita",
path: `/donation/[id]/(news)/${news}/edit-news` as any,
path: "",
color: "red",
},
]}
@@ -109,7 +112,23 @@ export default function DonationNews() {
message: "Apakah Anda yakin ingin menghapus berita ini?",
textLeft: "Batal",
textRight: "Hapus",
onPressRight: () => {
onPressRight: async () => {
const response = await apiDonationDeleteNews({
id: news as string,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal menghapus berita",
});
return;
}
Toast.show({
type: "success",
text1: "Berita berhasil dihapus",
});
router.back();
},
});

View File

@@ -19,7 +19,6 @@ import Toast from "react-native-toast-message";
export default function DonationAddNews() {
const { id } = useLocalSearchParams();
console.log("[ID]", id);
const [data, setData] = useState({
title: "",
deskripsi: "",
@@ -48,8 +47,6 @@ export default function DonationAddNews() {
data: newData,
});
console.log("[RESPONSE]", JSON.stringify(response, null, 2));
if (!response.success) {
Toast.show({
type: "error",

View File

@@ -12,7 +12,6 @@ import {
import { IconPlus } from "@/components/_Icon";
import { apiDonationGetNewsById } from "@/service/api-client/api-donation";
import { formatChatTime } from "@/utils/formatChatTime";
import dayjs from "dayjs";
import {
router,
Stack,
@@ -42,7 +41,6 @@ export default function DonationRecapOfNews() {
category: "get-all",
});
console.log("[RESPONSE LIST]", JSON.stringify(response, null, 2));
setList(response.data);
} catch (error) {
console.log("[ERROR]", error);

View File

@@ -13,7 +13,6 @@ import {
import { IconPlus } from "@/components/_Icon";
import { apiDonationGetNewsById } from "@/service/api-client/api-donation";
import { formatChatTime } from "@/utils/formatChatTime";
import dayjs from "dayjs";
import {
router,
Stack,
@@ -25,7 +24,6 @@ import { useCallback, useState } from "react";
export default function DonationRecapOfNews() {
const { id } = useLocalSearchParams();
console.log("[ID]", id);
const [openDrawer, setOpenDrawer] = useState(false);
const [list, setList] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState<boolean>(false);
@@ -44,7 +42,6 @@ export default function DonationRecapOfNews() {
category: "get-all",
});
console.log("[RESPONSE LIST]", JSON.stringify(response, null, 2));
setList(response.data);
} catch (error) {
console.log("[ERROR]", error);

View File

@@ -32,10 +32,6 @@ export default function DonationInformationFunrising() {
try {
setLoadList(true);
const response = await apiDonationFundrising({ id: id as string });
console.log(
"[RES GET FUNDRISING]",
JSON.stringify(response.data, null, 2)
);
setData(response?.data?.user);
setList(response?.data?.donasi);
@@ -78,7 +74,7 @@ export default function DonationInformationFunrising() {
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom>Belum ada data</TextCustom>
<TextCustom align="center" color="gray" size="small">Belum ada data</TextCustom>
) : (
list?.map((item: any, index: number) => (
<Donation_BoxPublish key={index} id={item?.id} data={item} />

View File

@@ -44,6 +44,8 @@ export default function EventBeranda() {
<TextCustom align="center">Belum ada event</TextCustom>
) : (
listData.map((item: any, index) => (
<Event_BoxPublishSection
key={index}
href={`/event/${item.id}/publish`}

View File

@@ -0,0 +1,500 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable react-hooks/exhaustive-deps */
import {
BaseBox,
ButtonCustom,
CenterCustom,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { useAuth } from "@/hooks/use-auth";
import {
apiEventConfirmationAction,
apiEventGetConfirmation,
apiEventJoin,
} from "@/service/api-client/api-event";
import { Ionicons } from "@expo/vector-icons";
import dayjs from "dayjs";
import isBetween from "dayjs/plugin/isBetween";
import {
Redirect,
router,
Stack,
useFocusEffect,
useLocalSearchParams,
} from "expo-router";
import React, { useCallback, useState } from "react";
import { View } from "react-native";
import Toast from "react-native-toast-message";
// Extend Day.js dengan plugin isBetween
dayjs.extend(isBetween);
interface DataEvent {
id: string;
title: string;
tanggal: Date;
tanggalSelesai: Date;
lokasi: string;
Author: {
id: string;
username: string;
Profile: {
id: string;
name: string;
};
};
}
export default function UserEventConfirmation() {
const { token } = useAuth();
const { id, userId: authorId } = useLocalSearchParams();
const { user } = useAuth();
const [data, setData] = useState<DataEvent | null>(null);
const [peserta, setPeserta] = useState<boolean | null>(null);
const [konfirmasi, setKonfirmasi] = useState<boolean | null>(null);
useFocusEffect(
useCallback(() => {
checkTokenAndDataParticipants() || console.log("Token is null");
}, [token, id, user?.id])
);
const checkTokenAndDataParticipants = async () => {
if (!token) {
return <Redirect href={`/`} />;
}
try {
const response = await apiEventGetConfirmation({
id: id as string,
userId: user?.id as string,
});
console.log("[RES CONFIRMATION]", JSON.stringify(response, null, 2));
if (response.success) {
setData(response.data?.dataEvent);
setPeserta(response.data?.peserta);
setKonfirmasi(response.data?.kehadiran);
}
} catch (error) {
console.log("[ERROR CONFIRMATION]", error);
}
};
const handlerReturn = () => {
const now = dayjs(); // asumsi: UTC, sesuai dengan API
// --- [1] Loading & Data tidak ditemukan ---
if (data === undefined || data === null) {
if (peserta === null && konfirmasi === null) {
return <LoaderCustom />;
}
return (
<BaseBox>
<TextCustom bold align="center" size={"large"}>
Data Tidak Ditemukan
</TextCustom>
<BackToOtherPath path="home" />
</BaseBox>
);
}
// --- [2] Ambil waktu event dari `data` ---
const eventStart = dayjs(data.tanggal);
const eventEnd = dayjs(data.tanggalSelesai);
// --- [3] Definisikan jendela konfirmasi: 1 jam sebelum mulai → 1 jam setelah selesai ---
const confirmationStart = eventStart.subtract(1, "hour");
const confirmationEnd = eventEnd.add(1, "hour");
const isWithinConfirmationWindow = now.isBetween(
confirmationStart,
confirmationEnd,
null,
"[]"
);
// --- [4] Status waktu event (untuk pesan UI) ---
const isBeforeEvent = now.isBefore(eventStart);
const isAfterEvent = now.isAfter(eventEnd);
const isDuringEvent = !isBeforeEvent && !isAfterEvent;
// --- [5] Handle berdasarkan waktu dan status peserta/konfirmasi ---
// 🟢 Acara sudah selesai
if (isAfterEvent) {
if (peserta === false) {
return (
<TamplateBox data={data}>
<TamplateText text="Event telah selesai, sehingga konfirmasi kehadiran sudah tidak dapat dilakukan. Terima kasih atas perhatian dan minat Anda. Kami berharap dapat bertemu di acara kami berikutnya." />
<BackToOtherPath
path="event"
id={data.id}
isAfterEvent={isAfterEvent}
/>
</TamplateBox>
);
}
return (
<TamplateBox data={data}>
<TamplateText
text={`Event telah selesai, anda terdaftar sebagai peserta dan${
konfirmasi
? "Anda telah mengonfirmasi kehadiran."
: "Anda tidak mengonfirmasi kehadiran."
}. Terima kasih atas perhatian dan minat Anda. Kami berharap dapat bertemu di acara kami berikutnya.`}
/>
<BackToOtherPath
path="event"
id={data.id}
isAfterEvent={isAfterEvent}
/>
</TamplateBox>
);
}
// 🔵 Acara belum mulai & belum terdaftar
if (isBeforeEvent) {
if (peserta === false) {
return (
<NotStarted_And_UserNotParticipan
id={data.id}
userId={user?.id as string}
data={data}
/>
);
}
// Peserta sudah daftar → cek apakah sudah boleh konfirmasi
if (isWithinConfirmationWindow && peserta === true) {
if (konfirmasi === false) {
return (
<TamplateBox data={data}>
<TamplateText text="Konfirmasi Kehadiran" />
</TamplateBox>
);
}
return (
<TamplateBox data={data}>
<TamplateText text="Anda telah mengonfirmasi kehadiran." />
<BackToOtherPath
path="event"
id={data.id}
isAfterEvent={isAfterEvent}
/>
</TamplateBox>
);
}
return (
<TamplateBox data={data}>
<TamplateText text="Anda terdaftar sebagai peserta. Konfirmasi kehadiran dibuka 1 jam sebelum acara dimulai." />
<BackToOtherPath
path="event"
id={data.id}
isAfterEvent={isAfterEvent}
/>
</TamplateBox>
);
}
// 🟡 Acara sedang berlangsung & belum terdaftar
if (isDuringEvent) {
if (peserta === false) {
return (
<UserNotParticipan_And_DuringEvent
id={data.id}
userId={user?.id as string}
data={data}
/>
);
}
if (peserta === true) {
if (isWithinConfirmationWindow) {
if (konfirmasi === false) {
return (
<TamplateBox data={data}>
<TamplateText text="Konfirmasi Kehadiran" />
</TamplateBox>
);
}
return (
<TamplateBox data={data}>
<TamplateText text="Anda telah mengonfirmasi kehadiran dalam event ini. Terima kasih dan selamat menikmati acara. Have fun!" />
<BackToOtherPath
path="event"
id={data.id}
isAfterEvent={isAfterEvent}
/>
</TamplateBox>
);
}
// Ini sangat jarang terjadi selama event berlangsung, tapi aman
return (
<TamplateBox data={data}>
<TamplateText text="Konfirmasi kehadiran tidak tersedia saat ini." />
<BackToOtherPath path="home" />
</TamplateBox>
);
}
}
// 🛑 Fallback aman
return (
<BaseBox>
<StackCustom>
<TamplateText text="Terjadi kesalahan tak terduga pada logika waktu." />
<BackToOtherPath path="home" />
</StackCustom>
</BaseBox>
);
};
return (
<>
<Stack.Screen
options={{
title: "Konfirmasi Event",
headerLeft: () => (
<Ionicons
name="arrow-back"
size={20}
color={MainColor.yellow}
onPress={() =>
router.navigate("/(application)/(user)/event/create")
}
/>
),
}}
/>
<ViewWrapper>{handlerReturn()}</ViewWrapper>
</>
);
}
const TamplateBox = ({
data,
children,
}: {
data: DataEvent;
children: React.ReactNode;
}) => {
return (
<>
<CenterCustom>
<BaseBox>
<StackCustom gap={"lg"}>
<StackCustom gap={"sm"}>
<TextCustom bold align="center" size={"large"}>
{data?.title}
</TextCustom>
<View
style={{
flexDirection: "row",
justifyContent: "center",
// backgroundColor: AccentColor.blue,
// borderColor: AccentColor.blue,
// borderWidth: 1,
// borderRadius: 4,
// padding: 3
}}
>
<TextCustom align="center" size="small" bold>
{dayjs(data?.tanggal).format("DD MMM YYYY: HH:mm")}
</TextCustom>
<TextCustom align="center" size="small" bold>
{" "}
-{" "}
</TextCustom>
<TextCustom align="center" size="small" bold>
{dayjs(data?.tanggalSelesai).format("DD MMM YYYY: HH:mm")}
</TextCustom>
</View>
</StackCustom>
{children}
</StackCustom>
</BaseBox>
</CenterCustom>
</>
);
};
const TamplateText = ({ text }: { text: string }) => {
return (
<>
<TextCustom align="center">{text}</TextCustom>
</>
);
};
type BackToOtherPathProps =
| { path: "home" | "beranda-event"; id?: never; isAfterEvent?: never }
| { path: "event"; id: string; isAfterEvent: boolean };
const BackToOtherPath = ({ path, id, isAfterEvent }: BackToOtherPathProps) => {
return (
<>
{path === "home" ? (
<ButtonCustom
onPress={() => {
router.replace("/(application)/home");
}}
>
Home
</ButtonCustom>
) : (
<View
style={{
flexDirection: "row",
gap: 10,
justifyContent: "center",
}}
>
<ButtonCustom
onPress={() => {
router.replace("/(application)/home");
}}
>
Home
</ButtonCustom>
<ButtonCustom
onPress={() => {
if (path === "event") {
if (isAfterEvent) {
router.push(`/(application)/(user)/event/${id}/history`);
} else {
router.push(`/(application)/(user)/event/${id}/publish`);
}
} else if (path === "beranda-event") {
router.push(`/(application)/(user)/event`);
} else {
console.log("[PATH]", path);
}
}}
>
Lihat {path === "event" ? "Event" : "Beranda Event"}
</ButtonCustom>
</View>
)}
</>
);
};
// 🔵 Acara belum mulai & belum terdaftar
const NotStarted_And_UserNotParticipan = ({
id,
userId,
data,
}: {
id: string;
userId: string;
data: DataEvent;
}) => {
const [isLoading, setIsLoading] = useState<boolean>(false);
const handlerJoinEvent = async () => {
try {
setIsLoading(true);
const response = await apiEventJoin({
id: id as string,
userId: userId as string,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Anda gagal join",
});
return;
}
Toast.show({
type: "success",
text1: "Anda berhasil join",
});
router.navigate(`/(application)/(user)/event/${id}/publish`);
} catch (error) {
console.log("[ERROR JOIN EVENT]", error);
} finally {
setIsLoading(false);
}
};
return (
<>
<TamplateBox data={data}>
<TamplateText text="Anda belum terdaftar sebagai peserta & Event belum dimulai. Silahkan daftarkan diri anda terlebih dahulu" />
<ButtonCustom onPress={handlerJoinEvent} isLoading={isLoading}>
Join
</ButtonCustom>
</TamplateBox>
</>
);
};
// 🟡 ZONA ACARA BERLANGSUNG
// Acara sedang berlangsung & belum terdaftar
const UserNotParticipan_And_DuringEvent = ({
id,
userId,
data,
}: {
id: string;
userId: string;
data: DataEvent;
}) => {
const [isLoading, setIsLoading] = useState<boolean>(false);
const handlerSubmit = async () => {
try {
setIsLoading(true);
const response = await apiEventConfirmationAction({
id: id as string,
userId: userId as string,
category: "join_and_confirm",
});
// console.log("[RES JOIN & CONFIRMATION EVENT]", response);
if (!response.success) {
Toast.show({
type: "error",
text1: "Anda gagal join & konfirmasi",
});
return;
}
Toast.show({
type: "success",
text1: "Anda berhasil join & konfirmasi",
});
router.navigate(`/(application)/(user)/event/${id}/publish`);
} catch (error) {
console.log("[ERROR JOIN & CONFIRMATION EVENT]", error);
} finally {
setIsLoading(false);
}
};
return (
<>
<TamplateBox data={data}>
<TamplateText text="Anda belum terdaftar sebagai peserta & Event sedang berlangsung. Silahkan daftarkan diri anda & Konfirmasi kehadiran" />
<ButtonCustom onPress={() => handlerSubmit()} isLoading={isLoading}>
Join & Konfirmasi
</ButtonCustom>
</TamplateBox>
</>
);
};

View File

@@ -55,6 +55,8 @@ export default function EventDetailPublish() {
userId: user?.id as string,
});
console.log("[RES CHECK PARTICIPANTS]", responseCheckParticipants);
if (
responseCheckParticipants.success &&
responseCheckParticipants.data
@@ -69,6 +71,8 @@ export default function EventDetailPublish() {
}
}
console.log("[participans]", isParticipant);
const handlePress = (item: IMenuDrawerItem) => {
console.log("PATH ", item.path);
router.navigate(item.path as any);

View File

@@ -191,7 +191,7 @@ export default function EventCreate() {
placeholder="Masukkan deskripsi event"
required
showCount
maxLength={100}
maxLength={1000}
onChangeText={(value: any) =>
setData({ ...data, deskripsi: value })
}

View File

@@ -11,6 +11,7 @@ import {
} from "@/components";
import { IconEdit } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import ReportBox from "@/components/Box/ReportBox";
import Job_BoxDetailSection from "@/screens/Job/BoxDetailSection";
import Job_ButtonStatusSection from "@/screens/Job/ButtonStatusSection";
import { apiJobGetOne } from "@/service/api-client/api-job";
@@ -70,7 +71,13 @@ export default function JobDetailStatus() {
<LoaderCustom />
) : (
<>
<StackCustom>
<StackCustom gap={"xs"}>
{data &&
data?.catatan &&
(status === "draft" || status === "rejected") && (
<ReportBox text={data?.catatan} />
)}
<Job_BoxDetailSection data={data} />
<Job_ButtonStatusSection
id={id as string}

View File

@@ -1,54 +1,226 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BaseBox,
BoxButtonOnFooter,
ButtonCenteredOnly,
ButtonCustom,
InformationBox,
LandscapeFrameUploaded,
MapCustom,
Spacing,
TextInputCustom,
ViewWrapper
ViewWrapper,
} from "@/components";
import { router, useLocalSearchParams } from "expo-router";
import API_IMAGE from "@/constants/api-storage";
import DIRECTORY_ID from "@/constants/directory-id";
import { apiMapsGetOne, apiMapsUpdate } from "@/service/api-client/api-maps";
import { uploadFileService } from "@/service/upload-service";
import pickFile, { IFileData } from "@/utils/pickFile";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import { StyleSheet, View } from "react-native";
import MapView, { LatLng, Marker } from "react-native-maps";
import Toast from "react-native-toast-message";
const defaultRegion = {
latitude: -8.737109,
longitude: 115.1756897,
latitudeDelta: 0.1,
longitudeDelta: 0.1,
};
export default function MapsEdit() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>({
id: "",
namePin: "",
latitude: "",
longitude: "",
imageId: "",
});
const [selectedLocation, setSelectedLocation] = useState<LatLng | null>(null);
const [image, setImage] = useState<IFileData | null>(null);
const [isLoading, setLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiMapsGetOne({ id: id as string });
if (response.success) {
setData({
id: response.data.id,
namePin: response.data.namePin,
latitude: response.data.latitude,
longitude: response.data.longitude,
imageId: response.data.imageId,
});
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const handleMapPress = (event: any) => {
const { latitude, longitude } = event.nativeEvent.coordinate;
const location = { latitude, longitude };
setSelectedLocation(location);
};
const handleSubmit = async () => {
let newData: any;
if (!data.namePin) {
Toast.show({
type: "error",
text1: "Nama pin harus diisi",
});
return;
}
newData = {
namePin: data?.namePin,
latitude: selectedLocation?.latitude || data?.latitude,
longitude: selectedLocation?.longitude || data?.longitude,
};
try {
setLoading(true);
if (image) {
const responseUpload = await uploadFileService({
dirId: DIRECTORY_ID.map_image,
imageUri: image?.uri,
});
if (!responseUpload?.data?.id) {
Toast.show({
type: "error",
text1: "Gagal mengunggah gambar",
});
return;
}
const imageId = responseUpload?.data?.id;
newData = {
namePin: data?.namePin,
latitude: selectedLocation?.latitude,
longitude: selectedLocation?.longitude,
newImageId: imageId,
};
}
const responseUpdate = await apiMapsUpdate({
id: data?.id,
data: newData,
});
if (!responseUpdate.success) {
Toast.show({
type: "error",
text1: "Gagal mengupdate map",
});
return;
}
Toast.show({
type: "success",
text1: "Map berhasil diupdate",
});
router.back();
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoading(false);
}
};
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log(`Simpan maps ${id}`);
router.back()
}}
disabled={!data.namePin}
onPress={handleSubmit}
isLoading={isLoading}
>
Simpan
Update
</ButtonCustom>
</BoxButtonOnFooter>
);
return (
<ViewWrapper footerComponent={buttonFooter}>
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
<BaseBox>
<MapCustom />
</BaseBox>
<View style={[styles.container, { height: 400 }]}>
<MapView
style={styles.map}
initialRegion={
data?.latitude && data?.longitude
? {
latitude: data?.latitude,
longitude: data?.longitude,
latitudeDelta: 0.1,
longitudeDelta: 0.1,
}
: defaultRegion
}
onPress={handleMapPress}
showsUserLocation={true}
showsMyLocationButton={true}
loadingEnabled={true}
loadingIndicatorColor="#666"
loadingBackgroundColor="#f0f0f0"
>
{selectedLocation ? (
<Marker
coordinate={selectedLocation}
title="Lokasi Dipilih"
description={`Lat: ${selectedLocation.latitude.toFixed(
6
)}, Lng: ${selectedLocation.longitude.toFixed(6)}`}
pinColor="red"
/>
) : (
<Marker
coordinate={defaultRegion}
title="Lokasi Dipilih"
description={`Lat: ${defaultRegion.latitude.toFixed(
6
)}, Lng: ${defaultRegion.longitude.toFixed(6)}`}
pinColor="red"
/>
)}
</MapView>
</View>
<TextInputCustom
required
label="Nama Pin"
placeholder="Masukkan nama pin maps"
value={data?.namePin}
onChangeText={(value) => setData({ ...data, namePin: value })}
/>
<Spacing />
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
<LandscapeFrameUploaded />
<LandscapeFrameUploaded
image={
image
? image?.uri
: API_IMAGE.GET({ fileId: data?.imageId as string })
}
/>
<ButtonCenteredOnly
icon="upload"
onPress={() => {
console.log("Upload foto ");
router.navigate(`/take-picture/${id}`);
pickFile({
allowedType: "image",
setImageUri(file) {
setImage(file);
},
});
}}
>
Upload
@@ -57,3 +229,16 @@ export default function MapsEdit() {
</ViewWrapper>
);
}
const styles = StyleSheet.create({
container: {
width: "100%",
backgroundColor: "#f5f5f5",
overflow: "hidden",
borderRadius: 8,
marginBottom: 20,
},
map: {
flex: 1,
},
});

View File

@@ -6,21 +6,96 @@ import {
InformationBox,
LandscapeFrameUploaded,
Spacing,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import MapSelected from "@/components/Map/MapSelected";
import DIRECTORY_ID from "@/constants/directory-id";
import { useAuth } from "@/hooks/use-auth";
import { apiMapsCreate } from "@/service/api-client/api-maps";
import { uploadFileService } from "@/service/upload-service";
import pickFile, { IFileData } from "@/utils/pickFile";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { LatLng } from "react-native-maps";
import Toast from "react-native-toast-message";
export default function MapsCreate() {
const { user } = useAuth();
const { id } = useLocalSearchParams();
const [selectedLocation, setSelectedLocation] = useState<LatLng | null>(null);
const [name, setName] = useState<string>("");
const [image, setImage] = useState<IFileData | null>(null);
const [isLoading, setLoading] = useState(false);
const handleSubmit = async () => {
try {
setLoading(true);
let newData: any;
newData = {
authorId: user?.id,
portofolioId: id,
namePin: name,
latitude: selectedLocation?.latitude,
longitude: selectedLocation?.longitude,
};
if (image) {
const responseUpload = await uploadFileService({
dirId: DIRECTORY_ID.map_image,
imageUri: image?.uri,
});
if (!responseUpload?.data?.id) {
Toast.show({
type: "error",
text1: "Gagal mengunggah gambar",
});
return;
}
const imageId = responseUpload?.data?.id;
newData = {
authorId: user?.id,
portofolioId: id,
namePin: name,
latitude: selectedLocation?.latitude,
longitude: selectedLocation?.longitude,
imageId: imageId,
};
}
const response = await apiMapsCreate({
data: newData,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal menambahkan map",
});
return;
}
Toast.show({
type: "success",
text1: "Map berhasil ditambahkan",
});
router.back();
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoading(false);
}
};
const buttonFooter = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => {
console.log(`Simpan maps ${id}`);
router.replace(`/portofolio/${id}`);
}}
isLoading={isLoading}
disabled={!selectedLocation || name === ""}
onPress={handleSubmit}
>
Simpan
</ButtonCustom>
@@ -30,25 +105,34 @@ export default function MapsCreate() {
<ViewWrapper footerComponent={buttonFooter}>
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
<BaseBox style={{ height: 400 }}>
<TextCustom>Maps Her</TextCustom>
<BaseBox>
<MapSelected
selectedLocation={selectedLocation as any}
setSelectedLocation={setSelectedLocation}
/>
</BaseBox>
<TextInputCustom
required
label="Nama Pin"
placeholder="Masukkan nama pin maps"
value={name}
onChangeText={setName}
/>
<Spacing height={50} />
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
<LandscapeFrameUploaded />
<LandscapeFrameUploaded image={image?.uri} />
<ButtonCenteredOnly
icon="upload"
onPress={() => {
console.log("Upload foto ");
router.navigate(`/take-picture/${id}`);
pickFile({
allowedType: "image",
setImageUri(file) {
setImage(file);
},
});
}}
>
Upload

View File

@@ -1,19 +1,234 @@
import { MapCustom, ViewWrapper } from "@/components";
import {
ButtonCustom,
DrawerCustom,
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 from "react-native-maps";
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 {
id: string | number;
latitude: number;
longitude: number;
name: string;
imageId?: string;
}
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 (
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
{/* <MapCustom height={"100%"} /> */}
<View style={{ flex: 1 }}>
<MapView
style={{
width: "100%",
height: "100%",
}}
/>
</View>
</ViewWrapper>
<>
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
{/* <MapCustom height={"100%"} /> */}
<View style={{ flex: 1 }}>
{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>
</>
);
}

View File

@@ -1,8 +1,18 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { DrawerCustom, LoaderCustom, Spacing, StackCustom } from "@/components";
import {
ButtonCustom,
DrawerCustom,
DummyLandscapeImage,
LoaderCustom,
Spacing,
StackCustom,
TextCustom,
} from "@/components";
import LeftButtonCustom from "@/components/Button/BackButton";
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth";
import Portofolio_BusinessLocation from "@/screens/Portofolio/BusinessLocationSection";
import Portofolio_ButtonDelete from "@/screens/Portofolio/ButtonDelete";
@@ -13,19 +23,20 @@ import Portofolio_SocialMediaSection from "@/screens/Portofolio/SocialMediaSecti
import { apiGetOnePortofolio } from "@/service/api-client/api-portofolio";
import { apiUser } from "@/service/api-client/api-user";
import { GStyles } from "@/styles/global-styles";
import { Ionicons } from "@expo/vector-icons";
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
import { FontAwesome, Ionicons } from "@expo/vector-icons";
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import { TouchableOpacity } from "react-native";
export default function Portofolio() {
const { user } = useAuth();
const { id } = useLocalSearchParams();
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const [isLoadingDelete, setIsLoadingDelete] = useState(false);
const [data, setData] = useState<any>();
const [profileId, setProfileId] = useState<any>();
const { user } = useAuth();
const [openDrawerLocation, setOpenDrawerLocation] = useState(false);
const openDrawer = () => {
setIsDrawerOpen(true);
@@ -43,19 +54,13 @@ export default function Portofolio() {
async function onLoadData(id: string) {
const response = await apiGetOnePortofolio({ id: id });
console.log(
"[PROFILE ID]>>",
JSON.stringify(response.data.Profile.id, null, 2)
);
setData(response.data);
}
const onLoadUserByToken = async () => {
const response = await apiUser(user?.id as string);
console.log(
"[PROFILE LOGIN]>>",
JSON.stringify(response.data?.Profile.id, null, 2)
);
setProfileId(response?.data?.Profile?.id);
};
@@ -89,15 +94,21 @@ export default function Portofolio() {
data={data}
listSubBidang={data?.Portofolio_BidangDanSubBidangBisnis as any[]}
/>
<Portofolio_BusinessLocation />
<Portofolio_BusinessLocation
data={data?.BusinessMaps}
imageId={data?.logoId}
setOpenDrawerLocation={setOpenDrawerLocation}
/>
<Portofolio_SocialMediaSection
data={data?.Portofolio_MediaSosial}
/>
<Portofolio_ButtonDelete
id={id as string}
isLoadingDelete={isLoadingDelete}
setIsLoadingDelete={setIsLoadingDelete}
/>
{data?.Profile?.id !== profileId ? null : (
<Portofolio_ButtonDelete
id={id as string}
isLoadingDelete={isLoadingDelete}
setIsLoadingDelete={setIsLoadingDelete}
/>
)}
<Spacing />
</StackCustom>
)}
@@ -110,10 +121,93 @@ export default function Portofolio() {
height={"auto"}
>
<Portofolio_MenuDrawerSection
drawerItems={drawerItemsPortofolio({ id: id as string })}
drawerItems={drawerItemsPortofolio({
id: id as string,
maps: data?.BusinessMaps,
})}
setIsDrawerOpen={setIsDrawerOpen}
/>
</DrawerCustom>
{/* Drawer Lokasi */}
<DrawerCustom
isVisible={openDrawerLocation}
closeDrawer={() => setOpenDrawerLocation(false)}
height={"auto"}
>
<DummyLandscapeImage
height={200}
imageId={data?.BusinessMaps?.imageId}
/>
<Spacing />
<StackCustom gap={"xs"}>
<GridTwoView
spanLeft={2}
spanRight={10}
leftIcon={
<FontAwesome
name="building-o"
size={ICON_SIZE_SMALL}
color="white"
/>
}
rightIcon={<TextCustom>{data?.BusinessMaps?.namePin}</TextCustom>}
/>
<GridTwoView
spanLeft={2}
spanRight={10}
leftIcon={
<Ionicons
name="list-outline"
size={ICON_SIZE_SMALL}
color="white"
/>
}
rightIcon={
<TextCustom>{data?.MasterBidangBisnis?.name}</TextCustom>
}
/>
<GridTwoView
spanLeft={2}
spanRight={10}
leftIcon={
<Ionicons
name="call-outline"
size={ICON_SIZE_SMALL}
color="white"
/>
}
rightIcon={<TextCustom>{data?.tlpn}</TextCustom>}
/>
<GridTwoView
spanLeft={2}
spanRight={10}
leftIcon={
<Ionicons
name="location-outline"
size={ICON_SIZE_SMALL}
color="white"
/>
}
rightIcon={<TextCustom>{data?.alamatKantor}</TextCustom>}
/>
<Spacing />
<ButtonCustom
onPress={() => {
openInDeviceMaps({
latitude: data?.BusinessMaps?.latitude,
longitude: data?.BusinessMaps?.longitude,
title: data?.BusinessMaps?.namePin,
});
}}
>
Buka Maps
</ButtonCustom>
</StackCustom>
</DrawerCustom>
</>
);
}

View File

@@ -26,7 +26,7 @@ export default function Profile() {
const [dataToken, setDataToken] = useState<IProfile>();
const [listPortofolio, setListPortofolio] = useState<any[]>();
const { logout, isAdmin, user } = useAuth();
const { token, logout, isAdmin, user, userData } = useAuth();
const openDrawer = () => {
setIsDrawerOpen(true);
@@ -42,7 +42,8 @@ export default function Profile() {
onLoadPortofolio(id as string);
onLoadUserByToken();
isUserCheck();
}, [id])
userData(token as string);
}, [id, token])
);
const isUserCheck = () => {
@@ -136,10 +137,7 @@ const ButtonnDot = ({
}) => {
const isId = id === undefined || id === null;
console.log("ID CHECK", id);
if (isId) {
console.log("ID UNDEFINED", id);
return (
<>
<TouchableOpacity onPress={logout}>

View File

@@ -8,11 +8,13 @@ import {
LoaderCustom,
MenuDrawerDynamicGrid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon";
import { IMenuDrawerItem } from "@/components/_Interface/types";
import ReportBox from "@/components/Box/ReportBox";
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
import { Voting_BoxDetailSection } from "@/screens/Voting/BoxDetailSection";
import Voting_ButtonStatusSection from "@/screens/Voting/ButtonStatusSection";
@@ -49,6 +51,8 @@ export default function VotingDetailStatus() {
setLoadingGetData(true);
const response = await apiVotingGetOne({ id: id as string });
console.log("[DATA BY ID]", JSON.stringify(response, null, 2));
if (response.success) {
setData(response.data);
}
@@ -127,6 +131,13 @@ export default function VotingDetailStatus() {
</BaseBox>
)}
<Spacing height={0} />
{data &&
data?.catatan &&
(status === "draft" || status === "rejected") && (
<ReportBox text={data?.catatan} />
)}
<Voting_BoxDetailSection data={data as any} />
{status === "publish" ? (
<Voting_BoxDetailHasilVotingSection

View File

@@ -9,6 +9,7 @@ import {
} from "@/components";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth";
import { apiUser } from "@/service/api-client/api-user";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import Toast from "react-native-toast-message";
@@ -19,12 +20,25 @@ export default function WaitingRoom() {
async function handleCheck() {
try {
const response = await userData(token as string);
if (response.active) {
Toast.show({
type: "success",
text1: "Akun anda telah aktif", // text2: "Anda berhasil login",
});
router.replace(`/(application)/(user)/profile/create`);
const checkProfile = await apiUser(response.id);
if (checkProfile?.data?.Profile) {
Toast.show({
type: "success",
text1: "Akun anda telah aktif kembali", // text2: "Anda berhasil login",
});
router.replace(`/(application)/(user)/home`);
} else {
Toast.show({
type: "success",
text1: "Akun anda telah aktif", // text2: "Anda berhasil login",
});
router.replace(`/(application)/(user)/profile/create`);
}
// router.replace(`/(application)/(user)/profile/create`);
} else {
Toast.show({
type: "error",

View File

@@ -15,7 +15,10 @@ import {
ICON_SIZE_XLARGE,
} from "@/constants/constans-value";
import { useAuth } from "@/hooks/use-auth";
import { adminListMenu } from "@/screens/Admin/listPageAdmin";
import {
adminListMenu,
superAdminListMenu,
} from "@/screens/Admin/listPageAdmin";
import { GStyles } from "@/styles/global-styles";
import { FontAwesome6, Ionicons } from "@expo/vector-icons";
import { router, Stack } from "expo-router";
@@ -24,8 +27,11 @@ import { useState } from "react";
export default function AdminLayout() {
const [openDrawerNavbar, setOpenDrawerNavbar] = useState(false);
const [openDrawerUser, setOpenDrawerUser] = useState(false);
// const [user, setUser] = useState(null);
const { logout } = useAuth();
const { logout, user } = useAuth();
console.log("[USER LAYOUT]", JSON.stringify(user, null, 2));
return (
<>
@@ -56,58 +62,58 @@ export default function AdminLayout() {
),
}}
>
<Stack.Screen name="dashboard" />
{/* <Stack.Screen name="dashboard" /> */}
{/* ================== Investment Start ================== */}
<Stack.Screen name="investment/index" />
{/* <Stack.Screen name="investment/index" /> */}
{/* ================== Investment End ================== */}
{/* ================== Maps Start ================== */}
<Stack.Screen name="maps" />
{/* <Stack.Screen name="maps" /> */}
{/* ================== Maps End ================== */}
{/* ================== App Information Start ================== */}
<Stack.Screen name="app-information/index" />
{/* <Stack.Screen name="app-information/index" /> */}
{/* ================== App Information End ================== */}
{/* ================== Job Start ================== */}
<Stack.Screen name="job/index" />
{/* <Stack.Screen name="job/index" /> */}
{/* <Stack.Screen name="job/publish" />
<Stack.Screen name="job/review" />
<Stack.Screen name="job/reject" /> */}
<Stack.Screen name="job/[status]/status" />
<Stack.Screen name="job/[id]/[status]/index" />
{/* <Stack.Screen name="job/[status]/status" />
<Stack.Screen name="job/[id]/[status]/index" /> */}
{/* ================== Collaboration Start ================== */}
<Stack.Screen name="collaboration/index" />
<Stack.Screen name="collaboration/publish" />
{/* <Stack.Screen name="collaboration/index" /> */}
{/* <Stack.Screen name="collaboration/publish" />
<Stack.Screen name="collaboration/group" />
<Stack.Screen name="collaboration/reject" />
<Stack.Screen name="collaboration/[id]/[status]" />
<Stack.Screen name="collaboration/[id]/group" />
<Stack.Screen name="collaboration/[id]/group" /> */}
{/* ================== Collaboration End ================== */}
{/* ================== Forum Start ================== */}
<Stack.Screen name="forum/index" />
<Stack.Screen name="forum/[id]/index" />
{/* <Stack.Screen name="forum/index" /> */}
{/* <Stack.Screen name="forum/[id]/index" />
<Stack.Screen name="forum/report-comment" />
<Stack.Screen name="forum/report-posting" />
<Stack.Screen name="forum/[id]/list-report-posting" />
<Stack.Screen name="forum/[id]/list-report-comment" />
<Stack.Screen name="forum/[id]/list-report-comment" /> */}
{/* ================== Forum End ================== */}
{/* ================== Voting Start ================== */}
<Stack.Screen name="voting/index" />
{/* <Stack.Screen name="voting/index" />
<Stack.Screen name="voting/[status]/status" />
<Stack.Screen name="voting/[id]/[status]/index" />
<Stack.Screen name="voting/[id]/reject-input" />
<Stack.Screen name="voting/[id]/reject-input" /> */}
{/* ================== Voting End ================== */}
{/* ================== Event Start ================== */}
<Stack.Screen name="event/index" />
{/* <Stack.Screen name="event/index" />
<Stack.Screen name="event/[status]/status" />
<Stack.Screen name="event/type-of-event" />
<Stack.Screen name="event/type-create" />
<Stack.Screen name="event/type-update" />
<Stack.Screen name="event/type-update" /> */}
{/* <Stack.Screen name="event/[id]/[status]/index" />
<Stack.Screen name="event/[id]/reject-input"/> */}
{/* ================== Event End ================== */}
@@ -134,7 +140,11 @@ export default function AdminLayout() {
/>
<NavbarMenu
items={adminListMenu}
items={
user?.masterUserRoleId === "2"
? adminListMenu
: superAdminListMenu
}
onClose={() => setOpenDrawerNavbar(false)}
/>
</StackCustom>
@@ -155,7 +165,7 @@ export default function AdminLayout() {
/>
}
>
<TextCustom>Username</TextCustom>
<TextCustom>{user?.username || "-"}</TextCustom>
</GridComponentView>
<GridComponentView
leftIcon={
@@ -166,7 +176,13 @@ export default function AdminLayout() {
/>
}
>
<TextCustom>User Role</TextCustom>
<TextCustom>
{user
? user?.masterUserRoleId === "2"
? "Admin"
: "Super Admin"
: "-"}
</TextCustom>
</GridComponentView>
<MenuDrawerDynamicGrid

View File

@@ -1,18 +1,89 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { router } from "expo-router";
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,
});
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,
});
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
onPress={() => router.back()}
disabled={!data?.name}
isLoading={isLoading}
onPress={() => handlerSubmit()}
>
Update
</ButtonCustom>
@@ -28,6 +99,15 @@ export default function AdminAppInformation_BusinessFieldDetail() {
label="Nama Bidang Bisnis"
placeholder="Masukan Nama Bidang Bisnis"
required
value={data?.name}
onChangeText={(value) => setData({ ...data, name: value })}
/>
<TextCustom>Status Aktivasi</TextCustom>
<Switch
color={MainColor.yellow}
value={data?.active}
onValueChange={(value) => setData({ ...data, active: value })}
/>
</StackCustom>
</ViewWrapper>

View File

@@ -6,13 +6,52 @@ import {
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { apiAdminMasterBusinessFieldCreate } from "@/service/api-admin/api-master-admin";
import { router } from "expo-router";
import { useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminAppInformation_BusinessFieldCreate() {
const [data, setData] = useState<any>({
name: "",
});
const [isLoading, setIsLoading] = useState(false);
const handlerSubmit = async () => {
if (!data.name) {
Toast.show({
type: "error",
text1: "Lengkapi Data",
});
return;
}
try {
setIsLoading(true);
const response = await apiAdminMasterBusinessFieldCreate({ data: data });
if (response.success) {
Toast.show({
type: "success",
text1: "Data berhasil di tambah",
});
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
Toast.show({
type: "error",
text1: "Gagal tambah data",
});
} finally {
setIsLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
onPress={() => handlerSubmit()}
isLoading={isLoading}
>
Tambah
</ButtonCustom>
@@ -28,6 +67,8 @@ export default function AdminAppInformation_BusinessFieldCreate() {
label="Nama Bidang Bisnis"
placeholder="Masukan Nama Bidang Bisnis"
required
value={data.name}
onChangeText={(value) => setData({ ...data, name: value })}
/>
</StackCustom>
</ViewWrapper>

View File

@@ -1,8 +1,4 @@
import {
ScrollableCustom,
StackCustom,
ViewWrapper
} from "@/components";
import { ScrollableCustom, StackCustom, ViewWrapper } from "@/components";
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import AdminAppInformation_BusinessFieldSection from "@/screens/Admin/App-Information/BusinessFieldSection";
@@ -10,6 +6,7 @@ import AdminAppInformation_Bank from "@/screens/Admin/App-Information/Informatio
import AdminAppInformation_StickerSection from "@/screens/Admin/App-Information/StickerSection";
import { router } from "expo-router";
import { useState } from "react";
import { Alert } from "react-native";
export default function AdminInformation() {
const [activeCategory, setActiveCategory] = useState<string | null>("bank");
@@ -57,7 +54,8 @@ export default function AdminInformation() {
} else if (activeCategory === "business") {
router.push("/admin/app-information/business-field/create");
} else if (activeCategory === "sticker") {
router.push("/admin/app-information/sticker/create");
Alert.alert("Coming Soon", "Next Update");
// router.push("/admin/app-information/sticker/create");
}
}}
/>

View File

@@ -1,18 +1,90 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextInputCustom,
ViewWrapper,
BoxButtonOnFooter,
ButtonCustom,
CenterCustom,
Grid,
StackCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { router } from "expo-router";
import { MainColor } from "@/constants/color-palet";
import {
apiAdminMasterBankById,
apiAdminMasterBankUpdate,
} 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_BankDetail() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [isLoading, setLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadList();
}, [id])
);
const onLoadList = async () => {
try {
const response = await apiAdminMasterBankById({ id: id as string });
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const handlerUpdate = async () => {
if (!data.namaBank || !data.namaAkun || !data.norek) {
Toast.show({
type: "error",
text1: "Lengkapi Data",
});
return;
}
try {
setLoading(true);
const response = await apiAdminMasterBankUpdate({
id: id as string,
data: data,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal update data",
});
} else {
Toast.show({
type: "success",
text1: "Success",
text2: "Data berhasil di update",
});
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
disabled={!data?.namaBank || !data?.namaAkun || !data?.norek}
isLoading={isLoading}
onPress={() => handlerUpdate()}
>
Update
</ButtonCustom>
@@ -25,22 +97,46 @@ export default function AdminAppInformation_BankDetail() {
<AdminBackButtonAntTitle title="Update Bank" />
<StackCustom>
<TextInputCustom
label="Nama Bank"
placeholder="Masukan Nama Bank"
required
/>
<Grid>
<Grid.Col span={6}>
<TextInputCustom
label="Nama Bank"
placeholder="Masukan Nama Bank"
required
value={data?.namaBank}
onChangeText={(value) =>
setData({ ...data, namaBank: value })
}
/>
</Grid.Col>
<Grid.Col span={6} style={{ alignItems: "center" }}>
<TextCustom>Status Aktivasi</TextCustom>
<CenterCustom>
<Switch
onValueChange={(value) =>
setData({ ...data, isActive: value })
}
color={MainColor.yellow}
value={data?.isActive}
/>
</CenterCustom>
</Grid.Col>
</Grid>
<TextInputCustom
label="Nama Rekening"
placeholder="Masukan Nama Rekening"
required
value={data?.namaAkun}
onChangeText={(value) => setData({ ...data, namaAkun: value })}
/>
<TextInputCustom
label="Nomor Rekening"
placeholder="Masukan Nomor Rekening"
required
value={data?.norek}
onChangeText={(value) => setData({ ...data, norek: value })}
/>
</StackCustom>
</StackCustom>

View File

@@ -1,19 +1,51 @@
import {
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextInputCustom,
ViewWrapper
BoxButtonOnFooter,
ButtonCustom,
StackCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { apiAdminMasterBankCreate } from "@/service/api-admin/api-master-admin";
import { router } from "expo-router";
import { useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminAppInformation_BankCreate() {
const [data, setData] = useState<any>({
namaBank: "",
namaAkun: "",
norek: "",
});
const [isLoading, setLoading] = useState(false);
const handlerSubmit = async () => {
try {
setLoading(true);
const response = await apiAdminMasterBankCreate({ data: data });
if (response.success) {
Toast.show({
type: "success",
text1: "Data berhasil di tambah",
});
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
Toast.show({
type: "error",
text1: "Gagal tambah data",
});
} finally {
setLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom
onPress={() => router.back()}
>
<ButtonCustom isLoading={isLoading} onPress={handlerSubmit}>
Tambah
</ButtonCustom>
</BoxButtonOnFooter>
@@ -29,18 +61,25 @@ export default function AdminAppInformation_BankCreate() {
label="Nama Bank"
placeholder="Masukan Nama Bank"
required
value={data.namaBank}
onChangeText={(value) => setData({ ...data, namaBank: value })}
/>
<TextInputCustom
label="Nama Rekening"
placeholder="Masukan Nama Rekening"
required
value={data.namaAkun}
onChangeText={(value) => setData({ ...data, namaAkun: value })}
/>
<TextInputCustom
keyboardType="numeric"
label="Nomor Rekening"
placeholder="Masukan Nomor Rekening"
required
value={data.norek}
onChangeText={(value) => setData({ ...data, norek: value })}
/>
</StackCustom>
</StackCustom>

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BaseBox,
BoxButtonOnFooter,
@@ -8,18 +9,45 @@ import {
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
import { MainColor } from "@/constants/color-palet";
import { useLocalSearchParams } from "expo-router";
import { apiAdminCollaborationGetById } from "@/service/api-admin/api-admin-collaboration";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminCollaborationPublish() {
const { id, status } = useLocalSearchParams();
console.log("params:", id, status);
const bottomFooter = (
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
handlerLoadData();
}, [status])
);
const handlerLoadData = async () => {
try {
const response = await apiAdminCollaborationGetById({
id: id as string,
category: status as any,
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const bottomFooter = status === "publish" && (
<BoxButtonOnFooter>
<ButtonCustom
backgroundColor={MainColor.red}
textColor="white"
onPress={() => {}}
onPress={() => {
router.push(`/admin/collaboration/${id}/reject-input`);
}}
>
Reject
</ButtonCustom>
@@ -29,12 +57,12 @@ export default function AdminCollaborationPublish() {
return (
<>
<ViewWrapper
headerComponent={<AdminBackButtonAntTitle title={`Detail ${status}`} />}
headerComponent={<AdminBackButtonAntTitle title={`Detail`} />}
footerComponent={bottomFooter}
>
<BaseBox>
<StackCustom>
{listData.map((item, i) => (
{listData(data)?.map((item, i) => (
<Grid key={i}>
<Grid.Col
span={4}
@@ -49,41 +77,49 @@ export default function AdminCollaborationPublish() {
))}
</StackCustom>
</BaseBox>
{data?.report && (
<BaseBox>
<GridTwoView
spanLeft={4}
spanRight={8}
leftIcon={<TextCustom bold>Catatan report</TextCustom>}
rightIcon={<TextCustom>{data?.report}</TextCustom>}
/>
</BaseBox>
)}
</ViewWrapper>
</>
);
}
const listData = [
const listData = (data: any) => [
{
label: "Username",
value: "Bagas Banuna",
value: (data && data?.Author?.username) || "-",
},
{
label: "Judul Proyek",
value:
"Judul Proyek: Lorem ipsum dolor sit amet consectetur adipisicing elit.",
value: (data && data?.title) || "-",
},
{
label: "Industri",
value: "Kesehatan",
value: (data && data?.ProjectCollaborationMaster_Industri?.name) || "-",
},
{
label: "Jumlah Partisipan ",
value: "0",
value: (data && data?.ProjectCollaboration_Partisipasi.length) || "0",
},
{
label: "Lokasi",
value: "Kuta Selatan, Bali",
value: (data && data?.lokasi) || "-",
},
{
label: "Tujuan Proyek",
value:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
value: (data && data?.purpose) || "-",
},
{
label: "Keuntungan",
value:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
value: (data && data?.benefit) || "-",
},
];

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BaseBox,
Grid,
@@ -6,23 +7,45 @@ import {
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import { useLocalSearchParams } from "expo-router";
import { apiAdminCollaborationGetById } from "@/service/api-admin/api-admin-collaboration";
import { useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminCollaborationGroup() {
const { id } = useLocalSearchParams();
console.log("params:", id);
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminCollaborationGetById({
id: id as string,
category: "group",
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
return (
<>
<ViewWrapper
headerComponent={
<AdminBackButtonAntTitle title={`Detail Group ${id}`} />
}
headerComponent={<AdminBackButtonAntTitle title={`Detail Group`} />}
>
<StackCustom gap={"xs"}>
<BaseBox>
<StackCustom>
{listData.map((item, i) => (
<Grid key={i}>
{listData(data).map((item: any, index: number) => (
<Grid key={index}>
<Grid.Col
span={4}
style={{ justifyContent: "center", paddingRight: 10 }}
@@ -40,21 +63,36 @@ export default function AdminCollaborationGroup() {
<StackCustom>
<TextCustom align="center">Anggota</TextCustom>
{Array.from({ length: 10 }).map((_, i) => (
<Grid key={i}>
<Grid.Col
span={4}
style={{ justifyContent: "center", paddingRight: 10 }}
>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
<Grid.Col span={8} style={{ justifyContent: "center" }}>
<TextCustom>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
</TextCustom>
</Grid.Col>
</Grid>
))}
{data?.ProjectCollaboration_AnggotaRoomChat?.map(
(item: any, index: number) => (
<StackCustom key={index} gap={0}>
<Grid>
<Grid.Col
span={4}
style={{ justifyContent: "center", paddingRight: 10 }}
>
<TextCustom bold>Nama</TextCustom>
</Grid.Col>
<Grid.Col span={8} style={{ justifyContent: "center" }}>
<TextCustom>
{item?.User?.Profile?.name || "-"}
</TextCustom>
</Grid.Col>
</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>
</BaseBox>
</StackCustom>
@@ -63,35 +101,35 @@ export default function AdminCollaborationGroup() {
);
}
const listData = [
const listData = (data: any) => [
{
label: "Admin Group",
value: "Bagas Banuna",
value: data?.ProjectCollaboration?.Author?.username || "-",
},
{
label: "Nama Group",
value: "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
value: data?.name || "-",
},
{
label: "Industri",
value: "Kesehatan",
value:
data?.ProjectCollaboration?.ProjectCollaborationMaster_Industri?.name ||
"-",
},
{
label: "Jumlah Partisipan ",
value: "0",
value: data?.ProjectCollaboration_AnggotaRoomChat?.length || "-",
},
{
label: "Lokasi",
value: "Kuta Selatan, Bali",
value: data?.ProjectCollaboration?.lokasi || "-",
},
{
label: "Tujuan Proyek",
value:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
value: data?.ProjectCollaboration?.purpose || "-",
},
{
label: "Keuntungan",
value:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
value: data?.ProjectCollaboration?.benefit || "-",
},
];

View File

@@ -6,12 +6,42 @@ import {
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import { apiAdminCollaborationReject } from "@/service/api-admin/api-admin-collaboration";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminJobRejectInput() {
export default function AdminCollaborationRejectInput() {
const { id } = useLocalSearchParams();
const [value, setValue] = useState(id as string);
const [value, setValue] = useState("");
const handlerReject = async () => {
console.log("value:", value);
// router.replace(`/admin/job/reject/status`);
try {
const response = await apiAdminCollaborationReject({
id: id as string,
data: value,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Report gagal",
});
}
Toast.show({
type: "success",
text1: "Report berhasil",
});
router.back();
} catch (error) {
console.log("[ERROR]", error);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<AdminButtonReject
@@ -22,12 +52,8 @@ export default function AdminJobRejectInput() {
message: "Apakah anda yakin ingin menolak data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressLeft: () => {
router.back();
},
onPressRight: () => {
console.log("value:", value);
router.replace(`/admin/job/reject/status`);
handlerReject();
},
})
}
@@ -39,7 +65,9 @@ export default function AdminJobRejectInput() {
<>
<ViewWrapper
footerComponent={buttonSubmit}
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
headerComponent={
<AdminBackButtonAntTitle title="Penolakan Collaboration" />
}
>
<TextAreaCustom
value={value}

View File

@@ -1,67 +1,101 @@
import {
ActionIcon,
BaseBox,
Spacing,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
ViewWrapper
} from "@/components";
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 { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
import { Octicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper";
export default function AdminCollaborationGroup() {
const [list, setList] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminCollaboration({
category: "group",
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
<StackCustom gap={"xs"}>
<StackCustom>
<AdminComp_BoxTitle title="Group" />
<BaseBox>
<>
<AdminTitleTable
title1="Aksi"
title2="Admin Group"
title3="Nama Group"
title2="Jumlah peserta"
title3="Nama group"
/>
<Spacing height={10} />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom align="center" color="gray">
Belum ada data
</TextCustom>
) : (
list?.map((item: any, index: number) => (
<View key={index}>
<AdminTableValue
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/collaboration/${item.id}/group`);
}}
/>
}
onPress={() => {
router.push(`/admin/collaboration/${index}/group`);
}}
value2={
<TextCustom truncate={1}>
{item?.ProjectCollaboration_AnggotaRoomChat?.length ||
"-"}
</TextCustom>
}
value3={
<TextCustom truncate={2}>{item?.name || "-"}</TextCustom>
}
/>
}
value2={
<TextCustom truncate={1}>Username username </TextCustom>
}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae
mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
</View>
))
)}
</>
</StackCustom>
</ViewWrapper>
</>

View File

@@ -2,14 +2,39 @@ import { StackCustom, ViewWrapper } from "@/components";
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
import { Entypo, FontAwesome } from "@expo/vector-icons";
import { useFocusEffect } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminCollaboration() {
const [list, setList] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
const response = await apiAdminCollaboration({
category: "dashboard",
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
<StackCustom gap={"xs"}>
{listData.map((item, i) => (
{listData(list as any).map((item, i) => (
<AdminComp_BoxDashboard key={i} item={item} />
))}
</StackCustom>
@@ -18,20 +43,22 @@ export default function AdminCollaboration() {
);
}
const listData = [
{
label: "Publish",
value: 4,
icon: <Entypo name="publish" size={25} color={MainColor.green} />,
},
{
label: "Group",
value: 7,
icon: <FontAwesome name="group" size={25} color={MainColor.yellow} />,
},
{
label: "Reject",
value: 7,
icon: <FontAwesome name="warning" size={25} color={MainColor.red} />,
},
];
const listData = (list: any) => {
return [
{
label: "Publish",
value: (list && list?.publish) || "0",
icon: <Entypo name="publish" size={25} color={MainColor.green} />,
},
{
label: "Group",
value: (list && list?.group) || "0",
icon: <FontAwesome name="group" size={25} color={MainColor.yellow} />,
},
{
label: "Reject",
value: (list && list?.reject) || "0",
icon: <FontAwesome name="warning" size={25} color={MainColor.red} />,
},
];
};

View File

@@ -1,7 +1,6 @@
import {
ActionIcon,
BaseBox,
Spacing,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
@@ -11,60 +10,95 @@ import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
import { Octicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper";
export default function AdminCollaborationPublish() {
const [list, setList] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminCollaboration({
category: "publish",
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
<StackCustom gap={"xs"}>
<StackCustom>
<AdminComp_BoxTitle title="Publish" />
<BaseBox>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Proyek"
/>
<Spacing height={10} />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/collaboration/${index}/publish`);
}}
/>
}
value2={
<TextCustom truncate={1}>Username username </TextCustom>
}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae
mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Proyek"
/>
{/* <Spacing height={10} /> */}
<Divider />
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom align="center" color="gray">
Belum ada data
</TextCustom>
) : (
list?.map((item: any, index: number) => (
<View key={index}>
<AdminTableValue
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/collaboration/${item?.id}/publish`);
}}
/>
}
value2={
<TextCustom align="center" truncate={1}>
{item?.Author?.username || "-"}{" "}
</TextCustom>
}
value3={
<TextCustom align="center" truncate={2}>
{item?.title || "-"}
</TextCustom>
}
/>
</View>
))
)}
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -1,67 +1,99 @@
import {
ActionIcon,
BaseBox,
Spacing,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
ViewWrapper
} from "@/components";
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 { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminCollaboration } from "@/service/api-admin/api-admin-collaboration";
import { Octicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { View } from "react-native";
import { Divider } from "react-native-paper";
export default function AdminCollaborationReject() {
const [list, setList] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminCollaboration({
category: "reject",
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Collaboration" />}>
<StackCustom gap={"xs"}>
<StackCustom>
<AdminComp_BoxTitle title="Reject" />
<BaseBox>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Proyek"
/>
<Spacing height={10} />
<Divider />
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Proyek"
/>
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/collaboration/${index}/reject`);
}}
/>
}
value2={
<TextCustom truncate={1}>Username username </TextCustom>
}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae
mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom>Belum ada data</TextCustom>
) : (
list?.map((item: any) => (
<View key={item.id}>
<AdminTableValue
key={item?.id}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/collaboration/${item?.id}/reject`);
}}
/>
}
value2={
<TextCustom align="center" truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
}
value3={
<TextCustom align="center" truncate={2}>
{item?.title || "-"}
</TextCustom>
}
/>
</View>
))
)}
</StackCustom>
</ViewWrapper>
</>

View File

@@ -1,13 +1,31 @@
import {
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import { StackCustom, TextCustom, ViewWrapper } from "@/components";
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import { MainColor } from "@/constants/color-palet";
import { apiAdminMainDashboardGetAll } from "@/service/api-admin/api-admin-main-dashboard";
import { Ionicons } from "@expo/vector-icons";
import { useEffect, useState } from "react";
export default function AdminDashboard() {
const [countUser, setCountUser] = useState(0);
const [countPortofolio, setCountPortofolio] = useState(0);
useEffect(() => {
onLoadData();
}, []);
const onLoadData = async () => {
try {
const response = await apiAdminMainDashboardGetAll();
if (response.success) {
setCountUser(response.data.user);
setCountPortofolio(response.data.portofolio);
}
} catch (error) {
console.log("[ERROR LOAD DATA]", error);
}
};
return (
<>
<ViewWrapper>
@@ -15,7 +33,7 @@ export default function AdminDashboard() {
<TextCustom bold size={30}>
Main Dashboard
</TextCustom>
{listData.map((item, i) => (
{listData(countUser, countPortofolio).map((item, i) => (
<AdminComp_BoxDashboard key={i} item={item} />
))}
</StackCustom>
@@ -24,15 +42,15 @@ export default function AdminDashboard() {
);
}
const listData = [
const listData = (countUser: number, countPortofolio: number) => [
{
label: "User",
value: 4,
value: countUser,
icon: <Ionicons name="people" size={30} color={MainColor.yellow} />,
},
{
label: "Portofolio",
value: 7,
value: countPortofolio,
icon: (
<Ionicons name="id-card-outline" size={30} color={MainColor.yellow} />
),

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
AlertDefaultSystem,
@@ -15,98 +16,96 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8";
import { MainColor } from "@/constants/color-palet";
import ReportBox from "@/components/Box/ReportBox";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import dayjs from "dayjs";
import { router, useLocalSearchParams } from "expo-router";
import { useAuth } from "@/hooks/use-auth";
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
import { DEEP_LINK_URL } from "@/service/api-config";
import { colorBadge } from "@/utils/colorBadge";
import { dateTimeView } from "@/utils/dateTimeView";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import React from "react";
import React, { useCallback } from "react";
import QRCode from "react-native-qrcode-svg";
export default function AdminEventDetail() {
const { user } = useAuth();
const { id, status } = useLocalSearchParams();
const [openDrawer, setOpenDrawer] = React.useState(false);
const colorBadge = () => {
if (status === "publish") {
return MainColor.green;
} else if (status === "review") {
return MainColor.orange;
} else if (status === "reject") {
return MainColor.red;
} else {
return MainColor.placeholder;
console.log("[ID QRCODE]", id);
console.log("[STATUS Detail]", status);
const [openDrawer, setOpenDrawer] = React.useState(false);
const newURL = DEEP_LINK_URL
console.log("[DEEP LINK URL]", newURL);
const [data, setData] = React.useState<any | null>(null);
const deepLinkURL = `${DEEP_LINK_URL}/--/event/${id}/confirmation?userId=${user?.id}`;
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminEventById({
id: id as string,
});
// console.log(`[RES DATA BY ID: ${id}]`, JSON.stringify(response, null, 2));
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const listData = [
{
label: "Pembuat Event",
value: `Bagas Banuna ${id}`,
value: (data && data?.Author?.username) || "-",
},
{
label: "Judul Event",
value: `Event 123`,
value: (data && data?.title) || "-",
},
{
label: "Status",
value: (
<BadgeCustom color={colorBadge()}>
{_.startCase(status as string)}
</BadgeCustom>
),
value:
(data && (
<BadgeCustom color={colorBadge({ status: status as string })}>
{_.startCase(status as string)}
</BadgeCustom>
)) ||
"-",
},
{
label: "Lokasi",
value: "Lokasi Event",
value: (data && data?.lokasi) || "-",
},
{
label: "Tipe Acara",
value: "Tipe Acara",
value: (data && data?.EventMaster_TipeAcara?.name) || "-",
},
{
label: "Mulai Event",
value: dayjs().format("DD/MM/YYYY HH:mm:ss"),
value:
(data && data?.tanggal && dateTimeView({ date: data?.tanggal })) || "-",
},
{
label: "Event Berakhir",
value: dayjs().add(3, "day").format("DD/MM/YYYY HH:mm:ss"),
value:
(data &&
data?.tanggalSelesai &&
dateTimeView({ date: data?.tanggalSelesai })) ||
"-",
},
{
label: "Deskripsi",
value: "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
value: (data && data?.deskripsi) || "-",
},
// {
// label: "Daftar Tipe Acara",
// value: (
// <>
// <List.Item
// title={<TextCustom>Pilihan 1</TextCustom>}
// left={(props) => (
// <List.Icon {...props} icon="circle" color={MainColor.yellow} />
// )}
// />
// <List.Item
// title={<TextCustom>Pilihan 2</TextCustom>}
// left={(props) => (
// <List.Icon {...props} icon="circle" color={MainColor.yellow} />
// )}
// />
// <List.Item
// title={<TextCustom>Pilihan 3</TextCustom>}
// left={(props) => (
// <List.Icon {...props} icon="circle" color={MainColor.yellow} />
// )}
// />
// <List.Item
// title={<TextCustom>Pilihan 4</TextCustom>}
// left={(props) => (
// <List.Icon {...props} icon="circle" color={MainColor.yellow} />
// )}
// />
// </>
// ),
// },
];
const rightComponent = (
@@ -118,6 +117,23 @@ export default function AdminEventDetail() {
/>
);
const handlerSubmit = async () => {
try {
const response = await funUpdateStatusEvent({
id: id as string,
changeStatus: "publish",
});
console.log("[RES PUBLISH]", JSON.stringify(response, null, 2));
if (response.success) {
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
}
};
return (
<>
<ViewWrapper
@@ -125,7 +141,7 @@ export default function AdminEventDetail() {
<AdminBackButtonAntTitle
title={`Detail Data`}
rightComponent={
(status === "publish" || status === "riwayat") && rightComponent
(status === "publish" || status === "history") && rightComponent
}
/>
}
@@ -143,12 +159,19 @@ export default function AdminEventDetail() {
<Spacing />
</BaseBox>
{(status === "publish" || status === "riwayat") && (
{data &&
data?.catatan &&
(status === "reject" || status === "review") && (
<ReportBox text={data?.catatan} />
)}
{(status === "publish" || status === "history") && (
<BaseBox>
<StackCustom style={{ alignItems: "center" }}>
<TextCustom bold>QR Code Event</TextCustom>
<QRCode
value="https://google.com"
value={deepLinkURL}
size={200}
// logo={require("@/assets/images/logo-hipmi.png")}
// logoSize={70}
@@ -168,16 +191,11 @@ export default function AdminEventDetail() {
message: "Apakah anda yakin ingin mempublikasikan data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressLeft: () => {
router.back();
},
onPressRight: () => {
router.back();
},
onPressRight: () => handlerSubmit(),
});
}}
onReject={() => {
router.push(`/admin/event/${id}/reject-input`);
router.push(`/admin/event/${id}/reject-input?status=${status}`);
}}
/>
)}
@@ -186,7 +204,7 @@ export default function AdminEventDetail() {
<AdminButtonReject
title="Tambah Catatan"
onReject={() => {
router.push(`/admin/event/${id}/reject-input`);
router.push(`/admin/event/${id}/reject-input?status=${status}`);
}}
/>
)}

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AlertDefaultSystem,
BoxButtonOnFooter,
@@ -6,15 +7,78 @@ import {
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminEventRejectInput() {
const { id } = useLocalSearchParams();
const [value, setValue] = useState(id as string);
const { id, status } = useLocalSearchParams();
const [data, setData] = useState<any>("");
const [isLoading, setIsLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminEventById({
id: id as string,
});
if (response.success) {
setData(response.data.catatan);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const handleUpdate = async ({
changeStatus,
}: {
changeStatus: "publish" | "review" | "reject";
}) => {
try {
setIsLoading(true);
const response = await funUpdateStatusEvent({
id: id as string,
changeStatus,
data: data,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Report gagal",
});
}
Toast.show({
type: "success",
text1: "Report berhasil",
});
if (status === "review") {
router.replace(`/admin/event/reject/status`);
} else if (status === "reject") {
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<AdminButtonReject
isLoading={isLoading}
title="Reject"
onReject={() =>
AlertDefaultSystem({
@@ -22,12 +86,8 @@ export default function AdminEventRejectInput() {
message: "Apakah anda yakin ingin menolak data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressLeft: () => {
router.back();
},
onPressRight: () => {
console.log("value:", value);
router.replace(`/admin/event/reject/status`);
handleUpdate({ changeStatus: "reject" });
},
})
}
@@ -42,8 +102,8 @@ export default function AdminEventRejectInput() {
headerComponent={<AdminBackButtonAntTitle title="Penolakan Event" />}
>
<TextAreaCustom
value={value}
onChangeText={setValue}
value={data}
onChangeText={setData}
placeholder="Masukan alasan"
required
showCount

View File

@@ -1,27 +1,67 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
LoaderCustom,
SearchInput,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
ViewWrapper
} from "@/components";
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 { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminEvent } from "@/service/api-admin/api-admin-event";
import { Octicons } from "@expo/vector-icons";
import { router, useLocalSearchParams } from "expo-router";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminEventStatus() {
const { status } = useLocalSearchParams();
console.log("[STATUS EVENT]", status);
const [listData, setListData] = useState<any[] | null>(null);
const [loadData, setLoadData] = useState(false);
const [search, setSearch] = useState<string>("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [status, search])
);
const onLoadData = async () => {
try {
setLoadData(true);
const response = await apiAdminEvent({
category: status as "publish" | "review" | "reject" | "history" as any,
search,
});
console.log(
`[RES LIST BY STATUS: ${status}]`,
JSON.stringify(response, null, 2)
);
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadData(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
value={search}
onChangeText={(value) => setSearch(value)}
/>
);
return (
@@ -32,44 +72,50 @@ export default function AdminEventStatus() {
rightComponent={rightComponent}
/>
<BaseBox>
<StackCustom gap={"sm"}>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Event"
/>
<Spacing />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/event/${index}/${status}`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
{loadData ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center" size="small" color="gray">Belum ada data</TextCustom>
) : (
listData?.map((item, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/event/${item.id}/${status}`);
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
}
value3={
<TextCustom align="center" truncate={2}>
{item?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);

View File

@@ -9,13 +9,67 @@ import {
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { apiAdminEvent } from "@/service/api-admin/api-admin-event";
import { useFocusEffect } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminVoting() {
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [])
);
const onLoadData = async () => {
try {
const response = await apiAdminEvent({
category: "dashboard",
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const listData = [
{
label: "Publish",
value: (data && data.publish) || 0,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Review",
value: (data && data.review) || 0,
icon: <IconReview size={25} color={MainColor.orange} />,
},
{
label: "Reject",
value: (data && data.reject) || 0,
icon: <IconReject size={25} color={MainColor.red} />,
},
{
label: "Riwayat",
value: (data && data.history) || 0,
icon: <IconArchive size={25} color={MainColor.placeholder} />,
},
{
label: "Tipe Acara",
value: (data && data.typeOfEvent) || 0,
icon: <IconList size={25} color={MainColor.placeholder} />,
},
];
return (
<>
<ViewWrapper>
<AdminTitlePage title="Event" />
<Spacing />
<StackCustom gap={"xs"}>
{listData.map((item, i) => (
<AdminComp_BoxDashboard key={i} item={item} />
@@ -25,31 +79,3 @@ export default function AdminVoting() {
</>
);
}
const listData = [
{
label: "Publish",
value: 3,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Review",
value: 8,
icon: <IconReview size={25} color={MainColor.orange} />,
},
{
label: "Reject",
value: 4,
icon: <IconReject size={25} color={MainColor.red} />,
},
{
label: "Riwayat",
value: 6,
icon: <IconArchive size={25} color={MainColor.placeholder} />,
},
{
label: "Tipe Acara",
value: 7,
icon: <IconList size={25} color={MainColor.placeholder} />,
},
];

View File

@@ -1,58 +1,79 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
AlertDefaultSystem,
BadgeCustom,
BaseBox,
DrawerCustom,
MenuDrawerDynamicGrid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import { IconDot, IconView } from "@/components/_Icon/IconComponent";
import { IconTrash } from "@/components/_Icon/IconTrash";
import { IconDot } from "@/components/_Icon/IconComponent";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8";
import { MainColor } from "@/constants/color-palet";
import {
ICON_SIZE_BUTTON,
ICON_SIZE_MEDIUM,
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 { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import { useState } from "react";
import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminForumDetailPosting() {
const { id } = useLocalSearchParams();
const [openDrawerPage, setOpenDrawerPage] = useState(false);
const [openDrawerAction, setOpenDrawerAction] = useState(false);
const [id, setId] = useState<any>();
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const handlerAction = (item: { value: string; path: string }) => {
if (item.value === "delete") {
AlertDefaultSystem({
title: "Hapus Posting",
message: "Apakah Anda yakin ingin menghapus posting ini?",
textLeft: "Batal",
textRight: "Hapus",
onPressRight: () => {
Toast.show({
type: "success",
text1: "Posting berhasil dihapus",
});
},
const onLoadData = async () => {
try {
const response = await apiAdminForumPostingById({
id: id as string,
});
} else {
router.navigate(item.path as any);
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
setOpenDrawerAction(false);
};
const listDataAction = [
{
label: "Username",
value: data?.Author?.username || "-",
},
{
label: "Status",
value:
(data && (
<BadgeCustom
color={
data?.ForumMaster_StatusPosting?.status === "Open"
? MainColor.green
: MainColor.red
}
>
{data?.ForumMaster_StatusPosting?.status || "-"}
</BadgeCustom>
)) ||
"-",
},
{
label: "Komentar",
value: data?.JumlahKomentar || 0,
},
{
label: "Total Report",
value: data?.JumlahReportPosting || 0,
},
];
return (
<>
<ViewWrapper
@@ -60,14 +81,25 @@ export default function AdminForumDetailPosting() {
<AdminBackButtonAntTitle
title="Detail Posting"
rightComponent={
<ActionIcon
icon={<IconDot size={16} color={MainColor.darkblue} />}
onPress={() => setOpenDrawerPage(true)}
/>
data &&
data?.isActive && (
<ActionIcon
icon={<IconDot size={16} color={MainColor.darkblue} />}
onPress={() => setOpenDrawerPage(true)}
/>
)
}
/>
}
>
{data && !data?.isActive && (
<BaseBox>
<TextCustom bold align="center" color="red">
Postingan ini telah di nonaktifkan
</TextCustom>
</BaseBox>
)}
<BaseBox>
<StackCustom gap={"sm"}>
{listDataAction.map((item, i) => (
@@ -77,50 +109,14 @@ export default function AdminForumDetailPosting() {
value={<TextCustom>{item.value}</TextCustom>}
/>
))}
<TextCustom bold>Posting</TextCustom>
<TextCustom>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Asperiores cupiditate nobis dignissimos explicabo quo unde dolorum
numquam eos ab laborum fugiat illo nam velit quibusdam, maxime
assumenda aut vero provident!
</TextCustom>
</StackCustom>
</BaseBox>
{/* <AdminComp_BoxTitle title="Komentar" rightComponent={rightComponent} /> */}
<BaseBox>
<AdminTitleTable title1="Aksi" title2="Username" title3="Komentar" />
<Spacing />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Ionicons
name="ellipsis-vertical-outline"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
setOpenDrawerAction(true);
setId(index + 1);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae mollitia.
</TextCustom>
}
/>
))}
<StackCustom gap={"sm"}>
<TextCustom bold>Postingan</TextCustom>
<TextCustom>{(data && data?.diskusi) || "-"}</TextCustom>
</StackCustom>
</BaseBox>
</ViewWrapper>
@@ -143,6 +139,18 @@ export default function AdminForumDetailPosting() {
value: "detail",
path: `/admin/forum/${id}/list-report-posting`,
},
{
icon: (
<Ionicons
name="list"
size={ICON_SIZE_XLARGE}
color={MainColor.white}
/>
),
label: "Daftar Komentar",
value: "detail",
path: `/admin/forum/${id}/list-comment`,
},
]}
onPressItem={(item) => {
router.navigate(item.path as any);
@@ -150,54 +158,6 @@ export default function AdminForumDetailPosting() {
}}
/>
</DrawerCustom>
<DrawerCustom
isVisible={openDrawerAction}
closeDrawer={() => setOpenDrawerAction(false)}
height={"auto"}
>
<MenuDrawerDynamicGrid
data={[
{
icon: <IconView />,
label: "Detail Komentar",
value: "detail",
path: `admin/forum/${id}/list-report-comment`,
},
{
icon: (
<IconTrash size={ICON_SIZE_MEDIUM} color={MainColor.white} />
),
label: "Hapus Komentar",
value: "delete",
path: "",
color: MainColor.red,
},
]}
onPressItem={(item) => {
handlerAction(item as any);
}}
/>
</DrawerCustom>
</>
);
}
const listDataAction = [
{
label: "Username",
value: "Username",
},
{
label: "Status",
value: <BadgeCustom color={MainColor.green}>Open</BadgeCustom>,
},
{
label: "Komentar",
value: "10",
},
{
label: "Total Report",
value: "1",
},
];

View File

@@ -0,0 +1,91 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import { IconOpenTo } from "@/components/_Icon/IconOpenTo";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { apiAdminForumCommentById } from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminForumListComment() {
const { id } = useLocalSearchParams();
const [listComment, setListComment] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadComment();
}, [id])
);
const onLoadComment = async () => {
try {
setLoadList(true);
const response = await apiAdminForumCommentById({
id: id as string,
category: "get-all",
});
if (response.success) {
setListComment(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
setListComment([]);
} finally {
setLoadList(false);
}
};
return (
<>
<ViewWrapper
headerComponent={<AdminBackButtonAntTitle title="Daftar Komentar" />}
>
<StackCustom>
<AdminTitleTable title1="Aksi" title2="Report" title3="Komentar" />
<Divider />
{loadList ? (
<LoaderCustom />
) : _.isEmpty(listComment) ? (
<TextCustom align="center" color="gray">
Tidak ada komentar
</TextCustom>
) : (
listComment?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<IconOpenTo
onPress={() => {
router.push(
`/admin/forum/${item.id}/list-report-comment`
);
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.countReport || 0}
</TextCustom>
}
value3={
<TextCustom truncate={2}>{item?.komentar || "-"}</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -1,10 +1,11 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
AlertDefaultSystem,
BaseBox,
DrawerCustom,
LoaderCustom,
MenuDrawerDynamicGrid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
@@ -18,14 +19,64 @@ import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { router } from "expo-router";
import { useState } from "react";
import {
apiAdminForumCommentById,
apiAdminForumDeactivateComment,
apiAdminForumListReportCommentById,
} from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminForumReportComment() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [listReport, setListReport] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState(false);
const [openDrawer, setOpenDrawer] = useState(false);
const [openDrawerAction, setOpenDrawerAction] = useState(false);
const [selectedReport, setSelectedReport] = useState({
id: "",
username: "",
kategori: "",
keterangan: "",
deskripsi: "",
});
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
setLoadList(true);
const response = await apiAdminForumCommentById({
id: id as string,
category: "get-one",
});
const responseReport = await apiAdminForumListReportCommentById({
id: id as string,
});
if (response.success) {
setData(response.data);
}
if (responseReport.success) {
setListReport(responseReport.data);
}
} catch (error) {
console.log("[ERROR]", error);
setData(null);
setListReport([]);
} finally {
setLoadList(false);
}
};
return (
<>
@@ -44,53 +95,65 @@ export default function AdminForumReportComment() {
>
<BaseBox>
<StackCustom gap={"sm"}>
{listData.map((item, i) => (
<GridDetail_4_8
key={i}
label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>}
/>
))}
<TextCustom bold>Posting</TextCustom>
<TextCustom>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Asperiores cupiditate nobis dignissimos explicabo quo unde dolorum
numquam eos ab laborum fugiat illo nam velit quibusdam, maxime
assumenda aut vero provident!
</TextCustom>
<GridDetail_4_8
label={<TextCustom bold>Username</TextCustom>}
value={<TextCustom>{data?.Author?.username || "-"}</TextCustom>}
/>
<GridDetail_4_8
label={<TextCustom bold>Komentar</TextCustom>}
value={<TextCustom>{data?.komentar || "-"}</TextCustom>}
/>
</StackCustom>
</BaseBox>
<AdminComp_BoxTitle title="Daftar Report Komentar" />
<BaseBox>
<StackCustom>
<AdminTitleTable
title1="Aksi"
title2="Username"
title2="Pelapor"
title3="Kategori Report"
/>
<Spacing />
<Divider />
{Array.from({ length: 5 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => {
setOpenDrawerAction(true);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2} align="center">
SPAM
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(listReport) ? (
<TextCustom align="center" color="gray">
Tidak ada report
</TextCustom>
) : (
listReport?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => {
setOpenDrawerAction(true);
setSelectedReport({
id: item.id,
username: item.User?.username,
kategori: item.ForumMaster_KategoriReport?.title,
keterangan: item.ForumMaster_KategoriReport?.deskripsi,
deskripsi: item.deskripsi,
});
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.User?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.ForumMaster_KategoriReport?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
<DrawerCustom
@@ -114,7 +177,19 @@ export default function AdminForumReportComment() {
message: "Apakah Anda yakin ingin menghapus komentar ini?",
textLeft: "Batal",
textRight: "Hapus",
onPressRight: () => {
onPressRight: async () => {
const deleteComment = await apiAdminForumDeactivateComment({
id: id as string,
});
if (!deleteComment.success) {
Toast.show({
type: "error",
text1: "Komentar gagal dihapus",
});
return;
}
setOpenDrawer(false);
Toast.show({
type: "success",
@@ -132,37 +207,39 @@ export default function AdminForumReportComment() {
closeDrawer={() => setOpenDrawerAction(false)}
height={"auto"}
>
{listDataAction.map((item, i) => (
<StackCustom>
<GridDetail_4_8
key={i}
label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>}
label={<TextCustom bold>Pelapor</TextCustom>}
value={<TextCustom>{selectedReport?.username || "-"}</TextCustom>}
/>
))}
{selectedReport?.kategori && (
<>
<GridDetail_4_8
label={<TextCustom bold>Kategori Report</TextCustom>}
value={
<TextCustom>{selectedReport?.kategori || "-"}</TextCustom>
}
/>
<GridDetail_4_8
label={<TextCustom bold>Keterangan</TextCustom>}
value={
<TextCustom>{selectedReport?.keterangan || "-"}</TextCustom>
}
/>
</>
)}
{selectedReport?.deskripsi && (
<GridDetail_4_8
label={<TextCustom bold>Deskripsi</TextCustom>}
value={
<TextCustom>{selectedReport?.deskripsi || "-"}</TextCustom>
}
/>
)}
</StackCustom>
</DrawerCustom>
</>
);
}
const listData = [
{
label: "Username",
value: "Username",
},
];
const listDataAction = [
{
label: "Username",
value: "Riyusa",
},
{
label: "Kategori Report",
value: "SPAM",
},
{
label: "Deskripsi",
value:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis asperiores quidem deleniti architecto eaque et nostrum, ad consequuntur eveniet quisquam quae voluptatum ducimus! Dolorem nobis modi officia debitis, beatae mollitia.",
},
];

View File

@@ -1,11 +1,12 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
AlertDefaultSystem,
BadgeCustom,
BaseBox,
DrawerCustom,
LoaderCustom,
MenuDrawerDynamicGrid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
@@ -19,15 +20,64 @@ import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { router } from "expo-router";
import { useState } from "react";
import {
apiAdminForumDeactivatePosting,
apiAdminForumListReportPostingById,
apiAdminForumPostingById,
} from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminForumReportPosting() {
const { id } = useLocalSearchParams();
const [openDrawerPage, setOpenDrawerPage] = useState(false);
const [openDrawerAction, setOpenDrawerAction] = useState(false);
const [data, setData] = useState<any | null>(null);
const [listReport, setListReport] = useState<any[] | null>(null);
const [loadListReport, setLoadListReport] = useState(false);
const [selectedReport, setSelectedReport] = useState({
id: "",
username: "",
kategori: "",
keterangan: "",
deskripsi: "",
});
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
setLoadListReport(true);
const response = await apiAdminForumPostingById({
id: id as string,
});
const responseReport = await apiAdminForumListReportPostingById({
id: id as string,
});
if (response.success) {
setData(response.data);
}
if (responseReport.success) {
setListReport(responseReport.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadListReport(false);
}
};
return (
<>
<ViewWrapper
@@ -45,50 +95,86 @@ export default function AdminForumReportPosting() {
>
<BaseBox>
<StackCustom gap={"sm"}>
{listData.map((item, i) => (
<GridDetail_4_8
key={i}
label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>}
/>
))}
<TextCustom bold>Posting</TextCustom>
<TextCustom>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Asperiores cupiditate nobis dignissimos explicabo quo unde dolorum
numquam eos ab laborum fugiat illo nam velit quibusdam, maxime
assumenda aut vero provident!
</TextCustom>
<GridDetail_4_8
label={<TextCustom bold>Username</TextCustom>}
value={<TextCustom>{data?.Author?.username || "-"}</TextCustom>}
/>
<GridDetail_4_8
label={<TextCustom bold>Status</TextCustom>}
value={
data && data?.ForumMaster_StatusPosting?.status ? (
<BadgeCustom
color={
data?.ForumMaster_StatusPosting?.status === "Open"
? MainColor.green
: MainColor.red
}
>
{data?.ForumMaster_StatusPosting?.status === "Open"
? "Open"
: "Close"}
</BadgeCustom>
) : (
<TextCustom>{"-"}</TextCustom>
)
}
/>
<GridDetail_4_8
label={<TextCustom bold>Postingan</TextCustom>}
value={<TextCustom>{data?.diskusi || "-"}</TextCustom>}
/>
</StackCustom>
</BaseBox>
<AdminComp_BoxTitle title="Daftar Report Posting" />
<BaseBox>
<StackCustom gap={"sm"}>
<AdminTitleTable
title1="Aksi"
title2="Username"
title2="Pelapor"
title3="Kategori Report"
/>
<Spacing />
<Divider />
{Array.from({ length: 5 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => setOpenDrawerAction(true)}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2} align="center">
SPAM
</TextCustom>
}
/>
))}
</BaseBox>
{loadListReport ? (
<LoaderCustom />
) : _.isEmpty(listReport) ? (
<TextCustom align="center" color={"gray"}>
Belum ada report
</TextCustom>
) : (
listReport?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => {
setOpenDrawerAction(true);
setSelectedReport({
id: item?.id,
username: item?.User?.username,
kategori: item?.ForumMaster_KategoriReport?.title,
keterangan: item?.ForumMaster_KategoriReport?.deskripsi,
deskripsi: item?.deskripsi,
});
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.User?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.ForumMaster_KategoriReport?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
<DrawerCustom
@@ -112,13 +198,25 @@ export default function AdminForumReportPosting() {
message: "Apakah Anda yakin ingin menghapus posting ini?",
textLeft: "Batal",
textRight: "Hapus",
onPressRight: () => {
onPressRight: async () => {
const response = await apiAdminForumDeactivatePosting({
id: id as string,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Posting gagal dihapus",
});
return;
}
setOpenDrawerPage(false);
Toast.show({
type: "success",
text1: "Posting berhasil dihapus",
});
router.back()
router.back();
},
});
}}
@@ -130,41 +228,39 @@ export default function AdminForumReportPosting() {
closeDrawer={() => setOpenDrawerAction(false)}
height={"auto"}
>
{listDataAction.map((item, i) => (
<StackCustom>
<GridDetail_4_8
key={i}
label={<TextCustom bold>{item.label}</TextCustom>}
value={<TextCustom>{item.value}</TextCustom>}
label={<TextCustom bold>Pelapor</TextCustom>}
value={<TextCustom>{selectedReport?.username || "-"}</TextCustom>}
/>
))}
{selectedReport?.kategori && (
<>
<GridDetail_4_8
label={<TextCustom bold>Kategori Report</TextCustom>}
value={
<TextCustom>{selectedReport?.kategori || "-"}</TextCustom>
}
/>
<GridDetail_4_8
label={<TextCustom bold>Keterangan</TextCustom>}
value={
<TextCustom>{selectedReport?.keterangan || "-"}</TextCustom>
}
/>
</>
)}
{selectedReport?.deskripsi && (
<GridDetail_4_8
label={<TextCustom bold>Deskripsi</TextCustom>}
value={
<TextCustom>{selectedReport?.deskripsi || "-"}</TextCustom>
}
/>
)}
</StackCustom>
</DrawerCustom>
</>
);
}
const listData = [
{
label: "Username",
value: "Username",
},
{
label: "Status",
value: <BadgeCustom color={MainColor.green}>Open</BadgeCustom>,
},
];
const listDataAction = [
{
label: "Username",
value: "Firman Nusantara",
},
{
label: "Kategori Report",
value: "SPAM",
},
{
label: "Deskripsi",
value:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Blanditiis asperiores quidem deleniti architecto eaque et nostrum, ad consequuntur eveniet quisquam quae voluptatum ducimus! Dolorem nobis modi officia debitis, beatae mollitia.",
},
];

View File

@@ -1,13 +1,53 @@
import { Spacing, StackCustom, ViewWrapper } from "@/components";
import {
IconPublish,
IconReport,
} from "@/components/_Icon/IconComponent";
import { IconPublish, IconReport } from "@/components/_Icon/IconComponent";
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
import { useFocusEffect } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminForum() {
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
const response = await apiAdminForum({
category: "dashboard",
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const listData = [
{
label: "Posting",
value: data?.posting || 0,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Report Posting",
value: data?.reportPosting || 0,
icon: <IconReport size={25} color={MainColor.orange} />,
},
{
label: "Report Comment",
value: data?.reportComment || 0,
icon: <IconReport size={25} color={MainColor.red} />,
},
];
return (
<>
<ViewWrapper>
@@ -22,21 +62,3 @@ export default function AdminForum() {
</>
);
}
const listData = [
{
label: "Posting",
value: 4,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Report Posting",
value: 7,
icon: <IconReport size={25} color={MainColor.orange} />,
},
{
label: "Report Comment",
value: 5,
icon: <IconReport size={25} color={MainColor.red} />,
},
];

View File

@@ -1,8 +1,9 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
LoaderCustom,
SearchInput,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
@@ -12,15 +13,47 @@ import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { router } from "expo-router";
import React from "react";
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import React, { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminForumPosting() {
const [list, setList] = useState<any | null>(null);
const [loadList, setLoadList] = useState(false);
const [search, setSearch] = useState("");
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [search])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminForum({
category: "posting",
search: search,
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
value={search}
onChangeText={setSearch}
/>
);
@@ -28,33 +61,39 @@ export default function AdminForumPosting() {
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Forum" />}>
<AdminComp_BoxTitle title={"Posting"} rightComponent={rightComponent} />
<BaseBox>
<StackCustom>
<AdminTitleTable title1="Aksi" title2="Username" title3="Postingan" />
<Spacing />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => {
router.push(`/admin/forum/${index + 1}`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom align="center" color="gray">
Belum ada data
</TextCustom>
) : (
list?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
onPress={() => {
router.push(`/admin/forum/${item?.id}`);
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2}>{item?.diskusi || "-"}</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);

View File

@@ -1,9 +1,9 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
Divider,
LoaderCustom,
SearchInput,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
@@ -14,14 +14,48 @@ import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { router } from "expo-router";
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminForumReportComment() {
const [listData, setListData] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState<boolean>(false);
const [search, setSearch] = useState<string>("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [search])
);
const onLoadData = async () => {
try {
setLoadList(true);
const response = await apiAdminForum({
category: "report_comment",
search: search,
});
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari Komentar"
value={search}
onChangeText={setSearch}
/>
);
@@ -29,36 +63,56 @@ export default function AdminForumReportComment() {
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Forum" />}>
<AdminComp_BoxTitle
title="Report Comment"
title="Report Komentar"
rightComponent={rightComponent}
/>
<BaseBox>
<AdminTitleTable title1="Aksi" title2="Pelapor" title3="Jenis Laporan" />
<Spacing />
<StackCustom gap={"sm"}>
<AdminTitleTable
title1="Aksi"
title2="Pelapor"
title3="Jenis Laporan"
/>
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<IconView size={ICON_SIZE_BUTTON} color={MainColor.black} />
}
onPress={() => {
router.push(`/admin/forum/${index + 1}/list-report-comment`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2} align="center">
SPAM
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center" color="gray">
Belum ada data
</TextCustom>
) : (
listData?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<IconView
size={ICON_SIZE_BUTTON}
color={MainColor.black}
/>
}
onPress={() => {
router.push(
`/admin/forum/${item?.Forum_Komentar?.id}/list-report-comment`
);
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.User?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.ForumMaster_KategoriReport?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);

View File

@@ -1,12 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
Divider,
LoaderCustom,
SearchInput,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
ViewWrapper
} from "@/components";
import { IconView } from "@/components/_Icon/IconComponent";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
@@ -15,17 +15,47 @@ import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { router } from "expo-router";
import { useState } from "react";
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
export default function AdminForumReportPosting() {
const [openDrawer, setOpenDrawer] = useState(false);
const [id, setId] = useState<any>();
const [listData, setListData] = useState<any[] | null>(null);
const [loadList, setLoadList] = useState<boolean>(false);
const [search, setSearch] = useState<string>("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [search])
);
const onLoadData = async () => {
try {
setLoadList(true);
const response = await apiAdminForum({
category: "report_posting",
search: search,
});
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
placeholder="Cari Postingan"
value={search}
onChangeText={setSearch}
/>
);
@@ -37,36 +67,49 @@ export default function AdminForumReportPosting() {
rightComponent={rightComponent}
/>
<BaseBox>
<StackCustom gap={"sm"}>
<AdminTitleTable title1="Aksi" title2="Pelapor" title3="Postingan" />
<Spacing />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<IconView size={ICON_SIZE_BUTTON} color={MainColor.black} />
}
onPress={() => {
router.push(`/admin/forum/${id}/list-report-posting`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2} align="center">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
Omnis laborum doloremque eius velit voluptate corrupti vel,
provident quaerat tempore animi sed accusamus amet.
Temporibus, praesentium? Rem voluptatum nesciunt voluptas
repellat.
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(listData) ? (
<TextCustom align="center" color="gray">
Belum ada data
</TextCustom>
) : (
listData?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<IconView
size={ICON_SIZE_BUTTON}
color={MainColor.black}
/>
}
onPress={() => {
router.push(
`/admin/forum/${item?.Forum_Posting?.id}/list-report-posting`
);
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.User?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.Forum_Posting?.diskusi || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AlertDefaultSystem,
BadgeCustom,
@@ -7,17 +8,43 @@ import {
Spacing,
StackCustom,
TextCustom,
ViewWrapper
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import ReportBox from "@/components/Box/ReportBox";
import { MainColor } from "@/constants/color-palet";
import { router, useLocalSearchParams } from "expo-router";
import funUpdateStatusJob from "@/screens/Admin/Job/funUpdateStatus";
import { apiAdminJobGetById } from "@/service/api-admin/api-admin-job";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminJobDetailStatus() {
const { id, status } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminJobGetById({
id: id as string,
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const colorBadge = () => {
if (status === "publish") {
@@ -32,11 +59,11 @@ export default function AdminJobDetailStatus() {
const listData = [
{
label: "Username",
value: "Bagas Banuna",
value: data?.Author?.username || "-",
},
{
label: "Judul",
value: `Judul Proyek: ${id}Lorem ipsum dolor sit amet consectetur adipisicing elit.`,
value: data?.title || "-",
},
{
label: "Status",
@@ -48,24 +75,43 @@ export default function AdminJobDetailStatus() {
},
{
label: "Konten",
value: "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
value: data?.content || "-",
},
{
label: "Deskripsi",
value: "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
value: data?.deskripsi || "-",
},
// {
// label: "Poster",
// value: (
// <ButtonCustom
// href={`/(application)/()/${id}`}
// >
// Lihat Poster
// </ButtonCustom>
// ),
// },
];
const handleUpdate = async ({
changeStatus,
}: {
changeStatus: "publish" | "review" | "reject";
}) => {
try {
const response = await funUpdateStatusJob({
id: id as string,
changeStatus,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal mempublikasikan data",
});
}
Toast.show({
type: "success",
text1: "Berhasil mempublikasikan data",
});
router.back();
} catch (error) {
console.log("[ERROR]", error);
}
};
return (
<>
<ViewWrapper
@@ -73,8 +119,8 @@ export default function AdminJobDetailStatus() {
>
<BaseBox>
<StackCustom>
{listData.map((item, i) => (
<Grid key={i}>
{listData?.map((item, index) => (
<Grid key={index}>
<Grid.Col
span={4}
style={{ justifyContent: "center", paddingRight: 10 }}
@@ -87,12 +133,19 @@ export default function AdminJobDetailStatus() {
</Grid>
))}
<TextCustom bold>Poster</TextCustom>
<DummyLandscapeImage />
{data && data?.imageId && (
<StackCustom>
<TextCustom bold>Poster</TextCustom>
<DummyLandscapeImage imageId={data?.imageId} />
</StackCustom>
)}
</StackCustom>
</BaseBox>
{data && data?.catatan && (status === "reject" || status === "review") && (
<ReportBox text={data?.catatan}/>
)}
{status === "review" && (
<AdminButtonReview
onPublish={() => {
@@ -101,24 +154,22 @@ export default function AdminJobDetailStatus() {
message: "Apakah anda yakin ingin mempublikasikan data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressLeft: () => {
router.back();
},
onPressRight: () => {
router.back();
handleUpdate({ changeStatus: "publish" });
},
});
}}
onReject={() => {
router.push(`/admin/job/${id}/reject-input`);
router.push(`/admin/job/${id}/${status}/reject-input`);
}}
/>
)}
{status === "reject" && (
<AdminButtonReject
title="Tambah Catatan"
onReject={() => {
router.push(`/admin/job/${id}/reject-input`);
router.push(`/admin/job/${id}/${status}/reject-input`);
}}
/>
)}

View File

@@ -0,0 +1,115 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AlertDefaultSystem,
BoxButtonOnFooter,
TextAreaCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import funUpdateStatusJob from "@/screens/Admin/Job/funUpdateStatus";
import { apiAdminJobGetById } from "@/service/api-admin/api-admin-job";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminJobRejectInput() {
const { id, status } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [isLoading, setIsLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminJobGetById({
id: id as string,
});
if (response.success) {
setData(response.data.catatan);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const handleUpdate = async ({
changeStatus,
}: {
changeStatus: "publish" | "review" | "reject";
}) => {
try {
setIsLoading(true);
const response = await funUpdateStatusJob({
id: id as string,
changeStatus,
data: data,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Report gagal",
});
}
Toast.show({
type: "success",
text1: "Report berhasil",
});
if (status === "review") {
router.replace(`/admin/job/reject/status`);
} else if (status === "reject") {
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<AdminButtonReject
isLoading={isLoading}
title="Report"
onReject={() =>
AlertDefaultSystem({
title: "Reject",
message: "Apakah anda yakin ingin menolak data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressRight: () => {
handleUpdate({ changeStatus: "reject" });
},
})
}
/>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper
footerComponent={buttonSubmit}
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
>
<TextAreaCustom
value={data}
onChangeText={setData}
placeholder="Masukan alasan"
required
showCount
maxLength={1000}
/>
</ViewWrapper>
</>
);
}

View File

@@ -1,8 +1,9 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
LoaderCustom,
SearchInput,
Spacing,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
@@ -11,17 +12,48 @@ import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminJob } from "@/service/api-admin/api-admin-job";
import { Octicons } from "@expo/vector-icons";
import { router, useLocalSearchParams } from "expo-router";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminJobStatus() {
const { status } = useLocalSearchParams();
const [list, setList] = useState<any | null>(null);
const [loadList, setLoadList] = useState(false);
const [search, setSearch] = useState("");
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [status, search])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminJob({
category: status as "publish" | "review" | "reject",
search,
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
onChangeText={setSearch}
value={search}
/>
);
return (
@@ -32,44 +64,53 @@ export default function AdminJobStatus() {
rightComponent={rightComponent}
/>
<BaseBox>
<StackCustom>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Pekerjaan"
/>
<Spacing />
{/* <Spacing /> */}
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/job/${index}/${status}`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom align="center" color="gray">
Tidak ada data
</TextCustom>
) : (
list?.map((item: any, index: number) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/job/${item.id}/${status}`);
}}
/>
}
value2={
<TextCustom align="center" truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
}
value3={
<TextCustom truncate={2} align="center">
{item?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Spacing, StackCustom, ViewWrapper } from "@/components";
import {
IconPublish,
@@ -7,15 +8,45 @@ import {
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { apiAdminJob } from "@/service/api-admin/api-admin-job";
import { useFocusEffect } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminJob() {
const [data, setData] = useState<any | null>(null);
const [loadList, setLoadList] = useState(false);
useFocusEffect(
useCallback(() => {
handlerLoadList();
}, [])
);
const handlerLoadList = async () => {
try {
setLoadList(true);
const response = await apiAdminJob({
category: "dashboard",
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
return (
<>
<ViewWrapper>
<AdminTitlePage title="Job Vacancy" />
<Spacing />
<StackCustom gap={"xs"}>
{listData.map((item, i) => (
{listData(data).map((item: any, i: number) => (
<AdminComp_BoxDashboard key={i} item={item} />
))}
</StackCustom>
@@ -24,20 +55,20 @@ export default function AdminJob() {
);
}
const listData = [
const listData = (data: any) => [
{
label: "Publish",
value: 4,
value: (data && data?.publish) || 0,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Review",
value: 7,
value: (data && data?.review) || 0,
icon: <IconReview size={25} color={MainColor.orange} />,
},
{
label: "Reject",
value: 5,
value: (data && data?.reject) || 0,
icon: <IconReject size={25} color={MainColor.red} />,
},
];

View File

@@ -1,11 +1,224 @@
import { MapCustom, ViewWrapper } from "@/components";
import { ButtonCustom, DrawerCustom, 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 {
id: string | number;
latitude: number;
longitude: number;
name: string;
imageId?: string;
}
export default function AdminMaps() {
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 (
<>
<ViewWrapper>
<MapCustom height={"100%"} />
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
{/* <MapCustom height={"100%"} /> */}
<View style={{ flex: 1 }}>
{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>
<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>
</>
);
}

View File

@@ -0,0 +1,128 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BoxButtonOnFooter,
ButtonCustom,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
import {
apiAdminUserAccessGetById,
apiAdminUserAccessUpdateStatus,
} from "@/service/api-admin/api-admin-user-access";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function SuperAdminDetail() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [loadData, setLoadData] = useState(false);
const [isLoading, setLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
setLoadData(true);
const response = await apiAdminUserAccessGetById({ id: id as string });
setData(response.data);
} catch (error) {
console.log("[ERROR LOAD DATA]", error);
} finally {
setLoadData(false);
}
};
const handlerSubmit = async () => {
try {
setLoading(true);
const response = await apiAdminUserAccessUpdateStatus({
id: id as string,
role: data?.masterUserRoleId === "2" ? "user" : "admin",
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Update role gagal",
});
return;
}
Toast.show({
type: "success",
text1: "Update role berhasil ",
});
router.back();
} catch (error) {
console.log("[ERROR UPDATE STATUS]", error);
} finally {
setLoading(false);
}
};
return (
<>
<ViewWrapper
headerComponent={<AdminBackButtonAntTitle title={`Detail User`} />}
footerComponent={
data && (
<BoxButtonOnFooter>
<ButtonCustom
isLoading={isLoading}
backgroundColor={
data?.masterUserRoleId === "2" ? "red" : "green"
}
textColor={"white"}
onPress={handlerSubmit}
>
{data?.masterUserRoleId === "2"
? "Hapus akses admin"
: "Tambah sebagai admin"}
</ButtonCustom>
</BoxButtonOnFooter>
)
}
>
{loadData ? (
<LoaderCustom />
) : (
<StackCustom>
{listData(data && data)?.map((item: any, index: number) => (
<GridTwoView
key={index}
spanLeft={4}
spanRight={8}
leftIcon={<TextCustom bold>{item?.label}</TextCustom>}
rightIcon={<TextCustom>{item?.value}</TextCustom>}
/>
))}
</StackCustom>
)}
</ViewWrapper>
</>
);
}
const listData = (data: any) => [
{
label: "Username",
value: (data && data?.username) || "-",
},
{
label: "Role",
value: data && data?.masterUserRoleId === "2" ? "Admin" : "User",
},
{
label: "Nomor",
value: (data && `+${data?.nomor}`) || "-",
},
];

View File

@@ -0,0 +1,148 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BadgeCustom,
CenterCustom,
Divider,
SearchInput,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
import { apiAdminUserAccessGetAll } from "@/service/api-admin/api-admin-user-access";
import { Ionicons } from "@expo/vector-icons";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
export default function SuperAdmin_ListUser() {
const [listData, setListData] = useState<any[] | null>(null);
const [search, setSearch] = useState("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [search])
);
const onLoadData = async () => {
try {
const response = await apiAdminUserAccessGetAll({
search: search,
category: "all-role",
});
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR LOAD DATA]", error);
}
};
const rightComponent = () => {
return (
<>
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari Username"
onChangeText={(text) => setSearch(text)}
/>
</>
);
};
return (
<>
<ViewWrapper
headerComponent={
<AdminComp_BoxTitle
title={"Super Admin"}
rightComponent={rightComponent()}
/>
}
>
<GridViewCustomSpan
span1={2}
span2={5}
span3={5}
component1={
<TextCustom align="center" bold>
Aksi
</TextCustom>
}
component2={
<TextCustom align="center" bold>
Username
</TextCustom>
}
component3={
<TextCustom align="center" bold>
Role
</TextCustom>
}
/>
<Divider />
<StackCustom>
{_.isEmpty(listData) ? (
<TextCustom align="center" color="gray" size={"small"}>
Tidak ada data
</TextCustom>
) : (
listData?.map((item: any, index: number) => (
<GridViewCustomSpan
key={index}
span1={2}
span2={5}
span3={5}
component1={
<CenterCustom>
<Ionicons
onPress={() =>
router.push(`/admin/super-admin/${item?.id}`)
}
name="open"
size={ICON_SIZE_XLARGE}
color={MainColor.yellow}
/>
</CenterCustom>
// <ButtonCustom
// onPress={() =>
// router.push(`/admin/super-admin/${item?.id}`)
// }
// >
// Detail
// </ButtonCustom>
}
component2={
<TextCustom bold truncate>
{item?.username || "-"}
</TextCustom>
}
component3={
<CenterCustom>
{item?.masterUserRoleId === "2" ? (
<BadgeCustom color={AccentColor.blue}>Admin</BadgeCustom>
) : (
<BadgeCustom color={AccentColor.softblue}>
User
</BadgeCustom>
)}
</CenterCustom>
}
style3={{ alignItems: "center", justifyContent: "center" }}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -1,117 +0,0 @@
import {
ButtonCustom,
Divider,
Grid,
SearchInput,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_MEDIUM } from "@/constants/constans-value";
import { Ionicons } from "@expo/vector-icons";
export default function AdminUserAccess() {
const rightComponent = () => {
return (
<>
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari User"
/>
</>
);
};
return (
<>
<ViewWrapper
headerComponent={
<AdminComp_BoxTitle
title="User Access"
rightComponent={rightComponent()}
/>
}
>
<Grid>
<Grid.Col span={4} style={{ alignItems: "center" }}>
<TextCustom bold>Aksi</TextCustom>
</Grid.Col>
<Grid.Col span={4} style={{ alignItems: "center" }}>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
<Grid.Col span={4} style={{ alignItems: "center" }}>
<TextCustom bold>Nomor</TextCustom>
</Grid.Col>
</Grid>
<Divider />
<StackCustom>
{Array.from({ length: 10 }).map((_, index) => (
<Grid key={index}>
<Grid.Col
span={4}
style={{
alignItems: "center",
paddingLeft: 5,
paddingRight: 5,
}}
>
<TextCustom bold>
<ButtonCustom
iconLeft={
<Ionicons
name={
index % 2 === 0
? "checkmark-outline"
: "close-circle-outline"
}
size={ICON_SIZE_MEDIUM}
color="black"
/>
}
onPress={() => {}}
backgroundColor={
index % 2 === 0 ? MainColor.green : MainColor.red
}
>
<TextCustom size={"small"} color={"black"}>
{index % 2 === 0 ? "Berikan Akses" : "Hapus Akses"}
</TextCustom>
</ButtonCustom>
</TextCustom>
</Grid.Col>
<Grid.Col
span={4}
style={{
alignItems: "center",
justifyContent: "center",
paddingLeft: 5,
paddingRight: 5,
}}
>
<TextCustom bold truncate>
Useraname
</TextCustom>
</Grid.Col>
<Grid.Col
span={4}
style={{
alignItems: "center",
justifyContent: "center",
paddingLeft: 5,
paddingRight: 5,
}}
>
<TextCustom bold truncate>
08123456789
</TextCustom>
</Grid.Col>
</Grid>
))}
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -0,0 +1,124 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BoxButtonOnFooter,
ButtonCustom,
LoaderCustom,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
import {
apiAdminUserAccessGetById,
apiAdminUserAccessUpdateStatus,
} from "@/service/api-admin/api-admin-user-access";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminUserAccessDetail() {
const { id } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [loadData, setLoadData] = useState(false);
const [isLoading, setLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
setLoadData(true);
const response = await apiAdminUserAccessGetById({ id: id as string });
setData(response.data);
} catch (error) {
console.log("[ERROR LOAD DATA]", error);
} finally {
setLoadData(false);
}
};
const handlerSubmit = async () => {
try {
setLoading(true);
const response = await apiAdminUserAccessUpdateStatus({
id: id as string,
active: !data?.active,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Update aktifasi gagal",
});
return;
}
Toast.show({
type: "success",
text1: "Update aktifasi berhasil ",
});
router.back();
} catch (error) {
console.log("[ERROR UPDATE STATUS]", error);
} finally {
setLoading(false);
}
};
return (
<>
<ViewWrapper
headerComponent={<AdminBackButtonAntTitle title={`Detail User`} />}
footerComponent={
data && (
<BoxButtonOnFooter>
<ButtonCustom
isLoading={isLoading}
backgroundColor={data?.active ? "red" : "green"}
textColor={"white"}
onPress={handlerSubmit}
>
{data?.active ? "Hapus Akses" : "Berikan Akses"}
</ButtonCustom>
</BoxButtonOnFooter>
)
}
>
{loadData ? (
<LoaderCustom />
) : (
<StackCustom>
{listData(data && data)?.map((item: any, index: number) => (
<GridTwoView
key={index}
spanLeft={4}
spanRight={8}
leftIcon={<TextCustom bold>{item?.label}</TextCustom>}
rightIcon={<TextCustom>{item?.value}</TextCustom>}
/>
))}
</StackCustom>
)}
</ViewWrapper>
</>
);
}
const listData = (data: any) => [
{
label: "Username",
value: (data && data?.username) || "-",
},
{
label: "Aktivasi",
value: data && data?.active ? "Aktif" : "Tidak Aktif",
},
{
label: "Nomor",
value: (data && `+${data?.nomor}`) || "-",
},
];

View File

@@ -0,0 +1,143 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
BadgeCustom,
CenterCustom,
Divider,
SearchInput,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
import { apiAdminUserAccessGetAll } from "@/service/api-admin/api-admin-user-access";
import { Ionicons } from "@expo/vector-icons";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
export default function AdminUserAccess() {
const [listData, setListData] = useState<any[] | null>(null);
const [search, setSearch] = useState("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [search])
);
const onLoadData = async () => {
try {
const response = await apiAdminUserAccessGetAll({
search: search,
category: "only-user",
});
if (response.success) {
setListData(response.data);
}
} catch (error) {
console.log("[ERROR LOAD DATA]", error);
}
};
const rightComponent = () => {
return (
<>
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari User"
onChangeText={(text) => setSearch(text)}
/>
</>
);
};
return (
<>
<ViewWrapper
headerComponent={
<AdminComp_BoxTitle
title="User Access"
rightComponent={rightComponent()}
/>
}
>
<GridViewCustomSpan
span1={2}
span2={5}
span3={5}
component1={
<TextCustom align="center" bold>
Aksi
</TextCustom>
}
component2={
<TextCustom align="center" bold>
Username
</TextCustom>
}
component3={
<TextCustom align="center" bold>
Status Akses
</TextCustom>
}
/>
<Divider />
<StackCustom>
{_.isEmpty(listData) ? (
<TextCustom align="center" color="gray" size={"small"}>
Tidak ada data
</TextCustom>
) : (
listData?.map((item: any, index: number) => (
<GridViewCustomSpan
key={index}
span1={2}
span2={5}
span3={5}
component1={
<CenterCustom>
<Ionicons
onPress={() =>
router.push(`/admin/user-access/${item?.id}`)
}
name="open"
size={ICON_SIZE_XLARGE}
color={MainColor.yellow}
/>
</CenterCustom>
// <ButtonCustom
// onPress={() =>
// router.push(`/admin/user-access/${item?.id}`)
// }
// >
// Detail
// </ButtonCustom>
}
component2={
<TextCustom bold truncate>
{item?.username || "-"}
</TextCustom>
}
component3={
<CenterCustom>
{item?.active ? (
<BadgeCustom color="green">Aktif</BadgeCustom>
) : (
<BadgeCustom color="red">Tidak Aktif</BadgeCustom>
)}
</CenterCustom>
}
style3={{ alignItems: "center", justifyContent: "center" }}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -1,3 +1,4 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AlertDefaultSystem,
BadgeCustom,
@@ -13,90 +14,153 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
import { GridDetail_4_8 } from "@/components/_ShareComponent/GridDetail_4_8";
import ReportBox from "@/components/Box/ReportBox";
import { MainColor } from "@/constants/color-palet";
import funUpdateStatusVoting from "@/screens/Admin/Voting/funUpdateStatus";
import { apiAdminVotingById } from "@/service/api-admin/api-admin-voting";
import { colorBadge } from "@/utils/colorBadge";
import { dateTimeView } from "@/utils/dateTimeView";
import { Entypo } from "@expo/vector-icons";
import dayjs from "dayjs";
import { router, useLocalSearchParams } from "expo-router";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { List } from "react-native-paper";
import Toast from "react-native-toast-message";
export default function AdminVotingDetail() {
const { id, status } = useLocalSearchParams();
const [data, setData] = useState<any | null>(null);
const [isLoading, setIsLoading] = useState(false);
const colorBadge = () => {
if (status === "publish") {
return MainColor.green;
} else if (status === "review") {
return MainColor.orange;
} else if (status === "reject") {
return MainColor.red;
} else {
return MainColor.placeholder;
console.log("[status]", status);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminVotingById({
id: id as string,
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const listData = [
{
label: "Username",
value: "Bagas Banuna",
value: (data && data?.Author?.username) || "-",
},
{
label: "Judul",
value: `Judul Proyek: ${id}Lorem ipsum dolor sit amet consectetur adipisicing elit.`,
value: (data && data?.title) || "-",
},
{
label: "Status",
value: (
<BadgeCustom color={colorBadge()}>
{_.startCase(status as string)}
</BadgeCustom>
),
value:
data && data?.Voting_Status?.name ? (
<BadgeCustom color={colorBadge({ status: status as string })}>
{status === "history" ? "Riwayat" : _.startCase(status as string)}
</BadgeCustom>
) : (
"-"
),
},
{
label: "Mulai Voting",
value: dayjs().format("DD/MM/YYYY"),
value:
(data && data?.awalVote && dateTimeView({ date: data?.awalVote })) ||
"-",
},
{
label: "Voting Berakhir",
value: dayjs().format("DD/MM/YYYY"),
value:
(data && data?.akhirVote && dateTimeView({ date: data?.akhirVote })) ||
"-",
},
{
label: "Deskripsi",
value: "Lorem ipsum dolor sit amet consectetur adipisicing elit.",
value: (data && data?.deskripsi) || "-",
},
{
label: "Daftar Pilhan",
value: (
<>
<List.Item
title={<TextCustom>Pilihan 1</TextCustom>}
left={(props) => (
<List.Icon {...props} icon="circle" color={MainColor.yellow} />
)}
/>
<List.Item
title={<TextCustom>Pilihan 2</TextCustom>}
left={(props) => (
<List.Icon {...props} icon="circle" color={MainColor.yellow} />
)}
/>
<List.Item
title={<TextCustom>Pilihan 3</TextCustom>}
left={(props) => (
<List.Icon {...props} icon="circle" color={MainColor.yellow} />
)}
/>
<List.Item
title={<TextCustom>Pilihan 4</TextCustom>}
left={(props) => (
<List.Icon {...props} icon="circle" color={MainColor.yellow} />
)}
/>
</>
),
label: "Daftar Pilihan",
value:
data && data?.Voting_DaftarNamaVote
? data?.Voting_DaftarNamaVote?.map((item: any, i: number) => (
<List.Item
key={i}
title={<TextCustom>{item?.value}</TextCustom>}
left={(props) => (
<Entypo name="chevron-right" color={MainColor.yellow} />
)}
/>
))
: "-",
},
];
const handleUpdate = async ({
changeStatus,
}: {
changeStatus: "publish" | "review" | "reject";
}) => {
try {
const dateNow = new Date();
// const dateNowHour = dateNow.getHours();
// const awalVoteHour = dayjs(data?.awalVote).hour();
const isBefore = dayjs(dateNow).diff(dayjs(data?.awalVote), "hours") < 0;
console.log("[IS BEFORE]", isBefore);
if (!isBefore) {
Toast.show({
type: "error",
text1: "Tanggal & waktu telah lewat",
text2: "Silahkan report dan ubah tanggal & waktu voting",
});
return;
}
Toast.show({
type: "success",
text1: "Berhasil mempublikasikan data",
});
setIsLoading(true);
const response = await funUpdateStatusVoting({
id: id as string,
changeStatus,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Gagal mempublikasikan data",
});
}
Toast.show({
type: "success",
text1: "Berhasil mempublikasikan data",
});
router.back();
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoading(false);
}
};
return (
<>
<ViewWrapper
@@ -114,46 +178,59 @@ export default function AdminVotingDetail() {
</StackCustom>
</BaseBox>
{status === "publish" && (
<BaseBox>
<TextCustom bold align="center">
Hasil Voting
</TextCustom>
<Spacing />
<Grid>
{Array.from({length: 4}).map((_, index) => (
<Grid.Col key={index} span={3} style={{ paddingRight: 3, paddingLeft: 3 }}>
<StackCustom gap={"sm"}>
<CircleContainer value={index % 3 * 3} style={{ alignSelf: "center" }} />
<TextCustom size="small" align="center">
Pilihan {index + 1}
</TextCustom>
</StackCustom>
</Grid.Col>
))}
</Grid>
</BaseBox>
)}
{status === "publish" ||
(status === "history" && (
<BaseBox>
<TextCustom bold align="center">
Hasil Voting
</TextCustom>
<Spacing />
<Grid>
{data?.Voting_DaftarNamaVote?.map(
(item: any, index: number) => (
<Grid.Col
key={index}
span={12 / data?.Voting_DaftarNamaVote?.length}
style={{ paddingRight: 3, paddingLeft: 3 }}
>
<StackCustom gap={"sm"}>
<CircleContainer
value={item?.jumlah}
style={{ alignSelf: "center" }}
/>
<TextCustom size="small" align="center">
{item?.value}
</TextCustom>
</StackCustom>
</Grid.Col>
)
)}
</Grid>
</BaseBox>
))}
{data &&
data?.catatan &&
(status === "review" || status === "reject") && (
<ReportBox text={data?.catatan} />
)}
{status === "review" && (
<AdminButtonReview
isLoading={isLoading}
onPublish={() => {
AlertDefaultSystem({
title: "Publish",
message: "Apakah anda yakin ingin mempublikasikan data ini?",
textLeft: "Cancel",
textRight: "Publish",
onPressLeft: () => {
router.back();
},
onPressRight: () => {
router.back();
handleUpdate({ changeStatus: "publish" });
},
});
}}
onReject={() => {
router.push(`/admin/voting/${id}/reject-input`);
router.push(`/admin/voting/${id}/${status}/reject-input`);
}}
/>
)}
@@ -162,7 +239,7 @@ export default function AdminVotingDetail() {
<AdminButtonReject
title="Tambah Catatan"
onReject={() => {
router.push(`/admin/voting/${id}/reject-input`);
router.push(`/admin/voting/${id}/${status}/reject-input`);
}}
/>
)}

View File

@@ -0,0 +1,113 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
AlertDefaultSystem,
BoxButtonOnFooter,
TextAreaCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import funUpdateStatusVoting from "@/screens/Admin/Voting/funUpdateStatus";
import { apiAdminVotingById } from "@/service/api-admin/api-admin-voting";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useState } from "react";
import Toast from "react-native-toast-message";
export default function AdminVotingRejectInput() {
const { id, status } = useLocalSearchParams();
const [data, setData] = useState("");
const [isLoading, setIsLoading] = useState(false);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [id])
);
const onLoadData = async () => {
try {
const response = await apiAdminVotingById({
id: id as string,
});
if (response.success) {
setData(response.data.catatan);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const handleUpdate = async ({
changeStatus,
}: {
changeStatus: "publish" | "review" | "reject";
}) => {
try {
setIsLoading(true);
const response = await funUpdateStatusVoting({
id: id as string,
changeStatus,
data: data,
});
if (!response.success) {
Toast.show({
type: "error",
text1: "Report gagal",
});
}
Toast.show({
type: "success",
text1: "Report berhasil",
});
if (status === "review") {
router.replace(`/admin/voting/reject/status`);
} else if (status === "reject") {
router.back();
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setIsLoading(false);
}
};
const buttonSubmit = (
<BoxButtonOnFooter>
<AdminButtonReject
title="Reject"
isLoading={isLoading}
onReject={() =>
AlertDefaultSystem({
title: "Reject",
message: "Apakah anda yakin ingin menolak data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressRight: () => handleUpdate({ changeStatus: "reject" }),
})
}
/>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper
footerComponent={buttonSubmit}
headerComponent={<AdminBackButtonAntTitle title="Penolakan Voting" />}
>
<TextAreaCustom
value={data}
onChangeText={setData}
placeholder="Masukan alasan"
required
showCount
maxLength={1000}
/>
</ViewWrapper>
</>
);
}

View File

@@ -1,55 +0,0 @@
import {
AlertDefaultSystem,
BoxButtonOnFooter,
TextAreaCustom,
ViewWrapper,
} from "@/components";
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
export default function AdminVotingRejectInput() {
const { id } = useLocalSearchParams();
const [value, setValue] = useState(id as string);
const buttonSubmit = (
<BoxButtonOnFooter>
<AdminButtonReject
title="Reject"
onReject={() =>
AlertDefaultSystem({
title: "Reject",
message: "Apakah anda yakin ingin menolak data ini?",
textLeft: "Batal",
textRight: "Ya",
onPressLeft: () => {
router.back();
},
onPressRight: () => {
console.log("value:", value);
router.replace(`/admin/voting/reject/status`);
},
})
}
/>
</BoxButtonOnFooter>
);
return (
<>
<ViewWrapper
footerComponent={buttonSubmit}
headerComponent={<AdminBackButtonAntTitle title="Penolakan Voting" />}
>
<TextAreaCustom
value={value}
onChangeText={setValue}
placeholder="Masukan alasan"
required
showCount
maxLength={1000}
/>
</ViewWrapper>
</>
);
}

View File

@@ -1,27 +1,60 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
BaseBox,
SearchInput,
Spacing,
TextCustom,
ViewWrapper,
ActionIcon,
LoaderCustom,
SearchInput,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
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 { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
import { Octicons } from "@expo/vector-icons";
import { router, useLocalSearchParams } from "expo-router";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminVotingStatus() {
const { status } = useLocalSearchParams();
const [list, setList] = useState<any | null>(null);
const [loadList, setLoadList] = useState(false);
const [search, setSearch] = useState<string>("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [status, search])
);
const onLoadData = async () => {
try {
setLoadList(true);
const response = await apiAdminVoting({
category: status as "publish" | "review" | "reject" as any,
search,
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
value={search}
onChangeText={setSearch}
/>
);
return (
@@ -32,44 +65,52 @@ export default function AdminVotingStatus() {
rightComponent={rightComponent}
/>
<BaseBox>
<StackCustom gap={"sm"}>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Voting"
/>
<Spacing />
<Divider />
{Array.from({ length: 10 }).map((_, index) => (
<AdminTableValue
key={index}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/voting/${index}/${status}`);
}}
/>
}
value2={<TextCustom truncate={1}>Username username</TextCustom>}
value3={
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Blanditiis asperiores quidem deleniti architecto eaque et
nostrum, ad consequuntur eveniet quisquam quae voluptatum
ducimus! Dolorem nobis modi officia debitis, beatae mollitia.
</TextCustom>
}
/>
))}
</BaseBox>
{loadList ? (
<LoaderCustom />
) : _.isEmpty(list) ? (
<TextCustom align="center" bold color="gray">
Belum ada data
</TextCustom>
) : (
list.map((item: any, i: number) => (
<AdminTableValue
key={i}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/voting/${item.id}/${status}`);
}}
/>
}
value2={
<TextCustom truncate={1}>
{item?.Author?.username || "-"}
</TextCustom>
}
value3={
<TextCustom align="center" truncate={2}>
{item?.title || "-"}
</TextCustom>
}
/>
))
)}
</StackCustom>
</ViewWrapper>
</>
);

View File

@@ -0,0 +1,104 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
ActionIcon,
LoaderCustom,
SearchInput,
StackCustom,
TextCustom,
ViewWrapper
} from "@/components";
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 { ICON_SIZE_BUTTON } from "@/constants/constans-value";
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
import { Octicons } from "@expo/vector-icons";
import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useState } from "react";
import { Divider } from "react-native-paper";
export default function AdminVotingHistory() {
const [list, setList] = useState<any | null>(null);
const [loadList, setLoadList] = useState(false);
const [search, setSearch] = useState<string>("");
useFocusEffect(
useCallback(() => {
onLoadData();
}, [ search])
);
const onLoadData = async () => {
try {
setLoadList(true);
const response = await apiAdminVoting({
category: "history",
search,
});
if (response.success) {
setList(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
} finally {
setLoadList(false);
}
};
const rightComponent = (
<SearchInput
containerStyle={{ width: "100%", marginBottom: 0 }}
placeholder="Cari"
value={search}
onChangeText={setSearch}
/>
);
return (
<>
<ViewWrapper headerComponent={<AdminTitlePage title="Voting" />}>
<AdminComp_BoxTitle
title="Riwayat"
rightComponent={rightComponent}
/>
<StackCustom gap={"sm"}>
<AdminTitleTable
title1="Aksi"
title2="Username"
title3="Judul Voting"
/>
<Divider />
{loadList ? <LoaderCustom/> : _.isEmpty(list) ? <TextCustom align="center" bold color="gray">Belum ada data</TextCustom> : list.map((item: any, i: number) => (
<AdminTableValue
key={i}
value1={
<ActionIcon
icon={
<Octicons
name="eye"
size={ICON_SIZE_BUTTON}
color="black"
/>
}
onPress={() => {
router.push(`/admin/voting/${item.id}/history`);
}}
/>
}
value2={<TextCustom truncate={1}>{item?.Author?.username || "-"}</TextCustom>}
value3={
<TextCustom align="center" truncate={2}>
{item?.title || "-"}
</TextCustom>
}
/>
))}
</StackCustom>
</ViewWrapper>
</>
);
}

View File

@@ -8,8 +8,56 @@ import {
import AdminComp_BoxDashboard from "@/components/_ShareComponent/Admin/BoxDashboard";
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
import { MainColor } from "@/constants/color-palet";
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
import { useFocusEffect } from "expo-router";
import { useCallback, useState } from "react";
export default function AdminVoting() {
const [data, setData] = useState<any | null>(null);
useFocusEffect(
useCallback(() => {
onLoadData();
}, [])
);
const onLoadData = async () => {
try {
const response = await apiAdminVoting({
category: "dashboard",
});
if (response.success) {
setData(response.data);
}
} catch (error) {
console.log("[ERROR]", error);
}
};
const listData = [
{
label: "Publish",
value: (data && data?.publish) || 0,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Review",
value: (data && data?.review) || 0,
icon: <IconReview size={25} color={MainColor.orange} />,
},
{
label: "Reject",
value: (data && data?.reject) || 0,
icon: <IconReject size={25} color={MainColor.red} />,
},
{
label: "Riwayat",
value: (data && data?.history) || 0,
icon: <IconArchive size={25} color={MainColor.white_gray} />,
},
];
return (
<>
<ViewWrapper>
@@ -24,26 +72,3 @@ export default function AdminVoting() {
</>
);
}
const listData = [
{
label: "Publish",
value: 4,
icon: <IconPublish size={25} color={MainColor.green} />,
},
{
label: "Review",
value: 7,
icon: <IconReview size={25} color={MainColor.orange} />,
},
{
label: "Reject",
value: 5,
icon: <IconReject size={25} color={MainColor.red} />,
},
{
label: "Riwayat",
value: 5,
icon: <IconArchive size={25} color={MainColor.white_gray} />,
},
];

View File

@@ -17,7 +17,6 @@
"axios": "^1.11.0",
"dayjs": "^1.11.13",
"expo": "^54.0.0",
"expo-blur": "~15.0.7",
"expo-camera": "~17.0.7",
"expo-clipboard": "~8.0.7",
"expo-constants": "~18.0.8",
@@ -1153,8 +1152,6 @@
"expo-asset": ["expo-asset@12.0.8", "", { "dependencies": { "@expo/image-utils": "^0.8.7", "expo-constants": "~18.0.8" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-jj2U8zw9+7orST2rlQGULYiqPoECOuUyffs2NguGrq84bYbkM041T7TOMXH2raPVJnM9lEAP54ezI6XL+GVYqw=="],
"expo-blur": ["expo-blur@15.0.7", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-SugQQbQd+zRPy8z2G5qDD4NqhcD7srBF7fN7O7yq6q7ZFK59VWvpDxtMoUkmSfdxgqONsrBN/rLdk00USADrMg=="],
"expo-camera": ["expo-camera@17.0.7", "", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-jdZfijfFjlVAuuIkDheA41YKpigPjqsN0juRvgyr7Lcyz+fvwZ3/RP50/n/hvuozH657wHxPfiyVVFa00z8TcQ=="],
"expo-clipboard": ["expo-clipboard@8.0.7", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-zvlfFV+wB2QQrQnHWlo0EKHAkdi2tycLtE+EXFUWTPZYkgu1XcH+aiKfd4ul7Z0SDF+1IuwoiW9AA9eO35aj3Q=="],

View File

@@ -1,6 +1,6 @@
import { MainColor } from "@/constants/color-palet";
import { Href, router } from "expo-router";
import { DimensionValue, TouchableOpacity } from "react-native";
import { DimensionValue, StyleProp, TouchableOpacity, ViewStyle } from "react-native";
type SizeType = "xs" | "sm" | "md" | "lg" | "xl" | number | string | undefined;
@@ -10,12 +10,14 @@ export default function ActionIcon({
icon,
size = "md",
disabled = false,
style,
}: {
href?: Href;
onPress?: () => void;
icon: React.ReactNode;
size?: SizeType;
disabled?: boolean;
style?: StyleProp<ViewStyle>;
}) {
const sizeMap = {
xs: 22,
@@ -39,7 +41,7 @@ export default function ActionIcon({
<TouchableOpacity
disabled={disabled}
activeOpacity={0.7}
style={{
style={[{
backgroundColor: disabled ? MainColor.disabled : MainColor.yellow,
padding: 5,
borderRadius: 50,
@@ -47,7 +49,7 @@ export default function ActionIcon({
justifyContent: "center",
width: iconSize,
height: iconSize,
}}
}, style]}
onPress={() => {
if (disabled) return;
if (href) {

View File

@@ -0,0 +1,16 @@
import StackCustom from "../Stack/StackCustom";
import TextCustom from "../Text/TextCustom";
import BaseBox from "./BaseBox";
export default function ReportBox({ text }: { text: string }) {
return (
<BaseBox>
<StackCustom gap={"sm"}>
<TextCustom bold>
Catatan penolakan
</TextCustom>
<TextCustom>{text}</TextCustom>
</StackCustom>
</BaseBox>
);
}

View File

@@ -1,27 +1,39 @@
import { MainColor } from "@/constants/color-palet";
import React from "react";
import { StyleProp, StyleSheet, TextInput, View, ViewStyle } from "react-native";
import {
StyleProp,
StyleSheet,
View,
ViewStyle
} from "react-native";
import TextCustom from "../Text/TextCustom";
interface CircularInputProps {
value?: string | number
value?: string | number;
onChange?: (value: string | number) => void;
icon?: React.ReactNode;
style?: StyleProp<ViewStyle>
style?: StyleProp<ViewStyle>;
}
const CircularInput: React.FC<CircularInputProps> = ({ value, onChange, icon, style }) => {
const CircularInput: React.FC<CircularInputProps> = ({
value,
onChange,
icon,
style,
}) => {
return (
<View style={[styles.circleContainer, style]}>
{icon ? (
icon
) : (
<TextInput
value={String(value)}
onChangeText={onChange}
<TextCustom
// text={String(value)}
style={styles.input}
keyboardType="numeric"
maxLength={2} // Batasan maksimal karakter
/>
// keyboardType="numeric"
// maxLength={2} // Batasan maksimal karakter
>
{value}
</TextCustom>
)}
</View>
);
@@ -39,7 +51,7 @@ const styles = StyleSheet.create({
},
input: {
color: MainColor.yellow, // Warna kuning
fontSize: 24,
fontSize: 18,
fontWeight: "bold",
textAlign: "center",
padding: 0,

View File

@@ -1,5 +1,7 @@
// components/MapComponent.js
import API_IMAGE from "@/constants/api-storage";
import { Image } from "expo-image";
import React from "react";
import { DimensionValue, StyleSheet, View } from "react-native";
import MapView, { Marker } from "react-native-maps";
@@ -10,6 +12,9 @@ interface MapComponentProps {
latitudeDelta?: number;
longitudeDelta?: number;
height?: DimensionValue;
namePin?: string;
imageId?: string;
onPress?: () => void;
}
const MapCustom = ({
@@ -18,6 +23,9 @@ const MapCustom = ({
latitudeDelta = 0.1,
longitudeDelta = 0.1,
height = 300,
namePin = "Bali",
imageId,
onPress,
}: MapComponentProps) => {
const initialRegion = {
latitude,
@@ -40,9 +48,22 @@ const MapCustom = ({
latitude,
longitude,
}}
title="Bali"
description="Badung, Bali, Indonesia"
/>
title={namePin}
onPress={onPress}
// Gunakan gambar kustom jika tersedia
>
<View style={{}}>
<Image
source={{ uri: API_IMAGE.GET({ fileId: imageId as string }) }}
style={{
width: 30,
height: 30,
borderRadius: 100,
borderWidth: 1,
}}
/>
</View>
</Marker>
</MapView>
</View>
);

View File

@@ -0,0 +1,79 @@
import React from "react";
import { StyleSheet, View } from "react-native";
import MapView, { LatLng, Marker } from "react-native-maps";
interface MapSelectedProps {
initialRegion?: {
latitude?: number;
longitude?: number;
latitudeDelta?: number;
longitudeDelta?: number;
};
onLocationSelect?: (location: LatLng) => void;
height?: number; // Opsional: tinggi peta dalam piksel
selectedLocation: LatLng;
setSelectedLocation: (location: LatLng) => void;
}
const MapSelected: React.FC<MapSelectedProps> = ({
initialRegion,
onLocationSelect,
selectedLocation,
setSelectedLocation,
height = 400, // Default height: 400
}) => {
const handleMapPress = (event: any) => {
const { latitude, longitude } = event.nativeEvent.coordinate;
const location = { latitude, longitude };
setSelectedLocation(location);
onLocationSelect?.(location);
};
// Default region sesuai permintaan
const defaultRegion = {
latitude: -8.737109,
longitude: 115.1756897,
latitudeDelta: 0.1,
longitudeDelta: 0.1,
};
return (
<View style={[styles.container, { height }]}>
<MapView
style={styles.map}
initialRegion={(initialRegion as any) || defaultRegion}
onPress={handleMapPress}
showsUserLocation={true}
showsMyLocationButton={true}
loadingEnabled={true}
loadingIndicatorColor="#666"
loadingBackgroundColor="#f0f0f0"
>
{selectedLocation && (
<Marker
coordinate={selectedLocation}
title="Lokasi Dipilih"
description={`Lat: ${selectedLocation.latitude.toFixed(
6
)}, Lng: ${selectedLocation.longitude.toFixed(6)}`}
pinColor="red"
/>
)}
</MapView>
</View>
);
};
const styles = StyleSheet.create({
container: {
width: "100%",
backgroundColor: "#f5f5f5",
overflow: "hidden",
borderRadius: 8,
},
map: {
flex: 1,
},
});
export default MapSelected;

View File

@@ -23,7 +23,7 @@ interface TextCustomProps {
bold?: boolean;
semiBold?: boolean;
size?: "default" | "large" | "small" | "xlarge" | number
color?: "default" | "yellow" | "red" | "gray" | "green" | "black"
color?: "default" | "yellow" | "red" | "gray" | "green" | "black" | "orange"
align?: TextAlign; // Prop untuk alignment
truncate?: boolean | number;
onPress?: () => void;
@@ -62,6 +62,7 @@ const TextCustom: React.FC<TextCustomProps> = ({
else if (color === "gray") selectedStyles.push(styles.gray);
else if (color === "green") selectedStyles.push(styles.green);
else if (color === "black") selectedStyles.push(styles.black);
else if (color === "orange") selectedStyles.push(styles.orange);
// Alignment
if (align) {
@@ -140,4 +141,7 @@ export const styles = StyleSheet.create({
black: {
color: MainColor.black,
},
orange: {
color: MainColor.orange,
},
});

View File

@@ -0,0 +1,27 @@
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
import { Ionicons } from "@expo/vector-icons";
import React from "react";
export { IconOpenTo };
function IconOpenTo({
color,
size,
onPress,
}: {
color?: string;
size?: number;
onPress?: () => void;
}) {
return (
<>
<Ionicons
onPress={onPress}
name="open"
size={size || ICON_SIZE_XLARGE}
color={color || MainColor.yellow}
/>
</>
);
}

View File

@@ -5,17 +5,20 @@ import { MainColor } from "@/constants/color-palet";
export default function AdminButtonReject({
title,
onReject,
isLoading,
}: {
title: string;
onReject: () => void;
isLoading?: boolean;
}) {
return (
<>
<ButtonCustom
iconLeft={<IconReject />}
iconLeft={<IconReject size={16} />}
backgroundColor={MainColor.red}
textColor="white"
onPress={onReject}
isLoading={isLoading}
>
{title}
</ButtonCustom>

View File

@@ -4,9 +4,11 @@ import Grid from "@/components/Grid/GridCustom";
import { MainColor } from "@/constants/color-palet";
export default function AdminButtonReview({
isLoading,
onPublish,
onReject,
}: {
isLoading?: boolean;
onPublish: () => void;
onReject: () => void;
}) {
@@ -15,6 +17,7 @@ export default function AdminButtonReview({
<Grid>
<Grid.Col span={6} style={{ paddingRight: 10 }}>
<ButtonCustom
isLoading={isLoading}
iconLeft={<IconPublish />}
backgroundColor={MainColor.green}
textColor="white"

View File

@@ -13,14 +13,32 @@ export default function AdminTitleTable({
return (
<>
<Grid>
<Grid.Col span={3} style={{ alignItems: "center", justifyContent: "center" }}>
<TextCustom bold align="center">{title1}</TextCustom>
<Grid.Col
span={3}
style={{
alignItems: "center",
justifyContent: "center",
paddingLeft: 5,
paddingRight: 5,
}}
>
<TextCustom truncate bold align="center">
{title1}
</TextCustom>
</Grid.Col>
<Grid.Col span={3} style={{ alignItems: "center", justifyContent: "center" }}>
<TextCustom bold align="center">{title2}</TextCustom>
<Grid.Col
span={3}
style={{ alignItems: "center", justifyContent: "center", paddingLeft: 5, paddingRight: 5 }}
>
<TextCustom truncate bold align="center">
{title2}
</TextCustom>
</Grid.Col>
<Grid.Col span={6} style={{ alignItems: "center", justifyContent: "center" }}>
<TextCustom bold align="center">{title3}</TextCustom>
<Grid.Col
span={6}
style={{ alignItems: "center", justifyContent: "center", paddingLeft: 5, paddingRight: 5 }}
>
<TextCustom truncate bold align="center">{title3}</TextCustom>
</Grid.Col>
</Grid>
</>

View File

@@ -7,10 +7,12 @@ export default function AdminTableValue({
value1,
value2,
value3,
bottomLine = false,
}: {
value1: React.ReactNode;
value2: React.ReactNode;
value3: React.ReactNode;
bottomLine?: boolean;
}) {
return (
<>
@@ -21,8 +23,8 @@ export default function AdminTableValue({
style={{
alignItems: "center",
justifyContent: "center",
paddingLeft: 5,
paddingRight: 5,
paddingLeft: 10,
paddingRight: 10,
}}
>
{value1}
@@ -32,8 +34,8 @@ export default function AdminTableValue({
style={{
alignItems: "center",
justifyContent: "center",
paddingLeft: 5,
paddingRight: 5,
paddingLeft: 10,
paddingRight: 10,
}}
>
{value2}
@@ -42,14 +44,15 @@ export default function AdminTableValue({
span={6}
style={{
justifyContent: "center",
paddingLeft: 5,
paddingRight: 5,
alignItems: "center",
paddingLeft: 10,
paddingRight: 10,
}}
>
{value3}
</Grid.Col>
</Grid>
<Divider />
{bottomLine && <Divider />}
</View>
</>
);

View File

@@ -0,0 +1,37 @@
import { ViewStyle } from "react-native";
import Grid from "../Grid/GridCustom";
export default function GridTwoView({
spanLeft = 6,
spanRight = 6,
leftIcon,
rightIcon,
styleLeft,
styleRight,
}: {
spanLeft?: number;
spanRight?: number;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
styleLeft?: ViewStyle;
styleRight?: ViewStyle;
}) {
const baseStyle: ViewStyle = { justifyContent: "center" };
return (
<Grid containerStyle={{ marginBottom: 0 }}>
<Grid.Col
span={spanLeft}
style={styleLeft ? { ...baseStyle, ...styleLeft } : baseStyle}
>
{leftIcon}
</Grid.Col>
<Grid.Col
span={spanRight}
style={styleRight ? { ...baseStyle, ...styleRight } : baseStyle}
>
{rightIcon}
</Grid.Col>
</Grid>
);
}

View File

@@ -1,4 +1,5 @@
import { Grid } from "@/components";
import { StyleProp, ViewStyle } from "react-native";
export const GridViewCustomSpan = ({
span1,
@@ -7,31 +8,52 @@ export const GridViewCustomSpan = ({
component1,
component2,
component3,
style1,
style2,
style3,
}: {
span1: number;
span2: number;
span3: number;
span1?: number;
span2?: number;
span3?: number;
component1: React.ReactNode;
component2: React.ReactNode;
component3: React.ReactNode;
style1?: StyleProp<ViewStyle>;
style2?: StyleProp<ViewStyle>;
style3?: StyleProp<ViewStyle>;
}) => {
return (
<Grid>
<Grid.Col
span={span1 || 4}
style={{ justifyContent: "center", paddingRight: 5, paddingLeft: 5 }}
style={{
justifyContent: "center",
paddingRight: 10,
paddingLeft: 10,
...(style1 as object)
} as ViewStyle}
>
{component1}
</Grid.Col>
<Grid.Col
span={span2 || 4}
style={{ justifyContent: "center", paddingRight: 5, paddingLeft: 5 }}
style={{
justifyContent: "center",
paddingRight: 10,
paddingLeft: 10,
...(style2 as object)
} as ViewStyle}
>
{component2}
</Grid.Col>
<Grid.Col
span={span3 || 4}
style={{ justifyContent: "center", paddingRight: 5, paddingLeft: 5 }}
style={{
justifyContent: "center",
paddingRight: 10,
paddingLeft: 10,
...(style3 as object)
} as ViewStyle}
>
{component3}
</Grid.Col>

View File

@@ -2,7 +2,7 @@ import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import TextInputCustom from "../TextInput/TextInputCustom";
import { Ionicons } from "@expo/vector-icons";
import { StyleProp, ViewStyle, TextStyle } from "react-native";
import { StyleProp, ViewStyle, TextStyle, StyleSheet } from "react-native";
interface SearchInputProps {
placeholder?: string;
@@ -12,6 +12,8 @@ interface SearchInputProps {
containerStyle?: StyleProp<ViewStyle>;
style?: StyleProp<TextStyle>;
onChangeText?: (value: string) => void;
value?: string;
disabled?: boolean;
}
export default function SearchInput({
placeholder,
@@ -21,6 +23,8 @@ export default function SearchInput({
containerStyle,
style,
onChangeText,
value,
disabled,
...props
}: SearchInputProps) {
return (
@@ -29,14 +33,21 @@ export default function SearchInput({
<Ionicons
name="search-outline"
size={ICON_SIZE_SMALL}
color={MainColor.placeholder}
color={disabled ? MainColor.white_gray : MainColor.placeholder}
/>
}
value={value}
onChangeText={onChangeText}
placeholder={placeholder}
borderRadius={50}
containerStyle={[containerStyle, { marginBottom: 0 }]}
containerStyle={[disabled ? styleses.disabled : styleses.containerStyle]}
disabled={disabled}
{...props}
/>
);
}
const styleses = StyleSheet.create({
containerStyle: { width: "100%", marginBottom: 0 },
disabled: { width: "100%", marginBottom: 0, color: MainColor.white_gray },
});

View File

@@ -12,7 +12,7 @@ import {
StyleProp,
ViewStyle,
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { NativeSafeAreaViewProps, SafeAreaView } from "react-native-safe-area-context";
interface ViewWrapperProps {
children: React.ReactNode;
@@ -21,6 +21,7 @@ interface ViewWrapperProps {
footerComponent?: React.ReactNode;
floatingButton?: React.ReactNode;
hideFooter?: boolean;
edgesFooter?: NativeSafeAreaViewProps["edges"];
style?: StyleProp<ViewStyle>;
}
@@ -37,6 +38,7 @@ const ViewWrapper = ({
footerComponent,
floatingButton,
hideFooter = false,
edgesFooter =[],
style,
}: ViewWrapperProps) => {
const assetBackground = require("../../assets/images/main-background.png");
@@ -77,7 +79,7 @@ const ViewWrapper = ({
{footerComponent ? (
<SafeAreaView
edges={["bottom"]}
edges={Platform.OS === "ios" ? edgesFooter : ["bottom"]}
style={{
backgroundColor: MainColor.darkblue,
height: OS_HEIGHT

View File

@@ -23,7 +23,7 @@ export {
// OS Height
const OS_ANDROID_HEIGHT = 115
const OS_IOS_HEIGHT = 65
const OS_IOS_HEIGHT = 70
const OS_HEIGHT = Platform.OS === "ios" ? OS_IOS_HEIGHT : OS_ANDROID_HEIGHT
// Text Size

View File

@@ -135,7 +135,7 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const userData = async (token: string) => {
try {
setIsLoading(true);
const response = await apiConfig.get(`/mobile/user?token=${token}`, {
const response = await apiConfig.get(`/mobile?token=${token}`, {
headers: {
Authorization: `Bearer ${token}`,
},

8
index.js Normal file
View File

@@ -0,0 +1,8 @@
import { registerRootComponent } from 'expo';
import App from './App';
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in Expo Go or in a native build,
// the environment is set up appropriately
registerRootComponent(App);

View File

@@ -9,25 +9,17 @@
/* Begin PBXBuildFile section */
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
3E483D87269347231471F774 /* libPods-HIPMIBADUNG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 10F5AA024C601F56B4EEA12E /* libPods-HIPMIBADUNG.a */; };
3E9DE75169D0FBDB1B473165 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F0C0055A487C52DF43B9AA8C /* PrivacyInfo.xcprivacy */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
E3BE778B8B6E53CE5D518808 /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A445166333AB51CC299A7683 /* ExpoModulesProvider.swift */; };
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
10F5AA024C601F56B4EEA12E /* libPods-HIPMIBADUNG.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HIPMIBADUNG.a"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07F961A680F5B00A75B9A /* HIPMIBADUNG.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HIPMIBADUNG.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = HIPMIBADUNG/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = HIPMIBADUNG/Info.plist; sourceTree = "<group>"; };
43EA3CD4BD89A00F8AB4791D /* Pods-HIPMIBADUNG.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HIPMIBADUNG.release.xcconfig"; path = "Target Support Files/Pods-HIPMIBADUNG/Pods-HIPMIBADUNG.release.xcconfig"; sourceTree = "<group>"; };
7355472C9F7484C7F5780400 /* Pods-HIPMIBADUNG.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HIPMIBADUNG.debug.xcconfig"; path = "Target Support Files/Pods-HIPMIBADUNG/Pods-HIPMIBADUNG.debug.xcconfig"; sourceTree = "<group>"; };
A445166333AB51CC299A7683 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-HIPMIBADUNG/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = HIPMIBADUNG/SplashScreen.storyboard; sourceTree = "<group>"; };
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F0C0055A487C52DF43B9AA8C /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = HIPMIBADUNG/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
F11748412D0307B40044C1D9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = HIPMIBADUNG/AppDelegate.swift; sourceTree = "<group>"; };
F11748442D0722820044C1D9 /* HIPMIBADUNG-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "HIPMIBADUNG-Bridging-Header.h"; path = "HIPMIBADUNG/HIPMIBADUNG-Bridging-Header.h"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -37,7 +29,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
3E483D87269347231471F774 /* libPods-HIPMIBADUNG.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -53,7 +44,6 @@
13B07FB51A68108700A75B9A /* Images.xcassets */,
13B07FB61A68108700A75B9A /* Info.plist */,
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
F0C0055A487C52DF43B9AA8C /* PrivacyInfo.xcprivacy */,
);
name = HIPMIBADUNG;
sourceTree = "<group>";
@@ -62,28 +52,10 @@
isa = PBXGroup;
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
10F5AA024C601F56B4EEA12E /* libPods-HIPMIBADUNG.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
523B30FE431343BE096F5C21 /* Pods */ = {
isa = PBXGroup;
children = (
7355472C9F7484C7F5780400 /* Pods-HIPMIBADUNG.debug.xcconfig */,
43EA3CD4BD89A00F8AB4791D /* Pods-HIPMIBADUNG.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
7E80613205F5353A90501151 /* HIPMIBADUNG */ = {
isa = PBXGroup;
children = (
A445166333AB51CC299A7683 /* ExpoModulesProvider.swift */,
);
name = HIPMIBADUNG;
sourceTree = "<group>";
};
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
@@ -98,8 +70,6 @@
832341AE1AAA6A7D00B99B32 /* Libraries */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
523B30FE431343BE096F5C21 /* Pods */,
92643B17548A700FA33507C4 /* ExpoModulesProviders */,
);
indentWidth = 2;
sourceTree = "<group>";
@@ -114,14 +84,6 @@
name = Products;
sourceTree = "<group>";
};
92643B17548A700FA33507C4 /* ExpoModulesProviders */ = {
isa = PBXGroup;
children = (
7E80613205F5353A90501151 /* HIPMIBADUNG */,
);
name = ExpoModulesProviders;
sourceTree = "<group>";
};
BB2F792B24A3F905000567C9 /* Supporting */ = {
isa = PBXGroup;
children = (
@@ -139,13 +101,11 @@
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "HIPMIBADUNG" */;
buildPhases = (
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
4DF2145C6600FA4F2932F421 /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
4BE0A502450842B2C872639D /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -195,7 +155,6 @@
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
3E9DE75169D0FBDB1B473165 /* PrivacyInfo.xcprivacy in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -209,6 +168,8 @@
files = (
);
inputPaths = (
"$(SRCROOT)/.xcode.env",
"$(SRCROOT)/.xcode.env.local",
);
name = "Bundle React Native code and images";
outputPaths = (
@@ -239,43 +200,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
4BE0A502450842B2C872639D /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-HIPMIBADUNG/Pods-HIPMIBADUNG-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HIPMIBADUNG/Pods-HIPMIBADUNG-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
4DF2145C6600FA4F2932F421 /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "[Expo] Configure project";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-HIPMIBADUNG/expo-configure-project.sh\"\n";
};
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -284,76 +208,14 @@
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-HIPMIBADUNG/Pods-HIPMIBADUNG-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RNSVG/RNSVGFilters.bundle",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/AntDesign.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Entypo.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Feather.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Brands.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Regular.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome5_Solid.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome6_Brands.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome6_Regular.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/FontAwesome6_Solid.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Fontisto.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Foundation.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/MaterialIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Octicons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf",
"${PODS_ROOT}/../../node_modules/react-native-vector-icons/Fonts/Zocial.ttf",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/react-native-maps/ReactNativeMapsPrivacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNSVGFilters.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AntDesign.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Entypo.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EvilIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Feather.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Brands.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Regular.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome5_Solid.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome6_Brands.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome6_Regular.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/FontAwesome6_Solid.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Fontisto.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Foundation.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Ionicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialCommunityIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/MaterialIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Octicons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SimpleLineIcons.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Zocial.ttf",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SDWebImage.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ReactNativeMapsPrivacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -368,7 +230,6 @@
buildActionMask = 2147483647;
files = (
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */,
E3BE778B8B6E53CE5D518808 /* ExpoModulesProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -377,13 +238,10 @@
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7355472C9F7484C7F5780400 /* Pods-HIPMIBADUNG.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = HIPMIBADUNG/HIPMIBADUNG.entitlements;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = A3FZDNQTUG;
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
@@ -401,26 +259,23 @@
"-ObjC",
"-lc++",
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = "com.anonymous.hipmi-mobile";
PRODUCT_NAME = HIPMIBADUNG;
PRODUCT_NAME = "HIPMIBADUNG";
SWIFT_OBJC_BRIDGING_HEADER = "HIPMIBADUNG/HIPMIBADUNG-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
TARGETED_DEVICE_FAMILY = "1,2";
CODE_SIGN_ENTITLEMENTS = HIPMIBADUNG/HIPMIBADUNG.entitlements;
};
name = Debug;
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 43EA3CD4BD89A00F8AB4791D /* Pods-HIPMIBADUNG.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = HIPMIBADUNG/HIPMIBADUNG.entitlements;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = A3FZDNQTUG;
INFOPLIST_FILE = HIPMIBADUNG/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = (
@@ -433,13 +288,13 @@
"-ObjC",
"-lc++",
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = "com.anonymous.hipmi-mobile";
PRODUCT_NAME = HIPMIBADUNG;
PRODUCT_NAME = "HIPMIBADUNG";
SWIFT_OBJC_BRIDGING_HEADER = "HIPMIBADUNG/HIPMIBADUNG-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
TARGETED_DEVICE_FAMILY = "1,2";
CODE_SIGN_ENTITLEMENTS = HIPMIBADUNG/HIPMIBADUNG.entitlements;
};
name = Release;
};
@@ -495,14 +350,10 @@
/usr/lib/swift,
"$(inherited)",
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
LIBRARY_SEARCH_PATHS = "\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "$(inherited) ";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
USE_HERMES = true;
};
name = Debug;
};
@@ -551,12 +402,9 @@
/usr/lib/swift,
"$(inherited)",
);
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
LIBRARY_SEARCH_PATHS = "\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = "$(inherited) ";
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
VALIDATE_PRODUCT = YES;
};
name = Release;

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:HIPMIBADUNG.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>0A2A.1</string>
<string>3B52.1</string>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>E174.1</string>
<string>85F4.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>

View File

@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EXPO-VIEWCONTROLLER-1">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24093.7" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EXPO-VIEWCONTROLLER-1">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24053.1"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@@ -35,6 +36,9 @@
</scenes>
<resources>
<image name="SplashScreenLogo" width="200" height="200"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<namedColor name="SplashScreenBackground">
<color alpha="1.000" blue="1.00000000000000" green="1.00000000000000" red="1.00000000000000" customColorSpace="sRGB" colorSpace="custom"/>
</namedColor>

View File

@@ -4,12 +4,11 @@ require File.join(File.dirname(`node --print "require.resolve('react-native/pack
require 'json'
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
ENV['RCT_NEW_ARCH_ENABLED'] = '0' if podfile_properties['newArchEnabled'] == 'false'
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
ENV['RCT_NEW_ARCH_ENABLED'] ||= '0' if podfile_properties['newArchEnabled'] == 'false'
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false'
ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false'
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
install! 'cocoapods',
:deterministic_uuids => false
prepare_react_native_project!
@@ -49,16 +48,5 @@ target 'HIPMIBADUNG' do
:mac_catalyst_enabled => false,
:ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true',
)
# This is necessary for Xcode 14, because it signs resource bundles by default
# when building for devices.
installer.target_installation_results.pod_target_installation_results
.each do |pod_name, target_installation_result|
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
resource_bundle_target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
end
end

File diff suppressed because it is too large Load Diff

7
metro.config.js Normal file
View File

@@ -0,0 +1,7 @@
// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require('expo/metro-config');
/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);
module.exports = config;

View File

@@ -24,7 +24,6 @@
"axios": "^1.11.0",
"dayjs": "^1.11.13",
"expo": "^54.0.0",
"expo-blur": "~15.0.7",
"expo-camera": "~17.0.7",
"expo-clipboard": "~8.0.7",
"expo-constants": "~18.0.8",

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