Compare commits
45 Commits
checkpoint
...
loaddata/9
| Author | SHA1 | Date | |
|---|---|---|---|
| 2705f96b01 | |||
| 38a6b424e8 | |||
| 83fa277e03 | |||
| c570a19d84 | |||
| 7415c8c8ce | |||
| 72a3d42013 | |||
| d0abd14047 | |||
| 5b2be20469 | |||
| 60177a1087 | |||
| 771ae45f26 | |||
| 41f4a8ac99 | |||
| 48196cd46b | |||
| ec79a1fbcd | |||
| ed16f1b204 | |||
| d693550a1f | |||
| b3bfbc0f7e | |||
| 71e45d06cc | |||
| 07e64c335e | |||
| 1aebc9b4e8 | |||
| 5665dc88ba | |||
| da82a02a45 | |||
| 14c0f0e499 | |||
| 0262423c50 | |||
| c2682246d6 | |||
| 465e01015e | |||
| 3b15871ad4 | |||
| 9123e73606 | |||
| 6e2046467f | |||
| ca33dd83bb | |||
| ea3fbdc541 | |||
| 33cd47aaed | |||
| 57ac1eb45e | |||
| 145ad73616 | |||
| 7c85e35c61 | |||
| d098b8ca16 | |||
| 73a473cdc7 | |||
| 3f85f330d2 | |||
| 7743a2467c | |||
| 54611ef812 | |||
| 1503707eed | |||
| a01a9bd93f | |||
| 05c1cac10f | |||
| d27c01ed56 | |||
| 34680a4c38 | |||
| 43c8c105cf |
169
QWEN.md
Normal file
169
QWEN.md
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
# HIPMI Mobile Application - Development Context
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
HIPMI Mobile is a cross-platform mobile application built with Expo and React Native. The application is named "HIPMI Badung Connect" and serves as a platform for the HIPMI (Himpunan Pengusaha dan Pengusaha Indonesia) Badung chapter. It's designed to run on iOS, Android, and web platforms using a single codebase.
|
||||||
|
|
||||||
|
### Key Technologies
|
||||||
|
- **Framework**: Expo (v54.0.0) with React Native (v0.81.4)
|
||||||
|
- **Language**: TypeScript
|
||||||
|
- **Architecture**: File-based routing with Expo Router
|
||||||
|
- **State Management**: Context API
|
||||||
|
- **UI Components**: React Native Paper, custom components
|
||||||
|
- **Maps Integration**: Mapbox Maps for React Native
|
||||||
|
- **Push Notifications**: React Native Firebase Messaging
|
||||||
|
- **Build System**: Metro bundler
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
```
|
||||||
|
hipmi-mobile/
|
||||||
|
├── app/ # Main application screens and routing
|
||||||
|
│ ├── _layout.tsx # Root layout component
|
||||||
|
│ ├── index.tsx # Entry point (Login screen)
|
||||||
|
│ └── ...
|
||||||
|
├── components/ # Reusable UI components
|
||||||
|
├── context/ # State management (AuthContext)
|
||||||
|
├── screens/ # Screen components organized by feature
|
||||||
|
│ ├── Admin/ # Admin panel screens
|
||||||
|
│ ├── Authentication/ # Login, registration flows
|
||||||
|
│ ├── Collaboration/ # Collaboration features
|
||||||
|
│ ├── Event/ # Event management
|
||||||
|
│ ├── Forum/ # Forum functionality
|
||||||
|
│ ├── Home/ # Home screen
|
||||||
|
│ ├── Maps/ # Map integration
|
||||||
|
│ ├── Profile/ # User profile
|
||||||
|
│ └── ...
|
||||||
|
├── assets/ # Images, icons, and static assets
|
||||||
|
├── constants/ # Constants and configuration values
|
||||||
|
├── hooks/ # Custom React hooks
|
||||||
|
├── lib/ # Utility libraries
|
||||||
|
├── navigation/ # Navigation configuration
|
||||||
|
├── service/ # API services and business logic
|
||||||
|
├── types/ # TypeScript type definitions
|
||||||
|
└── utils/ # Helper functions
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building and Running
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Node.js (with bun as the package manager)
|
||||||
|
- Expo CLI
|
||||||
|
- iOS Simulator or Android Emulator (for native builds)
|
||||||
|
|
||||||
|
### Setup and Development
|
||||||
|
|
||||||
|
1. **Install Dependencies**
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Run Development Server**
|
||||||
|
```bash
|
||||||
|
bun run start
|
||||||
|
```
|
||||||
|
Or use the shorthand:
|
||||||
|
```bash
|
||||||
|
bunx expo start
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Platform-Specific Commands**
|
||||||
|
- iOS: `bun run ios` or `bunx expo start --ios`
|
||||||
|
- Android: `bun run android` or `bunx expo start --android`
|
||||||
|
- Web: `bun run web` or `bunx expo start --web`
|
||||||
|
|
||||||
|
4. **Linting**
|
||||||
|
```bash
|
||||||
|
bun run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
The application uses environment variables defined in the app.config.js file:
|
||||||
|
- `API_BASE_URL`: Base URL for API endpoints
|
||||||
|
- `BASE_URL`: Base application URL
|
||||||
|
- `DEEP_LINK_URL`: URL for deep linking functionality
|
||||||
|
|
||||||
|
### EAS Build Configuration
|
||||||
|
The project uses Expo Application Services (EAS) for building and deploying:
|
||||||
|
- Development builds with development client
|
||||||
|
- Preview builds for internal distribution
|
||||||
|
- Production builds for app stores
|
||||||
|
|
||||||
|
## Features and Functionality
|
||||||
|
|
||||||
|
The application appears to include several key modules:
|
||||||
|
- **Authentication**: Login, registration, and verification flows
|
||||||
|
- **Admin Panel**: Administrative functions
|
||||||
|
- **Collaboration**: Tools for member collaboration
|
||||||
|
- **Events**: Event management and calendar
|
||||||
|
- **Forum**: Discussion forums
|
||||||
|
- **Maps**: Location-based services with Mapbox integration
|
||||||
|
- **Donations**: Donation functionality
|
||||||
|
- **Job Board**: Employment opportunities
|
||||||
|
- **Investment**: Investment-related features
|
||||||
|
- **Voting**: Voting systems
|
||||||
|
- **Portfolio**: Member portfolio showcase
|
||||||
|
- **Notifications**: Push notifications via Firebase
|
||||||
|
|
||||||
|
## Development Conventions
|
||||||
|
|
||||||
|
### Coding Standards
|
||||||
|
- TypeScript is used throughout the project for type safety
|
||||||
|
- Component-based architecture with reusable components
|
||||||
|
- Context API for state management
|
||||||
|
- File-based routing with Expo Router
|
||||||
|
- Consistent naming conventions using camelCase for variables and PascalCase for components
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- Linting is configured with ESLint
|
||||||
|
- Standard Expo linting configuration is used
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- Firebase is integrated for authentication and messaging
|
||||||
|
- Camera and location permissions are properly configured
|
||||||
|
- Deep linking is secured with app domain associations
|
||||||
|
|
||||||
|
## Key Dependencies
|
||||||
|
|
||||||
|
### Core Dependencies
|
||||||
|
- `@react-navigation/*`: Navigation solution for React Native
|
||||||
|
- `@react-native-firebase/*`: Firebase integration for React Native
|
||||||
|
- `@rnmapbox/maps`: Mapbox integration for React Native
|
||||||
|
- `expo-router`: File-based routing for Expo applications
|
||||||
|
- `react-native-paper`: Material Design components for React Native
|
||||||
|
- `react-native-toast-message`: Toast notifications
|
||||||
|
- `react-native-otp-entry`: OTP input components
|
||||||
|
- `react-native-qrcode-svg`: QR code generation
|
||||||
|
|
||||||
|
### Development Dependencies
|
||||||
|
- `@types/*`: TypeScript type definitions
|
||||||
|
- `eslint-config-expo`: Expo-specific ESLint configuration
|
||||||
|
- `typescript`: Type checking
|
||||||
|
|
||||||
|
## Platform Support
|
||||||
|
|
||||||
|
The application is configured to support:
|
||||||
|
- **iOS**: With tablet support and proper permissions
|
||||||
|
- **Android**: With adaptive icons and intent filters for deep linking
|
||||||
|
- **Web**: Static output configuration for web deployment
|
||||||
|
|
||||||
|
## Special Configurations
|
||||||
|
|
||||||
|
### iOS Configuration
|
||||||
|
- Bundle identifier: `com.anonymous.hipmi-mobile`
|
||||||
|
- Supports tablets
|
||||||
|
- Google Services integration
|
||||||
|
- Location permission handling
|
||||||
|
- Associated domains for deep linking
|
||||||
|
|
||||||
|
### Android Configuration
|
||||||
|
- Package name: `com.bip.hipmimobileapp`
|
||||||
|
- Adaptive icons
|
||||||
|
- Edge-to-edge display enabled
|
||||||
|
- Intent filters for HTTPS deep linking
|
||||||
|
- Google Services integration
|
||||||
|
|
||||||
|
### Maps Integration
|
||||||
|
The application uses Mapbox for mapping functionality with the `@rnmapbox/maps` plugin.
|
||||||
|
|
||||||
|
### Push Notifications
|
||||||
|
Firebase Cloud Messaging is integrated for push notifications with proper configuration for both iOS and Android platforms.
|
||||||
@@ -100,7 +100,7 @@ packagingOptions {
|
|||||||
applicationId 'com.bip.hipmimobileapp'
|
applicationId 'com.bip.hipmimobileapp'
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 3
|
versionCode 4
|
||||||
versionName "1.0.1"
|
versionName "1.0.1"
|
||||||
|
|
||||||
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
|
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
<data android:scheme="https"/>
|
<data android:scheme="https"/>
|
||||||
</intent>
|
</intent>
|
||||||
</queries>
|
</queries>
|
||||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:enableOnBackInvokedCallback="false">
|
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:enableOnBackInvokedCallback="false" android:fullBackupContent="@xml/secure_store_backup_rules" android:dataExtractionRules="@xml/secure_store_data_extraction_rules">
|
||||||
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color"/>
|
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color" tools:replace="android:resource"/>
|
||||||
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon"/>
|
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon"/>
|
||||||
<meta-data android:name="expo.modules.notifications.default_notification_color" android:resource="@color/notification_icon_color"/>
|
<meta-data android:name="expo.modules.notifications.default_notification_color" android:resource="@color/notification_icon_color"/>
|
||||||
<meta-data android:name="expo.modules.notifications.default_notification_icon" android:resource="@drawable/notification_icon"/>
|
<meta-data android:name="expo.modules.notifications.default_notification_icon" android:resource="@drawable/notification_icon"/>
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<full-backup-content>
|
||||||
|
<exclude domain="sharedpref" path="SECURESTORE"/>
|
||||||
|
</full-backup-content>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<data-extraction-rules>
|
||||||
|
<cloud-backup>
|
||||||
|
<exclude domain="sharedpref" path="SECURESTORE"/>
|
||||||
|
</cloud-backup>
|
||||||
|
<device-transfer>
|
||||||
|
<exclude domain="sharedpref" path="SECURESTORE"/>
|
||||||
|
</device-transfer>
|
||||||
|
</data-extraction-rules>
|
||||||
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.google.gms:google-services:4.4.1'
|
classpath 'com.google.gms:google-services:4.4.1'
|
||||||
classpath('com.android.tools.build:gradle')
|
classpath('com.android.tools.build:gradle')
|
||||||
classpath('com.facebook.react:react-native-gradle-plugin')
|
classpath('com.facebook.react:react-native-gradle-plugin')
|
||||||
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
|
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default {
|
|||||||
"Aplikasi membutuhkan akses lokasi untuk menampilkan peta.",
|
"Aplikasi membutuhkan akses lokasi untuk menampilkan peta.",
|
||||||
},
|
},
|
||||||
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"],
|
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"],
|
||||||
buildNumber: "15",
|
buildNumber: "20",
|
||||||
},
|
},
|
||||||
|
|
||||||
android: {
|
android: {
|
||||||
@@ -32,7 +32,7 @@ export default {
|
|||||||
},
|
},
|
||||||
edgeToEdgeEnabled: true,
|
edgeToEdgeEnabled: true,
|
||||||
package: "com.bip.hipmimobileapp",
|
package: "com.bip.hipmimobileapp",
|
||||||
versionCode: 3,
|
versionCode: 4,
|
||||||
// softwareKeyboardLayoutMode: 'resize', // option: untuk mengatur keyboard pada room chst collaboration
|
// softwareKeyboardLayoutMode: 'resize', // option: untuk mengatur keyboard pada room chst collaboration
|
||||||
intentFilters: [
|
intentFilters: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { CenterCustom, TextCustom, ViewWrapper } from "@/components";
|
import { CenterCustom, TextCustom, ViewWrapper } from "@/components";
|
||||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { Image } from "expo-image";
|
import { Image } from "expo-image";
|
||||||
import { useLocalSearchParams } from "expo-router";
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { View } from "react-native";
|
||||||
|
|
||||||
export default function PreviewImage() {
|
export default function PreviewImage() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
@@ -11,18 +13,48 @@ export default function PreviewImage() {
|
|||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
{id ? (
|
{id ? (
|
||||||
<Image
|
<View
|
||||||
onLoad={() => {
|
style={{
|
||||||
setIsLoading(false);
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
position: "relative",
|
||||||
}}
|
}}
|
||||||
source={
|
>
|
||||||
isLoading
|
{/* Main Image */}
|
||||||
? require("@/assets/images/loading.gif")
|
<Image
|
||||||
: API_STRORAGE.GET({ fileId: id as string })
|
onLoad={() => {
|
||||||
}
|
setIsLoading(false);
|
||||||
contentFit="contain"
|
}}
|
||||||
style={{ width: "100%", height: "100%" }}
|
source={API_STRORAGE.GET({ fileId: id as string })}
|
||||||
/>
|
contentFit="contain"
|
||||||
|
style={{ width: "100%", height: "100%" }}
|
||||||
|
// placeholder={require("@/assets/images/loading.gif")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Custom Loader Overlay */}
|
||||||
|
{isLoading && (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
backgroundColor: MainColor.darkblue,
|
||||||
|
zIndex: 1,
|
||||||
|
opacity: 0.5,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
source={require("@/assets/images/loading.gif")}
|
||||||
|
contentFit="contain"
|
||||||
|
style={{ width: 60, height: 60 }}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<CenterCustom>
|
<CenterCustom>
|
||||||
<TextCustom>File not found</TextCustom>
|
<TextCustom>File not found</TextCustom>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { BackButton } from "@/components";
|
import { BackButton } from "@/components";
|
||||||
|
import { IconPlus } from "@/components/_Icon";
|
||||||
|
import { IconDot } from "@/components/_Icon/IconComponent";
|
||||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
@@ -51,24 +53,35 @@ export default function UserLayout() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* ========== Notification Section ========= */}
|
{/* ========== Notification Section ========= */}
|
||||||
<Stack.Screen
|
|
||||||
|
{/* DIPINDAH DI FILE NOTIFICATION USER */}
|
||||||
|
{/* <Stack.Screen
|
||||||
name="notifications/index"
|
name="notifications/index"
|
||||||
options={{
|
options={{
|
||||||
title: "Notifikasi",
|
title: "Notifikasi",
|
||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
|
// headerRight: () => (
|
||||||
|
// <IconPlus
|
||||||
|
// color={MainColor.yellow}
|
||||||
|
// onPress={() => router.push("/test-notifications")}
|
||||||
|
// />
|
||||||
|
// ),
|
||||||
}}
|
}}
|
||||||
/>
|
/> */}
|
||||||
|
|
||||||
{/* ========== Event Section ========= */}
|
{/* ========== Event Section ========= */}
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="event/(tabs)"
|
name="event/(tabs)"
|
||||||
options={{
|
options={{
|
||||||
title: "Event",
|
title: "Event",
|
||||||
headerLeft: () => (
|
// NOTE: DIPINDAH DI FILE /Event/(Tabs)/_layout.tsx
|
||||||
<LeftButtonCustom path="/(application)/(user)/home" />
|
// headerLeft: () => (
|
||||||
),
|
// <LeftButtonCustom path="/(application)/(user)/home" />
|
||||||
|
// ),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="event/create"
|
name="event/create"
|
||||||
options={{
|
options={{
|
||||||
@@ -511,7 +524,8 @@ export default function UserLayout() {
|
|||||||
name="job/(tabs)"
|
name="job/(tabs)"
|
||||||
options={{
|
options={{
|
||||||
title: "Job Vacancy",
|
title: "Job Vacancy",
|
||||||
headerLeft: () => <BackButton path="/home" />,
|
// headerLeft: () => <BackButton path="/home" />,
|
||||||
|
// NOTE: headerLeft di pindahkan ke Tabs Layout
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -602,6 +616,20 @@ export default function UserLayout() {
|
|||||||
headerLeft: () => <BackButton />,
|
headerLeft: () => <BackButton />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="forum/[id]/preview-report-posting"
|
||||||
|
options={{
|
||||||
|
title: "Laporan Postingan",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="forum/[id]/preview-report-comment"
|
||||||
|
options={{
|
||||||
|
title: "Laporan Komentar",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* ========== Maps Section ========= */}
|
{/* ========== Maps Section ========= */}
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export default function DonationBeranda() {
|
|||||||
const response = await apiDonationGetAll({
|
const response = await apiDonationGetAll({
|
||||||
category: "beranda"
|
category: "beranda"
|
||||||
});
|
});
|
||||||
console.log("[RES GET ALL]", JSON.stringify(response.data, null, 2));
|
|
||||||
|
|
||||||
setList(response.data);
|
setList(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import { Href, router, useFocusEffect } from "expo-router";
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { View } from "react-native";
|
import { View } from "react-native";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function DonationMyDonation() {
|
export default function DonationMyDonation() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
@@ -25,20 +26,25 @@ export default function DonationMyDonation() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [user?.id])
|
}, [user?.id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
|
if (!user?.id) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Load data gagal, user tidak ditemukan",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoadList(true);
|
setLoadList(true);
|
||||||
const response = await apiDonationGetAll({
|
const response = await apiDonationGetAll({
|
||||||
category: "my-donation",
|
category: "my-donation",
|
||||||
authorId: user?.id,
|
authorId: user?.id,
|
||||||
});
|
});
|
||||||
console.log(
|
|
||||||
"[RES GET MY DONATION]",
|
|
||||||
JSON.stringify(response.data, null, 2)
|
|
||||||
);
|
|
||||||
|
|
||||||
setList(response.data);
|
setList(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,80 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import Donation_ScreenStatus from "@/screens/Donation/ScreenStatus";
|
||||||
import {
|
import { useLocalSearchParams } from "expo-router";
|
||||||
LoaderCustom,
|
|
||||||
ScrollableCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
|
||||||
import Donasi_BoxStatus from "@/screens/Donation/BoxStatus";
|
|
||||||
import { apiDonationGetByStatus } from "@/service/api-client/api-donation";
|
|
||||||
import { useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function DonationStatus() {
|
export default function DonationStatus() {
|
||||||
const { user } = useAuth();
|
const { status } = useLocalSearchParams<{ status?: string }>();
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
|
||||||
"publish"
|
|
||||||
);
|
|
||||||
const [listData, setListData] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadList();
|
|
||||||
}, [activeCategory])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiDonationGetByStatus({
|
|
||||||
authorId: user?.id as string,
|
|
||||||
status: activeCategory as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
setListData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setListData(null);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
|
||||||
setActiveCategory(item.value);
|
|
||||||
// tambahkan logika lain seperti filter dsb.
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollComponent = (
|
|
||||||
<ScrollableCustom
|
|
||||||
data={dummyMasterStatus.map((e, i) => ({
|
|
||||||
id: i,
|
|
||||||
label: e.label,
|
|
||||||
value: e.value,
|
|
||||||
}))}
|
|
||||||
onButtonPress={handlePress}
|
|
||||||
activeId={activeCategory as any}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper hideFooter headerComponent={scrollComponent}>
|
<Donation_ScreenStatus initialStatus={status || "publish"} />
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item: any, index: number) => (
|
|
||||||
<Donasi_BoxStatus
|
|
||||||
key={index}
|
|
||||||
data={item}
|
|
||||||
status={activeCategory as string}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,21 +10,32 @@ import {
|
|||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
import { LOCAL_STORAGE_KEY } from "@/constants/local-storage-key";
|
import { LOCAL_STORAGE_KEY } from "@/constants/local-storage-key";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function InvestmentInputDonation() {
|
export default function InvestmentInputDonation() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [nominal, setNominal] = useState<number>(0);
|
const [nominal, setNominal] = useState<number>(0);
|
||||||
|
|
||||||
const handlerSubmit = async () => {
|
const handlerSubmit = async () => {
|
||||||
|
if (!user?.id) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "User tidak ditemukan",
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await AsyncStorage.setItem(
|
await AsyncStorage.setItem(
|
||||||
LOCAL_STORAGE_KEY.transactionDonation,
|
LOCAL_STORAGE_KEY.transactionDonation,
|
||||||
JSON.stringify({ nominal: nominal.toString() })
|
JSON.stringify({ nominal: nominal.toString() }),
|
||||||
);
|
);
|
||||||
router.replace(`/donation/${id}/select-bank`);
|
router.replace(`/donation/${id}/select-bank`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import Donation_ComponentBoxDetailData from "@/screens/Donation/ComponentBoxDeta
|
|||||||
import Donation_ComponentStoryFunrising from "@/screens/Donation/ComponentStoryFunrising";
|
import Donation_ComponentStoryFunrising from "@/screens/Donation/ComponentStoryFunrising";
|
||||||
import Donation_ProgressSection from "@/screens/Donation/ProgressSection";
|
import Donation_ProgressSection from "@/screens/Donation/ProgressSection";
|
||||||
import { apiDonationGetOne } from "@/service/api-client/api-donation";
|
import { apiDonationGetOne } from "@/service/api-client/api-donation";
|
||||||
|
import { countDownAndCondition } from "@/utils/countDownAndCondition";
|
||||||
import { FontAwesome6 } from "@expo/vector-icons";
|
import { FontAwesome6 } from "@expo/vector-icons";
|
||||||
import {
|
import {
|
||||||
router,
|
router,
|
||||||
@@ -24,7 +25,7 @@ import {
|
|||||||
useLocalSearchParams,
|
useLocalSearchParams,
|
||||||
} from "expo-router";
|
} from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function DonasiDetailStatus() {
|
export default function DonasiDetailStatus() {
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
@@ -36,7 +37,7 @@ export default function DonasiDetailStatus() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -58,6 +59,27 @@ export default function DonasiDetailStatus() {
|
|||||||
setOpenDrawer(false);
|
setOpenDrawer(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [value, setValue] = useState({
|
||||||
|
sisa: 0,
|
||||||
|
reminder: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateCountDown();
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
const updateCountDown = () => {
|
||||||
|
const countDown = countDownAndCondition({
|
||||||
|
duration: data?.DonasiMaster_Durasi?.name,
|
||||||
|
publishTime: data?.publishTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
setValue({
|
||||||
|
sisa: countDown.durationDay,
|
||||||
|
reminder: countDown.reminder,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -74,10 +96,16 @@ export default function DonasiDetailStatus() {
|
|||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<Donation_ComponentBoxDetailData
|
<Donation_ComponentBoxDetailData
|
||||||
|
sisaHari={value.sisa}
|
||||||
|
reminder={value.reminder}
|
||||||
data={data}
|
data={data}
|
||||||
|
showSisaHari={status === "publish" ? true : false}
|
||||||
bottomSection={
|
bottomSection={
|
||||||
status === "publish" && (
|
status === "publish" && (
|
||||||
<Donation_ProgressSection id={id as string} />
|
<Donation_ProgressSection
|
||||||
|
id={id as string}
|
||||||
|
progres={Number(data?.progres) || 0}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
LoaderCustom,
|
LoaderCustom,
|
||||||
|
NewWrapper,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
@@ -60,7 +62,7 @@ export default function DonationEdit() {
|
|||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
onLoadList();
|
onLoadList();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -79,7 +81,6 @@ export default function DonationEdit() {
|
|||||||
imageId: response.data.imageId,
|
imageId: response.data.imageId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ERROR]", error);
|
console.log("[ERROR]", error);
|
||||||
}
|
}
|
||||||
@@ -182,7 +183,21 @@ export default function DonationEdit() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
|
hideFooter
|
||||||
|
footerComponent={
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={() => {
|
||||||
|
handlerSubmitUpdate();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
}
|
||||||
|
>
|
||||||
<InformationBox text="Lengkapi semua data di bawah untuk selanjutnya mengisi cerita penggalangan dana." />
|
<InformationBox text="Lengkapi semua data di bawah untuk selanjutnya mengisi cerita penggalangan dana." />
|
||||||
{!data || loadList ? (
|
{!data || loadList ? (
|
||||||
<LoaderCustom />
|
<LoaderCustom />
|
||||||
@@ -260,17 +275,9 @@ export default function DonationEdit() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom
|
|
||||||
isLoading={isLoading}
|
|
||||||
onPress={() => {
|
|
||||||
handlerSubmitUpdate();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
)}
|
)}
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
@@ -8,8 +9,8 @@ import {
|
|||||||
StackCustom,
|
StackCustom,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
|
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import {
|
import {
|
||||||
@@ -103,7 +104,7 @@ export default function DonationCreateStory() {
|
|||||||
type: "success",
|
type: "success",
|
||||||
text1: "Donasi berhasil disimpan",
|
text1: "Donasi berhasil disimpan",
|
||||||
});
|
});
|
||||||
router.replace("/donation/status");
|
router.replace("/donation/status?status=review");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ERROR]", error);
|
console.log("[ERROR]", error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -112,7 +113,23 @@ export default function DonationCreateStory() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
|
hideFooter
|
||||||
|
footerComponent={
|
||||||
|
<>
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={() => {
|
||||||
|
handlerSubmit();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="Cerita Anda adalah kunci untuk menginspirasi kebaikan. Jelaskan dengan jujur dan jelas tujuan penggalangan dana ini agar calon donatur memahami dampak positif yang dapat mereka wujudkan melalui kontribusi mereka." />
|
<InformationBox text="Cerita Anda adalah kunci untuk menginspirasi kebaikan. Jelaskan dengan jujur dan jelas tujuan penggalangan dana ini agar calon donatur memahami dampak positif yang dapat mereka wujudkan melalui kontribusi mereka." />
|
||||||
<TextAreaCustom
|
<TextAreaCustom
|
||||||
@@ -166,18 +183,8 @@ export default function DonationCreateStory() {
|
|||||||
value={data.rekening}
|
value={data.rekening}
|
||||||
onChangeText={(value) => setData({ ...data, rekening: value })}
|
onChangeText={(value) => setData({ ...data, rekening: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
|
||||||
<ButtonCustom
|
|
||||||
isLoading={isLoading}
|
|
||||||
onPress={() => {
|
|
||||||
handlerSubmit();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Simpan
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
@@ -8,8 +9,8 @@ import {
|
|||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
|
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { apiDonationCreate } from "@/service/api-client/api-donation";
|
import { apiDonationCreate } from "@/service/api-client/api-donation";
|
||||||
import { apiMasterDonation } from "@/service/api-client/api-master";
|
import { apiMasterDonation } from "@/service/api-client/api-master";
|
||||||
@@ -43,7 +44,7 @@ export default function DonationCreate() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadList();
|
onLoadList();
|
||||||
}, [])
|
}, []),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadList = async () => {
|
const onLoadList = async () => {
|
||||||
@@ -125,7 +126,24 @@ export default function DonationCreate() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
|
hideFooter
|
||||||
|
footerComponent={
|
||||||
|
<>
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={() => {
|
||||||
|
handlerSubmit();
|
||||||
|
// router.push(`/donation/create-story?id=${"dasdsadsa"}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Selanjutnya
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="Lengkapi semua data di bawah untuk selanjutnya mengisi cerita penggalangan dana." />
|
<InformationBox text="Lengkapi semua data di bawah untuk selanjutnya mengisi cerita penggalangan dana." />
|
||||||
|
|
||||||
@@ -201,20 +219,8 @@ export default function DonationCreate() {
|
|||||||
onChange={(value: any) => setData({ ...data, durasiId: value })}
|
onChange={(value: any) => setData({ ...data, durasiId: value })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacing />
|
|
||||||
<ButtonCustom
|
|
||||||
isLoading={isLoading}
|
|
||||||
onPress={() => {
|
|
||||||
handlerSubmit();
|
|
||||||
// router.push(`/donation/create-story?id=${"dasdsadsa"}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Selanjutnya
|
|
||||||
</ButtonCustom>
|
|
||||||
<Spacing />
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,34 @@ import {
|
|||||||
IconHome,
|
IconHome,
|
||||||
IconStatus,
|
IconStatus,
|
||||||
} from "@/components/_Icon";
|
} from "@/components/_Icon";
|
||||||
|
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
|
||||||
import { TabsStyles } from "@/styles/tabs-styles";
|
import { TabsStyles } from "@/styles/tabs-styles";
|
||||||
import { Tabs } from "expo-router";
|
import { router, Tabs, useLocalSearchParams, useNavigation } from "expo-router";
|
||||||
|
import { useLayoutEffect } from "react";
|
||||||
|
|
||||||
export default function EventTabsLayout() {
|
export default function EventTabsLayout() {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
const { from, category } = useLocalSearchParams<{
|
||||||
|
from?: string;
|
||||||
|
category?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
console.log("from", from);
|
||||||
|
console.log("category", category);
|
||||||
|
|
||||||
|
// Atur header secara dinamis
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
|
headerLeft: () => (
|
||||||
|
<BackButtonFromNotification
|
||||||
|
from={from as string}
|
||||||
|
category={category as string}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}, [from, router, navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs screenOptions={TabsStyles}>
|
<Tabs screenOptions={TabsStyles}>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
|
|||||||
@@ -1,115 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Event_ScreenContribution from "@/screens/Event/ScreenContribution";
|
||||||
AvatarUsernameAndOtherComponent,
|
|
||||||
BoxWithHeaderSection,
|
|
||||||
LoaderCustom,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import {
|
|
||||||
apiEventGetAll
|
|
||||||
} from "@/service/api-client/api-event";
|
|
||||||
import { dateTimeView } from "@/utils/dateTimeView";
|
|
||||||
import { useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import React, { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function EventContribution() {
|
export default function EventContribution() {
|
||||||
const { user } = useAuth();
|
|
||||||
const [listData, setListData] = useState<any>([]);
|
|
||||||
const [isLoadList, setIsLoadList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [user?.id])
|
|
||||||
);
|
|
||||||
|
|
||||||
async function onLoadData() {
|
|
||||||
try {
|
|
||||||
setIsLoadList(true);
|
|
||||||
const response = await apiEventGetAll({
|
|
||||||
category: "contribution",
|
|
||||||
userId: user?.id,
|
|
||||||
});
|
|
||||||
console.log("[DATA] ", JSON.stringify(response.data, null, 2));
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
|
|
||||||
// const responseListParticipants = await apiEventListOfParticipants({
|
|
||||||
// id: response?.data?.Event?.id,
|
|
||||||
// });
|
|
||||||
// console.log(
|
|
||||||
// "[LIST PARTICIPANTS]",
|
|
||||||
// JSON.stringify(responseListParticipants.data, null, 2)
|
|
||||||
// );
|
|
||||||
// if (responseListParticipants.success) {
|
|
||||||
// setListParticipants(responseListParticipants.data);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setIsLoadList(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper hideFooter>
|
<>
|
||||||
{isLoadList ? (
|
<Event_ScreenContribution />
|
||||||
<LoaderCustom />
|
</>
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Belum ada kontribusi</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item: any, index: number) => (
|
|
||||||
<BoxWithHeaderSection
|
|
||||||
key={index}
|
|
||||||
href={`/event/${item?.Event?.id}/contribution`}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<AvatarUsernameAndOtherComponent
|
|
||||||
avatar={item?.Event?.Author?.Profile?.imageId}
|
|
||||||
avatarHref={`/profile/${item?.Event?.Author?.Profile?.id}`}
|
|
||||||
name={item?.Event?.Author?.username}
|
|
||||||
rightComponent={
|
|
||||||
<TextCustom truncate>
|
|
||||||
{dateTimeView({
|
|
||||||
date: item?.Event?.tanggal,
|
|
||||||
withoutTime: true,
|
|
||||||
})}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextCustom bold align="center" size="xlarge" truncate={2}>
|
|
||||||
{item?.Event?.title}
|
|
||||||
</TextCustom>
|
|
||||||
<Spacing height={0} />
|
|
||||||
|
|
||||||
{/* <Grid>
|
|
||||||
{item?.Event?.Event_Peserta?.map(
|
|
||||||
(item2: any, index2: number) => (
|
|
||||||
<Grid.Col
|
|
||||||
style={{ alignItems: "center" }}
|
|
||||||
span={12 / item?.Event?.Event_Peserta?.length}
|
|
||||||
key={index2}
|
|
||||||
>
|
|
||||||
<AvatarComp
|
|
||||||
size="base"
|
|
||||||
href={`/profile/${item2?.User?.Profile?.id}`}
|
|
||||||
fileId={item2?.User?.Profile?.imageId}
|
|
||||||
/>
|
|
||||||
</Grid.Col>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Grid> */}
|
|
||||||
</StackCustom>
|
|
||||||
</BoxWithHeaderSection>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,104 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { ButtonCustom, LoaderCustom, Spacing, TextCustom } from "@/components";
|
import Event_ScreenHistory from "@/screens/Event/ScreenHistory";
|
||||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
|
||||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
|
|
||||||
import { apiEventGetAll } from "@/service/api-client/api-event";
|
|
||||||
import { dateTimeView } from "@/utils/dateTimeView";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
|
|
||||||
export default function EventHistory() {
|
export default function EventHistory() {
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>("all");
|
|
||||||
const { user } = useAuth();
|
|
||||||
const [listData, setListData] = useState<any>([]);
|
|
||||||
const [isLoadList, setIsLoadList] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onLoadData({ userId: user?.id });
|
|
||||||
}, [user?.id, activeCategory]);
|
|
||||||
|
|
||||||
async function onLoadData({ userId }: { userId?: string }) {
|
|
||||||
try {
|
|
||||||
setIsLoadList(true);
|
|
||||||
const response = await apiEventGetAll({
|
|
||||||
category: activeCategory === "all" ? "all-history" : "my-history",
|
|
||||||
userId: userId,
|
|
||||||
});
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setIsLoadList(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
|
||||||
setActiveCategory(item);
|
|
||||||
// tambahkan logika lain seperti filter dsb.
|
|
||||||
};
|
|
||||||
|
|
||||||
const headerComponent = (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
padding: 5,
|
|
||||||
backgroundColor: MainColor.soft_darkblue,
|
|
||||||
borderRadius: 50,
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ButtonCustom
|
|
||||||
backgroundColor={
|
|
||||||
activeCategory === "all" ? MainColor.yellow : AccentColor.blue
|
|
||||||
}
|
|
||||||
textColor={activeCategory === "all" ? MainColor.black : MainColor.white}
|
|
||||||
style={{ width: "49%" }}
|
|
||||||
onPress={() => handlePress("all")}
|
|
||||||
>
|
|
||||||
Semua Riwayat
|
|
||||||
</ButtonCustom>
|
|
||||||
<Spacing width={"2%"} />
|
|
||||||
<ButtonCustom
|
|
||||||
backgroundColor={
|
|
||||||
activeCategory === "main" ? MainColor.yellow : AccentColor.blue
|
|
||||||
}
|
|
||||||
textColor={
|
|
||||||
activeCategory === "main" ? MainColor.black : MainColor.white
|
|
||||||
}
|
|
||||||
style={{ width: "49%" }}
|
|
||||||
onPress={() => handlePress("main")}
|
|
||||||
>
|
|
||||||
Riwayat Saya
|
|
||||||
</ButtonCustom>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper headerComponent={headerComponent} hideFooter>
|
<>
|
||||||
{isLoadList ? (
|
<Event_ScreenHistory />
|
||||||
<LoaderCustom />
|
</>
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Belum ada riwayat</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item: any, index: number) => (
|
|
||||||
<Event_BoxPublishSection
|
|
||||||
key={index.toString()}
|
|
||||||
data={item}
|
|
||||||
rightComponentAvatar={
|
|
||||||
<TextCustom>
|
|
||||||
{dateTimeView({ date: item?.tanggal, withoutTime: true })}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
href={`/event/${item.id}/history`}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +1,9 @@
|
|||||||
import { LoaderCustom, TextCustom } from "@/components";
|
import Event_ScreenBeranda from "@/screens/Event/ScreenBeranda";
|
||||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
|
||||||
import FloatingButton from "@/components/Button/FloatingButton";
|
|
||||||
import Event_BoxPublishSection from "@/screens/Event/BoxPublishSection";
|
|
||||||
import { apiEventGetAll } from "@/service/api-client/api-event";
|
|
||||||
import { dateTimeView } from "@/utils/dateTimeView";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function EventBeranda() {
|
export default function EventBeranda() {
|
||||||
const [listData, setListData] = useState([]);
|
|
||||||
const [isLoadData, setIsLoadData] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setIsLoadData(true);
|
|
||||||
const response = await apiEventGetAll({category: "beranda"});
|
|
||||||
// console.log("Response", JSON.stringify(response.data, null, 2));
|
|
||||||
setListData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setIsLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<>
|
||||||
hideFooter
|
<Event_ScreenBeranda />
|
||||||
floatingButton={
|
</>
|
||||||
<FloatingButton onPress={() => router.push("/event/create")} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{isLoadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Belum ada event</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item: any, index) => (
|
|
||||||
|
|
||||||
|
|
||||||
<Event_BoxPublishSection
|
|
||||||
key={index}
|
|
||||||
href={`/event/${item.id}/publish`}
|
|
||||||
data={item}
|
|
||||||
rightComponentAvatar={
|
|
||||||
<TextCustom>
|
|
||||||
{dateTimeView({ date: item?.tanggal, withoutTime: true })}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,99 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Event_ScreenStatus from "@/screens/Event/ScreenStatus";
|
||||||
BoxWithHeaderSection,
|
|
||||||
Grid,
|
|
||||||
LoaderCustom,
|
|
||||||
ScrollableCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
} from "@/components";
|
|
||||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
|
||||||
import { apiEventGetByStatus } from "@/service/api-client/api-event";
|
|
||||||
import { useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function EventStatus() {
|
export default function EventStatus() {
|
||||||
const { user } = useAuth();
|
|
||||||
const id = user?.id || "";
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
|
||||||
"publish"
|
|
||||||
);
|
|
||||||
const [listData, setListData] = useState([]);
|
|
||||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [activeCategory, id])
|
|
||||||
);
|
|
||||||
|
|
||||||
async function onLoadData() {
|
|
||||||
try {
|
|
||||||
setLoadingGetData(true);
|
|
||||||
const response = await apiEventGetByStatus({
|
|
||||||
id: id!,
|
|
||||||
status: activeCategory!,
|
|
||||||
});
|
|
||||||
// console.log("Response", JSON.stringify(response.data, null, 2));
|
|
||||||
setListData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
setLoadingGetData(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
|
||||||
setActiveCategory(item.value);
|
|
||||||
// tambahkan logika lain seperti filter dsb.
|
|
||||||
};
|
|
||||||
|
|
||||||
const tabsComponent = (
|
|
||||||
<ScrollableCustom
|
|
||||||
data={dummyMasterStatus.map((e, i) => ({
|
|
||||||
id: i,
|
|
||||||
label: e.label,
|
|
||||||
value: e.value,
|
|
||||||
}))}
|
|
||||||
onButtonPress={handlePress}
|
|
||||||
activeId={activeCategory as any}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper headerComponent={tabsComponent}>
|
<>
|
||||||
{loadingGetData ? (
|
<Event_ScreenStatus />
|
||||||
<LoaderCustom />
|
</>
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item: any, i) => (
|
|
||||||
<BoxWithHeaderSection
|
|
||||||
key={i}
|
|
||||||
href={`/event/${item.id }/${activeCategory}/detail-event`}
|
|
||||||
>
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={8}>
|
|
||||||
<TextCustom truncate bold>
|
|
||||||
{item?.title}
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
|
||||||
<TextCustom>
|
|
||||||
{new Date(item?.tanggal).toLocaleDateString()}
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<TextCustom truncate={2}>{item?.deskripsi}</TextCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</BoxWithHeaderSection>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
LoaderCustom,
|
LoaderCustom,
|
||||||
|
NewWrapper,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
@@ -10,6 +12,7 @@ import {
|
|||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
|
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||||
import {
|
import {
|
||||||
apiEventGetOne,
|
apiEventGetOne,
|
||||||
@@ -48,14 +51,14 @@ export default function EventEdit() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
async function onLoadData() {
|
async function onLoadData() {
|
||||||
try {
|
try {
|
||||||
setIsLoadData(true);
|
setIsLoadData(true);
|
||||||
const response = await apiEventGetOne({ id: id as string });
|
const response = await apiEventGetOne({ id: id as string });
|
||||||
console.log("[DATA BY ID]", JSON.stringify(response, null, 2));
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
setSelectedDate(new Date(response.data.tanggal));
|
setSelectedDate(new Date(response.data.tanggal));
|
||||||
@@ -100,6 +103,15 @@ export default function EventEdit() {
|
|||||||
const startDate = new Date(selectedDate as any);
|
const startDate = new Date(selectedDate as any);
|
||||||
const endDate = new Date(selectedEndDate as any);
|
const endDate = new Date(selectedEndDate as any);
|
||||||
|
|
||||||
|
if (!startDate) {
|
||||||
|
Toast.show({
|
||||||
|
type: "info",
|
||||||
|
text1: "Info",
|
||||||
|
text2: "Tanggal mulai tidak valid",
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (startDate >= endDate) {
|
if (startDate >= endDate) {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "info",
|
type: "info",
|
||||||
@@ -146,7 +158,7 @@ export default function EventEdit() {
|
|||||||
|
|
||||||
const validateDateRange = (
|
const validateDateRange = (
|
||||||
selectedDate: string | Date,
|
selectedDate: string | Date,
|
||||||
selectedEndDate: string | Date
|
selectedEndDate: string | Date,
|
||||||
): { isValid: boolean; error?: string } => {
|
): { isValid: boolean; error?: string } => {
|
||||||
const startDate = new Date(selectedDate);
|
const startDate = new Date(selectedDate);
|
||||||
const endDate = new Date(selectedEndDate);
|
const endDate = new Date(selectedEndDate);
|
||||||
@@ -174,9 +186,19 @@ export default function EventEdit() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
|
footerComponent={
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
isLoading={isLoading}
|
||||||
|
title="Update"
|
||||||
|
onPress={handlerSubmit}
|
||||||
|
/>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
}
|
||||||
|
>
|
||||||
{isLoadData ? (
|
{isLoadData ? (
|
||||||
<LoaderCustom />
|
<ListSkeletonComponent />
|
||||||
) : (
|
) : (
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
@@ -186,26 +208,15 @@ export default function EventEdit() {
|
|||||||
value={data?.title}
|
value={data?.title}
|
||||||
onChangeText={(value) => setData({ ...data, title: value })}
|
onChangeText={(value) => setData({ ...data, title: value })}
|
||||||
/>
|
/>
|
||||||
<SelectCustom
|
<TextAreaCustom
|
||||||
label="Tipe Event"
|
label="Deskripsi"
|
||||||
placeholder="Pilih tipe event"
|
placeholder="Masukkan deskripsi event"
|
||||||
data={listTypeEvent.map((item: any) => ({
|
|
||||||
label: item.name,
|
|
||||||
value: item.id,
|
|
||||||
}))}
|
|
||||||
value={data?.eventMaster_TipeAcaraId || ""}
|
|
||||||
onChange={(value) => {
|
|
||||||
console.log(value);
|
|
||||||
setData({ ...data, eventMaster_TipeAcaraId: value });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<TextInputCustom
|
|
||||||
label="Lokasi"
|
|
||||||
placeholder="Masukkan lokasi event"
|
|
||||||
required
|
required
|
||||||
value={data?.lokasi}
|
showCount
|
||||||
onChangeText={(value) => setData({ ...data, lokasi: value })}
|
value={data?.deskripsi}
|
||||||
|
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DateTimePickerCustom
|
<DateTimePickerCustom
|
||||||
minimumDate={new Date(Date.now())}
|
minimumDate={new Date(Date.now())}
|
||||||
label="Tanggal & Waktu Mulai"
|
label="Tanggal & Waktu Mulai"
|
||||||
@@ -233,7 +244,7 @@ export default function EventEdit() {
|
|||||||
{
|
{
|
||||||
validateDateRange(
|
validateDateRange(
|
||||||
selectedDate as any,
|
selectedDate as any,
|
||||||
selectedEndDate as any
|
selectedEndDate as any,
|
||||||
).error
|
).error
|
||||||
}
|
}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
@@ -242,31 +253,37 @@ export default function EventEdit() {
|
|||||||
{
|
{
|
||||||
validateDateRange(
|
validateDateRange(
|
||||||
selectedDate as any,
|
selectedDate as any,
|
||||||
selectedEndDate as any
|
selectedEndDate as any,
|
||||||
).error
|
).error
|
||||||
}
|
}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
)}
|
)}
|
||||||
<Spacing />
|
{/* <Spacing /> */}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|
||||||
<TextAreaCustom
|
<SelectCustom
|
||||||
label="Deskripsi"
|
label="Tipe Event"
|
||||||
placeholder="Masukkan deskripsi event"
|
placeholder="Pilih tipe event"
|
||||||
required
|
data={listTypeEvent.map((item: any) => ({
|
||||||
showCount
|
label: item.name,
|
||||||
value={data?.deskripsi}
|
value: item.id,
|
||||||
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
}))}
|
||||||
|
value={data?.eventMaster_TipeAcaraId || ""}
|
||||||
|
onChange={(value) => {
|
||||||
|
console.log(value);
|
||||||
|
setData({ ...data, eventMaster_TipeAcaraId: value });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
<TextInputCustom
|
||||||
<ButtonCustom
|
label="Lokasi"
|
||||||
isLoading={isLoading}
|
placeholder="Masukkan lokasi event"
|
||||||
title="Update"
|
required
|
||||||
onPress={handlerSubmit}
|
value={data?.lokasi}
|
||||||
|
onChangeText={(value) => setData({ ...data, lokasi: value })}
|
||||||
/>
|
/>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
)}
|
)}
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default function EventDetailHistory() {
|
|||||||
<DrawerCustom
|
<DrawerCustom
|
||||||
isVisible={openDrawer}
|
isVisible={openDrawer}
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
closeDrawer={() => setOpenDrawer(false)}
|
||||||
height={250}
|
height={"auto"}
|
||||||
>
|
>
|
||||||
<MenuDrawerDynamicGrid
|
<MenuDrawerDynamicGrid
|
||||||
data={menuDrawerPublishEvent({ id: id as string })}
|
data={menuDrawerPublishEvent({ id: id as string })}
|
||||||
|
|||||||
@@ -1,110 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Event_ScreenListOfParticipants from "@/screens/Event/ScreenListOfParticipants";
|
||||||
AvatarUsernameAndOtherComponent,
|
|
||||||
BadgeCustom,
|
|
||||||
BaseBox,
|
|
||||||
LoaderCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import {
|
|
||||||
apiEventGetOne,
|
|
||||||
apiEventListOfParticipants,
|
|
||||||
} from "@/service/api-client/api-event";
|
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
|
||||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
|
|
||||||
export default function EventListOfParticipants() {
|
export default function EventListOfParticipants() {
|
||||||
const { id } = useLocalSearchParams();
|
|
||||||
const [startDate, setStartDate] = useState<Dayjs | undefined>();
|
|
||||||
const [listData, setListData] = useState<any[] | null>(null);
|
|
||||||
const [loadtData, setLoadData] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
handlerLoadData();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlerLoadData = () => {
|
|
||||||
try {
|
|
||||||
onLoadData();
|
|
||||||
onLoadList();
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
const response = await apiEventGetOne({ id: id as string });
|
|
||||||
if (response.success) {
|
|
||||||
const date = dayjs(response.data.tanggal);
|
|
||||||
setStartDate(date);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
const response = await apiEventListOfParticipants({ id: id as string });
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<>
|
||||||
{loadtData && !listData ? (
|
<Event_ScreenListOfParticipants />
|
||||||
<LoaderCustom />
|
</>
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Belum ada peserta
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item: any, index: number) => (
|
|
||||||
<BaseBox key={index}>
|
|
||||||
<AvatarUsernameAndOtherComponent
|
|
||||||
avatar={item?.User?.Profile?.imageId}
|
|
||||||
name={item?.User?.username}
|
|
||||||
avatarHref={`/profile/${item?.User?.Profile?.id}`}
|
|
||||||
rightComponent={
|
|
||||||
startDate && startDate.subtract(1, "hour").diff(dayjs()) < 0 ? (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<BadgeCustom color={item?.isPresent ? "green" : "red"}>
|
|
||||||
{item?.isPresent ? "Hadir" : "Tidak Hadir"}
|
|
||||||
</BadgeCustom>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<BadgeCustom color="gray">-</BadgeCustom>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
AlertDefaultSystem,
|
AlertDefaultSystem,
|
||||||
|
BackButton,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
DotButton,
|
DotButton,
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
LoaderCustom,
|
|
||||||
MenuDrawerDynamicGrid,
|
MenuDrawerDynamicGrid,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||||
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
|
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
|
||||||
@@ -18,23 +19,26 @@ import {
|
|||||||
apiEventGetOne,
|
apiEventGetOne,
|
||||||
apiEventJoin,
|
apiEventJoin,
|
||||||
} from "@/service/api-client/api-event";
|
} from "@/service/api-client/api-event";
|
||||||
|
import dayjs from "dayjs";
|
||||||
import {
|
import {
|
||||||
|
Redirect,
|
||||||
router,
|
router,
|
||||||
Stack,
|
Stack,
|
||||||
useFocusEffect,
|
useFocusEffect,
|
||||||
useLocalSearchParams,
|
useLocalSearchParams,
|
||||||
} from "expo-router";
|
} from "expo-router";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function EventDetailPublish() {
|
export default function EventDetailPublish() {
|
||||||
const { id } = useLocalSearchParams();
|
const now = new Date().toISOString();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
const [isLoadingData, setIsLoadingData] = useState(false);
|
const [isLoadingData, setIsLoadingData] = useState(false);
|
||||||
const [isLoadingJoin, setIsLoadingJoin] = useState(false);
|
const [isLoadingJoin, setIsLoadingJoin] = useState(false);
|
||||||
|
|
||||||
const [data, setData] = useState();
|
const [data, setData] = useState<any>();
|
||||||
const [isParticipant, setIsParticipant] = useState<boolean | null>(null);
|
const [isParticipant, setIsParticipant] = useState<boolean | null>(null);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
@@ -55,8 +59,6 @@ export default function EventDetailPublish() {
|
|||||||
userId: user?.id as string,
|
userId: user?.id as string,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[RES CHECK PARTICIPANTS]", responseCheckParticipants);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
responseCheckParticipants.success &&
|
responseCheckParticipants.success &&
|
||||||
responseCheckParticipants.data
|
responseCheckParticipants.data
|
||||||
@@ -108,7 +110,24 @@ export default function EventDetailPublish() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const footerButton = () => {
|
const isEventFinished =
|
||||||
|
id && data?.tanggalSelesai && dayjs(data.tanggalSelesai).isBefore(now);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isEventFinished) {
|
||||||
|
router.replace(`/(application)/(user)/event/${id}/history`);
|
||||||
|
}
|
||||||
|
}, [isEventFinished, id]);
|
||||||
|
|
||||||
|
if (isEventFinished) {
|
||||||
|
return (
|
||||||
|
<ViewWrapper>
|
||||||
|
<CustomSkeleton />
|
||||||
|
</ViewWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const FooterButton = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
@@ -138,17 +157,17 @@ export default function EventDetailPublish() {
|
|||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{
|
options={{
|
||||||
title: `Event Publish`,
|
title: `Event Publish`,
|
||||||
headerLeft: () => <LeftButtonCustom />,
|
headerLeft: () => <BackButton onPress={() => router.back()} />,
|
||||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
{isLoadingData ? (
|
{isLoadingData ? (
|
||||||
<LoaderCustom />
|
<CustomSkeleton height={400} />
|
||||||
) : (
|
) : (
|
||||||
<Event_BoxDetailPublishSection
|
<Event_BoxDetailPublishSection
|
||||||
data={data}
|
data={data}
|
||||||
footerButton={footerButton()}
|
footerButton={FooterButton()}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import {
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
|
NewWrapper,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
@@ -14,7 +15,7 @@ import { apiEventCreate } from "@/service/api-client/api-event";
|
|||||||
import { apiMasterEventType } from "@/service/api-client/api-master";
|
import { apiMasterEventType } from "@/service/api-client/api-master";
|
||||||
import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
|
import { DateTimePickerEvent } from "@react-native-community/datetimepicker";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import React, { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
interface EventCreateProps {
|
interface EventCreateProps {
|
||||||
@@ -78,23 +79,6 @@ export default function EventCreate() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (selectedDate) {
|
|
||||||
// console.log("Tanggal yang dipilih:", selectedDate);
|
|
||||||
// console.log(`ISO Format ${Platform.OS}:`, selectedDate.toString());
|
|
||||||
|
|
||||||
// // Kirim ke API atau proses lanjutan
|
|
||||||
// } else {
|
|
||||||
// console.log("Tanggal belum dipilih");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (selectedEndDate) {
|
|
||||||
// console.log("Tanggal yang dipilih:", selectedEndDate);
|
|
||||||
// console.log(`ISO Format ${Platform.OS}:`, selectedEndDate.toString());
|
|
||||||
// // Kirim ke API atau proses lanjutan
|
|
||||||
// } else {
|
|
||||||
// console.log("Tanggal berakhir belum dipilih");
|
|
||||||
// }
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
@@ -110,14 +94,13 @@ export default function EventCreate() {
|
|||||||
const response = await apiEventCreate(newData);
|
const response = await apiEventCreate(newData);
|
||||||
console.log("Response", JSON.stringify(response, null, 2));
|
console.log("Response", JSON.stringify(response, null, 2));
|
||||||
|
|
||||||
router.replace("/event/status");
|
router.replace("/event/status?status=review");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const buttonSubmit = (
|
const buttonSubmit = (
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
@@ -129,7 +112,9 @@ export default function EventCreate() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
|
footerComponent={<BoxButtonOnFooter>{buttonSubmit}</BoxButtonOnFooter>}
|
||||||
|
>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
placeholder="Masukkan nama event"
|
placeholder="Masukkan nama event"
|
||||||
@@ -138,24 +123,15 @@ export default function EventCreate() {
|
|||||||
onChangeText={(value: any) => setData({ ...data, title: value })}
|
onChangeText={(value: any) => setData({ ...data, title: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SelectCustom
|
<TextAreaCustom
|
||||||
label="Tipe Event"
|
label="Deskripsi"
|
||||||
placeholder="Pilih tipe event"
|
placeholder="Masukkan deskripsi event"
|
||||||
data={listTypeEvent.map((item: any) => ({
|
|
||||||
label: item.name,
|
|
||||||
value: item.id,
|
|
||||||
}))}
|
|
||||||
value={data?.eventMaster_TipeAcaraId || null}
|
|
||||||
onChange={(value: any) =>
|
|
||||||
setData({ ...data, eventMaster_TipeAcaraId: value })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextInputCustom
|
|
||||||
label="Lokasi"
|
|
||||||
placeholder="Masukkan lokasi event"
|
|
||||||
required
|
required
|
||||||
onChangeText={(value: any) => setData({ ...data, lokasi: value })}
|
showCount
|
||||||
|
value={data?.deskripsi || ""}
|
||||||
|
onChangeText={(value: any) =>
|
||||||
|
setData({ ...data, deskripsi: value })
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DateTimePickerCustom
|
<DateTimePickerCustom
|
||||||
@@ -185,22 +161,28 @@ export default function EventCreate() {
|
|||||||
</TextCustom>
|
</TextCustom>
|
||||||
)}
|
)}
|
||||||
<Spacing />
|
<Spacing />
|
||||||
|
<SelectCustom
|
||||||
|
label="Tipe Event"
|
||||||
|
placeholder="Pilih tipe event"
|
||||||
|
data={listTypeEvent.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}))}
|
||||||
|
value={data?.eventMaster_TipeAcaraId || null}
|
||||||
|
onChange={(value: any) =>
|
||||||
|
setData({ ...data, eventMaster_TipeAcaraId: value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInputCustom
|
||||||
|
label="Lokasi"
|
||||||
|
placeholder="Masukkan lokasi event"
|
||||||
|
required
|
||||||
|
onChangeText={(value: any) => setData({ ...data, lokasi: value })}
|
||||||
|
/>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|
||||||
<TextAreaCustom
|
|
||||||
label="Deskripsi"
|
|
||||||
placeholder="Masukkan deskripsi event"
|
|
||||||
required
|
|
||||||
showCount
|
|
||||||
value={data?.deskripsi || ""}
|
|
||||||
onChangeText={(value: any) =>
|
|
||||||
setData({ ...data, deskripsi: value })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{buttonSubmit}
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,271 +1,11 @@
|
|||||||
import {
|
import DetailForum from "@/screens/Forum/DetailForum";
|
||||||
ButtonCustom,
|
import DetailForum2 from "@/screens/Forum/DetailForum2";
|
||||||
DrawerCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
Spacing,
|
|
||||||
TextAreaCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import AlertWarning from "@/components/Alert/AlertWarning";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import Forum_CommentarBoxSection from "@/screens/Forum/CommentarBoxSection";
|
|
||||||
import Forum_BoxDetailSection from "@/screens/Forum/DiscussionBoxSection";
|
|
||||||
import Forum_MenuDrawerBerandaSection from "@/screens/Forum/MenuDrawerSection.tsx/MenuBeranda";
|
|
||||||
import Forum_MenuDrawerCommentar from "@/screens/Forum/MenuDrawerSection.tsx/MenuCommentar";
|
|
||||||
import {
|
|
||||||
apiForumCreateComment,
|
|
||||||
apiForumGetComment,
|
|
||||||
apiForumGetOne,
|
|
||||||
apiForumUpdateStatus,
|
|
||||||
} from "@/service/api-client/api-forum";
|
|
||||||
import { isBadContent } from "@/utils/badWordsIndonesia";
|
|
||||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useEffect, useState } from "react";
|
|
||||||
import { Alert } from "react-native";
|
|
||||||
|
|
||||||
interface CommentProps {
|
|
||||||
id: string;
|
|
||||||
isActive: boolean;
|
|
||||||
komentar: string;
|
|
||||||
createdAt: Date;
|
|
||||||
authorId: string;
|
|
||||||
Author: {
|
|
||||||
id: string;
|
|
||||||
username: string;
|
|
||||||
Profile: {
|
|
||||||
id: string;
|
|
||||||
imageId: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function ForumDetail() {
|
export default function ForumDetail() {
|
||||||
const { id } = useLocalSearchParams();
|
|
||||||
const { user } = useAuth();
|
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
const [data, setData] = useState<any | null>(null);
|
|
||||||
const [listComment, setListComment] = useState<CommentProps[] | null>(null);
|
|
||||||
const [isLoadingComment, setLoadingComment] = useState(false);
|
|
||||||
|
|
||||||
// Status
|
|
||||||
const [status, setStatus] = useState("");
|
|
||||||
const [text, setText] = useState("");
|
|
||||||
const [authorId, setAuthorId] = useState("");
|
|
||||||
const [dataId, setDataId] = useState("");
|
|
||||||
|
|
||||||
// Comentar
|
|
||||||
const [openDrawerCommentar, setOpenDrawerCommentar] = useState(false);
|
|
||||||
const [commentId, setCommentId] = useState("");
|
|
||||||
const [commentAuthorId, setCommentAuthorId] = useState("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData(id as string);
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async (id: string) => {
|
|
||||||
try {
|
|
||||||
const response = await apiForumGetOne({ id });
|
|
||||||
setData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onLoadListComment(id as string);
|
|
||||||
}, [id]);
|
|
||||||
|
|
||||||
const onLoadListComment = async (id: string) => {
|
|
||||||
try {
|
|
||||||
const response = await apiForumGetComment({
|
|
||||||
id: id as string,
|
|
||||||
});
|
|
||||||
setListComment(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update Status
|
|
||||||
const handlerUpdateStatus = async (value: any) => {
|
|
||||||
try {
|
|
||||||
const response = await apiForumUpdateStatus({
|
|
||||||
id: id as string,
|
|
||||||
data: value,
|
|
||||||
});
|
|
||||||
if (response.success) {
|
|
||||||
setStatus(response.data);
|
|
||||||
setData({
|
|
||||||
...data,
|
|
||||||
ForumMaster_StatusPosting: {
|
|
||||||
status: response.data,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create Commentar
|
|
||||||
const handlerCreateCommentar = async () => {
|
|
||||||
if (isBadContent(text)) {
|
|
||||||
AlertWarning({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const newData = {
|
|
||||||
comment: text,
|
|
||||||
authorId: user?.id,
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
setLoadingComment(true);
|
|
||||||
const response = await apiForumCreateComment({
|
|
||||||
id: id as string,
|
|
||||||
data: newData,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setText("");
|
|
||||||
const newComment = {
|
|
||||||
id: response.data.id,
|
|
||||||
isActive: response.data.isActive,
|
|
||||||
komentar: response.data.komentar,
|
|
||||||
createdAt: response.data.createdAt,
|
|
||||||
authorId: response.data.authorId,
|
|
||||||
Author: response.data.Author,
|
|
||||||
};
|
|
||||||
setListComment((prev) => [newComment, ...(prev || [])]);
|
|
||||||
setData({
|
|
||||||
...data,
|
|
||||||
count: data.count + 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadingComment(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
{/* <DetailForum />; */}
|
||||||
{!data && !listComment ? (
|
<DetailForum2 />
|
||||||
<LoaderCustom />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
{/* Box Posting */}
|
|
||||||
<Forum_BoxDetailSection
|
|
||||||
data={data}
|
|
||||||
onSetData={() => {
|
|
||||||
setOpenDrawer(true);
|
|
||||||
setStatus(data.ForumMaster_StatusPosting?.status);
|
|
||||||
setAuthorId(data.Author?.id);
|
|
||||||
setDataId(data.id);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Area Commentar */}
|
|
||||||
{data?.ForumMaster_StatusPosting?.status === "Open" && (
|
|
||||||
<>
|
|
||||||
<TextAreaCustom
|
|
||||||
placeholder="Ketik diskusi anda..."
|
|
||||||
maxLength={1000}
|
|
||||||
showCount
|
|
||||||
value={text}
|
|
||||||
onChangeText={setText}
|
|
||||||
style={{
|
|
||||||
marginBottom: 0,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ButtonCustom
|
|
||||||
isLoading={isLoadingComment}
|
|
||||||
style={{
|
|
||||||
alignSelf: "flex-end",
|
|
||||||
}}
|
|
||||||
onPress={() => {
|
|
||||||
handlerCreateCommentar();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Balas
|
|
||||||
</ButtonCustom>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Spacing height={40} />
|
|
||||||
|
|
||||||
{/* List Commentar */}
|
|
||||||
{_.isEmpty(listComment) ? (
|
|
||||||
<TextCustom align="center" color="gray" size={"small"}>
|
|
||||||
Tidak ada komentar
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
<TextCustom color="gray">Komentar :</TextCustom>
|
|
||||||
)}
|
|
||||||
<Spacing height={5} />
|
|
||||||
{listComment?.map((item: any, index: number) => (
|
|
||||||
<Forum_CommentarBoxSection
|
|
||||||
key={index}
|
|
||||||
data={item}
|
|
||||||
onSetData={(value) => {
|
|
||||||
setCommentId(value.setCommentId);
|
|
||||||
setOpenDrawerCommentar(value.setOpenDrawer);
|
|
||||||
setCommentAuthorId(value.setCommentAuthorId);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
{/* Posting Drawer */}
|
|
||||||
<DrawerCustom
|
|
||||||
height={"auto"}
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
>
|
|
||||||
<Forum_MenuDrawerBerandaSection
|
|
||||||
id={dataId}
|
|
||||||
authorUsername={data?.Author?.username as string}
|
|
||||||
status={status}
|
|
||||||
setIsDrawerOpen={() => {
|
|
||||||
setOpenDrawer(false);
|
|
||||||
}}
|
|
||||||
authorId={authorId}
|
|
||||||
handlerUpdateStatus={(value: any) => {
|
|
||||||
handlerUpdateStatus(value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
|
|
||||||
{/* Commentar Drawer */}
|
|
||||||
<DrawerCustom
|
|
||||||
height={"auto"}
|
|
||||||
isVisible={openDrawerCommentar}
|
|
||||||
closeDrawer={() => setOpenDrawerCommentar(false)}
|
|
||||||
>
|
|
||||||
<Forum_MenuDrawerCommentar
|
|
||||||
id={commentId as string}
|
|
||||||
commentId={commentId}
|
|
||||||
commentAuthorId={commentAuthorId}
|
|
||||||
setIsDrawerOpen={() => {
|
|
||||||
setOpenDrawerCommentar(false);
|
|
||||||
}}
|
|
||||||
listComment={listComment}
|
|
||||||
setListComment={setListComment}
|
|
||||||
countComment={data?.count}
|
|
||||||
setCountComment={(val: any) => {
|
|
||||||
setData((prev: any) => ({
|
|
||||||
...prev,
|
|
||||||
count: val,
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { apiForumCreateReportCommentar } from "@/service/api-client/api-master";
|
import { apiForumCreateReportCommentar } from "@/service/api-client/api-forum";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { apiForumCreateReportPosting } from "@/service/api-client/api-master";
|
import { apiForumCreateReportPosting } from "@/service/api-client/api-forum";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import {
|
||||||
|
BaseBox,
|
||||||
|
NewWrapper,
|
||||||
|
Spacing,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
} from "@/components";
|
||||||
|
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||||
|
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
||||||
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
|
import { apiForumGetReportComment } from "@/service/api-client/api-forum";
|
||||||
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
|
export default function ForumPreviewReportComment() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
const [listData, setListData] = useState<any | null>(null);
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
// Status
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData(id as string);
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async (id: string) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiForumGetReportComment({ id });
|
||||||
|
setData(response.data);
|
||||||
|
setListData(response?.data?.Forum_ReportKomentar);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NewWrapper>
|
||||||
|
<StackCustom>
|
||||||
|
<TextCustom color="red" bold>
|
||||||
|
Komentar anda telah melanggar aturan forum ! Admin mengambil
|
||||||
|
tindakan untuk menghapus komentar anda!
|
||||||
|
</TextCustom>
|
||||||
|
{loading ? (
|
||||||
|
<CustomSkeleton height={100} />
|
||||||
|
) : (
|
||||||
|
<BaseBox>
|
||||||
|
<TextCustom>"{data?.komentar ? data?.komentar : "-"}"</TextCustom>
|
||||||
|
</BaseBox>
|
||||||
|
)}
|
||||||
|
</StackCustom>
|
||||||
|
|
||||||
|
<Spacing height={10} />
|
||||||
|
<TextCustom bold>Beberapa laporan yang telah diterima</TextCustom>
|
||||||
|
<Spacing height={10} />
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<ListSkeletonComponent />
|
||||||
|
) : _.isEmpty(listData) ? (
|
||||||
|
<NoDataText />
|
||||||
|
) : (
|
||||||
|
listData?.map((e: any, index: number) => (
|
||||||
|
<BaseBox key={index}>
|
||||||
|
{e?.deskripsi ? (
|
||||||
|
<StackCustom gap={"sm"}>
|
||||||
|
<TextCustom bold>Laporan Lainnya</TextCustom>
|
||||||
|
<TextCustom>{e?.deskripsi}</TextCustom>
|
||||||
|
</StackCustom>
|
||||||
|
) : (
|
||||||
|
<StackCustom gap={"sm"}>
|
||||||
|
<TextCustom bold>
|
||||||
|
{e?.ForumMaster_KategoriReport?.title}
|
||||||
|
</TextCustom>
|
||||||
|
<TextCustom>
|
||||||
|
{e?.ForumMaster_KategoriReport?.deskripsi}
|
||||||
|
</TextCustom>
|
||||||
|
</StackCustom>
|
||||||
|
)}
|
||||||
|
</BaseBox>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</NewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import {
|
||||||
|
BaseBox,
|
||||||
|
NewWrapper,
|
||||||
|
Spacing,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
} from "@/components";
|
||||||
|
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||||
|
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
||||||
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
|
import { apiForumGetReportPosting } from "@/service/api-client/api-forum";
|
||||||
|
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
|
||||||
|
export default function ForumPreviewReportPosting() {
|
||||||
|
const { id } = useLocalSearchParams();
|
||||||
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
const [listData, setListData] = useState<any | null>(null);
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
// Status
|
||||||
|
|
||||||
|
useFocusEffect(
|
||||||
|
useCallback(() => {
|
||||||
|
onLoadData(id as string);
|
||||||
|
}, [id])
|
||||||
|
);
|
||||||
|
|
||||||
|
const onLoadData = async (id: string) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiForumGetReportPosting({ id });
|
||||||
|
setData(response.data);
|
||||||
|
setListData(response?.data?.Forum_ReportPosting);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NewWrapper>
|
||||||
|
<StackCustom>
|
||||||
|
<TextCustom color="red" bold>
|
||||||
|
Postingan anda telah melanggar aturan forum ! Admin mengambil
|
||||||
|
tindakan untuk menghapus komentar anda!
|
||||||
|
</TextCustom>
|
||||||
|
{loading ? (
|
||||||
|
<CustomSkeleton height={100} />
|
||||||
|
) : (
|
||||||
|
<BaseBox>
|
||||||
|
<TextCustom>"{data?.diskusi ? data?.diskusi : "-"}"</TextCustom>
|
||||||
|
</BaseBox>
|
||||||
|
)}
|
||||||
|
</StackCustom>
|
||||||
|
|
||||||
|
<Spacing height={10} />
|
||||||
|
<TextCustom bold>Beberapa laporan yang telah diterima</TextCustom>
|
||||||
|
<Spacing height={10} />
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<ListSkeletonComponent />
|
||||||
|
) : _.isEmpty(listData) ? (
|
||||||
|
<NoDataText />
|
||||||
|
) : (
|
||||||
|
listData?.map((e: any) => (
|
||||||
|
<BaseBox key={e?.id}>
|
||||||
|
{e?.deskripsi ? (
|
||||||
|
<StackCustom gap={"sm"}>
|
||||||
|
<TextCustom bold>Laporan Lainnya</TextCustom>
|
||||||
|
<TextCustom>{e?.deskripsi}</TextCustom>
|
||||||
|
</StackCustom>
|
||||||
|
) : (
|
||||||
|
<StackCustom gap={"sm"}>
|
||||||
|
<TextCustom bold>
|
||||||
|
{e?.ForumMaster_KategoriReport?.title}
|
||||||
|
</TextCustom>
|
||||||
|
<TextCustom>
|
||||||
|
{e?.ForumMaster_KategoriReport?.deskripsi}
|
||||||
|
</TextCustom>
|
||||||
|
</StackCustom>
|
||||||
|
)}
|
||||||
|
</BaseBox>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</NewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -8,7 +8,8 @@ import {
|
|||||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
|
import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
|
||||||
import { apiForumCreateReportCommentar, apiMasterForumReportList } from "@/service/api-client/api-master";
|
import { apiForumCreateReportCommentar } from "@/service/api-client/api-forum";
|
||||||
|
import { apiMasterForumReportList } from "@/service/api-client/api-master";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import {
|
|||||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
|
import Forum_ReportListSection from "@/screens/Forum/ReportListSection";
|
||||||
|
import { apiForumCreateReportPosting } from "@/service/api-client/api-forum";
|
||||||
import {
|
import {
|
||||||
apiForumCreateReportPosting,
|
|
||||||
apiMasterForumReportList,
|
apiMasterForumReportList,
|
||||||
} from "@/service/api-client/api-master";
|
} from "@/service/api-client/api-master";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ import {
|
|||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
ViewWrapper
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AlertWarning from "@/components/Alert/AlertWarning";
|
import AlertWarning from "@/components/Alert/AlertWarning";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { apiForumCreate } from "@/service/api-client/api-forum";
|
import { apiForumCreate } from "@/service/api-client/api-forum";
|
||||||
import { isBadContent } from "@/utils/badWordsIndonesia";
|
import { censorText, isBadContent } from "@/utils/badWordsIndonesia";
|
||||||
import { router } from "expo-router";
|
import { router } from "expo-router";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Alert } from "react-native";
|
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function ForumCreate() {
|
export default function ForumCreate() {
|
||||||
@@ -19,16 +18,22 @@ export default function ForumCreate() {
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const handlerSubmit = async () => {
|
const handlerSubmit = async () => {
|
||||||
|
if (text.trim() === "") {
|
||||||
if (isBadContent(text)) {
|
AlertWarning({
|
||||||
AlertWarning({})
|
title: "Lengkapi Data",
|
||||||
|
description: "Postingan tidak boleh kosong",
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bisa di sensor atau return dan tidak bisa di post
|
||||||
|
const cencorContent = censorText(text)
|
||||||
|
|
||||||
const newData = {
|
const newData = {
|
||||||
diskusi: text,
|
diskusi: cencorContent,
|
||||||
authorId: user?.id,
|
authorId: user?.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const response = await apiForumCreate({ data: newData });
|
const response = await apiForumCreate({ data: newData });
|
||||||
@@ -50,6 +55,7 @@ export default function ForumCreate() {
|
|||||||
const buttonFooter = (
|
const buttonFooter = (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
disabled={!text.trim() || isLoading}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
handlerSubmit();
|
handlerSubmit();
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import Forum_ViewBeranda from "@/screens/Forum/ViewBeranda";
|
import Forum_ViewBeranda from "@/screens/Forum/ViewBeranda";
|
||||||
import Forum_ViewBeranda2 from "@/screens/Forum/ViewBeranda2";
|
import Forum_ViewBeranda2 from "@/screens/Forum/ViewBeranda2";
|
||||||
|
import Forum_ViewBeranda3 from "@/screens/Forum/ViewBeranda3";
|
||||||
|
|
||||||
export default function Forum() {
|
export default function Forum() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* <Forum_ViewBeranda /> */}
|
{/* <Forum_ViewBeranda /> */}
|
||||||
<Forum_ViewBeranda2 />
|
{/* <Forum_ViewBeranda2 /> */}
|
||||||
|
<Forum_ViewBeranda3 />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
import { StackCustom, ViewWrapper } from "@/components";
|
import { StackCustom, ViewWrapper } from "@/components";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { useNotificationStore } from "@/hooks/use-notification-store";
|
||||||
import Home_BottomFeatureSection from "@/screens/Home/bottomFeatureSection";
|
import Home_BottomFeatureSection from "@/screens/Home/bottomFeatureSection";
|
||||||
|
import HeaderBell from "@/screens/Home/HeaderBell";
|
||||||
import Home_ImageSection from "@/screens/Home/imageSection";
|
import Home_ImageSection from "@/screens/Home/imageSection";
|
||||||
import TabSection from "@/screens/Home/tabSection";
|
import TabSection from "@/screens/Home/tabSection";
|
||||||
import { tabsHome } from "@/screens/Home/tabsList";
|
import { tabsHome } from "@/screens/Home/tabsList";
|
||||||
@@ -12,21 +14,21 @@ import { apiUser } from "@/service/api-client/api-user";
|
|||||||
import { apiVersion } from "@/service/api-config";
|
import { apiVersion } from "@/service/api-config";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { Redirect, router, Stack, useFocusEffect } from "expo-router";
|
import { Redirect, router, Stack, useFocusEffect } from "expo-router";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { RefreshControl } from "react-native";
|
import { RefreshControl } from "react-native";
|
||||||
|
|
||||||
export default function Application() {
|
export default function Application() {
|
||||||
const { token, user, userData } = useAuth();
|
const { token, user, userData } = useAuth();
|
||||||
const [data, setData] = useState<any>();
|
const [data, setData] = useState<any>();
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
console.log("[User] >>", JSON.stringify(user?.id, null, 2));
|
const { syncUnreadCount } = useNotificationStore();
|
||||||
|
|
||||||
// ‼️ Untuk cek apakah: 1. user ada, 2. user punya profile, 3. accept temrs of forum nya ada atau tidak
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
checkVersion();
|
checkVersion();
|
||||||
userData(token as string);
|
userData(token as string);
|
||||||
|
syncUnreadCount();
|
||||||
}, [user?.id, token])
|
}, [user?.id, token])
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -52,10 +54,10 @@ export default function Application() {
|
|||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (user && user?.termsOfServiceAccepted === false) {
|
// if (user && user?.termsOfServiceAccepted === false) {
|
||||||
console.log("User is not accept term service");
|
// console.log("User is not accept term service");
|
||||||
return <Redirect href={`/terms-agreement`} />;
|
// return <Redirect href={`/terms-agreement`} />;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (data && data?.active === false) {
|
if (data && data?.active === false) {
|
||||||
console.log("User is not active");
|
console.log("User is not active");
|
||||||
@@ -82,17 +84,7 @@ export default function Application() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
headerRight: () => (
|
headerRight: () => <HeaderBell />,
|
||||||
<Ionicons
|
|
||||||
disabled={true}
|
|
||||||
name="notifications"
|
|
||||||
size={20}
|
|
||||||
color={MainColor.placeholder}
|
|
||||||
onPress={() => {
|
|
||||||
router.push("/notifications");
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper
|
<ViewWrapper
|
||||||
@@ -109,6 +101,10 @@ export default function Application() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
|
{/* <ButtonCustom onPress={() => router.push("./test-notifications")}>
|
||||||
|
Test Notif
|
||||||
|
</ButtonCustom> */}
|
||||||
|
|
||||||
<Home_ImageSection />
|
<Home_ImageSection />
|
||||||
|
|
||||||
<Home_FeatureSection />
|
<Home_FeatureSection />
|
||||||
|
|||||||
@@ -1,9 +1,33 @@
|
|||||||
|
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
import { TabsStyles } from "@/styles/tabs-styles";
|
import { TabsStyles } from "@/styles/tabs-styles";
|
||||||
import { Feather, FontAwesome6, Ionicons } from "@expo/vector-icons";
|
import { Feather, FontAwesome6, Ionicons } from "@expo/vector-icons";
|
||||||
import { Tabs } from "expo-router";
|
import { router, Tabs, useLocalSearchParams, useNavigation } from "expo-router";
|
||||||
|
import { useLayoutEffect } from "react";
|
||||||
|
|
||||||
export default function InvestmentTabsLayout() {
|
export default function InvestmentTabsLayout() {
|
||||||
|
// const navigation = useNavigation();
|
||||||
|
|
||||||
|
// const { from, category } = useLocalSearchParams<{
|
||||||
|
// from?: string;
|
||||||
|
// category?: string;
|
||||||
|
// }>();
|
||||||
|
|
||||||
|
// console.log("from", from);
|
||||||
|
// console.log("category", category);
|
||||||
|
|
||||||
|
// // Atur header secara dinamis
|
||||||
|
// useLayoutEffect(() => {
|
||||||
|
// navigation.setOptions({
|
||||||
|
// headerLeft: () => (
|
||||||
|
// <BackButtonFromNotification
|
||||||
|
// from={from as string}
|
||||||
|
// category={category as string}
|
||||||
|
// />
|
||||||
|
// ),
|
||||||
|
// });
|
||||||
|
// }, [from, router, navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs screenOptions={TabsStyles}>
|
<Tabs screenOptions={TabsStyles}>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
|
|||||||
@@ -1,56 +1,9 @@
|
|||||||
import {
|
import Investment_ScreenBursa from "@/screens/Invesment/ScreenBursa";
|
||||||
FloatingButton,
|
|
||||||
LoaderCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
|
||||||
import Investment_BoxBerandaSection from "@/screens/Invesment/BoxBerandaSection";
|
|
||||||
import { apiInvestmentGetAll } from "@/service/api-client/api-investment";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function InvestmentBursa() {
|
export default function InvestmentBursa() {
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadingList, setLoadingList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadList();
|
|
||||||
}, [])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadingList(true);
|
|
||||||
const response = await apiInvestmentGetAll({
|
|
||||||
category: "bursa"
|
|
||||||
});
|
|
||||||
// console.log("[DATA LIST]", JSON.stringify(response.data, null, 2));
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadingList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<>
|
||||||
hideFooter
|
<Investment_ScreenBursa />
|
||||||
floatingButton={
|
</>
|
||||||
<FloatingButton onPress={() => router.push("/investment/create")} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{loadingList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<NoDataText />
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<Investment_BoxBerandaSection id={item.id} data={item} key={index} />
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,102 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Investment_ScreenMyHolding from "@/screens/Invesment/ScreenMyHolding";
|
||||||
BaseBox,
|
|
||||||
Grid,
|
|
||||||
LoaderCustom,
|
|
||||||
ProgressCustom,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import {
|
|
||||||
apiInvestmentGetAll
|
|
||||||
} from "@/service/api-client/api-investment";
|
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import React, { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
|
|
||||||
export default function InvestmentMyHolding() {
|
export default function InvestmentMyHolding() {
|
||||||
const { user } = useAuth();
|
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadingList, setLoadingList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadList();
|
|
||||||
}, [user?.id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadingList(true);
|
|
||||||
const response = await apiInvestmentGetAll({
|
|
||||||
category: "my-holding",
|
|
||||||
authorId: user?.id,
|
|
||||||
});
|
|
||||||
console.log("[DATA LIST]", JSON.stringify(response.data, null, 2));
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadingList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper hideFooter>
|
<>
|
||||||
{loadingList ? (
|
<Investment_ScreenMyHolding />
|
||||||
<LoaderCustom />
|
</>
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<NoDataText />
|
|
||||||
) : (
|
|
||||||
list?.map((item, index) => (
|
|
||||||
<BaseBox
|
|
||||||
key={index}
|
|
||||||
paddingTop={7}
|
|
||||||
paddingBottom={7}
|
|
||||||
onPress={() =>
|
|
||||||
router.push(`/investment/${item?.id}/(my-holding)/${item?.id}`)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={6}>
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<TextCustom truncate={2}>{item?.title}</TextCustom>
|
|
||||||
|
|
||||||
<Spacing height={5} />
|
|
||||||
<TextCustom size="small">
|
|
||||||
Rp. {formatCurrencyDisplay(item?.nominal)}
|
|
||||||
</TextCustom>
|
|
||||||
<TextCustom size="small">
|
|
||||||
{item?.lembarTerbeli} Lembar
|
|
||||||
</TextCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={1}>
|
|
||||||
<View />
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col
|
|
||||||
span={5}
|
|
||||||
style={{
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ProgressCustom
|
|
||||||
value={item?.progress}
|
|
||||||
label={`${item?.progress}%`}
|
|
||||||
size="lg"
|
|
||||||
/>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Investment_ScreenPortofolio from "@/screens/Invesment/ScreenPortofolio";
|
||||||
LoaderCustom,
|
|
||||||
ScrollableCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
|
||||||
import Investment_StatusBox from "@/screens/Invesment/StatusBox";
|
|
||||||
import { apiInvestmentGetByStatus } from "@/service/api-client/api-investment";
|
|
||||||
import { useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function InvestmentPortofolio() {
|
export default function InvestmentPortofolio() {
|
||||||
const { user } = useAuth();
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
|
||||||
"publish"
|
|
||||||
);
|
|
||||||
|
|
||||||
const [listData, setListData] = useState<any[]>([]);
|
|
||||||
const [loadingList, setLoadingList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [user?.id, activeCategory])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadingList(true);
|
|
||||||
const response = await apiInvestmentGetByStatus({
|
|
||||||
authorId: user?.id as string,
|
|
||||||
status: activeCategory as string,
|
|
||||||
});
|
|
||||||
setListData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadingList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
|
||||||
setActiveCategory(item.value);
|
|
||||||
// tambahkan logika lain seperti filter dsb.
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollComponent = (
|
|
||||||
<ScrollableCustom
|
|
||||||
data={dummyMasterStatus.map((e, i) => ({
|
|
||||||
id: i,
|
|
||||||
label: e.label,
|
|
||||||
value: e.value,
|
|
||||||
}))}
|
|
||||||
onButtonPress={handlePress}
|
|
||||||
activeId={activeCategory as any}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
<>
|
||||||
{loadingList ? (
|
<Investment_ScreenPortofolio />
|
||||||
<LoaderCustom />
|
</>
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item: any, index: number) => (
|
|
||||||
<Investment_StatusBox
|
|
||||||
key={index}
|
|
||||||
data={item}
|
|
||||||
status={activeCategory as string}
|
|
||||||
href={`/investment/${item.id}/${activeCategory}/detail`}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,124 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Investment_ScreenTransaction from "@/screens/Invesment/ScreenTransaction";
|
||||||
BadgeCustom,
|
|
||||||
BaseBox,
|
|
||||||
Grid,
|
|
||||||
LoaderCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { apiInvestmentGetInvoice } from "@/service/api-client/api-investment";
|
|
||||||
import { GStyles } from "@/styles/global-styles";
|
|
||||||
import { formatChatTime } from "@/utils/formatChatTime";
|
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
|
|
||||||
export default function InvestmentTransaction() {
|
export default function InvestmentTransaction() {
|
||||||
const { user } = useAuth();
|
|
||||||
const [list, setList] = useState<any>([]);
|
|
||||||
const [loadList, setLoadList] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadList();
|
|
||||||
}, [user?.id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiInvestmentGetInvoice({
|
|
||||||
authorId: user?.id as string,
|
|
||||||
category: "transaction",
|
|
||||||
});
|
|
||||||
console.log("[RESPONSE LIST]", JSON.stringify(response.data, null, 2));
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlerColor = (status: string) => {
|
|
||||||
if (status === "menunggu") {
|
|
||||||
return "orange";
|
|
||||||
} else if (status === "proses") {
|
|
||||||
return "white";
|
|
||||||
} else if (status === "berhasil") {
|
|
||||||
return "green";
|
|
||||||
} else if (status === "gagal") {
|
|
||||||
return "red";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePress = ({ id, status }: { id: string; status: string }) => {
|
|
||||||
if (status === "menunggu") {
|
|
||||||
router.push(`/investment/${id}/(transaction-flow)/invoice`);
|
|
||||||
} else if (status === "proses") {
|
|
||||||
router.push(`/investment/${id}/(transaction-flow)/process`);
|
|
||||||
} else if (status === "berhasil") {
|
|
||||||
router.push(`/investment/${id}/(transaction-flow)/success`);
|
|
||||||
} else if (status === "gagal") {
|
|
||||||
router.push(`/investment/${id}/(transaction-flow)/failed`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper hideFooter>
|
<>
|
||||||
{loadList ? (
|
<Investment_ScreenTransaction />
|
||||||
<LoaderCustom />
|
</>
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<NoDataText/>
|
|
||||||
) : (
|
|
||||||
list.map((item: any, i: number) => (
|
|
||||||
<BaseBox
|
|
||||||
key={i}
|
|
||||||
paddingTop={7}
|
|
||||||
paddingBottom={7}
|
|
||||||
onPress={() => {
|
|
||||||
handlePress({
|
|
||||||
id: item.id,
|
|
||||||
status: _.lowerCase(item.statusInvoice),
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={6}>
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<TextCustom truncate>{item?.title || "-"}</TextCustom>
|
|
||||||
<TextCustom color="gray" size="small">
|
|
||||||
{formatChatTime(item?.createdAt)}
|
|
||||||
</TextCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={1}>
|
|
||||||
<View />
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={5} style={{ alignItems: "flex-end" }}>
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
Rp. {formatCurrencyDisplay(item?.nominal) || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
<BadgeCustom
|
|
||||||
variant="light"
|
|
||||||
color={handlerColor(_.lowerCase(item.statusInvoice))}
|
|
||||||
style={GStyles.alignSelfFlexEnd}
|
|
||||||
>
|
|
||||||
{item?.statusInvoice || "-"}
|
|
||||||
</BadgeCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { LoaderCustom, TextCustom, ViewWrapper } from "@/components";
|
import Investment_ScreenListOfDocument from "@/screens/Invesment/Document/ScreenListDocument";
|
||||||
import Investment_BoxDetailDocument from "@/screens/Invesment/Document/RecapBoxDetail";
|
|
||||||
import { apiInvestmentGetDocument } from "@/service/api-client/api-investment";
|
|
||||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function InvestmentListOfDocument() {
|
export default function InvestmentListOfDocument() {
|
||||||
const { id } = useLocalSearchParams();
|
|
||||||
console.log("ID >> ", id);
|
|
||||||
|
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadListDocument();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadListDocument = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiInvestmentGetDocument({
|
|
||||||
id: id as string,
|
|
||||||
category: "all-document",
|
|
||||||
});
|
|
||||||
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setList([]);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<>
|
||||||
{loadList ? (
|
<Investment_ScreenListOfDocument />
|
||||||
<LoaderCustom />
|
</>
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Tidak ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<Investment_BoxDetailDocument
|
|
||||||
key={index}
|
|
||||||
title={item.title}
|
|
||||||
href={`/(file)/${item.fileId}`}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,213 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Investment_ScreenRecapOfDocument from "@/screens/Invesment/Document/ScreenRecapOfDocument";
|
||||||
AlertDefaultSystem,
|
|
||||||
BackButton,
|
|
||||||
DotButton,
|
|
||||||
DrawerCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
MenuDrawerDynamicGrid,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconEdit } from "@/components/_Icon";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
|
||||||
import Investment_BoxDetailDocument from "@/screens/Invesment/Document/RecapBoxDetail";
|
|
||||||
import {
|
|
||||||
apiInvestmentDeleteDocument,
|
|
||||||
apiInvestmentGetDocument,
|
|
||||||
} from "@/service/api-client/api-investment";
|
|
||||||
import { AntDesign, Ionicons } from "@expo/vector-icons";
|
|
||||||
import {
|
|
||||||
router,
|
|
||||||
Stack,
|
|
||||||
useFocusEffect,
|
|
||||||
useLocalSearchParams,
|
|
||||||
} from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
|
|
||||||
export default function InvestmentRecapOfDocument() {
|
export default function InvestmentRecapOfDocument() {
|
||||||
const { id } = useLocalSearchParams();
|
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
const [openDrawerBox, setOpenDrawerBox] = useState(false);
|
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
const [selectId, setSelectId] = useState<string | null>(null);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadListDocument();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadListDocument = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiInvestmentGetDocument({
|
|
||||||
id: id as string,
|
|
||||||
category: "all-document",
|
|
||||||
});
|
|
||||||
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setList([]);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlerDeleteDocument = async () => {
|
|
||||||
try {
|
|
||||||
const response = await apiInvestmentDeleteDocument({
|
|
||||||
id: selectId as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
Toast.show({
|
|
||||||
type: "success",
|
|
||||||
text1: "Data berhasil dihapus",
|
|
||||||
});
|
|
||||||
setList((prev: any[] | null) => {
|
|
||||||
if (!prev) return null;
|
|
||||||
return prev.filter((item: any) => item.id !== selectId);
|
|
||||||
});
|
|
||||||
setOpenDrawerBox(false);
|
|
||||||
setSelectId(null);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Gagal menghapus data",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Investment_ScreenRecapOfDocument />
|
||||||
options={{
|
|
||||||
title: "Rekap Dokumen",
|
|
||||||
headerLeft: () => <BackButton />,
|
|
||||||
headerRight: () => (
|
|
||||||
<DotButton
|
|
||||||
onPress={() => {
|
|
||||||
setOpenDrawer(true);
|
|
||||||
setOpenDrawerBox(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ViewWrapper>
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Tidak ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<Investment_BoxDetailDocument
|
|
||||||
key={index}
|
|
||||||
title={item.title}
|
|
||||||
leftIcon={
|
|
||||||
<Ionicons
|
|
||||||
name="ellipsis-horizontal-outline"
|
|
||||||
size={ICON_SIZE_SMALL}
|
|
||||||
color={MainColor.white}
|
|
||||||
style={{
|
|
||||||
zIndex: 10,
|
|
||||||
alignSelf: "flex-end",
|
|
||||||
}}
|
|
||||||
onPress={() => {
|
|
||||||
setSelectId(item.id);
|
|
||||||
setOpenDrawerBox(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
href={`/(file)/${item.fileId}`}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
{/* Drawer On Header */}
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<MenuDrawerDynamicGrid
|
|
||||||
data={[
|
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<AntDesign
|
|
||||||
name="plus-circle"
|
|
||||||
size={ICON_SIZE_SMALL}
|
|
||||||
color={MainColor.white}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
label: "Tambah Dokumen",
|
|
||||||
path: `/investment/${id}/(document)/add-document`,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressItem={(item) => {
|
|
||||||
router.push(item.path as any);
|
|
||||||
setOpenDrawer(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
|
|
||||||
{/* Drawer On Box */}
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawerBox}
|
|
||||||
closeDrawer={() => setOpenDrawerBox(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<MenuDrawerDynamicGrid
|
|
||||||
data={[
|
|
||||||
{
|
|
||||||
icon: <IconEdit />,
|
|
||||||
label: "Edit Dokumen",
|
|
||||||
path: `/investment/${selectId}/(document)/edit-document`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: (
|
|
||||||
<Ionicons
|
|
||||||
name="trash-outline"
|
|
||||||
size={ICON_SIZE_SMALL}
|
|
||||||
color={MainColor.white}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
label: "Hapus Dokumen",
|
|
||||||
path: "" as any,
|
|
||||||
color: MainColor.red,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressItem={(item) => {
|
|
||||||
if (item.path === ("" as any)) {
|
|
||||||
AlertDefaultSystem({
|
|
||||||
title: "Hapus Dokumen",
|
|
||||||
message: "Apakah anda yakin ingin menghapus dokumen ini?",
|
|
||||||
textLeft: "Batal",
|
|
||||||
textRight: "Hapus",
|
|
||||||
onPressRight: () => {
|
|
||||||
handlerDeleteDocument();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
router.push(item.path as any);
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpenDrawerBox(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,11 @@ export default function InvestmentAddNews() {
|
|||||||
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ButtonCustom isLoading={isLoading} onPress={handlerSubmit}>
|
<ButtonCustom
|
||||||
|
disabled={!data.title || !data.deskripsi || isLoading}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={handlerSubmit}
|
||||||
|
>
|
||||||
Simpan
|
Simpan
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|||||||
@@ -1,100 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import Investment_ScreenListOfNews from "@/screens/Invesment/News/ScreenListOfNews";
|
||||||
import {
|
import { useLocalSearchParams } from "expo-router";
|
||||||
BackButton,
|
|
||||||
BaseBox,
|
|
||||||
DrawerCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
MenuDrawerDynamicGrid,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconPlus } from "@/components/_Icon";
|
|
||||||
import { apiInvestmentGetNews } from "@/service/api-client/api-investment";
|
|
||||||
import {
|
|
||||||
router,
|
|
||||||
Stack,
|
|
||||||
useFocusEffect,
|
|
||||||
useLocalSearchParams,
|
|
||||||
} from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function InvestmentListOfNews() {
|
export default function InvestmentListOfNews() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadList();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiInvestmentGetNews({
|
|
||||||
id: id as string,
|
|
||||||
category: "all-news",
|
|
||||||
});
|
|
||||||
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Investment_ScreenListOfNews investmentId={id as string} />
|
||||||
<Stack.Screen
|
|
||||||
options={{
|
|
||||||
title: "Daftar Berita",
|
|
||||||
headerLeft: () => <BackButton />,
|
|
||||||
// headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ViewWrapper>
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Tidak ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<BaseBox
|
|
||||||
key={index}
|
|
||||||
paddingBlock={5}
|
|
||||||
href={`/investment/[id]/(news)/${item.id}`}
|
|
||||||
>
|
|
||||||
<TextCustom bold>{item.title}</TextCustom>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<MenuDrawerDynamicGrid
|
|
||||||
data={[
|
|
||||||
{
|
|
||||||
label: "Tambah Berita",
|
|
||||||
path: `/investment/${id}/add-news`,
|
|
||||||
icon: <IconPlus />,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressItem={(item) => {
|
|
||||||
router.push(item.path as any);
|
|
||||||
setOpenDrawer(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,101 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import Investment_ScreenRecapOfNews from "@/screens/Invesment/News/ScreenRecapOfNews";
|
||||||
import {
|
import { useLocalSearchParams } from "expo-router";
|
||||||
BackButton,
|
|
||||||
BaseBox,
|
|
||||||
DotButton,
|
|
||||||
DrawerCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
MenuDrawerDynamicGrid,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconPlus } from "@/components/_Icon";
|
|
||||||
import { apiInvestmentGetNews } from "@/service/api-client/api-investment";
|
|
||||||
import {
|
|
||||||
router,
|
|
||||||
Stack,
|
|
||||||
useFocusEffect,
|
|
||||||
useLocalSearchParams,
|
|
||||||
} from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function InvestmentRecapOfNews() {
|
export default function InvestmentRecapOfNews() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadList();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiInvestmentGetNews({
|
|
||||||
id: id as string,
|
|
||||||
category: "all-news",
|
|
||||||
});
|
|
||||||
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Investment_ScreenRecapOfNews investmentId={id as string} />
|
||||||
<Stack.Screen
|
|
||||||
options={{
|
|
||||||
title: "Rekap Berita",
|
|
||||||
headerLeft: () => <BackButton />,
|
|
||||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ViewWrapper>
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Tidak ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<BaseBox
|
|
||||||
key={index}
|
|
||||||
paddingBlock={5}
|
|
||||||
href={`/investment/[id]/(news)/${item.id}`}
|
|
||||||
>
|
|
||||||
<TextCustom bold>{item.title}</TextCustom>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<MenuDrawerDynamicGrid
|
|
||||||
data={[
|
|
||||||
{
|
|
||||||
label: "Tambah Berita",
|
|
||||||
path: `/investment/${id}/add-news`,
|
|
||||||
icon: <IconPlus />,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressItem={(item) => {
|
|
||||||
router.push(item.path as any);
|
|
||||||
setOpenDrawer(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,239 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Investment_ScreenInvoice from "@/screens/Invesment/ScreenInvoice";
|
||||||
BaseBox,
|
|
||||||
ButtonCenteredOnly,
|
|
||||||
ButtonCustom,
|
|
||||||
Grid,
|
|
||||||
InformationBox,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import CopyButton from "@/components/Button/CoyButton";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
|
||||||
import {
|
|
||||||
apiInvestmentGetInvoice,
|
|
||||||
apiInvestmentUpdateInvoice,
|
|
||||||
} from "@/service/api-client/api-investment";
|
|
||||||
import { uploadFileService } from "@/service/upload-service";
|
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
|
||||||
import pickFile, { IFileData } from "@/utils/pickFile";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
|
|
||||||
export default function InvestmentInvoice() {
|
export default function InvestmentInvoice() {
|
||||||
const { id } = useLocalSearchParams();
|
|
||||||
console.log("[ID]", id);
|
|
||||||
const [data, setData] = useState<any>({});
|
|
||||||
const [image, setImage] = useState<IFileData>({
|
|
||||||
name: "",
|
|
||||||
uri: "",
|
|
||||||
size: 0,
|
|
||||||
});
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
const response = await apiInvestmentGetInvoice({
|
|
||||||
id: id as string,
|
|
||||||
category: "invoice",
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("[RES INVOICE]", JSON.stringify(response.data, null, 2));
|
|
||||||
setData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlerSubmitUpdate = async () => {
|
|
||||||
try {
|
|
||||||
setIsLoading(true);
|
|
||||||
const responseUploadImage = await uploadFileService({
|
|
||||||
dirId: DIRECTORY_ID.investasi_bukti_transfer,
|
|
||||||
imageUri: image?.uri,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("[RESPONSE UPLOAD IMAGE]", responseUploadImage);
|
|
||||||
|
|
||||||
if (!responseUploadImage?.data?.id) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Gagal mengunggah bukti transfer",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await apiInvestmentUpdateInvoice({
|
|
||||||
id: id as string,
|
|
||||||
data: {
|
|
||||||
imageId: responseUploadImage?.data?.id,
|
|
||||||
},
|
|
||||||
status: "proses",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
console.log(
|
|
||||||
"[RESPONSE UPDATE]",
|
|
||||||
JSON.stringify(response.data, null, 2)
|
|
||||||
);
|
|
||||||
Toast.show({
|
|
||||||
type: "success",
|
|
||||||
text1: "Berhasil mengunggah bukti transfer",
|
|
||||||
});
|
|
||||||
router.push(`/investment/${id}/(transaction-flow)/process`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
<Investment_ScreenInvoice />
|
||||||
<StackCustom>
|
|
||||||
<InformationBox text="Mohon transfer ke rekening dibawah" />
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={4}>
|
|
||||||
<TextCustom>Bank</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={8}>
|
|
||||||
<TextCustom>{data?.MasterBank?.namaBank}</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
<Spacing height={10} />
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={4}>
|
|
||||||
<TextCustom>Nama Akun</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={8}>
|
|
||||||
<TextCustom>{data?.MasterBank?.namaAkun}</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<BaseBox backgroundColor={MainColor.soft_darkblue}>
|
|
||||||
<Grid containerStyle={{ justifyContent: "center" }}>
|
|
||||||
<Grid.Col
|
|
||||||
span={8}
|
|
||||||
style={{
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextCustom size="xlarge" bold color="yellow">
|
|
||||||
{data?.MasterBank?.norek}
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col
|
|
||||||
span={4}
|
|
||||||
style={{
|
|
||||||
alignItems: "flex-end",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CopyButton textToCopy={data?.MasterBank?.norek} />
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<TextCustom>Jumlah Transaksi</TextCustom>
|
|
||||||
|
|
||||||
<Spacing height={10} />
|
|
||||||
|
|
||||||
<BaseBox backgroundColor={MainColor.soft_darkblue}>
|
|
||||||
<Grid containerStyle={{ justifyContent: "center" }}>
|
|
||||||
<Grid.Col
|
|
||||||
span={8}
|
|
||||||
style={{
|
|
||||||
justifyContent: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextCustom size="xlarge" bold color="yellow">
|
|
||||||
Rp. {formatCurrencyDisplay(data?.nominal)}
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col
|
|
||||||
span={4}
|
|
||||||
style={{
|
|
||||||
alignItems: "flex-end",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CopyButton textToCopy={data?.nominal} />
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom>
|
|
||||||
<TextCustom align="center">
|
|
||||||
Upload bukti transfer anda.
|
|
||||||
</TextCustom>
|
|
||||||
{image ? (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
gap: 10,
|
|
||||||
paddingInline: 20,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TextCustom bold align="center" truncate>
|
|
||||||
{image?.name}
|
|
||||||
</TextCustom>
|
|
||||||
</View>
|
|
||||||
) : (
|
|
||||||
<TextCustom align="center">
|
|
||||||
Tidak ada gambar yang diunggah
|
|
||||||
</TextCustom>
|
|
||||||
)}
|
|
||||||
<ButtonCenteredOnly
|
|
||||||
onPress={() => {
|
|
||||||
pickFile({
|
|
||||||
allowedType: "image",
|
|
||||||
setImageUri(file: any) {
|
|
||||||
console.log("[IMAGE]", file);
|
|
||||||
setImage(file);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
icon="upload"
|
|
||||||
>
|
|
||||||
Upload
|
|
||||||
</ButtonCenteredOnly>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
<ButtonCustom
|
|
||||||
isLoading={isLoading}
|
|
||||||
disabled={!image}
|
|
||||||
onPress={() => {
|
|
||||||
handlerSubmitUpdate();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Saya Sudah Transfer
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
|
||||||
<Spacing />
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ export default function InvestmentSelectBank() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
console.log("[RESPONSE >>]", response);
|
|
||||||
const invoiceId = response.data.id;
|
const invoiceId = response.data.id;
|
||||||
|
|
||||||
const delStorage = await AsyncStorage.removeItem(
|
const delStorage = await AsyncStorage.removeItem(
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import Investment_ButtonInvestasiSection from "@/screens/Invesment/ButtonInvesta
|
|||||||
import Invesment_ComponentBoxOnBottomDetail from "@/screens/Invesment/ComponentBoxOnBottomDetail";
|
import Invesment_ComponentBoxOnBottomDetail from "@/screens/Invesment/ComponentBoxOnBottomDetail";
|
||||||
import Invesment_DetailDataPublishSection from "@/screens/Invesment/DetailDataPublishSection";
|
import Invesment_DetailDataPublishSection from "@/screens/Invesment/DetailDataPublishSection";
|
||||||
import { apiInvestmentGetOne } from "@/service/api-client/api-investment";
|
import { apiInvestmentGetOne } from "@/service/api-client/api-investment";
|
||||||
|
import { countDownAndCondition } from "@/utils/countDownAndCondition";
|
||||||
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
|
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
|
||||||
import {
|
import {
|
||||||
router,
|
router,
|
||||||
@@ -23,7 +24,7 @@ import {
|
|||||||
useLocalSearchParams,
|
useLocalSearchParams,
|
||||||
} from "expo-router";
|
} from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
export default function InvestmentDetailStatus() {
|
export default function InvestmentDetailStatus() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
@@ -63,6 +64,28 @@ export default function InvestmentDetailStatus() {
|
|||||||
setOpenDrawerPublish(false);
|
setOpenDrawerPublish(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [value, setValue] = useState({
|
||||||
|
sisa: 0,
|
||||||
|
reminder: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateCountDown();
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
|
||||||
|
const updateCountDown = () => {
|
||||||
|
const countDown = countDownAndCondition({
|
||||||
|
duration: data?.MasterPencarianInvestor.name,
|
||||||
|
publishTime: data?.countDown,
|
||||||
|
});
|
||||||
|
|
||||||
|
setValue({
|
||||||
|
sisa: countDown.durationDay,
|
||||||
|
reminder: countDown.reminder,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const bottomSection = (
|
const bottomSection = (
|
||||||
<Invesment_ComponentBoxOnBottomDetail
|
<Invesment_ComponentBoxOnBottomDetail
|
||||||
id={data?.id}
|
id={data?.id}
|
||||||
@@ -72,7 +95,11 @@ export default function InvestmentDetailStatus() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const buttonSection = (
|
const buttonSection = (
|
||||||
<Investment_ButtonInvestasiSection id={id as string} isMine={user?.id === data?.author?.id} />
|
<Investment_ButtonInvestasiSection
|
||||||
|
id={id as string}
|
||||||
|
isMine={user?.id === data?.author?.id}
|
||||||
|
reminder={value.reminder}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
LoaderCustom,
|
LoaderCustom,
|
||||||
|
NewWrapper,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextInputCustom,
|
TextInputCustom
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
@@ -18,10 +19,7 @@ import {
|
|||||||
apiInvestmentUpdateData,
|
apiInvestmentUpdateData,
|
||||||
} from "@/service/api-client/api-investment";
|
} from "@/service/api-client/api-investment";
|
||||||
import { apiMasterInvestment } from "@/service/api-client/api-master";
|
import { apiMasterInvestment } from "@/service/api-client/api-master";
|
||||||
import {
|
import { deleteFileService, uploadFileService } from "@/service/upload-service";
|
||||||
deleteFileService,
|
|
||||||
uploadFileService,
|
|
||||||
} from "@/service/upload-service";
|
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
import pickFile from "@/utils/pickFile";
|
import pickFile from "@/utils/pickFile";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
@@ -70,7 +68,7 @@ export default function InvestmentEdit() {
|
|||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadMaster();
|
onLoadMaster();
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadMaster = async () => {
|
const onLoadMaster = async () => {
|
||||||
@@ -178,7 +176,7 @@ export default function InvestmentEdit() {
|
|||||||
const responseUpdate = await apiInvestmentUpdateData({
|
const responseUpdate = await apiInvestmentUpdateData({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
data: newData,
|
data: newData,
|
||||||
category: "data"
|
category: "data",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (responseUpdate.success) {
|
if (responseUpdate.success) {
|
||||||
@@ -201,7 +199,15 @@ export default function InvestmentEdit() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
|
footerComponent={
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom isLoading={isLoading} onPress={handleSubmitUpdate}>
|
||||||
|
Simpan
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
}
|
||||||
|
>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="Gambar investasi bisa berupa ilustrasi, poster atau foto terkait investasi." />
|
<InformationBox text="Gambar investasi bisa berupa ilustrasi, poster atau foto terkait investasi." />
|
||||||
<LandscapeFrameUploaded
|
<LandscapeFrameUploaded
|
||||||
@@ -256,6 +262,8 @@ export default function InvestmentEdit() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
|
iconLeft="Rp."
|
||||||
|
// disabled
|
||||||
required
|
required
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
label="Total Lembar"
|
label="Total Lembar"
|
||||||
@@ -341,11 +349,7 @@ export default function InvestmentEdit() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom isLoading={isLoading} onPress={handleSubmitUpdate}>
|
|
||||||
Simpan
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
<Spacing height={50} />
|
</NewWrapper>
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,30 +63,27 @@ export default function InvestmentDetail() {
|
|||||||
setOpenDrawerPublish(false);
|
setOpenDrawerPublish(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const [value, setValue] = useState({
|
const [value, setValue] = useState({
|
||||||
sisa: 0,
|
sisa: 0,
|
||||||
reminder: false,
|
reminder: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateCountDown();
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
|
||||||
|
const updateCountDown = () => {
|
||||||
|
const countDown = countDownAndCondition({
|
||||||
|
duration: data?.MasterPencarianInvestor.name,
|
||||||
|
publishTime: data?.countDown,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
updateCountDown();
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
console.log("[DATA DETAIL]", JSON.stringify(data, null, 2));
|
|
||||||
|
|
||||||
const updateCountDown = () => {
|
|
||||||
const countDown = countDownAndCondition({
|
|
||||||
duration: data?.MasterPencarianInvestor.name,
|
|
||||||
publishTime: data?.countDown,
|
|
||||||
});
|
|
||||||
|
|
||||||
setValue({
|
|
||||||
sisa: countDown.durationDay,
|
|
||||||
reminder: countDown.reminder,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
setValue({
|
||||||
|
sisa: countDown.durationDay,
|
||||||
|
reminder: countDown.reminder,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const bottomSection = (
|
const bottomSection = (
|
||||||
<Invesment_ComponentBoxOnBottomDetail
|
<Invesment_ComponentBoxOnBottomDetail
|
||||||
@@ -97,7 +94,11 @@ export default function InvestmentDetail() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const buttonSection = (
|
const buttonSection = (
|
||||||
<Investment_ButtonInvestasiSection id={id as string} isMine={user?.id === data?.author?.id} reminder={value.reminder} />
|
<Investment_ButtonInvestasiSection
|
||||||
|
id={id as string}
|
||||||
|
isMine={user?.id === data?.author?.id}
|
||||||
|
reminder={value.reminder}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,67 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import Investment_ScreenInvestor from "@/screens/Invesment/ScreenInvestor";
|
||||||
import {
|
import { useLocalSearchParams } from "expo-router";
|
||||||
AvatarUsernameAndOtherComponent,
|
|
||||||
BoxWithHeaderSection,
|
|
||||||
LoaderCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
|
||||||
import { apiInvestmentGetInvestorById } from "@/service/api-client/api-investment";
|
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
|
||||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function InvestmentInvestor() {
|
export default function InvestmentInvestor() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadingList, setLoadingList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadList();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadingList(true);
|
|
||||||
const response = await apiInvestmentGetInvestorById({
|
|
||||||
id: id as string,
|
|
||||||
})
|
|
||||||
console.log("[DATA LIST]", JSON.stringify(response.data, null, 2));
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadingList(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Investment_ScreenInvestor investmentId={id as string} />
|
||||||
<ViewWrapper>
|
|
||||||
{loadingList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<NoDataText />
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<BoxWithHeaderSection key={index}>
|
|
||||||
<AvatarUsernameAndOtherComponent
|
|
||||||
avatar={item?.Author?.Profile?.imageId}
|
|
||||||
name={item?.Author?.username}
|
|
||||||
avatarHref={`/profile/${item?.Author?.Profile?.id}`}
|
|
||||||
/>
|
|
||||||
<TextCustom bold>
|
|
||||||
Rp. {formatCurrencyDisplay(item?.nominal)}
|
|
||||||
</TextCustom>
|
|
||||||
</BoxWithHeaderSection>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
CenterCustom,
|
CenterCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
LoaderCustom,
|
NewWrapper,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
@@ -54,7 +55,7 @@ export default function InvestmentCreate() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadMaster();
|
onLoadMaster();
|
||||||
}, [])
|
}, []),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadMaster = async () => {
|
const onLoadMaster = async () => {
|
||||||
@@ -167,7 +168,7 @@ export default function InvestmentCreate() {
|
|||||||
text1: "Berhasil",
|
text1: "Berhasil",
|
||||||
text2: response.message,
|
text2: response.message,
|
||||||
});
|
});
|
||||||
router.replace("/investment/portofolio");
|
router.replace("/investment/portofolio?status=review");
|
||||||
} else {
|
} else {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "error",
|
type: "error",
|
||||||
@@ -184,7 +185,19 @@ export default function InvestmentCreate() {
|
|||||||
|
|
||||||
// const [coba, setCoba] = useState("");
|
// const [coba, setCoba] = useState("");
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
|
footerComponent={
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
disabled={isLoading}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={() => handleSubmit()}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
}
|
||||||
|
>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="Gambar investasi bisa berupa ilustrasi, poster atau foto terkait investasi." />
|
<InformationBox text="Gambar investasi bisa berupa ilustrasi, poster atau foto terkait investasi." />
|
||||||
<LandscapeFrameUploaded image={image as string} />
|
<LandscapeFrameUploaded image={image as string} />
|
||||||
@@ -224,7 +237,6 @@ export default function InvestmentCreate() {
|
|||||||
onPress={() => {
|
onPress={() => {
|
||||||
pickFile({
|
pickFile({
|
||||||
setPdfUri: ({ uri, name, size }) => {
|
setPdfUri: ({ uri, name, size }) => {
|
||||||
|
|
||||||
setPdf({ uri, name, size });
|
setPdf({ uri, name, size });
|
||||||
},
|
},
|
||||||
allowedType: "pdf",
|
allowedType: "pdf",
|
||||||
@@ -265,6 +277,9 @@ export default function InvestmentCreate() {
|
|||||||
|
|
||||||
<StackCustom gap={0}>
|
<StackCustom gap={0}>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
|
iconLeft="Rp."
|
||||||
|
// disabled
|
||||||
|
editable={false}
|
||||||
required
|
required
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
label="Total Lembar"
|
label="Total Lembar"
|
||||||
@@ -291,7 +306,7 @@ export default function InvestmentCreate() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{loadingMaster ? (
|
{loadingMaster ? (
|
||||||
<LoaderCustom />
|
<CustomSkeleton height={50} />
|
||||||
) : (
|
) : (
|
||||||
<SelectCustom
|
<SelectCustom
|
||||||
required
|
required
|
||||||
@@ -313,7 +328,7 @@ export default function InvestmentCreate() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{loadingMaster ? (
|
{loadingMaster ? (
|
||||||
<LoaderCustom />
|
<CustomSkeleton height={50} />
|
||||||
) : (
|
) : (
|
||||||
<SelectCustom
|
<SelectCustom
|
||||||
required
|
required
|
||||||
@@ -335,7 +350,7 @@ export default function InvestmentCreate() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{loadingMaster ? (
|
{loadingMaster ? (
|
||||||
<LoaderCustom />
|
<CustomSkeleton height={50} />
|
||||||
) : (
|
) : (
|
||||||
<SelectCustom
|
<SelectCustom
|
||||||
required
|
required
|
||||||
@@ -357,11 +372,8 @@ export default function InvestmentCreate() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom isLoading={isLoading} onPress={() => handleSubmit()}>
|
|
||||||
Simpan
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
<Spacing height={50} />
|
{/* <Spacing height={50} /> */}
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,61 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import { BackButton } from "@/components";
|
||||||
import { IconHome, IconStatus } from "@/components/_Icon";
|
import { IconHome, IconStatus } from "@/components/_Icon";
|
||||||
|
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
|
||||||
import { TabsStyles } from "@/styles/tabs-styles";
|
import { TabsStyles } from "@/styles/tabs-styles";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { Tabs } from "expo-router";
|
import {
|
||||||
|
router,
|
||||||
|
Tabs,
|
||||||
|
useLocalSearchParams,
|
||||||
|
useNavigation
|
||||||
|
} from "expo-router";
|
||||||
|
import { useLayoutEffect } from "react";
|
||||||
|
|
||||||
export default function JobTabsLayout() {
|
export default function JobTabsLayout() {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
const { from, category } = useLocalSearchParams<{
|
||||||
|
from?: string;
|
||||||
|
category?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// Atur header secara dinamis
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
|
headerLeft: () => (
|
||||||
|
<BackButtonFromNotification from={from as string} category={category as string} />
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}, [from, router, navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs screenOptions={TabsStyles}>
|
<>
|
||||||
<Tabs.Screen
|
<Tabs screenOptions={TabsStyles}>
|
||||||
name="index"
|
<Tabs.Screen
|
||||||
options={{
|
name="index"
|
||||||
title: "Beranda",
|
options={{
|
||||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
title: "Beranda",
|
||||||
}}
|
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||||
/>
|
}}
|
||||||
<Tabs.Screen
|
/>
|
||||||
name="status"
|
<Tabs.Screen
|
||||||
options={{
|
name="status"
|
||||||
title: "Status",
|
options={{
|
||||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
title: "Status",
|
||||||
}}
|
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||||
/>
|
}}
|
||||||
<Tabs.Screen
|
/>
|
||||||
name="archive"
|
<Tabs.Screen
|
||||||
options={{
|
name="archive"
|
||||||
title: "Arsip",
|
options={{
|
||||||
tabBarIcon: ({ color }) => (
|
title: "Arsip",
|
||||||
<Ionicons size={20} name="archive" color={color} />
|
tabBarIcon: ({ color }) => (
|
||||||
),
|
<Ionicons size={20} name="archive" color={color} />
|
||||||
}}
|
),
|
||||||
/>
|
}}
|
||||||
</Tabs>
|
/>
|
||||||
|
</Tabs>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { BaseBox, LoaderCustom, TextCustom, ViewWrapper } from "@/components";
|
import Job_ScreenArchive2 from "@/screens/Job/ScreenArchive2";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { apiJobGetAll } from "@/service/api-client/api-job";
|
|
||||||
import { useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function JobArchive() {
|
export default function JobArchive() {
|
||||||
const { user } = useAuth();
|
|
||||||
const [listData, setListData] = useState<any[]>([]);
|
|
||||||
const [isLoadData, setIsLoadData] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [user?.id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setIsLoadData(true);
|
|
||||||
const response = await apiJobGetAll({
|
|
||||||
category: "archive",
|
|
||||||
authorId: user?.id,
|
|
||||||
});
|
|
||||||
setListData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setIsLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper hideFooter>
|
<>
|
||||||
{isLoadData ? (
|
<Job_ScreenArchive2 />
|
||||||
<LoaderCustom />
|
</>
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Anda tidak memiliki arsip</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item, index) => (
|
|
||||||
<BaseBox
|
|
||||||
key={index}
|
|
||||||
paddingTop={20}
|
|
||||||
paddingBottom={20}
|
|
||||||
href={`/job/${item.id}/archive`}
|
|
||||||
>
|
|
||||||
<TextCustom align="center" bold truncate size="large">
|
|
||||||
{item?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +1,10 @@
|
|||||||
import {
|
import Job_ScreenBeranda from "@/screens/Job/ScreenBeranda";
|
||||||
AvatarUsernameAndOtherComponent,
|
import Job_ScreenBeranda2 from "@/screens/Job/ScreenBeranda2";
|
||||||
BoxWithHeaderSection,
|
|
||||||
FloatingButton,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { apiJobGetAll } from "@/service/api-client/api-job";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function JobBeranda() {
|
export default function JobBeranda() {
|
||||||
const [listData, setListData] = useState<any[]>([]);
|
|
||||||
const [isLoadData, setIsLoadData] = useState(false);
|
|
||||||
const [search, setSearch] = useState("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData(search);
|
|
||||||
}, [search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async (search: string) => {
|
|
||||||
try {
|
|
||||||
setIsLoadData(true);
|
|
||||||
const response = await apiJobGetAll({ search, category: "beranda" });
|
|
||||||
setListData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setIsLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSearch = (search: string) => {
|
|
||||||
setSearch(search);
|
|
||||||
onLoadData(search);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<>
|
||||||
hideFooter
|
<Job_ScreenBeranda2 />
|
||||||
floatingButton={
|
</>
|
||||||
<FloatingButton onPress={() => router.push("/job/create")} />
|
|
||||||
}
|
|
||||||
headerComponent={
|
|
||||||
<SearchInput placeholder="Cari pekerjaan" onChangeText={handleSearch} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{isLoadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Belum ada lowongan</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item, index) => (
|
|
||||||
<BoxWithHeaderSection
|
|
||||||
key={index}
|
|
||||||
onPress={() => router.push(`/job/${item.id}`)}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<AvatarUsernameAndOtherComponent
|
|
||||||
avatar={item?.Author?.Profile?.imageId}
|
|
||||||
avatarHref={`/profile/${item?.Author?.Profile?.id}`}
|
|
||||||
name={item?.Author?.username}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextCustom truncate={2} align="center" bold size="large">
|
|
||||||
{item?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
</StackCustom>
|
|
||||||
<Spacing />
|
|
||||||
</BoxWithHeaderSection>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
<Spacing />
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,84 +1,12 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Job_MainViewStatus from "@/screens/Job/MainViewStatus";
|
||||||
BaseBox,
|
import Job_MainViewStatus2 from "@/screens/Job/MainViewStatus2";
|
||||||
LoaderCustom,
|
|
||||||
ScrollableCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
|
||||||
import { apiJobGetByStatus } from "@/service/api-client/api-job";
|
|
||||||
import { useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function JobStatus() {
|
export default function JobStatus() {
|
||||||
const { user } = useAuth();
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
|
||||||
"publish"
|
|
||||||
);
|
|
||||||
const [listData, setListData] = useState<any[]>([]);
|
|
||||||
const [isLoadList, setIsLoadList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [user?.id, activeCategory])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setIsLoadList(true);
|
|
||||||
const response = await apiJobGetByStatus({
|
|
||||||
authorId: user?.id as string,
|
|
||||||
status: activeCategory as string,
|
|
||||||
});
|
|
||||||
setListData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setIsLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
|
||||||
setActiveCategory(item.value);
|
|
||||||
// tambahkan logika lain seperti filter dsb.
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollComponent = (
|
|
||||||
<ScrollableCustom
|
|
||||||
data={dummyMasterStatus.map((e, i) => ({
|
|
||||||
id: i,
|
|
||||||
label: e.label,
|
|
||||||
value: e.value,
|
|
||||||
}))}
|
|
||||||
onButtonPress={handlePress}
|
|
||||||
activeId={activeCategory as any}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
<>
|
||||||
{isLoadList ? (
|
{/* <Job_MainViewStatus /> */}
|
||||||
<LoaderCustom />
|
<Job_MainViewStatus2 />
|
||||||
) : _.isEmpty(listData) ? (
|
</>
|
||||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((e, i) => (
|
|
||||||
<BaseBox
|
|
||||||
key={i}
|
|
||||||
paddingTop={20}
|
|
||||||
paddingBottom={20}
|
|
||||||
href={`/job/${e?.id}/${activeCategory}/detail`}
|
|
||||||
>
|
|
||||||
<TextCustom align="center" bold truncate size="large">
|
|
||||||
{e?.title}
|
|
||||||
</TextCustom>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default function JobDetailStatus() {
|
|||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
{data &&
|
{data &&
|
||||||
data?.catatan &&
|
data?.catatan &&
|
||||||
(status === "draft" || status === "rejected") && (
|
(status === "draft" || status === "reject") && (
|
||||||
<ReportBox text={data?.catatan} />
|
<ReportBox text={data?.catatan} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export default function JobDetail() {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const response = await apiJobGetOne({ id: id as string });
|
const response = await apiJobGetOne({ id: id as string });
|
||||||
|
|
||||||
|
console.log("DATA", JSON.stringify(response.data, null,2));
|
||||||
|
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ERROR]", error);
|
console.log("[ERROR]", error);
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import {
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
|
NewWrapper,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
TextInputCustom,
|
TextInputCustom
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
@@ -19,7 +20,7 @@ import { useState } from "react";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function JobCreate() {
|
export default function JobCreate() {
|
||||||
const nextUrl = "/(application)/(user)/job/(tabs)/status";
|
const nextUrl = "/(application)/(user)/job/(tabs)/status?status=review";
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [image, setImage] = useState<string | null>(null);
|
const [image, setImage] = useState<string | null>(null);
|
||||||
@@ -99,16 +100,17 @@ export default function JobCreate() {
|
|||||||
const buttonSubmit = () => {
|
const buttonSubmit = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ButtonCustom isLoading={isLoading} onPress={() => handlerOnSubmit()}>
|
<BoxButtonOnFooter>
|
||||||
Simpan
|
<ButtonCustom isLoading={isLoading} onPress={() => handlerOnSubmit()}>
|
||||||
</ButtonCustom>
|
Simpan
|
||||||
<Spacing />
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<NewWrapper footerComponent={buttonSubmit()}>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="Poster atau gambar lowongan kerja bersifat opsional, tidak wajib untuk dimasukkan dan upload lah gambar yang sesuai dengan deskripsi lowongan kerja." />
|
<InformationBox text="Poster atau gambar lowongan kerja bersifat opsional, tidak wajib untuk dimasukkan dan upload lah gambar yang sesuai dengan deskripsi lowongan kerja." />
|
||||||
|
|
||||||
@@ -160,9 +162,7 @@ export default function JobCreate() {
|
|||||||
value={data.deskripsi}
|
value={data.deskripsi}
|
||||||
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
onChangeText={(value) => setData({ ...data, deskripsi: value })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{buttonSubmit()}
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,111 +1,11 @@
|
|||||||
import {
|
import ScreenNotification_V1 from "@/screens/Notification/ScreenNotification_V1";
|
||||||
BaseBox,
|
import ScreenNotification_V2 from "@/screens/Notification/ScreenNotification_V2";
|
||||||
Grid,
|
|
||||||
ScrollableCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
|
|
||||||
const categories = [
|
export default function Notification() {
|
||||||
{ value: "all", label: "Semua" },
|
|
||||||
{ value: "event", label: "Event" },
|
|
||||||
{ value: "job", label: "Job" },
|
|
||||||
{ value: "voting", label: "Voting" },
|
|
||||||
{ value: "donasi", label: "Donasi" },
|
|
||||||
{ value: "investasi", label: "Investasi" },
|
|
||||||
{ value: "forum", label: "Forum" },
|
|
||||||
{ value: "collaboration", label: "Collaboration" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const selectedCategory = (value: string) => {
|
|
||||||
const category = categories.find((c) => c.value === value);
|
|
||||||
return category?.label;
|
|
||||||
};
|
|
||||||
|
|
||||||
const BoxNotification = ({
|
|
||||||
index,
|
|
||||||
activeCategory,
|
|
||||||
}: {
|
|
||||||
index: number;
|
|
||||||
activeCategory: string | null;
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BaseBox
|
<ScreenNotification_V2 />
|
||||||
onPress={() =>
|
{/* <ScreenNotification_V1 /> */}
|
||||||
console.log(
|
|
||||||
"Notification >",
|
|
||||||
selectedCategory(activeCategory as string)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<TextCustom bold>
|
|
||||||
# {selectedCategory(activeCategory as string)}
|
|
||||||
</TextCustom>
|
|
||||||
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
borderBottomColor: MainColor.white_gray,
|
|
||||||
borderBottomWidth: 1,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sint odio
|
|
||||||
unde quidem voluptate quam culpa sequi molestias ipsa corrupti id,
|
|
||||||
soluta, nostrum adipisci similique, et illo asperiores deleniti eum
|
|
||||||
labore.
|
|
||||||
</TextCustom>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={6}>
|
|
||||||
<TextCustom size="small" color="gray">
|
|
||||||
{index + 1} Agustus 2025
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={6} style={{ alignItems: "flex-end" }}>
|
|
||||||
<TextCustom size="small" color="gray">
|
|
||||||
Belum lihat
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
export default function Notifications() {
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>("all");
|
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
|
||||||
setActiveCategory(item.value);
|
|
||||||
// tambahkan logika lain seperti filter dsb.
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={
|
|
||||||
<ScrollableCustom
|
|
||||||
data={categories.map((e, i) => ({
|
|
||||||
id: i,
|
|
||||||
label: e.label,
|
|
||||||
value: e.value,
|
|
||||||
}))}
|
|
||||||
onButtonPress={handlePress}
|
|
||||||
activeId={activeCategory as string}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{Array.from({ length: 20 }).map((e, i) => (
|
|
||||||
<View key={i}>
|
|
||||||
<BoxNotification index={i} activeCategory={activeCategory as any} />
|
|
||||||
</View>
|
|
||||||
))}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
CenterCustom,
|
CenterCustom,
|
||||||
Grid,
|
Grid,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
|
NewWrapper,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
@@ -120,7 +121,7 @@ export default function PortofolioCreate() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<NewWrapper
|
||||||
footerComponent={
|
footerComponent={
|
||||||
<Portofolio_ButtonCreate
|
<Portofolio_ButtonCreate
|
||||||
id={id as string}
|
id={id as string}
|
||||||
@@ -357,8 +358,8 @@ export default function PortofolioCreate() {
|
|||||||
setDataMedsos({ ...dataMedsos, youtube: value })
|
setDataMedsos({ ...dataMedsos, youtube: value })
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Spacing />
|
{/* <Spacing /> */}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,15 @@ import {
|
|||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
CenterCustom,
|
CenterCustom,
|
||||||
|
NewWrapper,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
|
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||||
import {
|
import {
|
||||||
@@ -238,7 +239,7 @@ export default function PortofolioEdit() {
|
|||||||
return !dataArray.some(
|
return !dataArray.some(
|
||||||
(item: any) =>
|
(item: any) =>
|
||||||
!item.MasterSubBidangBisnis.id ||
|
!item.MasterSubBidangBisnis.id ||
|
||||||
item.MasterSubBidangBisnis.id.trim() === ""
|
item.MasterSubBidangBisnis.id.trim() === "",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,16 +320,16 @@ export default function PortofolioEdit() {
|
|||||||
if (!bidangBisnis || !subBidangBisnis) {
|
if (!bidangBisnis || !subBidangBisnis) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
<NewWrapper>
|
||||||
<ActivityIndicator size="large" color={MainColor.yellow} />
|
<ListSkeletonComponent height={80} />
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper footerComponent={buttonUpdate}>
|
<NewWrapper footerComponent={buttonUpdate}>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
required
|
required
|
||||||
@@ -471,7 +472,7 @@ export default function PortofolioEdit() {
|
|||||||
/>
|
/>
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,9 @@
|
|||||||
import { TextCustom, ViewWrapper } from "@/components";
|
import ViewListPortofolio from "@/screens/Portofolio/ViewListPortofolio";
|
||||||
import Portofolio_BoxView from "@/screens/Portofolio/BoxPortofolioView";
|
|
||||||
import { apiGetPortofolio } from "@/service/api-client/api-portofolio";
|
|
||||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function ListPortofolio() {
|
export default function ListPortofolio() {
|
||||||
const { id } = useLocalSearchParams();
|
|
||||||
const [data, setData] = useState<any[]>([]);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadPortofolio(id as string);
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadPortofolio = async (id: string) => {
|
|
||||||
const response = await apiGetPortofolio({ id: id });
|
|
||||||
setData(response.data);
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<>
|
||||||
{data ? data?.map((item: any, index: number) => (
|
<ViewListPortofolio />
|
||||||
<Portofolio_BoxView key={index} data={item} />
|
</>
|
||||||
)) : <TextCustom>Tidak ada portofolio</TextCustom>}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export default function ProfileDetailBlocked() {
|
|||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const response = await apiGetBlockedById({ id: String(id) });
|
const response = await apiGetBlockedById({ id: String(id) });
|
||||||
// console.log("[RESPONSE >>]", JSON.stringify(response, null, 2));
|
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,9 @@ const ButtonnDot = ({
|
|||||||
isUserCheck: boolean;
|
isUserCheck: boolean;
|
||||||
logout: () => Promise<void>;
|
logout: () => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
const isId = id === undefined || id === null;
|
console.log("[ID] >>", id);
|
||||||
|
|
||||||
|
const isId = id === undefined || id === "undefined";
|
||||||
|
|
||||||
if (isId) {
|
if (isId) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default function ProfileLayout() {
|
|||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
name="[id]/blocked-list"
|
name="[id]/blocked-list"
|
||||||
options={{ title: "Blocked List", headerLeft: () => <BackButton /> }}
|
options={{ title: "Daftar Blokir", headerLeft: () => <BackButton /> }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|||||||
75
app/(application)/(user)/test-notifications.tsx
Normal file
75
app/(application)/(user)/test-notifications.tsx
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import {
|
||||||
|
ButtonCustom,
|
||||||
|
NewWrapper,
|
||||||
|
StackCustom,
|
||||||
|
TextInputCustom,
|
||||||
|
} from "@/components";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { apiNotificationsSend } from "@/service/api-notifications";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
|
export default function TestNotification() {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const [data, setData] = useState("");
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
console.log("[Data Dikirim]", data);
|
||||||
|
setLoading(true);
|
||||||
|
const response = await apiNotificationsSend({
|
||||||
|
data: {
|
||||||
|
title: "Test Notification !!",
|
||||||
|
body: data,
|
||||||
|
userLoginId: user?.id || "",
|
||||||
|
appId: "hipmi",
|
||||||
|
status: "publish",
|
||||||
|
kategoriApp: "JOB",
|
||||||
|
type: "announcement",
|
||||||
|
deepLink: "/job/cmhjz8u3h0005cfaxezyeilrr",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
console.log("[RES SEND NOTIF]", JSON.stringify(response, null, 2));
|
||||||
|
Toast.show({
|
||||||
|
type: "success",
|
||||||
|
text1: "Notifikasi berhasil dikirim",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengirim notifikasi",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR SEND NOTIF]", error);
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal mengirim notifikasi",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NewWrapper>
|
||||||
|
<StackCustom>
|
||||||
|
<TextInputCustom
|
||||||
|
required
|
||||||
|
label="Pesan"
|
||||||
|
placeholder="Masukkan pesan"
|
||||||
|
value={data}
|
||||||
|
onChangeText={(text) => setData(text)}
|
||||||
|
/>
|
||||||
|
<ButtonCustom onPress={handleSubmit} disabled={loading}>
|
||||||
|
Kirim
|
||||||
|
</ButtonCustom>
|
||||||
|
</StackCustom>
|
||||||
|
</NewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,115 +1,11 @@
|
|||||||
import {
|
import UserSearchMainView from "@/screens/UserSeach/MainView";
|
||||||
AvatarComp,
|
import UserSearchMainView_V2 from "@/screens/UserSeach/MainView_V2";
|
||||||
ClickableCustom,
|
|
||||||
Grid,
|
|
||||||
LoaderCustom,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
TextInputCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
|
||||||
import { apiAllUser } from "@/service/api-client/api-user";
|
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
import { router } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
|
|
||||||
export default function UserSearch() {
|
export default function UserSearch() {
|
||||||
const [data, setData] = useState<any[]>([]);
|
|
||||||
const [search, setSearch] = useState<string>("");
|
|
||||||
const [isLoadList, setIsLoadList] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onLoadData(search);
|
|
||||||
}, [search]);
|
|
||||||
|
|
||||||
const onLoadData = async (search: string) => {
|
|
||||||
try {
|
|
||||||
setIsLoadList(true);
|
|
||||||
const response = await apiAllUser({ search: search });
|
|
||||||
console.log("[DATA USER] >", JSON.stringify(response.data, null, 2));
|
|
||||||
setData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error fetching data", error);
|
|
||||||
} finally {
|
|
||||||
setIsLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSearch = (search: string) => {
|
|
||||||
setSearch(search);
|
|
||||||
onLoadData(search);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper
|
{/* <UserSearchMainView /> */}
|
||||||
headerComponent={
|
<UserSearchMainView_V2 />
|
||||||
<TextInputCustom
|
|
||||||
value={search}
|
|
||||||
onChangeText={handleSearch}
|
|
||||||
iconLeft={
|
|
||||||
<Ionicons
|
|
||||||
name="search"
|
|
||||||
size={ICON_SIZE_SMALL}
|
|
||||||
color={MainColor.placeholder}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
placeholder="Cari Pengguna"
|
|
||||||
borderRadius={50}
|
|
||||||
containerStyle={{ marginBottom: 0 }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
{isLoadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : !_.isEmpty(data) ? (
|
|
||||||
data?.map((e, index) => {
|
|
||||||
return (
|
|
||||||
<ClickableCustom
|
|
||||||
key={index}
|
|
||||||
onPress={() => {
|
|
||||||
console.log("Ke Profile");
|
|
||||||
router.push(`/profile/${e?.Profile?.id}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={2}>
|
|
||||||
<AvatarComp fileId={e?.Profile?.imageId} size="base" />
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={9}>
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<TextCustom size="large">{e?.username}</TextCustom>
|
|
||||||
<TextCustom size="small">+{e?.nomor}</TextCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col
|
|
||||||
span={1}
|
|
||||||
style={{
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "flex-end",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Ionicons
|
|
||||||
name="chevron-forward"
|
|
||||||
size={ICON_SIZE_SMALL}
|
|
||||||
color={MainColor.white}
|
|
||||||
/>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</ClickableCustom>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<TextCustom align="center">Tidak ditemukan</TextCustom>
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
<Spacing height={50} />
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,37 @@
|
|||||||
import {
|
import {
|
||||||
IconContribution,
|
IconContribution,
|
||||||
IconHistory,
|
IconHistory,
|
||||||
IconHome,
|
IconHome,
|
||||||
IconStatus,
|
IconStatus,
|
||||||
} from "@/components/_Icon";
|
} from "@/components/_Icon";
|
||||||
|
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
|
||||||
import { TabsStyles } from "@/styles/tabs-styles";
|
import { TabsStyles } from "@/styles/tabs-styles";
|
||||||
import { Tabs } from "expo-router";
|
import { Tabs, useLocalSearchParams, useNavigation, router } from "expo-router";
|
||||||
|
import { useLayoutEffect } from "react";
|
||||||
|
|
||||||
export default function VotingTabsLayout() {
|
export default function VotingTabsLayout() {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
const { from, category } = useLocalSearchParams<{
|
||||||
|
from?: string;
|
||||||
|
category?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
console.log("from", from);
|
||||||
|
console.log("category", category);
|
||||||
|
|
||||||
|
// Atur header secara dinamis
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
navigation.setOptions({
|
||||||
|
headerLeft: () => (
|
||||||
|
<BackButtonFromNotification
|
||||||
|
from={from as string}
|
||||||
|
category={category as string}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}, [from, router, navigation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs screenOptions={TabsStyles}>
|
<Tabs screenOptions={TabsStyles}>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
|
|||||||
@@ -1,59 +1,6 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Voting_ScreenContribution from "@/screens/Voting/ScreenContribution";
|
||||||
LoaderCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
|
||||||
import { apiVotingGetAll } from "@/service/api-client/api-voting";
|
|
||||||
import { useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useState, useCallback } from "react";
|
|
||||||
|
|
||||||
export default function VotingContribution() {
|
export default function VotingContribution() {
|
||||||
const { user } = useAuth();
|
return <Voting_ScreenContribution />;
|
||||||
const [listData, setListData] = useState<any>([]);
|
|
||||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
|
||||||
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadingGetData(true);
|
|
||||||
const response = await apiVotingGetAll({
|
|
||||||
category: "contribution",
|
|
||||||
authorId: user?.id as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadingGetData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ViewWrapper hideFooter>
|
|
||||||
{loadingGetData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Tidak ada kontribusi</TextCustom>
|
|
||||||
) : listData.map((item: any, index: number) => (
|
|
||||||
<Voting_BoxPublishSection
|
|
||||||
data={item}
|
|
||||||
key={index}
|
|
||||||
href={`/voting/${item.id}/contribution`}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { LoaderCustom, TextCustom, ViewWrapper } from "@/components";
|
import Voting_ScreenHistory from "@/screens/Voting/ScreenHistory";
|
||||||
import TabsTwoButtonCustom from "@/components/_ShareComponent/TabsTwoHeaderCustom";
|
|
||||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { apiVotingGetAll } from "@/service/api-client/api-voting";
|
|
||||||
import { useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
|
|
||||||
export default function VotingHistory() {
|
export default function VotingHistory() {
|
||||||
const { user } = useAuth();
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>("all");
|
|
||||||
|
|
||||||
const [listData, setListData] = useState<any>([]);
|
|
||||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [activeCategory])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadingGetData(true);
|
|
||||||
const response = await apiVotingGetAll({
|
|
||||||
category: activeCategory === "all" ? "all-history" : "my-history",
|
|
||||||
authorId: user?.id as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadingGetData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
|
||||||
setActiveCategory(item);
|
|
||||||
// tambahkan logika lain seperti filter dsb.
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<>
|
||||||
hideFooter
|
<Voting_ScreenHistory />
|
||||||
headerComponent={
|
</>
|
||||||
<TabsTwoButtonCustom
|
|
||||||
leftValue="all"
|
|
||||||
rightValue="main"
|
|
||||||
leftText="Semua Riwayat"
|
|
||||||
rightText="Riwayat Saya"
|
|
||||||
activeCategory={activeCategory}
|
|
||||||
handlePress={handlePress}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{loadingGetData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Tidak ada riwayat</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item: any, index: number) => (
|
|
||||||
<Voting_BoxPublishSection
|
|
||||||
key={index}
|
|
||||||
id={item.id}
|
|
||||||
data={item}
|
|
||||||
href={`/voting/${item.id}/history`}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Voting_ScreenBeranda from "@/screens/Voting/ScreenBeranda";
|
||||||
FloatingButton,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import Voting_BoxPublishSection from "@/screens/Voting/BoxPublishSection";
|
|
||||||
import { apiVotingGetAll } from "@/service/api-client/api-voting";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function VotingBeranda() {
|
export default function VotingBeranda() {
|
||||||
const { user } = useAuth();
|
|
||||||
const [listData, setListData] = useState<any>([]);
|
|
||||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
|
||||||
const [search, setSearch] = useState("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadingGetData(true);
|
|
||||||
const response = await apiVotingGetAll({
|
|
||||||
search,
|
|
||||||
category: "beranda",
|
|
||||||
userLoginId: user?.id,
|
|
||||||
});
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadingGetData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<>
|
||||||
hideFooter
|
<Voting_ScreenBeranda />
|
||||||
floatingButton={
|
</>
|
||||||
<FloatingButton onPress={() => router.push("/voting/create")} />
|
|
||||||
}
|
|
||||||
headerComponent={
|
|
||||||
<SearchInput placeholder="Cari voting" onChangeText={setSearch} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{loadingGetData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Tidak ada data</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item: any, index: number) => (
|
|
||||||
<Voting_BoxPublishSection
|
|
||||||
data={item}
|
|
||||||
key={index}
|
|
||||||
href={`/voting/${item.id}`}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,98 +1,10 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Voting_ScreenStatus from "@/screens/Voting/ScreenStatus";
|
||||||
BadgeCustom,
|
|
||||||
BaseBox,
|
|
||||||
LoaderCustom,
|
|
||||||
ScrollableCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { dummyMasterStatus } from "@/lib/dummy-data/_master/status";
|
|
||||||
import { apiVotingGetByStatus } from "@/service/api-client/api-voting";
|
|
||||||
import { dateTimeView } from "@/utils/dateTimeView";
|
|
||||||
import { useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function VotingStatus() {
|
export default function VotingStatus() {
|
||||||
const { user } = useAuth();
|
|
||||||
const id = user?.id || "";
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
|
||||||
"publish"
|
|
||||||
);
|
|
||||||
|
|
||||||
const [listData, setListData] = useState([]);
|
|
||||||
const [loadingGetData, setLoadingGetData] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [activeCategory, id])
|
|
||||||
);
|
|
||||||
|
|
||||||
async function onLoadData() {
|
|
||||||
try {
|
|
||||||
setLoadingGetData(true);
|
|
||||||
const response = await apiVotingGetByStatus({
|
|
||||||
id: id as string,
|
|
||||||
status: activeCategory!,
|
|
||||||
});
|
|
||||||
setListData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
setLoadingGetData(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
|
||||||
setActiveCategory(item.value);
|
|
||||||
// tambahkan logika lain seperti filter dsb.
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollComponent = (
|
|
||||||
<ScrollableCustom
|
|
||||||
data={dummyMasterStatus.map((e, i) => ({
|
|
||||||
id: i,
|
|
||||||
label: e.label,
|
|
||||||
value: e.value,
|
|
||||||
}))}
|
|
||||||
onButtonPress={handlePress}
|
|
||||||
activeId={activeCategory as any}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper headerComponent={scrollComponent} hideFooter>
|
<>
|
||||||
{loadingGetData ? (
|
<Voting_ScreenStatus />
|
||||||
<LoaderCustom />
|
</>
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Tidak ada data {activeCategory}</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item: any, i: number) => (
|
|
||||||
<BaseBox
|
|
||||||
key={i}
|
|
||||||
paddingTop={20}
|
|
||||||
paddingBottom={20}
|
|
||||||
href={`/voting/${item.id}/${activeCategory}/detail`}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<TextCustom align="center" bold truncate={2} size="large">
|
|
||||||
{item?.title || ""}
|
|
||||||
</TextCustom>
|
|
||||||
<BadgeCustom
|
|
||||||
style={{ width: "70%", alignSelf: "center" }}
|
|
||||||
variant="light"
|
|
||||||
>
|
|
||||||
{item?.awalVote && dateTimeView({date: item?.awalVote, withoutTime: true})} -{" "}
|
|
||||||
{item?.akhirVote && dateTimeView({date: item?.akhirVote, withoutTime: true})}
|
|
||||||
</BadgeCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,6 @@ export default function VotingDetailStatus() {
|
|||||||
setLoadingGetData(true);
|
setLoadingGetData(true);
|
||||||
const response = await apiVotingGetOne({ id: id as string });
|
const response = await apiVotingGetOne({ id: id as string });
|
||||||
|
|
||||||
console.log("[DATA BY ID]", JSON.stringify(response, null, 2));
|
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ import {
|
|||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
CenterCustom,
|
CenterCustom,
|
||||||
LoaderCustom,
|
LoaderCustom,
|
||||||
|
NewWrapper,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
TextInputCustom,
|
TextInputCustom
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
|
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||||
@@ -34,7 +35,7 @@ interface IEditData {
|
|||||||
Voting_DaftarNamaVote?: [
|
Voting_DaftarNamaVote?: [
|
||||||
{
|
{
|
||||||
value?: string;
|
value?: string;
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +48,7 @@ export default function VotingEdit() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -188,9 +189,9 @@ export default function VotingEdit() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper footerComponent={buttonSubmit()}>
|
<NewWrapper footerComponent={buttonSubmit()}>
|
||||||
{loadingGetData ? (
|
{loadingGetData ? (
|
||||||
<LoaderCustom />
|
<ListSkeletonComponent />
|
||||||
) : (
|
) : (
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
@@ -210,7 +211,7 @@ export default function VotingEdit() {
|
|||||||
onChangeText={(text) => setData({ ...data, deskripsi: text })}
|
onChangeText={(text) => setData({ ...data, deskripsi: text })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
|
||||||
|
|
||||||
<DateTimePickerCustom
|
<DateTimePickerCustom
|
||||||
minimumDate={new Date(Date.now())}
|
minimumDate={new Date(Date.now())}
|
||||||
@@ -255,7 +256,7 @@ export default function VotingEdit() {
|
|||||||
}
|
}
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
)}
|
)}
|
||||||
<Spacing />
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|
||||||
{data?.Voting_DaftarNamaVote?.map((item: any, index: number) => (
|
{data?.Voting_DaftarNamaVote?.map((item: any, index: number) => (
|
||||||
@@ -270,7 +271,7 @@ export default function VotingEdit() {
|
|||||||
...(data as any),
|
...(data as any),
|
||||||
Voting_DaftarNamaVote: data?.Voting_DaftarNamaVote?.map(
|
Voting_DaftarNamaVote: data?.Voting_DaftarNamaVote?.map(
|
||||||
(item: any, i: any) =>
|
(item: any, i: any) =>
|
||||||
i === index ? { ...item, value } : item
|
i === index ? { ...item, value } : item,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -327,6 +328,6 @@ export default function VotingEdit() {
|
|||||||
<Spacing />
|
<Spacing />
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
)}
|
)}
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconArchive, IconContribution } from "@/components/_Icon";
|
import { IconArchive, IconContribution } from "@/components/_Icon";
|
||||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||||
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
|
import Voting_BoxDetailHasilVotingSection from "@/screens/Voting/BoxDetailHasilVotingSection";
|
||||||
import { Voting_BoxDetailPublishSection } from "@/screens/Voting/BoxDetailPublishSection";
|
import { Voting_BoxDetailPublishSection } from "@/screens/Voting/BoxDetailPublishSection";
|
||||||
@@ -22,13 +23,14 @@ import {
|
|||||||
apiVotingUpdateData,
|
apiVotingUpdateData,
|
||||||
} from "@/service/api-client/api-voting";
|
} from "@/service/api-client/api-voting";
|
||||||
import { today } from "@/utils/dateTimeView";
|
import { today } from "@/utils/dateTimeView";
|
||||||
|
import dayjs from "dayjs";
|
||||||
import {
|
import {
|
||||||
router,
|
router,
|
||||||
Stack,
|
Stack,
|
||||||
useFocusEffect,
|
useFocusEffect,
|
||||||
useLocalSearchParams,
|
useLocalSearchParams,
|
||||||
} from "expo-router";
|
} from "expo-router";
|
||||||
import React, { useCallback, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function VotingDetail() {
|
export default function VotingDetail() {
|
||||||
@@ -119,6 +121,23 @@ export default function VotingDetail() {
|
|||||||
setOpenDrawerPublish(false);
|
setOpenDrawerPublish(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
const isEventFinished = id && data?.akhirVote && dayjs(data.akhirVote).isBefore(now);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isEventFinished) {
|
||||||
|
router.replace(`/(application)/(user)/voting/${id}/history`);
|
||||||
|
}
|
||||||
|
}, [isEventFinished, id]);
|
||||||
|
|
||||||
|
if (isEventFinished) {
|
||||||
|
return (
|
||||||
|
<ViewWrapper>
|
||||||
|
<CustomSkeleton />
|
||||||
|
</ViewWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|||||||
@@ -1,69 +1,6 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import Voting_ScreenListOfContributor from "@/screens/Voting/ScreenListOfContributor";
|
||||||
AvatarUsernameAndOtherComponent,
|
|
||||||
BadgeCustom,
|
|
||||||
BaseBox,
|
|
||||||
LoaderCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { apiVotingContribution } from "@/service/api-client/api-voting";
|
|
||||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function Voting_ListOfContributor() {
|
export default function VotingListOfContributor() {
|
||||||
const { id } = useLocalSearchParams();
|
return <Voting_ScreenListOfContributor />;
|
||||||
const [listData, setListData] = useState<any>([]);
|
|
||||||
const [isLoadData, setIsLoadData] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadList();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setIsLoadData(true);
|
|
||||||
const response = await apiVotingContribution({
|
|
||||||
id: id as string,
|
|
||||||
authorId: "",
|
|
||||||
category: "list",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setIsLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ViewWrapper>
|
|
||||||
{isLoadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center">Tidak ada kontributor</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData.map((item: any, index: number) => (
|
|
||||||
<BaseBox paddingTop={5} paddingBottom={5} key={index.toString()}>
|
|
||||||
<AvatarUsernameAndOtherComponent
|
|
||||||
avatar={item?.Author?.Profile?.imageId || ""}
|
|
||||||
name={item?.Author?.username || "Username"}
|
|
||||||
avatarHref={`/profile/${item?.Author?.Profile?.id}`}
|
|
||||||
rightComponent={
|
|
||||||
<BadgeCustom style={{ alignSelf: "flex-end" }}>
|
|
||||||
{item?.Voting_DaftarNamaVote?.value}
|
|
||||||
</BadgeCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import {
|
|||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
CenterCustom,
|
CenterCustom,
|
||||||
|
NewWrapper,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
TextInputCustom,
|
TextInputCustom,
|
||||||
ViewWrapper
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
import DateTimePickerCustom from "@/components/DateInput/DateTimePickerCustom";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
@@ -79,7 +80,7 @@ export default function VotingCreate() {
|
|||||||
type: "success",
|
type: "success",
|
||||||
text1: "Data berhasil disimpan",
|
text1: "Data berhasil disimpan",
|
||||||
});
|
});
|
||||||
router.replace("/(application)/(user)/voting/(tabs)/status");
|
router.replace("/voting/(tabs)/status?status=review");
|
||||||
} else {
|
} else {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "error",
|
type: "error",
|
||||||
@@ -106,7 +107,7 @@ export default function VotingCreate() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper footerComponent={buttonSubmit()}>
|
<NewWrapper footerComponent={buttonSubmit()}>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
label="Judul Voting"
|
label="Judul Voting"
|
||||||
@@ -142,7 +143,6 @@ export default function VotingCreate() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
{listVote.map((item, index) => (
|
{listVote.map((item, index) => (
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
key={index}
|
key={index}
|
||||||
@@ -153,8 +153,8 @@ export default function VotingCreate() {
|
|||||||
onChangeText={(value: any) =>
|
onChangeText={(value: any) =>
|
||||||
setListVote(
|
setListVote(
|
||||||
listVote.map((item, i) =>
|
listVote.map((item, i) =>
|
||||||
i === index ? { ...item, value } : item
|
i === index ? { ...item, value } : item,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -198,6 +198,6 @@ export default function VotingCreate() {
|
|||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
AlertDefaultSystem,
|
AlertDefaultSystem,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
NewWrapper,
|
NewWrapper,
|
||||||
StackCustom,
|
StackCustom
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
import { BackButton } from "@/components";
|
import { BackButton } from "@/components";
|
||||||
|
import BackgroundNotificationHandler from "@/components/Notification/BackgroundNotificationHandler";
|
||||||
|
import NotificationInitializer from "@/components/Notification/NotificationInitializer";
|
||||||
|
import { NotificationProvider } from "@/hooks/use-notification-store";
|
||||||
import { HeaderStyles } from "@/styles/header-styles";
|
import { HeaderStyles } from "@/styles/header-styles";
|
||||||
import { Stack } from "expo-router";
|
import { Stack } from "expo-router";
|
||||||
|
|
||||||
export default function ApplicationLayout() {
|
export default function ApplicationLayout() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NotificationProvider>
|
||||||
|
<NotificationInitializer />
|
||||||
|
<BackgroundNotificationHandler />
|
||||||
|
<ApplicationStack />
|
||||||
|
</NotificationProvider>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ApplicationStack() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack screenOptions={HeaderStyles}>
|
<Stack screenOptions={HeaderStyles}>
|
||||||
<Stack.Screen name="(user)" options={{ headerShown: false }} />
|
<Stack.Screen name="(user)" options={{ headerShown: false }} />
|
||||||
<Stack.Screen name="admin" options={{ headerShown: false }} />
|
<Stack.Screen name="admin" options={{ headerShown: false }} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Take Picture */}
|
{/* Take Picture */}
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
ICON_SIZE_XLARGE,
|
ICON_SIZE_XLARGE,
|
||||||
} from "@/constants/constans-value";
|
} from "@/constants/constans-value";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import AdminNotificationBell from "@/screens/Admin/AdminNotificationBell";
|
||||||
import {
|
import {
|
||||||
adminListMenu,
|
adminListMenu,
|
||||||
superAdminListMenu,
|
superAdminListMenu,
|
||||||
@@ -192,11 +193,12 @@ export default function AdminLayout() {
|
|||||||
label: "Notifikasi",
|
label: "Notifikasi",
|
||||||
value: "notification",
|
value: "notification",
|
||||||
icon: (
|
icon: (
|
||||||
<Ionicons
|
// <Ionicons
|
||||||
name="notifications"
|
// name="notifications"
|
||||||
size={ICON_SIZE_SMALL}
|
// size={ICON_SIZE_SMALL}
|
||||||
color={MainColor.white}
|
// color={MainColor.white}
|
||||||
/>
|
// />
|
||||||
|
<AdminNotificationBell/>
|
||||||
),
|
),
|
||||||
path: "/admin/notification",
|
path: "/admin/notification",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
AlertDefaultSystem,
|
||||||
BadgeCustom,
|
BadgeCustom,
|
||||||
BaseBox,
|
BaseBox,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
@@ -9,6 +10,7 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import {
|
import {
|
||||||
apiAdminDonationInvoiceDetailById,
|
apiAdminDonationInvoiceDetailById,
|
||||||
apiAdminDonationInvoiceUpdateById,
|
apiAdminDonationInvoiceUpdateById,
|
||||||
@@ -22,6 +24,7 @@ import { useCallback, useState } from "react";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminDonasiTransactionDetail() {
|
export default function AdminDonasiTransactionDetail() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
console.log("[STATUS]", id, status);
|
console.log("[STATUS]", id, status);
|
||||||
|
|
||||||
@@ -33,7 +36,7 @@ export default function AdminDonasiTransactionDetail() {
|
|||||||
onLoadData();
|
onLoadData();
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -57,6 +60,7 @@ export default function AdminDonasiTransactionDetail() {
|
|||||||
const newData = {
|
const newData = {
|
||||||
donationId: data?.donasiId,
|
donationId: data?.donasiId,
|
||||||
nominal: data?.nominal,
|
nominal: data?.nominal,
|
||||||
|
senderId: user?.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await apiAdminDonationInvoiceUpdateById({
|
const response = await apiAdminDonationInvoiceUpdateById({
|
||||||
@@ -97,7 +101,15 @@ export default function AdminDonasiTransactionDetail() {
|
|||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
handlerSubmit();
|
AlertDefaultSystem({
|
||||||
|
title: "Konfirmasi transaksi",
|
||||||
|
message: "Apakah anda yakin ingin menyetujui transaksi ini?",
|
||||||
|
textLeft: "Tidak",
|
||||||
|
textRight: "Ya",
|
||||||
|
onPressRight: () => {
|
||||||
|
handlerSubmit();
|
||||||
|
},
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Terima donasi
|
Terima donasi
|
||||||
@@ -109,7 +121,7 @@ export default function AdminDonasiTransactionDetail() {
|
|||||||
return (
|
return (
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom disabled>
|
<ButtonCustom disabled>
|
||||||
{data?.DonasiMaster_StatusInvoice?.name}
|
{data?.DonasiMaster_StatusInvoice?.name}
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
</BoxButtonOnFooter>
|
</BoxButtonOnFooter>
|
||||||
);
|
);
|
||||||
@@ -140,7 +152,7 @@ export default function AdminDonasiTransactionDetail() {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{_.startCase(
|
{_.startCase(
|
||||||
(data?.DonasiMaster_StatusInvoice?.name as any) || "-"
|
(data?.DonasiMaster_StatusInvoice?.name as any) || "-",
|
||||||
)}
|
)}
|
||||||
</BadgeCustom>
|
</BadgeCustom>
|
||||||
)) ||
|
)) ||
|
||||||
@@ -157,7 +169,7 @@ export default function AdminDonasiTransactionDetail() {
|
|||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
router.push(
|
router.push(
|
||||||
`/(application)/(image)/preview-image/${data?.imageId}`
|
`/(application)/(image)/preview-image/${data?.imageId}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import { apiAdminDonationDetailById, apiAdminDonationDisbursementOfFundsCreated } from "@/service/api-admin/api-admin-donation";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import {
|
||||||
|
apiAdminDonationDetailById,
|
||||||
|
apiAdminDonationDisbursementOfFundsCreated,
|
||||||
|
} from "@/service/api-admin/api-admin-donation";
|
||||||
import { uploadFileService } from "@/service/upload-service";
|
import { uploadFileService } from "@/service/upload-service";
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
import pickFile from "@/utils/pickFile";
|
import pickFile from "@/utils/pickFile";
|
||||||
@@ -25,7 +29,7 @@ import Toast from "react-native-toast-message";
|
|||||||
|
|
||||||
export default function AdminDonationDisbursementOfFunds() {
|
export default function AdminDonationDisbursementOfFunds() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
const { user } = useAuth();
|
||||||
const [data, setData] = React.useState<any | null>(null);
|
const [data, setData] = React.useState<any | null>(null);
|
||||||
const [isLoading, setIsLoading] = React.useState(false);
|
const [isLoading, setIsLoading] = React.useState(false);
|
||||||
|
|
||||||
@@ -40,7 +44,7 @@ export default function AdminDonationDisbursementOfFunds() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -94,6 +98,7 @@ export default function AdminDonationDisbursementOfFunds() {
|
|||||||
|
|
||||||
const newData = {
|
const newData = {
|
||||||
...value,
|
...value,
|
||||||
|
authorId: user?.id,
|
||||||
imageId: imageId,
|
imageId: imageId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { funUpdateStatusDonation } from "@/screens/Admin/Donation/funDonationUpdateStatus";
|
import { funUpdateStatusDonation } from "@/screens/Admin/Donation/funDonationUpdateStatus";
|
||||||
import {
|
import { apiAdminDonationDetailById } from "@/service/api-admin/api-admin-donation";
|
||||||
apiAdminDonationDetailById
|
|
||||||
} from "@/service/api-admin/api-admin-donation";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminDonationRejectInput() {
|
export default function AdminDonationRejectInput() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
|
|
||||||
const [data, setData] = React.useState<any | null>(null);
|
const [data, setData] = React.useState<any | null>(null);
|
||||||
@@ -24,7 +24,7 @@ export default function AdminDonationRejectInput() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -48,11 +48,23 @@ export default function AdminDonationRejectInput() {
|
|||||||
changeStatus: "publish" | "review" | "reject";
|
changeStatus: "publish" | "review" | "reject";
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
|
if (!user?.id) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "User tidak ditemukan",
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const response = await funUpdateStatusDonation({
|
const response = await funUpdateStatusDonation({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
changeStatus,
|
changeStatus,
|
||||||
data: data,
|
data: {
|
||||||
|
senderId: user?.id as string,
|
||||||
|
catatan: data,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
@@ -61,7 +73,7 @@ export default function AdminDonationRejectInput() {
|
|||||||
text1: "Report gagal",
|
text1: "Report gagal",
|
||||||
});
|
});
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast.show({
|
Toast.show({
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ export default function AdminEventDetail() {
|
|||||||
const deepLinkURL = `${DEEP_LINK_URL}/event/${id}/confirmation?userId=${user?.id}`;
|
const deepLinkURL = `${DEEP_LINK_URL}/event/${id}/confirmation?userId=${user?.id}`;
|
||||||
const deepLinkURLDEV = `${DEEP_LINK_URL}/--/event/${id}/confirmation?userId=${user?.id}`;
|
const deepLinkURLDEV = `${DEEP_LINK_URL}/--/event/${id}/confirmation?userId=${user?.id}`;
|
||||||
|
|
||||||
const isDevLink = process.env.NODE_ENV === "development" ? deepLinkURLDEV : deepLinkURL;
|
const isDevLink =
|
||||||
|
process.env.NODE_ENV === "development" ? deepLinkURLDEV : deepLinkURL;
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
@@ -126,6 +126,7 @@ export default function AdminEventDetail() {
|
|||||||
const response = await funUpdateStatusEvent({
|
const response = await funUpdateStatusEvent({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
changeStatus: "publish",
|
changeStatus: "publish",
|
||||||
|
data: { catatan: "", senderId: user?.id as string },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
|
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
|
||||||
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
|
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
@@ -14,9 +15,13 @@ import { useCallback, useState } from "react";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminEventRejectInput() {
|
export default function AdminEventRejectInput() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
|
|
||||||
const [data, setData] = useState<any>("");
|
const [data, setData] = useState<any>({
|
||||||
|
catatan: "",
|
||||||
|
senderId: "",
|
||||||
|
});
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
@@ -45,10 +50,16 @@ export default function AdminEventRejectInput() {
|
|||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
|
const newData = {
|
||||||
|
catatan: data,
|
||||||
|
senderId: user?.id as string,
|
||||||
|
};
|
||||||
|
|
||||||
const response = await funUpdateStatusEvent({
|
const response = await funUpdateStatusEvent({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
changeStatus,
|
changeStatus,
|
||||||
data: data,
|
data: newData,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
|
|||||||
@@ -15,12 +15,11 @@ import { IconDot, IconView } from "@/components/_Icon/IconComponent";
|
|||||||
import { IconTrash } from "@/components/_Icon/IconTrash";
|
import { IconTrash } from "@/components/_Icon/IconTrash";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||||
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import {
|
import {
|
||||||
apiAdminForumCommentById,
|
apiAdminForumCommentById,
|
||||||
apiAdminForumDeactivateComment,
|
apiAdminForumDeactivateComment,
|
||||||
@@ -35,6 +34,7 @@ import Toast from "react-native-toast-message";
|
|||||||
|
|
||||||
export default function AdminForumReportComment() {
|
export default function AdminForumReportComment() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
const { user } = useAuth();
|
||||||
const [data, setData] = useState<any | null>(null);
|
const [data, setData] = useState<any | null>(null);
|
||||||
const [listReport, setListReport] = useState<any[] | null>(null);
|
const [listReport, setListReport] = useState<any[] | null>(null);
|
||||||
const [loadList, setLoadList] = useState(false);
|
const [loadList, setLoadList] = useState(false);
|
||||||
@@ -111,9 +111,13 @@ export default function AdminForumReportComment() {
|
|||||||
|
|
||||||
<AdminComp_BoxTitle title="Daftar Report Komentar" />
|
<AdminComp_BoxTitle title="Daftar Report Komentar" />
|
||||||
|
|
||||||
<StackCustom gap={"sm"}>
|
<StackCustom gap={"sm"}>
|
||||||
<GridSpan_NewComponent
|
<GridSpan_NewComponent
|
||||||
text1={<TextCustom bold align="center">Aksi</TextCustom>}
|
text1={
|
||||||
|
<TextCustom bold align="center">
|
||||||
|
Aksi
|
||||||
|
</TextCustom>
|
||||||
|
}
|
||||||
text2={<TextCustom bold>Pelapor</TextCustom>}
|
text2={<TextCustom bold>Pelapor</TextCustom>}
|
||||||
text3={<TextCustom bold>Kategori Report</TextCustom>}
|
text3={<TextCustom bold>Kategori Report</TextCustom>}
|
||||||
/>
|
/>
|
||||||
@@ -129,22 +133,24 @@ export default function AdminForumReportComment() {
|
|||||||
<View key={index}>
|
<View key={index}>
|
||||||
<GridSpan_NewComponent
|
<GridSpan_NewComponent
|
||||||
text1={
|
text1={
|
||||||
<CenterCustom>
|
<CenterCustom>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
icon={<IconView size={ICON_SIZE_BUTTON} color="black" />}
|
icon={
|
||||||
onPress={() => {
|
<IconView size={ICON_SIZE_BUTTON} color="black" />
|
||||||
setOpenDrawerAction(true);
|
}
|
||||||
setSelectedReport({
|
onPress={() => {
|
||||||
id: item.id,
|
setOpenDrawerAction(true);
|
||||||
username: item.User?.username,
|
setSelectedReport({
|
||||||
kategori: item.ForumMaster_KategoriReport?.title,
|
id: item.id,
|
||||||
keterangan:
|
username: item.User?.username,
|
||||||
item.ForumMaster_KategoriReport?.deskripsi,
|
kategori: item.ForumMaster_KategoriReport?.title,
|
||||||
deskripsi: item.deskripsi,
|
keterangan:
|
||||||
});
|
item.ForumMaster_KategoriReport?.deskripsi,
|
||||||
}}
|
deskripsi: item.deskripsi,
|
||||||
/>
|
});
|
||||||
</CenterCustom>
|
}}
|
||||||
|
/>
|
||||||
|
</CenterCustom>
|
||||||
}
|
}
|
||||||
text2={
|
text2={
|
||||||
<TextCustom truncate={1}>
|
<TextCustom truncate={1}>
|
||||||
@@ -188,15 +194,18 @@ export default function AdminForumReportComment() {
|
|||||||
onPressRight: async () => {
|
onPressRight: async () => {
|
||||||
const deleteComment = await apiAdminForumDeactivateComment({
|
const deleteComment = await apiAdminForumDeactivateComment({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
|
data: {
|
||||||
|
senderId: user?.id as string,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!deleteComment.success) {
|
// if (!deleteComment.success) {
|
||||||
Toast.show({
|
// Toast.show({
|
||||||
type: "error",
|
// type: "error",
|
||||||
text1: "Komentar gagal dihapus",
|
// text1: "Komentar gagal dihapus",
|
||||||
});
|
// });
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
setOpenDrawer(false);
|
setOpenDrawer(false);
|
||||||
Toast.show({
|
Toast.show({
|
||||||
|
|||||||
@@ -16,12 +16,11 @@ import { IconDot, IconView } from "@/components/_Icon/IconComponent";
|
|||||||
import { IconTrash } from "@/components/_Icon/IconTrash";
|
import { IconTrash } from "@/components/_Icon/IconTrash";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||||
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import {
|
import {
|
||||||
apiAdminForumDeactivatePosting,
|
apiAdminForumDeactivatePosting,
|
||||||
apiAdminForumListReportPostingById,
|
apiAdminForumListReportPostingById,
|
||||||
@@ -35,6 +34,7 @@ import { Divider } from "react-native-paper";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminForumReportPosting() {
|
export default function AdminForumReportPosting() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawerPage, setOpenDrawerPage] = useState(false);
|
const [openDrawerPage, setOpenDrawerPage] = useState(false);
|
||||||
const [openDrawerAction, setOpenDrawerAction] = useState(false);
|
const [openDrawerAction, setOpenDrawerAction] = useState(false);
|
||||||
@@ -215,6 +215,9 @@ export default function AdminForumReportPosting() {
|
|||||||
onPressRight: async () => {
|
onPressRight: async () => {
|
||||||
const response = await apiAdminForumDeactivatePosting({
|
const response = await apiAdminForumDeactivatePosting({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
|
data: {
|
||||||
|
senderId: user?.id as string,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default function AdminForumReportPosting() {
|
|||||||
<GridSpan_NewComponent
|
<GridSpan_NewComponent
|
||||||
text1={
|
text1={
|
||||||
<TextCustom bold truncate>
|
<TextCustom bold truncate>
|
||||||
Username
|
Pelapor
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
}
|
}
|
||||||
text2={
|
text2={
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
|
|||||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||||
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||||
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
import ReportBox from "@/components/Box/ReportBox";
|
import ReportBox from "@/components/Box/ReportBox";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||||
@@ -28,6 +29,7 @@ import {
|
|||||||
apiAdminInvestmentDetailById,
|
apiAdminInvestmentDetailById,
|
||||||
} from "@/service/api-admin/api-admin-investment";
|
} from "@/service/api-admin/api-admin-investment";
|
||||||
import { colorBadgeStatus } from "@/utils/colorBadge";
|
import { colorBadgeStatus } from "@/utils/colorBadge";
|
||||||
|
import { countDownAndCondition } from "@/utils/countDownAndCondition";
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
@@ -40,91 +42,41 @@ export default function AdminInvestmentDetail() {
|
|||||||
|
|
||||||
const [data, setData] = React.useState<any | null>(null);
|
const [data, setData] = React.useState<any | null>(null);
|
||||||
const [isLoading, setLoading] = React.useState(false);
|
const [isLoading, setLoading] = React.useState(false);
|
||||||
|
const [remind, setRemind] = React.useState({
|
||||||
|
sisa: 0,
|
||||||
|
reminder: false,
|
||||||
|
});
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
React.useCallback(() => {
|
React.useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await apiAdminInvestmentDetailById({ id: id as string });
|
const response = await apiAdminInvestmentDetailById({ id: id as string });
|
||||||
// console.log("[GETONE INVEST]", JSON.stringify(response, null, 2));
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
|
|
||||||
|
const duration = response?.data?.MasterPencarianInvestor?.name;
|
||||||
|
const publishTime = response?.data?.countDown;
|
||||||
|
|
||||||
|
const countDown = countDownAndCondition({
|
||||||
|
duration: duration,
|
||||||
|
publishTime: publishTime
|
||||||
|
});
|
||||||
|
|
||||||
|
setRemind({
|
||||||
|
sisa: countDown.durationDay,
|
||||||
|
reminder: countDown.reminder,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log("Error", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const listData = [
|
|
||||||
{
|
|
||||||
label: "Username",
|
|
||||||
value: (data && data?.author?.username) || "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Judul",
|
|
||||||
value: (data && data?.title) || "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Status",
|
|
||||||
value:
|
|
||||||
data && data?.MasterStatusInvestasi?.name ? (
|
|
||||||
<BadgeCustom
|
|
||||||
color={colorBadgeStatus({
|
|
||||||
status: data?.MasterStatusInvestasi?.name as string,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{_.startCase(data?.MasterStatusInvestasi?.name as string)}
|
|
||||||
</BadgeCustom>
|
|
||||||
) : (
|
|
||||||
"-"
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Dana Dibutuhkan",
|
|
||||||
value: `Rp. ${
|
|
||||||
(data && data?.targetDana && formatCurrencyDisplay(data?.targetDana)) ||
|
|
||||||
"-"
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Harga Perlembar",
|
|
||||||
value: `Rp. ${
|
|
||||||
(data &&
|
|
||||||
data?.hargaLembar &&
|
|
||||||
formatCurrencyDisplay(data?.hargaLembar)) ||
|
|
||||||
"-"
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Total Lembar",
|
|
||||||
value:
|
|
||||||
(data &&
|
|
||||||
data?.totalLembar &&
|
|
||||||
formatCurrencyDisplay(data?.totalLembar)) ||
|
|
||||||
"-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "ROI",
|
|
||||||
value: `${(data && data?.roi && data?.roi) || 0} %`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Pembagian Deviden",
|
|
||||||
value: (data && data?.MasterPembagianDeviden?.name) + " bulan" || "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Jadwal Pembagian",
|
|
||||||
value: (data && data?.MasterPeriodeDeviden?.name) || "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Pencarian Investor",
|
|
||||||
value: (data && data?.MasterPencarianInvestor?.name) + " hari" || "-",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const handlerSubmitPublish = async () => {
|
const handlerSubmitPublish = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -134,7 +86,6 @@ export default function AdminInvestmentDetail() {
|
|||||||
data: data,
|
data: data,
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log("[GET ON INVEST]", JSON.stringify(response, null, 2));
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "error",
|
type: "error",
|
||||||
@@ -164,6 +115,16 @@ export default function AdminInvestmentDetail() {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ViewWrapper>
|
||||||
|
<CustomSkeleton height={200} />
|
||||||
|
</ViewWrapper>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper
|
<ViewWrapper
|
||||||
@@ -177,8 +138,8 @@ export default function AdminInvestmentDetail() {
|
|||||||
{status === "publish" && (
|
{status === "publish" && (
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<ProgressCustom
|
<ProgressCustom
|
||||||
label={data && `${data.progress}%` || "0%"}
|
label={(data && `${data.progress}%`) || "0%"}
|
||||||
value={data && data.progress || 0}
|
value={(data && data.progress) || 0}
|
||||||
size="lg"
|
size="lg"
|
||||||
/>
|
/>
|
||||||
<Spacing />
|
<Spacing />
|
||||||
@@ -187,7 +148,8 @@ export default function AdminInvestmentDetail() {
|
|||||||
label={<TextCustom bold>Sisa Saham</TextCustom>}
|
label={<TextCustom bold>Sisa Saham</TextCustom>}
|
||||||
value={
|
value={
|
||||||
<TextCustom>
|
<TextCustom>
|
||||||
{data && formatCurrencyDisplay(data && data?.sisaLembar)} lembar
|
{data && formatCurrencyDisplay(data && data?.sisaLembar)}{" "}
|
||||||
|
lembar
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -206,13 +168,15 @@ export default function AdminInvestmentDetail() {
|
|||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<DummyLandscapeImage imageId={data?.imageId} />
|
<DummyLandscapeImage imageId={data?.imageId} />
|
||||||
{listData.map((item, i) => (
|
{listData({ data: data, reminder: remind.reminder })?.map(
|
||||||
<GridSpan_4_8
|
(item, i) => (
|
||||||
key={i}
|
<GridSpan_4_8
|
||||||
label={<TextCustom bold>{item.label}</TextCustom>}
|
key={i}
|
||||||
value={<TextCustom>{item.value}</TextCustom>}
|
label={<TextCustom bold>{item.label}</TextCustom>}
|
||||||
/>
|
value={<TextCustom>{item.value}</TextCustom>}
|
||||||
))}
|
/>
|
||||||
|
),
|
||||||
|
)}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
@@ -230,7 +194,7 @@ export default function AdminInvestmentDetail() {
|
|||||||
}
|
}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push(
|
router.push(
|
||||||
`/(application)/(file)/${data?.prospektusFileId}`
|
`/(application)/(file)/${data?.prospektusFileId}`,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -259,7 +223,7 @@ export default function AdminInvestmentDetail() {
|
|||||||
}
|
}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
router.push(
|
router.push(
|
||||||
`/(application)/(file)/${item?.fileId}`
|
`/(application)/(file)/${item?.fileId}`,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -299,8 +263,8 @@ export default function AdminInvestmentDetail() {
|
|||||||
onReject={() => {
|
onReject={() => {
|
||||||
router.push(
|
router.push(
|
||||||
`/admin/investment/${id}/reject-input?status=${_.lowerCase(
|
`/admin/investment/${id}/reject-input?status=${_.lowerCase(
|
||||||
data?.MasterStatusInvestasi?.name
|
data?.MasterStatusInvestasi?.name,
|
||||||
)}`
|
)}`,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -343,3 +307,67 @@ export default function AdminInvestmentDetail() {
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const listData = ({ data, reminder }: { data: any; reminder: boolean }) => [
|
||||||
|
{
|
||||||
|
label: "Username",
|
||||||
|
value: (data && data?.author?.username) || "-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Judul",
|
||||||
|
value: (data && data?.title) || "-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Status",
|
||||||
|
value:
|
||||||
|
data && data?.MasterStatusInvestasi?.name ? (
|
||||||
|
<BadgeCustom
|
||||||
|
color={colorBadgeStatus({
|
||||||
|
status: reminder ? "periode berakhir" : "publish",
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{reminder
|
||||||
|
? "Periode Berakhir"
|
||||||
|
: _.startCase(data?.MasterStatusInvestasi?.name as string)}
|
||||||
|
</BadgeCustom>
|
||||||
|
) : (
|
||||||
|
"-"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Dana Dibutuhkan",
|
||||||
|
value: `Rp. ${
|
||||||
|
(data && data?.targetDana && formatCurrencyDisplay(data?.targetDana)) ||
|
||||||
|
"-"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Harga Perlembar",
|
||||||
|
value: `Rp. ${
|
||||||
|
(data && data?.hargaLembar && formatCurrencyDisplay(data?.hargaLembar)) ||
|
||||||
|
"-"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Total Lembar",
|
||||||
|
value:
|
||||||
|
(data && data?.totalLembar && formatCurrencyDisplay(data?.totalLembar)) ||
|
||||||
|
"-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "ROI",
|
||||||
|
value: `${(data && data?.roi && data?.roi) || 0} %`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Pembagian Deviden",
|
||||||
|
value: (data && data?.MasterPembagianDeviden?.name) + " bulan" || "-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Jadwal Pembagian",
|
||||||
|
value: (data && data?.MasterPeriodeDeviden?.name) || "-",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Pencarian Investor",
|
||||||
|
value: (data && data?.MasterPencarianInvestor?.name) + " hari" || "-",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
|
|||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import {
|
import {
|
||||||
apiAdminInvestmentGetOneInvoiceById,
|
apiAdminInvestmentGetOneInvoiceById,
|
||||||
apiAdminInvestmentUpdateInvoice,
|
apiAdminInvestmentUpdateInvoice,
|
||||||
@@ -25,6 +26,7 @@ import { useCallback, useState } from "react";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminInvestmentTransactionDetail() {
|
export default function AdminInvestmentTransactionDetail() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [data, setData] = useState<any | null>(null);
|
const [data, setData] = useState<any | null>(null);
|
||||||
const [isLoading, setLoading] = useState<boolean>(false);
|
const [isLoading, setLoading] = useState<boolean>(false);
|
||||||
@@ -32,7 +34,7 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -40,7 +42,6 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
const response = await apiAdminInvestmentGetOneInvoiceById({
|
const response = await apiAdminInvestmentGetOneInvoiceById({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
});
|
});
|
||||||
// console.log("[RESPONSE]", JSON.stringify(response, null, 2));
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
}
|
}
|
||||||
@@ -92,7 +93,7 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
onPress={() =>
|
onPress={() =>
|
||||||
router.push(
|
router.push(
|
||||||
`/(application)/(image)/preview-image/${data?.imageId}`
|
`/(application)/(image)/preview-image/${data?.imageId}`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -109,6 +110,13 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
}: {
|
}: {
|
||||||
category: "accept" | "deny";
|
category: "accept" | "deny";
|
||||||
}) => {
|
}) => {
|
||||||
|
if (!user?.id) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "Gagal update status transaksi",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const response = await apiAdminInvestmentUpdateInvoice({
|
const response = await apiAdminInvestmentUpdateInvoice({
|
||||||
@@ -117,11 +125,10 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
data: {
|
data: {
|
||||||
investasiId: data?.investasiId,
|
investasiId: data?.investasiId,
|
||||||
lembarTerbeli: data?.lembarTerbeli,
|
lembarTerbeli: data?.lembarTerbeli,
|
||||||
|
senderId: user?.id as any,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log("[RESPONSE SUBMIT]", JSON.stringify(response, null, 2));
|
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
Toast.show({
|
Toast.show({
|
||||||
type: "error",
|
type: "error",
|
||||||
@@ -153,6 +160,7 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
styleRight={{ paddingLeft: 10 }}
|
styleRight={{ paddingLeft: 10 }}
|
||||||
leftIcon={
|
leftIcon={
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
disabled={isLoading}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
backgroundColor={MainColor.red}
|
backgroundColor={MainColor.red}
|
||||||
textColor="white"
|
textColor="white"
|
||||||
@@ -175,6 +183,7 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
}
|
}
|
||||||
rightIcon={
|
rightIcon={
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
|
disabled={isLoading}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
AlertDefaultSystem({
|
AlertDefaultSystem({
|
||||||
@@ -198,8 +207,8 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
} else if (data?.StatusInvoice?.name === "Gagal") {
|
} else if (data?.StatusInvoice?.name === "Gagal") {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ButtonCustom textColor="red" onPress={() => router.back()}>
|
<ButtonCustom disabled onPress={() => router.back()}>
|
||||||
Gagal
|
Transaksi telah gagal
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,34 +7,39 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||||
import { apiAdminInvestasiUpdateByStatus, apiAdminInvestmentDetailById } from "@/service/api-admin/api-admin-investment";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import {
|
||||||
|
apiAdminInvestasiUpdateByStatus,
|
||||||
|
apiAdminInvestmentDetailById,
|
||||||
|
} from "@/service/api-admin/api-admin-investment";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminInvestmentRejectInput() {
|
export default function AdminInvestmentRejectInput() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
console.log("[STATUS]", status);
|
console.log("[STATUS]", status);
|
||||||
const [value, setValue] = useState<any | null>(null);
|
const [value, setValue] = useState<any | null>(null);
|
||||||
const [isLoading , setLoading] = useState(false)
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id])
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await apiAdminInvestmentDetailById({ id: id as string });
|
const response = await apiAdminInvestmentDetailById({ id: id as string });
|
||||||
console.log("[DATA]", JSON.stringify(response, null, 2));
|
console.log("[DATA]", JSON.stringify(response, null, 2));
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setValue(response.data?.catatan);
|
setValue(response.data?.catatan);
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
}
|
||||||
};
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handlerSubmit = async () => {
|
const handlerSubmit = async () => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
@@ -45,12 +50,23 @@ export default function AdminInvestmentRejectInput() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!user?.id) {
|
||||||
|
Toast.show({
|
||||||
|
type: "error",
|
||||||
|
text1: "User tidak ditemukan",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true);
|
||||||
const response = await apiAdminInvestasiUpdateByStatus({
|
const response = await apiAdminInvestasiUpdateByStatus({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
status: "reject",
|
status: "reject",
|
||||||
data: value,
|
data: {
|
||||||
|
catatan: value,
|
||||||
|
senderId: user?.id as string,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[RESPONSE]", JSON.stringify(response, null, 2));
|
console.log("[RESPONSE]", JSON.stringify(response, null, 2));
|
||||||
@@ -76,7 +92,7 @@ export default function AdminInvestmentRejectInput() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(["ERROR"], error);
|
console.error(["ERROR"], error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import { Divider } from "react-native-paper";
|
|||||||
|
|
||||||
export default function AdminInvestmentStatus() {
|
export default function AdminInvestmentStatus() {
|
||||||
const { status } = useLocalSearchParams();
|
const { status } = useLocalSearchParams();
|
||||||
console.log("[STATUS]", status);
|
|
||||||
|
|
||||||
const [listData, setListData] = React.useState<any[] | null>(null);
|
const [listData, setListData] = React.useState<any[] | null>(null);
|
||||||
const [loadData, setLoadingData] = React.useState(false);
|
const [loadData, setLoadingData] = React.useState(false);
|
||||||
const [search, setSearch] = React.useState("");
|
const [search, setSearch] = React.useState("");
|
||||||
@@ -41,7 +39,7 @@ export default function AdminInvestmentStatus() {
|
|||||||
category: status as "publish" | "review" | "reject",
|
category: status as "publish" | "review" | "reject",
|
||||||
search,
|
search,
|
||||||
});
|
});
|
||||||
console.log("[LIST DATA]", JSON.stringify(response, null, 2));
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
setListData(response.data);
|
setListData(response.data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
|||||||
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
||||||
import ReportBox from "@/components/Box/ReportBox";
|
import ReportBox from "@/components/Box/ReportBox";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import funUpdateStatusJob from "@/screens/Admin/Job/funUpdateStatus";
|
import funUpdateStatusJob from "@/screens/Admin/Job/funUpdateStatus";
|
||||||
import { apiAdminJobGetById } from "@/service/api-admin/api-admin-job";
|
import { apiAdminJobGetById } from "@/service/api-admin/api-admin-job";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
@@ -23,8 +24,10 @@ import { useCallback, useState } from "react";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminJobDetailStatus() {
|
export default function AdminJobDetailStatus() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
const [data, setData] = useState<any | null>(null);
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
@@ -92,6 +95,9 @@ export default function AdminJobDetailStatus() {
|
|||||||
const response = await funUpdateStatusJob({
|
const response = await funUpdateStatusJob({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
changeStatus,
|
changeStatus,
|
||||||
|
data: {
|
||||||
|
senderId: user?.id as string,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
@@ -142,12 +148,15 @@ export default function AdminJobDetailStatus() {
|
|||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
{data && data?.catatan && (status === "reject" || status === "review") && (
|
{data &&
|
||||||
<ReportBox text={data?.catatan}/>
|
data?.catatan &&
|
||||||
)}
|
(status === "reject" || status === "review") && (
|
||||||
|
<ReportBox text={data?.catatan} />
|
||||||
|
)}
|
||||||
|
|
||||||
{status === "review" && (
|
{status === "review" && (
|
||||||
<AdminButtonReview
|
<AdminButtonReview
|
||||||
|
isLoading={isLoading}
|
||||||
onPublish={() => {
|
onPublish={() => {
|
||||||
AlertDefaultSystem({
|
AlertDefaultSystem({
|
||||||
title: "Publish",
|
title: "Publish",
|
||||||
@@ -156,6 +165,7 @@ export default function AdminJobDetailStatus() {
|
|||||||
textRight: "Ya",
|
textRight: "Ya",
|
||||||
onPressRight: () => {
|
onPressRight: () => {
|
||||||
handleUpdate({ changeStatus: "publish" });
|
handleUpdate({ changeStatus: "publish" });
|
||||||
|
setIsLoading(true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -15,7 +15,10 @@ import Toast from "react-native-toast-message";
|
|||||||
|
|
||||||
export default function AdminJobRejectInput() {
|
export default function AdminJobRejectInput() {
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
const [data, setData] = useState<any | null>(null);
|
const [data, setData] = useState({
|
||||||
|
catatan: "",
|
||||||
|
senderId: ""
|
||||||
|
});
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
@@ -48,7 +51,7 @@ export default function AdminJobRejectInput() {
|
|||||||
const response = await funUpdateStatusJob({
|
const response = await funUpdateStatusJob({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
changeStatus,
|
changeStatus,
|
||||||
data: data,
|
data: data ,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
@@ -102,8 +105,8 @@ export default function AdminJobRejectInput() {
|
|||||||
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
|
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
|
||||||
>
|
>
|
||||||
<TextAreaCustom
|
<TextAreaCustom
|
||||||
value={data}
|
value={data?.catatan}
|
||||||
onChangeText={setData}
|
onChangeText={(text) => setData({ ...data, catatan: text })}
|
||||||
placeholder="Masukan alasan"
|
placeholder="Masukan alasan"
|
||||||
required
|
required
|
||||||
showCount
|
showCount
|
||||||
|
|||||||
@@ -1,20 +1,10 @@
|
|||||||
import { BackButton, TextCustom, ViewWrapper } from "@/components";
|
import Admin_ScreenNotification from "@/screens/Admin/Notification-Admin/ScreenNotificationAdmin";
|
||||||
import { Stack } from "expo-router";
|
import Admin_ScreenNotification2 from "@/screens/Admin/Notification-Admin/ScreenNotificationAdmin2";
|
||||||
|
|
||||||
export default function AdminNotification() {
|
export default function AdminNotification() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Admin_ScreenNotification2 />
|
||||||
options={{
|
|
||||||
title: "Admin Notifikasi",
|
|
||||||
headerLeft: () => <BackButton />,
|
|
||||||
headerRight: () => <></>,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ViewWrapper>
|
|
||||||
<TextCustom>Notification</TextCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export default function SuperAdminDetail() {
|
|||||||
const response = await apiAdminUserAccessUpdateStatus({
|
const response = await apiAdminUserAccessUpdateStatus({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
role: data?.masterUserRoleId === "2" ? "user" : "admin",
|
role: data?.masterUserRoleId === "2" ? "user" : "admin",
|
||||||
|
category: "role"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import {
|
import {
|
||||||
apiAdminUserAccessGetById,
|
apiAdminUserAccessGetById,
|
||||||
apiAdminUserAccessUpdateStatus,
|
apiAdminUserAccessUpdateStatus,
|
||||||
@@ -18,6 +19,7 @@ import { useCallback, useState } from "react";
|
|||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
export default function AdminUserAccessDetail() {
|
export default function AdminUserAccessDetail() {
|
||||||
|
const { user } = useAuth();
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [data, setData] = useState<any | null>(null);
|
const [data, setData] = useState<any | null>(null);
|
||||||
const [loadData, setLoadData] = useState(false);
|
const [loadData, setLoadData] = useState(false);
|
||||||
@@ -33,6 +35,7 @@ export default function AdminUserAccessDetail() {
|
|||||||
try {
|
try {
|
||||||
setLoadData(true);
|
setLoadData(true);
|
||||||
const response = await apiAdminUserAccessGetById({ id: id as string });
|
const response = await apiAdminUserAccessGetById({ id: id as string });
|
||||||
|
console.log("[DATA]", JSON.stringify(response.data, null, 2));
|
||||||
|
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -48,6 +51,7 @@ export default function AdminUserAccessDetail() {
|
|||||||
const response = await apiAdminUserAccessUpdateStatus({
|
const response = await apiAdminUserAccessUpdateStatus({
|
||||||
id: id as string,
|
id: id as string,
|
||||||
active: !data?.active,
|
active: !data?.active,
|
||||||
|
category: "access",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
@@ -61,6 +65,7 @@ export default function AdminUserAccessDetail() {
|
|||||||
type: "success",
|
type: "success",
|
||||||
text1: "Update aktifasi berhasil ",
|
text1: "Update aktifasi berhasil ",
|
||||||
});
|
});
|
||||||
|
|
||||||
router.back();
|
router.back();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ERROR UPDATE STATUS]", error);
|
console.log("[ERROR UPDATE STATUS]", error);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user