Compare commits
8 Commits
fixed-admi
...
fixed-admi
| Author | SHA1 | Date | |
|---|---|---|---|
| 107d4312e1 | |||
| 4862975402 | |||
| f284e2ec02 | |||
| 1d61ad51e5 | |||
| 76845b71b4 | |||
| 97e1f50660 | |||
| 1cbe4ab330 | |||
| 42fa80c228 |
515
QWEN.md
515
QWEN.md
@@ -2,53 +2,68 @@
|
||||
|
||||
## Project Overview
|
||||
|
||||
HIPMI Mobile is a cross-platform mobile application built with Expo and React Native. The application is named "HIPMI Badung Connect" and serves as a platform for the HIPMI (Himpunan Pengusaha dan Pengusaha Indonesia) Badung chapter. It's designed to run on iOS, Android, and web platforms using a single codebase.
|
||||
HIPMI Mobile is a cross-platform mobile application built with Expo and React Native. The application is named **"HIPMI Badung Connect"** and serves as a platform for the HIPMI (Himpunan Pengusaha dan Pengusaha Indonesia) Badung chapter. It's designed to run on iOS, Android, and web platforms using a single codebase.
|
||||
|
||||
### Key Technologies
|
||||
- **Framework**: Expo (v54.0.0) with React Native (v0.81.4)
|
||||
- **Framework**: Expo (v54.0.0) with React Native (v0.81.5)
|
||||
- **Language**: TypeScript
|
||||
- **Architecture**: File-based routing with Expo Router
|
||||
- **State Management**: Context API
|
||||
- **State Management**: Context API (AuthContext)
|
||||
- **UI Components**: React Native Paper, custom components
|
||||
- **Maps Integration**: Mapbox Maps for React Native
|
||||
- **Push Notifications**: React Native Firebase Messaging
|
||||
- **Build System**: Metro bundler
|
||||
- **Package Manager**: Bun
|
||||
|
||||
### Project Structure
|
||||
```
|
||||
hipmi-mobile/
|
||||
├── app/ # Main application screens and routing
|
||||
├── app/ # Main application screens and routing (Expo Router)
|
||||
│ ├── _layout.tsx # Root layout component
|
||||
│ ├── index.tsx # Entry point (Login screen)
|
||||
│ └── ...
|
||||
│ └── (application)/ # Main app screens
|
||||
│ ├── admin/ # Admin panel screens
|
||||
│ ├── (user)/ # User screens
|
||||
│ └── ...
|
||||
├── components/ # Reusable UI components
|
||||
│ ├── _ShareComponent/ # Shared components (NewWrapper, Admin components)
|
||||
│ ├── _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
|
||||
│ ├── Collaboration/ # Collaboration features
|
||||
│ ├── Event/ # Event management
|
||||
│ ├── Forum/ # Forum functionality
|
||||
│ ├── Home/ # Home screen
|
||||
│ ├── Maps/ # Map integration
|
||||
│ ├── Profile/ # User profile
|
||||
│ ├── RootLayout/ # Root layout components
|
||||
│ └── ...
|
||||
├── assets/ # Images, icons, and static assets
|
||||
├── constants/ # Constants and configuration values
|
||||
├── hooks/ # Custom React hooks
|
||||
├── lib/ # Utility libraries
|
||||
├── navigation/ # Navigation configuration
|
||||
├── service/ # API services and business logic
|
||||
│ ├── api-admin/ # Admin API endpoints
|
||||
│ ├── api-client/ # Client API endpoints
|
||||
│ └── api-config.ts # Axios configuration
|
||||
├── hooks/ # Custom React hooks
|
||||
│ ├── use-pagination.tsx # Pagination hook
|
||||
│ └── ...
|
||||
├── helpers/ # Helper functions
|
||||
│ ├── paginationHelpers.tsx # Pagination UI helpers
|
||||
│ └── ...
|
||||
├── types/ # TypeScript type definitions
|
||||
└── utils/ # Helper functions
|
||||
├── utils/ # Utility functions
|
||||
├── constants/ # Constants and configuration values
|
||||
├── styles/ # Global styles
|
||||
├── assets/ # Images, icons, and static assets
|
||||
└── docs/ # Documentation files
|
||||
```
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Prerequisites
|
||||
- Node.js (with bun as the package manager)
|
||||
- Expo CLI
|
||||
- iOS Simulator or Android Emulator (for native builds)
|
||||
- **Node.js**: v18+ with Bun package manager
|
||||
- **Expo CLI**: Installed globally or via npx
|
||||
- **iOS**: Xcode (macOS only) for iOS simulator/builds
|
||||
- **Android**: Android Studio for Android emulator/builds
|
||||
|
||||
### Setup and Development
|
||||
|
||||
@@ -60,110 +75,416 @@ hipmi-mobile/
|
||||
2. **Run Development Server**
|
||||
```bash
|
||||
bun run start
|
||||
```
|
||||
Or use the shorthand:
|
||||
```bash
|
||||
# or
|
||||
bunx expo start
|
||||
```
|
||||
|
||||
3. **Platform-Specific Commands**
|
||||
- iOS: `bun run ios` or `bunx expo start --ios`
|
||||
- Android: `bun run android` or `bunx expo start --android`
|
||||
- Web: `bun run web` or `bunx expo start --web`
|
||||
```bash
|
||||
# iOS Simulator
|
||||
bun run ios
|
||||
# or
|
||||
bunx expo start --ios
|
||||
|
||||
# Android Emulator
|
||||
bun run android
|
||||
# or
|
||||
bunx expo start --android
|
||||
|
||||
# Web Browser
|
||||
bun run web
|
||||
# or
|
||||
bunx expo start --web
|
||||
```
|
||||
|
||||
4. **Linting**
|
||||
```bash
|
||||
bun run lint
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
The application uses environment variables defined in the app.config.js file:
|
||||
- `API_BASE_URL`: Base URL for API endpoints
|
||||
- `BASE_URL`: Base application URL
|
||||
- `DEEP_LINK_URL`: URL for deep linking functionality
|
||||
### Build Commands
|
||||
|
||||
### EAS Build Configuration
|
||||
The project uses Expo Application Services (EAS) for building and deploying:
|
||||
- Development builds with development client
|
||||
- Preview builds for internal distribution
|
||||
- Production builds for app stores
|
||||
#### EAS Build (Production)
|
||||
```bash
|
||||
# Production build (App Store / Play Store)
|
||||
eas build --profile production
|
||||
|
||||
## Features and Functionality
|
||||
# Preview build (Internal distribution)
|
||||
eas build --profile preview
|
||||
|
||||
The application appears to include several key modules:
|
||||
- **Authentication**: Login, registration, and verification flows
|
||||
- **Admin Panel**: Administrative functions
|
||||
- **Collaboration**: Tools for member collaboration
|
||||
- **Events**: Event management and calendar
|
||||
- **Forum**: Discussion forums
|
||||
- **Maps**: Location-based services with Mapbox integration
|
||||
- **Donations**: Donation functionality
|
||||
- **Job Board**: Employment opportunities
|
||||
- **Investment**: Investment-related features
|
||||
- **Voting**: Voting systems
|
||||
- **Portfolio**: Member portfolio showcase
|
||||
- **Notifications**: Push notifications via Firebase
|
||||
# Development build (Development client)
|
||||
eas build --profile development
|
||||
```
|
||||
|
||||
#### Local Native Builds
|
||||
```bash
|
||||
# Generate native folders (iOS & Android)
|
||||
npx expo prebuild
|
||||
|
||||
# iOS specific
|
||||
bunx expo prebuild --platform ios
|
||||
open ios/HIPMIBadungConnect.xcworkspace
|
||||
|
||||
# Android specific
|
||||
bunx expo prebuild --platform android
|
||||
```
|
||||
|
||||
#### Version Management
|
||||
```bash
|
||||
# Patch version update
|
||||
npm version patch
|
||||
|
||||
# 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
|
||||
|
||||
### Coding Standards
|
||||
- TypeScript is used throughout the project for type safety
|
||||
- Component-based architecture with reusable components
|
||||
- Context API for state management
|
||||
- File-based routing with Expo Router
|
||||
- Consistent naming conventions using camelCase for variables and PascalCase for components
|
||||
- **TypeScript**: Strict mode enabled
|
||||
- **Naming**:
|
||||
- Components: PascalCase (`Admin_ScreenDonationStatus`)
|
||||
- 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.)
|
||||
|
||||
### Component Structure
|
||||
```typescript
|
||||
// 1. Imports (grouped)
|
||||
import { ... } from "@/components";
|
||||
import { ... } from "@/hooks";
|
||||
import { ... } from "@/service";
|
||||
|
||||
// 2. Types/Interfaces
|
||||
interface Props { ... }
|
||||
|
||||
// 3. Main Component
|
||||
export function ComponentName() {
|
||||
// State
|
||||
// Hooks
|
||||
// Functions
|
||||
// Render
|
||||
}
|
||||
```
|
||||
|
||||
### Testing
|
||||
- Linting is configured with ESLint
|
||||
- Standard Expo linting configuration is used
|
||||
- Linting: `bun run lint`
|
||||
- No formal test suite configured yet
|
||||
|
||||
### Security
|
||||
- Firebase is integrated for authentication and messaging
|
||||
- Camera and location permissions are properly configured
|
||||
- Deep linking is secured with app domain associations
|
||||
### Git Workflow
|
||||
- Feature branches: `feature/xxx` or `fixed-admin/xxx`
|
||||
- Commit messages: Clear and descriptive
|
||||
- Use CHANGE_LOG.md for tracking changes
|
||||
|
||||
## Key Dependencies
|
||||
## Key Features
|
||||
|
||||
### Core Dependencies
|
||||
- `@react-navigation/*`: Navigation solution for React Native
|
||||
- `@react-native-firebase/*`: Firebase integration for React Native
|
||||
- `@rnmapbox/maps`: Mapbox integration for React Native
|
||||
- `expo-router`: File-based routing for Expo applications
|
||||
- `react-native-paper`: Material Design components for React Native
|
||||
- `react-native-toast-message`: Toast notifications
|
||||
- `react-native-otp-entry`: OTP input components
|
||||
- `react-native-qrcode-svg`: QR code generation
|
||||
### Authentication
|
||||
- Phone number login with OTP
|
||||
- User registration
|
||||
- Terms & Conditions acceptance
|
||||
- Session persistence with AsyncStorage
|
||||
|
||||
### Development Dependencies
|
||||
- `@types/*`: TypeScript type definitions
|
||||
- `eslint-config-expo`: Expo-specific ESLint configuration
|
||||
- `typescript`: Type checking
|
||||
### 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
|
||||
|
||||
## Platform Support
|
||||
### User Module
|
||||
- **Home**: Main dashboard
|
||||
- **Forum**: Discussion forums
|
||||
- **Profile**: User profile management
|
||||
- **Portfolio**: Member portfolio
|
||||
- **Notifications**: Push notifications via Firebase
|
||||
|
||||
The application is configured to support:
|
||||
- **iOS**: With tablet support and proper permissions
|
||||
- **Android**: With adaptive icons and intent filters for deep linking
|
||||
- **Web**: Static output configuration for web deployment
|
||||
## API Configuration
|
||||
|
||||
## Special Configurations
|
||||
### Base URLs
|
||||
```typescript
|
||||
// From app.config.js extra
|
||||
API_BASE_URL: process.env.API_BASE_URL
|
||||
BASE_URL: process.env.BASE_URL
|
||||
DEEP_LINK_URL: process.env.DEEP_LINK_URL
|
||||
```
|
||||
|
||||
### iOS Configuration
|
||||
- Bundle identifier: `com.anonymous.hipmi-mobile`
|
||||
- Supports tablets
|
||||
- Google Services integration
|
||||
- Location permission handling
|
||||
- Associated domains for deep linking
|
||||
### 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;
|
||||
});
|
||||
```
|
||||
|
||||
### Android Configuration
|
||||
- Package name: `com.bip.hipmimobileapp`
|
||||
- Adaptive icons
|
||||
- Edge-to-edge display enabled
|
||||
- Intent filters for HTTPS deep linking
|
||||
- Google Services integration
|
||||
## Platform Configuration
|
||||
|
||||
### Maps Integration
|
||||
The application uses Mapbox for mapping functionality with the `@rnmapbox/maps` plugin.
|
||||
### 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
|
||||
|
||||
### Push Notifications
|
||||
Firebase Cloud Messaging is integrated for push notifications with proper configuration for both iOS and Android platforms.
|
||||
### 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
|
||||
|
||||
### Mapbox
|
||||
- Map integration via `@rnmapbox/maps`
|
||||
- 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
|
||||
```
|
||||
|
||||
## Documentation Files
|
||||
|
||||
- `docs/CHANGE_LOG.md` - Change log for recent updates
|
||||
- `docs/COMMIT_NOTES.md` - Commit notes and guidelines
|
||||
- `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/)
|
||||
|
||||
@@ -21,7 +21,7 @@ export default {
|
||||
"Aplikasi membutuhkan akses lokasi untuk menampilkan peta.",
|
||||
},
|
||||
associatedDomains: ["applinks:cld-dkr-staging-hipmi.wibudev.com"],
|
||||
buildNumber: "20",
|
||||
buildNumber: "21",
|
||||
},
|
||||
|
||||
android: {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ButtonCustom,
|
||||
DrawerCustom,
|
||||
DummyLandscapeImage,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ButtonCustom,
|
||||
DrawerCustom,
|
||||
DummyLandscapeImage,
|
||||
LoaderCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
@@ -49,7 +49,7 @@ export default function Portofolio() {
|
||||
useCallback(() => {
|
||||
onLoadData(id as string);
|
||||
onLoadUserByToken();
|
||||
}, [id])
|
||||
}, [id]),
|
||||
);
|
||||
|
||||
async function onLoadData(id: string) {
|
||||
@@ -144,27 +144,27 @@ export default function Portofolio() {
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<FontAwesome
|
||||
name="building-o"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{data?.BusinessMaps?.namePin}</TextCustom>}
|
||||
rightItem={<TextCustom>{data?.BusinessMaps?.namePin}</TextCustom>}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="list-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={
|
||||
rightItem={
|
||||
<TextCustom>{data?.MasterBidangBisnis?.name}</TextCustom>
|
||||
}
|
||||
/>
|
||||
@@ -172,26 +172,26 @@ export default function Portofolio() {
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="call-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{data?.tlpn}</TextCustom>}
|
||||
rightItem={<TextCustom>{data?.tlpn}</TextCustom>}
|
||||
/>
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="location-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{data?.alamatKantor}</TextCustom>}
|
||||
rightItem={<TextCustom>{data?.alamatKantor}</TextCustom>}
|
||||
/>
|
||||
|
||||
<Spacing />
|
||||
|
||||
@@ -1,135 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
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";
|
||||
import { Admin_ScreenBusinessFieldDetail } from "@/screens/Admin/App-Information/ScreenBusinessFieldDetail";
|
||||
|
||||
export default function AdminAppInformation_BusinessFieldDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [data, setData] = useState<any | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadDetail();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
const onLoadDetail = async () => {
|
||||
try {
|
||||
const response = await apiAdminMasterBusinessFieldById({
|
||||
id: id as string,
|
||||
category: "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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenBusinessFieldDetail />;
|
||||
}
|
||||
|
||||
@@ -1,86 +1,5 @@
|
||||
import { ScrollableCustom, StackCustom, ViewWrapper } from "@/components";
|
||||
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import AdminAppInformation_BusinessFieldSection from "@/screens/Admin/App-Information/BusinessFieldSection";
|
||||
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";
|
||||
import { Admin_ScreenAppInformation } from "@/screens/Admin/App-Information/ScreenAppInformation";
|
||||
|
||||
export default function AdminInformation() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>("bank");
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenAppInformation />;
|
||||
}
|
||||
|
||||
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 */
|
||||
import {
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
BaseBox,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
@@ -22,7 +22,7 @@ export default function AdminCollaborationPublish() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
handlerLoadData();
|
||||
}, [status])
|
||||
}, [status]),
|
||||
);
|
||||
|
||||
const handlerLoadData = async () => {
|
||||
@@ -78,16 +78,16 @@ export default function AdminCollaborationPublish() {
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
|
||||
{data?.report && (
|
||||
<BaseBox>
|
||||
<GridTwoView
|
||||
spanLeft={4}
|
||||
spanRight={8}
|
||||
leftIcon={<TextCustom bold>Catatan report</TextCustom>}
|
||||
rightIcon={<TextCustom>{data?.report}</TextCustom>}
|
||||
/>
|
||||
</BaseBox>
|
||||
)}
|
||||
{data?.report && (
|
||||
<BaseBox>
|
||||
<GridTwoView
|
||||
spanLeft={4}
|
||||
spanRight={8}
|
||||
leftItem={<TextCustom bold>Catatan report</TextCustom>}
|
||||
rightItem={<TextCustom>{data?.report}</TextCustom>}
|
||||
/>
|
||||
</BaseBox>
|
||||
)}
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -42,19 +42,19 @@ export default function AdminDonationDetailDisbursementOfFunds() {
|
||||
const listData = [
|
||||
{
|
||||
label: "Nominal",
|
||||
value: `Rp ${(data && formatCurrencyDisplay(data?.nominalCair)) || 0}`,
|
||||
value: `Rp ${data ? formatCurrencyDisplay(data?.nominalCair) : 0}`,
|
||||
},
|
||||
{
|
||||
label: "Tanggal",
|
||||
value: dateTimeView({ date: data?.createdAt }),
|
||||
value: data ? dateTimeView({ date: data?.createdAt }) : "-",
|
||||
},
|
||||
{
|
||||
label: "Judul",
|
||||
value: (data && data?.title) || "-",
|
||||
value: data ? data?.title : "-",
|
||||
},
|
||||
{
|
||||
label: "Deskripsi",
|
||||
value: (data && data?.deskripsi) || "-",
|
||||
value: data ? data?.deskripsi : "-",
|
||||
},
|
||||
];
|
||||
return (
|
||||
|
||||
@@ -1,126 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
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";
|
||||
import { Admin_ScreenDonationListDisbursementOfFunds } from "@/screens/Admin/Donation/ScreenDonationListDisbursementOfFunds";
|
||||
|
||||
export default function AdminDonasiListOfDisbursementOfFunds() {
|
||||
const { id } = useLocalSearchParams();
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenDonationListDisbursementOfFunds />;
|
||||
}
|
||||
|
||||
@@ -1,186 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
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";
|
||||
import { Admin_ScreenDonationListOfDonatur } from "@/screens/Admin/Donation/ScreenDonationListOfDonatur";
|
||||
|
||||
export default function AdminDonasiListOfDonatur() {
|
||||
const { id } = useLocalSearchParams();
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenDonationListOfDonatur />;
|
||||
}
|
||||
|
||||
@@ -1,117 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ActionIcon,
|
||||
LoaderCustom,
|
||||
SearchInput,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||
import { 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";
|
||||
import { Admin_ScreenDonationStatus } from "@/screens/Admin/Donation/ScreenDonationStatus";
|
||||
|
||||
export default function AdminDonationStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenDonationStatus />;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ export default function AdminDonationCategoryUpdate() {
|
||||
const response = await apiAdminMasterDonationCategoryById({
|
||||
id: id as any,
|
||||
});
|
||||
console.log(JSON.stringify(response.data, null, 2));
|
||||
|
||||
setData(response.data);
|
||||
};
|
||||
@@ -44,10 +43,9 @@ export default function AdminDonationCategoryUpdate() {
|
||||
id: id as any,
|
||||
data: data,
|
||||
});
|
||||
console.log(JSON.stringify(response.data, null, 2));
|
||||
router.back();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.log("Error update category:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@@ -1,135 +1,5 @@
|
||||
import {
|
||||
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";
|
||||
import { Admin_ScreenDonationCategory } from "@/screens/Admin/Donation/ScreenDonationCategory";
|
||||
|
||||
export default function AdminDonationCategory() {
|
||||
const [listData, setListData] = useState<any[]>([]);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
fetchMaster();
|
||||
}, [])
|
||||
);
|
||||
|
||||
const fetchMaster = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await apiAdminMasterDonationCategory();
|
||||
if (response.success) {
|
||||
console.log(JSON.stringify(response.data, null, 2));
|
||||
setListData(response.data);
|
||||
} else {
|
||||
setListData([]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[Error]", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onRefresh = async () => {
|
||||
setRefreshing(true);
|
||||
await fetchMaster();
|
||||
setRefreshing(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenDonationCategory />;
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ export default function AdminEventDetail() {
|
||||
/>
|
||||
)}
|
||||
|
||||
<TextCustom align="center">{isDevLink}</TextCustom>
|
||||
{/* <TextCustom align="center">{isDevLink}</TextCustom> */}
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
)}
|
||||
|
||||
@@ -1,105 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
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";
|
||||
import { Admin_ScreenEventListOfParticipants } from "@/screens/Admin/Event/ScreenEventListOfParticipants";
|
||||
|
||||
export default function AdminEventListOfParticipants() {
|
||||
const { id } = useLocalSearchParams();
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenEventListOfParticipants />;
|
||||
}
|
||||
|
||||
@@ -1,135 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
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";
|
||||
import { Admin_ScreenEventStatus } from "@/screens/Admin/Event/ScreenEventStatus";
|
||||
|
||||
export default function AdminEventStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
console.log("[STATUS EVENT]", status);
|
||||
|
||||
const [listData, setListData] = useState<any[] | null>(null);
|
||||
const [loadData, setLoadData] = useState(false);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [status, search])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadData(true);
|
||||
const response = await apiAdminEvent({
|
||||
category: status as "publish" | "review" | "reject" | "history" as any,
|
||||
search,
|
||||
});
|
||||
|
||||
console.log(
|
||||
`[RES LIST BY STATUS: ${status}]`,
|
||||
JSON.stringify(response, null, 2)
|
||||
);
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rightComponent = (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari"
|
||||
value={search}
|
||||
onChangeText={(value) => setSearch(value)}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenEventStatus />;
|
||||
}
|
||||
|
||||
@@ -1,129 +1,5 @@
|
||||
import {
|
||||
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";
|
||||
import { Admin_ScreenEventTypeOfEvent } from "@/screens/Admin/Voting/ScreenEventTypeOfEvent";
|
||||
|
||||
export default function AdminEventTypeOfEvent() {
|
||||
const [listData, setListData] = useState<any[] | null>(null);
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenEventTypeOfEvent />;
|
||||
}
|
||||
|
||||
@@ -1,283 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
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";
|
||||
import { Admin_ScreenForumDetailReportPosting } from "@/screens/Admin/Forum/ScreenForumDetailReportPosting";
|
||||
|
||||
export default function AdminForumReportPosting() {
|
||||
const { user } = useAuth();
|
||||
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>
|
||||
</>
|
||||
);
|
||||
export default function AdminForumDetailReportPosting() {
|
||||
return <Admin_ScreenForumDetailReportPosting />;
|
||||
}
|
||||
|
||||
@@ -1,121 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
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";
|
||||
import { Admin_ScreenForumPosting } from "@/screens/Admin/Forum/ScreenForumPosting";
|
||||
|
||||
export default function AdminForumPosting() {
|
||||
const [list, setList] = useState<any | null>(null);
|
||||
const [loadList, setLoadList] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
handlerLoadList();
|
||||
}, [search])
|
||||
);
|
||||
|
||||
const handlerLoadList = async () => {
|
||||
try {
|
||||
setLoadList(true);
|
||||
const response = await apiAdminForum({
|
||||
category: "posting",
|
||||
search: search,
|
||||
});
|
||||
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenForumPosting />;
|
||||
}
|
||||
|
||||
@@ -1,137 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
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";
|
||||
import { Admin_ScreenForumReportComment } from "@/screens/Admin/Forum/ScreenForumReportComment";
|
||||
|
||||
export default function AdminForumReportComment() {
|
||||
const [listData, setListData] = useState<any[] | null>(null);
|
||||
const [loadList, setLoadList] = useState<boolean>(false);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [search])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadList(true);
|
||||
|
||||
const response = await apiAdminForum({
|
||||
category: "report_comment",
|
||||
search: search,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rightComponent = (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari Komentar"
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
/>
|
||||
);
|
||||
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenForumReportComment />;
|
||||
}
|
||||
|
||||
@@ -1,124 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
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";
|
||||
import { Admin_ScreenForumReportPosting } from "@/screens/Admin/Forum/ScreenForumReportPosting";
|
||||
|
||||
export default function AdminForumReportPosting() {
|
||||
const [listData, setListData] = useState<any[] | null>(null);
|
||||
const [loadList, setLoadList] = useState<boolean>(false);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [search])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadList(true);
|
||||
|
||||
const response = await apiAdminForum({
|
||||
category: "report_posting",
|
||||
search: search,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rightComponent = (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari 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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenForumReportPosting />;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButt
|
||||
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 GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||
import ReportBox from "@/components/Box/ReportBox";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
@@ -182,9 +183,9 @@ export default function AdminInvestmentDetail() {
|
||||
|
||||
<BaseBox>
|
||||
<StackCustom>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom bold>File Prospektus</TextCustom>}
|
||||
value={
|
||||
<GridTwoView
|
||||
leftItem={<TextCustom bold>File Prospektus</TextCustom>}
|
||||
rightItem={
|
||||
<ButtonCustom
|
||||
iconLeft={
|
||||
<IconProspectus
|
||||
@@ -202,9 +203,10 @@ export default function AdminInvestmentDetail() {
|
||||
</ButtonCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom bold>File Dokumen</TextCustom>}
|
||||
value={
|
||||
<GridTwoView
|
||||
|
||||
leftItem={<TextCustom bold>File Dokumen</TextCustom>}
|
||||
rightItem={
|
||||
<StackCustom>
|
||||
{_.isEmpty(data?.DokumenInvestasi) ? (
|
||||
<TextCustom align="center">-</TextCustom>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
ButtonCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
AlertDefaultSystem,
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
ButtonCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
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 { useAuth } from "@/hooks/use-auth";
|
||||
import {
|
||||
apiAdminInvestmentGetOneInvoiceById,
|
||||
apiAdminInvestmentUpdateInvoice,
|
||||
apiAdminInvestmentGetOneInvoiceById,
|
||||
apiAdminInvestmentUpdateInvoice,
|
||||
} from "@/service/api-admin/api-admin-investment";
|
||||
import { colorBadgeTransaction } from "@/utils/colorBadge";
|
||||
import { dateTimeView } from "@/utils/dateTimeView";
|
||||
@@ -60,7 +60,7 @@ export default function AdminInvestmentTransactionDetail() {
|
||||
value: (data && data?.MasterBank?.namaBank) || "-",
|
||||
},
|
||||
{
|
||||
label: "Jumlah Investasi",
|
||||
label: "Nominal",
|
||||
value: (data && `Rp. ${formatCurrencyDisplay(data?.nominal)}`) || "-",
|
||||
},
|
||||
{
|
||||
@@ -158,7 +158,7 @@ export default function AdminInvestmentTransactionDetail() {
|
||||
spanRight={6}
|
||||
styleLeft={{ paddingRight: 10 }}
|
||||
styleRight={{ paddingLeft: 10 }}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<ButtonCustom
|
||||
disabled={isLoading}
|
||||
isLoading={isLoading}
|
||||
@@ -181,7 +181,7 @@ export default function AdminInvestmentTransactionDetail() {
|
||||
Tolak
|
||||
</ButtonCustom>
|
||||
}
|
||||
rightIcon={
|
||||
rightItem={
|
||||
<ButtonCustom
|
||||
disabled={isLoading}
|
||||
isLoading={isLoading}
|
||||
|
||||
@@ -1,195 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
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";
|
||||
import { Admin_ScreenInvestmentListOfInvestor } from "@/screens/Admin/Investment/ScreenInvestmentListOfInvestor";
|
||||
|
||||
export default function AdminInvestmentListOfInvestor() {
|
||||
const { id } = useLocalSearchParams();
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenInvestmentListOfInvestor />;
|
||||
}
|
||||
|
||||
@@ -1,113 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ActionIcon,
|
||||
LoaderCustom,
|
||||
SearchInput,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||
import 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";
|
||||
import { Admin_ScreenInvestmentStatus } from "@/screens/Admin/Investment/ScreenInvestmentStatus";
|
||||
|
||||
export default function AdminInvestmentStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenInvestmentStatus />;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
BaseBox,
|
||||
DummyLandscapeImage,
|
||||
Grid,
|
||||
NewWrapper,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
@@ -120,7 +121,7 @@ export default function AdminJobDetailStatus() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
<NewWrapper
|
||||
headerComponent={<AdminBackButtonAntTitle title={`Detail Data`} />}
|
||||
>
|
||||
<BaseBox>
|
||||
@@ -184,7 +185,7 @@ export default function AdminJobDetailStatus() {
|
||||
/>
|
||||
)}
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
</NewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import {
|
||||
AlertDefaultSystem,
|
||||
BoxButtonOnFooter,
|
||||
NewWrapper,
|
||||
TextAreaCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
@@ -100,7 +101,7 @@ export default function AdminJobRejectInput() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
<NewWrapper
|
||||
footerComponent={buttonSubmit}
|
||||
headerComponent={<AdminBackButtonAntTitle title="Penolakan Job" />}
|
||||
>
|
||||
@@ -112,7 +113,7 @@ export default function AdminJobRejectInput() {
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
</ViewWrapper>
|
||||
</NewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,117 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ActionIcon,
|
||||
LoaderCustom,
|
||||
SearchInput,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||
import { 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";
|
||||
import { Admin_ScreenJobStatus } from "@/screens/Admin/Job/ScreenJobStatus";
|
||||
|
||||
export default function AdminJobStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
const [list, setList] = useState<any | null>(null);
|
||||
const [loadList, setLoadList] = useState(false);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
handlerLoadList();
|
||||
}, [status, search])
|
||||
);
|
||||
|
||||
const handlerLoadList = async () => {
|
||||
try {
|
||||
setLoadList(true);
|
||||
const response = await apiAdminJob({
|
||||
category: status as "publish" | "review" | "reject",
|
||||
search,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setList(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rightComponent = (
|
||||
<SearchInput
|
||||
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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenJobStatus />;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
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 API_IMAGE from "@/constants/api-storage";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
@@ -45,7 +54,7 @@ export default function AdminMaps() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
handlerLoadList();
|
||||
}, [])
|
||||
}, []),
|
||||
);
|
||||
|
||||
const handlerLoadList = async () => {
|
||||
@@ -144,52 +153,52 @@ export default function AdminMaps() {
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<FontAwesome
|
||||
name="building-o"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.namePin}</TextCustom>}
|
||||
rightItem={<TextCustom>{selected.namePin}</TextCustom>}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="list-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
||||
rightItem={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="call-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>+{selected.nomorTelepon}</TextCustom>}
|
||||
rightItem={<TextCustom>+{selected.nomorTelepon}</TextCustom>}
|
||||
/>
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="location-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
||||
rightItem={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
||||
/>
|
||||
|
||||
<Grid>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import {
|
||||
apiAdminUserAccessGetById,
|
||||
apiAdminUserAccessUpdateStatus,
|
||||
apiAdminUserAccessGetById,
|
||||
apiAdminUserAccessUpdateStatus,
|
||||
} from "@/service/api-admin/api-admin-user-access";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
@@ -26,7 +26,7 @@ export default function SuperAdminDetail() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
}, [id]),
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
@@ -48,7 +48,7 @@ export default function SuperAdminDetail() {
|
||||
const response = await apiAdminUserAccessUpdateStatus({
|
||||
id: id as string,
|
||||
role: data?.masterUserRoleId === "2" ? "user" : "admin",
|
||||
category: "role"
|
||||
category: "role",
|
||||
});
|
||||
|
||||
if (!response.success) {
|
||||
@@ -102,8 +102,8 @@ export default function SuperAdminDetail() {
|
||||
key={index}
|
||||
spanLeft={4}
|
||||
spanRight={8}
|
||||
leftIcon={<TextCustom bold>{item?.label}</TextCustom>}
|
||||
rightIcon={<TextCustom>{item?.value}</TextCustom>}
|
||||
leftItem={<TextCustom bold>{item?.label}</TextCustom>}
|
||||
rightItem={<TextCustom>{item?.value}</TextCustom>}
|
||||
/>
|
||||
))}
|
||||
</StackCustom>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
BoxButtonOnFooter,
|
||||
ButtonCustom,
|
||||
LoaderCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import {
|
||||
apiAdminUserAccessGetById,
|
||||
apiAdminUserAccessUpdateStatus,
|
||||
apiAdminUserAccessGetById,
|
||||
apiAdminUserAccessUpdateStatus,
|
||||
} from "@/service/api-admin/api-admin-user-access";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
@@ -28,7 +28,7 @@ export default function AdminUserAccessDetail() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
}, [id]),
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
@@ -102,8 +102,8 @@ export default function AdminUserAccessDetail() {
|
||||
key={index}
|
||||
spanLeft={4}
|
||||
spanRight={8}
|
||||
leftIcon={<TextCustom bold>{item?.label}</TextCustom>}
|
||||
rightIcon={<TextCustom>{item?.value}</TextCustom>}
|
||||
leftItem={<TextCustom bold>{item?.label}</TextCustom>}
|
||||
rightItem={<TextCustom>{item?.value}</TextCustom>}
|
||||
/>
|
||||
))}
|
||||
</StackCustom>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
BaseBox,
|
||||
CircleContainer,
|
||||
Grid,
|
||||
NewWrapper,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
@@ -13,7 +14,7 @@ import {
|
||||
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 GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import ReportBox from "@/components/Box/ReportBox";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
@@ -40,7 +41,7 @@ export default function AdminVotingDetail() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id])
|
||||
}, [id]),
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
@@ -169,26 +170,28 @@ export default function AdminVotingDetail() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper
|
||||
<NewWrapper
|
||||
hideFooter
|
||||
headerComponent={<AdminBackButtonAntTitle title={`Detail Data`} />}
|
||||
>
|
||||
<BaseBox>
|
||||
<StackCustom>
|
||||
{listData.map((item, i) => (
|
||||
<GridSpan_4_8
|
||||
<GridTwoView
|
||||
key={i}
|
||||
label={<TextCustom bold>{item.label}</TextCustom>}
|
||||
value={<TextCustom>{item.value}</TextCustom>}
|
||||
spanLeft={5}
|
||||
spanRight={7}
|
||||
leftItem={<TextCustom bold>{item.label}</TextCustom>}
|
||||
rightItem={<TextCustom>{item.value}</TextCustom>}
|
||||
/>
|
||||
))}
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
|
||||
{status === "publish" ||
|
||||
(status === "history" && (
|
||||
<BaseBox>
|
||||
<TextCustom bold align="center">
|
||||
Hasil Voting
|
||||
{(status === "publish" || status === "history") && (
|
||||
<BaseBox>
|
||||
<TextCustom bold align="center">
|
||||
Hasil Voting
|
||||
</TextCustom>
|
||||
<Spacing />
|
||||
<Grid>
|
||||
@@ -209,11 +212,11 @@ export default function AdminVotingDetail() {
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
)
|
||||
),
|
||||
)}
|
||||
</Grid>
|
||||
</BaseBox>
|
||||
))}
|
||||
)}
|
||||
|
||||
{data &&
|
||||
data?.catatan &&
|
||||
@@ -250,7 +253,7 @@ export default function AdminVotingDetail() {
|
||||
/>
|
||||
)}
|
||||
<Spacing />
|
||||
</ViewWrapper>
|
||||
</NewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,117 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ActionIcon,
|
||||
LoaderCustom,
|
||||
SearchInput,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
|
||||
import { Octicons } from "@expo/vector-icons";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
import { Divider } from "react-native-paper";
|
||||
import { Admin_ScreenVotingStatus } from "@/screens/Admin/Voting/ScreenVotingStatus";
|
||||
|
||||
export default function AdminVotingStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
const [list, setList] = useState<any | null>(null);
|
||||
const [loadList, setLoadList] = useState(false);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [status, search])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadList(true);
|
||||
const response = await apiAdminVoting({
|
||||
category: status as "publish" | "review" | "reject" as any,
|
||||
search,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setList(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rightComponent = (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari"
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenVotingStatus />;
|
||||
}
|
||||
|
||||
@@ -1,104 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ActionIcon,
|
||||
LoaderCustom,
|
||||
SearchInput,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import AdminTitleTable from "@/components/_ShareComponent/Admin/TableTitle";
|
||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||
import AdminTitlePage from "@/components/_ShareComponent/Admin/TitlePage";
|
||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
|
||||
import { Octicons } from "@expo/vector-icons";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
import { Divider } from "react-native-paper";
|
||||
import { Admin_ScreenVotingHistory } from "@/screens/Admin/Voting/ScreenVotingHistory";
|
||||
|
||||
export default function AdminVotingHistory() {
|
||||
const [list, setList] = useState<any | null>(null);
|
||||
const [loadList, setLoadList] = useState(false);
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [ search])
|
||||
);
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
setLoadList(true);
|
||||
const response = await apiAdminVoting({
|
||||
category: "history",
|
||||
search,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setList(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
} finally {
|
||||
setLoadList(false);
|
||||
}
|
||||
};
|
||||
|
||||
const rightComponent = (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari"
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<ViewWrapper headerComponent={<AdminTitlePage title="Voting" />}>
|
||||
<AdminComp_BoxTitle
|
||||
title="Riwayat"
|
||||
rightComponent={rightComponent}
|
||||
/>
|
||||
|
||||
<StackCustom gap={"sm"}>
|
||||
<AdminTitleTable
|
||||
title1="Aksi"
|
||||
title2="Username"
|
||||
title3="Judul Voting"
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{loadList ? <LoaderCustom/> : _.isEmpty(list) ? <TextCustom align="center" bold color="gray">Belum ada data</TextCustom> : list.map((item: any, i: number) => (
|
||||
<AdminTableValue
|
||||
key={i}
|
||||
value1={
|
||||
<ActionIcon
|
||||
icon={
|
||||
<Octicons
|
||||
name="eye"
|
||||
size={ICON_SIZE_BUTTON}
|
||||
color="black"
|
||||
/>
|
||||
}
|
||||
onPress={() => {
|
||||
router.push(`/admin/voting/${item.id}/history`);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
value2={<TextCustom truncate={1}>{item?.Author?.username || "-"}</TextCustom>}
|
||||
value3={
|
||||
<TextCustom truncate={2}>
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</StackCustom>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
return <Admin_ScreenVotingHistory />;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ export default function AdminComp_BoxTitle({
|
||||
style={{
|
||||
backgroundColor: AccentColor.darkblue,
|
||||
borderColor: AccentColor.blue,
|
||||
|
||||
padding: 10,
|
||||
paddingBlock: 5,
|
||||
paddingInline: 10,
|
||||
borderWidth: 1,
|
||||
borderRadius: 10,
|
||||
}}
|
||||
|
||||
@@ -4,15 +4,15 @@ import Grid from "../Grid/GridCustom";
|
||||
export default function GridTwoView({
|
||||
spanLeft = 6,
|
||||
spanRight = 6,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
leftItem,
|
||||
rightItem,
|
||||
styleLeft,
|
||||
styleRight,
|
||||
}: {
|
||||
spanLeft?: number;
|
||||
spanRight?: number;
|
||||
leftIcon?: React.ReactNode;
|
||||
rightIcon?: React.ReactNode;
|
||||
leftItem?: React.ReactNode;
|
||||
rightItem?: React.ReactNode;
|
||||
styleLeft?: ViewStyle;
|
||||
styleRight?: ViewStyle;
|
||||
}) {
|
||||
@@ -24,13 +24,13 @@ export default function GridTwoView({
|
||||
span={spanLeft}
|
||||
style={styleLeft ? { ...baseStyle, ...styleLeft } : baseStyle}
|
||||
>
|
||||
{leftIcon}
|
||||
{leftItem}
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={spanRight}
|
||||
style={styleRight ? { ...baseStyle, ...styleRight } : baseStyle}
|
||||
>
|
||||
{rightIcon}
|
||||
{rightItem}
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
);
|
||||
|
||||
28
docs/CHANGE_LOG.md
Normal file
28
docs/CHANGE_LOG.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# CHANGE LOG - fixed-admin/18-feb-26
|
||||
|
||||
## Perubahan Tampilan Admin
|
||||
|
||||
### File Baru (4)
|
||||
- `screens/Admin/Voting/ScreenVotingStatus.tsx`
|
||||
- `screens/Admin/Voting/ScreenVotingHistory.tsx`
|
||||
- `screens/Admin/Voting/ScreenEventTypeOfEvent.tsx`
|
||||
- `screens/Admin/Voting/BoxVotingStatus.tsx`
|
||||
|
||||
### File Diubah (3)
|
||||
- `app/(application)/admin/voting/[status]/status.tsx` → 5 baris
|
||||
- `app/(application)/admin/voting/history.tsx` → 5 baris
|
||||
- `app/(application)/admin/event/type-of-event.tsx` → 5 baris
|
||||
|
||||
### API Updates (2)
|
||||
- `service/api-admin/api-admin-voting.ts` → tambah param `page`
|
||||
- `service/api-admin/api-master-admin.ts` → tambah param `page`
|
||||
|
||||
## Fitur Baru
|
||||
- Pagination (infinite scroll)
|
||||
- Pull-to-Refresh
|
||||
- Skeleton Loading
|
||||
- Empty State
|
||||
- Search Functionality
|
||||
|
||||
## Stats
|
||||
+305 baris, -531 baris (net: -226)
|
||||
@@ -22,8 +22,8 @@ Jika tidak ada props page maka tambahkan props page dan default page: "1"
|
||||
Gunakan bahasa indonesia pada cli agar saya mudah membacanya.
|
||||
|
||||
<!-- Additional Prompt -->
|
||||
File refrensi: screens/Donation/ScreenListOfNews.tsx
|
||||
Anda bisa menggunakan refrensi dari "File refrensi" jika butuh pemahaman dengan tipe fitur yang sama
|
||||
File refrensi: screens/Admin/Event/ScreenEventStatus.tsx
|
||||
Anda bisa menggunakan refrensi dari "File refrensi" jika butuh pemahaman dengan tipe fitur yang hampir sama
|
||||
|
||||
<!-- ===================== End Penerapan Pagination ` ===================== -->
|
||||
|
||||
@@ -53,31 +53,69 @@ Terapkan NewWrapper pada file: app/(application)/(user)/donation/create.tsx
|
||||
Component yang digunakan: components/_ShareComponent/NewWrapper.tsx
|
||||
<!-- End Penerapan NewWrapper -->
|
||||
|
||||
<!-- Start Random Prompt -->
|
||||
|
||||
|
||||
Gunakan bahasa indonesia pada cli agar saya mudah membacanya.eclar
|
||||
<!-- End Random Prompt -->
|
||||
|
||||
<!-- START Prompt Admin Refactoring -->
|
||||
File source: app/(application)/admin/user-access/index.tsx
|
||||
Folder tujuan: screens/Admin/User-Access
|
||||
Nama file utama: ScreenUserAccess.tsx
|
||||
Nama function utama: Admin_ScreenUserAccess
|
||||
<!-- Pindah kode ke Screen Component -->
|
||||
File source: app/(application)/admin/forum/[id]/list-report-posting.tsx
|
||||
Folder tujuan: screens/Admin/Forum
|
||||
Nama file utama: ScreenForumDetailReportPosting.tsx
|
||||
Nama function utama: Admin_ScreenForumDetailReportPosting
|
||||
File komponen wrapper: components/_ShareComponent/NewWrapper.tsx
|
||||
Function fecth: apiAdminUserAccessGetAll
|
||||
File function fetch: service/api-admin/api-admin-user-access.ts
|
||||
|
||||
Buat file baru pada "Folder tujuan" dengan nama "Nama file utama" dan ubah nama function menjadi "Nama function utama" kemudian clean code, import dan panggil function tersebut pada file "File source"
|
||||
|
||||
Analisa juga file "Nama file utama" , jika belum menggunakan NewWrapper pada file "File komponen wrapper" , maka terapkan juga dan ganti wrapper lama yaitu komponen ViewWrapper
|
||||
|
||||
|
||||
<!-- Penerapan Pagination -->
|
||||
Function fecth: apiAdminForumListReportPostingById
|
||||
File function fetch: service/api-admin/api-admin-forum.ts
|
||||
|
||||
Terapkan pagination pada file "Nama file utama"
|
||||
|
||||
Komponen pagination yang digunaka berada pada file hooks/use-pagination.tsx dan helpers/paginationHelpers.tsx
|
||||
|
||||
Perbaiki fetch "Function fecth" , pada file "File function fetch"
|
||||
Jika tidak ada props page maka tambahkan props page dan default page: "1"
|
||||
Jika tidak ada props page maka tambahkan props page dan default page: "1" ( string )
|
||||
Kemudian rapikan code nya pisah komponen seperti render item dan lainnya agar lebih rapi dan di dalam return panggil komponen tersebut
|
||||
|
||||
|
||||
Gunakan bahasa indonesia pada cli agar saya mudah membacanya.
|
||||
<!-- END Prompt Admin Refactoring -->
|
||||
<!-- END Prompt Admin Refactoring -->
|
||||
|
||||
<!-- Additional -->
|
||||
File refrensi: screens/Admin/Forum/ScreenForumReportPosting.tsx
|
||||
Anda bisa menggunakan refrensi dari "File refrensi" jika butuh pemahaman dengan tipe fitur yang hampir sama
|
||||
|
||||
Untuk refrensi tampilan Box bisa anda gunakan dari file: screens/Admin/Donation/BoxDonationListOfDonatur.tsx dan buatkan komponen yang mirip untuk list of donatur dengan nama file: BoxDonationListOfInvestor.tsx
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Use Prompt Now -->
|
||||
Terapkan NewWrapper pada file: screens/Admin/App-Information/InformationBankSection.tsx
|
||||
Component yang digunakan: components/_ShareComponent/NewWrapper.tsx
|
||||
|
||||
Function fecth: apiAdminMasterBank
|
||||
File function fetch: service/api-admin/api-master-admin.ts
|
||||
|
||||
Terapkan pagination pada file "Nama file utama"
|
||||
Komponen pagination yang digunaka berada pada file hooks/use-pagination.tsx dan helpers/paginationHelpers.tsx
|
||||
Perbaiki fetch "Function fecth" , pada file "File function fetch"
|
||||
Jika tidak ada props page maka tambahkan props page dan default page: "1" ( string )
|
||||
|
||||
|
||||
<!-- Create FlatList -->
|
||||
File Utama: screens/Admin/App-Information/InformationBankSection.tsx
|
||||
Terapkan FlatList dan pagination pada file "File Utama"
|
||||
Komponen pagination yang digunaka berada pada file hooks/use-pagination.tsx dan helpers/paginationHelpers.tsx
|
||||
Function fecth: apiAdminMasterBank
|
||||
File function fetch: service/api-admin/api-master-admin.ts
|
||||
Jika tidak ada props page maka tambahkan props page dan default page: "1" ( string )
|
||||
Jika butuh refrensi FlatList bisa lihat pada file components/_ShareComponent/NewWrapper.tsx
|
||||
|
||||
<!-- Create Box -->
|
||||
File Utama: screens/Admin/Investment/ScreenInvestmentStatus.tsx
|
||||
Folder tujuan: screens/Admin/Investment
|
||||
Reffrensi: screens/Admin/Donation/BoxDonationStatus.tsx
|
||||
Buatkan box component baru pada file "File Utama" di bagian renderItem agar lebih rapi buat file baru dengan nama BoxInvestmentStatus.tsx
|
||||
|
||||
<!-- END Create Box -->
|
||||
|
||||
<!-- END Use Prompt Now -->
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>20</string>
|
||||
<string>21</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"expo-dev-client": "~6.0.12",
|
||||
"expo-device": "^8.0.9",
|
||||
"expo-document-picker": "~14.0.7",
|
||||
"expo-file-system": "^19.0.15",
|
||||
"expo-file-system": "^19.0.21",
|
||||
"expo-font": "~14.0.8",
|
||||
"expo-haptics": "~15.0.7",
|
||||
"expo-image": "~3.0.8",
|
||||
@@ -52,7 +52,7 @@
|
||||
"moti": "^0.30.0",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"react-native": "0.81.4",
|
||||
"react-native": "0.81.5",
|
||||
"react-native-dotenv": "^3.4.11",
|
||||
"react-native-gesture-handler": "~2.28.0",
|
||||
"react-native-international-phone-number": "^0.9.3",
|
||||
|
||||
@@ -1,114 +1,56 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
BadgeCustom,
|
||||
CenterCustom,
|
||||
Grid,
|
||||
LoaderCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||
import { apiAdminMasterBusinessField } from "@/service/api-admin/api-master-admin";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
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";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function AdminAppInformation_BusinessFieldSection() {
|
||||
const [listData, setListData] = useState<any[] | null>(null);
|
||||
const [loadData, setLoadData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadList();
|
||||
}, [])
|
||||
);
|
||||
|
||||
const onLoadList = async () => {
|
||||
try {
|
||||
setLoadData(true);
|
||||
const response = await apiAdminMasterBusinessField();
|
||||
|
||||
|
||||
if (response.success) {
|
||||
setListData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR LIST BUSINESS FIELD]", error);
|
||||
setListData([]);
|
||||
} finally {
|
||||
setLoadData(false);
|
||||
}
|
||||
interface Bidang {
|
||||
item: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
active: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
}
|
||||
|
||||
export default function AdminAppInformation_BusinessFieldSection({
|
||||
item,
|
||||
}: {
|
||||
item: any;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<StackCustom>
|
||||
<AdminBasicBox
|
||||
onPress={() =>
|
||||
router.push(`/admin/app-information/business-field/${item.item.id}`)
|
||||
}
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
<TextCustom bold>Aksi</TextCustom>
|
||||
<Grid.Col span={8} style={{ alignSelf: "center" }}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextCustom bold truncate>
|
||||
{item?.item?.name || "-"}
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={4} style={{ alignItems: "center" }}>
|
||||
<TextCustom bold>Status</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<TextCustom bold>Nama Bidang Bisnis</TextCustom>
|
||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
||||
<CenterCustom>
|
||||
{item?.item?.active ? (
|
||||
<BadgeCustom color="green">Aktif</BadgeCustom>
|
||||
) : (
|
||||
<BadgeCustom color="red">Tidak Aktif</BadgeCustom>
|
||||
)}
|
||||
</CenterCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<Divider />
|
||||
|
||||
{loadData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada data</TextCustom>
|
||||
) : (
|
||||
<StackCustom>
|
||||
{listData?.map((item: any, index: number) => (
|
||||
<View key={index}>
|
||||
<Grid>
|
||||
<Grid.Col span={2} style={{ alignItems: "center" }}>
|
||||
<ActionIcon
|
||||
icon={
|
||||
<FontAwesome5
|
||||
name="edit"
|
||||
size={ICON_SIZE_BUTTON}
|
||||
color="black"
|
||||
/>
|
||||
}
|
||||
onPress={() => {
|
||||
router.push(
|
||||
`/admin/app-information/business-field/${item.id}`
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={4}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<CenterCustom>
|
||||
<BadgeCustom
|
||||
color={
|
||||
item.active ? AccentColor.blue : AccentColor.blackgray
|
||||
}
|
||||
>
|
||||
{item.active ? "Aktif" : "Tidak Aktif"}
|
||||
</BadgeCustom>
|
||||
</CenterCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
||||
<TextCustom>{item.name}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</View>
|
||||
))}
|
||||
</StackCustom>
|
||||
)}
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,119 +1,59 @@
|
||||
import {
|
||||
ActionIcon,
|
||||
BadgeCustom,
|
||||
CenterCustom,
|
||||
Grid,
|
||||
LoaderCustom,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
import { AccentColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_BUTTON } from "@/constants/constans-value";
|
||||
import { apiAdminMasterBank } from "@/service/api-admin/api-master-admin";
|
||||
import { FontAwesome5 } from "@expo/vector-icons";
|
||||
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";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import { router } from "expo-router";
|
||||
|
||||
export default function AdminAppInformation_Bank() {
|
||||
const [listData, setListData] = useState<any | null>(null);
|
||||
const [loadData, setLoadData] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
loadMasterBank();
|
||||
}, [])
|
||||
);
|
||||
|
||||
const loadMasterBank = async () => {
|
||||
try {
|
||||
setLoadData(true);
|
||||
const response = await apiAdminMasterBank();
|
||||
|
||||
setListData(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR LIST BANK]", error);
|
||||
setListData([]);
|
||||
} finally {
|
||||
setLoadData(false);
|
||||
}
|
||||
interface BankProps {
|
||||
item: {
|
||||
id: string;
|
||||
namaBank: string;
|
||||
namaAkun: string;
|
||||
norek: string;
|
||||
isActive: boolean;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
}
|
||||
export default function AdminAppInformation_Bank({
|
||||
item,
|
||||
}: {
|
||||
item: BankProps;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<StackCustom>
|
||||
<AdminBasicBox
|
||||
onPress={() =>
|
||||
router.push(`/admin/app-information/information-bank/${item.item.id}`)
|
||||
}
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col span={3}>
|
||||
<TextCustom bold align="center">
|
||||
Aksi
|
||||
</TextCustom>
|
||||
<Grid.Col span={8}>
|
||||
<StackCustom gap={"xs"}>
|
||||
<TextCustom bold truncate>
|
||||
{item?.item?.namaBank || "-"}
|
||||
</TextCustom>
|
||||
<TextCustom size={"small"} bold truncate color="gray">
|
||||
{item?.item?.norek || "-"}
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={3}>
|
||||
<TextCustom bold align="center">
|
||||
Status
|
||||
</TextCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<TextCustom bold align="center">
|
||||
Nama Bank
|
||||
</TextCustom>
|
||||
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
|
||||
<CenterCustom>
|
||||
{item?.item?.isActive ? (
|
||||
<BadgeCustom color="green">Aktif</BadgeCustom>
|
||||
) : (
|
||||
<BadgeCustom color="red">Tidak Aktif</BadgeCustom>
|
||||
)}
|
||||
</CenterCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
|
||||
<Divider />
|
||||
|
||||
{loadData ? (
|
||||
<LoaderCustom />
|
||||
) : _.isEmpty(listData) ? (
|
||||
<TextCustom align="center">Tidak ada data</TextCustom>
|
||||
) : (
|
||||
<StackCustom>
|
||||
{listData?.map((item: any, index: number) => (
|
||||
<View key={index}>
|
||||
<Grid>
|
||||
<Grid.Col span={3} style={{ alignItems: "center" }}>
|
||||
<ActionIcon
|
||||
icon={
|
||||
<FontAwesome5
|
||||
name="edit"
|
||||
size={ICON_SIZE_BUTTON}
|
||||
color="black"
|
||||
/>
|
||||
}
|
||||
onPress={() => {
|
||||
router.push(
|
||||
`/admin/app-information/information-bank/${item.id}`
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={3}
|
||||
style={{ alignItems: "center", justifyContent: "center" }}
|
||||
>
|
||||
<CenterCustom>
|
||||
<BadgeCustom
|
||||
color={
|
||||
item.isActive
|
||||
? AccentColor.blue
|
||||
: AccentColor.blackgray
|
||||
}
|
||||
>
|
||||
{item.isActive ? "Aktif" : "Tidak Aktif"}
|
||||
</BadgeCustom>
|
||||
</CenterCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6} style={{ justifyContent: "center" }}>
|
||||
<TextCustom align="center">{item.namaBank}</TextCustom>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</View>
|
||||
))}
|
||||
</StackCustom>
|
||||
)}
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
153
screens/Admin/App-Information/ScreenAppInformation.tsx
Normal file
153
screens/Admin/App-Information/ScreenAppInformation.tsx
Normal file
@@ -0,0 +1,153 @@
|
||||
import { ScrollableCustom, StackCustom } from "@/components";
|
||||
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import AdminAppInformation_BusinessFieldSection from "@/screens/Admin/App-Information/BusinessFieldSection";
|
||||
import AdminAppInformation_Bank_Component from "@/screens/Admin/App-Information/InformationBankSection";
|
||||
import { apiFetchAdminMasterAppInformation } from "@/service/api-admin/api-master-admin";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import { Alert, RefreshControl } from "react-native";
|
||||
|
||||
export function Admin_ScreenAppInformation() {
|
||||
const [activeCategory, setActiveCategory] = useState<string | null>("bank");
|
||||
const [activePage, setActivePage] = useState<string>("Informasi Bank");
|
||||
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page) => {
|
||||
return await apiFetchAdminMasterAppInformation({
|
||||
category: activeCategory as string,
|
||||
page: String(page),
|
||||
});
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
dependencies: [activeCategory],
|
||||
onError: (error) => console.error("[ERROR] Fetch job by status:", error),
|
||||
});
|
||||
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
emptyMessage: `Tidak ada data ${activeCategory}`,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
const handlePress = (item: any) => {
|
||||
setActiveCategory(item.value);
|
||||
setActivePage(item.label);
|
||||
// tambahkan logika lain seperti filter dsb.
|
||||
};
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
pagination.onRefresh();
|
||||
}, [activeCategory]),
|
||||
);
|
||||
|
||||
const scrollComponent = (
|
||||
<StackCustom>
|
||||
<ScrollableCustom
|
||||
data={listPage.map((e, i) => ({
|
||||
id: i,
|
||||
label: e.label,
|
||||
value: e.value,
|
||||
}))}
|
||||
onButtonPress={handlePress}
|
||||
activeId={activeCategory as any}
|
||||
/>
|
||||
<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");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
);
|
||||
|
||||
// const renderContent = () => {
|
||||
// switch (activeCategory) {
|
||||
// case "bank":
|
||||
// return <AdminAppInformation_Bank_Component />;
|
||||
// case "business":
|
||||
// return <AdminAppInformation_BusinessFieldSection />;
|
||||
// // case "sticker":
|
||||
// // return <AdminAppInformation_StickerSection />;
|
||||
// default:
|
||||
// return <AdminAppInformation_Bank_Component />;
|
||||
// }
|
||||
// };
|
||||
|
||||
const renderItem = (item: any) => {
|
||||
if (activeCategory === "bank") {
|
||||
return <AdminAppInformation_Bank_Component key={item.id} item={item} />;
|
||||
} else if (activeCategory === "business") {
|
||||
return (
|
||||
<AdminAppInformation_BusinessFieldSection key={item.id} item={item} />
|
||||
);
|
||||
} else {
|
||||
return <AdminAppInformation_Bank_Component key={item.id} item={item} />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
headerComponent={scrollComponent}
|
||||
// ListHeaderComponent={
|
||||
|
||||
// }
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
tintColor={MainColor.yellow}
|
||||
colors={[MainColor.yellow]}
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
/>
|
||||
}
|
||||
onEndReached={pagination.loadMore}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
hideFooter
|
||||
// Data dan render
|
||||
listData={pagination.listData}
|
||||
renderItem={(item: any) => renderItem(item)}
|
||||
/>
|
||||
// {renderContent()}
|
||||
// </NewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
const listPage = [
|
||||
{
|
||||
id: "1",
|
||||
label: "Informasi Bank",
|
||||
value: "bank",
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
label: "Bidang & Sub Bidang",
|
||||
value: "business",
|
||||
},
|
||||
// {
|
||||
// id: "3",
|
||||
// label: "Stiker",
|
||||
// value: "sticker",
|
||||
// },
|
||||
];
|
||||
185
screens/Admin/App-Information/ScreenBusinessFieldDetail.tsx
Normal file
185
screens/Admin/App-Information/ScreenBusinessFieldDetail.tsx
Normal file
@@ -0,0 +1,185 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
CenterCustom,
|
||||
NewWrapper,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import { GridSpan_NewComponent } from "@/components/_ShareComponent/GridSpan_NewComponent";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import {
|
||||
ICON_SIZE_SMALL,
|
||||
PAGINATION_DEFAULT_TAKE,
|
||||
} from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminMasterBusinessFieldById } from "@/service/api-admin/api-master-admin";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
|
||||
export function Admin_ScreenBusinessFieldDetail() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [bidang, setBidang] = useState<any | null>(null);
|
||||
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page) => {
|
||||
return await apiAdminMasterBusinessFieldById({
|
||||
category: "only-sub-bidang",
|
||||
id: id as any,
|
||||
page: String(page),
|
||||
});
|
||||
// Pastikan mengembalikan struktur data yang sesuai dengan yang diharapkan oleh usePagination
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
dependencies: [id],
|
||||
onError: (error) => {
|
||||
console.log("Error fetching data sub bidang", error);
|
||||
},
|
||||
});
|
||||
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: "",
|
||||
emptyMessage: "Tidak ada data pengguna",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
});
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadBidang();
|
||||
pagination.onRefresh();
|
||||
}, [id]),
|
||||
);
|
||||
|
||||
const onLoadBidang = async () => {
|
||||
try {
|
||||
const response = await apiAdminMasterBusinessFieldById({
|
||||
id: id as string,
|
||||
category: "all",
|
||||
});
|
||||
setBidang(response.data);
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
setBidang(null);
|
||||
}
|
||||
};
|
||||
const renderHeader = () => (
|
||||
<View>
|
||||
<BaseBox
|
||||
onPress={() =>
|
||||
router.push(
|
||||
`/admin/app-information/business-field/${id}/bidang-update`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<StackCustom gap={"xs"}>
|
||||
<GridSpan_NewComponent
|
||||
span1={10}
|
||||
span2={2}
|
||||
text1={
|
||||
<StackCustom>
|
||||
<TextCustom bold size={"large"}>
|
||||
{bidang?.bidang?.name}
|
||||
</TextCustom>
|
||||
{bidang?.bidang.active ? (
|
||||
<BadgeCustom color="green">Aktif</BadgeCustom>
|
||||
) : (
|
||||
<BadgeCustom color="red">Tidak Aktif</BadgeCustom>
|
||||
)}
|
||||
</StackCustom>
|
||||
}
|
||||
text2={
|
||||
<CenterCustom>
|
||||
<Ionicons
|
||||
name="caret-forward"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
</CenterCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
|
||||
<CenterCustom>
|
||||
<TextCustom bold>Sub Bidang</TextCustom>
|
||||
</CenterCustom>
|
||||
<Spacing height={5} />
|
||||
</View>
|
||||
);
|
||||
|
||||
const renderItem = ({ item }: { item: any }) => (
|
||||
<BaseBox
|
||||
onPress={() =>
|
||||
router.push(
|
||||
`/admin/app-information/business-field/${item?.id}/sub-bidang-update`,
|
||||
)
|
||||
}
|
||||
>
|
||||
<StackCustom gap={"xs"}>
|
||||
<GridSpan_NewComponent
|
||||
span1={10}
|
||||
span2={2}
|
||||
text1={
|
||||
<StackCustom>
|
||||
<TextCustom bold size={"large"}>
|
||||
{item.name}
|
||||
</TextCustom>
|
||||
{item?.isActive ? (
|
||||
<BadgeCustom color="green">Aktif</BadgeCustom>
|
||||
) : (
|
||||
<BadgeCustom color="red">Tidak Aktif</BadgeCustom>
|
||||
)}
|
||||
</StackCustom>
|
||||
}
|
||||
text2={
|
||||
<CenterCustom>
|
||||
<Ionicons
|
||||
name="caret-forward"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
</CenterCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</BaseBox>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
onEndReached={pagination.loadMore}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
hideFooter
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor={MainColor.yellow}
|
||||
colors={[MainColor.yellow]}
|
||||
/>
|
||||
}
|
||||
headerComponent={
|
||||
<AdminBackButtonAntTitle title="Detail Bidang & Sub Bidang" />
|
||||
}
|
||||
ListHeaderComponent={renderHeader()}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
36
screens/Admin/Donation/BoxDonationCategory.tsx
Normal file
36
screens/Admin/Donation/BoxDonationCategory.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { TextCustom, BadgeCustom } from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
export default function Admin_BoxDonationCategory({item}: {item: any}) {
|
||||
return (
|
||||
<>
|
||||
<AdminBasicBox
|
||||
onPress={() => {
|
||||
router.push(`/admin/donation/category-update?id=${item.id}`);
|
||||
}}
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
>
|
||||
<GridTwoView
|
||||
leftItem={<TextCustom bold>{item?.name || "-"}</TextCustom>}
|
||||
rightItem={
|
||||
<View>
|
||||
{item?.active ? (
|
||||
<BadgeCustom color="green">Aktif</BadgeCustom>
|
||||
) : (
|
||||
<BadgeCustom color="red">Tidak Aktif</BadgeCustom>
|
||||
)}
|
||||
</View>
|
||||
}
|
||||
spanLeft={8}
|
||||
spanRight={4}
|
||||
styleRight={{
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
/>
|
||||
</AdminBasicBox>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Divider, StackCustom, TextCustom } from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||
import dayjs from "dayjs";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
interface BoxDonationListDisbursementOfFundsProps {
|
||||
item: any;
|
||||
}
|
||||
|
||||
export default function Admin_BoxDonationListDisbursementOfFunds({
|
||||
item,
|
||||
}: BoxDonationListDisbursementOfFundsProps) {
|
||||
return (
|
||||
<>
|
||||
<AdminBasicBox
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(
|
||||
`/admin/donation/${item?.id}/detail-disbursement-of-funds`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<StackCustom gap={0}>
|
||||
<View style={{ paddingBlock: 8 }}>
|
||||
<TextCustom size={"large"} bold truncate>
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
</View>
|
||||
<Divider />
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Tanggal</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{dayjs(item?.createdAt).format("DD-MM-YYYY") || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Nominal</TextCustom>}
|
||||
value={
|
||||
<TextCustom bold>
|
||||
Rp {formatCurrencyDisplay(item?.nominalCair)}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
68
screens/Admin/Donation/BoxDonationListOfDonatur.tsx
Normal file
68
screens/Admin/Donation/BoxDonationListOfDonatur.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import {
|
||||
BadgeCustom,
|
||||
Divider,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||
import { colorBadgeTransaction } from "@/utils/colorBadge";
|
||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||
import { router } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { View } from "react-native";
|
||||
|
||||
interface BoxDonationListOfDonaturProps {
|
||||
item: any;
|
||||
}
|
||||
|
||||
export default function Admin_BoxDonationListOfDonatur({
|
||||
item,
|
||||
}: BoxDonationListOfDonaturProps) {
|
||||
const statusName = item?.DonasiMaster_StatusInvoice?.name || "-";
|
||||
return (
|
||||
<>
|
||||
<AdminBasicBox
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(
|
||||
`/admin/donation/${item?.id}/${_.lowerCase(
|
||||
item?.DonasiMaster_StatusInvoice?.name,
|
||||
)}/transaction-detail`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<StackCustom gap={0}>
|
||||
<View style={{ paddingBlock: 8 }}>
|
||||
<TextCustom size={"large"} bold truncate>
|
||||
{item?.Author?.username || "-"}
|
||||
</TextCustom>
|
||||
</View>
|
||||
<Divider />
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Status</TextCustom>}
|
||||
value={
|
||||
<BadgeCustom
|
||||
color={colorBadgeTransaction({
|
||||
status: statusName,
|
||||
})}
|
||||
>
|
||||
{statusName}
|
||||
</BadgeCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Nominal</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{item?.nominal
|
||||
? `Rp ${formatCurrencyDisplay(item?.nominal)}`
|
||||
: "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
54
screens/Admin/Donation/BoxDonationStatus.tsx
Normal file
54
screens/Admin/Donation/BoxDonationStatus.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Divider, StackCustom, TextCustom } from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
interface BoxDonationStatusProps {
|
||||
item: any;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export default function Admin_BoxDonationStatus({
|
||||
item,
|
||||
status,
|
||||
}: BoxDonationStatusProps) {
|
||||
return (
|
||||
<>
|
||||
<AdminBasicBox
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(`/admin/donation/${item.id}/${status}`);
|
||||
}}
|
||||
>
|
||||
<StackCustom gap={0}>
|
||||
<View style={{ paddingBlock: 8 }}>
|
||||
<TextCustom size={"large"} bold truncate={2}>
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
</View>
|
||||
<Divider />
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Durasi</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{item?.DonasiMaster_Durasi?.name || "-"} hari
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Target Dana</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{item?.target
|
||||
? `Rp ${formatCurrencyDisplay(item?.target)}`
|
||||
: "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
107
screens/Admin/Donation/ScreenDonationCategory.tsx
Normal file
107
screens/Admin/Donation/ScreenDonationCategory.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
import { BadgeCustom, TextCustom } from "@/components";
|
||||
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminMasterDonationCategory } from "@/service/api-admin/api-master-admin";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
import Admin_BoxDonationCategory from "./BoxDonationCategory";
|
||||
|
||||
export function Admin_ScreenDonationCategory() {
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminMasterDonationCategory({
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [],
|
||||
});
|
||||
|
||||
// Komponen action plus untuk header
|
||||
const rightComponent = useMemo(
|
||||
() => (
|
||||
<AdminActionIconPlus
|
||||
onPress={() => {
|
||||
router.push(`/admin/donation/category-create`);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Header component untuk title
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle
|
||||
title="Kategori Donasi"
|
||||
rightComponent={rightComponent}
|
||||
/>
|
||||
),
|
||||
[rightComponent],
|
||||
);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
pagination.onRefresh();
|
||||
}, []),
|
||||
);
|
||||
|
||||
// Render item untuk daftar kategori donasi
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<Admin_BoxDonationCategory key={index} item={item} />
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Belum ada data",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 80,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor="#E1B525"
|
||||
colors={["#E1B525"]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminDonationDisbursementOfFundsListById } from "@/service/api-admin/api-admin-donation";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
import Admin_BoxDonationListDisbursementOfFunds from "./BoxDonationListDisbursementOfFunds";
|
||||
|
||||
export function Admin_ScreenDonationListDisbursementOfFunds() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminDonationDisbursementOfFundsListById({
|
||||
id: id as string,
|
||||
category: "get-all",
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: "",
|
||||
dependencies: [id],
|
||||
});
|
||||
|
||||
// Header component dengan back button dan title
|
||||
const headerComponent = useMemo(
|
||||
() => <AdminBackButtonAntTitle title="Daftar Pencairan Dana" />,
|
||||
[],
|
||||
);
|
||||
|
||||
// Render item untuk daftar pencairan dana
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<Admin_BoxDonationListDisbursementOfFunds key={index} item={item} />
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: "",
|
||||
emptyMessage: "Belum ada data",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor="#E1B525"
|
||||
colors={["#E1B525"]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
134
screens/Admin/Donation/ScreenDonationListOfDonatur.tsx
Normal file
134
screens/Admin/Donation/ScreenDonationListOfDonatur.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import { SelectCustom } from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminDonationListOfDonatur } from "@/service/api-admin/api-admin-donation";
|
||||
import { apiMasterTransaction } from "@/service/api-client/api-master";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
import Admin_BoxDonationListOfDonatur from "./BoxDonationListOfDonatur";
|
||||
|
||||
export function Admin_ScreenDonationListOfDonatur() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [selectValue, setSelectValue] = useState<string | null>(null);
|
||||
const [selectedStatus, setSelectedStatus] = useState<string | null>(null);
|
||||
const [master, setMaster] = useState<any[]>([]);
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminDonationListOfDonatur({
|
||||
id: id as string,
|
||||
status: selectedStatus as any,
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: "",
|
||||
dependencies: [id, selectedStatus],
|
||||
});
|
||||
|
||||
// Load master data untuk select option
|
||||
useEffect(() => {
|
||||
onLoadMaster();
|
||||
}, []);
|
||||
|
||||
const onLoadMaster = async () => {
|
||||
try {
|
||||
const response = await apiMasterTransaction();
|
||||
if (response.success) {
|
||||
setMaster(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
setMaster([]);
|
||||
}
|
||||
};
|
||||
|
||||
// Komponen select untuk filter status
|
||||
const searchComponent = useMemo(
|
||||
() => (
|
||||
<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>
|
||||
),
|
||||
[master, selectValue],
|
||||
);
|
||||
|
||||
// Header component dengan back button dan select filter
|
||||
const headerComponent = useMemo(
|
||||
() => <AdminBackButtonAntTitle newComponent={searchComponent} />,
|
||||
[searchComponent],
|
||||
);
|
||||
|
||||
// Render item untuk daftar donatur
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<Admin_BoxDonationListOfDonatur key={index} item={item} />
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: "",
|
||||
emptyMessage: "Belum ada data",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor="#E1B525"
|
||||
colors={["#E1B525"]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
112
screens/Admin/Donation/ScreenDonationStatus.tsx
Normal file
112
screens/Admin/Donation/ScreenDonationStatus.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { ActionIcon, SearchInput, TextCustom } from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import AdminTableValue from "@/components/_ShareComponent/Admin/TableValue";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import {
|
||||
ICON_SIZE_BUTTON,
|
||||
PAGINATION_DEFAULT_TAKE,
|
||||
} from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminDonation } from "@/service/api-admin/api-admin-donation";
|
||||
import { Octicons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
import Admin_BoxDonationStatus from "./BoxDonationStatus";
|
||||
|
||||
export function Admin_ScreenDonationStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminDonation({
|
||||
category: status as "publish" | "review" | "reject",
|
||||
search: searchQuery,
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [status],
|
||||
});
|
||||
|
||||
// Komponen search input untuk header
|
||||
const rightComponent = useMemo(
|
||||
() => (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari judul donasi"
|
||||
value={search}
|
||||
onChangeText={(value) => setSearch(value)}
|
||||
/>
|
||||
),
|
||||
[search],
|
||||
);
|
||||
|
||||
// Render item untuk daftar donasi
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<Admin_BoxDonationStatus
|
||||
key={index}
|
||||
item={item}
|
||||
status={status as string}
|
||||
/>
|
||||
),
|
||||
[status],
|
||||
);
|
||||
|
||||
// Header component dengan judul status donasi
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle
|
||||
title={`Donasi ${_.startCase(status as string)}`}
|
||||
rightComponent={rightComponent}
|
||||
/>
|
||||
),
|
||||
[status, rightComponent],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Belum ada data",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 120,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor="#E1B525"
|
||||
colors={["#E1B525"]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
55
screens/Admin/Event/BoxEventParticipant.tsx
Normal file
55
screens/Admin/Event/BoxEventParticipant.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import {
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
Grid,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
import dayjs from "dayjs";
|
||||
import { View } from "moti";
|
||||
|
||||
interface Admin_BoxEventParticipantProps {
|
||||
item: any;
|
||||
startDate?: dayjs.Dayjs;
|
||||
}
|
||||
|
||||
export function Admin_BoxEventParticipant({
|
||||
item,
|
||||
startDate,
|
||||
}: Admin_BoxEventParticipantProps) {
|
||||
|
||||
return (
|
||||
<BaseBox>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
48
screens/Admin/Event/BoxEventStatus.tsx
Normal file
48
screens/Admin/Event/BoxEventStatus.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { StackCustom, TextCustom } from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||
import { dateTimeView } from "@/utils/dateTimeView";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
import { Divider } from "react-native-paper";
|
||||
|
||||
interface Admin_BoxEventStatusProps {
|
||||
item: any;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export function Admin_BoxEventStatus({ item, status }: Admin_BoxEventStatusProps) {
|
||||
return (
|
||||
<AdminBasicBox
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(`/admin/event/${item.id}/${status}`);
|
||||
}}
|
||||
>
|
||||
<StackCustom gap={0}>
|
||||
<View style={{ paddingBlock: 8 }}>
|
||||
<TextCustom size={"large"} bold truncate={2}>
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
</View>
|
||||
<Divider />
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Mulai</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{dateTimeView({ date: item?.tanggal }) || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Berakhir</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{dateTimeView({ date: item?.tanggalSelesai }) || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
);
|
||||
}
|
||||
83
screens/Admin/Event/ScreenEventListOfParticipants.tsx
Normal file
83
screens/Admin/Event/ScreenEventListOfParticipants.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminEventListOfParticipants } from "@/service/api-admin/api-admin-event";
|
||||
import dayjs from "dayjs";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useCallback } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
import { Admin_BoxEventParticipant } from "./BoxEventParticipant";
|
||||
|
||||
export function Admin_ScreenEventListOfParticipants() {
|
||||
const { id } = useLocalSearchParams();
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminEventListOfParticipants({
|
||||
id: id as string,
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
dependencies: [id],
|
||||
onError: (error) => {
|
||||
console.error("Error loading participants:", error);
|
||||
},
|
||||
});
|
||||
|
||||
// Render item untuk daftar peserta
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<Admin_BoxEventParticipant
|
||||
key={index}
|
||||
item={item}
|
||||
startDate={dayjs(item?.Event?.tanggal)}
|
||||
/>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: "",
|
||||
emptyMessage: "Belum ada peserta",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 60,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id.toString()}
|
||||
headerComponent={<AdminBackButtonAntTitle title="Daftar Peserta" />}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor={MainColor.yellow}
|
||||
colors={[MainColor.yellow]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
108
screens/Admin/Event/ScreenEventStatus.tsx
Normal file
108
screens/Admin/Event/ScreenEventStatus.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import { SearchInput } from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminEvent } from "@/service/api-admin/api-admin-event";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
import { Admin_BoxEventStatus } from "./BoxEventStatus";
|
||||
|
||||
export function Admin_ScreenEventStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminEvent({
|
||||
category: status as
|
||||
| "publish"
|
||||
| "review"
|
||||
| "history"
|
||||
| "dashboard"
|
||||
| "type-of-event",
|
||||
search: searchQuery,
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [status],
|
||||
});
|
||||
|
||||
// Komponen kanan untuk header
|
||||
const rightComponent = useMemo(
|
||||
() => (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari"
|
||||
value={search}
|
||||
onChangeText={(value) => setSearch(value)}
|
||||
/>
|
||||
),
|
||||
[search],
|
||||
);
|
||||
|
||||
// Render item untuk daftar event
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<Admin_BoxEventStatus key={index} item={item} status={status as string} />
|
||||
),
|
||||
[status],
|
||||
);
|
||||
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle
|
||||
title={`Event ${_.startCase(status as string)}`}
|
||||
rightComponent={rightComponent}
|
||||
/>
|
||||
),
|
||||
[status, rightComponent],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Belum ada data",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id.toString()}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor={MainColor.yellow}
|
||||
colors={[MainColor.yellow]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
339
screens/Admin/Forum/ScreenForumDetailReportPosting.tsx
Normal file
339
screens/Admin/Forum/ScreenForumDetailReportPosting.tsx
Normal file
@@ -0,0 +1,339 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import {
|
||||
ActionIcon,
|
||||
AlertDefaultSystem,
|
||||
BadgeCustom,
|
||||
BaseBox,
|
||||
CenterCustom,
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} 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 NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import {
|
||||
ICON_SIZE_BUTTON,
|
||||
PAGINATION_DEFAULT_TAKE,
|
||||
} from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
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, useMemo, useState } from "react";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
import { Divider } from "react-native-paper";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export function Admin_ScreenForumDetailReportPosting() {
|
||||
const { user } = useAuth();
|
||||
const { id } = useLocalSearchParams();
|
||||
const [openDrawerPage, setOpenDrawerPage] = useState(false);
|
||||
const [openDrawerAction, setOpenDrawerAction] = useState(false);
|
||||
const [data, setData] = useState<any | null>(null);
|
||||
const [selectedReport, setSelectedReport] = useState({
|
||||
id: "",
|
||||
username: "",
|
||||
kategori: "",
|
||||
keterangan: "",
|
||||
deskripsi: "",
|
||||
});
|
||||
|
||||
// Load data postingan saat screen fokus
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadDataPosting();
|
||||
}, [id])
|
||||
);
|
||||
|
||||
// Pagination untuk list report
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminForumListReportPostingById({
|
||||
id: id as string,
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
}
|
||||
return { data: [] };
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
dependencies: [id],
|
||||
});
|
||||
|
||||
const onLoadDataPosting = async () => {
|
||||
try {
|
||||
const response = await apiAdminForumPostingById({
|
||||
id: id as string,
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setData(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Render item untuk daftar report
|
||||
const renderItem = useCallback(
|
||||
({ item }: { item: any }) => (
|
||||
<View>
|
||||
<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>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
// Header component dengan detail postingan
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminBackButtonAntTitle
|
||||
title="Detail Report Posting"
|
||||
rightComponent={
|
||||
<ActionIcon
|
||||
icon={<IconDot size={16} color={MainColor.darkblue} />}
|
||||
onPress={() => setOpenDrawerPage(true)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
// Detail postingan component
|
||||
const postingDetailComponent = useMemo(
|
||||
() => (
|
||||
<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>
|
||||
),
|
||||
[data]
|
||||
);
|
||||
|
||||
// Box title untuk daftar report
|
||||
const reportListTitleComponent = useMemo(
|
||||
() => <AdminComp_BoxTitle title="Daftar Report Posting" />,
|
||||
[]
|
||||
);
|
||||
|
||||
// Header untuk kolom daftar report
|
||||
const reportListHeaderComponent = useMemo(
|
||||
() => (
|
||||
<StackCustom gap={"sm"}>
|
||||
{postingDetailComponent}
|
||||
{reportListTitleComponent}
|
||||
<GridSpan_NewComponent
|
||||
text1={
|
||||
<TextCustom bold align="center">
|
||||
Aksi
|
||||
</TextCustom>
|
||||
}
|
||||
text2={<TextCustom bold>Pelapor</TextCustom>}
|
||||
text3={<TextCustom bold>Kategori Report</TextCustom>}
|
||||
/>
|
||||
<Divider />
|
||||
</StackCustom>
|
||||
),
|
||||
[postingDetailComponent, reportListTitleComponent]
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
emptyMessage: "Belum ada report",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
headerComponent={headerComponent}
|
||||
ListHeaderComponent={reportListHeaderComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor={MainColor.yellow}
|
||||
colors={[MainColor.yellow]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Drawer untuk menu halaman (hapus posting) */}
|
||||
<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>
|
||||
|
||||
{/* Drawer untuk detail report */}
|
||||
<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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
133
screens/Admin/Forum/ScreenForumPosting.tsx
Normal file
133
screens/Admin/Forum/ScreenForumPosting.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import {
|
||||
SearchInput, StackCustom,
|
||||
TextCustom
|
||||
} from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
|
||||
import { router } from "expo-router";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
import { Divider } from "react-native-paper";
|
||||
|
||||
export function Admin_ScreenForumPosting() {
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminForum({
|
||||
category: "posting",
|
||||
search: searchQuery || "",
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [],
|
||||
});
|
||||
|
||||
// Komponen search input
|
||||
const searchComponent = useMemo(
|
||||
() => (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari postingan"
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
/>
|
||||
),
|
||||
[search],
|
||||
);
|
||||
|
||||
// Header component
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle
|
||||
title={"Forum Posting"}
|
||||
rightComponent={searchComponent}
|
||||
/>
|
||||
),
|
||||
[searchComponent],
|
||||
);
|
||||
|
||||
// Render item untuk daftar posting
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<AdminBasicBox
|
||||
style={{ marginHorizontal: 5, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(`/admin/forum/${item.id}`);
|
||||
}}
|
||||
>
|
||||
<StackCustom gap={0}>
|
||||
<View style={{ paddingBlock: 8 }}>
|
||||
<TextCustom size={"large"} bold truncate={2}>
|
||||
{item?.diskusi || "-"}
|
||||
</TextCustom>
|
||||
</View>
|
||||
<Divider />
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Komentar</TextCustom>}
|
||||
value={
|
||||
<TextCustom truncate={1}>{item?.komentar || "-"}</TextCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Report</TextCustom>}
|
||||
value={
|
||||
<TextCustom truncate={1}>{item?.reportPosting || "-"}</TextCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Belum ada data posting",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor={MainColor.yellow}
|
||||
colors={[MainColor.yellow]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
156
screens/Admin/Forum/ScreenForumReportComment.tsx
Normal file
156
screens/Admin/Forum/ScreenForumReportComment.tsx
Normal file
@@ -0,0 +1,156 @@
|
||||
import { SearchInput, StackCustom, TextCustom } from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
|
||||
import { router } from "expo-router";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
import { Divider } from "react-native-paper";
|
||||
|
||||
export function Admin_ScreenForumReportComment() {
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminForum({
|
||||
category: "report_comment",
|
||||
search: searchQuery || "",
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
console.log("CEK", JSON.stringify(response.data, null, 2));
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [],
|
||||
});
|
||||
|
||||
// Komponen search input
|
||||
const searchComponent = useMemo(
|
||||
() => (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari Komentar"
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
/>
|
||||
),
|
||||
[search],
|
||||
);
|
||||
|
||||
// Header component dengan box title
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle
|
||||
title="Report Komentar"
|
||||
rightComponent={searchComponent}
|
||||
/>
|
||||
),
|
||||
[searchComponent],
|
||||
);
|
||||
|
||||
// Render item untuk daftar report comment
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<AdminBasicBox
|
||||
key={index}
|
||||
style={{ marginHorizontal: 5, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(
|
||||
`/admin/forum/${item?.Forum_Komentar?.id}/list-report-comment`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<StackCustom gap={0}>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Pelapor</TextCustom>}
|
||||
value={
|
||||
<TextCustom truncate={1}>
|
||||
{item?.User?.username || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Komentar</TextCustom>}
|
||||
value={
|
||||
<TextCustom truncate={2}>
|
||||
{item?.Forum_Komentar?.komentar || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
{item?.deskripsi ?
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Deskripsi</TextCustom>}
|
||||
value={
|
||||
<TextCustom truncate={2}>
|
||||
{item?.deskripsi|| "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/> : <GridSpan_4_8
|
||||
label={<TextCustom>Jenis Laporan</TextCustom>}
|
||||
value={
|
||||
<TextCustom truncate={2}>
|
||||
{item?.ForumMaster_KategoriReport?.title || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
}
|
||||
{/* <GridSpan_4_8
|
||||
label={<TextCustom>Jenis Laporan</TextCustom>}
|
||||
value={
|
||||
<TextCustom truncate={2}>
|
||||
{item?.ForumMaster_KategoriReport?.title || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/> */}
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Belum ada data report komentar",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor="#E1B525"
|
||||
colors={["#E1B525"]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
130
screens/Admin/Forum/ScreenForumReportPosting.tsx
Normal file
130
screens/Admin/Forum/ScreenForumReportPosting.tsx
Normal file
@@ -0,0 +1,130 @@
|
||||
import { SearchInput, StackCustom, TextCustom } from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminForum } from "@/service/api-admin/api-admin-forum";
|
||||
import { router } from "expo-router";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
|
||||
export function Admin_ScreenForumReportPosting() {
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminForum({
|
||||
category: "report_posting",
|
||||
search: searchQuery || "",
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [],
|
||||
});
|
||||
|
||||
// Komponen search input
|
||||
const searchComponent = useMemo(
|
||||
() => (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari Postingan"
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
/>
|
||||
),
|
||||
[search],
|
||||
);
|
||||
|
||||
// Box title component
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle
|
||||
title="Report Posting"
|
||||
rightComponent={searchComponent}
|
||||
/>
|
||||
),
|
||||
[searchComponent],
|
||||
);
|
||||
|
||||
// Render item untuk daftar report posting
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<AdminBasicBox
|
||||
key={index}
|
||||
style={{ marginHorizontal: 5, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(
|
||||
`/admin/forum/${item?.Forum_Posting?.id}/list-report-posting`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<StackCustom gap={0}>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Pelapor</TextCustom>}
|
||||
value={
|
||||
<TextCustom truncate={1}>
|
||||
{item?.User?.username || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Postingan</TextCustom>}
|
||||
value={
|
||||
<TextCustom truncate={2}>
|
||||
{item?.Forum_Posting?.diskusi || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Belum ada data report posting",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor={MainColor.yellow}
|
||||
colors={[MainColor.yellow]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
63
screens/Admin/Investment/BoxInvestmentListOfInvestor.tsx
Normal file
63
screens/Admin/Investment/BoxInvestmentListOfInvestor.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { BadgeCustom, Divider, StackCustom, TextCustom } from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||
import { colorBadgeTransaction } from "@/utils/colorBadge";
|
||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||
import { router } from "expo-router";
|
||||
import _ from "lodash";
|
||||
|
||||
interface BoxInvestmentListOfInvestorProps {
|
||||
item: any;
|
||||
}
|
||||
|
||||
export default function Admin_BoxInvestmentListOfInvestor({
|
||||
item,
|
||||
}: BoxInvestmentListOfInvestorProps) {
|
||||
const statusName = item?.StatusInvoice?.name || "-";
|
||||
|
||||
return (
|
||||
<>
|
||||
<AdminBasicBox
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(
|
||||
`/admin/investment/${item?.id}/${_.lowerCase(
|
||||
item?.StatusInvoice?.name,
|
||||
)}/transaction-detail`,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<StackCustom gap={0}>
|
||||
<StackCustom style={{ paddingBlock: 8 }}>
|
||||
<TextCustom size="large" bold truncate>
|
||||
{item?.Author?.username || "-"}
|
||||
</TextCustom>
|
||||
</StackCustom>
|
||||
<Divider />
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Status</TextCustom>}
|
||||
value={
|
||||
<BadgeCustom
|
||||
color={colorBadgeTransaction({
|
||||
status: statusName,
|
||||
})}
|
||||
>
|
||||
{statusName}
|
||||
</BadgeCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Nominal</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{item?.nominal
|
||||
? `Rp ${formatCurrencyDisplay(item?.nominal)}`
|
||||
: "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
54
screens/Admin/Investment/BoxInvestmentStatus.tsx
Normal file
54
screens/Admin/Investment/BoxInvestmentStatus.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Divider, StackCustom, TextCustom } from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||
import { formatCurrencyDisplay } from "@/utils/formatCurrencyDisplay";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
interface BoxInvestmentStatusProps {
|
||||
item: any;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export default function Admin_BoxInvestmentStatus({
|
||||
item,
|
||||
status,
|
||||
}: BoxInvestmentStatusProps) {
|
||||
return (
|
||||
<>
|
||||
<AdminBasicBox
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(`/admin/investment/${item.id}/${status}`);
|
||||
}}
|
||||
>
|
||||
<StackCustom gap={0}>
|
||||
<View style={{ paddingBlock: 8 }}>
|
||||
<TextCustom size={"large"} bold truncate={2}>
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
</View>
|
||||
<Divider />
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Durasi</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{item?.MasterPencarianInvestor?.name || "-"} hari
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Target Dana</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{item?.targetDana
|
||||
? `Rp ${formatCurrencyDisplay(item?.targetDana)}`
|
||||
: "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
132
screens/Admin/Investment/ScreenInvestmentListOfInvestor.tsx
Normal file
132
screens/Admin/Investment/ScreenInvestmentListOfInvestor.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import { SelectCustom } from "@/components";
|
||||
import AdminBackButtonAntTitle from "@/components/_ShareComponent/Admin/BackButtonAntTitle";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminInvestmentListOfInvestor } from "@/service/api-admin/api-admin-investment";
|
||||
import { apiMasterTransaction } from "@/service/api-client/api-master";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
import Admin_BoxInvestmentListOfInvestor from "./BoxInvestmentListOfInvestor";
|
||||
|
||||
export function Admin_ScreenInvestmentListOfInvestor() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [selectValue, setSelectValue] = useState<string | null>(null);
|
||||
const [selectedStatus, setSelectedStatus] = useState<string | null>(null);
|
||||
const [master, setMaster] = useState<any[]>([]);
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminInvestmentListOfInvestor({
|
||||
id: id as string,
|
||||
status: selectedStatus as any,
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: "",
|
||||
dependencies: [id, selectedStatus],
|
||||
});
|
||||
|
||||
// Load master data untuk select option
|
||||
useEffect(() => {
|
||||
onLoadMaster();
|
||||
}, []);
|
||||
|
||||
const onLoadMaster = async () => {
|
||||
try {
|
||||
const response = await apiMasterTransaction();
|
||||
if (response.success) {
|
||||
setMaster(response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[ERROR]", error);
|
||||
setMaster([]);
|
||||
}
|
||||
};
|
||||
|
||||
// Komponen select untuk filter status
|
||||
const searchComponent = useMemo(
|
||||
() => (
|
||||
<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
|
||||
/>
|
||||
),
|
||||
[master, selectValue]
|
||||
);
|
||||
|
||||
// Header component dengan back button dan select filter
|
||||
const headerComponent = useMemo(
|
||||
() => <AdminBackButtonAntTitle newComponent={searchComponent} />,
|
||||
[searchComponent]
|
||||
);
|
||||
|
||||
// Render item untuk daftar investor
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<Admin_BoxInvestmentListOfInvestor key={index} item={item} />
|
||||
),
|
||||
[]
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: "",
|
||||
emptyMessage: "Belum ada data investor",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor="#E1B525"
|
||||
colors={["#E1B525"]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
109
screens/Admin/Investment/ScreenInvestmentStatus.tsx
Normal file
109
screens/Admin/Investment/ScreenInvestmentStatus.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import { SearchInput } from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import {
|
||||
PAGINATION_DEFAULT_TAKE,
|
||||
} from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminInvestment } from "@/service/api-admin/api-admin-investment";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
import Admin_BoxInvestmentStatus from "./BoxInvestmentStatus";
|
||||
|
||||
export function Admin_ScreenInvestmentStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminInvestment({
|
||||
category: status as "publish" | "review" | "reject",
|
||||
search: searchQuery,
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [status],
|
||||
});
|
||||
|
||||
// Komponen search input untuk header
|
||||
const rightComponent = useMemo(
|
||||
() => (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari judul investasi"
|
||||
value={search}
|
||||
onChangeText={(value) => setSearch(value)}
|
||||
/>
|
||||
),
|
||||
[search],
|
||||
);
|
||||
|
||||
// Render item untuk daftar investasi menggunakan Box Component
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<Admin_BoxInvestmentStatus
|
||||
key={index}
|
||||
item={item}
|
||||
status={status as string}
|
||||
/>
|
||||
),
|
||||
[status],
|
||||
);
|
||||
|
||||
// Header component dengan judul status investasi
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle
|
||||
title={`Investasi ${_.startCase(status as string)}`}
|
||||
rightComponent={rightComponent}
|
||||
/>
|
||||
),
|
||||
[status, rightComponent],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Belum ada data",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 120,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor="#E1B525"
|
||||
colors={["#E1B525"]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
29
screens/Admin/Job/BoxStatusJob.tsx
Normal file
29
screens/Admin/Job/BoxStatusJob.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Spacing, StackCustom, TextCustom } from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
import { Divider } from "react-native-paper";
|
||||
|
||||
interface BoxStatusJobProps {
|
||||
item: any;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export function BoxStatusJob({ item, status }: BoxStatusJobProps) {
|
||||
return (
|
||||
<AdminBasicBox
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(`/admin/job/${item.id}/${status}`);
|
||||
}}
|
||||
>
|
||||
<StackCustom>
|
||||
<View style={{paddingBlock: 8}}>
|
||||
<TextCustom size={"large"} align="center" bold truncate={2}>
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
</View>
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
);
|
||||
}
|
||||
103
screens/Admin/Job/ScreenJobStatus.tsx
Normal file
103
screens/Admin/Job/ScreenJobStatus.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import { SearchInput } from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminJob } from "@/service/api-admin/api-admin-job";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
import { Divider } from "react-native-paper";
|
||||
import { BoxStatusJob } from "./BoxStatusJob";
|
||||
|
||||
export function Admin_ScreenJobStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminJob({
|
||||
category: status as "publish" | "review" | "reject",
|
||||
search: searchQuery,
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [status],
|
||||
});
|
||||
|
||||
// Komponen kanan untuk header
|
||||
const rightComponent = useMemo(
|
||||
() => (
|
||||
<SearchInput
|
||||
placeholder="Cari perkerjaan"
|
||||
onChangeText={setSearch}
|
||||
value={search}
|
||||
/>
|
||||
),
|
||||
[search],
|
||||
);
|
||||
|
||||
// Render item untuk daftar pekerjaan
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<BoxStatusJob key={index} item={item} status={status as string} />
|
||||
),
|
||||
[status],
|
||||
);
|
||||
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle
|
||||
title={`Job ${_.startCase(status as string)}`}
|
||||
rightComponent={rightComponent}
|
||||
/>
|
||||
),
|
||||
[status, rightComponent],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Tidak ada data",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id.toString()}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor={MainColor.yellow}
|
||||
colors={[MainColor.yellow]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -5,20 +5,18 @@ import {
|
||||
Grid,
|
||||
SearchInput,
|
||||
StackCustom,
|
||||
TextCustom
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import {
|
||||
PAGINATION_DEFAULT_TAKE
|
||||
} from "@/constants/constans-value";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminUserAccessGetAll } from "@/service/api-admin/api-admin-user-access";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
|
||||
export function Admin_ScreenUserAccess() {
|
||||
@@ -54,6 +52,12 @@ export function Admin_ScreenUserAccess() {
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
});
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
pagination.onRefresh();
|
||||
}, []),
|
||||
);
|
||||
|
||||
const rightComponent = () => {
|
||||
return (
|
||||
<>
|
||||
|
||||
54
screens/Admin/Voting/BoxVotingStatus.tsx
Normal file
54
screens/Admin/Voting/BoxVotingStatus.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Divider, StackCustom, TextCustom } from "@/components";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import { GridSpan_4_8 } from "@/components/_ShareComponent/GridSpan_4_8";
|
||||
import { dateTimeView } from "@/utils/dateTimeView";
|
||||
import { router } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
|
||||
interface BoxVotingStatusProps {
|
||||
item: any;
|
||||
status?: string;
|
||||
path: any;
|
||||
}
|
||||
|
||||
export default function Admin_BoxVotingStatus({
|
||||
item,
|
||||
status,
|
||||
path,
|
||||
}: BoxVotingStatusProps) {
|
||||
return (
|
||||
<>
|
||||
<AdminBasicBox
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
onPress={() => {
|
||||
router.push(path);
|
||||
}}
|
||||
>
|
||||
<StackCustom gap={0}>
|
||||
<View style={{ paddingBlock: 8 }}>
|
||||
<TextCustom size={"large"} bold truncate={2}>
|
||||
{item?.title || "-"}
|
||||
</TextCustom>
|
||||
</View>
|
||||
<Divider />
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Mulai</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{dateTimeView({ date: item?.awalVote }) || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
<GridSpan_4_8
|
||||
label={<TextCustom>Berakhir</TextCustom>}
|
||||
value={
|
||||
<TextCustom>
|
||||
{dateTimeView({ date: item?.akhirVote }) || "-"}
|
||||
</TextCustom>
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</AdminBasicBox>
|
||||
</>
|
||||
);
|
||||
}
|
||||
152
screens/Admin/Voting/ScreenEventTypeOfEvent.tsx
Normal file
152
screens/Admin/Voting/ScreenEventTypeOfEvent.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
import { BadgeCustom, TextCustom } from "@/components";
|
||||
import AdminActionIconPlus from "@/components/_ShareComponent/Admin/ActionIconPlus";
|
||||
import AdminBasicBox from "@/components/_ShareComponent/Admin/AdminBasicBox";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import { GridViewCustomSpan } from "@/components/_ShareComponent/GridViewCustomSpan";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminMasterTypeOfEvent } from "@/service/api-admin/api-master-admin";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
|
||||
export function Admin_ScreenEventTypeOfEvent() {
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminMasterTypeOfEvent({
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [],
|
||||
});
|
||||
|
||||
// Komponen action plus untuk header
|
||||
const rightComponent = useMemo(
|
||||
() => (
|
||||
<AdminActionIconPlus
|
||||
onPress={() => {
|
||||
router.push(`/admin/event/type-create`);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Header component untuk title
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle title="Tipe Acara Event" rightComponent={rightComponent} />
|
||||
),
|
||||
[rightComponent],
|
||||
);
|
||||
|
||||
// Render header tabel (Aksi, Status, Tipe Acara)
|
||||
const renderTableHeader = useMemo(
|
||||
() => (
|
||||
<>
|
||||
<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>}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Render item untuk daftar tipe event (mengikuti pattern InformationBankSection)
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<AdminBasicBox
|
||||
onPress={() => {
|
||||
router.push(`/admin/event/type-update?id=${item.id}`);
|
||||
}}
|
||||
style={{ marginHorizontal: 10, marginVertical: 5 }}
|
||||
>
|
||||
<GridTwoView
|
||||
leftItem={<TextCustom bold>{item?.name || "-"}</TextCustom>}
|
||||
rightItem={
|
||||
<View>
|
||||
{item?.active ? (
|
||||
<BadgeCustom color="green">Aktif</BadgeCustom>
|
||||
) : (
|
||||
<BadgeCustom color="red">Tidak Aktif</BadgeCustom>
|
||||
)}
|
||||
</View>
|
||||
}
|
||||
spanLeft={8}
|
||||
spanRight={4}
|
||||
styleRight={{
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
/>
|
||||
</AdminBasicBox>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
pagination.onRefresh();
|
||||
}, []),
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Belum ada data",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor="#E1B525"
|
||||
colors={["#E1B525"]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
102
screens/Admin/Voting/ScreenVotingHistory.tsx
Normal file
102
screens/Admin/Voting/ScreenVotingHistory.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import { SearchInput } from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import {
|
||||
PAGINATION_DEFAULT_TAKE
|
||||
} from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
import Admin_BoxVotingStatus from "./BoxVotingStatus";
|
||||
|
||||
export function Admin_ScreenVotingHistory() {
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminVoting({
|
||||
category: "history",
|
||||
search: searchQuery,
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [],
|
||||
});
|
||||
|
||||
// Komponen search input untuk header
|
||||
const rightComponent = useMemo(
|
||||
() => (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari"
|
||||
value={search}
|
||||
onChangeText={(value) => setSearch(value)}
|
||||
/>
|
||||
),
|
||||
[search],
|
||||
);
|
||||
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<Admin_BoxVotingStatus
|
||||
key={index}
|
||||
item={item}
|
||||
path={`/admin/voting/${item.id}/history`}
|
||||
/>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
// Header component dengan judul voting history
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle title="Riwayat" rightComponent={rightComponent} />
|
||||
),
|
||||
[rightComponent],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Belum ada data",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 80,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor="#E1B525"
|
||||
colors={["#E1B525"]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
110
screens/Admin/Voting/ScreenVotingStatus.tsx
Normal file
110
screens/Admin/Voting/ScreenVotingStatus.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
import { SearchInput } from "@/components";
|
||||
import AdminComp_BoxTitle from "@/components/_ShareComponent/Admin/BoxTitlePage";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import {
|
||||
PAGINATION_DEFAULT_TAKE
|
||||
} from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAdminVoting } from "@/service/api-admin/api-admin-voting";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { RefreshControl } from "react-native";
|
||||
import Admin_BoxVotingStatus from "./BoxVotingStatus";
|
||||
|
||||
export function Admin_ScreenVotingStatus() {
|
||||
const { status } = useLocalSearchParams();
|
||||
const [search, setSearch] = useState<string>("");
|
||||
|
||||
// Gunakan hook pagination
|
||||
const pagination = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAdminVoting({
|
||||
category: status as any,
|
||||
search: searchQuery,
|
||||
page: String(page),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
return { data: response.data };
|
||||
} else {
|
||||
return { data: [] };
|
||||
}
|
||||
},
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
dependencies: [status],
|
||||
});
|
||||
|
||||
// Komponen search input untuk header
|
||||
const rightComponent = useMemo(
|
||||
() => (
|
||||
<SearchInput
|
||||
containerStyle={{ width: "100%", marginBottom: 0 }}
|
||||
placeholder="Cari"
|
||||
value={search}
|
||||
onChangeText={(value) => setSearch(value)}
|
||||
/>
|
||||
),
|
||||
[search],
|
||||
);
|
||||
|
||||
// Render item untuk daftar voting
|
||||
const renderItem = useCallback(
|
||||
({ item, index }: { item: any; index: number }) => (
|
||||
<Admin_BoxVotingStatus
|
||||
key={index}
|
||||
item={item}
|
||||
status={status as string}
|
||||
path={`/admin/voting/${item.id}/${status}`}
|
||||
/>
|
||||
),
|
||||
[status],
|
||||
);
|
||||
|
||||
// Header component dengan judul status voting
|
||||
const headerComponent = useMemo(
|
||||
() => (
|
||||
<AdminComp_BoxTitle
|
||||
title={`Voting ${_.startCase(status as string)}`}
|
||||
rightComponent={rightComponent}
|
||||
/>
|
||||
),
|
||||
[status, rightComponent],
|
||||
);
|
||||
|
||||
// Buat komponen-komponen pagination
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Belum ada data",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 80,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={(item: any) => item.id?.toString() || `fallback-${item.id}`}
|
||||
headerComponent={headerComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
tintColor="#E1B525"
|
||||
colors={["#E1B525"]}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -72,16 +72,16 @@ const adminListMenu: NavbarItem[] = [
|
||||
{ label: "Report Komentar", link: "/admin/forum/report-comment" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Collaboration",
|
||||
icon: "people",
|
||||
links: [
|
||||
{ label: "Dashboard", link: "/admin/collaboration" },
|
||||
{ label: "Publish", link: "/admin/collaboration/publish" },
|
||||
{ label: "Group", link: "/admin/collaboration/group" },
|
||||
{ label: "Reject", link: "/admin/collaboration/reject" },
|
||||
],
|
||||
},
|
||||
// {
|
||||
// label: "Collaboration",
|
||||
// icon: "people",
|
||||
// links: [
|
||||
// { label: "Dashboard", link: "/admin/collaboration" },
|
||||
// { label: "Publish", link: "/admin/collaboration/publish" },
|
||||
// { label: "Group", link: "/admin/collaboration/group" },
|
||||
// { label: "Reject", link: "/admin/collaboration/reject" },
|
||||
// ],
|
||||
// },
|
||||
{ label: "Maps", icon: "map", link: "/admin/maps" },
|
||||
{
|
||||
label: "App Information",
|
||||
@@ -165,16 +165,16 @@ const superAdminListMenu: NavbarItem[] = [
|
||||
{ label: "Report Komentar", link: "/admin/forum/report-comment" },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Collaboration",
|
||||
icon: "people",
|
||||
links: [
|
||||
{ label: "Dashboard", link: "/admin/collaboration" },
|
||||
{ label: "Publish", link: "/admin/collaboration/publish" },
|
||||
{ label: "Group", link: "/admin/collaboration/group" },
|
||||
{ label: "Reject", link: "/admin/collaboration/reject" },
|
||||
],
|
||||
},
|
||||
// {
|
||||
// label: "Collaboration",
|
||||
// icon: "people",
|
||||
// links: [
|
||||
// { label: "Dashboard", link: "/admin/collaboration" },
|
||||
// { label: "Publish", link: "/admin/collaboration/publish" },
|
||||
// { label: "Group", link: "/admin/collaboration/group" },
|
||||
// { label: "Reject", link: "/admin/collaboration/reject" },
|
||||
// ],
|
||||
// },
|
||||
{ label: "Maps", icon: "map", link: "/admin/maps" },
|
||||
{
|
||||
label: "App Information",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {
|
||||
ViewWrapper,
|
||||
DrawerCustom,
|
||||
DummyLandscapeImage,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
Grid,
|
||||
ButtonCustom,
|
||||
ButtonCustom,
|
||||
DrawerCustom,
|
||||
DummyLandscapeImage,
|
||||
Grid,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import API_IMAGE from "@/constants/api-storage";
|
||||
@@ -15,8 +15,8 @@ import { apiMapsGetAll } from "@/service/api-client/api-maps";
|
||||
import { openInDeviceMaps } from "@/utils/openInDeviceMaps";
|
||||
import { FontAwesome, Ionicons } from "@expo/vector-icons";
|
||||
import { Image } from "expo-image";
|
||||
import { useFocusEffect, router } from "expo-router";
|
||||
import { useState, useCallback } from "react";
|
||||
import { router, useFocusEffect } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import MapView, { Marker } from "react-native-maps";
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function MapsView() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
handlerLoadList();
|
||||
}, [])
|
||||
}, []),
|
||||
);
|
||||
|
||||
const handlerLoadList = async () => {
|
||||
@@ -146,52 +146,52 @@ export default function MapsView() {
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<FontAwesome
|
||||
name="building-o"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.namePin}</TextCustom>}
|
||||
rightItem={<TextCustom>{selected.namePin}</TextCustom>}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="list-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
||||
rightItem={<TextCustom>{selected.bidangBisnis}</TextCustom>}
|
||||
/>
|
||||
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="call-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.nomorTelepon}</TextCustom>}
|
||||
rightItem={<TextCustom>{selected.nomorTelepon}</TextCustom>}
|
||||
/>
|
||||
<GridTwoView
|
||||
spanLeft={2}
|
||||
spanRight={10}
|
||||
leftIcon={
|
||||
leftItem={
|
||||
<Ionicons
|
||||
name="location-outline"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color="white"
|
||||
/>
|
||||
}
|
||||
rightIcon={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
||||
rightItem={<TextCustom>{selected.alamatBisnis}</TextCustom>}
|
||||
/>
|
||||
|
||||
<Grid>
|
||||
|
||||
@@ -4,13 +4,15 @@ import { apiConfig } from "../api-config";
|
||||
export async function apiAdminDonation({
|
||||
category,
|
||||
search,
|
||||
page = "1",
|
||||
}: {
|
||||
category: "dashboard" | "publish" | "review" | "reject";
|
||||
search?: string;
|
||||
page?: string;
|
||||
}) {
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/donation?category=${category}&search=${search}`
|
||||
`/mobile/admin/donation?category=${category}&search=${search}&page=${page}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
@@ -131,12 +133,16 @@ export async function apiAdminDonationDisbursementOfFundsCreated({
|
||||
export async function apiAdminDonationDisbursementOfFundsListById({
|
||||
id,
|
||||
category,
|
||||
page = "1",
|
||||
}: {
|
||||
id: string;
|
||||
category: "get-all" | "get-one"
|
||||
category: "get-all" | "get-one";
|
||||
page?: string;
|
||||
}) {
|
||||
try {
|
||||
const response = await apiConfig.get(`/mobile/admin/donation/${id}/disbursement?category=${category}`);
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/donation/${id}/disbursement?category=${category}&page=${page}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
||||
@@ -3,13 +3,15 @@ import { apiConfig } from "../api-config";
|
||||
export async function apiAdminEvent({
|
||||
category,
|
||||
search,
|
||||
page = "1",
|
||||
}: {
|
||||
category: "dashboard" | "history" | "publish" | "review" | "type-of-event";
|
||||
search?: string;
|
||||
page?: string;
|
||||
}) {
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/event?category=${category}&search=${search}`
|
||||
`/mobile/admin/event?category=${category}&search=${search}&page=${page}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
@@ -48,10 +50,18 @@ export async function apiAdminEventUpdateStatus({
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiAdminEventListOfParticipants({ id }: { id: string }) {
|
||||
export async function apiAdminEventListOfParticipants({
|
||||
id,
|
||||
page = "1",
|
||||
search = ""
|
||||
}: {
|
||||
id: string;
|
||||
page?: string;
|
||||
search?: string;
|
||||
}) {
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/event/${id}/participants`
|
||||
`/mobile/admin/event/${id}/participants?page=${page}&search=${search}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,13 +3,15 @@ import { apiConfig } from "../api-config";
|
||||
export async function apiAdminForum({
|
||||
category,
|
||||
search,
|
||||
page = "1",
|
||||
}: {
|
||||
category: "dashboard" | "posting" | "report_posting" | "report_comment";
|
||||
search?: string;
|
||||
page?: string;
|
||||
}) {
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/forum?category=${category}&search=${search}`
|
||||
`/mobile/admin/forum?category=${category}&search=${search}&page=${page}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
@@ -78,12 +80,14 @@ export async function apiAdminForumDeactivateComment({
|
||||
|
||||
export async function apiAdminForumListReportPostingById({
|
||||
id,
|
||||
page = "1",
|
||||
}: {
|
||||
id: string;
|
||||
page?: string;
|
||||
}) {
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/forum/${id}/report-posting`
|
||||
`/mobile/admin/forum/${id}/report-posting?page=${page}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
|
||||
@@ -4,14 +4,16 @@ import { apiConfig } from "../api-config";
|
||||
export async function apiAdminInvestment({
|
||||
category,
|
||||
search,
|
||||
page = "1",
|
||||
}: {
|
||||
category: "dashboard" | "publish" | "review" | "reject";
|
||||
search?: string;
|
||||
page?: string;
|
||||
}) {
|
||||
const propsQuery =
|
||||
category === "dashboard"
|
||||
? `category=${category}`
|
||||
: `category=${category}&search=${search}`;
|
||||
? `category=${category}&page=${page}`
|
||||
: `category=${category}&search=${search}&page=${page}`;
|
||||
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
@@ -57,15 +59,23 @@ export async function apiAdminInvestasiUpdateByStatus({
|
||||
export async function apiAdminInvestmentListOfInvestor({
|
||||
id,
|
||||
status,
|
||||
page = "1",
|
||||
}: {
|
||||
id: string;
|
||||
status: "berhasil" | "gagal" | "proses" | "menunggu" | null;
|
||||
page?: string;
|
||||
}) {
|
||||
const query = status && status !== null ? `?status=${status}` : "";
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (status && status !== null) {
|
||||
queryParams.append("status", status);
|
||||
}
|
||||
|
||||
queryParams.append("page", page);
|
||||
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/investment/${id}/investor${query}`
|
||||
`/mobile/admin/investment/${id}/investor?${queryParams.toString()}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,13 +3,15 @@ import { apiConfig } from "../api-config";
|
||||
export async function apiAdminJob({
|
||||
category,
|
||||
search,
|
||||
page = "1",
|
||||
}: {
|
||||
category: "dashboard" | "publish" | "review" | "reject";
|
||||
search?: string;
|
||||
page?: string;
|
||||
}) {
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/job?category=${category}&search=${search}`
|
||||
`/mobile/admin/job?category=${category}&search=${search}&page=${page}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
|
||||
@@ -4,13 +4,15 @@ import { apiConfig } from "../api-config";
|
||||
export async function apiAdminVoting({
|
||||
category,
|
||||
search,
|
||||
page = "1",
|
||||
}: {
|
||||
category: "dashboard" | "history" | "publish" | "review" | "report";
|
||||
search?: string;
|
||||
page?: string;
|
||||
}) {
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/voting?category=${category}&search=${search}`
|
||||
`/mobile/admin/voting?category=${category}&search=${search}&page=${page}`
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { apiConfig } from "../api-config";
|
||||
|
||||
// ================== START MASTER BANK ================== //
|
||||
export async function apiAdminMasterBank() {
|
||||
export async function apiAdminMasterBank({ page = "1" }: { page?: string }) {
|
||||
try {
|
||||
const response = await apiConfig.get(`/mobile/admin/master/bank`);
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/master/bank?page=${page}`,
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@@ -51,9 +53,15 @@ export async function apiAdminMasterBankCreate({ data }: { data: any }) {
|
||||
|
||||
// ================== START BUSINNES FIELD ================== //
|
||||
|
||||
export async function apiAdminMasterBusinessField() {
|
||||
export async function apiAdminMasterBusinessField({
|
||||
page = "1",
|
||||
}: {
|
||||
page: string;
|
||||
}) {
|
||||
try {
|
||||
const response = await apiConfig.get(`/mobile/admin/master/business-field`);
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/master/business-field?page=${page}`,
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@@ -64,16 +72,19 @@ export async function apiAdminMasterBusinessFieldById({
|
||||
id,
|
||||
subBidangId,
|
||||
category,
|
||||
page = "1"
|
||||
}: {
|
||||
id: string;
|
||||
subBidangId?: string | null;
|
||||
category: "bidang" | "sub-bidang" | "all";
|
||||
category: "bidang" | "sub-bidang" | "all" | "only-sub-bidang"
|
||||
page?: string
|
||||
}) {
|
||||
const queryCategory = category ? `?category=${category}` : "";
|
||||
const querySubBidang = subBidangId ? `&subBidangId=${subBidangId}` : "";
|
||||
const queryPage = page ? `&page=${page}` : "";
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/master/business-field/${id}${queryCategory}${querySubBidang}`
|
||||
`/mobile/admin/master/business-field/${id}${queryCategory}${querySubBidang}${queryPage}`,
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
@@ -95,7 +106,7 @@ export async function apiAdminMasterBusinessFieldUpdate({
|
||||
`/mobile/admin/master/business-field/${id}?category=${category}`,
|
||||
{
|
||||
data: data,
|
||||
}
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
@@ -113,7 +124,7 @@ export async function apiAdminMasterBusinessFieldCreate({
|
||||
`/mobile/admin/master/business-field`,
|
||||
{
|
||||
data: data,
|
||||
}
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
@@ -124,9 +135,9 @@ export async function apiAdminMasterBusinessFieldCreate({
|
||||
// ================== END BUSINNES FIELD ================== //
|
||||
|
||||
// ================== START EVENT ================== //
|
||||
export async function apiAdminMasterTypeOfEvent() {
|
||||
export async function apiAdminMasterTypeOfEvent({ page = "1" }: { page?: string }) {
|
||||
try {
|
||||
const response = await apiConfig.get(`/mobile/admin/master/type-of-event`);
|
||||
const response = await apiConfig.get(`/mobile/admin/master/type-of-event?page=${page}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@@ -139,7 +150,7 @@ export async function apiEventCreateTypeOfEvent({ data }: { data: string }) {
|
||||
`/mobile/admin/master/type-of-event`,
|
||||
{
|
||||
data: data,
|
||||
}
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
@@ -150,7 +161,7 @@ export async function apiEventCreateTypeOfEvent({ data }: { data: string }) {
|
||||
export async function apiAdminMasterTypeOfEventGetOne({ id }: { id: string }) {
|
||||
try {
|
||||
const response = await apiConfig.get(
|
||||
`/mobile/admin/master/type-of-event/${id}`
|
||||
`/mobile/admin/master/type-of-event/${id}`,
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
@@ -170,7 +181,7 @@ export async function apiAdminMasterTypeOfEventUpdate({
|
||||
`/mobile/admin/master/type-of-event/${id}`,
|
||||
{
|
||||
data: data,
|
||||
}
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
@@ -182,9 +193,9 @@ export async function apiAdminMasterTypeOfEventUpdate({
|
||||
|
||||
// ================== START DONATION ================== //
|
||||
|
||||
export async function apiAdminMasterDonationCategory() {
|
||||
export async function apiAdminMasterDonationCategory({ page = "1" }: { page?: string }) {
|
||||
try {
|
||||
const response = await apiConfig.get(`/mobile/admin/master/donation`);
|
||||
const response = await apiConfig.get(`/mobile/admin/master/donation?page=${page}`);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@@ -216,7 +227,7 @@ export async function apiAdminMasterDonationCategoryUpdate({
|
||||
`/mobile/admin/master/donation/${id}`,
|
||||
{
|
||||
data: data,
|
||||
}
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
@@ -240,3 +251,27 @@ export async function apiAdminMasterDonationCategoryCreate({
|
||||
}
|
||||
|
||||
// ================== END DONATION ================== //
|
||||
|
||||
// ================== START FECTH APP INFORMATION ================== //
|
||||
|
||||
export async function apiFetchAdminMasterAppInformation({
|
||||
page = "1",
|
||||
category,
|
||||
}: {
|
||||
page: string;
|
||||
category?: "bank" | "business" | string
|
||||
}) {
|
||||
if (category === "bank") {
|
||||
const response = await apiAdminMasterBank({ page });
|
||||
// TODO: implement bank logic
|
||||
return response;
|
||||
} else if (category === "business") {
|
||||
const response = await apiAdminMasterBusinessField({ page });
|
||||
// TODO: implement business logic
|
||||
return response
|
||||
} else {
|
||||
throw new Error("Category is required");
|
||||
}
|
||||
}
|
||||
|
||||
// ================== END FECTH APP INFORMATION ================== //
|
||||
|
||||
@@ -16,7 +16,7 @@ export const GStyles = StyleSheet.create({
|
||||
// =============== Main Styles =============== //
|
||||
container: {
|
||||
flex: 1,
|
||||
paddingInline: PADDING_MEDIUM,
|
||||
paddingInline: PADDING_SMALL,
|
||||
paddingTop: PADDING_EXTRA_SMALL,
|
||||
paddingBottom: 5,
|
||||
// paddingBlock: PADDING_EXTRA_SMALL,
|
||||
|
||||
Reference in New Issue
Block a user