Compare commits
44 Commits
loaddata/3
...
clean-code
| Author | SHA1 | Date | |
|---|---|---|---|
| 57c9215771 | |||
| 4efdbd3c7b | |||
| ad32eb6fe6 | |||
| a5026cc285 | |||
| 836ef709d2 | |||
| 3bbee15c3a | |||
| ad7dbaf162 | |||
| 9c94ec0454 | |||
| 4c63485a5b | |||
| f5d09a2906 | |||
| 67070bb2f1 | |||
| fb19ec60b2 | |||
| e8f5c5b174 | |||
| 74a4d88277 | |||
| 2ad93a26a8 | |||
| 768b0caa9e | |||
| 208b0ce813 | |||
| 66e6aebf41 | |||
| 32a42d1b60 | |||
| 107d4312e1 | |||
| 4862975402 | |||
| f284e2ec02 | |||
| 1d61ad51e5 | |||
| 76845b71b4 | |||
| 97e1f50660 | |||
| 1cbe4ab330 | |||
| 42fa80c228 | |||
| fb697366fe | |||
| 6d71c3a86f | |||
| e030b8f486 | |||
| 5c931b069c | |||
| b2be7be533 | |||
| 2705f96b01 | |||
| 38a6b424e8 | |||
| 83fa277e03 | |||
| c570a19d84 | |||
| 7415c8c8ce | |||
| 72a3d42013 | |||
| d0abd14047 | |||
| 5b2be20469 | |||
| 60177a1087 | |||
| 771ae45f26 | |||
| 41f4a8ac99 | |||
| 48196cd46b |
586
QWEN.md
586
QWEN.md
@@ -1,179 +1,515 @@
|
|||||||
# HIPMI Mobile Application - Development Guide
|
# HIPMI Mobile Application - Development Context
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
HIPMI Badung Connect is a mobile application built with Expo and React Native. It serves as a connection platform for HIPMI (Himpunan Pengusaha Muda Indonesia) Badung members, featuring authentication, user management, and various business-related functionalities.
|
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
|
### Key Technologies
|
||||||
- **Framework**: Expo (v54.0.0) with React Native (0.81.4)
|
- **Framework**: Expo (v54.0.0) with React Native (v0.81.5)
|
||||||
- **Architecture**: File-based routing with Expo Router
|
|
||||||
- **State Management**: React Context API
|
|
||||||
- **Styling**: React Native components with custom color palettes
|
|
||||||
- **Authentication**: Token-based authentication with OTP verification
|
|
||||||
- **Database**: AsyncStorage for local storage
|
|
||||||
- **Maps**: React Native Maps and Mapbox integration
|
|
||||||
- **Notifications**: Expo Notifications and Firebase Messaging
|
|
||||||
- **Language**: TypeScript
|
- **Language**: TypeScript
|
||||||
|
- **Architecture**: File-based routing with Expo Router
|
||||||
|
- **State Management**: Context API (AuthContext)
|
||||||
|
- **UI Components**: React Native Paper, custom components
|
||||||
|
- **Maps Integration**: Maplibre Maps for React Native (`@maplibre/maplibre-react-native` v10.4.2)
|
||||||
|
- **Push Notifications**: React Native Firebase Messaging
|
||||||
|
- **Build System**: Metro bundler
|
||||||
|
- **Package Manager**: Bun
|
||||||
|
|
||||||
### Project Structure
|
### Project Structure
|
||||||
```
|
```
|
||||||
hipmi-mobile/
|
hipmi-mobile/
|
||||||
├── app/ # File-based routing structure
|
├── app/ # Main application screens and routing (Expo Router)
|
||||||
│ ├── (application)/ # Main application screens
|
│ ├── _layout.tsx # Root layout component
|
||||||
│ │ ├── (file)/ # File management screens
|
│ ├── index.tsx # Entry point (Login screen)
|
||||||
│ │ ├── (image)/ # Image management screens
|
│ └── (application)/ # Main app screens
|
||||||
│ │ ├── (user)/ # User-specific screens
|
│ ├── admin/ # Admin panel screens
|
||||||
│ │ └── admin/ # Admin-specific screens
|
│ ├── (user)/ # User screens
|
||||||
│ ├── _layout.tsx # Root layout wrapper
|
│ └── ...
|
||||||
│ ├── index.tsx # Home screen
|
|
||||||
│ ├── eula.tsx # Terms and conditions screen
|
|
||||||
│ ├── register.tsx # Registration screen
|
|
||||||
│ └── verification.tsx # OTP verification screen
|
|
||||||
├── assets/ # Static assets (images, icons)
|
|
||||||
├── components/ # Reusable UI components
|
├── components/ # Reusable UI components
|
||||||
├── constants/ # Configuration constants
|
│ ├── _ShareComponent/ # Shared components (NewWrapper, Admin components)
|
||||||
├── context/ # React Context providers
|
│ ├── _Icon/ # Icon components
|
||||||
|
│ └── ...
|
||||||
|
├── context/ # State management (AuthContext)
|
||||||
|
├── screens/ # Screen components organized by feature
|
||||||
|
│ ├── Admin/ # Admin panel screens
|
||||||
|
│ │ ├── Donation/ # Donation management screens
|
||||||
|
│ │ ├── Voting/ # Voting management screens
|
||||||
|
│ │ ├── Event/ # Event management screens
|
||||||
|
│ │ └── ...
|
||||||
|
│ ├── Authentication/ # Login, registration flows
|
||||||
|
│ ├── RootLayout/ # Root layout components
|
||||||
|
│ └── ...
|
||||||
|
├── service/ # API services and business logic
|
||||||
|
│ ├── api-admin/ # Admin API endpoints
|
||||||
|
│ ├── api-client/ # Client API endpoints
|
||||||
|
│ └── api-config.ts # Axios configuration
|
||||||
├── hooks/ # Custom React hooks
|
├── hooks/ # Custom React hooks
|
||||||
├── screens/ # Screen components
|
│ ├── use-pagination.tsx # Pagination hook
|
||||||
├── service/ # API services and configurations
|
│ └── ...
|
||||||
|
├── helpers/ # Helper functions
|
||||||
|
│ ├── paginationHelpers.tsx # Pagination UI helpers
|
||||||
|
│ └── ...
|
||||||
├── types/ # TypeScript type definitions
|
├── types/ # TypeScript type definitions
|
||||||
├── app.config.js # Expo configuration
|
├── utils/ # Utility functions
|
||||||
├── package.json # Dependencies and scripts
|
├── constants/ # Constants and configuration values
|
||||||
└── ...
|
├── styles/ # Global styles
|
||||||
|
├── assets/ # Images, icons, and static assets
|
||||||
|
└── docs/ # Documentation files
|
||||||
```
|
```
|
||||||
|
|
||||||
## Building and Running
|
## Building and Running
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
- Node.js (with bun >=1.0.0 as specified in package.json)
|
- **Node.js**: v18+ with Bun package manager
|
||||||
- Expo CLI or bun installed globally
|
- **Expo CLI**: Installed globally or via npx
|
||||||
|
- **iOS**: Xcode (macOS only) for iOS simulator/builds
|
||||||
|
- **Android**: Android Studio for Android emulator/builds
|
||||||
|
|
||||||
### Setup Instructions
|
### Setup and Development
|
||||||
1. **Install dependencies**:
|
|
||||||
|
1. **Install Dependencies**
|
||||||
```bash
|
```bash
|
||||||
bun install
|
bun install
|
||||||
# or if using npm
|
|
||||||
npm install
|
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Environment Variables**:
|
2. **Run Development Server**
|
||||||
Create a `.env` file with the following variables:
|
|
||||||
```
|
|
||||||
API_BASE_URL=your_api_base_url
|
|
||||||
BASE_URL=your_base_url
|
|
||||||
DEEP_LINK_URL=your_deep_link_url
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Start the development server**:
|
|
||||||
```bash
|
```bash
|
||||||
# Using bun (as specified in package.json)
|
|
||||||
bun run start
|
bun run start
|
||||||
# or using expo directly
|
# or
|
||||||
npx expo start
|
bunx expo start
|
||||||
```
|
```
|
||||||
|
|
||||||
4. **Platform-specific commands**:
|
3. **Platform-Specific Commands**
|
||||||
```bash
|
```bash
|
||||||
# Android
|
# iOS Simulator
|
||||||
bun run android
|
|
||||||
# iOS
|
|
||||||
bun run ios
|
bun run ios
|
||||||
# Web
|
# or
|
||||||
|
bunx expo start --ios
|
||||||
|
|
||||||
|
# Android Emulator
|
||||||
|
bun run android
|
||||||
|
# or
|
||||||
|
bunx expo start --android
|
||||||
|
|
||||||
|
# Web Browser
|
||||||
bun run web
|
bun run web
|
||||||
|
# or
|
||||||
|
bunx expo start --web
|
||||||
```
|
```
|
||||||
|
|
||||||
### EAS Build Configuration
|
4. **Linting**
|
||||||
The project uses Expo Application Services (EAS) for building and deployment:
|
```bash
|
||||||
- Development builds: `eas build --profile development`
|
bun run lint
|
||||||
- Preview builds: `eas build --profile preview`
|
```
|
||||||
- Production builds: `eas build --profile production`
|
|
||||||
|
|
||||||
## Authentication Flow
|
### Build Commands
|
||||||
|
|
||||||
The application implements a phone number-based authentication system with OTP verification:
|
#### EAS Build (Production)
|
||||||
|
```bash
|
||||||
|
# Production build (App Store / Play Store)
|
||||||
|
eas build --profile production
|
||||||
|
|
||||||
1. **Login**: User enters phone number → OTP sent via SMS
|
# Preview build (Internal distribution)
|
||||||
2. **Verification**: User enters OTP code → Validates and creates session
|
eas build --profile preview
|
||||||
3. **Registration**: If user doesn't exist, registration flow is triggered
|
|
||||||
4. **Terms Agreement**: User must accept terms and conditions
|
|
||||||
5. **Session Management**: Tokens stored in AsyncStorage
|
|
||||||
|
|
||||||
### Key Authentication Functions
|
# Development build (Development client)
|
||||||
- `loginWithNomor()`: Initiates OTP sending
|
eas build --profile development
|
||||||
- `validateOtp()`: Verifies OTP and creates session
|
```
|
||||||
- `registerUser()`: Registers new users
|
|
||||||
- `logout()`: Clears session and removes tokens
|
|
||||||
- `acceptedTerms()`: Handles terms acceptance
|
|
||||||
|
|
||||||
## Key Features
|
#### Local Native Builds
|
||||||
|
```bash
|
||||||
|
# Generate native folders (iOS & Android)
|
||||||
|
npx expo prebuild
|
||||||
|
|
||||||
### User Management
|
# iOS specific
|
||||||
- Phone number-based registration and login
|
bunx expo prebuild --platform ios
|
||||||
- OTP verification system
|
open ios/HIPMIBadungConnect.xcworkspace
|
||||||
- Terms and conditions agreement
|
|
||||||
- User profile management
|
|
||||||
|
|
||||||
### Business Features
|
# Android specific
|
||||||
- Business field management (admin section)
|
bunx expo prebuild --platform android
|
||||||
- File and image management capabilities
|
```
|
||||||
- Location services integration
|
|
||||||
- Push notifications
|
|
||||||
|
|
||||||
### UI Components
|
#### Version Management
|
||||||
- Custom color palette with blue/yellow theme
|
```bash
|
||||||
- Responsive layouts using SafeAreaView
|
# Patch version update
|
||||||
- Toast notifications for user feedback
|
npm version patch
|
||||||
- Bottom tab navigation and drawer navigation
|
|
||||||
|
# Update iOS build number
|
||||||
|
bunx expo prebuild --platform ios
|
||||||
|
|
||||||
|
# Update Android version code
|
||||||
|
bunx expo prebuild --platform android
|
||||||
|
```
|
||||||
|
|
||||||
|
### Android Debugging
|
||||||
|
```bash
|
||||||
|
# List connected devices
|
||||||
|
adb devices
|
||||||
|
|
||||||
|
# Install APK to device/emulator
|
||||||
|
adb install android/app/build/outputs/apk/debug/app-debug.apk
|
||||||
|
|
||||||
|
# Install to specific device
|
||||||
|
adb -s <device_id> install android/app/build/outputs/apk/debug/app-debug.apk
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Create a `.env` file in the project root with:
|
||||||
|
|
||||||
|
```env
|
||||||
|
API_BASE_URL=https://your-api-base-url.com
|
||||||
|
BASE_URL=https://your-app-url.com
|
||||||
|
DEEP_LINK_URL=hipmimobile://
|
||||||
|
```
|
||||||
|
|
||||||
|
These are loaded in `app.config.js` and accessible via `Constants.expoConfig.extra`.
|
||||||
|
|
||||||
|
## Architecture Patterns
|
||||||
|
|
||||||
|
### 1. Separation of Concerns
|
||||||
|
|
||||||
|
**Route Files** (`app/`) should be minimal (max 5 lines):
|
||||||
|
```typescript
|
||||||
|
import { Admin_ScreenXXX } from "@/screens/Admin/XXX/ScreenXXX";
|
||||||
|
|
||||||
|
export default function AdminXXX() {
|
||||||
|
return <Admin_ScreenXXX />;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Screen Components** (`screens/`) contain all business logic:
|
||||||
|
```typescript
|
||||||
|
export function Admin_ScreenXXX() {
|
||||||
|
// Logic, hooks, state management
|
||||||
|
return <NewWrapper ... />;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Pagination Pattern
|
||||||
|
|
||||||
|
Using `usePagination` hook with infinite scroll:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const pagination = usePagination({
|
||||||
|
fetchFunction: async (page, searchQuery) => {
|
||||||
|
const response = await apiXXX({ page: String(page) });
|
||||||
|
if (response.success) {
|
||||||
|
return { data: response.data };
|
||||||
|
}
|
||||||
|
return { data: [] };
|
||||||
|
},
|
||||||
|
pageSize: PAGINATION_DEFAULT_TAKE, // 10
|
||||||
|
searchQuery: search,
|
||||||
|
dependencies: [dependency],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { ListEmptyComponent, ListFooterComponent } =
|
||||||
|
createPaginationComponents({
|
||||||
|
loading: pagination.loading,
|
||||||
|
refreshing: pagination.refreshing,
|
||||||
|
listData: pagination.listData,
|
||||||
|
emptyMessage: "Belum ada data",
|
||||||
|
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Wrapper Components
|
||||||
|
|
||||||
|
**NewWrapper** (preferred for lists):
|
||||||
|
```typescript
|
||||||
|
<NewWrapper
|
||||||
|
listData={pagination.listData}
|
||||||
|
renderItem={renderItem}
|
||||||
|
headerComponent={headerComponent}
|
||||||
|
ListEmptyComponent={ListEmptyComponent}
|
||||||
|
ListFooterComponent={ListFooterComponent}
|
||||||
|
onEndReached={pagination.loadMore}
|
||||||
|
refreshControl={<RefreshControl ... />}
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
**AdminBasicBox** (for card layouts):
|
||||||
|
```typescript
|
||||||
|
<AdminBasicBox
|
||||||
|
onPress={() => router.push(`/path/${item.id}`)}
|
||||||
|
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||||
|
>
|
||||||
|
<StackCustom gap={0}>
|
||||||
|
<GridSpan_4_8 label="Label" value={<TextCustom>Value</TextCustom>} />
|
||||||
|
</StackCustom>
|
||||||
|
</AdminBasicBox>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. API Service Structure
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// service/api-admin/api-xxx.ts
|
||||||
|
export async function apiXXX({ page = "1" }: { page?: string }) {
|
||||||
|
try {
|
||||||
|
const response = await apiConfig.get(`/mobile/admin/xxx?page=${page}`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important**: All list APIs should support pagination with `page` parameter (default: "1").
|
||||||
|
|
||||||
|
### 5. Authentication Flow
|
||||||
|
|
||||||
|
Managed by `AuthContext`:
|
||||||
|
- `loginWithNomor()` - Send phone number, receive OTP
|
||||||
|
- `validateOtp()` - Validate OTP, get token
|
||||||
|
- `registerUser()` - Register new user
|
||||||
|
- `logout()` - Clear session and logout
|
||||||
|
- `userData()` - Fetch user data by token
|
||||||
|
|
||||||
## Development Conventions
|
## Development Conventions
|
||||||
|
|
||||||
### Naming Conventions
|
### Coding Standards
|
||||||
- Components: PascalCase (e.g., `UserProfile.tsx`)
|
- **TypeScript**: Strict mode enabled
|
||||||
- Functions: camelCase (e.g., `getUserData()`)
|
- **Naming**:
|
||||||
- Constants: UPPER_SNAKE_CASE (e.g., `API_BASE_URL`)
|
- Components: PascalCase (`Admin_ScreenDonationStatus`)
|
||||||
- Files: kebab-case or camelCase for utility files
|
- Files: PascalCase for components (`ScreenDonationStatus.tsx`)
|
||||||
|
- Variables: camelCase
|
||||||
|
- Constants: UPPER_SNAKE_CASE
|
||||||
|
- **Path Aliases**: `@/*` maps to project root
|
||||||
|
- **Imports**: Group imports by type (components, hooks, services, etc.)
|
||||||
|
|
||||||
### Code Organization
|
### Component Structure
|
||||||
- Components are organized by feature/functionality
|
```typescript
|
||||||
- API services are centralized in the `service/` directory
|
// 1. Imports (grouped)
|
||||||
- Type definitions are maintained in the `types/` directory
|
import { ... } from "@/components";
|
||||||
- Constants are grouped by category in the `constants/` directory
|
import { ... } from "@/hooks";
|
||||||
|
import { ... } from "@/service";
|
||||||
|
|
||||||
### Styling Approach
|
// 2. Types/Interfaces
|
||||||
- Color palette defined in `constants/color-palet.ts`
|
interface Props { ... }
|
||||||
- Reusable styles and themes centralized
|
|
||||||
- Responsive design using React Native's flexbox system
|
// 3. Main Component
|
||||||
|
export function ComponentName() {
|
||||||
|
// State
|
||||||
|
// Hooks
|
||||||
|
// Functions
|
||||||
|
// Render
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
- Linting: `bun run lint` (uses ESLint with Expo config)
|
- Linting: `bun run lint`
|
||||||
- No specific test framework mentioned in package.json
|
- No formal test suite configured yet
|
||||||
|
|
||||||
## Environment Configuration
|
### Git Workflow
|
||||||
|
- Feature branches: `feature/xxx` or `fixed-admin/xxx`
|
||||||
|
- Commit messages: Clear and descriptive
|
||||||
|
- Use CHANGE_LOG.md for tracking changes
|
||||||
|
|
||||||
The application supports multiple environments through:
|
## Key Features
|
||||||
- Environment variables loaded via dotenv
|
|
||||||
- Expo's extra configuration in `app.config.js`
|
|
||||||
- Platform-specific configurations for iOS and Android
|
|
||||||
|
|
||||||
### Supported Platforms
|
### Authentication
|
||||||
- iOS (with tablet support)
|
- Phone number login with OTP
|
||||||
- Android (with adaptive icons)
|
- User registration
|
||||||
- Web (static output)
|
- Terms & Conditions acceptance
|
||||||
|
- Session persistence with AsyncStorage
|
||||||
|
|
||||||
## Third-party Integrations
|
### Admin Module
|
||||||
|
- **Dashboard**: Overview and statistics
|
||||||
|
- **User Access**: User management
|
||||||
|
- **Event**: Event CRUD with status management
|
||||||
|
- **Voting**: Voting management (publish/review/reject)
|
||||||
|
- **Donation**: Donation management with categories and transaction tracking
|
||||||
|
- **Collaboration**: Collaboration requests
|
||||||
|
- **Investment**: Investment management
|
||||||
|
- **Maps**: Location-based features
|
||||||
|
- **App Information**: Bank and business field management
|
||||||
|
|
||||||
- **Firebase**: Authentication, messaging, and analytics
|
### User Module
|
||||||
- **Mapbox**: Advanced mapping capabilities
|
- **Home**: Main dashboard
|
||||||
- **React Navigation**: Screen navigation and routing
|
- **Forum**: Discussion forums
|
||||||
- **React Native Paper**: Material Design components
|
- **Profile**: User profile management
|
||||||
- **Axios**: HTTP client for API requests
|
- **Portfolio**: Member portfolio
|
||||||
- **Lodash**: Utility functions
|
- **Notifications**: Push notifications via Firebase
|
||||||
- **QR Code SVG**: QR code generation
|
|
||||||
|
|
||||||
## Important Configuration Files
|
## API Configuration
|
||||||
|
|
||||||
- `app.config.js`: Expo configuration, app metadata, and plugin setup
|
### Base URLs
|
||||||
- `eas.json`: EAS build profiles and submission configuration
|
```typescript
|
||||||
- `tsconfig.json`: TypeScript compiler options
|
// From app.config.js extra
|
||||||
- `package.json`: Dependencies, scripts, and project metadata
|
API_BASE_URL: process.env.API_BASE_URL
|
||||||
- `metro.config.js`: Metro bundler configuration
|
BASE_URL: process.env.BASE_URL
|
||||||
|
DEEP_LINK_URL: process.env.DEEP_LINK_URL
|
||||||
|
```
|
||||||
|
|
||||||
|
### Axios Interceptor
|
||||||
|
All API calls use `apiConfig` with automatic token injection:
|
||||||
|
```typescript
|
||||||
|
apiConfig.interceptors.request.use(async (config) => {
|
||||||
|
const token = await AsyncStorage.getItem("authToken");
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Platform Configuration
|
||||||
|
|
||||||
|
### iOS
|
||||||
|
- **Bundle ID**: `com.anonymous.hipmi-mobile`
|
||||||
|
- **Build Number**: 21
|
||||||
|
- **Google Services**: Configured
|
||||||
|
- **Associated Domains**: `applinks:cld-dkr-staging-hipmi.wibudev.com`
|
||||||
|
- **Tablet Support**: Enabled
|
||||||
|
|
||||||
|
### Android
|
||||||
|
- **Package**: `com.bip.hipmimobileapp`
|
||||||
|
- **Version Code**: 4
|
||||||
|
- **Google Services**: Configured (`google-services.json`)
|
||||||
|
- **Deep Links**: HTTPS intent filters configured
|
||||||
|
- **Edge-to-Edge**: Enabled
|
||||||
|
|
||||||
|
### Web
|
||||||
|
- **Output**: Static
|
||||||
|
- **Bundler**: Metro
|
||||||
|
|
||||||
|
## Special Integrations
|
||||||
|
|
||||||
|
### Firebase
|
||||||
|
- Authentication
|
||||||
|
- Push Notifications (FCM)
|
||||||
|
- Configured for both iOS and Android
|
||||||
|
|
||||||
|
### Maplibre
|
||||||
|
- Map integration via `@maplibre/maplibre-react-native`
|
||||||
|
- Location permissions configured
|
||||||
|
|
||||||
|
### Deep Linking
|
||||||
|
- Scheme: `hipmimobile://`
|
||||||
|
- HTTPS: `cld-dkr-staging-hipmi.wibudev.com`
|
||||||
|
- Configured for both platforms
|
||||||
|
|
||||||
|
### Camera
|
||||||
|
- Camera and microphone permissions
|
||||||
|
- QR code generation support
|
||||||
|
|
||||||
|
## Common Development Tasks
|
||||||
|
|
||||||
|
### Adding a New Admin Screen
|
||||||
|
|
||||||
|
1. **Create Screen Component** (`screens/Admin/Feature/ScreenXXX.tsx`):
|
||||||
|
```typescript
|
||||||
|
export function Admin_ScreenXXX() {
|
||||||
|
const pagination = usePagination({...});
|
||||||
|
const renderItem = useCallback(...);
|
||||||
|
const headerComponent = useMemo(...);
|
||||||
|
|
||||||
|
return <NewWrapper ... />;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Create Box Component** (optional, for custom item rendering):
|
||||||
|
```typescript
|
||||||
|
export default function Admin_BoxXXX({ item }: { item: any }) {
|
||||||
|
return (
|
||||||
|
<AdminBasicBox onPress={() => router.push(...)}>
|
||||||
|
...
|
||||||
|
</AdminBasicBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Update API** (add pagination if needed):
|
||||||
|
```typescript
|
||||||
|
export async function apiXXX({ page = "1" }: { page?: string }) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Create Route File** (`app/(application)/admin/feature/xxx.tsx`):
|
||||||
|
```typescript
|
||||||
|
import { Admin_ScreenXXX } from "@/screens/Admin/Feature/ScreenXXX";
|
||||||
|
|
||||||
|
export default function AdminXXX() {
|
||||||
|
return <Admin_ScreenXXX />;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Updating API Endpoints
|
||||||
|
|
||||||
|
1. Add function in appropriate service file
|
||||||
|
2. Include `page` parameter for list endpoints
|
||||||
|
3. Use `apiConfig` axios instance
|
||||||
|
4. Handle errors properly
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Build Issues
|
||||||
|
```bash
|
||||||
|
# Clean and rebuild
|
||||||
|
rm -rf node_modules
|
||||||
|
bun install
|
||||||
|
bunx expo prebuild --clean
|
||||||
|
|
||||||
|
# iOS specific
|
||||||
|
cd ios && pod install && cd ..
|
||||||
|
|
||||||
|
# Android specific
|
||||||
|
cd android && ./gradlew clean && cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cache Issues
|
||||||
|
```bash
|
||||||
|
# Clear Expo cache
|
||||||
|
bunx expo start -c
|
||||||
|
|
||||||
|
# Clear Metro cache
|
||||||
|
bunx expo start --clear
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dependency Issues
|
||||||
|
```bash
|
||||||
|
# Reinstall dependencies
|
||||||
|
rm -rf node_modules bun.lock
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
### iOS Maplibre Crash Fix
|
||||||
|
|
||||||
|
When using Maplibre MapView on iOS, prevent "Attempt to recycle a mounted view" crash:
|
||||||
|
|
||||||
|
1. **Always render PointAnnotation** (not conditional)
|
||||||
|
2. **Use opacity for visibility** instead of conditional rendering
|
||||||
|
3. **Avoid key prop changes** that force remounting
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// ✅ GOOD: Stable PointAnnotation
|
||||||
|
<PointAnnotation
|
||||||
|
coordinate={annotationCoordinate} // Always rendered
|
||||||
|
...
|
||||||
|
>
|
||||||
|
<View style={{ opacity: selectedLocation ? 1 : 0 }}>
|
||||||
|
<SelectedLocationMarker />
|
||||||
|
</View>
|
||||||
|
</PointAnnotation>
|
||||||
|
|
||||||
|
// ❌ BAD: Conditional rendering causes crash
|
||||||
|
{selectedLocation && (
|
||||||
|
<PointAnnotation coordinate={selectedLocation} ... />
|
||||||
|
)}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation Files
|
||||||
|
|
||||||
|
- `docs/CHANGE_LOG.md` - Change log for recent updates
|
||||||
|
- `docs/hipmi-note.md` - Build and deployment notes
|
||||||
|
- `docs/prompt-for-qwen-code.md` - Development prompts and patterns
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Expo Documentation](https://docs.expo.dev/)
|
||||||
|
- [React Native Documentation](https://reactnative.dev/)
|
||||||
|
- [Expo Router Documentation](https://docs.expo.dev/router/introduction/)
|
||||||
|
- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
|
||||||
|
- [Maplibre React Native](https://github.com/maplibre/maplibre-react-native)
|
||||||
|
|||||||
@@ -100,8 +100,8 @@ 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 4
|
versionCode 1
|
||||||
versionName "1.0.1"
|
versionName "1.0.2"
|
||||||
|
|
||||||
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
|
buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter android:autoVerify="true" data-generated="true">
|
<intent-filter android:autoVerify="true" data-generated="true">
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<data android:scheme="https" android:host="cld-dkr-staging-hipmi.wibudev.com" android:pathPrefix="/"/>
|
<data android:scheme="https" android:host="cld-dkr-hipmi-stg.wibudev.com" android:pathPrefix="/"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
// app.config.js
|
// app.config.js
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
|
|
||||||
|
// const isDev = process.env.NODE_ENV === "development";
|
||||||
|
// const isStaging = process.env.NEXT_PUBLIC_ENV === "staging";
|
||||||
|
// const isProd = process.env.NEXT_PUBLIC_ENV === "production";
|
||||||
|
|
||||||
|
// Domain berdasarkan environment
|
||||||
|
// const domain = isDev
|
||||||
|
// ? "localhost:3000"
|
||||||
|
// : isStaging
|
||||||
|
// ? "cld-dkr-hipmi-stg.wibudev.com"
|
||||||
|
// : "hipmi.muku.id"; // Production domain
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "HIPMI Badung Connect",
|
name: "HIPMI Badung Connect",
|
||||||
slug: "hipmi-mobile",
|
slug: "hipmi-mobile",
|
||||||
version: "1.0.1",
|
version: "1.0.2",
|
||||||
orientation: "portrait",
|
orientation: "portrait",
|
||||||
icon: "./assets/images/icon.png",
|
icon: "./assets/images/icon.png",
|
||||||
scheme: "hipmimobile",
|
scheme: "hipmimobile",
|
||||||
@@ -20,8 +31,10 @@ export default {
|
|||||||
NSLocationWhenInUseUsageDescription:
|
NSLocationWhenInUseUsageDescription:
|
||||||
"Aplikasi membutuhkan akses lokasi untuk menampilkan peta.",
|
"Aplikasi membutuhkan akses lokasi untuk menampilkan peta.",
|
||||||
},
|
},
|
||||||
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"],
|
associatedDomains: [
|
||||||
buildNumber: "20",
|
"applinks:cld-dkr-hipmi-stg.wibudev.com",
|
||||||
|
],
|
||||||
|
buildNumber: "4",
|
||||||
},
|
},
|
||||||
|
|
||||||
android: {
|
android: {
|
||||||
@@ -32,7 +45,7 @@ export default {
|
|||||||
},
|
},
|
||||||
edgeToEdgeEnabled: true,
|
edgeToEdgeEnabled: true,
|
||||||
package: "com.bip.hipmimobileapp",
|
package: "com.bip.hipmimobileapp",
|
||||||
versionCode: 4,
|
versionCode: 1,
|
||||||
// softwareKeyboardLayoutMode: 'resize', // option: untuk mengatur keyboard pada room chst collaboration
|
// softwareKeyboardLayoutMode: 'resize', // option: untuk mengatur keyboard pada room chst collaboration
|
||||||
intentFilters: [
|
intentFilters: [
|
||||||
{
|
{
|
||||||
@@ -41,7 +54,7 @@ export default {
|
|||||||
data: [
|
data: [
|
||||||
{
|
{
|
||||||
scheme: "https",
|
scheme: "https",
|
||||||
host: "cld-dkr-staging-hipmi.wibudev.com",
|
host: "cld-dkr-hipmi-stg.wibudev.com",
|
||||||
pathPrefix: "/",
|
pathPrefix: "/",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -77,7 +90,6 @@ export default {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
"expo-font",
|
"expo-font",
|
||||||
"@rnmapbox/maps",
|
|
||||||
"@react-native-firebase/app",
|
"@react-native-firebase/app",
|
||||||
[
|
[
|
||||||
"expo-notifications",
|
"expo-notifications",
|
||||||
@@ -87,6 +99,7 @@ export default {
|
|||||||
iosDisplayInForeground: true,
|
iosDisplayInForeground: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"@maplibre/maplibre-react-native",
|
||||||
],
|
],
|
||||||
|
|
||||||
experiments: {
|
experiments: {
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export default function TakePicture() {
|
|||||||
</Pressable>
|
</Pressable>
|
||||||
|
|
||||||
<Pressable onPress={pickImage}>
|
<Pressable onPress={pickImage}>
|
||||||
<AntDesign name="folderopen" size={32} color="white" />
|
<AntDesign name="folder-open" size={32} color="white" />
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -1,56 +1,9 @@
|
|||||||
import {
|
import Donation_ScreenBeranda from "@/screens/Donation/ScreenBeranda";
|
||||||
FloatingButton,
|
|
||||||
LoaderCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import Donation_BoxPublish from "@/screens/Donation/BoxPublish";
|
|
||||||
import { apiDonationGetAll } from "@/service/api-client/api-donation";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function DonationBeranda() {
|
export default function DonationBeranda() {
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiDonationGetAll({
|
|
||||||
category: "beranda"
|
|
||||||
});
|
|
||||||
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper
|
<>
|
||||||
hideFooter
|
<Donation_ScreenBeranda />
|
||||||
floatingButton={
|
</>
|
||||||
<FloatingButton onPress={() => router.push("/donation/create")} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" color="gray">Belum ada donasi</TextCustom>
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<Donation_BoxPublish data={item} key={index} id={item.id} />
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,148 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import Donation_ScreenMyDonation from "@/screens/Donation/ScreenMyDonation";
|
||||||
import {
|
|
||||||
BadgeCustom,
|
|
||||||
BaseBox,
|
|
||||||
DummyLandscapeImage,
|
|
||||||
Grid,
|
|
||||||
LoaderCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { apiDonationGetAll } from "@/service/api-client/api-donation";
|
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
|
||||||
import { Href, router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
|
|
||||||
export default function DonationMyDonation() {
|
export default function DonationMyDonation() {
|
||||||
const { user } = useAuth();
|
return <Donation_ScreenMyDonation />;
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [user?.id]),
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
if (!user?.id) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Load data gagal, user tidak ditemukan",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiDonationGetAll({
|
|
||||||
category: "my-donation",
|
|
||||||
authorId: user?.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
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 = ({
|
|
||||||
invoiceId,
|
|
||||||
donationId,
|
|
||||||
status,
|
|
||||||
}: {
|
|
||||||
invoiceId: string;
|
|
||||||
donationId: string;
|
|
||||||
status: string;
|
|
||||||
}) => {
|
|
||||||
const url: Href = `../${donationId}/(transaction-flow)/${invoiceId}`;
|
|
||||||
if (status === "menunggu") {
|
|
||||||
router.push(`${url}/invoice`);
|
|
||||||
} else if (status === "proses") {
|
|
||||||
router.push(`${url}/process`);
|
|
||||||
} else if (status === "berhasil") {
|
|
||||||
router.push(`${url}/success`);
|
|
||||||
} else if (status === "gagal") {
|
|
||||||
router.push(`${url}/failed`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ViewWrapper hideFooter>
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Belum ada transaksi
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
list?.map((item, index) => (
|
|
||||||
<BaseBox
|
|
||||||
key={index}
|
|
||||||
paddingTop={7}
|
|
||||||
paddingBottom={7}
|
|
||||||
onPress={() => {
|
|
||||||
handlePress({
|
|
||||||
status: _.lowerCase(item.statusInvoice),
|
|
||||||
invoiceId: item.id,
|
|
||||||
donationId: item.donasiId,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={5}>
|
|
||||||
<DummyLandscapeImage
|
|
||||||
height={100}
|
|
||||||
unClickPath
|
|
||||||
imageId={item.imageId}
|
|
||||||
/>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={1}>
|
|
||||||
<View />
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={6}>
|
|
||||||
<StackCustom>
|
|
||||||
<TextCustom truncate={2} bold>
|
|
||||||
{item.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
|
|
||||||
<TextCustom bold color="yellow">
|
|
||||||
Rp. {formatCurrencyDisplay(item.nominal)}
|
|
||||||
</TextCustom>
|
|
||||||
|
|
||||||
<BadgeCustom
|
|
||||||
variant="light"
|
|
||||||
color={handlerColor(_.lowerCase(item.statusInvoice))}
|
|
||||||
fullWidth
|
|
||||||
>
|
|
||||||
{item.statusInvoice}
|
|
||||||
</BadgeCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,82 +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, useLocalSearchParams } 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 { status } = useLocalSearchParams<{ status?: string }>();
|
||||||
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
|
||||||
status || "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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -31,7 +32,7 @@ export default function DonationEditNews() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [news])
|
}, [news]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -104,7 +105,21 @@ export default function DonationEditNews() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<ViewWrapper
|
||||||
|
footerComponent={
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
disabled={!data?.title || !data?.deskripsi}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={() => {
|
||||||
|
handlerSubmitUpdate();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Update
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
}
|
||||||
|
>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="Upload gambar bersifat opsional untuk melengkapi kabar terkait donasi Anda." />
|
<InformationBox text="Upload gambar bersifat opsional untuk melengkapi kabar terkait donasi Anda." />
|
||||||
<LandscapeFrameUploaded
|
<LandscapeFrameUploaded
|
||||||
@@ -148,15 +163,6 @@ export default function DonationEditNews() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom
|
|
||||||
disabled={!data?.title || !data?.deskripsi}
|
|
||||||
isLoading={isLoading}
|
|
||||||
onPress={() => {
|
|
||||||
handlerSubmitUpdate();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
InformationBox,
|
InformationBox,
|
||||||
LandscapeFrameUploaded,
|
LandscapeFrameUploaded,
|
||||||
|
NewWrapper,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
@@ -53,7 +55,7 @@ export default function DonationAddNews() {
|
|||||||
text1: "Gagal menambah berita",
|
text1: "Gagal menambah berita",
|
||||||
});
|
});
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast.show({
|
Toast.show({
|
||||||
@@ -70,7 +72,21 @@ export default function DonationAddNews() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
|
footerComponent={
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
disabled={!data.title || !data.deskripsi}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={() => {
|
||||||
|
handlerSubmit();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
}
|
||||||
|
>
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<InformationBox text="Upload gambar bersifat opsional untuk melengkapi kabar terkait donasi Anda." />
|
<InformationBox text="Upload gambar bersifat opsional untuk melengkapi kabar terkait donasi Anda." />
|
||||||
<LandscapeFrameUploaded image={image?.uri} />
|
<LandscapeFrameUploaded image={image?.uri} />
|
||||||
@@ -116,17 +132,7 @@ export default function DonationAddNews() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom
|
|
||||||
disabled={!data.title || !data.deskripsi}
|
|
||||||
isLoading={isLoading}
|
|
||||||
onPress={() => {
|
|
||||||
handlerSubmit();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Simpan
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
<Spacing />
|
</NewWrapper>
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,110 +1,8 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import {
|
import Donation_ScreenListOfNews from "@/screens/Donation/ScreenListOfNews";
|
||||||
BackButton,
|
|
||||||
BaseBox,
|
|
||||||
DrawerCustom,
|
|
||||||
Grid,
|
|
||||||
LoaderCustom,
|
|
||||||
MenuDrawerDynamicGrid,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconPlus } from "@/components/_Icon";
|
|
||||||
import { apiDonationGetNewsById } from "@/service/api-client/api-donation";
|
|
||||||
import { formatChatTime } from "@/utils/formatChatTime";
|
|
||||||
import {
|
|
||||||
router,
|
|
||||||
Stack,
|
|
||||||
useFocusEffect,
|
|
||||||
useLocalSearchParams,
|
|
||||||
} from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function DonationRecapOfNews() {
|
export default function DonationRecapOfNews() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
return <Donation_ScreenListOfNews donationId={id as string} />;
|
||||||
useCallback(() => {
|
|
||||||
onLoadList();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiDonationGetNewsById({
|
|
||||||
id: id as string,
|
|
||||||
category: "get-all",
|
|
||||||
});
|
|
||||||
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setList([]);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Stack.Screen
|
|
||||||
options={{
|
|
||||||
title: "Daftar Kabar",
|
|
||||||
headerLeft: () => <BackButton />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ViewWrapper>
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Tidak ada kabar
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<BaseBox key={index} href={`/donation/[id]/(news)/${item.id}`}>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={8}>
|
|
||||||
<TextCustom truncate bold>
|
|
||||||
{item?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
|
||||||
<TextCustom size="small">
|
|
||||||
{formatChatTime(item?.createdAt)}
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<MenuDrawerDynamicGrid
|
|
||||||
data={[
|
|
||||||
{
|
|
||||||
icon: <IconPlus />,
|
|
||||||
label: "Tambah Berita",
|
|
||||||
path: `/donation/${id}/(news)/add-news`,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressItem={(item) => {
|
|
||||||
console.log("PATH ", item.path);
|
|
||||||
router.navigate(item.path as any);
|
|
||||||
setOpenDrawer(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,112 +1,8 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import {
|
import Donation_ScreenRecapOfNews from "@/screens/Donation/ScreenRecapOfNews";
|
||||||
BackButton,
|
|
||||||
BaseBox,
|
|
||||||
DotButton,
|
|
||||||
DrawerCustom,
|
|
||||||
Grid,
|
|
||||||
LoaderCustom,
|
|
||||||
MenuDrawerDynamicGrid,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconPlus } from "@/components/_Icon";
|
|
||||||
import { apiDonationGetNewsById } from "@/service/api-client/api-donation";
|
|
||||||
import { formatChatTime } from "@/utils/formatChatTime";
|
|
||||||
import {
|
|
||||||
router,
|
|
||||||
Stack,
|
|
||||||
useFocusEffect,
|
|
||||||
useLocalSearchParams,
|
|
||||||
} from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function DonationRecapOfNews() {
|
export default function DonationRecapOfNews() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
return <Donation_ScreenRecapOfNews donationId={id as string} />;
|
||||||
useCallback(() => {
|
|
||||||
onLoadList();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiDonationGetNewsById({
|
|
||||||
id: id as string,
|
|
||||||
category: "get-all",
|
|
||||||
});
|
|
||||||
|
|
||||||
setList(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setList([]);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Stack.Screen
|
|
||||||
options={{
|
|
||||||
title: "Rekap Kabar",
|
|
||||||
headerLeft: () => <BackButton />,
|
|
||||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ViewWrapper>
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Tidak ada kabar
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<BaseBox key={index} href={`/donation/[id]/(news)/${item.id}`}>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={8}>
|
|
||||||
<TextCustom truncate bold>
|
|
||||||
{item?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
|
||||||
<TextCustom size="small">
|
|
||||||
{formatChatTime(item?.createdAt)}
|
|
||||||
</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<MenuDrawerDynamicGrid
|
|
||||||
data={[
|
|
||||||
{
|
|
||||||
icon: <IconPlus />,
|
|
||||||
label: "Tambah Berita",
|
|
||||||
path: `/donation/${id}/(news)/add-news`,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressItem={(item) => {
|
|
||||||
console.log("PATH ", item.path);
|
|
||||||
router.navigate(item.path as any);
|
|
||||||
setOpenDrawer(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
|
BoxButtonOnFooter,
|
||||||
ButtonCenteredOnly,
|
ButtonCenteredOnly,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
Grid,
|
Grid,
|
||||||
@@ -35,7 +36,7 @@ export default function DonationInvoice() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [invoiceId])
|
}, [invoiceId]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -100,7 +101,22 @@ export default function DonationInvoice() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper>
|
<ViewWrapper
|
||||||
|
hideFooter
|
||||||
|
footerComponent={
|
||||||
|
<BoxButtonOnFooter>
|
||||||
|
<ButtonCustom
|
||||||
|
disabled={!image}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onPress={() => {
|
||||||
|
handlerUpdateInvoice();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</ButtonCustom>
|
||||||
|
</BoxButtonOnFooter>
|
||||||
|
}
|
||||||
|
>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<InformationBox
|
<InformationBox
|
||||||
text={`Mohon transfer donasi anda ke rekening dibawah`}
|
text={`Mohon transfer donasi anda ke rekening dibawah`}
|
||||||
@@ -204,16 +220,6 @@ export default function DonationInvoice() {
|
|||||||
</ButtonCenteredOnly>
|
</ButtonCenteredOnly>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
<ButtonCustom
|
|
||||||
disabled={!image}
|
|
||||||
isLoading={isLoading}
|
|
||||||
onPress={() => {
|
|
||||||
handlerUpdateInvoice();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Simpan
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import {
|
|||||||
DotButton,
|
DotButton,
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
MenuDrawerDynamicGrid,
|
MenuDrawerDynamicGrid,
|
||||||
|
NewWrapper,
|
||||||
Spacing,
|
Spacing,
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconEdit, IconNews } from "@/components/_Icon";
|
import { IconEdit, IconNews } from "@/components/_Icon";
|
||||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||||
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
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 Donation_ButtonStatusSection from "@/screens/Donation/ButtonStatusSection";
|
import Donation_ButtonStatusSection from "@/screens/Donation/ButtonStatusSection";
|
||||||
@@ -26,18 +27,19 @@ import {
|
|||||||
} from "expo-router";
|
} from "expo-router";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
import { RefreshControl } from "react-native";
|
||||||
|
|
||||||
export default function DonasiDetailStatus() {
|
export default function DonasiDetailStatus() {
|
||||||
const { id, status } = useLocalSearchParams();
|
const { id, status } = useLocalSearchParams();
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
const [openDrawer, setOpenDrawer] = useState(false);
|
||||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||||
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
const [data, setData] = useState<any>();
|
const [data, setData] = useState<any | null>(null);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -80,6 +82,17 @@ export default function DonasiDetailStatus() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onRefresh = useCallback(() => {
|
||||||
|
try {
|
||||||
|
setRefreshing(true);
|
||||||
|
onLoadData();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error refresh");
|
||||||
|
} finally {
|
||||||
|
setRefreshing(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -94,31 +107,50 @@ export default function DonasiDetailStatus() {
|
|||||||
) : null,
|
) : null,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
<Donation_ComponentBoxDetailData
|
refreshControl={
|
||||||
sisaHari={value.sisa}
|
<RefreshControl
|
||||||
reminder={value.reminder}
|
refreshing={refreshing}
|
||||||
data={data}
|
onRefresh={onRefresh}
|
||||||
bottomSection={
|
tintColor={MainColor.yellow}
|
||||||
status === "publish" && (
|
colors={[MainColor.yellow]}
|
||||||
<Donation_ProgressSection
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{!data ? (
|
||||||
|
<CustomSkeleton height={400} />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Donation_ComponentBoxDetailData
|
||||||
|
sisaHari={value.sisa}
|
||||||
|
reminder={value.reminder}
|
||||||
|
data={data}
|
||||||
|
showSisaHari={status === "publish" ? true : false}
|
||||||
|
bottomSection={
|
||||||
|
status === "publish" && (
|
||||||
|
<Donation_ProgressSection
|
||||||
|
id={id as string}
|
||||||
|
progres={Number(data?.progres) || 0}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Donation_ComponentStoryFunrising
|
||||||
|
id={id as string}
|
||||||
|
dataStory={data?.CeritaDonasi}
|
||||||
|
/>
|
||||||
|
<Spacing />
|
||||||
|
{data && (
|
||||||
|
<Donation_ButtonStatusSection
|
||||||
id={id as string}
|
id={id as string}
|
||||||
progres={Number(data?.progres) || 0}
|
status={status as string}
|
||||||
/>
|
/>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
/>
|
<Spacing />
|
||||||
<Donation_ComponentStoryFunrising
|
</>
|
||||||
id={id as string}
|
)}
|
||||||
dataStory={data?.CeritaDonasi}
|
</NewWrapper>
|
||||||
/>
|
|
||||||
<Spacing />
|
|
||||||
<Donation_ButtonStatusSection
|
|
||||||
id={id as string}
|
|
||||||
status={status as string}
|
|
||||||
/>
|
|
||||||
<Spacing />
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
<DrawerCustom
|
||||||
isVisible={openDrawer}
|
isVisible={openDrawer}
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
/* 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,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
|
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||||
import API_IMAGE from "@/constants/api-storage";
|
import API_IMAGE from "@/constants/api-storage";
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
import DIRECTORY_ID from "@/constants/directory-id";
|
||||||
import {
|
import {
|
||||||
@@ -60,7 +63,7 @@ export default function DonationEdit() {
|
|||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
onLoadList();
|
onLoadList();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -79,7 +82,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,10 +184,24 @@ 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 />
|
<ListSkeletonComponent />
|
||||||
) : (
|
) : (
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
@@ -260,17 +276,9 @@ export default function DonationEdit() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom
|
|
||||||
isLoading={isLoading}
|
|
||||||
onPress={() => {
|
|
||||||
handlerSubmitUpdate();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
)}
|
)}
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,124 +1,8 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import {
|
import Donation_ScreenFundDisbursement from "@/screens/Donation/ScreenFundDisbursement";
|
||||||
BaseBox,
|
|
||||||
ButtonCenteredOnly,
|
|
||||||
Grid,
|
|
||||||
InformationBox,
|
|
||||||
LoaderCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import {
|
|
||||||
apiDonationDisbursementOfFundsListById,
|
|
||||||
apiDonationGetOne,
|
|
||||||
} from "@/service/api-client/api-donation";
|
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
export default function DonationFundDisbursement() {
|
export default function DonationFundDisbursement() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
|
|
||||||
const [data, setData] = useState({
|
return <Donation_ScreenFundDisbursement donationId={id as string} />;
|
||||||
totalPencairan: 0,
|
|
||||||
akumulasiPencairan: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [listData, setListData] = React.useState<any[] | null>(null);
|
|
||||||
const [loadData, setLoadData] = React.useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
React.useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
|
|
||||||
const responseData = await apiDonationGetOne({
|
|
||||||
id: id as string,
|
|
||||||
category: "permanent",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (responseData.success) {
|
|
||||||
setData({
|
|
||||||
totalPencairan: responseData.data.totalPencairan,
|
|
||||||
akumulasiPencairan: responseData.data.akumulasiPencairan,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseList = await apiDonationDisbursementOfFundsListById({
|
|
||||||
id: id as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (responseList.success) {
|
|
||||||
setListData(responseList.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper>
|
|
||||||
<InformationBox text="Pencairan dana akan dilakukan oleh Admin HIPMI tanpa campur tangan pihak manapun, jika berita pencairan dana dibawah tidak sesuai dengan kabar yang diberikan oleh PENGGALANG DANA. Maka pegguna lain dapat melaporkannya pada Admin HIPMI !" />
|
|
||||||
<BaseBox>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={6}>
|
|
||||||
<TextCustom bold color="yellow">
|
|
||||||
Rp. {formatCurrencyDisplay(data?.totalPencairan)}
|
|
||||||
</TextCustom>
|
|
||||||
<TextCustom size="small">Total Pencairan Dana</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={6}>
|
|
||||||
<TextCustom bold color="yellow">
|
|
||||||
{data?.akumulasiPencairan} kali
|
|
||||||
</TextCustom>
|
|
||||||
<TextCustom size="small">Akumulasi Pencairan</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Belum ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item, index) => (
|
|
||||||
<BaseBox key={index}>
|
|
||||||
<StackCustom>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={8}>
|
|
||||||
<TextCustom bold>{item?.title}</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
|
||||||
<TextCustom>{dayjs(item?.createdAt).format("DD MMM YYYY")}</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
<TextCustom>{item?.deskripsi}</TextCustom>
|
|
||||||
<ButtonCenteredOnly
|
|
||||||
onPress={() => {
|
|
||||||
router.navigate(`/(application)/(image)/preview-image/${item?.imageId}`);
|
|
||||||
}}
|
|
||||||
icon="file-text"
|
|
||||||
>
|
|
||||||
Bukti Transaksi
|
|
||||||
</ButtonCenteredOnly>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,12 @@ import {
|
|||||||
DotButton,
|
DotButton,
|
||||||
DrawerCustom,
|
DrawerCustom,
|
||||||
MenuDrawerDynamicGrid,
|
MenuDrawerDynamicGrid,
|
||||||
|
NewWrapper,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { IconNews } from "@/components/_Icon";
|
import { IconNews } from "@/components/_Icon";
|
||||||
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import Donation_ComponentBoxDetailData from "@/screens/Donation/ComponentBoxDetailData";
|
import Donation_ComponentBoxDetailData from "@/screens/Donation/ComponentBoxDetailData";
|
||||||
import Donation_ComponentInfoFundrising from "@/screens/Donation/ComponentInfoFundrising";
|
import Donation_ComponentInfoFundrising from "@/screens/Donation/ComponentInfoFundrising";
|
||||||
@@ -34,7 +36,7 @@ export default function DonasiDetailBeranda() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -75,10 +77,10 @@ export default function DonasiDetailBeranda() {
|
|||||||
<>
|
<>
|
||||||
<BoxButtonOnFooter>
|
<BoxButtonOnFooter>
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
disabled={value?.reminder}
|
disabled={value?.reminder || !data}
|
||||||
onPress={() => router.navigate(`/donation/${id}/(transaction-flow)`)}
|
onPress={() => router.navigate(`/donation/${id}/(transaction-flow)`)}
|
||||||
>
|
>
|
||||||
{value?.reminder ? "Waktu berakhir" : "Donasi"}
|
{!data ? "Loading..." : value?.reminder ? "Waktu berakhir" : "Donasi"}
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
</BoxButtonOnFooter>
|
</BoxButtonOnFooter>
|
||||||
</>
|
</>
|
||||||
@@ -96,21 +98,30 @@ export default function DonasiDetailBeranda() {
|
|||||||
) : null,
|
) : null,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper footerComponent={buttonSection}>
|
<NewWrapper footerComponent={buttonSection}>
|
||||||
<StackCustom>
|
{!data ? (
|
||||||
<Donation_ComponentBoxDetailData
|
<CustomSkeleton height={400} />
|
||||||
sisaHari={value.sisa}
|
) : (
|
||||||
reminder={value.reminder}
|
<StackCustom>
|
||||||
data={data}
|
<Donation_ComponentBoxDetailData
|
||||||
bottomSection={<Donation_ProgressSection id={id as string} progres={Number(data?.progres) || 0} />}
|
sisaHari={value.sisa}
|
||||||
/>
|
reminder={value.reminder}
|
||||||
<Donation_ComponentInfoFundrising dataAuthor={data?.Author} />
|
data={data}
|
||||||
<Donation_ComponentStoryFunrising
|
bottomSection={
|
||||||
id={id as string}
|
<Donation_ProgressSection
|
||||||
dataStory={data?.CeritaDonasi}
|
id={id as string}
|
||||||
/>
|
progres={Number(data?.progres) || 0}
|
||||||
</StackCustom>
|
/>
|
||||||
</ViewWrapper>
|
}
|
||||||
|
/>
|
||||||
|
<Donation_ComponentInfoFundrising dataAuthor={data?.Author} />
|
||||||
|
<Donation_ComponentStoryFunrising
|
||||||
|
id={id as string}
|
||||||
|
dataStory={data?.CeritaDonasi}
|
||||||
|
/>
|
||||||
|
</StackCustom>
|
||||||
|
)}
|
||||||
|
</NewWrapper>
|
||||||
|
|
||||||
<DrawerCustom
|
<DrawerCustom
|
||||||
isVisible={openDrawer}
|
isVisible={openDrawer}
|
||||||
|
|||||||
@@ -1,94 +1,8 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import {
|
import Donation_ScreenListOfDonatur from "@/screens/Donation/ScreenListOfDonatur";
|
||||||
BaseBox,
|
|
||||||
Grid,
|
|
||||||
LoaderCustom,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { apiAdminDonationListOfDonaturById } from "@/service/api-admin/api-admin-donation";
|
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
|
||||||
import { FontAwesome6 } from "@expo/vector-icons";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function Donation_ListOfDonatur() {
|
export default function DonationListOfDonatur() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [listData, setListData] = useState<any[] | null>(null);
|
|
||||||
const [loadData, setLoadData] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
return <Donation_ScreenListOfDonatur donationId={id as string} />;
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
const response = await apiAdminDonationListOfDonaturById({
|
|
||||||
id: id as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper>
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Belum ada donatur
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item: any, index: number) => (
|
|
||||||
<BaseBox key={index}>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col
|
|
||||||
span={3}
|
|
||||||
style={{ alignItems: "center", justifyContent: "center" }}
|
|
||||||
>
|
|
||||||
<FontAwesome6
|
|
||||||
name="face-smile-wink"
|
|
||||||
size={50}
|
|
||||||
style={{ color: MainColor.yellow }}
|
|
||||||
/>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={9}>
|
|
||||||
<TextCustom bold size="large">
|
|
||||||
{item?.Author?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
<Spacing/>
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<TextCustom size={"small"}>Berdonas sebesar </TextCustom>
|
|
||||||
<TextCustom bold size="large" color="yellow">
|
|
||||||
Rp. {formatCurrencyDisplay(item?.nominal)}
|
|
||||||
</TextCustom>
|
|
||||||
<TextCustom>
|
|
||||||
{dayjs(item?.createdAt).format("DD MMM YYYY, HH:mm")}
|
|
||||||
</TextCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,101 +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, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function EventStatus() {
|
export default function EventStatus() {
|
||||||
const { user } = useAuth();
|
|
||||||
const { status } = useLocalSearchParams<{ status?: string }>();
|
|
||||||
|
|
||||||
const id = user?.id || "";
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
|
||||||
status || "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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import {
|
import {
|
||||||
apiEventConfirmationAction,
|
apiEventConfirmationAction,
|
||||||
@@ -60,7 +60,7 @@ export default function UserEventConfirmation() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
checkTokenAndDataParticipants() || console.log("Token is null");
|
checkTokenAndDataParticipants() || console.log("Token is null");
|
||||||
}, [token, id, user?.id])
|
}, [token, id, user?.id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const checkTokenAndDataParticipants = async () => {
|
const checkTokenAndDataParticipants = async () => {
|
||||||
@@ -113,7 +113,7 @@ export default function UserEventConfirmation() {
|
|||||||
confirmationStart,
|
confirmationStart,
|
||||||
confirmationEnd,
|
confirmationEnd,
|
||||||
null,
|
null,
|
||||||
"[]"
|
"[]",
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- [4] Status waktu event (untuk pesan UI) ---
|
// --- [4] Status waktu event (untuk pesan UI) ---
|
||||||
@@ -218,9 +218,14 @@ export default function UserEventConfirmation() {
|
|||||||
if (isWithinConfirmationWindow) {
|
if (isWithinConfirmationWindow) {
|
||||||
if (konfirmasi === false) {
|
if (konfirmasi === false) {
|
||||||
return (
|
return (
|
||||||
<TamplateBox data={data}>
|
// <TamplateBox data={data}>
|
||||||
<TamplateText text="Konfirmasi Kehadiran" />
|
// <TamplateText text="Konfirmasi Kehadiran" />
|
||||||
</TamplateBox>
|
// </TamplateBox>
|
||||||
|
<UserParticipan_And_DuringEvent
|
||||||
|
id={data.id}
|
||||||
|
userId={user?.id as string}
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -261,16 +266,14 @@ export default function UserEventConfirmation() {
|
|||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{
|
options={{
|
||||||
title: "Konfirmasi Event",
|
title: "Konfirmasi Event",
|
||||||
// headerLeft: () => (
|
headerLeft: () => (
|
||||||
// <Ionicons
|
<Ionicons
|
||||||
// name="arrow-back"
|
name="arrow-back"
|
||||||
// size={20}
|
size={20}
|
||||||
// color={MainColor.yellow}
|
color={MainColor.yellow}
|
||||||
// onPress={() =>
|
onPress={() => router.navigate("/")}
|
||||||
// router.navigate("/(application)/(user)/event/create")
|
/>
|
||||||
// }
|
),
|
||||||
// />
|
|
||||||
// ),
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>{handlerReturn()}</ViewWrapper>
|
<ViewWrapper>{handlerReturn()}</ViewWrapper>
|
||||||
@@ -497,7 +500,6 @@ const UserNotParticipan_And_DuringEvent = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// 🟡 ZONA ACARA BERLANGSUN
|
// 🟡 ZONA ACARA BERLANGSUN
|
||||||
// User sudah terdaftar & Event sedang berlangsung & user harus konfirmasi
|
// User sudah terdaftar & Event sedang berlangsung & user harus konfirmasi
|
||||||
const UserParticipan_And_DuringEvent = ({
|
const UserParticipan_And_DuringEvent = ({
|
||||||
|
|||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,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";
|
||||||
@@ -101,7 +102,6 @@ export default function EventCreate() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const buttonSubmit = (
|
const buttonSubmit = (
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
@@ -112,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"
|
||||||
@@ -121,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
|
||||||
@@ -168,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,115 +1,199 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { StackCustom, ViewWrapper } from "@/components";
|
import { BasicWrapper, StackCustom, ViewWrapper } from "@/components";
|
||||||
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
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 { 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 HeaderBell from "@/screens/Home/HeaderBell";
|
||||||
|
import { stylesHome } from "@/screens/Home/homeViewStyle";
|
||||||
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";
|
||||||
import Home_FeatureSection from "@/screens/Home/topFeatureSection";
|
import Home_FeatureSection from "@/screens/Home/topFeatureSection";
|
||||||
|
import { apiJobGetAll } from "@/service/api-client/api-job";
|
||||||
import { apiUser } from "@/service/api-client/api-user";
|
import { apiUser } from "@/service/api-client/api-user";
|
||||||
import { apiVersion } from "@/service/api-config";
|
import { apiVersion } from "@/service/api-config";
|
||||||
|
import { GStyles } from "@/styles/global-styles";
|
||||||
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, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { RefreshControl } from "react-native";
|
import { RefreshControl, View } 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);
|
||||||
const { syncUnreadCount } = useNotificationStore();
|
const { syncUnreadCount } = useNotificationStore();
|
||||||
|
const [listData, setListData] = useState<any[] | null>(null);
|
||||||
|
|
||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
|
onLoadDataJob();
|
||||||
checkVersion();
|
checkVersion();
|
||||||
userData(token as string);
|
userData(token as string).catch((error) => {
|
||||||
|
console.log("[ERROR userData]", error?.message);
|
||||||
|
console.log("[ERROR userData Response]", error?.response?.data);
|
||||||
|
});
|
||||||
syncUnreadCount();
|
syncUnreadCount();
|
||||||
}, [user?.id, token])
|
}, [user?.id, token]),
|
||||||
);
|
);
|
||||||
|
|
||||||
async function onLoadData() {
|
async function onLoadData() {
|
||||||
const response = await apiUser(user?.id as string);
|
try {
|
||||||
console.log(
|
const response = await apiUser(user?.id as string);
|
||||||
"[Profile ID]>>",
|
setData(response.data);
|
||||||
JSON.stringify(response?.data?.Profile?.id, null, 2)
|
} catch (error: any) {
|
||||||
);
|
console.log("[ERROR onLoadData]", error?.message);
|
||||||
|
console.log("[ERROR Response]", error?.response?.data);
|
||||||
setData(response.data);
|
// Set data tetap agar UI tidak stuck di loading
|
||||||
|
setData(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onLoadDataJob = async () => {
|
||||||
|
try {
|
||||||
|
const response = await apiJobGetAll({
|
||||||
|
category: "beranda",
|
||||||
|
});
|
||||||
|
const result = response.data
|
||||||
|
.sort(
|
||||||
|
(a: any, b: any) =>
|
||||||
|
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
||||||
|
)
|
||||||
|
.slice(0, 2);
|
||||||
|
setListData(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR]", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
const checkVersion = async () => {
|
const checkVersion = async () => {
|
||||||
const response = await apiVersion();
|
try {
|
||||||
console.log("[Version] >>", JSON.stringify(response.data, null, 2));
|
const response = await apiVersion();
|
||||||
|
console.log("[Version] >>", JSON.stringify(response.data, null, 2));
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log("[ERROR checkVersion]", error?.message);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onRefresh = useCallback(() => {
|
const onRefresh = useCallback(() => {
|
||||||
setRefreshing(true);
|
setRefreshing(true);
|
||||||
onLoadData();
|
onLoadData();
|
||||||
|
onLoadDataJob();
|
||||||
checkVersion();
|
checkVersion();
|
||||||
setRefreshing(false);
|
setRefreshing(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// if (user && user?.termsOfServiceAccepted === false) {
|
|
||||||
// console.log("User is not accept term service");
|
|
||||||
// return <Redirect href={`/terms-agreement`} />;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (data && data?.active === false) {
|
if (data && data?.active === false) {
|
||||||
console.log("User is not active");
|
console.warn("User is not active");
|
||||||
return <Redirect href={`/waiting-room`} />;
|
return (
|
||||||
|
<BasicWrapper>
|
||||||
|
<Redirect href={`/waiting-room`} />
|
||||||
|
</BasicWrapper>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data && data?.Profile === null) {
|
if (data && data?.Profile === null) {
|
||||||
console.log("Profile is null");
|
console.warn("Profile is null");
|
||||||
return <Redirect href={`/profile/create`} />;
|
return (
|
||||||
|
<BasicWrapper>
|
||||||
|
<Redirect href={`/profile/create`} />
|
||||||
|
</BasicWrapper>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (data && data?.masterUserRoleId !== "1") {
|
||||||
|
// console.log("User is not admin");
|
||||||
|
// return (
|
||||||
|
// <BasicWrapper>
|
||||||
|
// <Redirect href={`/admin/dashboard`} />
|
||||||
|
// </BasicWrapper>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{
|
options={{
|
||||||
title: `HIPMI`,
|
title: `HIPMI`,
|
||||||
headerLeft: () => (
|
headerLeft: () =>
|
||||||
<Ionicons
|
data ? (
|
||||||
name="search"
|
<Ionicons
|
||||||
size={20}
|
name="search"
|
||||||
color={MainColor.yellow}
|
size={20}
|
||||||
onPress={() => {
|
color={MainColor.yellow}
|
||||||
router.push("/user-search");
|
onPress={() => {
|
||||||
}}
|
router.push("/user-search");
|
||||||
/>
|
}}
|
||||||
),
|
/>
|
||||||
headerRight: () => <HeaderBell />,
|
) : (
|
||||||
|
<CustomSkeleton height={30} width={30} radius={100} />
|
||||||
|
),
|
||||||
|
headerRight: () =>
|
||||||
|
data ? (
|
||||||
|
<HeaderBell />
|
||||||
|
) : (
|
||||||
|
<CustomSkeleton height={30} width={30} radius={100} />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper
|
<ViewWrapper
|
||||||
refreshControl={
|
refreshControl={
|
||||||
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
|
<RefreshControl
|
||||||
|
refreshing={refreshing}
|
||||||
|
onRefresh={onRefresh}
|
||||||
|
tintColor={MainColor.yellow}
|
||||||
|
colors={[MainColor.yellow]}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
footerComponent={
|
footerComponent={
|
||||||
<TabSection
|
data && data ? (
|
||||||
tabs={tabsHome({
|
<TabSection
|
||||||
acceptedForumTermsAt: data?.acceptedForumTermsAt,
|
tabs={tabsHome({
|
||||||
profileId: data?.Profile?.id,
|
acceptedForumTermsAt: data?.acceptedForumTermsAt,
|
||||||
})}
|
profileId: data?.Profile?.id,
|
||||||
/>
|
})}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<View style={GStyles.tabBar}>
|
||||||
|
<View style={[GStyles.tabContainer, { paddingTop: 10 }]}>
|
||||||
|
{Array.from({ length: 4 }).map((e, index) => (
|
||||||
|
<CustomSkeleton
|
||||||
|
key={index}
|
||||||
|
height={40}
|
||||||
|
width={40}
|
||||||
|
radius={100}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
{/* <ButtonCustom onPress={() => router.push("./test-notifications")}>
|
|
||||||
Test Notif
|
|
||||||
</ButtonCustom> */}
|
|
||||||
|
|
||||||
<Home_ImageSection />
|
<Home_ImageSection />
|
||||||
|
|
||||||
<Home_FeatureSection />
|
{data && data ? (
|
||||||
|
<Home_FeatureSection />
|
||||||
|
) : (
|
||||||
|
<View style={stylesHome.gridContainer}>
|
||||||
|
{Array.from({ length: 4 }).map((item, index) => (
|
||||||
|
<CustomSkeleton
|
||||||
|
key={index}
|
||||||
|
style={stylesHome.gridItem}
|
||||||
|
radius={50}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
<Home_BottomFeatureSection />
|
{data ? (
|
||||||
|
<Home_BottomFeatureSection listData={listData} />
|
||||||
|
) : (
|
||||||
|
<CustomSkeleton height={200} />
|
||||||
|
)}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -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,83 +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}`)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<TextCustom truncate={2}>{item?.title}</TextCustom>
|
|
||||||
<TextCustom>
|
|
||||||
Rp. {formatCurrencyDisplay(item?.nominal)}
|
|
||||||
</TextCustom>
|
|
||||||
<TextCustom>{item?.lembarTerbeli} Lembar</TextCustom>
|
|
||||||
<ProgressCustom
|
|
||||||
label={`${item.progress}%`}
|
|
||||||
value={Number(item.progress)}
|
|
||||||
size="lg"
|
|
||||||
animated
|
|
||||||
color="primary"
|
|
||||||
/>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,82 +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, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function InvestmentPortofolio() {
|
export default function InvestmentPortofolio() {
|
||||||
const { user } = useAuth();
|
|
||||||
const { status } = useLocalSearchParams<{ status?: string }>();
|
|
||||||
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
|
||||||
status || "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>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,230 +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();
|
|
||||||
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",
|
|
||||||
});
|
|
||||||
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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) {
|
|
||||||
setImage(file);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
icon="upload"
|
|
||||||
>
|
|
||||||
Upload
|
|
||||||
</ButtonCenteredOnly>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
<ButtonCustom
|
|
||||||
isLoading={isLoading}
|
|
||||||
disabled={!image || isLoading}
|
|
||||||
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(
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ export default function InvestmentDetailStatus() {
|
|||||||
updateCountDown();
|
updateCountDown();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
console.log("[DATA DETAIL]", JSON.stringify(data, null, 2));
|
|
||||||
|
|
||||||
const updateCountDown = () => {
|
const updateCountDown = () => {
|
||||||
const countDown = countDownAndCondition({
|
const countDown = countDownAndCondition({
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -198,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
|
||||||
@@ -253,7 +262,8 @@ export default function InvestmentEdit() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
disabled
|
iconLeft="Rp."
|
||||||
|
// disabled
|
||||||
required
|
required
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
label="Total Lembar"
|
label="Total Lembar"
|
||||||
@@ -339,11 +349,7 @@ export default function InvestmentEdit() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom isLoading={isLoading} onPress={handleSubmitUpdate}>
|
|
||||||
Simpan
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
<Spacing height={50} />
|
</NewWrapper>
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ export default function InvestmentDetail() {
|
|||||||
updateCountDown();
|
updateCountDown();
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
console.log("[DATA DETAIL]", JSON.stringify(data, null, 2));
|
|
||||||
|
|
||||||
const updateCountDown = () => {
|
const updateCountDown = () => {
|
||||||
const countDown = countDownAndCondition({
|
const countDown = countDownAndCondition({
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -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} />
|
||||||
@@ -264,7 +277,9 @@ export default function InvestmentCreate() {
|
|||||||
|
|
||||||
<StackCustom gap={0}>
|
<StackCustom gap={0}>
|
||||||
<TextInputCustom
|
<TextInputCustom
|
||||||
disabled
|
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,15 +372,8 @@ export default function InvestmentCreate() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<ButtonCustom
|
|
||||||
disabled={isLoading}
|
|
||||||
isLoading={isLoading}
|
|
||||||
onPress={() => handleSubmit()}
|
|
||||||
>
|
|
||||||
Simpan
|
|
||||||
</ButtonCustom>
|
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
<Spacing height={50} />
|
{/* <Spacing height={50} /> */}
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,91 +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, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function JobStatus() {
|
export default function JobStatus() {
|
||||||
const { user } = useAuth();
|
|
||||||
const { status } = useLocalSearchParams<{ status?: string }>();
|
|
||||||
console.log("STATUS", status);
|
|
||||||
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
|
||||||
status || "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>
|
{/* <Job_MainViewStatus /> */}
|
||||||
{isLoadList ? (
|
<Job_MainViewStatus2 />
|
||||||
<LoaderCustom />
|
|
||||||
) : _.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>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
@@ -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,244 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Maps_ScreenMapsEdit } from "@/screens/Maps/ScreenMapsEdit";
|
||||||
import {
|
|
||||||
BoxButtonOnFooter,
|
|
||||||
ButtonCenteredOnly,
|
|
||||||
ButtonCustom,
|
|
||||||
InformationBox,
|
|
||||||
LandscapeFrameUploaded,
|
|
||||||
Spacing,
|
|
||||||
TextInputCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import API_IMAGE from "@/constants/api-storage";
|
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
|
||||||
import { apiMapsGetOne, apiMapsUpdate } from "@/service/api-client/api-maps";
|
|
||||||
import { uploadFileService } from "@/service/upload-service";
|
|
||||||
import pickFile, { IFileData } from "@/utils/pickFile";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { StyleSheet, View } from "react-native";
|
|
||||||
import MapView, { LatLng, Marker } from "react-native-maps";
|
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
|
|
||||||
const defaultRegion = {
|
|
||||||
latitude: -8.737109,
|
|
||||||
longitude: 115.1756897,
|
|
||||||
latitudeDelta: 0.1,
|
|
||||||
longitudeDelta: 0.1,
|
|
||||||
};
|
|
||||||
export default function MapsEdit() {
|
export default function MapsEdit() {
|
||||||
const { id } = useLocalSearchParams();
|
return <Maps_ScreenMapsEdit />;
|
||||||
const [data, setData] = useState<any | null>({
|
|
||||||
id: "",
|
|
||||||
namePin: "",
|
|
||||||
latitude: "",
|
|
||||||
longitude: "",
|
|
||||||
imageId: "",
|
|
||||||
});
|
|
||||||
const [selectedLocation, setSelectedLocation] = useState<LatLng | null>(null);
|
|
||||||
const [image, setImage] = useState<IFileData | null>(null);
|
|
||||||
const [isLoading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
const response = await apiMapsGetOne({ id: id as string });
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setData({
|
|
||||||
id: response.data.id,
|
|
||||||
namePin: response.data.namePin,
|
|
||||||
latitude: response.data.latitude,
|
|
||||||
longitude: response.data.longitude,
|
|
||||||
imageId: response.data.imageId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMapPress = (event: any) => {
|
|
||||||
const { latitude, longitude } = event.nativeEvent.coordinate;
|
|
||||||
const location = { latitude, longitude };
|
|
||||||
setSelectedLocation(location);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
let newData: any;
|
|
||||||
if (!data.namePin) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Nama pin harus diisi",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
newData = {
|
|
||||||
namePin: data?.namePin,
|
|
||||||
latitude: selectedLocation?.latitude || data?.latitude,
|
|
||||||
longitude: selectedLocation?.longitude || data?.longitude,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
if (image) {
|
|
||||||
const responseUpload = await uploadFileService({
|
|
||||||
dirId: DIRECTORY_ID.map_image,
|
|
||||||
imageUri: image?.uri,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!responseUpload?.data?.id) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Gagal mengunggah gambar",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const imageId = responseUpload?.data?.id;
|
|
||||||
|
|
||||||
newData = {
|
|
||||||
namePin: data?.namePin,
|
|
||||||
latitude: selectedLocation?.latitude,
|
|
||||||
longitude: selectedLocation?.longitude,
|
|
||||||
newImageId: imageId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const responseUpdate = await apiMapsUpdate({
|
|
||||||
id: data?.id,
|
|
||||||
data: newData,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!responseUpdate.success) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Gagal mengupdate map",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.show({
|
|
||||||
type: "success",
|
|
||||||
text1: "Map berhasil diupdate",
|
|
||||||
});
|
|
||||||
router.back();
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttonFooter = (
|
|
||||||
<BoxButtonOnFooter>
|
|
||||||
<ButtonCustom
|
|
||||||
disabled={!data.namePin}
|
|
||||||
onPress={handleSubmit}
|
|
||||||
isLoading={isLoading}
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</ButtonCustom>
|
|
||||||
</BoxButtonOnFooter>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ViewWrapper footerComponent={buttonFooter}>
|
|
||||||
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
|
|
||||||
|
|
||||||
<View style={[styles.container, { height: 400 }]}>
|
|
||||||
<MapView
|
|
||||||
style={styles.map}
|
|
||||||
initialRegion={
|
|
||||||
data?.latitude && data?.longitude
|
|
||||||
? {
|
|
||||||
latitude: data?.latitude,
|
|
||||||
longitude: data?.longitude,
|
|
||||||
latitudeDelta: 0.1,
|
|
||||||
longitudeDelta: 0.1,
|
|
||||||
}
|
|
||||||
: defaultRegion
|
|
||||||
}
|
|
||||||
onPress={handleMapPress}
|
|
||||||
showsUserLocation={true}
|
|
||||||
showsMyLocationButton={true}
|
|
||||||
loadingEnabled={true}
|
|
||||||
loadingIndicatorColor="#666"
|
|
||||||
loadingBackgroundColor="#f0f0f0"
|
|
||||||
>
|
|
||||||
{selectedLocation ? (
|
|
||||||
<Marker
|
|
||||||
coordinate={selectedLocation}
|
|
||||||
title="Lokasi Dipilih"
|
|
||||||
description={`Lat: ${selectedLocation.latitude.toFixed(
|
|
||||||
6
|
|
||||||
)}, Lng: ${selectedLocation.longitude.toFixed(6)}`}
|
|
||||||
pinColor="red"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<Marker
|
|
||||||
coordinate={defaultRegion}
|
|
||||||
title="Lokasi Dipilih"
|
|
||||||
description={`Lat: ${defaultRegion.latitude.toFixed(
|
|
||||||
6
|
|
||||||
)}, Lng: ${defaultRegion.longitude.toFixed(6)}`}
|
|
||||||
pinColor="red"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</MapView>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
<TextInputCustom
|
|
||||||
required
|
|
||||||
label="Nama Pin"
|
|
||||||
placeholder="Masukkan nama pin maps"
|
|
||||||
value={data?.namePin}
|
|
||||||
onChangeText={(value) => setData({ ...data, namePin: value })}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Spacing />
|
|
||||||
|
|
||||||
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
|
|
||||||
<LandscapeFrameUploaded
|
|
||||||
image={
|
|
||||||
image
|
|
||||||
? image?.uri
|
|
||||||
: API_IMAGE.GET({ fileId: data?.imageId as string })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ButtonCenteredOnly
|
|
||||||
icon="upload"
|
|
||||||
onPress={() => {
|
|
||||||
pickFile({
|
|
||||||
allowedType: "image",
|
|
||||||
setImageUri(file) {
|
|
||||||
setImage(file);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Upload
|
|
||||||
</ButtonCenteredOnly>
|
|
||||||
<Spacing height={50} />
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
width: "100%",
|
|
||||||
backgroundColor: "#f5f5f5",
|
|
||||||
overflow: "hidden",
|
|
||||||
borderRadius: 8,
|
|
||||||
marginBottom: 20,
|
|
||||||
},
|
|
||||||
map: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -1,143 +1,9 @@
|
|||||||
import {
|
import Maps_ScreenMapsCreate from "@/screens/Maps/ScreenMapsCreate";
|
||||||
BaseBox,
|
|
||||||
BoxButtonOnFooter,
|
|
||||||
ButtonCenteredOnly,
|
|
||||||
ButtonCustom,
|
|
||||||
InformationBox,
|
|
||||||
LandscapeFrameUploaded,
|
|
||||||
Spacing,
|
|
||||||
TextInputCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import MapSelected from "@/components/Map/MapSelected";
|
|
||||||
import DIRECTORY_ID from "@/constants/directory-id";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { apiMapsCreate } from "@/service/api-client/api-maps";
|
|
||||||
import { uploadFileService } from "@/service/upload-service";
|
|
||||||
import pickFile, { IFileData } from "@/utils/pickFile";
|
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { LatLng } from "react-native-maps";
|
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
|
|
||||||
export default function MapsCreate() {
|
export default function MapsCreate() {
|
||||||
const { user } = useAuth();
|
|
||||||
const { id } = useLocalSearchParams();
|
|
||||||
const [selectedLocation, setSelectedLocation] = useState<LatLng | null>(null);
|
|
||||||
const [name, setName] = useState<string>("");
|
|
||||||
const [image, setImage] = useState<IFileData | null>(null);
|
|
||||||
const [isLoading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
let newData: any;
|
|
||||||
newData = {
|
|
||||||
authorId: user?.id,
|
|
||||||
portofolioId: id,
|
|
||||||
namePin: name,
|
|
||||||
latitude: selectedLocation?.latitude,
|
|
||||||
longitude: selectedLocation?.longitude,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (image) {
|
|
||||||
const responseUpload = await uploadFileService({
|
|
||||||
dirId: DIRECTORY_ID.map_image,
|
|
||||||
imageUri: image?.uri,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!responseUpload?.data?.id) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Gagal mengunggah gambar",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const imageId = responseUpload?.data?.id;
|
|
||||||
|
|
||||||
newData = {
|
|
||||||
authorId: user?.id,
|
|
||||||
portofolioId: id,
|
|
||||||
namePin: name,
|
|
||||||
latitude: selectedLocation?.latitude,
|
|
||||||
longitude: selectedLocation?.longitude,
|
|
||||||
imageId: imageId,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await apiMapsCreate({
|
|
||||||
data: newData,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.success) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Gagal menambahkan map",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.show({
|
|
||||||
type: "success",
|
|
||||||
text1: "Map berhasil ditambahkan",
|
|
||||||
});
|
|
||||||
router.back();
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttonFooter = (
|
|
||||||
<BoxButtonOnFooter>
|
|
||||||
<ButtonCustom
|
|
||||||
isLoading={isLoading}
|
|
||||||
disabled={!selectedLocation || name === ""}
|
|
||||||
onPress={handleSubmit}
|
|
||||||
>
|
|
||||||
Simpan
|
|
||||||
</ButtonCustom>
|
|
||||||
</BoxButtonOnFooter>
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<ViewWrapper footerComponent={buttonFooter}>
|
<>
|
||||||
<InformationBox text="Tentukan lokasi pin map dengan menekan pada map." />
|
<Maps_ScreenMapsCreate />
|
||||||
|
</>
|
||||||
<BaseBox>
|
|
||||||
<MapSelected
|
|
||||||
selectedLocation={selectedLocation as any}
|
|
||||||
setSelectedLocation={setSelectedLocation}
|
|
||||||
/>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
<TextInputCustom
|
|
||||||
required
|
|
||||||
label="Nama Pin"
|
|
||||||
placeholder="Masukkan nama pin maps"
|
|
||||||
value={name}
|
|
||||||
onChangeText={setName}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Spacing height={50} />
|
|
||||||
|
|
||||||
<InformationBox text="Upload foto lokasi bisnis anda untuk ditampilkan dalam detail maps." />
|
|
||||||
<LandscapeFrameUploaded image={image?.uri} />
|
|
||||||
<ButtonCenteredOnly
|
|
||||||
icon="upload"
|
|
||||||
onPress={() => {
|
|
||||||
pickFile({
|
|
||||||
allowedType: "image",
|
|
||||||
setImageUri(file) {
|
|
||||||
setImage(file);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Upload
|
|
||||||
</ButtonCenteredOnly>
|
|
||||||
<Spacing height={50} />
|
|
||||||
</ViewWrapper>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import MapsView from "@/screens/Maps/MapsView";
|
|
||||||
import MapsView2 from "@/screens/Maps/MapsView2";
|
import MapsView2 from "@/screens/Maps/MapsView2";
|
||||||
import { Text, View } from "react-native";
|
|
||||||
|
|
||||||
export interface LocationItem {
|
export interface LocationItem {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
@@ -13,8 +11,14 @@ export interface LocationItem {
|
|||||||
export default function Maps() {
|
export default function Maps() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MapsView />
|
{/* <Stack.Screen
|
||||||
{/* <MapsView2 />, */}
|
options={{
|
||||||
|
title: "Maps",
|
||||||
|
headerLeft: () => <BackButton />,
|
||||||
|
}}
|
||||||
|
/> */}
|
||||||
|
{/* {Platform.OS === "ios" ? <MapsView /> : <MapsView2 />} */}
|
||||||
|
<MapsView2 />
|
||||||
{/* <View style={{ flex: 1, backgroundColor: "gray" }}><Text style={{ color: "white" }}>Map disabled</Text></View> */}
|
{/* <View style={{ flex: 1, backgroundColor: "gray" }}><Text style={{ color: "white" }}>Map disabled</Text></View> */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import ScreenNotification from "@/screens/Notification/ScreenNotification";
|
import ScreenNotification_V1 from "@/screens/Notification/ScreenNotification_V1";
|
||||||
|
import ScreenNotification_V2 from "@/screens/Notification/ScreenNotification_V2";
|
||||||
|
|
||||||
export default function Notification() {
|
export default function Notification() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ScreenNotification />
|
<ScreenNotification_V2 />
|
||||||
|
{/* <ScreenNotification_V1 /> */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
||||||
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";
|
||||||
@@ -49,7 +50,7 @@ export default function Portofolio() {
|
|||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData(id as string);
|
onLoadData(id as string);
|
||||||
onLoadUserByToken();
|
onLoadUserByToken();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
async function onLoadData(id: string) {
|
async function onLoadData(id: string) {
|
||||||
@@ -64,6 +65,8 @@ export default function Portofolio() {
|
|||||||
setProfileId(response?.data?.Profile?.id);
|
setProfileId(response?.data?.Profile?.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
@@ -87,18 +90,24 @@ export default function Portofolio() {
|
|||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
{!data || !profileId ? (
|
{!data || !profileId ? (
|
||||||
<LoaderCustom />
|
<StackCustom>
|
||||||
|
<CustomSkeleton height={400} />
|
||||||
|
<CustomSkeleton height={300} />
|
||||||
|
</StackCustom>
|
||||||
) : (
|
) : (
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<Portofolio_Data
|
<Portofolio_Data
|
||||||
data={data}
|
data={data}
|
||||||
listSubBidang={data?.Portofolio_BidangDanSubBidangBisnis as any[]}
|
listSubBidang={data?.Portofolio_BidangDanSubBidangBisnis as any[]}
|
||||||
/>
|
/>
|
||||||
<Portofolio_BusinessLocation
|
{data?.BusinessMaps && (
|
||||||
data={data?.BusinessMaps}
|
<Portofolio_BusinessLocation
|
||||||
imageId={data?.logoId}
|
data={data?.BusinessMaps}
|
||||||
setOpenDrawerLocation={setOpenDrawerLocation}
|
imageId={data?.logoId}
|
||||||
/>
|
setOpenDrawerLocation={setOpenDrawerLocation}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<Portofolio_SocialMediaSection
|
<Portofolio_SocialMediaSection
|
||||||
data={data?.Portofolio_MediaSosial}
|
data={data?.Portofolio_MediaSosial}
|
||||||
/>
|
/>
|
||||||
@@ -135,36 +144,38 @@ export default function Portofolio() {
|
|||||||
closeDrawer={() => setOpenDrawerLocation(false)}
|
closeDrawer={() => setOpenDrawerLocation(false)}
|
||||||
height={"auto"}
|
height={"auto"}
|
||||||
>
|
>
|
||||||
<DummyLandscapeImage
|
{data?.BusinessMaps?.imageId && (
|
||||||
height={200}
|
<DummyLandscapeImage
|
||||||
imageId={data?.BusinessMaps?.imageId}
|
height={200}
|
||||||
/>
|
imageId={data?.BusinessMaps?.imageId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<GridTwoView
|
<GridTwoView
|
||||||
spanLeft={2}
|
spanLeft={2}
|
||||||
spanRight={10}
|
spanRight={10}
|
||||||
leftIcon={
|
leftItem={
|
||||||
<FontAwesome
|
<FontAwesome
|
||||||
name="building-o"
|
name="building-o"
|
||||||
size={ICON_SIZE_SMALL}
|
size={ICON_SIZE_SMALL}
|
||||||
color="white"
|
color="white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
rightIcon={<TextCustom>{data?.BusinessMaps?.namePin}</TextCustom>}
|
rightItem={<TextCustom>{data?.BusinessMaps?.namePin}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<GridTwoView
|
<GridTwoView
|
||||||
spanLeft={2}
|
spanLeft={2}
|
||||||
spanRight={10}
|
spanRight={10}
|
||||||
leftIcon={
|
leftItem={
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name="list-outline"
|
name="list-outline"
|
||||||
size={ICON_SIZE_SMALL}
|
size={ICON_SIZE_SMALL}
|
||||||
color="white"
|
color="white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
rightIcon={
|
rightItem={
|
||||||
<TextCustom>{data?.MasterBidangBisnis?.name}</TextCustom>
|
<TextCustom>{data?.MasterBidangBisnis?.name}</TextCustom>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@@ -172,26 +183,26 @@ export default function Portofolio() {
|
|||||||
<GridTwoView
|
<GridTwoView
|
||||||
spanLeft={2}
|
spanLeft={2}
|
||||||
spanRight={10}
|
spanRight={10}
|
||||||
leftIcon={
|
leftItem={
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name="call-outline"
|
name="call-outline"
|
||||||
size={ICON_SIZE_SMALL}
|
size={ICON_SIZE_SMALL}
|
||||||
color="white"
|
color="white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
rightIcon={<TextCustom>{data?.tlpn}</TextCustom>}
|
rightItem={<TextCustom>{data?.tlpn}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
<GridTwoView
|
<GridTwoView
|
||||||
spanLeft={2}
|
spanLeft={2}
|
||||||
spanRight={10}
|
spanRight={10}
|
||||||
leftIcon={
|
leftItem={
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name="location-outline"
|
name="location-outline"
|
||||||
size={ICON_SIZE_SMALL}
|
size={ICON_SIZE_SMALL}
|
||||||
color="white"
|
color="white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
rightIcon={<TextCustom>{data?.alamatKantor}</TextCustom>}
|
rightItem={<TextCustom>{data?.alamatKantor}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Spacing />
|
<Spacing />
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { LoaderCustom } from "@/components";
|
import { NewWrapper, StackCustom } from "@/components";
|
||||||
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
|
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||||
import DrawerCustom from "@/components/Drawer/DrawerCustom";
|
import DrawerCustom from "@/components/Drawer/DrawerCustom";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
@@ -16,8 +16,8 @@ import { GStyles } from "@/styles/global-styles";
|
|||||||
import { IProfile } from "@/types/Type-Profile";
|
import { IProfile } from "@/types/Type-Profile";
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import React, { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { TouchableOpacity } from "react-native";
|
import { RefreshControl, TouchableOpacity } from "react-native";
|
||||||
|
|
||||||
export default function Profile() {
|
export default function Profile() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
@@ -25,6 +25,7 @@ export default function Profile() {
|
|||||||
const [data, setData] = useState<IProfile>();
|
const [data, setData] = useState<IProfile>();
|
||||||
const [dataToken, setDataToken] = useState<IProfile>();
|
const [dataToken, setDataToken] = useState<IProfile>();
|
||||||
const [listPortofolio, setListPortofolio] = useState<any[]>();
|
const [listPortofolio, setListPortofolio] = useState<any[]>();
|
||||||
|
const [refreshing, setRefreshing] = useState(false);
|
||||||
|
|
||||||
const { token, logout, isAdmin, user, userData } = useAuth();
|
const { token, logout, isAdmin, user, userData } = useAuth();
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ export default function Profile() {
|
|||||||
onLoadUserByToken();
|
onLoadUserByToken();
|
||||||
isUserCheck();
|
isUserCheck();
|
||||||
userData(token as string);
|
userData(token as string);
|
||||||
}, [id, token])
|
}, [id, token]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const isUserCheck = () => {
|
const isUserCheck = () => {
|
||||||
@@ -54,13 +55,21 @@ export default function Profile() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onLoadData = async (id: string) => {
|
const onLoadData = async (id: string) => {
|
||||||
const response = await apiProfile({ id: id });
|
try {
|
||||||
setData(response.data);
|
const response = await apiProfile({ id: id });
|
||||||
|
setData(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR onLoadData]", error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onLoadUserByToken = async () => {
|
const onLoadUserByToken = async () => {
|
||||||
const response = await apiUser(user?.id as string);
|
try {
|
||||||
setDataToken(response?.data?.Profile);
|
const response = await apiUser(user?.id as string);
|
||||||
|
setDataToken(response?.data?.Profile);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("[ERROR onLoadUserByToken]", error);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onLoadPortofolio = async (id: string) => {
|
const onLoadPortofolio = async (id: string) => {
|
||||||
@@ -69,15 +78,25 @@ export default function Profile() {
|
|||||||
const lastTwoByDate = response.data
|
const lastTwoByDate = response.data
|
||||||
.sort(
|
.sort(
|
||||||
(a: any, b: any) =>
|
(a: any, b: any) =>
|
||||||
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
|
||||||
) // urut desc
|
) // urut desc
|
||||||
.slice(0, 2);
|
.slice(0, 2);
|
||||||
setListPortofolio(lastTwoByDate);
|
setListPortofolio(lastTwoByDate);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("[ERROR]", error);
|
console.log("[ERROR onLoadPortofolio]", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onRefresh = useCallback(() => {
|
||||||
|
setRefreshing(true);
|
||||||
|
onLoadData(id as string);
|
||||||
|
onLoadPortofolio(id as string);
|
||||||
|
onLoadUserByToken();
|
||||||
|
isUserCheck();
|
||||||
|
userData(token as string);
|
||||||
|
setRefreshing(false);
|
||||||
|
}, [id, token]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
@@ -97,9 +116,21 @@ export default function Profile() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* Main View */}
|
{/* Main View */}
|
||||||
<ViewWrapper>
|
<NewWrapper
|
||||||
|
refreshControl={
|
||||||
|
<RefreshControl
|
||||||
|
refreshing={refreshing}
|
||||||
|
onRefresh={onRefresh}
|
||||||
|
tintColor={MainColor.yellow}
|
||||||
|
colors={[MainColor.yellow]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
{!data || !dataToken ? (
|
{!data || !dataToken ? (
|
||||||
<LoaderCustom />
|
<StackCustom>
|
||||||
|
<CustomSkeleton height={400} />
|
||||||
|
<CustomSkeleton height={200} />
|
||||||
|
</StackCustom>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ProfileSection data={data as any} />
|
<ProfileSection data={data as any} />
|
||||||
@@ -110,7 +141,7 @@ export default function Profile() {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
|
|
||||||
{/* Drawer Komponen Eksternal */}
|
{/* Drawer Komponen Eksternal */}
|
||||||
<DrawerCustom
|
<DrawerCustom
|
||||||
|
|||||||
@@ -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,106 +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, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function VotingStatus() {
|
export default function VotingStatus() {
|
||||||
const { user } = useAuth();
|
|
||||||
const { status } = useLocalSearchParams<{ status?: string }>();
|
|
||||||
|
|
||||||
const id = user?.id || "";
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>(
|
|
||||||
status || "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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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?status=review");
|
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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
} from "@/components";
|
} from "@/components";
|
||||||
import DrawerAdmin from "@/components/Drawer/DrawerAdmin";
|
import DrawerAdmin from "@/components/Drawer/DrawerAdmin";
|
||||||
import NavbarMenu from "@/components/Drawer/NavbarMenu";
|
import NavbarMenu from "@/components/Drawer/NavbarMenu";
|
||||||
|
import NavbarMenu_V2 from "@/components/Drawer/NavbarMenu_V2";
|
||||||
|
import NavbarMenu_V3 from "@/components/Drawer/NavbarMenu_V3";
|
||||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||||
import {
|
import {
|
||||||
ICON_SIZE_MEDIUM,
|
ICON_SIZE_MEDIUM,
|
||||||
@@ -15,11 +17,16 @@ 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 { useNotificationStore } from "@/hooks/use-notification-store";
|
||||||
import AdminNotificationBell from "@/screens/Admin/AdminNotificationBell";
|
import AdminNotificationBell from "@/screens/Admin/AdminNotificationBell";
|
||||||
import {
|
import {
|
||||||
adminListMenu,
|
adminListMenu,
|
||||||
superAdminListMenu,
|
superAdminListMenu,
|
||||||
} from "@/screens/Admin/listPageAdmin";
|
} from "@/screens/Admin/listPageAdmin";
|
||||||
|
import {
|
||||||
|
adminListMenu_V2,
|
||||||
|
superAdminListMenu_V2,
|
||||||
|
} from "@/screens/Admin/listPageAdmin_V2";
|
||||||
import { GStyles } from "@/styles/global-styles";
|
import { GStyles } from "@/styles/global-styles";
|
||||||
import { FontAwesome6, Ionicons } from "@expo/vector-icons";
|
import { FontAwesome6, Ionicons } from "@expo/vector-icons";
|
||||||
import { router, Stack } from "expo-router";
|
import { router, Stack } from "expo-router";
|
||||||
@@ -148,6 +155,24 @@ export default function AdminLayout() {
|
|||||||
}
|
}
|
||||||
onClose={() => setOpenDrawerNavbar(false)}
|
onClose={() => setOpenDrawerNavbar(false)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* <NavbarMenu_V2
|
||||||
|
items={
|
||||||
|
user?.masterUserRoleId === "2"
|
||||||
|
? adminListMenu_V2
|
||||||
|
: superAdminListMenu_V2
|
||||||
|
}
|
||||||
|
onClose={() => setOpenDrawerNavbar(false)}
|
||||||
|
/> */}
|
||||||
|
|
||||||
|
{/* <NavbarMenu_V3
|
||||||
|
items={
|
||||||
|
user?.masterUserRoleId === "2"
|
||||||
|
? adminListMenu_V2
|
||||||
|
: superAdminListMenu_V2
|
||||||
|
}
|
||||||
|
onClose={() => setOpenDrawerNavbar(false)}
|
||||||
|
/> */}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</DrawerAdmin>
|
</DrawerAdmin>
|
||||||
|
|
||||||
@@ -198,7 +223,7 @@ export default function AdminLayout() {
|
|||||||
// size={ICON_SIZE_SMALL}
|
// size={ICON_SIZE_SMALL}
|
||||||
// color={MainColor.white}
|
// color={MainColor.white}
|
||||||
// />
|
// />
|
||||||
<AdminNotificationBell/>
|
<AdminNotificationBell />
|
||||||
),
|
),
|
||||||
path: "/admin/notification",
|
path: "/admin/notification",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,135 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenBusinessFieldDetail } from "@/screens/Admin/App-Information/ScreenBusinessFieldDetail";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
BaseBox,
|
|
||||||
CenterCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconEdit } from "@/components/_Icon";
|
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
|
||||||
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { apiAdminMasterBusinessFieldById } from "@/service/api-admin/api-master-admin";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminAppInformation_BusinessFieldDetail() {
|
export default function AdminAppInformation_BusinessFieldDetail() {
|
||||||
const { id } = useLocalSearchParams();
|
return <Admin_ScreenBusinessFieldDetail />;
|
||||||
const [data, setData] = useState<any | null>(null);
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadDetail();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadDetail = async () => {
|
|
||||||
try {
|
|
||||||
const response = await apiAdminMasterBusinessFieldById({
|
|
||||||
id: id as string,
|
|
||||||
category: "all",
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Response >>", JSON.stringify(response, null, 2));
|
|
||||||
|
|
||||||
setData(response.data);
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setData(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper>
|
|
||||||
<StackCustom>
|
|
||||||
<AdminBackButtonAntTitle title="Detail Bidang & Sub Bidang" />
|
|
||||||
|
|
||||||
{!data ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : (
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<TextCustom bold>Nama Bidang</TextCustom>
|
|
||||||
<Spacing height={5} />
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<TextCustom bold>
|
|
||||||
Status: {data?.bidang?.active ? "Aktif" : "Tidak Aktif"}
|
|
||||||
</TextCustom>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
span1={10}
|
|
||||||
span2={2}
|
|
||||||
text1={
|
|
||||||
<TextCustom bold size={"large"}>
|
|
||||||
{data?.bidang?.name}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text2={
|
|
||||||
<CenterCustom>
|
|
||||||
<ActionIcon
|
|
||||||
icon={<IconEdit size={16} color={MainColor.black} />}
|
|
||||||
onPress={() =>
|
|
||||||
router.push(
|
|
||||||
`/admin/app-information/business-field/${id}/bidang-update`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
{/* <Divider /> */}
|
|
||||||
<Spacing height={5} />
|
|
||||||
|
|
||||||
<TextCustom bold>Sub Bidang Bisnis</TextCustom>
|
|
||||||
<Spacing height={5} />
|
|
||||||
|
|
||||||
{data?.subBidang?.map((item: any, index: number) => (
|
|
||||||
<BaseBox key={index}>
|
|
||||||
<StackCustom gap={0}>
|
|
||||||
<TextCustom bold>
|
|
||||||
Status: {item?.isActive ? "Aktif" : "Tidak Aktif"}
|
|
||||||
</TextCustom>
|
|
||||||
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
span1={10}
|
|
||||||
span2={2}
|
|
||||||
text1={
|
|
||||||
<TextCustom bold size={"large"}>
|
|
||||||
{item.name}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text2={
|
|
||||||
<CenterCustom>
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<IconEdit size={16} color={MainColor.black} />
|
|
||||||
}
|
|
||||||
onPress={() =>
|
|
||||||
router.push(
|
|
||||||
`/admin/app-information/business-field/${item?.id}/sub-bidang-update`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
))}
|
|
||||||
</StackCustom>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* <TextCustom>{JSON.stringify(data, null, 2)}</TextCustom> */}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,86 +1,5 @@
|
|||||||
import { ScrollableCustom, StackCustom, ViewWrapper } from "@/components";
|
import { Admin_ScreenAppInformation } from "@/screens/Admin/App-Information/ScreenAppInformation";
|
||||||
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminAppInformation_BusinessFieldSection from "@/screens/Admin/App-Information/BusinessFieldSection";
|
|
||||||
import AdminAppInformation_Bank from "@/screens/Admin/App-Information/InformationBankSection";
|
|
||||||
import AdminAppInformation_StickerSection from "@/screens/Admin/App-Information/StickerSection";
|
|
||||||
import { router } from "expo-router";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { Alert } from "react-native";
|
|
||||||
|
|
||||||
export default function AdminInformation() {
|
export default function AdminInformation() {
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>("bank");
|
return <Admin_ScreenAppInformation />;
|
||||||
const [activePage, setActivePage] = useState<string>("Informasi Bank");
|
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
|
||||||
setActiveCategory(item.value);
|
|
||||||
setActivePage(item.label);
|
|
||||||
// tambahkan logika lain seperti filter dsb.
|
|
||||||
};
|
|
||||||
|
|
||||||
const scrollComponent = (
|
|
||||||
<StackCustom>
|
|
||||||
<ScrollableCustom
|
|
||||||
data={listPage}
|
|
||||||
onButtonPress={handlePress}
|
|
||||||
activeId={activeCategory as any}
|
|
||||||
/>
|
|
||||||
</StackCustom>
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderContent = () => {
|
|
||||||
switch (activeCategory) {
|
|
||||||
case "bank":
|
|
||||||
return <AdminAppInformation_Bank />;
|
|
||||||
case "business":
|
|
||||||
return <AdminAppInformation_BusinessFieldSection />;
|
|
||||||
case "sticker":
|
|
||||||
return <AdminAppInformation_StickerSection />;
|
|
||||||
default:
|
|
||||||
return <AdminAppInformation_Bank />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={scrollComponent}>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title={activePage}
|
|
||||||
rightComponent={
|
|
||||||
<AdminActionIconPlus
|
|
||||||
onPress={() => {
|
|
||||||
if (activeCategory === "bank") {
|
|
||||||
router.push("/admin/app-information/information-bank/create");
|
|
||||||
} else if (activeCategory === "business") {
|
|
||||||
router.push("/admin/app-information/business-field/create");
|
|
||||||
} else if (activeCategory === "sticker") {
|
|
||||||
Alert.alert("Coming Soon", "Next Update");
|
|
||||||
// router.push("/admin/app-information/sticker/create");
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{renderContent()}
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const listPage = [
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
label: "Informasi Bank",
|
|
||||||
value: "bank",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
label: "Bidang & Sub Bidang",
|
|
||||||
value: "business",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "3",
|
|
||||||
label: "Stiker",
|
|
||||||
value: "sticker",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BaseBox,
|
BaseBox,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
Grid,
|
Grid,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
@@ -22,7 +22,7 @@ export default function AdminCollaborationPublish() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
handlerLoadData();
|
handlerLoadData();
|
||||||
}, [status])
|
}, [status]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlerLoadData = async () => {
|
const handlerLoadData = async () => {
|
||||||
@@ -78,16 +78,16 @@ export default function AdminCollaborationPublish() {
|
|||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
{data?.report && (
|
{data?.report && (
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<GridTwoView
|
<GridTwoView
|
||||||
spanLeft={4}
|
spanLeft={4}
|
||||||
spanRight={8}
|
spanRight={8}
|
||||||
leftIcon={<TextCustom bold>Catatan report</TextCustom>}
|
leftItem={<TextCustom bold>Catatan report</TextCustom>}
|
||||||
rightIcon={<TextCustom>{data?.report}</TextCustom>}
|
rightItem={<TextCustom>{data?.report}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
)}
|
)}
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -42,19 +42,19 @@ export default function AdminDonationDetailDisbursementOfFunds() {
|
|||||||
const listData = [
|
const listData = [
|
||||||
{
|
{
|
||||||
label: "Nominal",
|
label: "Nominal",
|
||||||
value: `Rp ${(data && formatCurrencyDisplay(data?.nominalCair)) || 0}`,
|
value: `Rp ${data ? formatCurrencyDisplay(data?.nominalCair) : 0}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Tanggal",
|
label: "Tanggal",
|
||||||
value: dateTimeView({ date: data?.createdAt }),
|
value: data ? dateTimeView({ date: data?.createdAt }) : "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Judul",
|
label: "Judul",
|
||||||
value: (data && data?.title) || "-",
|
value: data ? data?.title : "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Deskripsi",
|
label: "Deskripsi",
|
||||||
value: (data && data?.deskripsi) || "-",
|
value: data ? data?.deskripsi : "-",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,126 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenDonationListDisbursementOfFunds } from "@/screens/Admin/Donation/ScreenDonationListDisbursementOfFunds";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
CenterCustom,
|
|
||||||
Divider,
|
|
||||||
LoaderCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconView } from "@/components/_Icon/IconComponent";
|
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
|
||||||
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminDonationDisbursementOfFundsListById } from "@/service/api-admin/api-admin-donation";
|
|
||||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import React, { useCallback } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
|
|
||||||
export default function AdminDonasiListOfDisbursementOfFunds() {
|
export default function AdminDonasiListOfDisbursementOfFunds() {
|
||||||
const { id } = useLocalSearchParams();
|
return <Admin_ScreenDonationListDisbursementOfFunds />;
|
||||||
const [listData, setListData] = React.useState<any[] | null>(null);
|
|
||||||
const [loadData, setLoadData] = React.useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
const response = await apiAdminDonationDisbursementOfFundsListById({
|
|
||||||
id: id as string,
|
|
||||||
category: "get-all",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={
|
|
||||||
<AdminBackButtonAntTitle title="Daftar Pencairan Dana" />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<GridViewCustomSpan
|
|
||||||
span1={3}
|
|
||||||
span2={5}
|
|
||||||
span3={4}
|
|
||||||
component1={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Aksi
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component2={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Tanggal
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component3={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Nominal
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
<StackCustom>
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Belum ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item, index) => (
|
|
||||||
<View key={index}>
|
|
||||||
<GridViewCustomSpan
|
|
||||||
span1={3}
|
|
||||||
span2={5}
|
|
||||||
span3={4}
|
|
||||||
component1={
|
|
||||||
<CenterCustom>
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<IconView size={ICON_SIZE_BUTTON} color="black" />
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
router.push(
|
|
||||||
`/admin/donation/${item?.id}/detail-disbursement-of-funds`
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
component2={
|
|
||||||
<TextCustom align="center" truncate>
|
|
||||||
{dayjs(item?.createdAt).format("DD-MM-YYYY")}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component3={
|
|
||||||
<TextCustom align="center" truncate>
|
|
||||||
Rp. {formatCurrencyDisplay(item?.nominalCair)}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,186 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenDonationListOfDonatur } from "@/screens/Admin/Donation/ScreenDonationListOfDonatur";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
BadgeCustom,
|
|
||||||
CenterCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
SelectCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconView } from "@/components/_Icon/IconComponent";
|
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
|
||||||
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminDonationListOfDonatur } from "@/service/api-admin/api-admin-donation";
|
|
||||||
import { apiMasterTransaction } from "@/service/api-client/api-master";
|
|
||||||
import { colorBadgeTransaction } from "@/utils/colorBadge";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import React, { useEffect } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminDonasiListOfDonatur() {
|
export default function AdminDonasiListOfDonatur() {
|
||||||
const { id } = useLocalSearchParams();
|
return <Admin_ScreenDonationListOfDonatur />;
|
||||||
const [listData, setListData] = React.useState<any[] | null>(null);
|
|
||||||
const [loadData, setLoadData] = React.useState(false);
|
|
||||||
const [master, setMaster] = React.useState<any[]>([]);
|
|
||||||
|
|
||||||
const [selectValue, setSelectValue] = React.useState<string | null>(null);
|
|
||||||
const [selectedStatus, setSelectedStatus] = React.useState<string | null>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
React.useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id, selectValue])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
const response = await apiAdminDonationListOfDonatur({
|
|
||||||
id: id as string,
|
|
||||||
status: selectedStatus as any,
|
|
||||||
});
|
|
||||||
// console.log("[LIST OF DONATUR]", JSON.stringify(response, null, 2));
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setListData([]);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onLoadMaster();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onLoadMaster = async () => {
|
|
||||||
try {
|
|
||||||
const response = await apiMasterTransaction();
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setMaster(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setMaster([]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const searchComponent = (
|
|
||||||
<View style={{ flexDirection: "row", gap: 5 }}>
|
|
||||||
<SelectCustom
|
|
||||||
placeholder="Pilih status transaksi"
|
|
||||||
data={
|
|
||||||
_.isEmpty(master)
|
|
||||||
? []
|
|
||||||
: master?.map((item: any) => ({
|
|
||||||
label: item.name,
|
|
||||||
value: item.id,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
value={selectValue}
|
|
||||||
onChange={(value: any) => {
|
|
||||||
setSelectValue(value);
|
|
||||||
const nameSelected = master.find((item: any) => item.id === value);
|
|
||||||
const statusChooses = _.lowerCase(nameSelected?.name);
|
|
||||||
setSelectedStatus(statusChooses);
|
|
||||||
}}
|
|
||||||
styleContainer={{ width: "100%", marginBottom: 0 }}
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={
|
|
||||||
<AdminBackButtonAntTitle newComponent={searchComponent} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<GridViewCustomSpan
|
|
||||||
span1={3}
|
|
||||||
span2={5}
|
|
||||||
span3={4}
|
|
||||||
component1={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Aksi
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component2={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Donatur
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component3={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Status
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
<StackCustom>
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Belum ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item: any, index: number) => (
|
|
||||||
<View key={index}>
|
|
||||||
<GridViewCustomSpan
|
|
||||||
span1={3}
|
|
||||||
span2={5}
|
|
||||||
span3={4}
|
|
||||||
component1={
|
|
||||||
<CenterCustom>
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<IconView size={ICON_SIZE_BUTTON} color="black" />
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
router.push(
|
|
||||||
`/admin/donation/${item?.id}/${_.lowerCase(
|
|
||||||
item?.DonasiMaster_StatusInvoice?.name
|
|
||||||
)}/transaction-detail`
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
component2={
|
|
||||||
<TextCustom bold align="center" truncate>
|
|
||||||
{item?.Author?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component3={
|
|
||||||
<BadgeCustom
|
|
||||||
style={{ alignSelf: "center" }}
|
|
||||||
color={colorBadgeTransaction({
|
|
||||||
status: item?.DonasiMaster_StatusInvoice?.name,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item?.DonasiMaster_StatusInvoice?.name}
|
|
||||||
</BadgeCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,117 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenDonationStatus } from "@/screens/Admin/Donation/ScreenDonationStatus";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminDonation } from "@/service/api-admin/api-admin-donation";
|
|
||||||
import { Octicons } from "@expo/vector-icons";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminDonationStatus() {
|
export default function AdminDonationStatus() {
|
||||||
const { status } = useLocalSearchParams();
|
return <Admin_ScreenDonationStatus />;
|
||||||
console.log("[STATUS]", status);
|
|
||||||
|
|
||||||
const [data, setData] = useState<any | null>(null);
|
|
||||||
const [search, setSearch] = useState<string>("");
|
|
||||||
const [loadData, setLoadData] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [status, search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
const response = await apiAdminDonation({
|
|
||||||
category: status as "publish" | "review" | "reject",
|
|
||||||
search,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("[RES]", JSON.stringify(response, null, 2));
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setData([]);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rightComponent = (
|
|
||||||
<SearchInput
|
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari"
|
|
||||||
value={search}
|
|
||||||
onChangeText={(value) => setSearch(value)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Donasi" />}>
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title={`${_.startCase(status as string)}`}
|
|
||||||
rightComponent={rightComponent}
|
|
||||||
/>
|
|
||||||
<AdminTitleTable
|
|
||||||
title1="Aksi"
|
|
||||||
title2="Username"
|
|
||||||
title3="Judul Donasi"
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(data) ? (
|
|
||||||
<TextCustom align="center" size="small" color="gray">
|
|
||||||
Belum ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
data?.map((item: any, index: number) => (
|
|
||||||
<AdminTableValue
|
|
||||||
key={index}
|
|
||||||
value1={
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<Octicons
|
|
||||||
name="eye"
|
|
||||||
size={ICON_SIZE_BUTTON}
|
|
||||||
color="black"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/donation/${item.id}/${status}`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
value2={<TextCustom truncate={1}>{item?.Author?.username || "-"}</TextCustom>}
|
|
||||||
value3={
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
{item?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ export default function AdminDonationCategoryUpdate() {
|
|||||||
const response = await apiAdminMasterDonationCategoryById({
|
const response = await apiAdminMasterDonationCategoryById({
|
||||||
id: id as any,
|
id: id as any,
|
||||||
});
|
});
|
||||||
console.log(JSON.stringify(response.data, null, 2));
|
|
||||||
|
|
||||||
setData(response.data);
|
setData(response.data);
|
||||||
};
|
};
|
||||||
@@ -44,10 +43,9 @@ export default function AdminDonationCategoryUpdate() {
|
|||||||
id: id as any,
|
id: id as any,
|
||||||
data: data,
|
data: data,
|
||||||
});
|
});
|
||||||
console.log(JSON.stringify(response.data, null, 2));
|
|
||||||
router.back();
|
router.back();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log("Error update category:", error);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,135 +1,5 @@
|
|||||||
import {
|
import { Admin_ScreenDonationCategory } from "@/screens/Admin/Donation/ScreenDonationCategory";
|
||||||
BadgeCustom,
|
|
||||||
CenterCustom,
|
|
||||||
ClickableCustom,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
|
||||||
import { apiAdminMasterDonationCategory } from "@/service/api-admin/api-master-admin";
|
|
||||||
import { colorActivationForBadge } from "@/utils/colorActivationForBadge";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { RefreshControl, View } from "react-native";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminDonationCategory() {
|
export default function AdminDonationCategory() {
|
||||||
const [listData, setListData] = useState<any[]>([]);
|
return <Admin_ScreenDonationCategory />;
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
fetchMaster();
|
|
||||||
}, [])
|
|
||||||
);
|
|
||||||
|
|
||||||
const fetchMaster = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const response = await apiAdminMasterDonationCategory();
|
|
||||||
if (response.success) {
|
|
||||||
console.log(JSON.stringify(response.data, null, 2));
|
|
||||||
setListData(response.data);
|
|
||||||
} else {
|
|
||||||
setListData([]);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[Error]", error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onRefresh = async () => {
|
|
||||||
setRefreshing(true);
|
|
||||||
await fetchMaster();
|
|
||||||
setRefreshing(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper
|
|
||||||
refreshControl={
|
|
||||||
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
|
|
||||||
}
|
|
||||||
headerComponent={<AdminTitlePage title="Donasi" />}
|
|
||||||
>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title="Kategori"
|
|
||||||
rightComponent={
|
|
||||||
<AdminActionIconPlus
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/donation/category-create`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<View>
|
|
||||||
<GridSpan_4_8
|
|
||||||
label={<TextCustom bold>Status</TextCustom>}
|
|
||||||
value={<TextCustom bold>Kategori</TextCustom>}
|
|
||||||
/>
|
|
||||||
{/* <Grid>
|
|
||||||
<Grid.Col style={{ paddingLeft: 10 }} span={4}>
|
|
||||||
<TextCustom bold>Status</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={8}>
|
|
||||||
<TextCustom bold>Kategori</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid> */}
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
<Spacing />
|
|
||||||
|
|
||||||
<StackCustom>
|
|
||||||
{listData.map((item, index) => (
|
|
||||||
<ClickableCustom
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/donation/category-update?id=${item.id}`);
|
|
||||||
}}
|
|
||||||
key={index}
|
|
||||||
>
|
|
||||||
<GridSpan_4_8
|
|
||||||
label={
|
|
||||||
<CenterCustom>
|
|
||||||
<BadgeCustom
|
|
||||||
color={colorActivationForBadge({
|
|
||||||
status: item.active,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item.active ? "Aktif" : "Tidak Aktif"}
|
|
||||||
</BadgeCustom>
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
value={<TextCustom>{item.name}</TextCustom>}
|
|
||||||
/>
|
|
||||||
{/* <Grid containerStyle={{ paddingBottom: 10 }}>
|
|
||||||
<Grid.Col span={4} style={{ paddingLeft: 10 }}>
|
|
||||||
<CenterCustom>
|
|
||||||
<BadgeCustom
|
|
||||||
color={item.active ? MainColor.green : MainColor.red}
|
|
||||||
>
|
|
||||||
{item.active ? "Aktif" : "Tidak Aktif"}
|
|
||||||
</BadgeCustom>
|
|
||||||
</CenterCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={8}>
|
|
||||||
<TextCustom bold>{item.name}</TextCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid> */}
|
|
||||||
<Divider />
|
|
||||||
</ClickableCustom>
|
|
||||||
))}
|
|
||||||
</StackCustom>
|
|
||||||
</View>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,254 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenEventDetail } from "@/screens/Admin/Event/ScreenEventDetail";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
AlertDefaultSystem,
|
|
||||||
BadgeCustom,
|
|
||||||
BaseBox,
|
|
||||||
DrawerCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
MenuDrawerDynamicGrid,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconDot, IconList } from "@/components/_Icon/IconComponent";
|
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
|
||||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
|
||||||
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
|
||||||
import ReportBox from "@/components/Box/ReportBox";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { funUpdateStatusEvent } from "@/screens/Admin/Event/funUpdateStatus";
|
|
||||||
import { apiAdminEventById } from "@/service/api-admin/api-admin-event";
|
|
||||||
import { DEEP_LINK_URL } from "@/service/api-config";
|
|
||||||
import { colorBadgeStatus } from "@/utils/colorBadge";
|
|
||||||
import { dateTimeView } from "@/utils/dateTimeView";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import React, { useCallback } from "react";
|
|
||||||
import QRCode from "react-native-qrcode-svg";
|
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
|
|
||||||
export default function AdminEventDetail() {
|
export default function AdminEventDetail() {
|
||||||
const { user } = useAuth();
|
return <Admin_ScreenEventDetail />;
|
||||||
const { id, status } = useLocalSearchParams();
|
|
||||||
const [openDrawer, setOpenDrawer] = React.useState(false);
|
|
||||||
|
|
||||||
const [data, setData] = React.useState<any | null>(null);
|
|
||||||
const [loadData, setLoadData] = React.useState(false);
|
|
||||||
const deepLinkURL = `${DEEP_LINK_URL}/event/${id}/confirmation?userId=${user?.id}`;
|
|
||||||
const deepLinkURLDEV = `${DEEP_LINK_URL}/--/event/${id}/confirmation?userId=${user?.id}`;
|
|
||||||
|
|
||||||
const isDevLink =
|
|
||||||
process.env.NODE_ENV === "development" ? deepLinkURLDEV : deepLinkURL;
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
const response = await apiAdminEventById({
|
|
||||||
id: id as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const listData = [
|
|
||||||
{
|
|
||||||
label: "Pembuat Event",
|
|
||||||
value: (data && data?.Author?.username) || "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Judul Event",
|
|
||||||
value: (data && data?.title) || "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Status",
|
|
||||||
value:
|
|
||||||
(data && (
|
|
||||||
<BadgeCustom color={colorBadgeStatus({ status: status as string })}>
|
|
||||||
{_.startCase(status as string)}
|
|
||||||
</BadgeCustom>
|
|
||||||
)) ||
|
|
||||||
"-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Lokasi",
|
|
||||||
value: (data && data?.lokasi) || "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Tipe Acara",
|
|
||||||
value: (data && data?.EventMaster_TipeAcara?.name) || "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Mulai Event",
|
|
||||||
value:
|
|
||||||
(data && data?.tanggal && dateTimeView({ date: data?.tanggal })) || "-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Event Berakhir",
|
|
||||||
value:
|
|
||||||
(data &&
|
|
||||||
data?.tanggalSelesai &&
|
|
||||||
dateTimeView({ date: data?.tanggalSelesai })) ||
|
|
||||||
"-",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Deskripsi",
|
|
||||||
value: (data && data?.deskripsi) || "-",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const rightComponent = (
|
|
||||||
<ActionIcon
|
|
||||||
icon={<IconDot size={ICON_SIZE_BUTTON} />}
|
|
||||||
onPress={() => {
|
|
||||||
setOpenDrawer(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlerSubmit = async () => {
|
|
||||||
try {
|
|
||||||
const response = await funUpdateStatusEvent({
|
|
||||||
id: id as string,
|
|
||||||
changeStatus: "publish",
|
|
||||||
data: { catatan: "", senderId: user?.id as string },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.success) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Gagal mempublikasikan event",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toast.show({
|
|
||||||
type: "success",
|
|
||||||
text1: "Event berhasil dipublikasikan",
|
|
||||||
});
|
|
||||||
router.back();
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={
|
|
||||||
<AdminBackButtonAntTitle
|
|
||||||
title={`Detail Data`}
|
|
||||||
rightComponent={
|
|
||||||
(status === "publish" || status === "history") && rightComponent
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom>
|
|
||||||
{listData.map((item, i) => (
|
|
||||||
<GridSpan_4_8
|
|
||||||
key={i}
|
|
||||||
label={<TextCustom bold>{item.label}</TextCustom>}
|
|
||||||
value={<TextCustom>{item.value}</TextCustom>}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</StackCustom>
|
|
||||||
|
|
||||||
<Spacing />
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
{data &&
|
|
||||||
data?.catatan &&
|
|
||||||
(status === "reject" || status === "review") && (
|
|
||||||
<ReportBox text={data?.catatan} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{(status === "publish" || status === "history") && (
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom style={{ alignItems: "center" }}>
|
|
||||||
<TextCustom bold>QR Code Event</TextCustom>
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : (
|
|
||||||
<QRCode
|
|
||||||
value={isDevLink}
|
|
||||||
size={200}
|
|
||||||
// logo={require("@/assets/images/logo-hipmi.png")}
|
|
||||||
// logoSize={70}
|
|
||||||
// logoBackgroundColor="transparent"
|
|
||||||
// logoBorderRadius={50}
|
|
||||||
// color="black"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<TextCustom align="center">{isDevLink}</TextCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{status === "review" && (
|
|
||||||
<AdminButtonReview
|
|
||||||
onPublish={() => {
|
|
||||||
AlertDefaultSystem({
|
|
||||||
title: "Publish",
|
|
||||||
message: "Apakah anda yakin ingin mempublikasikan data ini?",
|
|
||||||
textLeft: "Batal",
|
|
||||||
textRight: "Ya",
|
|
||||||
onPressRight: () => handlerSubmit(),
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onReject={() => {
|
|
||||||
router.push(`/admin/event/${id}/reject-input?status=${status}`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{status === "reject" && (
|
|
||||||
<AdminButtonReject
|
|
||||||
title="Tambah Catatan"
|
|
||||||
onReject={() => {
|
|
||||||
router.push(`/admin/event/${id}/reject-input?status=${status}`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Spacing />
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<MenuDrawerDynamicGrid
|
|
||||||
data={[
|
|
||||||
{
|
|
||||||
label: "Daftar Peserta",
|
|
||||||
icon: <IconList />,
|
|
||||||
path: `/admin/event/${id}/list-of-participants`,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressItem={(item) => {
|
|
||||||
setOpenDrawer(false);
|
|
||||||
router.push(item.path as any);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,105 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenEventListOfParticipants } from "@/screens/Admin/Event/ScreenEventListOfParticipants";
|
||||||
import {
|
|
||||||
BadgeCustom,
|
|
||||||
BaseBox,
|
|
||||||
Grid,
|
|
||||||
LoaderCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
|
||||||
import { apiAdminEventListOfParticipants } from "@/service/api-admin/api-admin-event";
|
|
||||||
import dayjs, { Dayjs } from "dayjs";
|
|
||||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { View } from "moti";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function AdminEventListOfParticipants() {
|
export default function AdminEventListOfParticipants() {
|
||||||
const { id } = useLocalSearchParams();
|
return <Admin_ScreenEventListOfParticipants />;
|
||||||
const [listData, setListData] = useState<any[] | null>(null);
|
|
||||||
const [loadData, setLoadData] = useState(false);
|
|
||||||
const [startDate, setStartDate] = useState<Dayjs | undefined>();
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
const response = await apiAdminEventListOfParticipants({
|
|
||||||
id: id as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("[DATA]", JSON.stringify(response, null, 2));
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
setStartDate(dayjs(response.data.Event.tanggal));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={<AdminBackButtonAntTitle title="Daftar Peserta" />}
|
|
||||||
>
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Belum ada peserta
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item: any, index: number) => (
|
|
||||||
<BaseBox key={index}>
|
|
||||||
<Grid>
|
|
||||||
<Grid.Col span={6}>
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
{item?.User?.username}
|
|
||||||
</TextCustom>
|
|
||||||
<TextCustom>+{item?.User?.nomor}</TextCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</Grid.Col>
|
|
||||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
|
||||||
{startDate &&
|
|
||||||
startDate.subtract(1, "hour").diff(dayjs()) < 0 ? (
|
|
||||||
<BadgeCustom
|
|
||||||
style={{ alignSelf: "flex-end" }}
|
|
||||||
color={item?.isPresent ? "green" : "red"}
|
|
||||||
>
|
|
||||||
{item?.isPresent ? "Hadir" : "Tidak Hadir"}
|
|
||||||
</BadgeCustom>
|
|
||||||
) : (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<BadgeCustom
|
|
||||||
style={{ alignSelf: "flex-end" }}
|
|
||||||
color="gray"
|
|
||||||
>
|
|
||||||
-
|
|
||||||
</BadgeCustom>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</Grid.Col>
|
|
||||||
</Grid>
|
|
||||||
</BaseBox>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,135 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenEventStatus } from "@/screens/Admin/Event/ScreenEventStatus";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
ClickableCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminEvent } from "@/service/api-admin/api-admin-event";
|
|
||||||
import { dateTimeView } from "@/utils/dateTimeView";
|
|
||||||
import { Octicons } from "@expo/vector-icons";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminEventStatus() {
|
export default function AdminEventStatus() {
|
||||||
const { status } = useLocalSearchParams();
|
return <Admin_ScreenEventStatus />;
|
||||||
console.log("[STATUS EVENT]", status);
|
|
||||||
|
|
||||||
const [listData, setListData] = useState<any[] | null>(null);
|
|
||||||
const [loadData, setLoadData] = useState(false);
|
|
||||||
const [search, setSearch] = useState<string>("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [status, search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
const response = await apiAdminEvent({
|
|
||||||
category: status as "publish" | "review" | "reject" | "history" as any,
|
|
||||||
search,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`[RES LIST BY STATUS: ${status}]`,
|
|
||||||
JSON.stringify(response, null, 2)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rightComponent = (
|
|
||||||
<SearchInput
|
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari"
|
|
||||||
value={search}
|
|
||||||
onChangeText={(value) => setSearch(value)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Event" />}>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title={`${_.startCase(status as string)}`}
|
|
||||||
rightComponent={rightComponent}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<AdminTitleTable
|
|
||||||
title1="Username"
|
|
||||||
title2="Tanggal"
|
|
||||||
title3="Judul Event"
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" size="small" color="gray">
|
|
||||||
Belum ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item, index) => (
|
|
||||||
<ClickableCustom
|
|
||||||
key={index}
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/event/${item.id}/${status}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AdminTableValue
|
|
||||||
key={index}
|
|
||||||
value1={
|
|
||||||
<TextCustom truncate={1}>
|
|
||||||
{item?.Author?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
// <ActionIcon
|
|
||||||
// icon={
|
|
||||||
// <Octicons
|
|
||||||
// name="eye"
|
|
||||||
// size={ICON_SIZE_BUTTON}
|
|
||||||
// color="black"
|
|
||||||
// />
|
|
||||||
// }
|
|
||||||
// onPress={() => {
|
|
||||||
// router.push(`/admin/event/${item.id}/${status}`);
|
|
||||||
// }}
|
|
||||||
// />
|
|
||||||
}
|
|
||||||
value2={
|
|
||||||
<TextCustom truncate={1}>
|
|
||||||
{dateTimeView({ date: item?.tanggal })}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
value3={
|
|
||||||
<TextCustom truncate={2}>{item?.title || "-"}</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Divider/>
|
|
||||||
</ClickableCustom>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,129 +1,5 @@
|
|||||||
import {
|
import { Admin_ScreenEventTypeOfEvent } from "@/screens/Admin/Voting/ScreenEventTypeOfEvent";
|
||||||
ActionIcon,
|
|
||||||
BadgeCustom,
|
|
||||||
CenterCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import { IconEdit } from "@/components/_Icon";
|
|
||||||
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminMasterTypeOfEvent } from "@/service/api-admin/api-master-admin";
|
|
||||||
import { colorActivationForBadge } from "@/utils/colorActivationForBadge";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminEventTypeOfEvent() {
|
export default function AdminEventTypeOfEvent() {
|
||||||
const [listData, setListData] = useState<any[] | null>(null);
|
return <Admin_ScreenEventTypeOfEvent />;
|
||||||
const [loadData, setLoadData] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
const response = await apiAdminMasterTypeOfEvent();
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]",error);
|
|
||||||
setListData([]);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Event" />}>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title="Tipe Acara"
|
|
||||||
rightComponent={
|
|
||||||
<AdminActionIconPlus
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/event/type-create`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<>
|
|
||||||
<GridViewCustomSpan
|
|
||||||
span1={2}
|
|
||||||
span2={5}
|
|
||||||
span3={5}
|
|
||||||
component1={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Aksi
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component2={<TextCustom bold align="center">Status</TextCustom>}
|
|
||||||
component3={<TextCustom bold>Tipe Acara</TextCustom>}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
<Spacing />
|
|
||||||
|
|
||||||
<StackCustom>
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Belum ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item, index) => (
|
|
||||||
<View key={index}>
|
|
||||||
<GridViewCustomSpan
|
|
||||||
span1={2}
|
|
||||||
span2={5}
|
|
||||||
span3={5}
|
|
||||||
component1={
|
|
||||||
<CenterCustom>
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<IconEdit size={ICON_SIZE_BUTTON} color="black" />
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/event/type-update?id=${item.id}`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
style2={{ alignItems: "center" }}
|
|
||||||
component2={
|
|
||||||
<CenterCustom>
|
|
||||||
<BadgeCustom
|
|
||||||
color={colorActivationForBadge({
|
|
||||||
status: item?.active,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item?.active ? "Aktif" : "Tidak Aktif"}
|
|
||||||
</BadgeCustom>
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
component3={<TextCustom >{item.name}</TextCustom>}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function AdminForumDetailPosting() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -72,6 +72,10 @@ export default function AdminForumDetailPosting() {
|
|||||||
label: "Total Report",
|
label: "Total Report",
|
||||||
value: data?.JumlahReportPosting || 0,
|
value: data?.JumlahReportPosting || 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Postingan",
|
||||||
|
value: (data && data?.diskusi) || "-",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -111,13 +115,6 @@ export default function AdminForumDetailPosting() {
|
|||||||
))}
|
))}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<TextCustom bold>Postingan</TextCustom>
|
|
||||||
<TextCustom>{(data && data?.diskusi) || "-"}</TextCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
<DrawerCustom
|
<DrawerCustom
|
||||||
|
|||||||
@@ -1,91 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenForumListComment } from "@/screens/Admin/Forum/ScreenForumListComment";
|
||||||
import {
|
|
||||||
LoaderCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import { IconOpenTo } from "@/components/_Icon/IconOpenTo";
|
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import { apiAdminForumCommentById } from "@/service/api-admin/api-admin-forum";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminForumListComment() {
|
export default function AdminForumListComment() {
|
||||||
const { id } = useLocalSearchParams();
|
return <Admin_ScreenForumListComment />;
|
||||||
const [listComment, setListComment] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadComment();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadComment = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiAdminForumCommentById({
|
|
||||||
id: id as string,
|
|
||||||
category: "get-all",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListComment(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setListComment([]);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={<AdminBackButtonAntTitle title="Daftar Komentar" />}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<AdminTitleTable title1="Aksi" title2="Report" title3="Komentar" />
|
|
||||||
<Divider />
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listComment) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Tidak ada komentar
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listComment?.map((item: any, index: number) => (
|
|
||||||
<AdminTableValue
|
|
||||||
key={index}
|
|
||||||
value1={
|
|
||||||
<IconOpenTo
|
|
||||||
onPress={() => {
|
|
||||||
router.push(
|
|
||||||
`/admin/forum/${item.id}/list-report-comment`
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
value2={
|
|
||||||
<TextCustom truncate={1}>
|
|
||||||
{item?.countReport || 0}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
value3={
|
|
||||||
<TextCustom truncate={2}>{item?.komentar || "-"}</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,262 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenForumDetailReportComment } from "@/screens/Admin/Forum/ScreenForumDetailReportComment";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
AlertDefaultSystem,
|
|
||||||
BaseBox,
|
|
||||||
CenterCustom,
|
|
||||||
DrawerCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
MenuDrawerDynamicGrid,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconDot, IconView } from "@/components/_Icon/IconComponent";
|
|
||||||
import { IconTrash } from "@/components/_Icon/IconTrash";
|
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
|
||||||
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import {
|
|
||||||
apiAdminForumCommentById,
|
|
||||||
apiAdminForumDeactivateComment,
|
|
||||||
apiAdminForumListReportCommentById,
|
|
||||||
} from "@/service/api-admin/api-admin-forum";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
|
|
||||||
export default function AdminForumReportComment() {
|
export default function AdminForumReportComment() {
|
||||||
const { id } = useLocalSearchParams();
|
return <Admin_ScreenForumDetailReportComment />;
|
||||||
const { user } = useAuth();
|
|
||||||
const [data, setData] = useState<any | null>(null);
|
|
||||||
const [listReport, setListReport] = useState<any[] | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
const [openDrawerAction, setOpenDrawerAction] = useState(false);
|
|
||||||
const [selectedReport, setSelectedReport] = useState({
|
|
||||||
id: "",
|
|
||||||
username: "",
|
|
||||||
kategori: "",
|
|
||||||
keterangan: "",
|
|
||||||
deskripsi: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiAdminForumCommentById({
|
|
||||||
id: id as string,
|
|
||||||
category: "get-one",
|
|
||||||
});
|
|
||||||
|
|
||||||
const responseReport = await apiAdminForumListReportCommentById({
|
|
||||||
id: id as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setData(response.data);
|
|
||||||
}
|
|
||||||
if (responseReport.success) {
|
|
||||||
setListReport(responseReport.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setData(null);
|
|
||||||
setListReport([]);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={
|
|
||||||
<AdminBackButtonAntTitle
|
|
||||||
title="Report Komentar"
|
|
||||||
rightComponent={
|
|
||||||
<ActionIcon
|
|
||||||
icon={<IconDot size={16} color={MainColor.darkblue} />}
|
|
||||||
onPress={() => setOpenDrawer(true)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={<TextCustom bold>Username</TextCustom>}
|
|
||||||
text2={<TextCustom>{data?.Author?.username || "-"}</TextCustom>}
|
|
||||||
/>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={<TextCustom bold>Komentar</TextCustom>}
|
|
||||||
text2={<TextCustom>{data?.komentar || "-"}</TextCustom>}
|
|
||||||
/>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
<AdminComp_BoxTitle title="Daftar Report Komentar" />
|
|
||||||
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Aksi
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text2={<TextCustom bold>Pelapor</TextCustom>}
|
|
||||||
text3={<TextCustom bold>Kategori Report</TextCustom>}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listReport) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Tidak ada report
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listReport?.map((item: any, index: number) => (
|
|
||||||
<View key={index}>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={
|
|
||||||
<CenterCustom>
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<IconView size={ICON_SIZE_BUTTON} color="black" />
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
setOpenDrawerAction(true);
|
|
||||||
setSelectedReport({
|
|
||||||
id: item.id,
|
|
||||||
username: item.User?.username,
|
|
||||||
kategori: item.ForumMaster_KategoriReport?.title,
|
|
||||||
keterangan:
|
|
||||||
item.ForumMaster_KategoriReport?.deskripsi,
|
|
||||||
deskripsi: item.deskripsi,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
text2={
|
|
||||||
<TextCustom truncate={1}>
|
|
||||||
{item?.User?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text3={
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
{item?.ForumMaster_KategoriReport?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<MenuDrawerDynamicGrid
|
|
||||||
data={[
|
|
||||||
{
|
|
||||||
icon: <IconTrash />,
|
|
||||||
label: "Hapus Komentar",
|
|
||||||
value: "delete",
|
|
||||||
path: "",
|
|
||||||
color: MainColor.red,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressItem={(item) => {
|
|
||||||
AlertDefaultSystem({
|
|
||||||
title: "Hapus Komentar",
|
|
||||||
message: "Apakah Anda yakin ingin menghapus komentar ini?",
|
|
||||||
textLeft: "Batal",
|
|
||||||
textRight: "Hapus",
|
|
||||||
onPressRight: async () => {
|
|
||||||
const deleteComment = await apiAdminForumDeactivateComment({
|
|
||||||
id: id as string,
|
|
||||||
data: {
|
|
||||||
senderId: user?.id as string,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// if (!deleteComment.success) {
|
|
||||||
// Toast.show({
|
|
||||||
// type: "error",
|
|
||||||
// text1: "Komentar gagal dihapus",
|
|
||||||
// });
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
setOpenDrawer(false);
|
|
||||||
Toast.show({
|
|
||||||
type: "success",
|
|
||||||
text1: "Komentar berhasil dihapus",
|
|
||||||
});
|
|
||||||
router.back();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawerAction}
|
|
||||||
closeDrawer={() => setOpenDrawerAction(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<GridSpan_4_8
|
|
||||||
label={<TextCustom bold>Pelapor</TextCustom>}
|
|
||||||
value={<TextCustom>{selectedReport?.username || "-"}</TextCustom>}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{selectedReport?.kategori && (
|
|
||||||
<>
|
|
||||||
<GridSpan_4_8
|
|
||||||
label={<TextCustom bold>Kategori Report</TextCustom>}
|
|
||||||
value={
|
|
||||||
<TextCustom>{selectedReport?.kategori || "-"}</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<GridSpan_4_8
|
|
||||||
label={<TextCustom bold>Keterangan</TextCustom>}
|
|
||||||
value={
|
|
||||||
<TextCustom>{selectedReport?.keterangan || "-"}</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedReport?.deskripsi && (
|
|
||||||
<GridSpan_4_8
|
|
||||||
label={<TextCustom bold>Deskripsi</TextCustom>}
|
|
||||||
value={
|
|
||||||
<TextCustom>{selectedReport?.deskripsi || "-"}</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,283 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenForumDetailReportPosting } from "@/screens/Admin/Forum/ScreenForumDetailReportPosting";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
AlertDefaultSystem,
|
|
||||||
BadgeCustom,
|
|
||||||
BaseBox,
|
|
||||||
CenterCustom,
|
|
||||||
DrawerCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
MenuDrawerDynamicGrid,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconDot, IconView } from "@/components/_Icon/IconComponent";
|
|
||||||
import { IconTrash } from "@/components/_Icon/IconTrash";
|
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
|
||||||
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import {
|
|
||||||
apiAdminForumDeactivatePosting,
|
|
||||||
apiAdminForumListReportPostingById,
|
|
||||||
apiAdminForumPostingById,
|
|
||||||
} from "@/service/api-admin/api-admin-forum";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
import Toast from "react-native-toast-message";
|
|
||||||
|
|
||||||
export default function AdminForumReportPosting() {
|
export default function AdminForumDetailReportPosting() {
|
||||||
const { user } = useAuth();
|
return <Admin_ScreenForumDetailReportPosting />;
|
||||||
const { id } = useLocalSearchParams();
|
|
||||||
const [openDrawerPage, setOpenDrawerPage] = useState(false);
|
|
||||||
const [openDrawerAction, setOpenDrawerAction] = useState(false);
|
|
||||||
|
|
||||||
const [data, setData] = useState<any | null>(null);
|
|
||||||
const [listReport, setListReport] = useState<any[] | null>(null);
|
|
||||||
const [loadListReport, setLoadListReport] = useState(false);
|
|
||||||
const [selectedReport, setSelectedReport] = useState({
|
|
||||||
id: "",
|
|
||||||
username: "",
|
|
||||||
kategori: "",
|
|
||||||
keterangan: "",
|
|
||||||
deskripsi: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadListReport(true);
|
|
||||||
const response = await apiAdminForumPostingById({
|
|
||||||
id: id as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
const responseReport = await apiAdminForumListReportPostingById({
|
|
||||||
id: id as string,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setData(response.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (responseReport.success) {
|
|
||||||
setListReport(responseReport.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadListReport(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={
|
|
||||||
<AdminBackButtonAntTitle
|
|
||||||
title="Report Posting"
|
|
||||||
rightComponent={
|
|
||||||
<ActionIcon
|
|
||||||
icon={<IconDot size={16} color={MainColor.darkblue} />}
|
|
||||||
onPress={() => setOpenDrawerPage(true)}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<BaseBox>
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={<TextCustom bold>Username</TextCustom>}
|
|
||||||
text2={<TextCustom>{data?.Author?.username || "-"}</TextCustom>}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={<TextCustom bold>Status</TextCustom>}
|
|
||||||
text2={
|
|
||||||
data && data?.ForumMaster_StatusPosting?.status ? (
|
|
||||||
<BadgeCustom
|
|
||||||
color={
|
|
||||||
data?.ForumMaster_StatusPosting?.status === "Open"
|
|
||||||
? MainColor.green
|
|
||||||
: MainColor.red
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{data?.ForumMaster_StatusPosting?.status === "Open"
|
|
||||||
? "Open"
|
|
||||||
: "Close"}
|
|
||||||
</BadgeCustom>
|
|
||||||
) : (
|
|
||||||
<TextCustom>{"-"}</TextCustom>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={<TextCustom bold>Postingan</TextCustom>}
|
|
||||||
text2={<TextCustom>{data?.diskusi || "-"}</TextCustom>}
|
|
||||||
/>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
|
|
||||||
<AdminComp_BoxTitle title="Daftar Report Posting" />
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Aksi
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text2={<TextCustom bold>Pelapor</TextCustom>}
|
|
||||||
text3={<TextCustom bold>Kategori Report</TextCustom>}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
{loadListReport ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listReport) ? (
|
|
||||||
<TextCustom align="center" color={"gray"}>
|
|
||||||
Belum ada report
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listReport?.map((item: any, index: number) => (
|
|
||||||
<View key={index}>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={
|
|
||||||
<CenterCustom>
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<IconView size={ICON_SIZE_BUTTON} color="black" />
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
setOpenDrawerAction(true);
|
|
||||||
setSelectedReport({
|
|
||||||
id: item?.id,
|
|
||||||
username: item?.User?.username,
|
|
||||||
kategori: item?.ForumMaster_KategoriReport?.title,
|
|
||||||
keterangan:
|
|
||||||
item?.ForumMaster_KategoriReport?.deskripsi,
|
|
||||||
deskripsi: item?.deskripsi,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
text2={
|
|
||||||
<TextCustom truncate>
|
|
||||||
{item?.User?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text3={
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
{item?.ForumMaster_KategoriReport?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawerPage}
|
|
||||||
closeDrawer={() => setOpenDrawerPage(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<MenuDrawerDynamicGrid
|
|
||||||
data={[
|
|
||||||
{
|
|
||||||
icon: <IconTrash />,
|
|
||||||
label: "Hapus Posting",
|
|
||||||
value: "delete",
|
|
||||||
path: "",
|
|
||||||
color: MainColor.red,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressItem={(item) => {
|
|
||||||
AlertDefaultSystem({
|
|
||||||
title: "Hapus Posting",
|
|
||||||
message: "Apakah Anda yakin ingin menghapus posting ini?",
|
|
||||||
textLeft: "Batal",
|
|
||||||
textRight: "Hapus",
|
|
||||||
onPressRight: async () => {
|
|
||||||
const response = await apiAdminForumDeactivatePosting({
|
|
||||||
id: id as string,
|
|
||||||
data: {
|
|
||||||
senderId: user?.id as string,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.success) {
|
|
||||||
Toast.show({
|
|
||||||
type: "error",
|
|
||||||
text1: "Posting gagal dihapus",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpenDrawerPage(false);
|
|
||||||
Toast.show({
|
|
||||||
type: "success",
|
|
||||||
text1: "Posting berhasil dihapus",
|
|
||||||
});
|
|
||||||
router.back();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawerAction}
|
|
||||||
closeDrawer={() => setOpenDrawerAction(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<GridSpan_4_8
|
|
||||||
label={<TextCustom bold>Pelapor</TextCustom>}
|
|
||||||
value={<TextCustom>{selectedReport?.username || "-"}</TextCustom>}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{selectedReport?.kategori && (
|
|
||||||
<>
|
|
||||||
<GridSpan_4_8
|
|
||||||
label={<TextCustom bold>Kategori Report</TextCustom>}
|
|
||||||
value={
|
|
||||||
<TextCustom>{selectedReport?.kategori || "-"}</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<GridSpan_4_8
|
|
||||||
label={<TextCustom bold>Keterangan</TextCustom>}
|
|
||||||
value={
|
|
||||||
<TextCustom>{selectedReport?.keterangan || "-"}</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{selectedReport?.deskripsi && (
|
|
||||||
<GridSpan_4_8
|
|
||||||
label={<TextCustom bold>Deskripsi</TextCustom>}
|
|
||||||
value={
|
|
||||||
<TextCustom>{selectedReport?.deskripsi || "-"}</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,121 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenForumPosting } from "@/screens/Admin/Forum/ScreenForumPosting";
|
||||||
import {
|
|
||||||
ClickableCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
|
||||||
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
|
||||||
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminForumPosting() {
|
export default function AdminForumPosting() {
|
||||||
const [list, setList] = useState<any | null>(null);
|
return <Admin_ScreenForumPosting />;
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
const [search, setSearch] = useState("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
handlerLoadList();
|
|
||||||
}, [search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlerLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiAdminForum({
|
|
||||||
category: "posting",
|
|
||||||
search: search,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("DATA", JSON.stringify(response, null, 2));
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setList(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rightComponent = (
|
|
||||||
<SearchInput
|
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari postingan"
|
|
||||||
value={search}
|
|
||||||
onChangeText={setSearch}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Forum" />}>
|
|
||||||
<AdminComp_BoxTitle title={"Posting"} rightComponent={rightComponent} />
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={<TextCustom bold truncate>Username</TextCustom>}
|
|
||||||
text2={<TextCustom bold truncate> Postingan</TextCustom>}
|
|
||||||
text3={<TextCustom bold align="center" truncate> Report Posting</TextCustom>}
|
|
||||||
text4={<TextCustom bold align="center" truncate> Komentar</TextCustom>}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
<Spacing />
|
|
||||||
<StackCustom>
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Belum ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<View key={index}>
|
|
||||||
<ClickableCustom
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/forum/${item.id}`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={
|
|
||||||
<TextCustom truncate={1}>
|
|
||||||
{item?.Author?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text2={
|
|
||||||
<TextCustom truncate>
|
|
||||||
{item?.diskusi || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text3={
|
|
||||||
<TextCustom align="center" truncate={2}>
|
|
||||||
{item?.reportPosting || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text4={
|
|
||||||
<TextCustom align="center" truncate={2}>
|
|
||||||
{item?.komentar || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
</ClickableCustom>
|
|
||||||
<Divider />
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,137 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenForumReportComment } from "@/screens/Admin/Forum/ScreenForumReportComment";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
ClickableCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
Spacing,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconView } from "@/components/_Icon/IconComponent";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminForumReportComment() {
|
export default function AdminForumReportComment() {
|
||||||
const [listData, setListData] = useState<any[] | null>(null);
|
return <Admin_ScreenForumReportComment />;
|
||||||
const [loadList, setLoadList] = useState<boolean>(false);
|
|
||||||
const [search, setSearch] = useState<string>("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
|
|
||||||
const response = await apiAdminForum({
|
|
||||||
category: "report_comment",
|
|
||||||
search: search,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rightComponent = (
|
|
||||||
<SearchInput
|
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari Komentar"
|
|
||||||
value={search}
|
|
||||||
onChangeText={setSearch}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Forum" />}>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title="Report Komentar"
|
|
||||||
rightComponent={rightComponent}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
Pelapor
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text2={
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
Komentar
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text3={
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
Jenis Laporan
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
<Spacing />
|
|
||||||
<StackCustom gap={"lg"}>
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Belum ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item: any, index: number) => (
|
|
||||||
<View key={index}>
|
|
||||||
<ClickableCustom
|
|
||||||
onPress={() => {
|
|
||||||
router.push(
|
|
||||||
`/admin/forum/${item?.Forum_Komentar?.id}/list-report-comment`
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={
|
|
||||||
<TextCustom truncate={1}>
|
|
||||||
{item?.User?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text2={
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
{item?.Forum_Komentar?.komentar || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text3={
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
{item?.ForumMaster_KategoriReport?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ClickableCustom>
|
|
||||||
<Spacing />
|
|
||||||
<Divider />
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,124 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenForumReportPosting } from "@/screens/Admin/Forum/ScreenForumReportPosting";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
ClickableCustom,
|
|
||||||
Divider,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconView } from "@/components/_Icon/IconComponent";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
|
|
||||||
export default function AdminForumReportPosting() {
|
export default function AdminForumReportPosting() {
|
||||||
const [listData, setListData] = useState<any[] | null>(null);
|
return <Admin_ScreenForumReportPosting />;
|
||||||
const [loadList, setLoadList] = useState<boolean>(false);
|
|
||||||
const [search, setSearch] = useState<string>("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
|
|
||||||
const response = await apiAdminForum({
|
|
||||||
category: "report_posting",
|
|
||||||
search: search,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rightComponent = (
|
|
||||||
<SearchInput
|
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari Postingan"
|
|
||||||
value={search}
|
|
||||||
onChangeText={setSearch}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Forum" />}>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title="Report Posting"
|
|
||||||
rightComponent={rightComponent}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
Pelapor
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text2={
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
Postingan
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
<StackCustom>
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Belum ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item: any, index: number) => (
|
|
||||||
<View key={index}>
|
|
||||||
<ClickableCustom
|
|
||||||
onPress={() => {
|
|
||||||
router.push(
|
|
||||||
`/admin/forum/${item?.Forum_Posting?.id}/list-report-posting`
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GridSpan_NewComponent
|
|
||||||
text1={
|
|
||||||
<TextCustom truncate={1}>
|
|
||||||
{item?.User?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
text2={
|
|
||||||
<TextCustom truncate={1}>
|
|
||||||
{item?.Forum_Posting?.diskusi || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ClickableCustom>
|
|
||||||
<Divider />
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
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";
|
||||||
@@ -182,9 +183,9 @@ export default function AdminInvestmentDetail() {
|
|||||||
|
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
<GridSpan_4_8
|
<GridTwoView
|
||||||
label={<TextCustom bold>File Prospektus</TextCustom>}
|
leftItem={<TextCustom bold>File Prospektus</TextCustom>}
|
||||||
value={
|
rightItem={
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
iconLeft={
|
iconLeft={
|
||||||
<IconProspectus
|
<IconProspectus
|
||||||
@@ -202,9 +203,10 @@ export default function AdminInvestmentDetail() {
|
|||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<GridSpan_4_8
|
<GridTwoView
|
||||||
label={<TextCustom bold>File Dokumen</TextCustom>}
|
|
||||||
value={
|
leftItem={<TextCustom bold>File Dokumen</TextCustom>}
|
||||||
|
rightItem={
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
{_.isEmpty(data?.DokumenInvestasi) ? (
|
{_.isEmpty(data?.DokumenInvestasi) ? (
|
||||||
<TextCustom align="center">-</TextCustom>
|
<TextCustom align="center">-</TextCustom>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
AlertDefaultSystem,
|
AlertDefaultSystem,
|
||||||
BadgeCustom,
|
BadgeCustom,
|
||||||
BaseBox,
|
BaseBox,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||||
@@ -15,8 +15,8 @@ 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 { useAuth } from "@/hooks/use-auth";
|
||||||
import {
|
import {
|
||||||
apiAdminInvestmentGetOneInvoiceById,
|
apiAdminInvestmentGetOneInvoiceById,
|
||||||
apiAdminInvestmentUpdateInvoice,
|
apiAdminInvestmentUpdateInvoice,
|
||||||
} from "@/service/api-admin/api-admin-investment";
|
} from "@/service/api-admin/api-admin-investment";
|
||||||
import { colorBadgeTransaction } from "@/utils/colorBadge";
|
import { colorBadgeTransaction } from "@/utils/colorBadge";
|
||||||
import { dateTimeView } from "@/utils/dateTimeView";
|
import { dateTimeView } from "@/utils/dateTimeView";
|
||||||
@@ -60,7 +60,7 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
value: (data && data?.MasterBank?.namaBank) || "-",
|
value: (data && data?.MasterBank?.namaBank) || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Jumlah Investasi",
|
label: "Nominal",
|
||||||
value: (data && `Rp. ${formatCurrencyDisplay(data?.nominal)}`) || "-",
|
value: (data && `Rp. ${formatCurrencyDisplay(data?.nominal)}`) || "-",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -158,7 +158,7 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
spanRight={6}
|
spanRight={6}
|
||||||
styleLeft={{ paddingRight: 10 }}
|
styleLeft={{ paddingRight: 10 }}
|
||||||
styleRight={{ paddingLeft: 10 }}
|
styleRight={{ paddingLeft: 10 }}
|
||||||
leftIcon={
|
leftItem={
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
@@ -181,7 +181,7 @@ export default function AdminInvestmentTransactionDetail() {
|
|||||||
Tolak
|
Tolak
|
||||||
</ButtonCustom>
|
</ButtonCustom>
|
||||||
}
|
}
|
||||||
rightIcon={
|
rightItem={
|
||||||
<ButtonCustom
|
<ButtonCustom
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
|||||||
@@ -1,195 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenInvestmentListOfInvestor } from "@/screens/Admin/Investment/ScreenInvestmentListOfInvestor";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
BadgeCustom,
|
|
||||||
CenterCustom,
|
|
||||||
LoaderCustom,
|
|
||||||
SelectCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconView } from "@/components/_Icon/IconComponent";
|
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
|
||||||
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
|
|
||||||
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminInvestmentListOfInvestor } from "@/service/api-admin/api-admin-investment";
|
|
||||||
import { apiMasterTransaction } from "@/service/api-client/api-master";
|
|
||||||
import { colorBadgeTransaction } from "@/utils/colorBadge";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import React, { useEffect } from "react";
|
|
||||||
import { View } from "react-native";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminInvestmentListOfInvestor() {
|
export default function AdminInvestmentListOfInvestor() {
|
||||||
const { id } = useLocalSearchParams();
|
return <Admin_ScreenInvestmentListOfInvestor />;
|
||||||
console.log("[ID]", id);
|
|
||||||
|
|
||||||
const [listData, setListData] = React.useState<any[] | null>(null);
|
|
||||||
const [loadData, setLoadData] = React.useState(false);
|
|
||||||
const [master, setMaster] = React.useState<any[]>([]);
|
|
||||||
|
|
||||||
const [selectValue, setSelectValue] = React.useState<string | null>(null);
|
|
||||||
const [selectedStatus, setSelectedStatus] = React.useState<string | null>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onLoadMaster();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onLoadMaster = async () => {
|
|
||||||
try {
|
|
||||||
const response = await apiMasterTransaction();
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setMaster(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setMaster([]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
React.useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [id, selectValue])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadData(true);
|
|
||||||
const response = await apiAdminInvestmentListOfInvestor({
|
|
||||||
id: id as string,
|
|
||||||
status: selectedStatus as any,
|
|
||||||
});
|
|
||||||
console.log("[LIST OF INVESTOR]", JSON.stringify(response, null, 2));
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
setListData([]);
|
|
||||||
} finally {
|
|
||||||
setLoadData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onLoadMaster();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const searchComponent = (
|
|
||||||
<View style={{ flexDirection: "row", gap: 5 }}>
|
|
||||||
<SelectCustom
|
|
||||||
placeholder="Pilih status transaksi"
|
|
||||||
data={
|
|
||||||
_.isEmpty(master)
|
|
||||||
? []
|
|
||||||
: master?.map((item: any) => ({
|
|
||||||
label: item.name,
|
|
||||||
value: item.id,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
value={selectValue}
|
|
||||||
onChange={(value: any) => {
|
|
||||||
setSelectValue(value);
|
|
||||||
const nameSelected = master.find((item: any) => item.id === value);
|
|
||||||
const statusChooses = _.lowerCase(nameSelected?.name);
|
|
||||||
setSelectedStatus(statusChooses);
|
|
||||||
}}
|
|
||||||
styleContainer={{ width: "100%", marginBottom: 0 }}
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
|
|
||||||
const headerComponent = (
|
|
||||||
<StackCustom gap={"xs"}>
|
|
||||||
<AdminBackButtonAntTitle title="Daftar Investor" />
|
|
||||||
{searchComponent}
|
|
||||||
</StackCustom>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={headerComponent}>
|
|
||||||
<StackCustom>
|
|
||||||
<GridViewCustomSpan
|
|
||||||
span1={3}
|
|
||||||
span2={5}
|
|
||||||
span3={4}
|
|
||||||
component1={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Aksi
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component2={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Investor
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component3={
|
|
||||||
<TextCustom bold align="center">
|
|
||||||
Status
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
<StackCustom>
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<NoDataText />
|
|
||||||
) : (
|
|
||||||
listData?.map((item: any, index: number) => (
|
|
||||||
<View key={index}>
|
|
||||||
<GridViewCustomSpan
|
|
||||||
span1={3}
|
|
||||||
span2={5}
|
|
||||||
span3={4}
|
|
||||||
component1={
|
|
||||||
<CenterCustom>
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<IconView size={ICON_SIZE_BUTTON} color="black" />
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
router.push(
|
|
||||||
`/admin/investment/${item?.id}/${_.lowerCase(
|
|
||||||
item?.StatusInvoice?.name
|
|
||||||
)}/transaction-detail`
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
component2={
|
|
||||||
<TextCustom bold align="center" truncate>
|
|
||||||
{item?.Author?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component3={
|
|
||||||
<BadgeCustom
|
|
||||||
style={{ alignSelf: "center" }}
|
|
||||||
color={colorBadgeTransaction({
|
|
||||||
status: item?.StatusInvoice?.name,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item?.StatusInvoice?.name}
|
|
||||||
</BadgeCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,113 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenInvestmentStatus } from "@/screens/Admin/Investment/ScreenInvestmentStatus";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminInvestment } from "@/service/api-admin/api-admin-investment";
|
|
||||||
import { Octicons } from "@expo/vector-icons";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import React, { useCallback } from "react";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminInvestmentStatus() {
|
export default function AdminInvestmentStatus() {
|
||||||
const { status } = useLocalSearchParams();
|
return <Admin_ScreenInvestmentStatus />;
|
||||||
const [listData, setListData] = React.useState<any[] | null>(null);
|
|
||||||
const [loadData, setLoadingData] = React.useState(false);
|
|
||||||
const [search, setSearch] = React.useState("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [status, search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadingData(true);
|
|
||||||
const response = await apiAdminInvestment({
|
|
||||||
category: status as "publish" | "review" | "reject",
|
|
||||||
search,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
setListData([]);
|
|
||||||
} finally {
|
|
||||||
setLoadingData(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rightComponent = (
|
|
||||||
<SearchInput
|
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari"
|
|
||||||
value={search}
|
|
||||||
onChangeText={setSearch}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Investasi" />}>
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title={`${_.startCase(status as string)}`}
|
|
||||||
rightComponent={rightComponent}
|
|
||||||
/>
|
|
||||||
<AdminTitleTable
|
|
||||||
title1="Aksi"
|
|
||||||
title2="Username"
|
|
||||||
title3="Judul Investasi"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{loadData ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<NoDataText />
|
|
||||||
) : (
|
|
||||||
listData?.map((item: any, index: number) => (
|
|
||||||
<AdminTableValue
|
|
||||||
key={index}
|
|
||||||
value1={
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<Octicons
|
|
||||||
name="eye"
|
|
||||||
size={ICON_SIZE_BUTTON}
|
|
||||||
color="black"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/investment/${item.id}/${status}`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
value2={<TextCustom truncate={1}>{item?.author?.username}</TextCustom>}
|
|
||||||
value3={
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
{item?.title}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
BaseBox,
|
BaseBox,
|
||||||
DummyLandscapeImage,
|
DummyLandscapeImage,
|
||||||
Grid,
|
Grid,
|
||||||
|
NewWrapper,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
@@ -120,7 +121,7 @@ export default function AdminJobDetailStatus() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper
|
<NewWrapper
|
||||||
headerComponent={<AdminBackButtonAntTitle title={`Detail Data`} />}
|
headerComponent={<AdminBackButtonAntTitle title={`Detail Data`} />}
|
||||||
>
|
>
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
@@ -184,7 +185,7 @@ export default function AdminJobDetailStatus() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import {
|
import {
|
||||||
AlertDefaultSystem,
|
AlertDefaultSystem,
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
|
NewWrapper,
|
||||||
TextAreaCustom,
|
TextAreaCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
@@ -100,7 +101,7 @@ export default function AdminJobRejectInput() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper
|
<NewWrapper
|
||||||
footerComponent={buttonSubmit}
|
footerComponent={buttonSubmit}
|
||||||
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
|
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
|
||||||
>
|
>
|
||||||
@@ -112,7 +113,7 @@ export default function AdminJobRejectInput() {
|
|||||||
showCount
|
showCount
|
||||||
maxLength={1000}
|
maxLength={1000}
|
||||||
/>
|
/>
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,117 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenJobStatus } from "@/screens/Admin/Job/ScreenJobStatus";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminJob } from "@/service/api-admin/api-admin-job";
|
|
||||||
import { Octicons } from "@expo/vector-icons";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminJobStatus() {
|
export default function AdminJobStatus() {
|
||||||
const { status } = useLocalSearchParams();
|
return <Admin_ScreenJobStatus />;
|
||||||
const [list, setList] = useState<any | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
const [search, setSearch] = useState("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
handlerLoadList();
|
|
||||||
}, [status, search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const handlerLoadList = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiAdminJob({
|
|
||||||
category: status as "publish" | "review" | "reject",
|
|
||||||
search,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setList(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rightComponent = (
|
|
||||||
<SearchInput
|
|
||||||
placeholder="Cari"
|
|
||||||
onChangeText={setSearch}
|
|
||||||
value={search}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Job Vacancy" />}>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title={`${_.startCase(status as string)}`}
|
|
||||||
rightComponent={rightComponent}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<StackCustom>
|
|
||||||
<AdminTitleTable
|
|
||||||
title1="Aksi"
|
|
||||||
title2="Username"
|
|
||||||
title3="Judul Pekerjaan"
|
|
||||||
/>
|
|
||||||
{/* <Spacing /> */}
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" color="gray">
|
|
||||||
Tidak ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
list?.map((item: any, index: number) => (
|
|
||||||
<AdminTableValue
|
|
||||||
key={index}
|
|
||||||
value1={
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<Octicons
|
|
||||||
name="eye"
|
|
||||||
size={ICON_SIZE_BUTTON}
|
|
||||||
color="black"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/job/${item.id}/${status}`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
value2={
|
|
||||||
<TextCustom align="center" truncate={1}>
|
|
||||||
{item?.Author?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
value3={
|
|
||||||
<TextCustom truncate={2} align="center">
|
|
||||||
{item?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,22 @@
|
|||||||
import { ButtonCustom, DrawerCustom, DummyLandscapeImage, Grid, Spacing, StackCustom, TextCustom, ViewWrapper } from "@/components";
|
import {
|
||||||
|
ButtonCustom,
|
||||||
|
DrawerCustom,
|
||||||
|
DummyLandscapeImage,
|
||||||
|
Grid,
|
||||||
|
Spacing,
|
||||||
|
StackCustom,
|
||||||
|
TextCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
import API_IMAGE from "@/constants/api-storage";
|
import { MapMarker, MapsV2Custom } from "@/components/Map/MapsV2Custom";
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||||
import { apiMapsGetAll } from "@/service/api-client/api-maps";
|
import { apiMapsGetAll } from "@/service/api-client/api-maps";
|
||||||
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
|
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
|
||||||
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
||||||
import { Image } from "expo-image";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
import { router, useFocusEffect } from "expo-router";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { View } from "react-native";
|
|
||||||
import MapView, { Marker } from "react-native-maps";
|
|
||||||
|
|
||||||
const defaultRegion = {
|
|
||||||
latitude: -8.737109,
|
|
||||||
longitude: 115.1756897,
|
|
||||||
latitudeDelta: 0.1,
|
|
||||||
longitudeDelta: 0.1,
|
|
||||||
height: 300,
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface LocationItem {
|
|
||||||
id: string | number;
|
|
||||||
latitude: number;
|
|
||||||
longitude: number;
|
|
||||||
name: string;
|
|
||||||
imageId?: string;
|
|
||||||
}
|
|
||||||
export default function AdminMaps() {
|
export default function AdminMaps() {
|
||||||
const [list, setList] = useState<any[] | null>(null);
|
const [list, setList] = useState<any[] | null>(null);
|
||||||
const [loadList, setLoadList] = useState(false);
|
const [loadList, setLoadList] = useState(false);
|
||||||
@@ -45,7 +36,7 @@ export default function AdminMaps() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
handlerLoadList();
|
handlerLoadList();
|
||||||
}, [])
|
}, []),
|
||||||
);
|
);
|
||||||
|
|
||||||
const handlerLoadList = async () => {
|
const handlerLoadList = async () => {
|
||||||
@@ -63,74 +54,30 @@ export default function AdminMaps() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const markers: MapMarker[] = list?.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
coordinate: [item.longitude, item.latitude] as [number, number],
|
||||||
|
imageId: item.Portofolio?.logoId,
|
||||||
|
onSelected: () => {
|
||||||
|
setOpenDrawer(true);
|
||||||
|
setSelected({
|
||||||
|
id: item?.id,
|
||||||
|
bidangBisnis: item?.Portofolio?.MasterBidangBisnis?.name,
|
||||||
|
nomorTelepon: item?.Portofolio?.tlpn,
|
||||||
|
alamatBisnis: item?.Portofolio?.alamatKantor,
|
||||||
|
namePin: item?.namePin,
|
||||||
|
imageId: item?.imageId,
|
||||||
|
portofolioId: item?.Portofolio?.id,
|
||||||
|
latitude: item?.latitude,
|
||||||
|
longitude: item?.longitude,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})) || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
|
<ViewWrapper style={{ paddingInline: 0, paddingBlock: 0 }}>
|
||||||
{/* <MapCustom height={"100%"} /> */}
|
<MapsV2Custom markers={markers} />
|
||||||
<View style={{ flex: 1 }}>
|
|
||||||
{loadList ? (
|
|
||||||
<MapView
|
|
||||||
initialRegion={defaultRegion}
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<MapView
|
|
||||||
initialRegion={defaultRegion}
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{list?.map((item: any, index: number) => {
|
|
||||||
return (
|
|
||||||
<Marker
|
|
||||||
key={item?.id}
|
|
||||||
coordinate={{
|
|
||||||
latitude: item?.latitude,
|
|
||||||
longitude: item?.longitude,
|
|
||||||
}}
|
|
||||||
title={item?.namePin}
|
|
||||||
onPress={() => {
|
|
||||||
setOpenDrawer(true);
|
|
||||||
setSelected({
|
|
||||||
id: item?.id,
|
|
||||||
bidangBisnis:
|
|
||||||
item?.Portofolio?.MasterBidangBisnis?.name,
|
|
||||||
nomorTelepon: item?.Portofolio?.tlpn,
|
|
||||||
alamatBisnis: item?.Portofolio?.alamatKantor,
|
|
||||||
namePin: item?.namePin,
|
|
||||||
imageId: item?.imageId,
|
|
||||||
portofolioId: item?.Portofolio?.id,
|
|
||||||
latitude: item?.latitude,
|
|
||||||
longitude: item?.longitude,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
// Gunakan gambar kustom jika tersedia
|
|
||||||
>
|
|
||||||
<View>
|
|
||||||
<Image
|
|
||||||
source={{
|
|
||||||
uri: API_IMAGE.GET({
|
|
||||||
fileId: item?.Portofolio?.logoId,
|
|
||||||
}),
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
width: 30,
|
|
||||||
height: 30,
|
|
||||||
borderRadius: 100,
|
|
||||||
borderWidth: 1,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</Marker>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</MapView>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</ViewWrapper>
|
</ViewWrapper>
|
||||||
|
|
||||||
<DrawerCustom
|
<DrawerCustom
|
||||||
@@ -138,58 +85,60 @@ export default function AdminMaps() {
|
|||||||
closeDrawer={() => setOpenDrawer(false)}
|
closeDrawer={() => setOpenDrawer(false)}
|
||||||
height={"auto"}
|
height={"auto"}
|
||||||
>
|
>
|
||||||
<DummyLandscapeImage height={200} imageId={selected.imageId} />
|
{selected.imageId && (
|
||||||
|
<DummyLandscapeImage height={200} imageId={selected.imageId} />
|
||||||
|
)}
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<StackCustom gap={"xs"}>
|
<StackCustom gap={"xs"}>
|
||||||
<GridTwoView
|
<GridTwoView
|
||||||
spanLeft={2}
|
spanLeft={2}
|
||||||
spanRight={10}
|
spanRight={10}
|
||||||
leftIcon={
|
leftItem={
|
||||||
<FontAwesome
|
<FontAwesome
|
||||||
name="building-o"
|
name="building-o"
|
||||||
size={ICON_SIZE_SMALL}
|
size={ICON_SIZE_SMALL}
|
||||||
color="white"
|
color="white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
rightIcon={<TextCustom>{selected.namePin}</TextCustom>}
|
rightItem={<TextCustom>{selected.namePin}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<GridTwoView
|
<GridTwoView
|
||||||
spanLeft={2}
|
spanLeft={2}
|
||||||
spanRight={10}
|
spanRight={10}
|
||||||
leftIcon={
|
leftItem={
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name="list-outline"
|
name="list-outline"
|
||||||
size={ICON_SIZE_SMALL}
|
size={ICON_SIZE_SMALL}
|
||||||
color="white"
|
color="white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
rightIcon={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
rightItem={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<GridTwoView
|
<GridTwoView
|
||||||
spanLeft={2}
|
spanLeft={2}
|
||||||
spanRight={10}
|
spanRight={10}
|
||||||
leftIcon={
|
leftItem={
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name="call-outline"
|
name="call-outline"
|
||||||
size={ICON_SIZE_SMALL}
|
size={ICON_SIZE_SMALL}
|
||||||
color="white"
|
color="white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
rightIcon={<TextCustom>+{selected.nomorTelepon}</TextCustom>}
|
rightItem={<TextCustom>+{selected.nomorTelepon}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
<GridTwoView
|
<GridTwoView
|
||||||
spanLeft={2}
|
spanLeft={2}
|
||||||
spanRight={10}
|
spanRight={10}
|
||||||
leftIcon={
|
leftItem={
|
||||||
<Ionicons
|
<Ionicons
|
||||||
name="location-outline"
|
name="location-outline"
|
||||||
size={ICON_SIZE_SMALL}
|
size={ICON_SIZE_SMALL}
|
||||||
color="white"
|
color="white"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
rightIcon={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
rightItem={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
|
|||||||
@@ -1,213 +1,10 @@
|
|||||||
import {
|
import Admin_ScreenNotification from "@/screens/Admin/Notification-Admin/ScreenNotificationAdmin";
|
||||||
AlertDefaultSystem,
|
import Admin_ScreenNotification2 from "@/screens/Admin/Notification-Admin/ScreenNotificationAdmin2";
|
||||||
BackButton,
|
|
||||||
BaseBox,
|
|
||||||
DrawerCustom,
|
|
||||||
MenuDrawerDynamicGrid,
|
|
||||||
NewWrapper,
|
|
||||||
ScrollableCustom,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
} from "@/components";
|
|
||||||
import { IconPlus } from "@/components/_Icon";
|
|
||||||
import { IconDot } from "@/components/_Icon/IconComponent";
|
|
||||||
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
|
||||||
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
|
||||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
|
||||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { useNotificationStore } from "@/hooks/use-notification-store";
|
|
||||||
import { apiGetNotificationsById } from "@/service/api-notifications";
|
|
||||||
import { listOfcategoriesAppNotification } from "@/types/type-notification-category";
|
|
||||||
import { formatChatTime } from "@/utils/formatChatTime";
|
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
import { router, Stack, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { RefreshControl, View } from "react-native";
|
|
||||||
|
|
||||||
const selectedCategory = (value: string) => {
|
|
||||||
const category = listOfcategoriesAppNotification.find(
|
|
||||||
(c) => c.value === value
|
|
||||||
);
|
|
||||||
return category?.label;
|
|
||||||
};
|
|
||||||
|
|
||||||
const BoxNotification = ({
|
|
||||||
data,
|
|
||||||
activeCategory,
|
|
||||||
}: {
|
|
||||||
data: any;
|
|
||||||
activeCategory: string | null;
|
|
||||||
}) => {
|
|
||||||
const { markAsRead } = useNotificationStore();
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<BaseBox
|
|
||||||
backgroundColor={data.isRead ? AccentColor.darkblue : AccentColor.blue}
|
|
||||||
onPress={() => {
|
|
||||||
console.log(
|
|
||||||
"Notification >",
|
|
||||||
selectedCategory(activeCategory as string)
|
|
||||||
);
|
|
||||||
router.push(data.deepLink);
|
|
||||||
markAsRead(data.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<StackCustom>
|
|
||||||
<TextCustom truncate={2} bold>
|
|
||||||
{data.title}
|
|
||||||
</TextCustom>
|
|
||||||
|
|
||||||
<TextCustom truncate={2}>{data.pesan}</TextCustom>
|
|
||||||
|
|
||||||
<TextCustom size="small" color="gray">
|
|
||||||
{formatChatTime(data.createdAt)}
|
|
||||||
</TextCustom>
|
|
||||||
</StackCustom>
|
|
||||||
</BaseBox>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function AdminNotification() {
|
export default function AdminNotification() {
|
||||||
const { user } = useAuth();
|
|
||||||
const [activeCategory, setActiveCategory] = useState<string | null>("event");
|
|
||||||
const [listData, setListData] = useState<any[]>([]);
|
|
||||||
const [refreshing, setRefreshing] = useState(false);
|
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [openDrawer, setOpenDrawer] = useState(false);
|
|
||||||
|
|
||||||
const { markAsReadAll } = useNotificationStore();
|
|
||||||
|
|
||||||
const handlePress = (item: any) => {
|
|
||||||
setActiveCategory(item.value);
|
|
||||||
// tambahkan logika lain seperti filter dsb.
|
|
||||||
};
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
fecthData();
|
|
||||||
}, [activeCategory])
|
|
||||||
);
|
|
||||||
|
|
||||||
const fecthData = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
|
||||||
const response = await apiGetNotificationsById({
|
|
||||||
id: user?.id as any,
|
|
||||||
category: activeCategory as any,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
} else {
|
|
||||||
setListData([]);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Error Notification", error);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onRefresh = () => {
|
|
||||||
setRefreshing(true);
|
|
||||||
fecthData();
|
|
||||||
setRefreshing(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Admin_ScreenNotification2 />
|
||||||
options={{
|
|
||||||
title: "Admin Notifikasi",
|
|
||||||
headerLeft: () => <BackButton />,
|
|
||||||
headerRight: () => (
|
|
||||||
<IconDot
|
|
||||||
color={MainColor.yellow}
|
|
||||||
onPress={() => setOpenDrawer(true)}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<NewWrapper
|
|
||||||
headerComponent={
|
|
||||||
<ScrollableCustom
|
|
||||||
data={listOfcategoriesAppNotification.map((e, i) => ({
|
|
||||||
id: i,
|
|
||||||
label: e.label,
|
|
||||||
value: e.value,
|
|
||||||
}))}
|
|
||||||
onButtonPress={handlePress}
|
|
||||||
activeId={activeCategory as string}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
refreshControl={
|
|
||||||
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{loading ? (
|
|
||||||
<ListSkeletonComponent />
|
|
||||||
) : _.isEmpty(listData) ? (
|
|
||||||
<NoDataText text="Belum ada notifikasi" />
|
|
||||||
) : (
|
|
||||||
listData.map((e, i) => (
|
|
||||||
<View key={i}>
|
|
||||||
<BoxNotification
|
|
||||||
data={e}
|
|
||||||
activeCategory={activeCategory as any}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</NewWrapper>
|
|
||||||
|
|
||||||
<DrawerCustom
|
|
||||||
isVisible={openDrawer}
|
|
||||||
closeDrawer={() => setOpenDrawer(false)}
|
|
||||||
height={"auto"}
|
|
||||||
>
|
|
||||||
<MenuDrawerDynamicGrid
|
|
||||||
data={[
|
|
||||||
{
|
|
||||||
label: "Tandai Semua Dibaca",
|
|
||||||
value: "read-all",
|
|
||||||
icon: (
|
|
||||||
<Ionicons
|
|
||||||
name="reader-outline"
|
|
||||||
size={ICON_SIZE_SMALL}
|
|
||||||
color={MainColor.white}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
path: "",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onPressItem={(item: any) => {
|
|
||||||
console.log("Item", item.value);
|
|
||||||
if (item.value === "read-all") {
|
|
||||||
AlertDefaultSystem({
|
|
||||||
title: "Tandai Semua Dibaca",
|
|
||||||
message:
|
|
||||||
"Apakah Anda yakin ingin menandai semua notifikasi dibaca?",
|
|
||||||
textLeft: "Batal",
|
|
||||||
textRight: "Ya",
|
|
||||||
onPressRight: () => {
|
|
||||||
markAsReadAll(user?.id as any);
|
|
||||||
const data = _.cloneDeep(listData);
|
|
||||||
data.forEach((e) => {
|
|
||||||
e.isRead = true;
|
|
||||||
});
|
|
||||||
setListData(data);
|
|
||||||
onRefresh();
|
|
||||||
setOpenDrawer(false);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DrawerCustom>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
LoaderCustom,
|
LoaderCustom,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
import {
|
import {
|
||||||
apiAdminUserAccessGetById,
|
apiAdminUserAccessGetById,
|
||||||
apiAdminUserAccessUpdateStatus,
|
apiAdminUserAccessUpdateStatus,
|
||||||
} from "@/service/api-admin/api-admin-user-access";
|
} from "@/service/api-admin/api-admin-user-access";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
@@ -26,7 +26,7 @@ export default function SuperAdminDetail() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -48,7 +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"
|
category: "role",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.success) {
|
if (!response.success) {
|
||||||
@@ -102,8 +102,8 @@ export default function SuperAdminDetail() {
|
|||||||
key={index}
|
key={index}
|
||||||
spanLeft={4}
|
spanLeft={4}
|
||||||
spanRight={8}
|
spanRight={8}
|
||||||
leftIcon={<TextCustom bold>{item?.label}</TextCustom>}
|
leftItem={<TextCustom bold>{item?.label}</TextCustom>}
|
||||||
rightIcon={<TextCustom>{item?.value}</TextCustom>}
|
rightItem={<TextCustom>{item?.value}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import {
|
import {
|
||||||
BoxButtonOnFooter,
|
BoxButtonOnFooter,
|
||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
LoaderCustom,
|
LoaderCustom,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
ViewWrapper,
|
ViewWrapper,
|
||||||
} from "@/components";
|
} from "@/components";
|
||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
import {
|
import {
|
||||||
apiAdminUserAccessGetById,
|
apiAdminUserAccessGetById,
|
||||||
apiAdminUserAccessUpdateStatus,
|
apiAdminUserAccessUpdateStatus,
|
||||||
} from "@/service/api-admin/api-admin-user-access";
|
} from "@/service/api-admin/api-admin-user-access";
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
@@ -28,7 +28,7 @@ export default function AdminUserAccessDetail() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -102,8 +102,8 @@ export default function AdminUserAccessDetail() {
|
|||||||
key={index}
|
key={index}
|
||||||
spanLeft={4}
|
spanLeft={4}
|
||||||
spanRight={8}
|
spanRight={8}
|
||||||
leftIcon={<TextCustom bold>{item?.label}</TextCustom>}
|
leftItem={<TextCustom bold>{item?.label}</TextCustom>}
|
||||||
rightIcon={<TextCustom>{item?.value}</TextCustom>}
|
rightItem={<TextCustom>{item?.value}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
|
|||||||
@@ -1,139 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenUserAccess } from "@/screens/Admin/User-Access/ScreenUserAccess";
|
||||||
import {
|
|
||||||
BadgeCustom,
|
|
||||||
CenterCustom,
|
|
||||||
Divider,
|
|
||||||
SearchInput,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
|
|
||||||
import { MainColor } from "@/constants/color-palet";
|
|
||||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
|
||||||
import { apiAdminUserAccessGetAll } from "@/service/api-admin/api-admin-user-access";
|
|
||||||
import { Ionicons } from "@expo/vector-icons";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
export default function AdminUserAccess() {
|
export default function AdminUserAccess() {
|
||||||
const [listData, setListData] = useState<any[] | null>(null);
|
return <Admin_ScreenUserAccess />;
|
||||||
const [search, setSearch] = useState("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
const response = await apiAdminUserAccessGetAll({
|
|
||||||
search: search,
|
|
||||||
category: "only-user",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setListData(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR LOAD DATA]", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rightComponent = () => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SearchInput
|
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari User"
|
|
||||||
onChangeText={(text) => setSearch(text)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper
|
|
||||||
headerComponent={
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title="User Access"
|
|
||||||
rightComponent={rightComponent()}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<GridViewCustomSpan
|
|
||||||
span1={2}
|
|
||||||
span2={5}
|
|
||||||
span3={5}
|
|
||||||
component1={
|
|
||||||
<TextCustom align="center" bold>
|
|
||||||
Aksi
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component2={<TextCustom bold>Username</TextCustom>}
|
|
||||||
component3={
|
|
||||||
<TextCustom align="center" bold>
|
|
||||||
Status Akses
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<StackCustom>
|
|
||||||
{_.isEmpty(listData) ? (
|
|
||||||
<TextCustom align="center" color="gray" size={"small"}>
|
|
||||||
Tidak ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
listData?.map((item: any, index: number) => (
|
|
||||||
<GridViewCustomSpan
|
|
||||||
key={index}
|
|
||||||
span1={2}
|
|
||||||
span2={5}
|
|
||||||
span3={5}
|
|
||||||
component1={
|
|
||||||
<CenterCustom>
|
|
||||||
<Ionicons
|
|
||||||
onPress={() =>
|
|
||||||
router.push(`/admin/user-access/${item?.id}`)
|
|
||||||
}
|
|
||||||
name="open"
|
|
||||||
size={ICON_SIZE_XLARGE}
|
|
||||||
color={MainColor.yellow}
|
|
||||||
/>
|
|
||||||
</CenterCustom>
|
|
||||||
// <ButtonCustom
|
|
||||||
// onPress={() =>
|
|
||||||
// router.push(`/admin/user-access/${item?.id}`)
|
|
||||||
// }
|
|
||||||
// >
|
|
||||||
// Detail
|
|
||||||
// </ButtonCustom>
|
|
||||||
}
|
|
||||||
component2={
|
|
||||||
<TextCustom bold truncate>
|
|
||||||
{item?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
component3={
|
|
||||||
<CenterCustom>
|
|
||||||
{item?.active ? (
|
|
||||||
<BadgeCustom color="green">Aktif</BadgeCustom>
|
|
||||||
) : (
|
|
||||||
<BadgeCustom color="red">Tidak Aktif</BadgeCustom>
|
|
||||||
)}
|
|
||||||
</CenterCustom>
|
|
||||||
}
|
|
||||||
style3={{ alignItems: "center", justifyContent: "center" }}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
BaseBox,
|
BaseBox,
|
||||||
CircleContainer,
|
CircleContainer,
|
||||||
Grid,
|
Grid,
|
||||||
|
NewWrapper,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
TextCustom,
|
TextCustom,
|
||||||
@@ -13,7 +14,7 @@ import {
|
|||||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||||
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
import AdminButtonReject from "@/components/_ShareComponent/Admin/ButtonReject";
|
||||||
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
import AdminButtonReview from "@/components/_ShareComponent/Admin/ButtonReview";
|
||||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||||
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 { useAuth } from "@/hooks/use-auth";
|
||||||
@@ -40,7 +41,7 @@ export default function AdminVotingDetail() {
|
|||||||
useFocusEffect(
|
useFocusEffect(
|
||||||
useCallback(() => {
|
useCallback(() => {
|
||||||
onLoadData();
|
onLoadData();
|
||||||
}, [id])
|
}, [id]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const onLoadData = async () => {
|
const onLoadData = async () => {
|
||||||
@@ -169,26 +170,28 @@ export default function AdminVotingDetail() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ViewWrapper
|
<NewWrapper
|
||||||
|
hideFooter
|
||||||
headerComponent={<AdminBackButtonAntTitle title={`Detail Data`} />}
|
headerComponent={<AdminBackButtonAntTitle title={`Detail Data`} />}
|
||||||
>
|
>
|
||||||
<BaseBox>
|
<BaseBox>
|
||||||
<StackCustom>
|
<StackCustom>
|
||||||
{listData.map((item, i) => (
|
{listData.map((item, i) => (
|
||||||
<GridSpan_4_8
|
<GridTwoView
|
||||||
key={i}
|
key={i}
|
||||||
label={<TextCustom bold>{item.label}</TextCustom>}
|
spanLeft={5}
|
||||||
value={<TextCustom>{item.value}</TextCustom>}
|
spanRight={7}
|
||||||
|
leftItem={<TextCustom bold>{item.label}</TextCustom>}
|
||||||
|
rightItem={<TextCustom>{item.value}</TextCustom>}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
|
|
||||||
{status === "publish" ||
|
{(status === "publish" || status === "history") && (
|
||||||
(status === "history" && (
|
<BaseBox>
|
||||||
<BaseBox>
|
<TextCustom bold align="center">
|
||||||
<TextCustom bold align="center">
|
Hasil Voting
|
||||||
Hasil Voting
|
|
||||||
</TextCustom>
|
</TextCustom>
|
||||||
<Spacing />
|
<Spacing />
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -209,11 +212,11 @@ export default function AdminVotingDetail() {
|
|||||||
</TextCustom>
|
</TextCustom>
|
||||||
</StackCustom>
|
</StackCustom>
|
||||||
</Grid.Col>
|
</Grid.Col>
|
||||||
)
|
),
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</BaseBox>
|
</BaseBox>
|
||||||
))}
|
)}
|
||||||
|
|
||||||
{data &&
|
{data &&
|
||||||
data?.catatan &&
|
data?.catatan &&
|
||||||
@@ -250,7 +253,7 @@ export default function AdminVotingDetail() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Spacing />
|
<Spacing />
|
||||||
</ViewWrapper>
|
</NewWrapper>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,117 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenVotingStatus } from "@/screens/Admin/Voting/ScreenVotingStatus";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper,
|
|
||||||
} from "@/components";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
|
|
||||||
import { Octicons } from "@expo/vector-icons";
|
|
||||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminVotingStatus() {
|
export default function AdminVotingStatus() {
|
||||||
const { status } = useLocalSearchParams();
|
return <Admin_ScreenVotingStatus />;
|
||||||
const [list, setList] = useState<any | null>(null);
|
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
const [search, setSearch] = useState<string>("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [status, search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiAdminVoting({
|
|
||||||
category: status as "publish" | "review" | "reject" as any,
|
|
||||||
search,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setList(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rightComponent = (
|
|
||||||
<SearchInput
|
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari"
|
|
||||||
value={search}
|
|
||||||
onChangeText={setSearch}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Voting" />}>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title={`${_.startCase(status as string)}`}
|
|
||||||
rightComponent={rightComponent}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<AdminTitleTable
|
|
||||||
title1="Aksi"
|
|
||||||
title2="Username"
|
|
||||||
title3="Judul Voting"
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{loadList ? (
|
|
||||||
<LoaderCustom />
|
|
||||||
) : _.isEmpty(list) ? (
|
|
||||||
<TextCustom align="center" bold color="gray">
|
|
||||||
Belum ada data
|
|
||||||
</TextCustom>
|
|
||||||
) : (
|
|
||||||
list.map((item: any, i: number) => (
|
|
||||||
<AdminTableValue
|
|
||||||
key={i}
|
|
||||||
value1={
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<Octicons
|
|
||||||
name="eye"
|
|
||||||
size={ICON_SIZE_BUTTON}
|
|
||||||
color="black"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/voting/${item.id}/${status}`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
value2={
|
|
||||||
<TextCustom truncate={1}>
|
|
||||||
{item?.Author?.username || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
value3={
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
{item?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,104 +1,5 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
import { Admin_ScreenVotingHistory } from "@/screens/Admin/Voting/ScreenVotingHistory";
|
||||||
import {
|
|
||||||
ActionIcon,
|
|
||||||
LoaderCustom,
|
|
||||||
SearchInput,
|
|
||||||
StackCustom,
|
|
||||||
TextCustom,
|
|
||||||
ViewWrapper
|
|
||||||
} from "@/components";
|
|
||||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
|
||||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
|
||||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
|
||||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
|
||||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
|
||||||
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
|
|
||||||
import { Octicons } from "@expo/vector-icons";
|
|
||||||
import { router, useFocusEffect } from "expo-router";
|
|
||||||
import _ from "lodash";
|
|
||||||
import { useCallback, useState } from "react";
|
|
||||||
import { Divider } from "react-native-paper";
|
|
||||||
|
|
||||||
export default function AdminVotingHistory() {
|
export default function AdminVotingHistory() {
|
||||||
const [list, setList] = useState<any | null>(null);
|
return <Admin_ScreenVotingHistory />;
|
||||||
const [loadList, setLoadList] = useState(false);
|
|
||||||
const [search, setSearch] = useState<string>("");
|
|
||||||
|
|
||||||
useFocusEffect(
|
|
||||||
useCallback(() => {
|
|
||||||
onLoadData();
|
|
||||||
}, [ search])
|
|
||||||
);
|
|
||||||
|
|
||||||
const onLoadData = async () => {
|
|
||||||
try {
|
|
||||||
setLoadList(true);
|
|
||||||
const response = await apiAdminVoting({
|
|
||||||
category: "history",
|
|
||||||
search,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
setList(response.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("[ERROR]", error);
|
|
||||||
} finally {
|
|
||||||
setLoadList(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rightComponent = (
|
|
||||||
<SearchInput
|
|
||||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
|
||||||
placeholder="Cari"
|
|
||||||
value={search}
|
|
||||||
onChangeText={setSearch}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<ViewWrapper headerComponent={<AdminTitlePage title="Voting" />}>
|
|
||||||
<AdminComp_BoxTitle
|
|
||||||
title="Riwayat"
|
|
||||||
rightComponent={rightComponent}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<StackCustom gap={"sm"}>
|
|
||||||
<AdminTitleTable
|
|
||||||
title1="Aksi"
|
|
||||||
title2="Username"
|
|
||||||
title3="Judul Voting"
|
|
||||||
/>
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
{loadList ? <LoaderCustom/> : _.isEmpty(list) ? <TextCustom align="center" bold color="gray">Belum ada data</TextCustom> : list.map((item: any, i: number) => (
|
|
||||||
<AdminTableValue
|
|
||||||
key={i}
|
|
||||||
value1={
|
|
||||||
<ActionIcon
|
|
||||||
icon={
|
|
||||||
<Octicons
|
|
||||||
name="eye"
|
|
||||||
size={ICON_SIZE_BUTTON}
|
|
||||||
color="black"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
onPress={() => {
|
|
||||||
router.push(`/admin/voting/${item.id}/history`);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
value2={<TextCustom truncate={1}>{item?.Author?.username || "-"}</TextCustom>}
|
|
||||||
value3={
|
|
||||||
<TextCustom truncate={2}>
|
|
||||||
{item?.title || "-"}
|
|
||||||
</TextCustom>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</StackCustom>
|
|
||||||
</ViewWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
import { BackButton, StackCustom, TextCustom, ViewWrapper } from "@/components";
|
import { BackButton, StackCustom, TextCustom, ViewWrapper } from "@/components";
|
||||||
import { Stack } from "expo-router";
|
import { router, Stack } from "expo-router";
|
||||||
|
|
||||||
export default function NotFoundScreen() {
|
export default function NotFoundScreen() {
|
||||||
|
// Setelah (dengan penanganan):
|
||||||
|
const handleBack = () => {
|
||||||
|
if (router.canGoBack()) {
|
||||||
|
router.back();
|
||||||
|
} else {
|
||||||
|
// Alternatif action ketika tidak bisa kembali
|
||||||
|
router.replace('/'); // atau navigasi ke halaman default
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Stack.Screen
|
<Stack.Screen
|
||||||
options={{ headerShown: true, title: "", headerLeft: () => <BackButton /> }}
|
options={{ headerShown: true, title: "", headerLeft: () => <BackButton onPress={() => handleBack()} /> }}
|
||||||
/>
|
/>
|
||||||
<ViewWrapper>
|
<ViewWrapper>
|
||||||
<StackCustom
|
<StackCustom
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user