Compare commits
41 Commits
amalia/23-
...
join
| Author | SHA1 | Date | |
|---|---|---|---|
| e2a601c590 | |||
| 4681f0a0cc | |||
| 31b7cf6a30 | |||
| 64aaafa2be | |||
| 42cb7c8f8e | |||
| 8c63c08bc3 | |||
| 6ca935483a | |||
| 039b26f5aa | |||
| 10212aa5de | |||
| f0373ef479 | |||
| acacf9c125 | |||
| 700192dd8d | |||
| 4df0a44ac9 | |||
| 27b0b7d51f | |||
| 8012f7f322 | |||
| 65278df750 | |||
| 064a8ccaad | |||
| 8b98fee632 | |||
| d3802ca26c | |||
| e254cf8ed2 | |||
| ddfee00410 | |||
| 1aa51adab0 | |||
| ebe8380829 | |||
| c421d267b9 | |||
| bbacd40ae9 | |||
| 9bab420f91 | |||
| 7d8b72fdfa | |||
| e9c11a889d | |||
| 10b74ccde9 | |||
| 73c6a19880 | |||
| 225ed63027 | |||
| f34df12b2f | |||
| a24a698f86 | |||
| 6b55433c02 | |||
| 13cf7ef9c5 | |||
| febb56f6e9 | |||
| 8da7c598a7 | |||
| 6cc5d07017 | |||
| 7a589e11e4 | |||
| c230e0b18b | |||
| 2433cf4bc9 |
86
GEMINI.md
Normal file
86
GEMINI.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Project Overview: Desa+
|
||||
|
||||
Desa+ is a mobile application built with React Native and Expo, designed to facilitate management and communication within villages/communities. It aims to streamline village administration, inter-community communication, and the management of essential information.
|
||||
|
||||
## Key Features:
|
||||
- Village announcements and information
|
||||
- Community discussion forum
|
||||
- Village activity calendar
|
||||
- Village documentation and archives
|
||||
- Project and task management
|
||||
- Member and organizational structure management
|
||||
- Push notifications for important updates
|
||||
- Verification and authentication features
|
||||
|
||||
## Technologies Used:
|
||||
- **React Native**: Cross-platform mobile development framework.
|
||||
- **Expo**: Platform for React Native application development.
|
||||
- **Firebase**: Backend services including Authentication, Realtime Database, and Cloud Messaging.
|
||||
- **Redux Toolkit**: State management.
|
||||
- **React Navigation**: Application navigation.
|
||||
- **TypeScript**: For type safety.
|
||||
|
||||
## Building and Running:
|
||||
|
||||
### Installation
|
||||
1. **Clone the repository:**
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd mobile-darmasaba
|
||||
```
|
||||
2. **Install dependencies:**
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
3. **Configure environment variables:**
|
||||
Create a `.env` file in the root directory and add the following variables:
|
||||
```
|
||||
URL_API=<api-endpoint>
|
||||
URL_OTP=<otp-service-endpoint>
|
||||
URL_STORAGE=<storage-endpoint>
|
||||
URL_FIREBASE_DB=<firebase-database-url>
|
||||
PASS_ENC=<encryption-password>
|
||||
WA_SERVER_TOKEN=<whatsapp-server-token>
|
||||
IOS_GOOGLE_SERVICES_FILE=<path-to-ios-google-services>
|
||||
```
|
||||
|
||||
### Running the Application
|
||||
- **Start development server:**
|
||||
```bash
|
||||
npx expo start
|
||||
```
|
||||
- **Run on Android emulator/device:**
|
||||
```bash
|
||||
npm run android
|
||||
```
|
||||
- **Run on iOS simulator/device:**
|
||||
```bash
|
||||
npm run ios
|
||||
```
|
||||
|
||||
### Build Production
|
||||
- **Build Android production package:**
|
||||
```bash
|
||||
npm run build:android
|
||||
```
|
||||
|
||||
## Development Conventions:
|
||||
|
||||
### Project Structure:
|
||||
- `app/`: Main page files.
|
||||
- `components/`: Reusable UI components, categorized by feature (e.g., `announcement/`, `auth/`, `discussion/`).
|
||||
- `assets/`: Images and static assets.
|
||||
- `constants/`: Global constants.
|
||||
- `lib/`: Libraries and utilities.
|
||||
|
||||
### Contribution Guidelines:
|
||||
1. Fork the repository.
|
||||
2. Create a new feature branch (`git checkout -b feature/FeatureName`).
|
||||
3. Commit your changes (`git commit -m 'Add FeatureName feature'`).
|
||||
4. Push to the branch (`git push origin feature/FeatureName`).
|
||||
5. Create a pull request.
|
||||
|
||||
## Platform Support:
|
||||
- ✅ Android
|
||||
- ✅ iOS
|
||||
- ❌ Web (not yet optimized)
|
||||
249
Panduan-Penggunaan-Aplikasi.md
Normal file
249
Panduan-Penggunaan-Aplikasi.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# Panduan Aplikasi Desa+
|
||||
|
||||
## Daftar Isi
|
||||
|
||||
1. [Gambaran Umum Aplikasi](#gambaran-umum-aplikasi)
|
||||
2. [User Roles dan Hak Akses](#user-roles-dan-hak-akses)
|
||||
3. [Fitur-fitur Aplikasi](#fitur-fitur-aplikasi)
|
||||
4. [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Gambaran Umum Aplikasi
|
||||
|
||||
Aplikasi Desa+ adalah platform digital berbasis mobile yang dirancang untuk khusus untuk pegawai desa dalam mengelola data dan memantau progres kegiatan internal. Aplikasi ini menyediakan berbagai fitur seperti pengelolaan data per divisi, pemantauan kegiatan umum, forum diskusi, pengumuman, hingga manajemen folder dokumen, aplikasi ini membantu meningkatkan efisiensi kerja, koordinasi, serta transparansi di lingkungan desa.
|
||||
|
||||
### Teknologi yang Digunakan
|
||||
|
||||
- React Native dengan Expo
|
||||
- Firebase (Authentication, Realtime Database, Cloud Messaging)
|
||||
- Redux Toolkit untuk manajemen state
|
||||
- TypeScript untuk type safety
|
||||
|
||||
## User Roles dan Hak Akses
|
||||
|
||||
Aplikasi Desa+ memiliki sistem hierarki peran pengguna sebagai berikut:
|
||||
|
||||
### 1. Super Admin
|
||||
|
||||
- **Hak akses:**
|
||||
- Semua fitur dan fungsi dalam aplikasi
|
||||
- Manajemen pengguna dengan role Wakil Super Admin, Admin, Wakil Admin, dan User
|
||||
- Akses ke semua data dan fungsi administratif
|
||||
|
||||
### 2. Wakil Super Admin
|
||||
|
||||
- **Hak akses:**
|
||||
- Manajemen pengguna dengan role Admin, Wakil Admin, dan User
|
||||
- Akses ke sebagian besar fitur administratif
|
||||
- Dapat mengelola banner
|
||||
|
||||
### 3. Admin
|
||||
|
||||
- **Hak akses:**
|
||||
- Manajemen pengguna dengan role Wakil Admin dan User
|
||||
- Akses ke fitur-fitur administratif dasar
|
||||
- Tidak dapat mengelola Wakil Super Admin dan Super Admin
|
||||
|
||||
### 4. Wakil Admin
|
||||
|
||||
- **Hak akses:**
|
||||
- Manajemen pengguna dengan role User
|
||||
- Akses terbatas ke fitur-fitur administratif
|
||||
- Tidak dapat mengelola Admin ke atas
|
||||
|
||||
### 5. User
|
||||
|
||||
- **Hak akses:**
|
||||
- Akses ke fitur-fitur umum
|
||||
- Tidak dapat mengelola pengguna lain
|
||||
- Tidak dapat mengakses fungsi administratif (kecuali dalam divisi dimana pengguna tersebut adalah anggota)
|
||||
|
||||
## Fitur-fitur Aplikasi
|
||||
|
||||
### 1. Otentikasi (Login & Verifikasi)
|
||||
|
||||
**Deskripsi:** Sistem login menggunakan nomor telepon dan verifikasi OTP (One Time Password)
|
||||
|
||||
- **Fungsi:** Memverifikasi identitas pengguna sebelum mengakses aplikasi
|
||||
- **Siapa yang bisa mengakses:** Semua pengguna yang terdaftar
|
||||
|
||||
### 2. Dashboard/Home Screen
|
||||
|
||||
**Deskripsi:** Tampilan utama aplikasi yang menampilkan informasi dan akses cepat ke berbagai fitur
|
||||
|
||||
- **Fungsi:** Menyediakan ringkasan informasi desa dan akses cepat ke fitur-fitur utama
|
||||
- **Siapa yang bisa mengakses:** Semua pengguna yang telah login
|
||||
- **Komponen:**
|
||||
- Carousel banner untuk promosi atau informasi penting
|
||||
- Fitur untuk mengakses semua fitur aplikasi
|
||||
- Grafik progres kegiatan
|
||||
- Grafik jumlah dokumen
|
||||
- Daftar kegiatan terupdate
|
||||
- Daftar divisi aktif
|
||||
- Daftar acara mendatang
|
||||
- Diskusi terbaru
|
||||
|
||||
### 3. Pencarian
|
||||
|
||||
**Deskripsi:** Fitur untuk mencari anggota, kegiatan dan divisi
|
||||
|
||||
- **Fungsi:** Mencari anggota, kegiatan dan divisi
|
||||
- **Siapa yang bisa mengakses:** Semua pengguna
|
||||
|
||||
### 4. Notifikasi
|
||||
|
||||
**Deskripsi:** Sistem notifikasi untuk memberitahu pengguna tentang aktivitas penting
|
||||
|
||||
- **Fungsi:** Memberitahu pengguna tentang pengumuman, komentar, atau aktivitas lainnya
|
||||
- **Siapa yang bisa mengakses:** Semua pengguna
|
||||
|
||||
### 5. Profil
|
||||
|
||||
**Deskripsi:** Fitur untuk melihat dan mengedit informasi pribadi pengguna
|
||||
|
||||
- **Fungsi:** Menampilkan dan mengelola informasi akun pengguna
|
||||
- **Siapa yang bisa mengakses:** Pengguna yang bersangkutan
|
||||
|
||||
### 6. Banner
|
||||
|
||||
**Deskripsi:** Fitur untuk mengelola banner promosi atau informasi penting di halaman utama
|
||||
|
||||
- **Fungsi:** Menampilkan informasi atau promosi penting di tampilan awal
|
||||
- **Siapa yang bisa mengakses:** Super Admin, Wakil Super Admin
|
||||
|
||||
### 7. Lembaga Desa
|
||||
|
||||
**Deskripsi:** Fitur untuk mengelola berbagai lembaga dalam desa
|
||||
|
||||
- **Fungsi:** Mengorganisir struktur organisasi desa berdasarkan lembaga
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin
|
||||
- Melihat: Super Admin
|
||||
|
||||
### 8. Jabatan
|
||||
|
||||
**Deskripsi:** Fitur untuk mengelola posisi atau jabatan
|
||||
|
||||
- **Fungsi:** Mengelola data jabatan
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Wakil Super Admin, Admin, Wakil Admin
|
||||
- Melihat: Semua pengguna
|
||||
|
||||
### 9. Anggota
|
||||
|
||||
**Deskripsi:** Fitur untuk mengelola data pengguna
|
||||
|
||||
- **Fungsi:** Menyimpan dan mengelola informasi tentang pengguna
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Wakil Super Admin, Admin, Wakil Admin
|
||||
- Melihat: Semua pengguna
|
||||
|
||||
### 10. Diskusi Umum
|
||||
|
||||
**Deskripsi:** Forum diskusi untuk komunikasi anggota terpilih
|
||||
|
||||
- **Fungsi:** Tempat berdiskusi mengenai berbagai topik yang berkaitan dengan desa
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Wakil Super Admin, Admin
|
||||
- Melihat: Semua pengguna
|
||||
- Berkomentar: Pengguna terpilih
|
||||
|
||||
### 11. Kegiatan/Proyek
|
||||
|
||||
**Deskripsi:** Fitur untuk mengelola dan melacak proyek atau kegiatan desa
|
||||
|
||||
- **Fungsi:** Mengelola dan memonitor kemajuan proyek-proyek desa
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus/Membatalkan/Mengelola anggota: Super Admin, Wakil Super Admin, Admin
|
||||
- Mengelola detail (file, task, link, laporan) : Super Admin, Wakil Super Admin, Admin, Anggota dari kegiatan
|
||||
- Melihat: Semua pengguna
|
||||
- **Status Kegiatan:**
|
||||
- Segera: Proyek yang akan segera dimulai
|
||||
- Dikerjakan: Proyek yang sedang dalam proses pengerjaan
|
||||
- Selesai: Proyek yang telah selesai
|
||||
- Batal: Proyek yang dibatalkan
|
||||
|
||||
### 12. Pengumuman
|
||||
|
||||
**Deskripsi:** Fitur untuk membuat, melihat, dan mengelola pengumuman desa
|
||||
|
||||
- **Fungsi:** Menyebarkan informasi penting kepada anggota divisi terpilih
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Wakil Super Admin, Admin
|
||||
- Melihat:
|
||||
- Super admin: Semua pengumuman
|
||||
- Wakil super admin & admin : Pengumuman sesuai lembaga desa
|
||||
- Lainnya: Pengumuman yang ditujukan ke divisi mereka
|
||||
|
||||
### 13. Divisi
|
||||
|
||||
**Deskripsi:** Fitur untuk mengelola data desa berdasarkan divisi
|
||||
|
||||
- **Fungsi:** Mengorganisir tugas-tugas berdasarkan divisi-divisi tertentu
|
||||
- **Catatan:** Anggota divisi (role : Wakil Admin dan User) yg diangkat menjadi "Admin Divisi", mendapat akses khusus untuk mengelola divisi tersebut
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Wakil Super Admin, Admin
|
||||
- Edit Divisi / Non aktifkan Divisi tertentu / Mengelola Anggota divisi tertentu : Super Admin, Wakil Super Admin, Admin, Admin Divisi
|
||||
- Laporan semua divisi : Super Admin, Wakil Super Admin
|
||||
- Laporan divisi tertentu : semua pengguna
|
||||
- Melihat: Semua pengguna
|
||||
|
||||
### 14. Diskusi Divisi
|
||||
|
||||
**Deskripsi:** Forum diskusi khusus untuk masing-masing divisi
|
||||
|
||||
- **Fungsi:** Tempat berdiskusi secara internal dalam divisi
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Wakil Super Admin, Admin, Admin Divisi
|
||||
- Memberi komentar : Super Admin, Wakil Super Admin, Admin, Anggota divisi
|
||||
- Melihat: Semua pengguna
|
||||
|
||||
### 15. Tugas Divisi
|
||||
|
||||
**Deskripsi:** Fitur untuk mengelola tugas-tugas dalam masing-masing divisi
|
||||
|
||||
- **Fungsi:** Menetapkan dan melacak tugas-tugas yang harus diselesaikan oleh anggota divisi
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Wakil Super Admin, Admin, Admin Divisi
|
||||
- Mengelola detail (file, task, link, laporan) : Super Admin, Wakil Super Admin, Admin, Anggota divisi
|
||||
- Melihat: Semua pengguna
|
||||
|
||||
### 16. Dokumen Divisi
|
||||
|
||||
**Deskripsi:** Sistem manajemen dokumen untuk menyimpan dan mengelola file-file disetiap divisi
|
||||
|
||||
- **Fungsi:** Menyimpan dokumen penting dalam struktur folder disetiap divisi
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Wakil Super Admin, Admin, Anggota divisi
|
||||
- Melihat: Semua pengguna
|
||||
|
||||
### 17. Kalender/Acara Divisi
|
||||
|
||||
**Deskripsi:** Fitur untuk menjadwalkan dan mengelola acara-acara desa disetiap divisi
|
||||
|
||||
- **Fungsi:** Menjadwalkan kegiatan dan acara penting desa disetiap divisi
|
||||
- **Siapa yang bisa mengakses:**
|
||||
- Pembuatan/Edit/Hapus: Super Admin, Wakil Super Admin, Admin, Anggota divisi
|
||||
- Melihat: Semua pengguna
|
||||
- Riwayat: Semua pengguna
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Masalah Login
|
||||
|
||||
- Pastikan nomor telepon yang dimasukkan sudah benar dan terdaftar
|
||||
- Pastikan koneksi internet stabil saat menerima OTP
|
||||
- Jika tidak menerima OTP, coba kirim ulang setelah beberapa menit
|
||||
|
||||
### Tidak Bisa Mengakses Fitur Tertentu
|
||||
|
||||
- Pastikan peran Anda memiliki hak akses ke fitur tersebut
|
||||
- Beberapa fitur hanya tersedia untuk peran tertentu (misalnya Admin ke atas)
|
||||
|
||||
### Lupa Password
|
||||
|
||||
- Aplikasi ini menggunakan sistem login OTP, jadi tidak ada password yang disimpan
|
||||
- Cukup gunakan nomor telepon dan minta OTP kembali
|
||||
|
||||
## Dukungan dan Bantuan
|
||||
|
||||
Jika Anda mengalami masalah atau memiliki pertanyaan tentang penggunaan aplikasi, silakan hubungi tim pengembang aplikasi.
|
||||
153
QWEN.md
Normal file
153
QWEN.md
Normal file
@@ -0,0 +1,153 @@
|
||||
# Desa+ Mobile Application - Project Overview
|
||||
|
||||
## Project Summary
|
||||
Desa+ is a comprehensive village/desa management mobile application built with React Native and Expo. The application serves as a digital platform for village administration, community communication, and information management. It provides various features to facilitate village governance, resident communication, and administrative tasks.
|
||||
|
||||
## Architecture & Technology Stack
|
||||
- **Framework**: React Native with Expo (SDK 53)
|
||||
- **State Management**: Redux Toolkit with React-Redux
|
||||
- **Navigation**: Expo Router with React Navigation
|
||||
- **Backend Services**: Firebase (Authentication, Realtime Database, Cloud Messaging)
|
||||
- **UI Components**: Custom-built components with React Native elements
|
||||
- **Language**: TypeScript for type safety
|
||||
- **Build System**: EAS (Expo Application Service) for builds and deployments
|
||||
|
||||
## Key Features
|
||||
- Announcement and village information system
|
||||
- Community discussion forums
|
||||
- Village event calendar
|
||||
- Document management and archiving
|
||||
- Project and task management
|
||||
- Member and organizational structure management
|
||||
- Push notifications for important updates
|
||||
- Verification and authentication features
|
||||
|
||||
## Project Structure
|
||||
```
|
||||
├── app/ # Application routes and pages (Expo Router)
|
||||
│ ├── (application)/ # Main application screens
|
||||
│ │ ├── announcement/
|
||||
│ │ ├── banner/
|
||||
│ │ ├── discussion/
|
||||
│ │ ├── division/
|
||||
│ │ ├── group/
|
||||
│ │ ├── member/
|
||||
│ │ ├── position/
|
||||
│ │ ├── project/
|
||||
│ │ ├── _layout.tsx
|
||||
│ │ ├── edit-profile.tsx
|
||||
│ │ ├── feature.tsx
|
||||
│ │ ├── home.tsx
|
||||
│ │ ├── notification.tsx
|
||||
│ │ ├── profile.tsx
|
||||
│ │ └── search.tsx
|
||||
│ ├── _layout.tsx # Root layout
|
||||
│ ├── index.tsx # Splash/login screen
|
||||
│ ├── verification.tsx # OTP verification screen
|
||||
├── components/ # Reusable UI components
|
||||
│ ├── announcement/
|
||||
│ ├── auth/
|
||||
│ ├── banner/
|
||||
│ ├── calendar/
|
||||
│ ├── discussion/
|
||||
│ ├── division/
|
||||
│ ├── document/
|
||||
│ ├── group/
|
||||
│ ├── home/
|
||||
│ ├── member/
|
||||
│ ├── position/
|
||||
│ ├── project/
|
||||
│ ├── task/
|
||||
│ ├── alertKonfirmasi.ts
|
||||
│ ├── AppHeader.tsx
|
||||
│ ├── Text.tsx
|
||||
│ └── ... (many more components)
|
||||
├── constants/ # Constants and styles
|
||||
│ ├── Colors.ts
|
||||
│ ├── ColorsStatus.ts
|
||||
│ ├── ConstEnv.ts
|
||||
│ ├── Headers.ts
|
||||
│ ├── RoleUser.ts
|
||||
│ ├── Styles.ts
|
||||
│ └── ... (other constants)
|
||||
├── assets/ # Static assets (images, fonts)
|
||||
├── lib/ # Business logic and API utilities
|
||||
├── providers/ # Context providers (AuthProvider)
|
||||
├── android/ # Android native code
|
||||
├── ios/ # iOS native code
|
||||
├── scripts/ # Build and utility scripts
|
||||
├── index.js # Entry point
|
||||
├── app.config.js # Expo configuration
|
||||
├── package.json # Dependencies and scripts
|
||||
└── eas.json # EAS build configuration
|
||||
```
|
||||
|
||||
## Environment Configuration
|
||||
The application uses environment variables defined in a `.env` file:
|
||||
- `URL_API` - API endpoint
|
||||
- `URL_OTP` - OTP service endpoint
|
||||
- `URL_STORAGE` - Storage endpoint
|
||||
- `URL_FIREBASE_DB` - Firebase database URL
|
||||
- `PASS_ENC` - Encryption password
|
||||
- `WA_SERVER_TOKEN` - WhatsApp server token
|
||||
- `IOS_GOOGLE_SERVICES_FILE` - Path to iOS Google services file
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Development
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Run the development server:
|
||||
```bash
|
||||
npx expo start
|
||||
```
|
||||
|
||||
3. For platform-specific builds:
|
||||
- Android: `npm run android`
|
||||
- iOS: `npm run ios`
|
||||
- Web: `npm run web`
|
||||
|
||||
### Production Builds
|
||||
- Android: `npm run build:android` (uses EAS to build an app bundle)
|
||||
|
||||
### Testing
|
||||
- Run tests: `npm run test`
|
||||
|
||||
### Linting
|
||||
- Check code quality: `npm run lint`
|
||||
|
||||
## Key Dependencies
|
||||
- `@react-native-firebase/*` - Firebase integration
|
||||
- `@react-navigation/*` - Navigation solutions
|
||||
- `@reduxjs/toolkit` - State management
|
||||
- `expo-router` - File-based routing
|
||||
- `react-native-gesture-handler` - Touch gesture support
|
||||
- `react-native-reanimated` - Advanced animations
|
||||
- `react-native-svg` - SVG rendering support
|
||||
|
||||
## Development Conventions
|
||||
- Uses TypeScript for type safety
|
||||
- Implements custom styling through the Styles.ts constant file
|
||||
- Follows Expo's file-based routing convention
|
||||
- Uses Redux Toolkit for centralized state management
|
||||
- Implements custom components in the components directory
|
||||
- Uses absolute imports with @/ alias (e.g., "@/components/...")
|
||||
- Implements Firebase for authentication and real-time data
|
||||
|
||||
## Deployment
|
||||
- Uses EAS for building and submitting to app stores
|
||||
- Supports both Android (APK and App Bundle) and iOS (TestFlight/App Store)
|
||||
- Configured for internal testing, preview, and production distributions
|
||||
|
||||
## Special Features
|
||||
- Background message handling for push notifications
|
||||
- Biometric authentication support
|
||||
- Image picking and media library access
|
||||
- Document picker functionality
|
||||
- Date/time pickers with localization
|
||||
- Custom toast notifications
|
||||
- Carousel components for featured content
|
||||
- Data visualization with charts
|
||||
124
README.md
124
README.md
@@ -1,50 +1,114 @@
|
||||
# Welcome to your Expo app 👋
|
||||
# Desa+
|
||||
|
||||
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
|
||||
Desa+ adalah aplikasi mobile berbasis React Native yang dikembangkan dengan Expo untuk membantu pengelolaan dan komunikasi di lingkungan desa/kelurahan. Aplikasi ini menyediakan berbagai fitur untuk memudahkan administrasi desa, komunikasi antar warga, dan pengelolaan informasi penting.
|
||||
|
||||
## Get started
|
||||
## Fitur Utama
|
||||
|
||||
1. Install dependencies
|
||||
- 📢 Pengumuman dan informasi desa
|
||||
- 💬 Forum diskusi komunitas
|
||||
- 📅 Kalender kegiatan desa
|
||||
- 📄 Dokumentasi dan arsip desa
|
||||
- 📊 Pengelolaan proyek dan tugas desa
|
||||
- 👥 Manajemen anggota dan struktur organisasi
|
||||
- 📱 Notifikasi push untuk informasi penting
|
||||
- 🎯 Fitur verifikasi dan otentikasi
|
||||
|
||||
## Teknologi yang Digunakan
|
||||
|
||||
- [React Native](https://reactnative.dev/) - Framework mobile cross-platform
|
||||
- [Expo](https://expo.dev/) - Platform pengembangan aplikasi React Native
|
||||
- [Firebase](https://firebase.google.com/) - Backend services (Authentication, Realtime Database, Cloud Messaging)
|
||||
- [Redux Toolkit](https://redux-toolkit.js.org/) - State management
|
||||
- [React Navigation](https://reactnavigation.org/) - Navigasi aplikasi
|
||||
- [TypeScript](https://www.typescriptlang.org/) - Type safety
|
||||
|
||||
## Instalasi
|
||||
|
||||
1. Clone repository ini
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd mobile-darmasaba
|
||||
```
|
||||
|
||||
2. Install dependencies
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Start the app
|
||||
|
||||
```bash
|
||||
npx expo start
|
||||
3. Konfigurasi environment variables
|
||||
Buat file `.env` di root direktori dan tambahkan variabel berikut:
|
||||
```
|
||||
URL_API=<api-endpoint>
|
||||
URL_OTP=<otp-service-endpoint>
|
||||
URL_STORAGE=<storage-endpoint>
|
||||
URL_FIREBASE_DB=<firebase-database-url>
|
||||
PASS_ENC=<encryption-password>
|
||||
WA_SERVER_TOKEN=<whatsapp-server-token>
|
||||
IOS_GOOGLE_SERVICES_FILE=<path-to-ios-google-services>
|
||||
```
|
||||
|
||||
In the output, you'll find options to open the app in a
|
||||
4. Jalankan aplikasi
|
||||
```bash
|
||||
npx expo start
|
||||
```
|
||||
|
||||
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
|
||||
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
|
||||
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
|
||||
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
|
||||
## Struktur Proyek
|
||||
|
||||
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
|
||||
|
||||
## Get a fresh project
|
||||
|
||||
When you're ready, run:
|
||||
|
||||
```bash
|
||||
npm run reset-project
|
||||
```
|
||||
├── app/ # File-file halaman utama
|
||||
├── components/ # Komponen reusable
|
||||
│ ├── announcement/ # Komponen pengumuman
|
||||
│ ├── auth/ # Komponen otentikasi
|
||||
│ ├── discussion/ # Komponen forum diskusi
|
||||
│ ├── document/ # Komponen dokumentasi
|
||||
│ ├── project/ # Komponen pengelolaan proyek
|
||||
│ └── ...
|
||||
├── assets/ # Gambar dan aset statis
|
||||
├── constants/ # Konstanta global
|
||||
├── lib/ # Library dan utilitas
|
||||
└── ...
|
||||
```
|
||||
|
||||
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
|
||||
## Platform Support
|
||||
|
||||
## Learn more
|
||||
Aplikasi ini didukung untuk:
|
||||
- ✅ Android
|
||||
- ✅ iOS
|
||||
- ❌ Web (belum dioptimalkan)
|
||||
|
||||
To learn more about developing your project with Expo, look at the following resources:
|
||||
## Development
|
||||
|
||||
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
|
||||
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
|
||||
Untuk menjalankan aplikasi di masing-masing platform:
|
||||
|
||||
## Join the community
|
||||
### Android
|
||||
```bash
|
||||
npm run android
|
||||
```
|
||||
|
||||
Join our community of developers creating universal apps.
|
||||
### iOS
|
||||
```bash
|
||||
npm run ios
|
||||
```
|
||||
|
||||
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
|
||||
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.
|
||||
### Build Production
|
||||
|
||||
Untuk membuat build production Android:
|
||||
```bash
|
||||
npm run build:android
|
||||
```
|
||||
|
||||
## Kontribusi
|
||||
|
||||
1. Fork repository ini
|
||||
2. Buat branch fitur baru (`git checkout -b fitur/NamaFitur`)
|
||||
3. Commit perubahan Anda (`git commit -m 'Tambahkan fitur NamaFitur'`)
|
||||
4. Push ke branch (`git push origin fitur/NamaFitur`)
|
||||
5. Buat pull request
|
||||
|
||||
## Lisensi
|
||||
|
||||
Proyek ini dilisensikan di bawah lisensi MIT - lihat file [LICENSE](LICENSE) untuk detail selengkapnya.
|
||||
|
||||
## Dukungan
|
||||
|
||||
Jika Anda menemukan masalah atau memiliki pertanyaan, silakan buka issue di repository ini.
|
||||
|
||||
@@ -79,6 +79,12 @@ export default {
|
||||
URL_FIREBASE_DB: process.env.URL_FIREBASE_DB,
|
||||
PASS_ENC: process.env.PASS_ENC,
|
||||
WA_SERVER_TOKEN: process.env.WA_SERVER_TOKEN,
|
||||
FIREBASE_API_KEY: process.env.FIREBASE_API_KEY,
|
||||
FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN,
|
||||
FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID,
|
||||
FIREBASE_STORAGE_BUCKET: process.env.FIREBASE_STORAGE_BUCKET,
|
||||
FIREBASE_MESSAGING_SENDER_ID: process.env.FIREBASE_MESSAGING_SENDER_ID,
|
||||
FIREBASE_APP_ID: process.env.FIREBASE_APP_ID,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import HeaderRightAnnouncementList from "@/components/announcement/headerAnnouncementList";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import HeaderDiscussionGeneral from "@/components/discussion_general/headerDiscussionGeneral";
|
||||
import HeaderRightDivisionList from "@/components/division/headerDivisionList";
|
||||
import HeaderRightGroupList from "@/components/group/headerGroupList";
|
||||
@@ -8,7 +8,6 @@ import HeaderRightPositionList from "@/components/position/headerRightPositionLi
|
||||
import HeaderRightProjectList from "@/components/project/headerProjectList";
|
||||
import Text from "@/components/Text";
|
||||
import ToastCustom from "@/components/toastCustom";
|
||||
import { Headers } from "@/constants/Headers";
|
||||
import { apiReadOneNotification } from "@/lib/api";
|
||||
import { pushToPage } from "@/lib/pushToPage";
|
||||
import store from "@/lib/store";
|
||||
@@ -91,60 +90,133 @@ export default function RootLayout() {
|
||||
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<Stack screenOptions={Headers.shadow} >
|
||||
<Stack screenOptions={{
|
||||
headerShown: true,
|
||||
animation: "slide_from_right",
|
||||
|
||||
// ⬇️ PENTING BANGET
|
||||
animationTypeForReplace: "pop",
|
||||
fullScreenGestureEnabled: true,
|
||||
gestureEnabled: true,
|
||||
}} >
|
||||
<Stack.Screen name="home" options={{ title: 'Home' }} />
|
||||
<Stack.Screen name="feature" options={{ title: 'Fitur' }} />
|
||||
<Stack.Screen name="search" options={{ title: 'Pencarian' }} />
|
||||
<Stack.Screen name="notification" options={{
|
||||
title: 'Notifikasi',
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Notifikasi',
|
||||
headerTitleAlign: 'center'
|
||||
headerTitleAlign: 'center',
|
||||
header: () => (
|
||||
<AppHeader title="Notifikasi" showBack={true} onPressLeft={() => router.back()} />
|
||||
)
|
||||
}} />
|
||||
<Stack.Screen name="profile" options={{ title: 'Profile' }} />
|
||||
<Stack.Screen name="setting/index" options={{
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
title: 'Pengaturan',
|
||||
headerTitleAlign: 'center',
|
||||
// headerRight: () => <HeaderRightProjectList />
|
||||
header: () => (
|
||||
<AppHeader title="Pengaturan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
/>
|
||||
)
|
||||
}} />
|
||||
<Stack.Screen name="member/index" options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
title: 'Anggota',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderMemberList />
|
||||
// headerRight: () => <HeaderMemberList />
|
||||
header: () => (
|
||||
<AppHeader title="Anggota"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<HeaderMemberList />}
|
||||
/>
|
||||
)
|
||||
}} />
|
||||
<Stack.Screen name="discussion/index" options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
title: 'Diskusi Umum',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderDiscussionGeneral />
|
||||
// headerRight: () => <HeaderDiscussionGeneral />
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Diskusi Umum"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<HeaderDiscussionGeneral />}
|
||||
/>
|
||||
)
|
||||
}} />
|
||||
<Stack.Screen name="project/index" options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
title: 'Kegiatan',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderRightProjectList />
|
||||
// headerRight: () => <HeaderRightProjectList />
|
||||
header: () => (
|
||||
<AppHeader title="Kegiatan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<HeaderRightProjectList />}
|
||||
/>
|
||||
)
|
||||
}} />
|
||||
<Stack.Screen name="division/index" options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
title: 'Divisi',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderRightDivisionList />
|
||||
// headerRight: () => <HeaderRightDivisionList />
|
||||
header: () => (
|
||||
<AppHeader title="Divisi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<HeaderRightDivisionList />}
|
||||
/>
|
||||
)
|
||||
}} />
|
||||
<Stack.Screen name="division/[id]/(fitur-division)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="group/index" options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Lembaga Desa',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderRightGroupList />
|
||||
// headerRight: () => <HeaderRightGroupList />
|
||||
header: () => (
|
||||
<AppHeader title="Lembaga Desa"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<HeaderRightGroupList />}
|
||||
/>
|
||||
)
|
||||
}} />
|
||||
<Stack.Screen name="position/index" options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Jabatan',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderRightPositionList />
|
||||
// headerRight: () => <HeaderRightPositionList />
|
||||
header: () => (
|
||||
<AppHeader title="Jabatan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<HeaderRightPositionList />}
|
||||
/>
|
||||
)
|
||||
}} />
|
||||
<Stack.Screen name="announcement/index"
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Pengumuman',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderRightAnnouncementList />
|
||||
// headerRight: () => <HeaderRightAnnouncementList />
|
||||
header: () => (
|
||||
<AppHeader title="Pengumuman"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<HeaderRightAnnouncementList />}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -1,36 +1,61 @@
|
||||
import HeaderRightAnnouncementDetail from "@/components/announcement/headerAnnouncementDetail";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import Skeleton from "@/components/skeleton";
|
||||
import Text from '@/components/Text';
|
||||
import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import { isImageFile } from "@/constants/FileExtensions";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetAnnouncementOne } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Entypo, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons";
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import { startActivityAsync } from 'expo-intent-launcher';
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import * as Sharing from 'expo-sharing';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Alert, Dimensions, Platform, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import { Dimensions, Platform, Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import ImageViewing from 'react-native-image-viewing';
|
||||
import * as mime from 'react-native-mime-types';
|
||||
import RenderHTML from 'react-native-render-html';
|
||||
import Toast from "react-native-toast-message";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
type Props = {
|
||||
id: string
|
||||
title: string
|
||||
desc: string
|
||||
// Define TypeScript interfaces for better type safety
|
||||
interface AnnouncementData {
|
||||
id: string;
|
||||
title: string;
|
||||
desc: string;
|
||||
}
|
||||
|
||||
interface FileData {
|
||||
id: string;
|
||||
idStorage: string;
|
||||
name: string;
|
||||
extension: string;
|
||||
}
|
||||
|
||||
interface MemberData {
|
||||
group: string;
|
||||
division: string;
|
||||
}
|
||||
|
||||
interface ApiResponse {
|
||||
success: boolean;
|
||||
data: AnnouncementData;
|
||||
member: Record<string, MemberData[]>;
|
||||
file: FileData[];
|
||||
message: string;
|
||||
}
|
||||
|
||||
export default function DetailAnnouncement() {
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const [data, setData] = useState<Props>({ id: '', title: '', desc: '' })
|
||||
const [dataMember, setDataMember] = useState<any>({})
|
||||
const [dataFile, setDataFile] = useState<{ id: string; idStorage: string; name: string; extension: string }[]>([])
|
||||
const { colors } = useTheme();
|
||||
const [data, setData] = useState<AnnouncementData>({ id: '', title: '', desc: '' })
|
||||
const [dataMember, setDataMember] = useState<Record<string, MemberData[]>>({})
|
||||
const [dataFile, setDataFile] = useState<FileData[]>([])
|
||||
const update = useSelector((state: any) => state.announcementUpdate)
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const contentWidth = Dimensions.get('window').width
|
||||
@@ -38,13 +63,25 @@ export default function DetailAnnouncement() {
|
||||
const arrSkeleton = Array.from({ length: 2 }, (_, index) => index)
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const [loadingOpen, setLoadingOpen] = useState(false)
|
||||
const [preview, setPreview] = useState(false)
|
||||
const [chooseFile, setChooseFile] = useState<FileData>()
|
||||
|
||||
/**
|
||||
* Opens the image preview modal for the selected image file
|
||||
* @param item The file data object containing image information
|
||||
*/
|
||||
|
||||
function handleChooseFile(item: FileData) {
|
||||
setChooseFile(item)
|
||||
setPreview(true)
|
||||
}
|
||||
|
||||
|
||||
async function handleLoad(loading: boolean) {
|
||||
try {
|
||||
setLoading(loading)
|
||||
const hasil = await decryptToken(String(token?.current))
|
||||
const response = await apiGetAnnouncementOne({ id: id, user: hasil })
|
||||
const response: ApiResponse = await apiGetAnnouncementOne({ id: id, user: hasil })
|
||||
if (response.success) {
|
||||
setData(response.data)
|
||||
setDataMember(response.member)
|
||||
@@ -68,30 +105,47 @@ export default function DetailAnnouncement() {
|
||||
handleLoad(true)
|
||||
}, [])
|
||||
|
||||
/**
|
||||
* Checks if a string contains HTML tags
|
||||
* @param text The text to check for HTML tags
|
||||
* @returns True if the text contains HTML tags, false otherwise
|
||||
*/
|
||||
function hasHtmlTags(text: string) {
|
||||
const htmlRegex = /<[a-z][\s\S]*>/i;
|
||||
return htmlRegex.test(text);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles pull-to-refresh functionality
|
||||
* Reloads the announcement data without showing loading indicators
|
||||
*/
|
||||
const handleRefresh = async () => {
|
||||
setRefreshing(true)
|
||||
handleLoad(false)
|
||||
// Simulate network request delay for better UX
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
setRefreshing(false)
|
||||
};
|
||||
|
||||
const openFile = (item: { idStorage: string; name: string; extension: string }) => {
|
||||
if (Platform.OS == 'android') setLoadingOpen(true)
|
||||
let remoteUrl = ConstEnv.url_storage + '/files/' + item.idStorage;
|
||||
const fileName = item.name + '.' + item.extension;
|
||||
let localPath = `${FileSystem.documentDirectory}/${fileName}`;
|
||||
const mimeType = mime.lookup(fileName)
|
||||
const openFile = async (item: FileData) => {
|
||||
try {
|
||||
setLoadingOpen(true);
|
||||
const remoteUrl = ConstEnv.url_storage + '/files/' + item.idStorage;
|
||||
const fileName = item.name + '.' + item.extension;
|
||||
const localPath = `${FileSystem.documentDirectory}/${fileName}`;
|
||||
const mimeType = mime.lookup(fileName);
|
||||
|
||||
// Download the file
|
||||
const downloadResult = await FileSystem.downloadAsync(remoteUrl, localPath);
|
||||
|
||||
if (downloadResult.status !== 200) {
|
||||
throw new Error(`Download failed with status ${downloadResult.status}`);
|
||||
}
|
||||
|
||||
const contentURL = await FileSystem.getContentUriAsync(downloadResult.uri);
|
||||
|
||||
FileSystem.downloadAsync(remoteUrl, localPath).then(async ({ uri }) => {
|
||||
const contentURL = await FileSystem.getContentUriAsync(uri);
|
||||
setLoadingOpen(false)
|
||||
try {
|
||||
if (Platform.OS == 'android') {
|
||||
if (Platform.OS === 'android') {
|
||||
await startActivityAsync(
|
||||
'android.intent.action.VIEW',
|
||||
{
|
||||
@@ -100,39 +154,58 @@ export default function DetailAnnouncement() {
|
||||
type: mimeType as string,
|
||||
}
|
||||
);
|
||||
} else if (Platform.OS == 'ios') {
|
||||
Sharing.shareAsync(localPath);
|
||||
} else if (Platform.OS === 'ios') {
|
||||
await Sharing.shareAsync(localPath);
|
||||
}
|
||||
} catch (error) {
|
||||
Alert.alert('INFO', 'Gagal membuka file, tidak ada aplikasi yang dapat membuka file ini');
|
||||
} finally {
|
||||
if (Platform.OS == 'android') setLoadingOpen(false)
|
||||
} catch (openError) {
|
||||
console.error('Error opening file:', openError);
|
||||
Toast.show({
|
||||
type: 'error',
|
||||
text1: 'Tidak ada aplikasi yang dapat membuka file ini'
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error downloading or opening file:', error);
|
||||
Toast.show({
|
||||
type: 'error',
|
||||
text1: 'Gagal membuka file',
|
||||
text2: 'Silakan coba lagi nanti'
|
||||
});
|
||||
} finally {
|
||||
setLoadingOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Pengumuman',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => entityUser.role != 'user' && entityUser.role != 'coadmin' ? <HeaderRightAnnouncementDetail id={id} /> : <></>,
|
||||
// headerRight: () => entityUser.role != 'user' && entityUser.role != 'coadmin' ? <HeaderRightAnnouncementDetail id={id} /> : <></>,
|
||||
header: () => (
|
||||
<AppHeader title="Pengumuman"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={entityUser.role != 'user' && entityUser.role != 'coadmin' ? <HeaderRightAnnouncementDetail id={id} /> : <></>}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={() => handleRefresh()}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
<View style={[Styles.wrapPaper]}>
|
||||
<View style={[Styles.p15, Styles.mb50]}>
|
||||
<View style={[Styles.wrapPaper, Styles.borderAll, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
{
|
||||
loading ?
|
||||
<View>
|
||||
@@ -149,7 +222,7 @@ export default function DetailAnnouncement() {
|
||||
:
|
||||
<>
|
||||
<View style={[Styles.rowItemsCenter, { alignItems: 'flex-start' }]}>
|
||||
<MaterialIcons name="campaign" size={25} color="black" style={[Styles.mr05]} />
|
||||
<MaterialIcons name="campaign" size={25} color={colors.text} style={[Styles.mr05]} />
|
||||
<Text style={[Styles.textDefaultSemiBold, Styles.w90, Styles.mt02]}>{data?.title}</Text>
|
||||
</View>
|
||||
<View style={[Styles.mt10]}>
|
||||
@@ -158,7 +231,7 @@ export default function DetailAnnouncement() {
|
||||
<RenderHTML
|
||||
contentWidth={contentWidth}
|
||||
source={{ html: data?.desc }}
|
||||
baseStyle={{ color: 'black' }}
|
||||
baseStyle={{ color: colors.text }}
|
||||
/>
|
||||
:
|
||||
<Text>{data?.desc}</Text>
|
||||
@@ -170,24 +243,32 @@ export default function DetailAnnouncement() {
|
||||
</View>
|
||||
{
|
||||
dataFile.length > 0 && (
|
||||
<View style={[Styles.wrapPaper, Styles.mt10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.borderAll, Styles.mt10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<View style={[Styles.mb05]}>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>File</Text>
|
||||
</View>
|
||||
{dataFile.map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
key={`${item.id}-${index}`}
|
||||
borderType="bottom"
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
title={item.name}
|
||||
icon={<MaterialCommunityIcons
|
||||
name={isImageFile(item.extension) ? "file-image-outline" : "file-document-outline"}
|
||||
size={25}
|
||||
color={colors.text}
|
||||
/>}
|
||||
title={item.name + '.' + item.extension}
|
||||
titleWeight="normal"
|
||||
onPress={() => { openFile({ idStorage: item.idStorage, name: item.name, extension: item.extension }) }}
|
||||
onPress={() => {
|
||||
isImageFile(item.extension) ?
|
||||
handleChooseFile(item)
|
||||
: openFile(item)
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
<View style={[Styles.wrapPaper, Styles.mt10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.borderAll, Styles.mt10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
{
|
||||
loading ?
|
||||
arrSkeleton.map((item, index) => {
|
||||
@@ -208,7 +289,7 @@ export default function DetailAnnouncement() {
|
||||
dataMember[v].map((item: any, x: any) => {
|
||||
return (
|
||||
<View key={x} style={[Styles.rowItemsCenter, Styles.w90]}>
|
||||
<Entypo name="dot-single" size={24} color="black" />
|
||||
<Entypo name="dot-single" size={24} color={colors.text} />
|
||||
<Text style={[Styles.textDefault]} numberOfLines={1} ellipsizeMode='tail'>{item.division}</Text>
|
||||
</View>
|
||||
)
|
||||
@@ -222,6 +303,45 @@ export default function DetailAnnouncement() {
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
<ImageViewing
|
||||
images={[{ uri: `${ConstEnv.url_storage}/files/${chooseFile?.idStorage}` }]}
|
||||
imageIndex={0}
|
||||
visible={preview}
|
||||
onRequestClose={() => setPreview(false)}
|
||||
doubleTapToZoomEnabled
|
||||
HeaderComponent={({ imageIndex }) => (
|
||||
<View style={[Styles.headerModalViewImg]}>
|
||||
{/* CLOSE */}
|
||||
<Pressable
|
||||
onPress={() => setPreview(false)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Close image viewer"
|
||||
>
|
||||
<Text style={{ color: 'white', fontSize: 26 }}>✕</Text>
|
||||
</Pressable>
|
||||
|
||||
{/* MENU */}
|
||||
<Pressable
|
||||
onPress={() => chooseFile && openFile(chooseFile)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Download or share image"
|
||||
disabled={loadingOpen}
|
||||
>
|
||||
<Text style={{ color: loadingOpen ? 'gray' : 'white', fontSize: 26 }}>⋯</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
)}
|
||||
FooterComponent={({ imageIndex }) => (
|
||||
<View style={{
|
||||
paddingBottom: 20,
|
||||
paddingHorizontal: 16,
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Text style={{ color: 'white', fontSize: 16 }}>{chooseFile?.name}.{chooseFile?.extension}</Text>
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
@@ -12,10 +12,11 @@ import Styles from "@/constants/Styles";
|
||||
import { setUpdateAnnouncement } from "@/lib/announcementUpdate";
|
||||
import { apiCreateAnnouncement } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Entypo, Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import * as DocumentPicker from "expo-document-picker";
|
||||
import { router, Stack } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { SafeAreaView, ScrollView, StyleSheet, View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
@@ -24,6 +25,7 @@ export default function CreateAnnouncement() {
|
||||
const dispatch = useDispatch()
|
||||
const update = useSelector((state: any) => state.announcementUpdate)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const [disableBtn, setDisableBtn] = useState(true);
|
||||
const [modalDivisi, setModalDivisi] = useState(false);
|
||||
const [divisionMember, setDivisionMember] = useState<any>([])
|
||||
@@ -129,35 +131,53 @@ export default function CreateAnnouncement() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah Pengumuman",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || divisionMember.length == 0 || loading ? true : false}
|
||||
category="create"
|
||||
onPress={() => {
|
||||
divisionMember.length == 0
|
||||
? Toast.show({ type: 'small', text1: "Anda belum memilih divisi", })
|
||||
: handleCreate();
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disableBtn || divisionMember.length == 0 || loading ? true : false}
|
||||
// category="create"
|
||||
// onPress={() => {
|
||||
// divisionMember.length == 0
|
||||
// ? Toast.show({ type: 'small', text1: "Anda belum memilih divisi", })
|
||||
// : handleCreate();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Pengumuman"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || divisionMember.length == 0 || loading ? true : false}
|
||||
category="create"
|
||||
onPress={() => {
|
||||
divisionMember.length == 0
|
||||
? Toast.show({ type: 'small', text1: "Anda belum memilih divisi", })
|
||||
: handleCreate();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<LoadingOverlay visible={loading} />
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
<InputForm
|
||||
@@ -166,6 +186,7 @@ export default function CreateAnnouncement() {
|
||||
placeholder="Judul Pengumuman"
|
||||
required
|
||||
error={error.title}
|
||||
bg={colors.card}
|
||||
errorText="Judul harus diisi"
|
||||
onChange={(val) => validationForm("title", val)}
|
||||
/>
|
||||
@@ -175,6 +196,7 @@ export default function CreateAnnouncement() {
|
||||
placeholder="Deskripsi Pengumuman"
|
||||
required
|
||||
error={error.desc}
|
||||
bg={colors.card}
|
||||
errorText="Pengumuman harus diisi"
|
||||
onChange={(val) => validationForm("desc", val)}
|
||||
multiline
|
||||
@@ -183,15 +205,16 @@ export default function CreateAnnouncement() {
|
||||
{
|
||||
fileForm.length > 0
|
||||
&&
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10, Styles.mb10]}>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, Styles.mb10, { borderColor: colors.icon + '20' }]}>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>File</Text>
|
||||
{
|
||||
fileForm.map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType={fileForm.length > 1 ? "bottom" : "none"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
borderType={fileForm.length - 1 == index ? "none" : "bottom"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name}
|
||||
bgColor="transparent"
|
||||
titleWeight="normal"
|
||||
onPress={() => { setIndexDelFile(index); setModalFile(true) }}
|
||||
/>
|
||||
@@ -210,7 +233,7 @@ export default function CreateAnnouncement() {
|
||||
{
|
||||
divisionMember.length > 0
|
||||
&&
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10]}>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, { borderColor: colors.icon + '20' }]}>
|
||||
{
|
||||
divisionMember.map((item: { name: any; Division: any }, index: any) => {
|
||||
return (
|
||||
@@ -219,7 +242,7 @@ export default function CreateAnnouncement() {
|
||||
{
|
||||
item.Division.map((division: any, i: any) => (
|
||||
<View key={i} style={[Styles.rowItemsCenter, Styles.w90]}>
|
||||
<Entypo name="dot-single" size={24} color="black" />
|
||||
<Entypo name="dot-single" size={24} color={colors.text} />
|
||||
<Text style={[Styles.textDefault]} numberOfLines={1} ellipsizeMode='tail'>{division.name}</Text>
|
||||
</View>
|
||||
))
|
||||
@@ -248,7 +271,7 @@ export default function CreateAnnouncement() {
|
||||
<DrawerBottom animation="slide" isVisible={isModalFile} setVisible={setModalFile} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => { deleteFile(indexDelFile) }}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
@@ -12,6 +12,7 @@ import Styles from "@/constants/Styles";
|
||||
import { setUpdateAnnouncement } from "@/lib/announcementUpdate";
|
||||
import { apiEditAnnouncement, apiGetAnnouncementOne } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Entypo, Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import * as DocumentPicker from "expo-document-picker";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
@@ -35,6 +36,7 @@ export default function EditAnnouncement() {
|
||||
const dispatch = useDispatch()
|
||||
const update = useSelector((state: any) => state.announcementUpdate)
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const { colors } = useTheme();
|
||||
const [modalDivisi, setModalDivisi] = useState(false);
|
||||
const [disableBtn, setDisableBtn] = useState(true);
|
||||
const [dataMember, setDataMember] = useState<any>([]);
|
||||
@@ -180,35 +182,53 @@ export default function EditAnnouncement() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Edit Pengumuman",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading ? true : false}
|
||||
category="update"
|
||||
onPress={() => {
|
||||
dataMember.length == 0
|
||||
? Toast.show({ type: 'small', text1: "Anda belum memilih divisi", })
|
||||
: handleEdit();
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disableBtn || loading ? true : false}
|
||||
// category="update"
|
||||
// onPress={() => {
|
||||
// dataMember.length == 0
|
||||
// ? Toast.show({ type: 'small', text1: "Anda belum memilih divisi", })
|
||||
// : handleEdit();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Pengumuman"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading ? true : false}
|
||||
category="update"
|
||||
onPress={() => {
|
||||
dataMember.length == 0
|
||||
? Toast.show({ type: 'small', text1: "Anda belum memilih divisi", })
|
||||
: handleEdit();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<LoadingOverlay visible={loading} />
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
<InputForm
|
||||
@@ -217,6 +237,7 @@ export default function EditAnnouncement() {
|
||||
placeholder="Judul Pengumuman"
|
||||
required
|
||||
error={error.title}
|
||||
bg={colors.card}
|
||||
errorText="Judul harus diisi"
|
||||
onChange={(val) => validationForm("title", val)}
|
||||
value={dataForm.title}
|
||||
@@ -227,6 +248,7 @@ export default function EditAnnouncement() {
|
||||
placeholder="Deskripsi Pengumuman"
|
||||
required
|
||||
error={error.desc}
|
||||
bg={colors.card}
|
||||
errorText="Pengumuman harus diisi"
|
||||
onChange={(val) => validationForm("desc", val)}
|
||||
value={dataForm.desc}
|
||||
@@ -236,16 +258,17 @@ export default function EditAnnouncement() {
|
||||
{
|
||||
(fileForm.length > 0 || dataFile.filter((val) => !val.delete).length > 0)
|
||||
&&
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10, Styles.mb10]}>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, Styles.mb10, { borderColor: colors.icon + '20' }]}>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>File</Text>
|
||||
{
|
||||
dataFile.filter((val) => !val.delete).map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType={(fileForm.length + dataFile.length) > 1 ? "bottom" : "none"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name + '.' + item.extension}
|
||||
titleWeight="normal"
|
||||
bgColor="transparent"
|
||||
onPress={() => { setIndexDelFile({ id: item.id, cat: "oldFile" }); setModalFile(true) }}
|
||||
/>
|
||||
))
|
||||
@@ -255,9 +278,10 @@ export default function EditAnnouncement() {
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType={(fileForm.length + dataFile.length) > 1 ? "bottom" : "none"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name}
|
||||
titleWeight="normal"
|
||||
bgColor="transparent"
|
||||
onPress={() => { setIndexDelFile({ id: index, cat: "newFile" }); setModalFile(true) }}
|
||||
/>
|
||||
))
|
||||
@@ -273,7 +297,7 @@ export default function EditAnnouncement() {
|
||||
{
|
||||
dataMember.length > 0
|
||||
&&
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10]}>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, { borderColor: colors.icon + '20' }]}>
|
||||
{
|
||||
dataMember.map((item: { name: any; Division: any }, index: any) => {
|
||||
return (
|
||||
@@ -282,7 +306,7 @@ export default function EditAnnouncement() {
|
||||
{
|
||||
item.Division.map((division: any, i: any) => (
|
||||
<View key={i} style={[Styles.rowItemsCenter, Styles.w90]}>
|
||||
<Entypo name="dot-single" size={24} color="black" />
|
||||
<Entypo name="dot-single" size={24} color={colors.text} />
|
||||
<Text style={[Styles.textDefault]} numberOfLines={1} ellipsizeMode='tail'>{division.name}</Text>
|
||||
</View>
|
||||
))
|
||||
@@ -312,7 +336,7 @@ export default function EditAnnouncement() {
|
||||
<DrawerBottom animation="slide" isVisible={isModalFile} setVisible={setModalFile} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => { deleteFile(indexDelFile.id, indexDelFile.cat) }}
|
||||
/>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetAnnouncement } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { MaterialIcons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -22,6 +23,7 @@ type Props = {
|
||||
|
||||
export default function Announcement() {
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
const [search, setSearch] = useState('')
|
||||
const update = useSelector((state: any) => state.announcementUpdate)
|
||||
@@ -83,7 +85,7 @@ export default function Announcement() {
|
||||
})
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<View>
|
||||
<InputSearch onChange={setSearch} />
|
||||
</View>
|
||||
@@ -108,10 +110,11 @@ export default function Announcement() {
|
||||
key={index}
|
||||
onPress={() => { router.push(`/announcement/${item.id}`) }}
|
||||
borderType="bottom"
|
||||
bgColor="transparent"
|
||||
icon={
|
||||
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
|
||||
<MaterialIcons name="campaign" size={25} color={'#384288'} />
|
||||
</View>
|
||||
// <View style={[Styles.iconContent]}>
|
||||
<MaterialIcons name="campaign" size={25} color={'white'} />
|
||||
// </View>
|
||||
}
|
||||
title={item.title}
|
||||
desc={item.desc.replace(/<[^>]*>?/gm, '').replace(/\r?\n|\r/g, ' ')}
|
||||
@@ -127,11 +130,12 @@ export default function Announcement() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada pengumuman</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada pengumuman</Text>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import Text from "@/components/Text";
|
||||
@@ -7,6 +7,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiEditBanner, apiGetBanner, apiGetBannerOne } from "@/lib/api";
|
||||
import { setEntities } from "@/lib/bannerSlice";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Entypo } from "@expo/vector-icons";
|
||||
import * as ImagePicker from "expo-image-picker";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
@@ -24,6 +25,7 @@ import { useDispatch } from "react-redux";
|
||||
export default function EditBanner() {
|
||||
const dispatch = useDispatch();
|
||||
const { decryptToken, token } = useAuthSession();
|
||||
const { colors } = useTheme();
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const [selectedImage, setSelectedImage] = useState<
|
||||
string | undefined | { uri: string }
|
||||
@@ -112,25 +114,38 @@ export default function EditBanner() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Edit Banner",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => <ButtonSaveHeader
|
||||
disable={title == "" || error || loading ? true : false}
|
||||
onPress={() => { handleUpdateEntity() }}
|
||||
category="update" />,
|
||||
// headerRight: () => <ButtonSaveHeader
|
||||
// disable={title == "" || error || loading ? true : false}
|
||||
// onPress={() => { handleUpdateEntity() }}
|
||||
// category="update" />,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Banner"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={title == "" || error || loading ? true : false}
|
||||
onPress={() => { handleUpdateEntity() }}
|
||||
category="update" />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100]}>
|
||||
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100, { backgroundColor: colors.background }]}>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<View style={[Styles.mb15]}>
|
||||
{selectedImage != undefined ? (
|
||||
@@ -166,7 +181,7 @@ export default function EditBanner() {
|
||||
type="default"
|
||||
placeholder="Judul"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={title}
|
||||
error={error}
|
||||
onChange={onValidate}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import Text from "@/components/Text";
|
||||
@@ -6,6 +6,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiCreateBanner, apiGetBanner } from "@/lib/api";
|
||||
import { setEntities } from "@/lib/bannerSlice";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Entypo } from "@expo/vector-icons";
|
||||
import * as ImagePicker from "expo-image-picker";
|
||||
import { router, Stack } from "expo-router";
|
||||
@@ -22,6 +23,7 @@ import { useDispatch } from "react-redux";
|
||||
|
||||
export default function CreateBanner() {
|
||||
const { decryptToken, token } = useAuthSession();
|
||||
const { colors } = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
const [selectedImage, setSelectedImage] = useState<string | undefined>(
|
||||
undefined
|
||||
@@ -94,30 +96,46 @@ export default function CreateBanner() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah Banner",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={title == "" || selectedImage == undefined || error || loading ? true : false}
|
||||
category="create"
|
||||
onPress={() => {
|
||||
handleCreateEntity();
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={title == "" || selectedImage == undefined || error || loading ? true : false}
|
||||
// category="create"
|
||||
// onPress={() => {
|
||||
// handleCreateEntity();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Fitur"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={title == "" || selectedImage == undefined || error || loading ? true : false}
|
||||
category="create"
|
||||
onPress={() => {
|
||||
handleCreateEntity();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100]}>
|
||||
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100, { backgroundColor: colors.background }]}>
|
||||
<View style={[Styles.p15]}>
|
||||
<View style={[Styles.mb15]}>
|
||||
{selectedImage != undefined ? (
|
||||
@@ -149,7 +167,7 @@ export default function CreateBanner() {
|
||||
type="default"
|
||||
placeholder="Judul"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
onChange={onValidate}
|
||||
error={error}
|
||||
errorText="Judul harus diisi"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi"
|
||||
import AppHeader from "@/components/AppHeader"
|
||||
import HeaderRightBannerList from "@/components/banner/headerBannerList"
|
||||
import BorderBottomItem from "@/components/borderBottomItem"
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader"
|
||||
import DrawerBottom from "@/components/drawerBottom"
|
||||
import MenuItemRow from "@/components/menuItemRow"
|
||||
import ModalConfirmation from "@/components/ModalConfirmation"
|
||||
import ModalLoading from "@/components/modalLoading"
|
||||
import Text from "@/components/Text"
|
||||
import { ConstEnv } from "@/constants/ConstEnv"
|
||||
@@ -11,6 +11,7 @@ import Styles from "@/constants/Styles"
|
||||
import { apiDeleteBanner, apiGetBanner } from "@/lib/api"
|
||||
import { setEntities } from "@/lib/bannerSlice"
|
||||
import { useAuthSession } from "@/providers/AuthProvider"
|
||||
import { useTheme } from "@/providers/ThemeProvider"
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons"
|
||||
import * as FileSystem from 'expo-file-system'
|
||||
import { startActivityAsync } from 'expo-intent-launcher'
|
||||
@@ -32,6 +33,7 @@ type Props = {
|
||||
|
||||
export default function BannerList() {
|
||||
const { decryptToken, token } = useAuthSession()
|
||||
const { colors } = useTheme()
|
||||
const [isModal, setModal] = useState(false)
|
||||
const entities = useSelector((state: any) => state.banner)
|
||||
const [dataId, setDataId] = useState('')
|
||||
@@ -40,6 +42,7 @@ export default function BannerList() {
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const [loadingOpen, setLoadingOpen] = useState(false)
|
||||
const [viewImg, setViewImg] = useState(false)
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
|
||||
const handleDeleteEntity = async () => {
|
||||
try {
|
||||
@@ -105,13 +108,23 @@ export default function BannerList() {
|
||||
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Banner',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderRightBannerList />
|
||||
// headerRight: () => <HeaderRightBannerList />
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Banner"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<HeaderRightBannerList />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ModalLoading isVisible={loadingOpen} setVisible={setLoadingOpen} />
|
||||
@@ -120,9 +133,10 @@ export default function BannerList() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
{
|
||||
entities.length > 0
|
||||
@@ -150,7 +164,7 @@ export default function BannerList() {
|
||||
</View>
|
||||
:
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada data</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center' }]}>Tidak ada data</Text>
|
||||
</View>
|
||||
}
|
||||
|
||||
@@ -160,7 +174,7 @@ export default function BannerList() {
|
||||
<DrawerBottom animation="slide" isVisible={isModal} setVisible={() => setModal(false)} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color={colors.text} size={25} />}
|
||||
title="Edit"
|
||||
onPress={() => {
|
||||
setModal(false)
|
||||
@@ -168,7 +182,7 @@ export default function BannerList() {
|
||||
}}
|
||||
/>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="file-eye" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="file-eye" color={colors.text} size={25} />}
|
||||
title="Lihat"
|
||||
onPress={() => {
|
||||
setModal(false)
|
||||
@@ -179,15 +193,13 @@ export default function BannerList() {
|
||||
}}
|
||||
/>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => {
|
||||
setModal(false)
|
||||
AlertKonfirmasi({
|
||||
title: 'Konfirmasi',
|
||||
desc: 'Apakah anda yakin ingin menghapus data?',
|
||||
onPress: () => { handleDeleteEntity() }
|
||||
})
|
||||
setTimeout(() => {
|
||||
setShowDeleteModal(true)
|
||||
}, 600)
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -200,6 +212,19 @@ export default function BannerList() {
|
||||
onRequestClose={() => setViewImg(false)}
|
||||
doubleTapToZoomEnabled
|
||||
/>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showDeleteModal}
|
||||
title="Konfirmasi"
|
||||
message="Apakah anda yakin ingin menghapus data?"
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false)
|
||||
handleDeleteEntity()
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
confirmText="Hapus"
|
||||
cancelText="Batal"
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
@@ -1,28 +1,28 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import BorderBottomItem2 from "@/components/borderBottomItem2";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import HeaderRightDiscussionGeneralDetail from "@/components/discussion_general/headerDiscussionDetail";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import LabelStatus from "@/components/labelStatus";
|
||||
import MenuItemRow from "@/components/menuItemRow";
|
||||
import ModalConfirmation from "@/components/ModalConfirmation";
|
||||
import Skeleton from "@/components/skeleton";
|
||||
import SkeletonContent from "@/components/skeletonContent";
|
||||
import Text from '@/components/Text';
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import { regexOnlySpacesOrEnter } from "@/constants/OnlySpaceOrEnter";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiDeleteDiscussionGeneralCommentar, apiGetDiscussionGeneralOne, apiSendDiscussionGeneralCommentar, apiUpdateDiscussionGeneralCommentar } from "@/lib/api";
|
||||
import { getDB } from "@/lib/firebaseDatabase";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Feather, Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons";
|
||||
import { ref } from '@react-native-firebase/database';
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { KeyboardAvoidingView, Platform, Pressable, RefreshControl, ScrollView, View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
import { useSelector } from "react-redux";
|
||||
@@ -56,6 +56,7 @@ type PropsFile = {
|
||||
|
||||
export default function DetailDiscussionGeneral() {
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const entities = useSelector((state: any) => state.entities)
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
@@ -79,8 +80,7 @@ export default function DetailDiscussionGeneral() {
|
||||
comment: ''
|
||||
})
|
||||
const [viewEdit, setViewEdit] = useState(false)
|
||||
|
||||
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const onValueChange = reference.on('value', snapshot => {
|
||||
@@ -225,20 +225,29 @@ export default function DetailDiscussionGeneral() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Diskusi',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderRightDiscussionGeneralDetail id={id} active={data?.isActive !== undefined ? data.isActive : false} status={data?.status !== undefined ? data.status : 0} />,
|
||||
// headerRight: () => <HeaderRightDiscussionGeneralDetail id={id} active={data?.isActive !== undefined ? data.isActive : false} status={data?.status !== undefined ? data.status : 0} />,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Diskusi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<HeaderRightDiscussionGeneralDetail id={id} active={data?.isActive !== undefined ? data.isActive : false} status={data?.status !== undefined ? data.status : 0} />}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={{ flex: 1 }}>
|
||||
<View style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={() => handleRefresh()}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -252,8 +261,8 @@ export default function DetailDiscussionGeneral() {
|
||||
descEllipsize={false}
|
||||
borderType="bottom"
|
||||
icon={
|
||||
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
|
||||
<MaterialIcons name="chat" size={25} color={'#384288'} />
|
||||
<View style={[Styles.iconContent]}>
|
||||
<MaterialIcons name="chat" size={25} color={'black'} />
|
||||
</View>
|
||||
}
|
||||
title={data?.title}
|
||||
@@ -267,13 +276,13 @@ export default function DetailDiscussionGeneral() {
|
||||
desc={data?.desc}
|
||||
leftBottomInfo={
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
<Ionicons name="chatbox-ellipses-outline" size={18} color="grey" style={Styles.mr05} />
|
||||
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>{dataKomentar.length} Komentar</Text>
|
||||
<Ionicons name="chatbox-ellipses-outline" size={18} color={colors.dimmed} style={Styles.mr05} />
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }, Styles.mb05]}>{dataKomentar.length} Komentar</Text>
|
||||
</View>
|
||||
}
|
||||
rightBottomInfo={
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>{data?.createdAt}</Text>
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }, Styles.mb05]}>{data?.createdAt}</Text>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
@@ -301,6 +310,7 @@ export default function DetailDiscussionGeneral() {
|
||||
desc={item.comment}
|
||||
rightBottomInfo={item.isEdited ? "Edited" : ""}
|
||||
descEllipsize={detailMore.includes(item.id) ? false : true}
|
||||
bgColor="transparent"
|
||||
onPress={() => {
|
||||
setDetailMore((prev: any) => {
|
||||
if (prev.includes(item.id)) {
|
||||
@@ -327,7 +337,7 @@ export default function DetailDiscussionGeneral() {
|
||||
<View style={[
|
||||
Styles.contentItemCenter,
|
||||
Styles.w100,
|
||||
{ backgroundColor: "#f4f4f4" },
|
||||
{ backgroundColor: colors.background },
|
||||
viewEdit && Styles.borderTop
|
||||
]}>
|
||||
{
|
||||
@@ -335,11 +345,11 @@ export default function DetailDiscussionGeneral() {
|
||||
<>
|
||||
<View style={[Styles.w90, Styles.rowSpaceBetween, Styles.pv05]}>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
<Feather name="edit-3" color="black" size={22} style={[Styles.mh05]} />
|
||||
<Feather name="edit-3" color={colors.text} size={22} style={[Styles.mh05]} />
|
||||
<Text style={[Styles.textMediumSemiBold]}>Edit Komentar</Text>
|
||||
</View>
|
||||
<Pressable onPress={() => handleViewEditKomentar()}>
|
||||
<MaterialIcons name="close" color="black" size={22} />
|
||||
<MaterialIcons name="close" color={colors.text} size={22} />
|
||||
</Pressable>
|
||||
</View>
|
||||
<InputForm
|
||||
@@ -347,7 +357,6 @@ export default function DetailDiscussionGeneral() {
|
||||
type="default"
|
||||
round
|
||||
placeholder="Kirim Komentar"
|
||||
bg="white"
|
||||
onChange={(val: string) => setSelectKomentar({ ...selectKomentar, comment: val })}
|
||||
value={selectKomentar.comment}
|
||||
multiline
|
||||
@@ -361,7 +370,7 @@ export default function DetailDiscussionGeneral() {
|
||||
Platform.OS == 'android' && Styles.mb12,
|
||||
]}
|
||||
>
|
||||
<MaterialIcons name="send" size={25} style={(loadingSendKomentar || selectKomentar.comment == '' || regexOnlySpacesOrEnter.test(selectKomentar.comment) || data?.status === 2 || !data?.isActive || (!memberDiscussion && (entityUser.role == "user" || entityUser.role == "coadmin"))) ? Styles.cGray : Styles.cDefault} />
|
||||
<MaterialIcons name="send" size={25} style={(loadingSendKomentar || selectKomentar.comment == '' || regexOnlySpacesOrEnter.test(selectKomentar.comment) || data?.status === 2 || !data?.isActive || (!memberDiscussion && (entityUser.role == "user" || entityUser.role == "coadmin"))) ? { color: colors.dimmed } : { color: colors.tint }} />
|
||||
</Pressable>
|
||||
}
|
||||
/>
|
||||
@@ -374,7 +383,6 @@ export default function DetailDiscussionGeneral() {
|
||||
type="default"
|
||||
round
|
||||
placeholder="Kirim Komentar"
|
||||
bg="white"
|
||||
onChange={setKomentar}
|
||||
value={komentar}
|
||||
multiline
|
||||
@@ -388,13 +396,13 @@ export default function DetailDiscussionGeneral() {
|
||||
Platform.OS == 'android' && Styles.mb12,
|
||||
]}
|
||||
>
|
||||
<MaterialIcons name="send" size={25} style={(loadingSendKomentar || komentar == '' || regexOnlySpacesOrEnter.test(komentar) || data?.status === 2 || !data?.isActive || (!memberDiscussion && (entityUser.role == "user" || entityUser.role == "coadmin"))) ? Styles.cGray : Styles.cDefault} />
|
||||
<MaterialIcons name="send" size={25} style={(loadingSendKomentar || komentar == '' || regexOnlySpacesOrEnter.test(komentar) || data?.status === 2 || !data?.isActive || (!memberDiscussion && (entityUser.role == "user" || entityUser.role == "coadmin"))) ? { color: colors.dimmed } : { color: colors.tint }} />
|
||||
</Pressable>
|
||||
}
|
||||
/>
|
||||
:
|
||||
<View style={[Styles.pv20, { alignItems: 'center' }]}>
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }]}>
|
||||
{
|
||||
data?.status == 2 ? "Diskusi telah ditutup" : data?.isActive == false ? "Diskusi telah diarsipkan" : "Hanya anggota diskusi yang dapat memberikan komentar"
|
||||
}
|
||||
@@ -409,25 +417,35 @@ export default function DetailDiscussionGeneral() {
|
||||
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={setVisible} title="Komentar">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color={colors.text} size={25} />}
|
||||
title="Edit"
|
||||
onPress={() => { handleViewEditKomentar() }}
|
||||
/>
|
||||
<MenuItemRow
|
||||
icon={<MaterialIcons name="delete" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => {
|
||||
AlertKonfirmasi({
|
||||
title: 'Konfirmasi',
|
||||
desc: 'Apakah anda yakin ingin menghapus komentar?',
|
||||
onPress: () => {
|
||||
handleDeleteKomentar()
|
||||
}
|
||||
})
|
||||
setVisible(false)
|
||||
setTimeout(() => {
|
||||
setShowDeleteModal(true)
|
||||
}, 600)
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</DrawerBottom>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showDeleteModal}
|
||||
title="Konfirmasi"
|
||||
message="Apakah anda yakin ingin menghapus komentar?"
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false)
|
||||
handleDeleteKomentar()
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
confirmText="Hapus"
|
||||
cancelText="Batal"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import ImageWithLabel from "@/components/imageWithLabel";
|
||||
@@ -9,6 +9,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiAddMemberDiscussionGeneral, apiGetDiscussionGeneralOne, apiGetUser } from "@/lib/api";
|
||||
import { setUpdateDiscussionGeneralDetail } from "@/lib/discussionGeneralDetail";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -26,6 +27,7 @@ export default function AddMemberDiscussionDetail() {
|
||||
const dispatch = useDispatch()
|
||||
const update = useSelector((state: any) => state.discussionGeneralDetailUpdate)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
const [dataOld, setDataOld] = useState<Props[]>([])
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
@@ -92,24 +94,40 @@ export default function AddMemberDiscussionDetail() {
|
||||
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Tambah Anggota Diskusi',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="update"
|
||||
// disable={selectMember.length == 0 || loading ? true : false}
|
||||
// onPress={() => {
|
||||
// handleAddMember()
|
||||
// }}
|
||||
// />
|
||||
// )
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Anggota Diskusi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={[Styles.p15]}>
|
||||
<View style={[Styles.p15, {flex: 1, backgroundColor: colors.background }]}>
|
||||
<InputSearch onChange={setSearch} value={search} />
|
||||
|
||||
{
|
||||
@@ -131,7 +149,7 @@ export default function AddMemberDiscussionDetail() {
|
||||
</View>
|
||||
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
|
||||
<Text style={[Styles.textDefault, Styles.pv05, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada member yang dipilih</Text>
|
||||
}
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
@@ -144,7 +162,7 @@ export default function AddMemberDiscussionDetail() {
|
||||
return (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[Styles.itemSelectModal]}
|
||||
style={[Styles.itemSelectModal, { borderColor: colors.icon + '20' }]}
|
||||
onPress={() => {
|
||||
!found && onChoose(item.id, item.name, item.img)
|
||||
}}
|
||||
@@ -154,12 +172,12 @@ export default function AddMemberDiscussionDetail() {
|
||||
<View style={[Styles.ml10]}>
|
||||
<Text style={[Styles.textDefault]}>{item.name}</Text>
|
||||
{
|
||||
found && <Text style={[Styles.textInformation, Styles.cGray]}>sudah menjadi anggota</Text>
|
||||
found && <Text style={[Styles.textInformation, {color: colors.dimmed}]}>sudah menjadi anggota</Text>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={'black'} />
|
||||
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={colors.text} />
|
||||
}
|
||||
</Pressable>
|
||||
)
|
||||
@@ -170,6 +188,6 @@ export default function AddMemberDiscussionDetail() {
|
||||
}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
@@ -16,6 +16,7 @@ import { apiCreateDiscussionGeneral } from "@/lib/api";
|
||||
import { setUpdateDiscussionGeneralDetail } from "@/lib/discussionGeneralDetail";
|
||||
import { setMemberChoose } from "@/lib/memberChoose";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import * as DocumentPicker from "expo-document-picker";
|
||||
import { router, Stack } from "expo-router";
|
||||
@@ -27,6 +28,7 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function CreateDiscussionGeneral() {
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const entityUser = useSelector((state: any) => state.user);
|
||||
const userLogin = useSelector((state: any) => state.entities)
|
||||
const [chooseGroup, setChooseGroup] = useState({ val: "", label: "" });
|
||||
@@ -163,31 +165,49 @@ export default function CreateDiscussionGeneral() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => { handleBack() }}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => { handleBack() }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah Diskusi",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={disableBtn || loading ? true : false}
|
||||
onPress={() => {
|
||||
entitiesMember.length == 0
|
||||
? Toast.show({ type: 'small', text1: 'Anda belum memilih anggota', })
|
||||
: handleCreate()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="create"
|
||||
// disable={disableBtn || loading ? true : false}
|
||||
// onPress={() => {
|
||||
// entitiesMember.length == 0
|
||||
// ? Toast.show({ type: 'small', text1: 'Anda belum memilih anggota', })
|
||||
// : handleCreate()
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Diskusi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={disableBtn || loading ? true : false}
|
||||
onPress={() => {
|
||||
entitiesMember.length == 0
|
||||
? Toast.show({ type: 'small', text1: 'Anda belum memilih anggota', })
|
||||
: handleCreate()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<LoadingOverlay visible={loading} />
|
||||
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100]}>
|
||||
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100, { backgroundColor: colors.background }]}>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
{
|
||||
(entityUser.role == "supadmin" ||
|
||||
@@ -197,6 +217,7 @@ export default function CreateDiscussionGeneral() {
|
||||
placeholder="Pilih Lembaga Desa"
|
||||
value={chooseGroup.label}
|
||||
required
|
||||
bg={colors.card}
|
||||
onPress={() => {
|
||||
setValChoose(chooseGroup.val);
|
||||
setValSelect("group");
|
||||
@@ -213,6 +234,7 @@ export default function CreateDiscussionGeneral() {
|
||||
placeholder="Judul"
|
||||
required
|
||||
error={error.title}
|
||||
bg={colors.card}
|
||||
errorText="Judul tidak boleh kosong"
|
||||
onChange={(val) => { validationForm("title", val) }}
|
||||
/>
|
||||
@@ -222,6 +244,7 @@ export default function CreateDiscussionGeneral() {
|
||||
placeholder="Hal yang didiskusikan"
|
||||
required
|
||||
error={error.desc}
|
||||
bg={colors.card}
|
||||
errorText="Diskusi tidak boleh kosong"
|
||||
onChange={(val) => { validationForm("desc", val) }}
|
||||
multiline
|
||||
@@ -230,15 +253,16 @@ export default function CreateDiscussionGeneral() {
|
||||
{
|
||||
fileForm.length > 0
|
||||
&&
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10, Styles.mb10]}>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, Styles.mb10, { borderColor: colors.icon + '20' }]}>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>File</Text>
|
||||
{
|
||||
fileForm.map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType={fileForm.length > 1 ? "bottom" : "none"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
borderType={fileForm.length - 1 == index ? "none" : "bottom"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name}
|
||||
bgColor="transparent"
|
||||
titleWeight="normal"
|
||||
onPress={() => { setIndexDelFile(index); setModalFile(true) }}
|
||||
/>
|
||||
@@ -273,17 +297,18 @@ export default function CreateDiscussionGeneral() {
|
||||
<Text>Total {entitiesMember.length} Anggota</Text>
|
||||
</View>
|
||||
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10]}>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, { borderColor: colors.icon + '20' }]}>
|
||||
{
|
||||
entitiesMember.map((item: { img: any; name: any; }, index: any) => {
|
||||
return (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType="bottom"
|
||||
borderType={entitiesMember.length - 1 == index ? "none" : "bottom"}
|
||||
icon={
|
||||
<ImageUser src={`${ConstEnv.url_storage}/files/${item.img}`} size="sm" />
|
||||
<ImageUser src={`${ConstEnv.url_storage}/files/${item.img}`} size="xs" />
|
||||
}
|
||||
title={item.name}
|
||||
bgColor="transparent"
|
||||
/>
|
||||
)
|
||||
})
|
||||
@@ -309,7 +334,7 @@ export default function CreateDiscussionGeneral() {
|
||||
<DrawerBottom animation="slide" isVisible={isModalFile} setVisible={setModalFile} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => { deleteFile(indexDelFile) }}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import Text from "@/components/Text";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
@@ -11,6 +11,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiEditDiscussionGeneral, apiGetDiscussionGeneralOne } from "@/lib/api";
|
||||
import { setUpdateDiscussionGeneralDetail } from "@/lib/discussionGeneralDetail";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import * as DocumentPicker from "expo-document-picker";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
@@ -21,6 +22,7 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function EditDiscussionGeneral() {
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const { colors } = useTheme();
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const [disableBtn, setDisableBtn] = useState(false)
|
||||
const dispatch = useDispatch()
|
||||
@@ -162,35 +164,50 @@ export default function EditDiscussionGeneral() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Edit Diskusi",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading ? true : false}
|
||||
category="update"
|
||||
onPress={() => { handleEdit() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disableBtn || loading ? true : false}
|
||||
// category="update"
|
||||
// onPress={() => { handleEdit() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Diskusi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading ? true : false}
|
||||
category="update"
|
||||
onPress={() => { handleEdit() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<LoadingOverlay visible={loading} />
|
||||
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100]}>
|
||||
<ScrollView showsVerticalScrollIndicator={false} style={[Styles.h100, { backgroundColor: colors.background }]}>
|
||||
<View style={[Styles.p15]}>
|
||||
<InputForm
|
||||
label="Judul"
|
||||
type="default"
|
||||
placeholder="Judul"
|
||||
required
|
||||
bg={colors.card}
|
||||
error={error.title}
|
||||
value={dataForm.title}
|
||||
errorText="Judul tidak boleh kosong"
|
||||
@@ -201,6 +218,7 @@ export default function EditDiscussionGeneral() {
|
||||
type="default"
|
||||
placeholder="Hal yang didiskusikan"
|
||||
required
|
||||
bg={colors.card}
|
||||
error={error.desc}
|
||||
value={dataForm.desc}
|
||||
errorText="Diskusi tidak boleh kosong"
|
||||
@@ -211,16 +229,17 @@ export default function EditDiscussionGeneral() {
|
||||
{
|
||||
(fileForm.length > 0 || dataFile.filter((val) => !val.delete).length > 0)
|
||||
&&
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10, Styles.mb10]}>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>File</Text>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, Styles.mb10, { borderColor: colors.icon + '20' }]}>
|
||||
<Text style={[Styles.textDefault]}>File</Text>
|
||||
{
|
||||
dataFile.filter((val) => !val.delete).map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType={(fileForm.length + dataFile.length) > 1 ? "bottom" : "none"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name + '.' + item.extension}
|
||||
titleWeight="normal"
|
||||
bgColor="transparent"
|
||||
onPress={() => { setIndexDelFile({ id: item.id, cat: "oldFile" }); setModalFile(true) }}
|
||||
/>
|
||||
))
|
||||
@@ -230,9 +249,10 @@ export default function EditDiscussionGeneral() {
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType={(fileForm.length + dataFile.length) > 1 ? "bottom" : "none"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name}
|
||||
titleWeight="normal"
|
||||
bgColor="transparent"
|
||||
onPress={() => { setIndexDelFile({ id: index, cat: "newFile" }); setModalFile(true) }}
|
||||
/>
|
||||
))
|
||||
@@ -245,7 +265,7 @@ export default function EditDiscussionGeneral() {
|
||||
<DrawerBottom animation="slide" isVisible={isModalFile} setVisible={setModalFile} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => { deleteFile(indexDelFile.id, indexDelFile.cat) }}
|
||||
/>
|
||||
|
||||
@@ -4,10 +4,12 @@ import InputSearch from "@/components/inputSearch";
|
||||
import LabelStatus from "@/components/labelStatus";
|
||||
import SkeletonContent from "@/components/skeletonContent";
|
||||
import Text from "@/components/Text";
|
||||
import WrapTab from "@/components/wrapTab";
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetDiscussionGeneral } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign, Feather, Ionicons, MaterialIcons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -27,6 +29,7 @@ type Props = {
|
||||
export default function Discussion() {
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const { active, group } = useLocalSearchParams<{ active?: string, group?: string }>()
|
||||
const [search, setSearch] = useState('')
|
||||
const [nameGroup, setNameGroup] = useState('')
|
||||
@@ -96,33 +99,34 @@ export default function Discussion() {
|
||||
})
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<View>
|
||||
{
|
||||
entityUser.role != "user" && entityUser.role != "coadmin" &&
|
||||
<View style={[Styles.wrapBtnTab]}>
|
||||
<WrapTab>
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="true"
|
||||
onPress={() => { setStatus("true") }}
|
||||
label="Aktif"
|
||||
icon={<Feather name="check-circle" color={status == "false" ? 'black' : 'white'} size={20} />}
|
||||
icon={<Feather name="check-circle" color={status == "false" ? colors.dimmed : 'white'} size={20} />}
|
||||
n={2} />
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="false"
|
||||
onPress={() => { setStatus("false") }}
|
||||
label="Arsip"
|
||||
icon={<AntDesign name="closecircleo" color={status == "true" ? 'black' : 'white'} size={20} />}
|
||||
icon={<AntDesign name="closecircleo" color={status == "true" ? colors.dimmed : 'white'} size={20} />}
|
||||
n={2} />
|
||||
</View>
|
||||
</WrapTab>
|
||||
}
|
||||
|
||||
<InputSearch onChange={setSearch} />
|
||||
{
|
||||
(entityUser.role == "supadmin" || entityUser.role == "developer") &&
|
||||
<View style={[Styles.mv05]}>
|
||||
<Text>Filter : {nameGroup}</Text>
|
||||
<View style={[Styles.mv05, Styles.rowOnly]}>
|
||||
<Text>Filter :</Text>
|
||||
<LabelStatus size="small" category="secondary" text={nameGroup} style={[Styles.mh05]} />
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
@@ -144,12 +148,13 @@ export default function Discussion() {
|
||||
renderItem={({ item, index }: { item: Props, index: number }) => {
|
||||
return (
|
||||
<BorderBottomItem
|
||||
bgColor="transparent"
|
||||
key={index}
|
||||
onPress={() => { router.push(`/discussion/${item.id}`) }}
|
||||
borderType="bottom"
|
||||
icon={
|
||||
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
|
||||
<MaterialIcons name="chat" size={25} color={'#384288'} />
|
||||
<View style={[Styles.iconContent]}>
|
||||
<MaterialIcons name="chat" size={25} color={'black'} />
|
||||
</View>
|
||||
}
|
||||
title={item.title}
|
||||
@@ -160,8 +165,8 @@ export default function Discussion() {
|
||||
desc={item.desc.replace(/<[^>]*>?/gm, ' ').replace(/\r?\n|\r/g, ' ')}
|
||||
leftBottomInfo={
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
<Ionicons name="chatbox-ellipses-outline" size={18} color="grey" style={Styles.mr05} />
|
||||
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>Diskusikan</Text>
|
||||
<Ionicons name="chatbox-ellipses-outline" size={18} color={colors.dimmed} style={Styles.mr05} />
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }, Styles.mb05]}>Diskusikan</Text>
|
||||
</View>
|
||||
}
|
||||
rightBottomInfo={`${item.total_komentar} Komentar`}
|
||||
@@ -177,11 +182,12 @@ export default function Discussion() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada data</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada data</Text>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import MenuItemRow from "@/components/menuItemRow";
|
||||
import ModalConfirmation from "@/components/ModalConfirmation";
|
||||
import SkeletonTwoItem from "@/components/skeletonTwoItem";
|
||||
import Text from '@/components/Text';
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
@@ -11,6 +11,7 @@ import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiDeleteMemberDiscussionGeneral, apiGetDiscussionGeneralOne } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Feather, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -26,6 +27,7 @@ type Props = {
|
||||
|
||||
export default function MemberDiscussionDetail() {
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
@@ -34,6 +36,8 @@ export default function MemberDiscussionDetail() {
|
||||
const update = useSelector((state: any) => state.discussionGeneralDetailUpdate)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const arrSkeleton = Array.from({ length: 5 }, (_, index) => index)
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
|
||||
|
||||
async function handleLoad(loading: boolean) {
|
||||
try {
|
||||
@@ -71,18 +75,25 @@ export default function MemberDiscussionDetail() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Anggota Diskusi',
|
||||
headerTitleAlign: 'center',
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Anggota Diskusi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
<ScrollView style={{ backgroundColor: colors.background }}>
|
||||
<View style={[Styles.p15]}>
|
||||
<Text style={[Styles.textDefault, Styles.mv05]}>{data.length} Anggota</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.mb100]}>
|
||||
<View style={[Styles.wrapPaper, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
{
|
||||
entityUser.role != "user" && entityUser.role != "coadmin" &&
|
||||
<BorderBottomItem
|
||||
@@ -128,7 +139,7 @@ export default function MemberDiscussionDetail() {
|
||||
<DrawerBottom animation="slide" isVisible={isModal} setVisible={setModal} title={chooseUser.name}>
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="account-eye" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="account-eye" color={colors.text} size={25} />}
|
||||
title="Lihat Profil"
|
||||
onPress={() => {
|
||||
setModal(false)
|
||||
@@ -138,24 +149,32 @@ export default function MemberDiscussionDetail() {
|
||||
{
|
||||
entityUser.role != "user" && entityUser.role != "coadmin" &&
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="account-remove" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="account-remove" color={colors.text} size={25} />}
|
||||
title="Keluarkan"
|
||||
onPress={() => {
|
||||
setModal(false)
|
||||
AlertKonfirmasi({
|
||||
title: 'Konfirmasi',
|
||||
desc: 'Apakah Anda yakin ingin mengeluarkan anggota?',
|
||||
onPress: () => {
|
||||
handleDeleteUser()
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
setShowDeleteModal(true)
|
||||
}, 600)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
||||
</View>
|
||||
</DrawerBottom>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showDeleteModal}
|
||||
title="Konfirmasi"
|
||||
message="Apakah anda yakin ingin mengeluarkan anggota?"
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false)
|
||||
handleDeleteUser()
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
confirmText="Hapus"
|
||||
cancelText="Batal"
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader"
|
||||
import AppHeader from "@/components/AppHeader"
|
||||
import HeaderRightDiscussionList from "@/components/discussion/headerDiscussionList"
|
||||
import HeaderRightTaskList from "@/components/task/headerTaskList"
|
||||
import { Headers } from "@/constants/Headers"
|
||||
@@ -9,22 +9,49 @@ export default function RootLayout() {
|
||||
<>
|
||||
<Stack screenOptions={Headers.shadow}>
|
||||
<Stack.Screen name="task/index" options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
title: 'Tugas Divisi',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderRightTaskList />
|
||||
// headerRight: () => <HeaderRightTaskList />
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tugas Divisi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<HeaderRightTaskList />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}} />
|
||||
<Stack.Screen name="discussion/index" options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
title: 'Diskusi Divisi',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderRightDiscussionList />
|
||||
// headerRight: () => <HeaderRightDiscussionList />
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Diskusi Divisi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<HeaderRightDiscussionList />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}} />
|
||||
<Stack.Screen name="calendar/history"
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Riwayat Acara',
|
||||
headerTitleAlign: 'center',
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Riwayat Acara"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import ImageWithLabel from "@/components/imageWithLabel";
|
||||
@@ -9,6 +9,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiAddMemberCalendar, apiGetCalendarOne, apiGetDivisionMember } from "@/lib/api";
|
||||
import { setUpdateCalendar } from "@/lib/calendarUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -23,6 +24,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function AddMemberCalendarEvent() {
|
||||
const { colors } = useTheme();
|
||||
const dispatch = useDispatch()
|
||||
const update = useSelector((state: any) => state.calendarUpdate)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
@@ -100,19 +102,35 @@ export default function AddMemberCalendarEvent() {
|
||||
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Tambah Anggota',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="update"
|
||||
// disable={selectMember.length == 0 || loading ? true : false}
|
||||
// onPress={() => {
|
||||
// handleAddMember()
|
||||
// }}
|
||||
// />
|
||||
// )
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Anggota"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
@@ -138,7 +156,7 @@ export default function AddMemberCalendarEvent() {
|
||||
</View>
|
||||
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
|
||||
<Text style={[Styles.textDefault, Styles.pv05, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada member yang dipilih</Text>
|
||||
}
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
@@ -161,12 +179,12 @@ export default function AddMemberCalendarEvent() {
|
||||
<View style={[Styles.ml10, { width: '80%' }]}>
|
||||
<Text numberOfLines={1} ellipsizeMode="tail" style={[Styles.textDefault]}>{item.name}</Text>
|
||||
{
|
||||
found && <Text style={[Styles.textInformation, Styles.cGray]}>sudah menjadi anggota</Text>
|
||||
found && <Text style={[Styles.textInformation, { color: colors.dimmed }]}>sudah menjadi anggota</Text>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
selectMember.some((i: any) => i.idUser == item.idUser) && <AntDesign name="check" size={20} color={'black'} />
|
||||
selectMember.some((i: any) => i.idUser == item.idUser) && <AntDesign name="check" size={20} color={colors.text} />
|
||||
}
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader"
|
||||
import AppHeader from "@/components/AppHeader"
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader"
|
||||
import { InputDate } from "@/components/inputDate"
|
||||
import { InputForm } from "@/components/inputForm"
|
||||
@@ -9,6 +9,7 @@ import { valueTypeEventRepeat } from "@/constants/TypeEventRepeat"
|
||||
import { apiGetCalendarOne, apiUpdateCalendar } from "@/lib/api"
|
||||
import { stringToDateTime } from "@/lib/fun_stringToDate"
|
||||
import { useAuthSession } from "@/providers/AuthProvider"
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useHeaderHeight } from "@react-navigation/elements"
|
||||
import { Stack, router, useLocalSearchParams } from "expo-router"
|
||||
import moment from "moment"
|
||||
@@ -17,6 +18,7 @@ import { KeyboardAvoidingView, Platform, SafeAreaView, ScrollView, View } from "
|
||||
import Toast from "react-native-toast-message"
|
||||
|
||||
export default function EditEventCalendar() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const [choose, setChoose] = useState({ val: "", label: "" })
|
||||
const [isSelect, setSelect] = useState(false)
|
||||
@@ -162,20 +164,36 @@ export default function EditEventCalendar() {
|
||||
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Edit Acara',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () =>
|
||||
<ButtonSaveHeader
|
||||
disable={Object.values(error).some((val) => val == true) || data.title == "" || data.dateStart == "" || data.timeStart == "" || data.timeEnd == "" || data.repeatEventTyper == "" || loading}
|
||||
category="update-calendar"
|
||||
onPress={() => {
|
||||
handleUpdate()
|
||||
}}
|
||||
// headerRight: () =>
|
||||
// <ButtonSaveHeader
|
||||
// disable={Object.values(error).some((val) => val == true) || data.title == "" || data.dateStart == "" || data.timeStart == "" || data.timeEnd == "" || data.repeatEventTyper == "" || loading}
|
||||
// category="update-calendar"
|
||||
// onPress={() => {
|
||||
// handleUpdate()
|
||||
// }}
|
||||
// />
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Acara"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={Object.values(error).some((val) => val == true) || data.title == "" || data.dateStart == "" || data.timeStart == "" || data.timeEnd == "" || data.repeatEventTyper == "" || loading}
|
||||
category="update-calendar"
|
||||
onPress={() => {
|
||||
handleUpdate()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
@@ -189,7 +207,7 @@ export default function EditEventCalendar() {
|
||||
type="default"
|
||||
placeholder="Nama Acara"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={data.title}
|
||||
onChange={(val) => validationForm("title", val)}
|
||||
error={error.title}
|
||||
@@ -235,12 +253,12 @@ export default function EditEventCalendar() {
|
||||
label="Link Meet"
|
||||
type="default"
|
||||
placeholder="Link Meet"
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={data.linkMeet}
|
||||
onChange={(val) => validationForm("linkMeet", val)}
|
||||
/>
|
||||
<SelectForm
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
label="Ulangi Acara"
|
||||
placeholder="Ulangi Acara"
|
||||
value={choose.label}
|
||||
@@ -252,7 +270,7 @@ export default function EditEventCalendar() {
|
||||
type="numeric"
|
||||
placeholder="Jumlah Pengulangan"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={String(data.repeatValue)}
|
||||
onChange={(val) => validationForm("repeatValue", val)}
|
||||
error={error.repeatValue}
|
||||
@@ -263,7 +281,7 @@ export default function EditEventCalendar() {
|
||||
label="Deskripsi"
|
||||
type="default"
|
||||
placeholder="Deskripsi"
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={data.desc}
|
||||
onChange={(val) => validationForm("desc", val)}
|
||||
multiline
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi"
|
||||
import ModalConfirmation from "@/components/ModalConfirmation"
|
||||
import AppHeader from "@/components/AppHeader"
|
||||
import BorderBottomItem from "@/components/borderBottomItem"
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader"
|
||||
import HeaderRightCalendarDetail from "@/components/calendar/headerCalendarDetail"
|
||||
@@ -12,6 +13,7 @@ import Styles from "@/constants/Styles"
|
||||
import { apiDeleteCalendarMember, apiGetCalendarOne, apiGetDivisionOneFeature } from "@/lib/api"
|
||||
import { setUpdateCalendar } from "@/lib/calendarUpdate"
|
||||
import { useAuthSession } from "@/providers/AuthProvider"
|
||||
import { useTheme } from "@/providers/ThemeProvider"
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons"
|
||||
import Clipboard from "@react-native-clipboard/clipboard"
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router"
|
||||
@@ -44,6 +46,7 @@ type PropsMember = {
|
||||
}
|
||||
|
||||
export default function DetailEventCalendar() {
|
||||
const { colors } = useTheme()
|
||||
const { id, detail } = useLocalSearchParams<{ id: string, detail: string }>();
|
||||
const [data, setData] = useState<Props>()
|
||||
const [member, setMember] = useState<PropsMember[]>([])
|
||||
@@ -54,6 +57,7 @@ export default function DetailEventCalendar() {
|
||||
const dispatch = useDispatch()
|
||||
const entityUser = useSelector((state: any) => state.user);
|
||||
const [isMemberDivision, setIsMemberDivision] = useState(false);
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
|
||||
@@ -151,13 +155,23 @@ export default function DetailEventCalendar() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Detail Acara',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (entityUser.role == "user" || entityUser.role == "coadmin") && !isMemberDivision ? <></> : <HeaderRightCalendarDetail id={String(data?.idCalendar)} idReminder={String(detail)} />
|
||||
// headerRight: () => (entityUser.role == "user" || entityUser.role == "coadmin") && !isMemberDivision ? <></> : <HeaderRightCalendarDetail id={String(data?.idCalendar)} idReminder={String(detail)} />
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail Acara"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
(entityUser.role == "user" || entityUser.role == "coadmin") && !isMemberDivision ? <></> : <HeaderRightCalendarDetail id={String(data?.idCalendar)} idReminder={String(detail)} />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
@@ -166,13 +180,14 @@ export default function DetailEventCalendar() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
<View style={[Styles.wrapPaper, Styles.mb15]}>
|
||||
<View style={[Styles.wrapPaper, Styles.mb15, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
<View style={[Styles.rowItemsCenter, { alignItems: 'flex-start' }]}>
|
||||
<MaterialCommunityIcons name="calendar-text" size={30} color="black" style={Styles.mr10} />
|
||||
<MaterialCommunityIcons name="calendar-text" size={30} color={colors.text} style={Styles.mr10} />
|
||||
{
|
||||
loading ?
|
||||
<Skeleton width={80} height={10} borderRadius={10} widthType="percent" />
|
||||
@@ -181,7 +196,7 @@ export default function DetailEventCalendar() {
|
||||
|
||||
</View>
|
||||
<View style={[Styles.rowItemsCenter, Styles.mt10]}>
|
||||
<MaterialCommunityIcons name="calendar-month-outline" size={30} color="black" style={Styles.mr10} />
|
||||
<MaterialCommunityIcons name="calendar-month-outline" size={30} color={colors.text} style={Styles.mr10} />
|
||||
{
|
||||
loading ?
|
||||
<Skeleton width={80} height={10} borderRadius={10} widthType="percent" />
|
||||
@@ -190,7 +205,7 @@ export default function DetailEventCalendar() {
|
||||
}
|
||||
</View>
|
||||
<View style={[Styles.rowItemsCenter, Styles.mt10]}>
|
||||
<MaterialCommunityIcons name="clock-outline" size={30} color="black" style={Styles.mr10} />
|
||||
<MaterialCommunityIcons name="clock-outline" size={30} color={colors.text} style={Styles.mr10} />
|
||||
{
|
||||
loading ?
|
||||
<Skeleton width={80} height={10} borderRadius={10} widthType="percent" />
|
||||
@@ -199,7 +214,7 @@ export default function DetailEventCalendar() {
|
||||
}
|
||||
</View>
|
||||
<View style={[Styles.rowItemsCenter, Styles.mt10]}>
|
||||
<MaterialCommunityIcons name="repeat" size={30} color="black" style={Styles.mr10} />
|
||||
<MaterialCommunityIcons name="repeat" size={30} color={colors.text} style={Styles.mr10} />
|
||||
{
|
||||
loading ?
|
||||
<Skeleton width={80} height={10} borderRadius={10} widthType="percent" />
|
||||
@@ -217,7 +232,7 @@ export default function DetailEventCalendar() {
|
||||
}
|
||||
</View>
|
||||
<View style={[Styles.rowItemsCenter, Styles.mt10]}>
|
||||
<MaterialCommunityIcons name="link-variant" size={30} color="black" style={Styles.mr10} />
|
||||
<MaterialCommunityIcons name="link-variant" size={30} color={colors.text} style={Styles.mr10} />
|
||||
{
|
||||
loading ?
|
||||
<Skeleton width={80} height={10} borderRadius={10} widthType="percent" />
|
||||
@@ -230,7 +245,7 @@ export default function DetailEventCalendar() {
|
||||
}
|
||||
</View>
|
||||
<View style={[Styles.rowItemsCenter, Styles.mt10, { alignItems: 'flex-start' }]}>
|
||||
<MaterialCommunityIcons name="card-text-outline" size={30} color="black" style={Styles.mr10} />
|
||||
<MaterialCommunityIcons name="card-text-outline" size={30} color={colors.text} style={Styles.mr10} />
|
||||
{
|
||||
loading ?
|
||||
<Skeleton width={80} height={10} borderRadius={10} widthType="percent" />
|
||||
@@ -246,7 +261,7 @@ export default function DetailEventCalendar() {
|
||||
<Text style={[Styles.textDefault]}>Total {member.length} Anggota</Text>
|
||||
</View>
|
||||
|
||||
<View style={[Styles.wrapPaper]}>
|
||||
<View style={[Styles.wrapPaper, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
{
|
||||
member.map((item, index) => (
|
||||
<BorderBottomItem
|
||||
@@ -275,7 +290,7 @@ export default function DetailEventCalendar() {
|
||||
<DrawerBottom animation="slide" isVisible={isModalMember} setVisible={setModalMember} title={memberChoose.name}>
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="account-eye" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="account-eye" color={colors.text} size={25} />}
|
||||
title="Lihat Profil"
|
||||
onPress={() => {
|
||||
setModalMember(false)
|
||||
@@ -284,22 +299,30 @@ export default function DetailEventCalendar() {
|
||||
/>
|
||||
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="account-remove" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="account-remove" color={colors.text} size={25} />}
|
||||
title="Keluarkan"
|
||||
onPress={() => {
|
||||
setModalMember(false)
|
||||
AlertKonfirmasi({
|
||||
title: 'Konfirmasi',
|
||||
desc: 'Apakah Anda yakin ingin mengeluarkan anggota?',
|
||||
onPress: () => {
|
||||
handleDeleteUser()
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
setShowDeleteModal(true)
|
||||
}, 600)
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</DrawerBottom>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showDeleteModal}
|
||||
title="Konfirmasi"
|
||||
message="Apakah Anda yakin ingin mengeluarkan anggota?"
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false)
|
||||
handleDeleteUser()
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
confirmText="Keluar"
|
||||
cancelText="Batal"
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import ImageWithLabel from "@/components/imageWithLabel";
|
||||
@@ -10,6 +10,7 @@ import { apiCreateCalendar, apiGetDivisionMember } from "@/lib/api";
|
||||
import { setFormCreateCalendar } from "@/lib/calendarCreate";
|
||||
import { setUpdateCalendar } from "@/lib/calendarUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -24,6 +25,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function CreateCalendarAddMember() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
@@ -90,17 +92,31 @@ export default function CreateCalendarAddMember() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Pilih Anggota',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => { handleAddMember() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="create"
|
||||
// disable={selectMember.length == 0 || loading ? true : false}
|
||||
// onPress={() => { handleAddMember() }}
|
||||
// />
|
||||
// )
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Pilih Anggota"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => { handleAddMember() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
@@ -127,7 +143,7 @@ export default function CreateCalendarAddMember() {
|
||||
</View>
|
||||
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
|
||||
<Text style={[Styles.textDefault, Styles.pv05, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada member yang dipilih</Text>
|
||||
}
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
@@ -140,7 +156,7 @@ export default function CreateCalendarAddMember() {
|
||||
return (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[Styles.itemSelectModal]}
|
||||
style={[Styles.itemSelectModal, {borderColor: colors.icon + '20'}]}
|
||||
onPress={() => { onChoose(item.idUser, item.name, item.img) }}
|
||||
>
|
||||
<View style={[Styles.rowItemsCenter, Styles.w70]}>
|
||||
@@ -150,7 +166,7 @@ export default function CreateCalendarAddMember() {
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
selectMember.some((i: any) => i.idUser == item.idUser) && <AntDesign name="check" size={20} color={'black'} />
|
||||
selectMember.some((i: any) => i.idUser == item.idUser) && <AntDesign name="check" size={20} color={colors.text} />
|
||||
}
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ButtonNextHeader from "@/components/buttonNextHeader";
|
||||
import { InputDate } from "@/components/inputDate";
|
||||
@@ -7,6 +8,8 @@ import SelectForm from "@/components/selectForm";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { setFormCreateCalendar } from "@/lib/calendarCreate";
|
||||
import { stringToDateTime } from "@/lib/fun_stringToDate";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Stack, router, useLocalSearchParams } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
@@ -17,9 +20,9 @@ import {
|
||||
View
|
||||
} from "react-native";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
|
||||
export default function CalendarDivisionCreate() {
|
||||
const { colors } = useTheme();
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
const [choose, setChoose] = useState({ val: "", label: "" })
|
||||
const [isSelect, setSelect] = useState(false)
|
||||
@@ -125,38 +128,54 @@ export default function CalendarDivisionCreate() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah Acara",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonNextHeader
|
||||
onPress={() => { handleSetData() }}
|
||||
disable={Object.values(error).some((val) => val == true) || data.title == "" || data.dateStart == "" || data.timeStart == "" || data.timeEnd == "" || data.repeatEventType == ""}
|
||||
// headerRight: () => (
|
||||
// <ButtonNextHeader
|
||||
// onPress={() => { handleSetData() }}
|
||||
// disable={Object.values(error).some((val) => val == true) || data.title == "" || data.dateStart == "" || data.timeStart == "" || data.timeEnd == "" || data.repeatEventType == ""}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Acara"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonNextHeader
|
||||
onPress={() => { handleSetData() }}
|
||||
disable={Object.values(error).some((val) => val == true) || data.title == "" || data.dateStart == "" || data.timeStart == "" || data.timeEnd == "" || data.repeatEventType == ""}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={headerHeight}
|
||||
>
|
||||
<ScrollView>
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100]}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
<InputForm
|
||||
label="Nama Acara"
|
||||
type="default"
|
||||
placeholder="Nama Acara"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={data.title}
|
||||
onChange={(val) => validationForm("title", val)}
|
||||
error={error.title}
|
||||
@@ -202,12 +221,12 @@ export default function CalendarDivisionCreate() {
|
||||
label="Link Meet"
|
||||
type="default"
|
||||
placeholder="Link Meet"
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={data.linkMeet}
|
||||
onChange={(val) => validationForm("linkMeet", val)}
|
||||
/>
|
||||
<SelectForm
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
label="Ulangi Acara"
|
||||
placeholder="Ulangi Acara"
|
||||
value={choose.label}
|
||||
@@ -219,7 +238,7 @@ export default function CalendarDivisionCreate() {
|
||||
type="numeric"
|
||||
placeholder="Jumlah Pengulangan"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={String(data.repeatValue)}
|
||||
onChange={(val) => validationForm("repeatValue", val)}
|
||||
error={error.repeatValue}
|
||||
@@ -230,7 +249,7 @@ export default function CalendarDivisionCreate() {
|
||||
label="Deskripsi"
|
||||
type="default"
|
||||
placeholder="Deskripsi"
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={data.desc}
|
||||
onChange={(val) => validationForm("desc", val)}
|
||||
multiline
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import InputSearch from "@/components/inputSearch";
|
||||
import Skeleton from "@/components/skeleton";
|
||||
import Text from "@/components/Text";
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetCalendarHistory } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FlatList, View, VirtualizedList } from "react-native";
|
||||
@@ -15,6 +15,7 @@ type Props = {
|
||||
data: []
|
||||
}
|
||||
export default function CalendarHistory() {
|
||||
const { colors, activeTheme } = useTheme();
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
@@ -64,11 +65,11 @@ export default function CalendarHistory() {
|
||||
})
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<View>
|
||||
<InputSearch onChange={(val) => setSearch(val)} />
|
||||
</View>
|
||||
<View style={[{ flex: 2, }]}>
|
||||
<View style={[{ flex: 2 }, Styles.mt10]}>
|
||||
{
|
||||
loading ?
|
||||
arrSkeleton.map((item, index) => (
|
||||
@@ -81,7 +82,7 @@ export default function CalendarHistory() {
|
||||
getItem={getItem}
|
||||
renderItem={({ item, index }: { item: Props, index: number }) => {
|
||||
return (
|
||||
<View key={index} style={[{ flexDirection: 'row' }, Styles.mv05, ColorsStatus.lightGreen, Styles.p10, Styles.round10]}>
|
||||
<View key={index} style={[{ flexDirection: 'row' }, Styles.mb05, Styles.borderAll, { backgroundColor: colors.card }, Styles.p10, Styles.round05, { borderColor: colors.icon + '20' }]}>
|
||||
<View style={[Styles.mr10, Styles.ph05]}>
|
||||
<Text style={[Styles.textSubtitle]}>{String(item.dateStart)}</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center' }]}>{item.year}</Text>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import HeaderRightCalendarList from "@/components/calendar/headerCalendarList";
|
||||
import ItemDateCalendar from "@/components/calendar/itemDateCalendar";
|
||||
import EventItem from "@/components/eventItem";
|
||||
@@ -7,6 +7,7 @@ import Text from "@/components/Text";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetCalendarByDateDivision, apiGetIndicatorCalendar } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import 'intl';
|
||||
@@ -34,6 +35,7 @@ type Props = {
|
||||
};
|
||||
|
||||
export default function CalendarDivision() {
|
||||
const { colors, activeTheme } = useTheme();
|
||||
const [selected, setSelected] = useState<any>(new Date())
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
@@ -117,27 +119,35 @@ export default function CalendarDivision() {
|
||||
);
|
||||
},
|
||||
IconNext: <Pressable onPress={() => !loadingBtn ? setMonth(month + 1) : null}>
|
||||
<Feather name="chevron-right" size={20} color={loadingBtn ? 'gray' : 'black'} />
|
||||
<Feather name="chevron-right" size={20} color={loadingBtn ? 'gray' : colors.text} />
|
||||
</Pressable>,
|
||||
IconPrev: <Pressable onPress={() => !loadingBtn ? setMonth(month - 1) : null}>
|
||||
<Feather name="chevron-left" size={20} color={loadingBtn ? 'gray' : 'black'} />
|
||||
<Feather name="chevron-left" size={20} color={loadingBtn ? 'gray' : colors.text} />
|
||||
</Pressable>,
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Kalender",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => <HeaderRightCalendarList />,
|
||||
// headerRight: () => <HeaderRightCalendarList />,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Kalender"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<HeaderRightCalendarList />}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
@@ -145,12 +155,13 @@ export default function CalendarDivision() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
style={[Styles.h100]}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
<Datepicker
|
||||
components={components}
|
||||
mode="single"
|
||||
@@ -159,19 +170,19 @@ export default function CalendarDivision() {
|
||||
onMonthChange={(month) => setMonth(month)}
|
||||
styles={{
|
||||
selected: Styles.selectedDate,
|
||||
month_label: Styles.cBlack,
|
||||
month_selector_label: Styles.cBlack,
|
||||
year_label: Styles.cBlack,
|
||||
year_selector_label: Styles.cBlack,
|
||||
day_label: Styles.cBlack,
|
||||
time_label: Styles.cBlack,
|
||||
weekday_label: Styles.cBlack,
|
||||
month_label: { color: colors.text },
|
||||
month_selector_label: { color: colors.text },
|
||||
year_label: { color: colors.text },
|
||||
year_selector_label: { color: colors.text },
|
||||
day_label: { color: colors.text },
|
||||
time_label: { color: colors.text },
|
||||
weekday_label: { color: colors.text },
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
<View style={[Styles.mb15, Styles.mt15]}>
|
||||
<Text style={[Styles.textDefaultSemiBold, Styles.mb05]}>Acara</Text>
|
||||
<View style={[Styles.wrapPaper]}>
|
||||
<View style={[Styles.wrapPaper, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
{
|
||||
loading ?
|
||||
<>
|
||||
@@ -194,7 +205,7 @@ export default function CalendarDivision() {
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada acara</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada acara</Text>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
@@ -11,6 +11,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiEditDiscussion, apiGetDiscussionOne } from "@/lib/api";
|
||||
import { setUpdateDiscussion } from "@/lib/discussionUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import * as DocumentPicker from "expo-document-picker";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
@@ -20,6 +21,7 @@ import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function DiscussionDivisionEdit() {
|
||||
const { colors } = useTheme();
|
||||
const { id, detail } = useLocalSearchParams<{ id: string; detail: string }>();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const [data, setData] = useState("");
|
||||
@@ -127,27 +129,43 @@ export default function DiscussionDivisionEdit() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Edit Diskusi",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={data == "" || loading}
|
||||
category="update"
|
||||
onPress={() => {
|
||||
handleUpdate();
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={data == "" || loading}
|
||||
// category="update"
|
||||
// onPress={() => {
|
||||
// handleUpdate();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Diskusi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={data == "" || loading}
|
||||
category="update"
|
||||
onPress={() => {
|
||||
handleUpdate();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<LoadingOverlay visible={loading} />
|
||||
@@ -161,20 +179,21 @@ export default function DiscussionDivisionEdit() {
|
||||
value={data}
|
||||
onChange={setData}
|
||||
multiline
|
||||
bg={colors.card}
|
||||
/>
|
||||
|
||||
<ButtonSelect value="Upload File" onPress={pickDocumentAsync} />
|
||||
{
|
||||
(fileForm.length > 0 || dataFile.filter((val) => !val.delete).length > 0)
|
||||
&&
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10, Styles.mb10]}>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, Styles.mb10, { borderColor: colors.background, backgroundColor: colors.card }]}>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>File</Text>
|
||||
{
|
||||
dataFile.filter((val) => !val.delete).map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType={(fileForm.length + dataFile.length) > 1 ? "bottom" : "none"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name + '.' + item.extension}
|
||||
titleWeight="normal"
|
||||
onPress={() => { setIndexDelFile({ id: item.id, cat: "oldFile" }); setModalFile(true) }}
|
||||
@@ -186,7 +205,7 @@ export default function DiscussionDivisionEdit() {
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType={fileForm.length > 1 ? "bottom" : "none"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name}
|
||||
titleWeight="normal"
|
||||
onPress={() => { setIndexDelFile({ id: index, cat: "newFile" }); setModalFile(true) }}
|
||||
@@ -202,7 +221,7 @@ export default function DiscussionDivisionEdit() {
|
||||
<DrawerBottom animation="slide" isVisible={isModalFile} setVisible={setModalFile} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => { deleteFile(indexDelFile.id, indexDelFile.cat) }}
|
||||
/>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import BorderBottomItem2 from "@/components/borderBottomItem2";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import HeaderRightDiscussionDetail from "@/components/discussion/headerDiscussionDetail";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import LabelStatus from "@/components/labelStatus";
|
||||
import MenuItemRow from "@/components/menuItemRow";
|
||||
import ModalConfirmation from "@/components/ModalConfirmation";
|
||||
import Skeleton from "@/components/skeleton";
|
||||
import SkeletonContent from "@/components/skeletonContent";
|
||||
import Text from "@/components/Text";
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
} from "@/lib/api";
|
||||
import { getDB } from "@/lib/firebaseDatabase";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Feather, Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons";
|
||||
import { ref } from "@react-native-firebase/database";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
@@ -64,6 +65,7 @@ type PropsFile = {
|
||||
}
|
||||
|
||||
export default function DiscussionDetail() {
|
||||
const { colors } = useTheme();
|
||||
const { id, detail } = useLocalSearchParams<{ id: string; detail: string }>();
|
||||
const [data, setData] = useState<Props>();
|
||||
const [dataComment, setDataComment] = useState<PropsComment[]>([]);
|
||||
@@ -90,6 +92,7 @@ export default function DiscussionDetail() {
|
||||
comment: ''
|
||||
})
|
||||
const [viewEdit, setViewEdit] = useState(false)
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
|
||||
|
||||
|
||||
@@ -272,31 +275,47 @@ export default function DiscussionDetail() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Diskusi",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () =>
|
||||
(entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision || isCreator ?
|
||||
<HeaderRightDiscussionDetail
|
||||
id={detail}
|
||||
status={data?.status}
|
||||
isActive={data?.isActive}
|
||||
/> : (<></>)
|
||||
,
|
||||
// headerRight: () =>
|
||||
// (entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision || isCreator ?
|
||||
// <HeaderRightDiscussionDetail
|
||||
// id={detail}
|
||||
// status={data?.status}
|
||||
// isActive={data?.isActive}
|
||||
// /> : (<></>)
|
||||
// ,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Diskusi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision || isCreator) ?
|
||||
<HeaderRightDiscussionDetail
|
||||
id={detail}
|
||||
status={data?.status}
|
||||
isActive={data?.isActive}
|
||||
/> : (<></>)
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={{ flex: 1 }}>
|
||||
<View style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<ScrollView
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -337,7 +356,7 @@ export default function DiscussionDetail() {
|
||||
color="grey"
|
||||
style={Styles.mr05}
|
||||
/>
|
||||
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]} >
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }, Styles.mb05]} >
|
||||
{dataComment.length} Komentar
|
||||
</Text>
|
||||
</View>
|
||||
@@ -368,6 +387,7 @@ export default function DiscussionDetail() {
|
||||
desc={item.comment}
|
||||
rightBottomInfo={item.isEdited ? "Edited" : ""}
|
||||
descEllipsize={detailMore.includes(item.id) ? false : true}
|
||||
bgColor="transparent"
|
||||
onPress={() => {
|
||||
setDetailMore((prev: any) => {
|
||||
if (prev.includes(item.id)) {
|
||||
@@ -395,7 +415,7 @@ export default function DiscussionDetail() {
|
||||
style={[
|
||||
Styles.contentItemCenter,
|
||||
Styles.w100,
|
||||
{ backgroundColor: "#f4f4f4" },
|
||||
{ backgroundColor: colors.background },
|
||||
viewEdit && Styles.borderTop
|
||||
]}
|
||||
>
|
||||
@@ -404,15 +424,15 @@ export default function DiscussionDetail() {
|
||||
<>
|
||||
<View style={[Styles.w90, Styles.rowSpaceBetween, Styles.pv05]}>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
<Feather name="edit-3" color="black" size={22} style={[Styles.mh05]} />
|
||||
<Feather name="edit-3" color={colors.text} size={22} style={[Styles.mh05]} />
|
||||
<Text style={[Styles.textMediumSemiBold]}>Edit Komentar</Text>
|
||||
</View>
|
||||
<Pressable onPress={() => handleViewEditKomentar()}>
|
||||
<MaterialIcons name="close" color="black" size={22} />
|
||||
<MaterialIcons name="close" color={colors.text} size={22} />
|
||||
</Pressable>
|
||||
</View>
|
||||
<InputForm
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
type="default"
|
||||
round
|
||||
multiline
|
||||
@@ -459,7 +479,7 @@ export default function DiscussionDetail() {
|
||||
isMemberDivision)
|
||||
?
|
||||
<InputForm
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
type="default"
|
||||
round
|
||||
multiline
|
||||
@@ -502,7 +522,7 @@ export default function DiscussionDetail() {
|
||||
/>
|
||||
:
|
||||
<View style={[Styles.pv20, { alignItems: 'center' }]}>
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }]}>
|
||||
{
|
||||
data?.status == 2 ? "Diskusi telah ditutup" : data?.isActive == false ? "Diskusi telah diarsipkan" : "Hanya anggota divisi yang dapat memberikan komentar"
|
||||
}
|
||||
@@ -516,25 +536,35 @@ export default function DiscussionDetail() {
|
||||
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={setVisible} title="Komentar">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color={colors.text} size={25} />}
|
||||
title="Edit"
|
||||
onPress={() => { handleViewEditKomentar() }}
|
||||
/>
|
||||
<MenuItemRow
|
||||
icon={<MaterialIcons name="delete" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => {
|
||||
AlertKonfirmasi({
|
||||
title: 'Konfirmasi',
|
||||
desc: 'Apakah anda yakin ingin menghapus komentar?',
|
||||
onPress: () => {
|
||||
handleDeleteKomentar()
|
||||
}
|
||||
})
|
||||
setVisible(false)
|
||||
setTimeout(() => {
|
||||
setShowDeleteModal(true)
|
||||
}, 600)
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</DrawerBottom>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showDeleteModal}
|
||||
title="Konfirmasi"
|
||||
message="Apakah anda yakin ingin menghapus komentar?"
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false)
|
||||
handleDeleteKomentar()
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
confirmText="Hapus"
|
||||
cancelText="Batal"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/AppHeader"
|
||||
import BorderBottomItem from "@/components/borderBottomItem"
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader"
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader"
|
||||
import ButtonSelect from "@/components/buttonSelect"
|
||||
import DrawerBottom from "@/components/drawerBottom"
|
||||
@@ -11,6 +11,7 @@ import Styles from "@/constants/Styles"
|
||||
import { apiCreateDiscussion } from "@/lib/api"
|
||||
import { setUpdateDiscussion } from "@/lib/discussionUpdate"
|
||||
import { useAuthSession } from "@/providers/AuthProvider"
|
||||
import { useTheme } from "@/providers/ThemeProvider"
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons"
|
||||
import * as DocumentPicker from "expo-document-picker"
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router"
|
||||
@@ -21,6 +22,7 @@ import { useDispatch, useSelector } from "react-redux"
|
||||
|
||||
|
||||
export default function CreateDiscussionDivision() {
|
||||
const { colors } = useTheme();
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
const [desc, setDesc] = useState('')
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
@@ -88,18 +90,33 @@ export default function CreateDiscussionDivision() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Tambah Diskusi',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <ButtonSaveHeader
|
||||
disable={desc == "" || loading}
|
||||
category="create"
|
||||
onPress={() => {
|
||||
handleCreate()
|
||||
}} />
|
||||
// headerRight: () => <ButtonSaveHeader
|
||||
// disable={desc == "" || loading}
|
||||
// category="create"
|
||||
// onPress={() => {
|
||||
// handleCreate()
|
||||
// }} />
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Diskusi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={desc == "" || loading}
|
||||
category="create"
|
||||
onPress={() => {
|
||||
handleCreate()
|
||||
}} />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<LoadingOverlay visible={loading} />
|
||||
@@ -112,19 +129,20 @@ export default function CreateDiscussionDivision() {
|
||||
required
|
||||
onChange={setDesc}
|
||||
multiline
|
||||
bg={colors.card}
|
||||
/>
|
||||
<ButtonSelect value="Upload File" onPress={pickDocumentAsync} />
|
||||
{
|
||||
fileForm.length > 0
|
||||
&&
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10, Styles.mb10]}>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, Styles.mb10, { borderColor: colors.background, backgroundColor: colors.card }]}>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>File</Text>
|
||||
{
|
||||
fileForm.map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType={fileForm.length > 1 ? "bottom" : "none"}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name}
|
||||
titleWeight="normal"
|
||||
onPress={() => { setIndexDelFile(index); setModalFile(true) }}
|
||||
@@ -139,7 +157,7 @@ export default function CreateDiscussionDivision() {
|
||||
<DrawerBottom animation="slide" isVisible={isModalFile} setVisible={setModalFile} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => { deleteFile(indexDelFile) }}
|
||||
/>
|
||||
|
||||
@@ -5,10 +5,12 @@ import InputSearch from "@/components/inputSearch";
|
||||
import LabelStatus from "@/components/labelStatus";
|
||||
import SkeletonContent from "@/components/skeletonContent";
|
||||
import Text from "@/components/Text";
|
||||
import WrapTab from "@/components/wrapTab";
|
||||
import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetDiscussion, apiGetDivisionOneFeature } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign, Feather, Ionicons } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -30,6 +32,7 @@ type Props = {
|
||||
|
||||
|
||||
export default function DiscussionDivision() {
|
||||
const { colors } = useTheme();
|
||||
const { id, active } = useLocalSearchParams<{ id: string, active?: string }>()
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
@@ -128,26 +131,26 @@ export default function DiscussionDivision() {
|
||||
})
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
{
|
||||
((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision) &&
|
||||
<View>
|
||||
<View style={[Styles.wrapBtnTab]}>
|
||||
<WrapTab>
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="true"
|
||||
onPress={() => { setStatus("true") }}
|
||||
label="Aktif"
|
||||
icon={<Feather name="check-circle" color={status == "false" ? 'black' : 'white'} size={20} />}
|
||||
icon={<Feather name="check-circle" color={status == "false" ? colors.dimmed : 'white'} size={20} />}
|
||||
n={2} />
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="false"
|
||||
onPress={() => { setStatus("false") }}
|
||||
label="Arsip"
|
||||
icon={<AntDesign name="closecircleo" color={status == "true" ? 'black' : 'white'} size={20} />}
|
||||
icon={<AntDesign name="closecircleo" color={status == "true" ? colors.dimmed : 'white'} size={20} />}
|
||||
n={2} />
|
||||
</View>
|
||||
</WrapTab>
|
||||
<InputSearch onChange={setSearch} />
|
||||
</View>
|
||||
}
|
||||
@@ -184,11 +187,12 @@ export default function DiscussionDivision() {
|
||||
desc={item.desc}
|
||||
leftBottomInfo={
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
<Ionicons name="chatbox-ellipses-outline" size={18} color="grey" style={Styles.mr05} />
|
||||
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>Diskusikan</Text>
|
||||
<Ionicons name="chatbox-ellipses-outline" size={18} color={colors.dimmed} style={Styles.mr05} />
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }, Styles.mb05]}>Diskusikan</Text>
|
||||
</View>
|
||||
}
|
||||
rightBottomInfo={item.total_komentar + ' Komentar'}
|
||||
bgColor="transparent"
|
||||
/>
|
||||
)
|
||||
}}
|
||||
@@ -200,11 +204,12 @@ export default function DiscussionDivision() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
:
|
||||
(<Text style={[Styles.textDefault, Styles.cGray, Styles.mv10, { textAlign: "center" }]}>Tidak ada diskusi</Text>)
|
||||
(<Text style={[Styles.textDefault, Styles.mv10, { textAlign: "center", color:colors.dimmed }]}>Tidak ada diskusi</Text>)
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ModalConfirmation from "@/components/ModalConfirmation";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import { ButtonHeader } from "@/components/buttonHeader";
|
||||
import HeaderRightDocument from "@/components/document/headerDocument";
|
||||
import ItemFile from "@/components/document/itemFile";
|
||||
@@ -12,7 +12,6 @@ import ModalLoading from "@/components/modalLoading";
|
||||
import ModalSelectMultiple from "@/components/modalSelectMultiple";
|
||||
import Skeleton from "@/components/skeleton";
|
||||
import Text from "@/components/Text";
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import Styles from "@/constants/Styles";
|
||||
import {
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
} from "@/lib/api";
|
||||
import { setUpdateDokumen } from "@/lib/dokumenUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import {
|
||||
AntDesign,
|
||||
MaterialCommunityIcons,
|
||||
@@ -66,6 +66,7 @@ type PropsPath = {
|
||||
};
|
||||
|
||||
export default function DocumentDivision() {
|
||||
const { colors } = useTheme();
|
||||
const [loadingRename, setLoadingRename] = useState(false)
|
||||
const [isShare, setShare] = useState(false)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
@@ -88,6 +89,7 @@ export default function DocumentDivision() {
|
||||
const [loadingOpen, setLoadingOpen] = useState(false)
|
||||
const [isMemberDivision, setIsMemberDivision] = useState(false)
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
const [bodyRename, setBodyRename] = useState({
|
||||
id: "",
|
||||
name: "",
|
||||
@@ -334,51 +336,86 @@ export default function DocumentDivision() {
|
||||
}, [path]);
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () =>
|
||||
selectedFiles.length > 0 || dariSelectAll ? (
|
||||
<ButtonHeader
|
||||
item={<MaterialIcons name="close" size={20} color="white" />}
|
||||
onPress={() => {
|
||||
handleBatal();
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () =>
|
||||
// selectedFiles.length > 0 || dariSelectAll ? (
|
||||
// <ButtonHeader
|
||||
// item={<MaterialIcons name="close" size={20} color="white" />}
|
||||
// onPress={() => {
|
||||
// handleBatal();
|
||||
// }}
|
||||
// />
|
||||
// ) : (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle:
|
||||
selectedFiles.length > 0 || dariSelectAll
|
||||
? `${selectedFiles.length} item terpilih`
|
||||
: "Dokumen Divisi",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () =>
|
||||
selectedFiles.length > 0 || dariSelectAll ? (
|
||||
<ButtonHeader
|
||||
item={
|
||||
<MaterialIcons name="checklist-rtl" size={20} color="white" />
|
||||
}
|
||||
onPress={() => {
|
||||
handleSelectAll();
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<HeaderRightDocument path={path} isMember={isMemberDivision} />
|
||||
),
|
||||
// headerRight: () =>
|
||||
// selectedFiles.length > 0 || dariSelectAll ? (
|
||||
// <ButtonHeader
|
||||
// item={
|
||||
// <MaterialIcons name="checklist-rtl" size={20} color="white" />
|
||||
// }
|
||||
// onPress={() => {
|
||||
// handleSelectAll();
|
||||
// }}
|
||||
// />
|
||||
// ) : (
|
||||
// <HeaderRightDocument path={path} isMember={isMemberDivision} />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title={
|
||||
selectedFiles.length > 0 || dariSelectAll
|
||||
? `${selectedFiles.length} item terpilih`
|
||||
: "Dokumen Divisi"
|
||||
}
|
||||
showBack={(selectedFiles.length > 0 || dariSelectAll) ? false : true}
|
||||
left={
|
||||
<ButtonHeader
|
||||
item={<MaterialIcons name="close" size={25} color="white" />}
|
||||
onPress={() => {
|
||||
handleBatal();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
onPressLeft={() => {
|
||||
(selectedFiles.length > 0 || dariSelectAll) ? handleBatal() : router.back();
|
||||
}}
|
||||
right={
|
||||
selectedFiles.length > 0 || dariSelectAll ? (
|
||||
<ButtonHeader
|
||||
item={
|
||||
<MaterialIcons name="checklist-rtl" size={25} color="white" />
|
||||
}
|
||||
onPress={() => {
|
||||
handleSelectAll();
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<HeaderRightDocument path={path} isMember={isMemberDivision} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ModalLoading isVisible={loadingOpen} setVisible={setLoadingOpen} />
|
||||
<ScrollView
|
||||
style={[Styles.h100]}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
@@ -393,9 +430,9 @@ export default function DocumentDivision() {
|
||||
}}
|
||||
>
|
||||
{item.id != "home" && (
|
||||
<AntDesign name="right" style={[Styles.mh05, Styles.mt02]} color="black" />
|
||||
<AntDesign name="right" style={[Styles.mh05, Styles.mt02]} color={colors.text} />
|
||||
)}
|
||||
<Text> {item.name} </Text>
|
||||
<Text style={{ color: colors.text }}> {item.name} </Text>
|
||||
</Pressable>
|
||||
))
|
||||
}
|
||||
@@ -444,14 +481,7 @@ export default function DocumentDivision() {
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<Text
|
||||
style={[
|
||||
Styles.textDefault,
|
||||
Styles.cGray,
|
||||
Styles.mt15,
|
||||
{ textAlign: "center" },
|
||||
]}
|
||||
>
|
||||
<Text style={[Styles.textDefault, Styles.mt15, { textAlign: "center", color: colors.dimmed }]} >
|
||||
Tidak ada dokumen
|
||||
</Text>
|
||||
)}
|
||||
@@ -459,7 +489,7 @@ export default function DocumentDivision() {
|
||||
</View>
|
||||
</ScrollView>
|
||||
{(selectedFiles.length > 0 || dariSelectAll) && (
|
||||
<View style={[ColorsStatus.primary, Styles.bottomMenuSelectDocument]}>
|
||||
<View style={[Styles.bottomMenuSelectDocument, { backgroundColor: colors.header }]}>
|
||||
<View style={[Styles.rowItemsCenter, { justifyContent: "center" }]}>
|
||||
<MenuItemRow
|
||||
icon={
|
||||
@@ -471,13 +501,7 @@ export default function DocumentDivision() {
|
||||
}
|
||||
title="Hapus"
|
||||
onPress={() => {
|
||||
AlertKonfirmasi({
|
||||
title: "Konfirmasi",
|
||||
desc: "Apakah anda yakin ingin menghapus dokumen?",
|
||||
onPress: () => {
|
||||
handleDelete();
|
||||
},
|
||||
});
|
||||
setShowDeleteModal(true)
|
||||
}}
|
||||
column="many"
|
||||
color="white"
|
||||
@@ -584,6 +608,19 @@ export default function DocumentDivision() {
|
||||
value={id}
|
||||
item={selectedFiles[0]?.id}
|
||||
/>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showDeleteModal}
|
||||
title="Konfirmasi"
|
||||
message="Apakah anda yakin ingin menghapus dokumen?"
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false)
|
||||
handleDelete()
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
confirmText="Hapus"
|
||||
cancelText="Batal"
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
@@ -9,6 +9,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiAddFileTask, apiCheckFileTask } from "@/lib/api";
|
||||
import { setUpdateTask } from "@/lib/taskUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import * as DocumentPicker from "expo-document-picker";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
@@ -23,6 +24,7 @@ import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function TaskDivisionAddFile() {
|
||||
const { colors } = useTheme();
|
||||
const { id, detail } = useLocalSearchParams<{ id: string; detail: string }>();
|
||||
const [fileForm, setFileForm] = useState<any[]>([]);
|
||||
const [listFile, setListFile] = useState<any[]>([]);
|
||||
@@ -127,25 +129,39 @@ export default function TaskDivisionAddFile() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah File",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={fileForm.length == 0 || loading ? true : false}
|
||||
onPress={() => { handleAddFile() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="create"
|
||||
// disable={fileForm.length == 0 || loading ? true : false}
|
||||
// onPress={() => { handleAddFile() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah File"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={fileForm.length == 0 || loading ? true : false}
|
||||
onPress={() => { handleAddFile() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
@@ -155,13 +171,13 @@ export default function TaskDivisionAddFile() {
|
||||
listFile.length > 0 && (
|
||||
<View style={[Styles.mb15]}>
|
||||
<Text style={[Styles.textDefaultSemiBold, Styles.mv05]}>File</Text>
|
||||
<View style={[Styles.wrapPaper]}>
|
||||
<View style={[Styles.wrapPaper, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
{
|
||||
listFile.map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType="all"
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item}
|
||||
titleWeight="normal"
|
||||
onPress={() => { setIndexDelFile(index); setModal(true) }}
|
||||
@@ -183,7 +199,7 @@ export default function TaskDivisionAddFile() {
|
||||
<DrawerBottom animation="slide" isVisible={isModal} setVisible={setModal} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => { deleteFile(indexDelFile) }}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import ImageWithLabel from "@/components/imageWithLabel";
|
||||
@@ -9,6 +9,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiAddMemberTask, apiGetDivisionMember, apiGetTaskOne } from "@/lib/api";
|
||||
import { setUpdateTask } from "@/lib/taskUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -23,6 +24,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function AddMemberTask() {
|
||||
const { colors } = useTheme();
|
||||
const dispatch = useDispatch()
|
||||
const update = useSelector((state: any) => state.projectUpdate)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
@@ -97,21 +99,37 @@ export default function AddMemberTask() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Tambah Anggota Kegiatan',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="update"
|
||||
// disable={selectMember.length == 0 || loading ? true : false}
|
||||
// onPress={() => {
|
||||
// handleAddMember()
|
||||
// }}
|
||||
// />
|
||||
// )
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Anggota Kegiatan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<InputSearch onChange={(val) => setSearch(val)} value={search} />
|
||||
|
||||
{
|
||||
@@ -133,7 +151,7 @@ export default function AddMemberTask() {
|
||||
</View>
|
||||
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
|
||||
<Text style={[Styles.textDefault, Styles.pv05, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada member yang dipilih</Text>
|
||||
}
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
@@ -146,22 +164,22 @@ export default function AddMemberTask() {
|
||||
return (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[Styles.itemSelectModal]}
|
||||
style={[Styles.itemSelectModal, { borderColor: colors.icon + '20' }]}
|
||||
onPress={() => {
|
||||
!found && onChoose(item.idUser, item.name, item.img)
|
||||
}}
|
||||
>
|
||||
<View style={[Styles.rowItemsCenter, Styles.w80]}>
|
||||
<View style={[Styles.rowItemsCenter, Styles.w80,]}>
|
||||
<ImageUser src={`${ConstEnv.url_storage}/files/${item.img}`} border />
|
||||
<View style={[Styles.ml10]}>
|
||||
<Text style={[Styles.textDefault]} numberOfLines={1}>{item.name}</Text>
|
||||
{
|
||||
found && <Text style={[Styles.textInformation, Styles.cGray]}>sudah menjadi anggota</Text>
|
||||
found && <Text style={[Styles.textInformation, { color: colors.dimmed }]}>sudah menjadi anggota</Text>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
selectMember.some((i: any) => i.idUser == item.idUser) && <AntDesign name="check" size={20} color={'black'} />
|
||||
selectMember.some((i: any) => i.idUser == item.idUser) && <AntDesign name="check" size={20} color={colors.text} />
|
||||
}
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import ModalAddDetailTugasTask from "@/components/task/modalAddDetailTugasTask";
|
||||
import Text from "@/components/Text";
|
||||
@@ -9,6 +10,7 @@ import { formatDateOnly } from "@/lib/fun_formatDateOnly";
|
||||
import { getDatesInRange } from "@/lib/fun_getDatesInRange";
|
||||
import { setUpdateTask } from "@/lib/taskUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import 'intl';
|
||||
@@ -16,7 +18,8 @@ import 'intl/locale-data/jsonp/id';
|
||||
import moment from "moment";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
KeyboardAvoidingView, Platform, Pressable, SafeAreaView,
|
||||
KeyboardAvoidingView, Platform,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
View
|
||||
} from "react-native";
|
||||
@@ -25,6 +28,7 @@ import DateTimePicker, { DateType } from "react-native-ui-datepicker";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function TaskDivisionAddTask() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const dispatch = useDispatch();
|
||||
const update = useSelector((state: any) => state.taskUpdate);
|
||||
@@ -138,27 +142,43 @@ export default function TaskDivisionAddTask() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah Tugas",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={disable || loading}
|
||||
onPress={() => {
|
||||
handleCreate();
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="create"
|
||||
// disable={disable || loading}
|
||||
// onPress={() => {
|
||||
// handleCreate();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Tugas"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={disable || loading}
|
||||
onPress={() => {
|
||||
handleCreate();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
@@ -167,7 +187,7 @@ export default function TaskDivisionAddTask() {
|
||||
>
|
||||
<ScrollView>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
<DateTimePicker
|
||||
mode="range"
|
||||
startDate={range.startDate}
|
||||
@@ -177,13 +197,13 @@ export default function TaskDivisionAddTask() {
|
||||
selected: Styles.selectedDate,
|
||||
selected_label: Styles.cWhite,
|
||||
range_fill: Styles.selectRangeDate,
|
||||
month_label: Styles.cBlack,
|
||||
month_selector_label: Styles.cBlack,
|
||||
year_label: Styles.cBlack,
|
||||
year_selector_label: Styles.cBlack,
|
||||
day_label: Styles.cBlack,
|
||||
time_label: Styles.cBlack,
|
||||
weekday_label: Styles.cBlack,
|
||||
month_label: { color: colors.text },
|
||||
month_selector_label: { color: colors.text },
|
||||
year_label: { color: colors.text },
|
||||
year_selector_label: { color: colors.text },
|
||||
day_label: { color: colors.text },
|
||||
time_label: { color: colors.text },
|
||||
weekday_label: { color: colors.text },
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -191,38 +211,39 @@ export default function TaskDivisionAddTask() {
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
<View style={[{ width: "48%" }]}>
|
||||
<Text style={[Styles.mb05]}>
|
||||
Tanggal Mulai <Text style={Styles.cError}>*</Text>
|
||||
Tanggal Mulai <Text style={{ color: colors.error }}>*</Text>
|
||||
</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: "center" }}>{from}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={[{ width: "48%" }]}>
|
||||
<Text style={[Styles.mb05]}>
|
||||
Tanggal Berakhir <Text style={Styles.cError}>*</Text>
|
||||
Tanggal Berakhir <Text style={{ color: colors.error }}>*</Text>
|
||||
</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: "center" }}>{to}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, Styles.cError, Styles.mt05]}>Tanggal tidak boleh kosong</Text>
|
||||
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, { color: colors.error }, Styles.mt05]}>Tanggal tidak boleh kosong</Text>
|
||||
}
|
||||
<Pressable
|
||||
{/* <Pressable
|
||||
style={[Styles.btnTab, Styles.btnLainnya, dsbButton && Styles.btnDisabled]}
|
||||
disabled={dsbButton}
|
||||
onPress={() => { setModalDetail(true) }}
|
||||
>
|
||||
<Text style={[dsbButton ? Styles.cGray : Styles.cWhite]}>Detail</Text>
|
||||
</Pressable>
|
||||
</Pressable> */}
|
||||
<ButtonSelect value="Detail" onPress={() => { setModalDetail(true) }} />
|
||||
</View>
|
||||
<InputForm
|
||||
label="Judul Tugas"
|
||||
type="default"
|
||||
placeholder="Judul Tugas"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={title}
|
||||
error={error.title}
|
||||
errorText="Judul tidak boleh kosong"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiCancelTask } from "@/lib/api";
|
||||
import { setUpdateTask } from "@/lib/taskUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SafeAreaView, ScrollView, View } from "react-native";
|
||||
@@ -12,6 +13,7 @@ import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function TaskDivisionCancel() {
|
||||
const { colors } = useTheme();
|
||||
const { id, detail } = useLocalSearchParams<{ id: string; detail: string }>();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const dispatch = useDispatch();
|
||||
@@ -69,27 +71,43 @@ export default function TaskDivisionCancel() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Pembatalan Tugas",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disable || loading}
|
||||
category="cancel"
|
||||
onPress={() => {
|
||||
handleCancel();
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disable || loading}
|
||||
// category="cancel"
|
||||
// onPress={() => {
|
||||
// handleCancel();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Pembatalan Tugas"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disable || loading}
|
||||
category="cancel"
|
||||
onPress={() => {
|
||||
handleCancel();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
@@ -99,7 +117,7 @@ export default function TaskDivisionCancel() {
|
||||
type="default"
|
||||
placeholder="Alasan Pembatalan"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
error={error}
|
||||
errorText="Alasan pembatalan harus diisi"
|
||||
onChange={(val) => onValidation(val)}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiEditTask, apiGetTaskOne } from "@/lib/api";
|
||||
import { setUpdateTask } from "@/lib/taskUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SafeAreaView, ScrollView, View } from "react-native";
|
||||
@@ -12,6 +13,7 @@ import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function TaskDivisionEdit() {
|
||||
const { colors } = useTheme();
|
||||
const { id, detail } = useLocalSearchParams<{ id: string; detail: string }>();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const [judul, setJudul] = useState("");
|
||||
@@ -87,25 +89,38 @@ export default function TaskDivisionEdit() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Edit Judul",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={disable || loading}
|
||||
onPress={() => { handleUpdate() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="update"
|
||||
// disable={disable || loading}
|
||||
// onPress={() => { handleUpdate() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader title="Tambah Kegiatan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={disable || loading}
|
||||
onPress={() => { handleUpdate() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
@@ -115,7 +130,7 @@ export default function TaskDivisionEdit() {
|
||||
type="default"
|
||||
placeholder="Judul Kegiatan"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={judul}
|
||||
onChange={(val) => { onValidation(val) }}
|
||||
error={error}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import SectionCancel from "@/components/sectionCancel";
|
||||
import SectionProgress from "@/components/sectionProgress";
|
||||
import HeaderRightTaskDetail from "@/components/task/headerTaskDetail";
|
||||
@@ -10,6 +10,7 @@ import SectionTanggalTugasTask from "@/components/task/sectionTanggalTugasTask";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetDivisionOneFeature, apiGetTaskOne } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
@@ -25,6 +26,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function DetailTaskDivision() {
|
||||
const { colors } = useTheme();
|
||||
const { id, detail } = useLocalSearchParams<{ id: string, detail: string }>();
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const [data, setData] = useState<Props>()
|
||||
@@ -97,15 +99,27 @@ export default function DetailTaskDivision() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: loading ? 'Loading... ' : data?.title,
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (entityUser.role == "user" || entityUser.role == "coadmin") && !isMemberDivision
|
||||
? <></>
|
||||
: <HeaderRightTaskDetail id={detail} division={id} status={data?.status} isAdminDivision={isAdminDivision} />,
|
||||
// headerRight: () => (entityUser.role == "user" || entityUser.role == "coadmin") && !isMemberDivision
|
||||
// ? <></>
|
||||
// : <HeaderRightTaskDetail id={detail} division={id} status={data?.status} isAdminDivision={isAdminDivision} />,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title={loading ? 'Loading...' : data ? data?.title : ''}
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
(entityUser.role == "user" || entityUser.role == "coadmin") && !isMemberDivision
|
||||
? <></>
|
||||
: <HeaderRightTaskDetail id={detail} division={id} status={data?.status} isAdminDivision={isAdminDivision} />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
@@ -113,6 +127,7 @@ export default function DetailTaskDivision() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetTaskOne, apiReportTask } from "@/lib/api";
|
||||
import { setUpdateTask } from "@/lib/taskUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SafeAreaView, ScrollView, View } from "react-native";
|
||||
@@ -12,6 +13,7 @@ import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function TaskDivisionReport() {
|
||||
const { colors } = useTheme();
|
||||
const { id, detail } = useLocalSearchParams<{ id: string; detail: string }>();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const [laporan, setLaporan] = useState("");
|
||||
@@ -87,25 +89,38 @@ export default function TaskDivisionReport() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Laporan Kegiatan",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={disable || loading}
|
||||
onPress={() => { handleUpdate() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="update"
|
||||
// disable={disable || loading}
|
||||
// onPress={() => { handleUpdate() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader title="Laporan Kegiatan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={disable || loading}
|
||||
onPress={() => { handleUpdate() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
@@ -115,7 +130,7 @@ export default function TaskDivisionReport() {
|
||||
type="default"
|
||||
placeholder="Laporan Kegiatan"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={laporan}
|
||||
onChange={(val) => { onValidation(val) }}
|
||||
error={error}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
@@ -16,6 +16,7 @@ import { setMemberChoose } from "@/lib/memberChoose";
|
||||
import { setTaskCreate } from "@/lib/taskCreate";
|
||||
import { setUpdateTask } from "@/lib/taskUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import * as DocumentPicker from "expo-document-picker";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
@@ -26,6 +27,7 @@ import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
|
||||
export default function CreateTaskDivision() {
|
||||
const { colors } = useTheme();
|
||||
const { id } = useLocalSearchParams();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const dispatch = useDispatch();
|
||||
@@ -113,25 +115,39 @@ export default function CreateTaskDivision() {
|
||||
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
handleBack();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// handleBack();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: `Tambah Tugas`,
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={title == "" || entitiesMember.length == 0 || taskCreate.length == 0 || loading}
|
||||
category="create"
|
||||
onPress={() => { handleCreate() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={title == "" || entitiesMember.length == 0 || taskCreate.length == 0 || loading}
|
||||
// category="create"
|
||||
// onPress={() => { handleCreate() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Tugas"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={title == "" || entitiesMember.length == 0 || taskCreate.length == 0 || loading}
|
||||
category="create"
|
||||
onPress={() => { handleCreate() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
@@ -147,6 +163,7 @@ export default function CreateTaskDivision() {
|
||||
val == "" || val == "null" ? setError(true) : setError(false);
|
||||
}}
|
||||
error={error}
|
||||
bg={colors.card}
|
||||
errorText="Judul Tugas tidak boleh kosong"
|
||||
/>
|
||||
<ButtonSelect value="Tambah Tanggal & Tugas" onPress={() => { router.push(`/division/${id}/task/create/task`); }} />
|
||||
@@ -157,13 +174,13 @@ export default function CreateTaskDivision() {
|
||||
fileForm.length > 0 && (
|
||||
<View style={[Styles.mb15]}>
|
||||
<Text style={[Styles.textDefaultSemiBold, Styles.mv05]}>File</Text>
|
||||
<View style={[Styles.wrapPaper]}>
|
||||
<View style={[Styles.wrapPaper, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
{
|
||||
fileForm.map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType="all"
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name}
|
||||
titleWeight="normal"
|
||||
onPress={() => { setIndexDelFile(index); setModal(true) }}
|
||||
@@ -181,7 +198,7 @@ export default function CreateTaskDivision() {
|
||||
<Text>Total {entitiesMember.length} Anggota</Text>
|
||||
</View>
|
||||
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10]}>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
{entitiesMember.map(
|
||||
(item: { img: any; name: any }, index: any) => {
|
||||
return (
|
||||
@@ -209,7 +226,7 @@ export default function CreateTaskDivision() {
|
||||
<DrawerBottom animation="slide" isVisible={isModal} setVisible={setModal} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => { deleteFile(indexDelFile) }}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import ImageWithLabel from "@/components/imageWithLabel";
|
||||
@@ -9,6 +9,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiGetDivisionMember } from "@/lib/api";
|
||||
import { setMemberChoose } from "@/lib/memberChoose";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -23,6 +24,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function AddMemberCreateTask() {
|
||||
const { colors } = useTheme();
|
||||
const dispatch = useDispatch()
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { id } = useLocalSearchParams<{ id: string, detail: string }>()
|
||||
@@ -67,21 +69,37 @@ export default function AddMemberCreateTask() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Pilih Anggota',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={selectMember.length > 0 ? false : true}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="create"
|
||||
// disable={selectMember.length > 0 ? false : true}
|
||||
// onPress={() => {
|
||||
// handleAddMember()
|
||||
// }}
|
||||
// />
|
||||
// )
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Pilih Anggota"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={selectMember.length > 0 ? false : true}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<InputSearch onChange={(val) => setSearch(val)} value={search} />
|
||||
|
||||
{
|
||||
@@ -103,7 +121,7 @@ export default function AddMemberCreateTask() {
|
||||
</View>
|
||||
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
|
||||
<Text style={[Styles.textDefault, Styles.pv05, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada member yang dipilih</Text>
|
||||
}
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
@@ -115,7 +133,7 @@ export default function AddMemberCreateTask() {
|
||||
return (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[Styles.itemSelectModal]}
|
||||
style={[Styles.itemSelectModal, { borderColor: colors.icon + '20' }]}
|
||||
onPress={() => {
|
||||
onChoose(item.idUser, item.name, item.img)
|
||||
}}
|
||||
@@ -127,7 +145,7 @@ export default function AddMemberCreateTask() {
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
selectMember.some((i: any) => i.idUser == item.idUser) && <AntDesign name="check" size={20} color={'black'} />
|
||||
selectMember.some((i: any) => i.idUser == item.idUser) && <AntDesign name="check" size={20} color={colors.text} />
|
||||
}
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import ModalAddDetailTugasTask from "@/components/task/modalAddDetailTugasTask";
|
||||
import Text from "@/components/Text";
|
||||
@@ -7,6 +8,7 @@ import Styles from "@/constants/Styles";
|
||||
import { formatDateOnly } from "@/lib/fun_formatDateOnly";
|
||||
import { getDatesInRange } from "@/lib/fun_getDatesInRange";
|
||||
import { setTaskCreate } from "@/lib/taskCreate";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import { router, Stack } from "expo-router";
|
||||
import 'intl';
|
||||
@@ -16,7 +18,6 @@ import { useEffect, useState } from "react";
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Pressable,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
View
|
||||
@@ -27,6 +28,7 @@ import DateTimePicker, {
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function CreateTaskAddTugas() {
|
||||
const { colors } = useTheme();
|
||||
const headerHeight = useHeaderHeight();
|
||||
const dispatch = useDispatch()
|
||||
const [disable, setDisable] = useState(true);
|
||||
@@ -118,25 +120,38 @@ export default function CreateTaskAddTugas() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah Tugas",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disable}
|
||||
category="create"
|
||||
onPress={() => { handleCreate() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disable}
|
||||
// category="create"
|
||||
// onPress={() => { handleCreate() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader title="Tambah Tugas"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disable}
|
||||
category="create"
|
||||
onPress={() => { handleCreate() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
@@ -145,7 +160,7 @@ export default function CreateTaskAddTugas() {
|
||||
>
|
||||
<ScrollView>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
<DateTimePicker
|
||||
mode="range"
|
||||
startDate={range.startDate}
|
||||
@@ -155,13 +170,13 @@ export default function CreateTaskAddTugas() {
|
||||
selected: Styles.selectedDate,
|
||||
selected_label: Styles.cWhite,
|
||||
range_fill: Styles.selectRangeDate,
|
||||
month_label: Styles.cBlack,
|
||||
month_selector_label: Styles.cBlack,
|
||||
year_label: Styles.cBlack,
|
||||
year_selector_label: Styles.cBlack,
|
||||
day_label: Styles.cBlack,
|
||||
time_label: Styles.cBlack,
|
||||
weekday_label: Styles.cBlack,
|
||||
month_label: { color: colors.text },
|
||||
month_selector_label: { color: colors.text },
|
||||
year_label: { color: colors.text },
|
||||
year_selector_label: { color: colors.text },
|
||||
day_label: { color: colors.text },
|
||||
time_label: { color: colors.text },
|
||||
weekday_label: { color: colors.text },
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -169,38 +184,39 @@ export default function CreateTaskAddTugas() {
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
<View style={[{ width: "48%" }]}>
|
||||
<Text style={[Styles.mb05]}>
|
||||
Tanggal Mulai <Text style={Styles.cError}>*</Text>
|
||||
Tanggal Mulai <Text style={{ color: colors.error }}>*</Text>
|
||||
</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: "center" }}>{from}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={[{ width: "48%" }]}>
|
||||
<Text style={[Styles.mb05]}>
|
||||
Tanggal Berakhir <Text style={Styles.cError}>*</Text>
|
||||
Tanggal Berakhir <Text style={{ color: colors.error }}>*</Text>
|
||||
</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: "center" }}>{to}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, Styles.cError, Styles.mt05]}>Tanggal tidak boleh kosong</Text>
|
||||
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, { color: colors.error }, Styles.mt05]}>Tanggal tidak boleh kosong</Text>
|
||||
}
|
||||
<Pressable
|
||||
{/* <Pressable
|
||||
style={[Styles.btnTab, Styles.btnLainnya, dsbButton && Styles.btnDisabled]}
|
||||
disabled={dsbButton}
|
||||
onPress={() => { setModalDetail(true) }}
|
||||
>
|
||||
<Text style={[dsbButton ? Styles.cGray : Styles.cWhite]}>Detail</Text>
|
||||
</Pressable>
|
||||
</Pressable> */}
|
||||
<ButtonSelect value="Detail" onPress={() => { setModalDetail(true) }} />
|
||||
</View>
|
||||
<InputForm
|
||||
label="Judul Tugas"
|
||||
type="default"
|
||||
placeholder="Judul Tugas"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={title}
|
||||
error={error.title}
|
||||
errorText="Judul tidak boleh kosong"
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetTask } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import {
|
||||
AntDesign,
|
||||
Ionicons,
|
||||
@@ -31,7 +32,8 @@ type Props = {
|
||||
};
|
||||
|
||||
export default function ListTask() {
|
||||
const { id, status } = useLocalSearchParams<{ id: string; status: string }>()
|
||||
const { colors } = useTheme()
|
||||
const { id, status, year } = useLocalSearchParams<{ id: string; status: string; year: string }>()
|
||||
const [isList, setList] = useState(false)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
@@ -43,6 +45,8 @@ export default function ListTask() {
|
||||
const [page, setPage] = useState(1)
|
||||
const [waiting, setWaiting] = useState(false)
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const [isYear, setYear] = useState("")
|
||||
|
||||
|
||||
async function handleLoad(loading: boolean, thisPage: number) {
|
||||
try {
|
||||
@@ -55,8 +59,12 @@ export default function ListTask() {
|
||||
division: id,
|
||||
status: statusFix,
|
||||
search,
|
||||
page: thisPage
|
||||
page: thisPage,
|
||||
year
|
||||
});
|
||||
|
||||
setYear(response.tahun)
|
||||
|
||||
if (thisPage == 1) {
|
||||
setData(response.data);
|
||||
} else if (thisPage > 1 && response.data.length > 0) {
|
||||
@@ -104,7 +112,7 @@ export default function ListTask() {
|
||||
})
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<View>
|
||||
<ScrollView horizontal style={[Styles.mb10]} showsHorizontalScrollIndicator={false}>
|
||||
<ButtonTab
|
||||
@@ -115,7 +123,7 @@ export default function ListTask() {
|
||||
icon={
|
||||
<MaterialCommunityIcons
|
||||
name="clock-alert-outline"
|
||||
color={statusFix == "0" ? "white" : "black"}
|
||||
color={statusFix == "0" ? "white" : colors.dimmed}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
@@ -129,7 +137,7 @@ export default function ListTask() {
|
||||
icon={
|
||||
<MaterialCommunityIcons
|
||||
name="progress-check"
|
||||
color={statusFix == "1" ? "white" : "black"}
|
||||
color={statusFix == "1" ? "white" : colors.dimmed}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
@@ -143,7 +151,7 @@ export default function ListTask() {
|
||||
icon={
|
||||
<Ionicons
|
||||
name="checkmark-done-circle-outline"
|
||||
color={statusFix == "2" ? "white" : "black"}
|
||||
color={statusFix == "2" ? "white" : colors.dimmed}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
@@ -157,7 +165,7 @@ export default function ListTask() {
|
||||
icon={
|
||||
<AntDesign
|
||||
name="closecircleo"
|
||||
color={statusFix == "3" ? "white" : "black"}
|
||||
color={statusFix == "3" ? "white" : colors.dimmed}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
@@ -173,13 +181,19 @@ export default function ListTask() {
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={isList ? "format-list-bulleted" : "view-grid"}
|
||||
color={"black"}
|
||||
color={colors.text}
|
||||
size={30}
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
<View style={[{ flex: 2 }, Styles.mt05]}>
|
||||
<View style={[Styles.mv05]}>
|
||||
<View style={[Styles.rowOnly]}>
|
||||
<Text style={[Styles.mr05]}>Filter :</Text>
|
||||
<LabelStatus size="small" category="secondary" text={isYear} style={{ marginRight: 5 }} />
|
||||
</View>
|
||||
</View>
|
||||
<View style={[{ flex: 2 }]}>
|
||||
{
|
||||
loading ?
|
||||
isList ?
|
||||
@@ -221,6 +235,7 @@ export default function ListTask() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -262,11 +277,11 @@ export default function ListTask() {
|
||||
<LabelStatus
|
||||
size="default"
|
||||
category={
|
||||
item.status === 0 ? 'primary' :
|
||||
item.status === 0 ? 'secondary' :
|
||||
item.status === 1 ? 'warning' :
|
||||
item.status === 2 ? 'success' :
|
||||
item.status === 3 ? 'error' :
|
||||
'primary'
|
||||
'secondary'
|
||||
}
|
||||
text={
|
||||
item.status === 0 ? 'SEGERA' :
|
||||
@@ -287,6 +302,7 @@ export default function ListTask() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -326,7 +342,7 @@ export default function ListTask() {
|
||||
</View>
|
||||
)
|
||||
) : (
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: "center" },]} >
|
||||
<Text style={[Styles.textDefault, { textAlign: "center", color: colors.dimmed }]} >
|
||||
Tidak ada data
|
||||
</Text>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import ModalAddDetailTugasTask from "@/components/task/modalAddDetailTugasTask";
|
||||
import Text from "@/components/Text";
|
||||
@@ -9,6 +10,7 @@ import { formatDateOnly } from "@/lib/fun_formatDateOnly";
|
||||
import { getDatesInRange } from "@/lib/fun_getDatesInRange";
|
||||
import { setUpdateTask } from "@/lib/taskUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import 'intl';
|
||||
@@ -18,7 +20,6 @@ import { useEffect, useState } from "react";
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Pressable,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
View
|
||||
@@ -28,6 +29,7 @@ import DateTimePicker, { DateType } from "react-native-ui-datepicker";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function UpdateProjectTaskDivision() {
|
||||
const { colors } = useTheme();
|
||||
const headerHeight = useHeaderHeight();
|
||||
const { detail } = useLocalSearchParams<{ detail: string }>();
|
||||
const dispatch = useDispatch();
|
||||
@@ -186,27 +188,43 @@ export default function UpdateProjectTaskDivision() {
|
||||
}, [range])
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Edit Tanggal dan Tugas",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loadingSubmit}
|
||||
category="update"
|
||||
onPress={() => {
|
||||
handleEdit()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disableBtn || loadingSubmit}
|
||||
// category="update"
|
||||
// onPress={() => {
|
||||
// handleEdit()
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Tanggal dan Tugas"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loadingSubmit}
|
||||
category="update"
|
||||
onPress={() => {
|
||||
handleEdit()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
@@ -215,7 +233,7 @@ export default function UpdateProjectTaskDivision() {
|
||||
>
|
||||
<ScrollView>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
{!loading && (
|
||||
<DateTimePicker
|
||||
mode="range"
|
||||
@@ -228,13 +246,13 @@ export default function UpdateProjectTaskDivision() {
|
||||
selected: Styles.selectedDate,
|
||||
selected_label: Styles.cWhite,
|
||||
range_fill: Styles.selectRangeDate,
|
||||
month_label: Styles.cBlack,
|
||||
month_selector_label: Styles.cBlack,
|
||||
year_label: Styles.cBlack,
|
||||
year_selector_label: Styles.cBlack,
|
||||
day_label: Styles.cBlack,
|
||||
time_label: Styles.cBlack,
|
||||
weekday_label: Styles.cBlack,
|
||||
month_label: { color: colors.text },
|
||||
month_selector_label: { color: colors.text },
|
||||
year_label: { color: colors.text },
|
||||
year_selector_label: { color: colors.text },
|
||||
day_label: { color: colors.text },
|
||||
time_label: { color: colors.text },
|
||||
weekday_label: { color: colors.text },
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -243,40 +261,41 @@ export default function UpdateProjectTaskDivision() {
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
<View style={[{ width: "48%" }]}>
|
||||
<Text style={[Styles.mb05]}>
|
||||
Tanggal Mulai <Text style={Styles.cError}>*</Text>
|
||||
Tanggal Mulai <Text style={{ color: colors.error }}>*</Text>
|
||||
</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: "center" }}>{from}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={[{ width: "48%" }]}>
|
||||
<Text style={[Styles.mb05]}>
|
||||
Tanggal Berakhir <Text style={Styles.cError}>*</Text>
|
||||
Tanggal Berakhir <Text style={{ color: colors.error }}>*</Text>
|
||||
</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: "center" }}>{to}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{(error.endDate || error.startDate) && (
|
||||
<Text style={[Styles.textInformation, Styles.cError, Styles.mt05]} >
|
||||
<Text style={[Styles.textInformation, { color: colors.error }, Styles.mt05]} >
|
||||
Tanggal tidak boleh kosong
|
||||
</Text>
|
||||
)}
|
||||
<Pressable
|
||||
{/* <Pressable
|
||||
style={[Styles.btnTab, Styles.btnLainnya, dsbButton && Styles.btnDisabled]}
|
||||
disabled={dsbButton}
|
||||
onPress={() => { setModalDetail(true) }}
|
||||
>
|
||||
<Text style={[dsbButton ? Styles.cGray : Styles.cWhite]}>Detail</Text>
|
||||
</Pressable>
|
||||
</Pressable> */}
|
||||
<ButtonSelect value="Detail" onPress={() => { setModalDetail(true) }} />
|
||||
</View>
|
||||
<InputForm
|
||||
label="Judul Tugas"
|
||||
type="default"
|
||||
placeholder="Judul Tugas"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={title}
|
||||
error={error.title}
|
||||
errorText="Judul tidak boleh kosong"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import ImageWithLabel from "@/components/imageWithLabel";
|
||||
@@ -8,11 +8,12 @@ import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiAddMemberDivision, apiGetDivisionOneDetail, apiGetUser } from "@/lib/api";
|
||||
import { setUpdateDivision } from "@/lib/divisionUpdate";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Pressable, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import { Pressable, ScrollView, View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
@@ -23,6 +24,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function AddMemberDivision() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
const [dataOld, setDataOld] = useState<Props[]>([])
|
||||
@@ -100,21 +102,37 @@ export default function AddMemberDivision() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Tambah Anggota',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="update"
|
||||
// disable={selectMember.length == 0 || loading ? true : false}
|
||||
// onPress={() => {
|
||||
// handleAddMember()
|
||||
// }}
|
||||
// />
|
||||
// )
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Anggota"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<InputSearch onChange={(val) => handleSearch(val)} value={search} />
|
||||
|
||||
{
|
||||
@@ -136,7 +154,7 @@ export default function AddMemberDivision() {
|
||||
</View>
|
||||
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
|
||||
<Text style={[Styles.textDefault, Styles.pv05, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada member yang dipilih</Text>
|
||||
}
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
@@ -159,12 +177,12 @@ export default function AddMemberDivision() {
|
||||
<View style={[Styles.ml10]}>
|
||||
<Text style={[Styles.textDefault]} numberOfLines={1} ellipsizeMode="tail">{item.name}</Text>
|
||||
{
|
||||
found && <Text style={[Styles.textInformation, Styles.cGray]}>sudah menjadi anggota</Text>
|
||||
found && <Text style={[Styles.textInformation, { color: colors.dimmed }]}>sudah menjadi anggota</Text>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={'black'} />
|
||||
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={colors.text} />
|
||||
}
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiEditDivision, apiGetDivisionOneDetail } from "@/lib/api";
|
||||
import { setUpdateDivision } from "@/lib/divisionUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SafeAreaView, ScrollView, View } from "react-native";
|
||||
@@ -12,6 +13,7 @@ import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function EditDivision() {
|
||||
const { colors } = useTheme();
|
||||
const dispatch = useDispatch()
|
||||
const update = useSelector((state: any) => state.divisionUpdate)
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
@@ -63,28 +65,42 @@ export default function EditDivision() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Edit Divisi",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={error.name || loading ? true : false}
|
||||
category="update"
|
||||
onPress={() => { handleEdit() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={error.name || loading ? true : false}
|
||||
// category="update"
|
||||
// onPress={() => { handleEdit() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Divisi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={error.name || loading ? true : false}
|
||||
category="update"
|
||||
onPress={() => { handleEdit() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
<ScrollView style={{ backgroundColor: colors.background }}>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<InputForm
|
||||
label="Nama Divisi"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader"
|
||||
import AppHeader from "@/components/AppHeader"
|
||||
import DiscussionDivisionDetail from "@/components/division/discussionDivisionDetail"
|
||||
import FileDivisionDetail from "@/components/division/fileDivisionDetail"
|
||||
import FiturDivisionDetail from "@/components/division/fiturDivisionDetail"
|
||||
@@ -8,6 +8,7 @@ import CaraouselHome from "@/components/home/carouselHome"
|
||||
import Styles from "@/constants/Styles"
|
||||
import { apiGetDivisionOneDetail } from "@/lib/api"
|
||||
import { useAuthSession } from "@/providers/AuthProvider"
|
||||
import { useTheme } from "@/providers/ThemeProvider"
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router"
|
||||
import { useEffect, useState } from "react"
|
||||
import { RefreshControl, SafeAreaView, ScrollView, View } from "react-native"
|
||||
@@ -22,6 +23,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function DetailDivisionFitur() {
|
||||
const { colors } = useTheme()
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
const [data, setData] = useState<Props>()
|
||||
@@ -54,13 +56,21 @@ export default function DetailDivisionFitur() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: loading ? 'Loading... ' : data?.name,
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <HeaderRightDivisionDetail id={id} />,
|
||||
// headerRight: () => <HeaderRightDivisionDetail id={id} />,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title={loading ? 'Loading...' : data?.name || ''}
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<HeaderRightDivisionDetail id={id} />}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
@@ -68,16 +78,17 @@ export default function DetailDivisionFitur() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
showsVerticalScrollIndicator={false}
|
||||
>
|
||||
<CaraouselHome refreshing={refreshing} />
|
||||
<View style={[Styles.ph15, Styles.mb100]}>
|
||||
<FiturDivisionDetail refreshing={refreshing}/>
|
||||
<TaskDivisionDetail refreshing={refreshing}/>
|
||||
<FileDivisionDetail refreshing={refreshing}/>
|
||||
<DiscussionDivisionDetail refreshing={refreshing}/>
|
||||
<FiturDivisionDetail refreshing={refreshing} />
|
||||
<TaskDivisionDetail refreshing={refreshing} />
|
||||
<FileDivisionDetail refreshing={refreshing} />
|
||||
<DiscussionDivisionDetail refreshing={refreshing} />
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi"
|
||||
import ModalConfirmation from "@/components/ModalConfirmation"
|
||||
import AppHeader from "@/components/AppHeader"
|
||||
import BorderBottomItem from "@/components/borderBottomItem"
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader"
|
||||
import HeaderRightDivisionInfo from "@/components/division/headerDivisionInfo"
|
||||
import DrawerBottom from "@/components/drawerBottom"
|
||||
import ImageUser from "@/components/imageNew"
|
||||
@@ -13,6 +13,7 @@ import { ConstEnv } from "@/constants/ConstEnv"
|
||||
import Styles from "@/constants/Styles"
|
||||
import { apiDeleteMemberDivision, apiGetDivisionOneDetail, apiGetDivisionOneFeature, apiUpdateStatusAdminDivision } from "@/lib/api"
|
||||
import { useAuthSession } from "@/providers/AuthProvider"
|
||||
import { useTheme } from "@/providers/ThemeProvider"
|
||||
import { Feather, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons"
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router"
|
||||
import { useEffect, useState } from "react"
|
||||
@@ -39,6 +40,7 @@ type PropsMember = {
|
||||
}
|
||||
|
||||
export default function InformationDivision() {
|
||||
const { colors } = useTheme()
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
@@ -57,14 +59,13 @@ export default function InformationDivision() {
|
||||
name: '',
|
||||
isAdmin: false
|
||||
})
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
|
||||
function handleMemberOut() {
|
||||
setModal(false)
|
||||
AlertKonfirmasi({
|
||||
title: 'Konfirmasi',
|
||||
desc: 'Apakah anda yakin ingin mengeluarkan anggota?',
|
||||
onPress: () => { memberOut() }
|
||||
})
|
||||
setTimeout(() => {
|
||||
setShowDeleteModal(true)
|
||||
}, 600)
|
||||
}
|
||||
|
||||
async function memberOut() {
|
||||
@@ -161,13 +162,23 @@ export default function InformationDivision() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Informasi Divisi',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => ((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision) && <HeaderRightDivisionInfo id={id} active={dataDetail?.isActive} />,
|
||||
// headerRight: () => ((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision) && <HeaderRightDivisionInfo id={id} active={dataDetail?.isActive} />,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Informasi Divisi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision) && <HeaderRightDivisionInfo id={id} active={dataDetail?.isActive} />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
@@ -175,9 +186,10 @@ export default function InformationDivision() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
{
|
||||
@@ -187,7 +199,7 @@ export default function InformationDivision() {
|
||||
}
|
||||
<View style={[Styles.mb15]}>
|
||||
<Text style={[Styles.textDefaultSemiBold, Styles.mb05]}>Deskripsi Divisi</Text>
|
||||
<View style={[Styles.wrapPaper]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.icon + '20' }]}>
|
||||
{loading ?
|
||||
arrSkeleton.map((item, index) => {
|
||||
return (
|
||||
@@ -201,7 +213,7 @@ export default function InformationDivision() {
|
||||
</View>
|
||||
<View style={[Styles.mb15]}>
|
||||
<Text style={[Styles.textDefault, Styles.mv05]}>{dataMember.length} Anggota</Text>
|
||||
<View style={[Styles.wrapPaper]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.icon + '20' }]}>
|
||||
{
|
||||
((entityUser.role != "user" && entityUser.role != "coadmin") || isAdminDivision) &&
|
||||
dataDetail?.isActive && (
|
||||
@@ -209,8 +221,8 @@ export default function InformationDivision() {
|
||||
onPress={() => { router.push(`/division/${id}/add-member`) }}
|
||||
borderType="none"
|
||||
icon={
|
||||
<View style={[Styles.iconContent, ColorsStatus.gray]}>
|
||||
<Feather name="user-plus" size={25} color={'#384288'} />
|
||||
<View style={[Styles.iconContent]}>
|
||||
<Feather name="user-plus" size={25} color={'black'} />
|
||||
</View>
|
||||
}
|
||||
title="Tambah Anggota"
|
||||
@@ -250,8 +262,8 @@ export default function InformationDivision() {
|
||||
<View>
|
||||
<Pressable style={[Styles.wrapItemBorderBottom]} onPress={() => { handleMemberAdmin() }}>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
<View style={[Styles.iconContent, ColorsStatus.info]}>
|
||||
<MaterialIcons name="verified-user" size={25} color='#19345E' />
|
||||
<View style={[Styles.iconContent]}>
|
||||
<MaterialIcons name="verified-user" size={25} color={'black'} />
|
||||
</View>
|
||||
<View style={[Styles.rowSpaceBetween, { width: '88%' }]}>
|
||||
<View style={[Styles.ml10]}>
|
||||
@@ -264,7 +276,7 @@ export default function InformationDivision() {
|
||||
<Pressable style={[Styles.wrapItemBorderBottom]} onPress={() => { handleMemberOut() }}>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
<View style={[Styles.iconContent, ColorsStatus.info]}>
|
||||
<MaterialCommunityIcons name="close-circle" size={25} color='#19345E' />
|
||||
<MaterialCommunityIcons name="close-circle" size={25} color={colors.primary} />
|
||||
</View>
|
||||
<View style={[Styles.rowSpaceBetween, { width: '88%' }]}>
|
||||
<View style={[Styles.ml10]}>
|
||||
@@ -275,6 +287,19 @@ export default function InformationDivision() {
|
||||
</Pressable>
|
||||
</View>
|
||||
</DrawerBottom>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showDeleteModal}
|
||||
title="Konfirmasi"
|
||||
message="Apakah anda yakin ingin mengeluarkan anggota?"
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false)
|
||||
memberOut()
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
confirmText="Keluar"
|
||||
cancelText="Batal"
|
||||
/>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader"
|
||||
import AppHeader from "@/components/AppHeader"
|
||||
import ReportChartDocument from "@/components/division/reportChartDocument"
|
||||
import ReportChartEvent from "@/components/division/reportChartEvent"
|
||||
import ReportChartProgress from "@/components/division/reportChartProgress"
|
||||
@@ -6,6 +6,7 @@ import { InputDate } from "@/components/inputDate"
|
||||
import Styles from "@/constants/Styles"
|
||||
import { apiGetDivisionReport } from "@/lib/api"
|
||||
import { stringToDate } from "@/lib/fun_stringToDate"
|
||||
import { useTheme } from "@/providers/ThemeProvider"
|
||||
import { useAuthSession } from "@/providers/AuthProvider"
|
||||
import dayjs from "dayjs"
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router"
|
||||
@@ -14,6 +15,7 @@ import { SafeAreaView, ScrollView, View } from "react-native"
|
||||
import Toast from "react-native-toast-message"
|
||||
|
||||
export default function ReportDivision() {
|
||||
const { colors } = useTheme();
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const [showReport, setShowReport] = useState(false);
|
||||
@@ -104,15 +106,22 @@ export default function ReportDivision() {
|
||||
}, [showReport]);
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Laporan Divisi',
|
||||
headerTitleAlign: 'center',
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Laporan Divisi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
<ScrollView style={{ backgroundColor: colors.background }}>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<InputDate
|
||||
onChange={(val) => validationForm("date", val)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ModalConfirmation from "@/components/ModalConfirmation";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonNextHeader from "@/components/buttonNextHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import ModalSelect from "@/components/modalSelect";
|
||||
@@ -8,6 +8,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiCheckDivisionName } from "@/lib/api";
|
||||
import { setFormCreateDivision } from "@/lib/divisionCreate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SafeAreaView, ScrollView, View } from "react-native";
|
||||
@@ -15,6 +16,7 @@ import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function CreateDivision() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const [isSelect, setSelect] = useState(false)
|
||||
const [chooseGroup, setChooseGroup] = useState({ val: "", label: "" })
|
||||
@@ -23,6 +25,7 @@ export default function CreateDivision() {
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const userLogin = useSelector((state: any) => state.entities)
|
||||
const [loadingBtn, setLoadingBtn] = useState(false)
|
||||
const [showWarningModal, setShowWarningModal] = useState(false)
|
||||
const [error, setError] = useState({
|
||||
idGroup: false,
|
||||
name: false,
|
||||
@@ -67,12 +70,7 @@ export default function CreateDivision() {
|
||||
const response = await apiCheckDivisionName({ data: { ...dataForm }, user: hasil })
|
||||
if (response.success) {
|
||||
if (!response.available) {
|
||||
AlertKonfirmasi({
|
||||
title: 'Peringatan',
|
||||
category: 'warning',
|
||||
desc: 'Nama divisi sudah ada. Tidak dapat membuat divisi dengan nama yang sama',
|
||||
onPress: () => { }
|
||||
})
|
||||
setShowWarningModal(true)
|
||||
} else {
|
||||
handleSetData()
|
||||
}
|
||||
@@ -99,28 +97,41 @@ export default function CreateDivision() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah Divisi",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonNextHeader
|
||||
onPress={() => { handleCheckName() }}
|
||||
disable={loadingBtn || error.idGroup || error.name || chooseGroup.val == "" || chooseGroup.val == "null" || dataForm.name == "" || dataForm.name == "null"}
|
||||
// headerRight: () => (
|
||||
// <ButtonNextHeader
|
||||
// onPress={() => { handleCheckName() }}
|
||||
// disable={loadingBtn || error.idGroup || error.name || chooseGroup.val == "" || chooseGroup.val == "null" || dataForm.name == "" || dataForm.name == "null"}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader title="Tambah Divisi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<ButtonNextHeader
|
||||
onPress={() => { handleCheckName() }}
|
||||
disable={loadingBtn || error.idGroup || error.name || chooseGroup.val == "" || chooseGroup.val == "null" || dataForm.name == "" || dataForm.name == "null"}
|
||||
/>}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
{
|
||||
(entityUser.role == "supadmin" || entityUser.role == "developer") &&
|
||||
(
|
||||
@@ -166,6 +177,15 @@ export default function CreateDivision() {
|
||||
open={isSelect}
|
||||
valChoose={chooseGroup.val}
|
||||
/>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showWarningModal}
|
||||
title="Peringatan"
|
||||
message="Nama divisi sudah ada. Tidak dapat membuat divisi dengan nama yang sama"
|
||||
onConfirm={() => setShowWarningModal(false)}
|
||||
onCancel={() => setShowWarningModal(false)}
|
||||
confirmText="Oke"
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import Text from "@/components/Text";
|
||||
@@ -8,11 +8,12 @@ import { apiCreateDivision } from "@/lib/api";
|
||||
import { setFormCreateDivision } from "@/lib/divisionCreate";
|
||||
import { setUpdateDivision } from "@/lib/divisionUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { StackActions, useNavigation } from "@react-navigation/native";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Pressable, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import { Pressable, ScrollView, View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
@@ -23,6 +24,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function CreateDivisionAddAdmin() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const navigation = useNavigation()
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
@@ -77,21 +79,34 @@ export default function CreateDivisionAddAdmin() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Pilih Admin Divisi',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="create"
|
||||
// disable={selectMember.length == 0 || loading ? true : false}
|
||||
// onPress={() => {
|
||||
// handleAddMember()
|
||||
// }}
|
||||
// />
|
||||
// )
|
||||
header: () => (
|
||||
<AppHeader title="Pilih Admin Divisi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
/>}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<ScrollView>
|
||||
{
|
||||
data.length > 0 ?
|
||||
@@ -110,12 +125,12 @@ export default function CreateDivisionAddAdmin() {
|
||||
<View style={[Styles.ml10]}>
|
||||
<Text style={[Styles.textDefault]} numberOfLines={1} ellipsizeMode="tail">{item.name}</Text>
|
||||
{
|
||||
found && <Text style={[Styles.textInformation, Styles.cGray]}>sudah menjadi anggota</Text>
|
||||
found && <Text style={[Styles.textInformation, { color: colors.dimmed }]}>sudah menjadi anggota</Text>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
selectMember.some((i: any) => i == item.idUser) && <AntDesign name="check" size={20} color={'black'} />
|
||||
selectMember.some((i: any) => i == item.idUser) && <AntDesign name="check" size={20} color={colors.text} />
|
||||
}
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonNextHeader from "@/components/buttonNextHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import ImageWithLabel from "@/components/imageWithLabel";
|
||||
@@ -10,6 +10,7 @@ import { apiGetUser } from "@/lib/api";
|
||||
import { setFormCreateDivision } from "@/lib/divisionCreate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Pressable, ScrollView, View } from "react-native";
|
||||
@@ -22,6 +23,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function CreateDivisionAddMember() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
const [dataOld, setDataOld] = useState<Props[]>([])
|
||||
@@ -63,18 +65,28 @@ export default function CreateDivisionAddMember() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Pilih Anggota',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (
|
||||
<ButtonNextHeader
|
||||
disable={selectMember.length > 0 ? false : true}
|
||||
onPress={() => { handleAddMember() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonNextHeader
|
||||
// disable={selectMember.length > 0 ? false : true}
|
||||
// onPress={() => { handleAddMember() }}
|
||||
// />
|
||||
// )
|
||||
header: () => (
|
||||
<AppHeader title="Pilih Anggota"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={<ButtonNextHeader
|
||||
disable={selectMember.length > 0 ? false : true}
|
||||
onPress={() => { handleAddMember() }}
|
||||
/>}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<InputSearch onChange={(val) => setSearch(val)} value={search} />
|
||||
|
||||
{
|
||||
@@ -96,7 +108,7 @@ export default function CreateDivisionAddMember() {
|
||||
</View>
|
||||
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
|
||||
<Text style={[Styles.textDefault, Styles.pv05, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada member yang dipilih</Text>
|
||||
}
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
@@ -119,12 +131,12 @@ export default function CreateDivisionAddMember() {
|
||||
<View style={[Styles.ml10]}>
|
||||
<Text style={[Styles.textDefault]} numberOfLines={1} ellipsizeMode="tail">{item.name}</Text>
|
||||
{
|
||||
found && <Text style={[Styles.textInformation, Styles.cGray]}>sudah menjadi anggota</Text>
|
||||
found && <Text style={[Styles.textInformation, { color: colors.dimmed }]}>sudah menjadi anggota</Text>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={'black'} />
|
||||
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={colors.text} />
|
||||
}
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonTab from "@/components/buttonTab";
|
||||
import InputSearch from "@/components/inputSearch";
|
||||
import LabelStatus from "@/components/labelStatus";
|
||||
import PaperGridContent from "@/components/paperGridContent";
|
||||
import Skeleton from "@/components/skeleton";
|
||||
import SkeletonTwoItem from "@/components/skeletonTwoItem";
|
||||
import Text from "@/components/Text";
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import WrapTab from "@/components/wrapTab";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetDivision } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import {
|
||||
AntDesign,
|
||||
Feather,
|
||||
Ionicons,
|
||||
MaterialCommunityIcons,
|
||||
MaterialIcons,
|
||||
MaterialCommunityIcons
|
||||
} from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -37,9 +38,11 @@ export default function ListDivision() {
|
||||
const [isList, setList] = useState(false);
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const [search, setSearch] = useState("")
|
||||
const [nameGroup, setNameGroup] = useState("")
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
// ... state same ...
|
||||
const update = useSelector((state: any) => state.divisionUpdate)
|
||||
const arrSkeleton = Array.from({ length: 3 }, (_, index) => index)
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -113,11 +116,11 @@ export default function ListDivision() {
|
||||
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<View>
|
||||
{
|
||||
entityUser.role != "user" && entityUser.role != "coadmin" ?
|
||||
<View style={[Styles.wrapBtnTab]}>
|
||||
<WrapTab>
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="true"
|
||||
@@ -126,7 +129,7 @@ export default function ListDivision() {
|
||||
icon={
|
||||
<Feather
|
||||
name="check-circle"
|
||||
color={status == "false" ? "black" : "white"}
|
||||
color={status == "false" ? colors.dimmed : "white"}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
@@ -140,15 +143,15 @@ export default function ListDivision() {
|
||||
icon={
|
||||
<AntDesign
|
||||
name="closecircleo"
|
||||
color={status == "true" ? "black" : "white"}
|
||||
color={status == "true" ? colors.dimmed : "white"}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
n={2}
|
||||
/>
|
||||
</View>
|
||||
</WrapTab>
|
||||
:
|
||||
<View style={[Styles.wrapBtnTab]}>
|
||||
<WrapTab>
|
||||
<ButtonTab
|
||||
active={category == "semua" ? "false" : "true"}
|
||||
value="true"
|
||||
@@ -157,7 +160,7 @@ export default function ListDivision() {
|
||||
icon={
|
||||
<Ionicons
|
||||
name="file-tray-outline"
|
||||
color={category == "semua" ? "black" : "white"}
|
||||
color={category == "semua" ? colors.dimmed : "white"}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
@@ -171,13 +174,13 @@ export default function ListDivision() {
|
||||
icon={
|
||||
<Ionicons
|
||||
name="file-tray-stacked-outline"
|
||||
color={category == "semua" ? "white" : "black"}
|
||||
color={category == "semua" ? "white" : colors.dimmed}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
n={2}
|
||||
/>
|
||||
</View>
|
||||
</WrapTab>
|
||||
}
|
||||
|
||||
<View style={[Styles.rowSpaceBetween, { alignItems: 'center' }]}>
|
||||
@@ -189,14 +192,15 @@ export default function ListDivision() {
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={isList ? "format-list-bulleted" : "view-grid"}
|
||||
color={"black"}
|
||||
color={colors.text}
|
||||
size={30}
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
{(entityUser.role == "supadmin" || entityUser.role == "developer") && (
|
||||
<View style={[Styles.mv05]}>
|
||||
<Text>Filter : {nameGroup}</Text>
|
||||
<View style={[Styles.mv05, Styles.rowOnly]}>
|
||||
<Text>Filter :</Text>
|
||||
<LabelStatus size="small" category="secondary" text={nameGroup} style={[Styles.mh05]} />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
@@ -214,7 +218,7 @@ export default function ListDivision() {
|
||||
:
|
||||
data.length == 0 ? (
|
||||
<View style={[Styles.mt15]}>
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada data</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada data</Text>
|
||||
</View>
|
||||
) : (
|
||||
isList ? (
|
||||
@@ -230,9 +234,10 @@ export default function ListDivision() {
|
||||
key={index}
|
||||
onPress={() => { router.push(`/division/${item.id}`) }}
|
||||
borderType="bottom"
|
||||
bgColor="transparent"
|
||||
icon={
|
||||
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
|
||||
<MaterialIcons name="group" size={25} color={"#384288"} />
|
||||
<View style={[Styles.iconContent]}>
|
||||
<Feather name="users" size={25} color={'black'} />
|
||||
</View>
|
||||
}
|
||||
title={item.name}
|
||||
@@ -248,6 +253,7 @@ export default function ListDivision() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -283,6 +289,7 @@ export default function ListDivision() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ReportChartDocument from "@/components/division/reportChartDocument";
|
||||
import ReportChartEvent from "@/components/division/reportChartEvent";
|
||||
import ReportChartProgress from "@/components/division/reportChartProgress";
|
||||
@@ -9,6 +9,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiGetDivisionReport } from "@/lib/api";
|
||||
import { stringToDate } from "@/lib/fun_stringToDate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import dayjs from "dayjs";
|
||||
import { router, Stack } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -16,6 +17,7 @@ import { SafeAreaView, ScrollView, View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
export default function Report() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const [chooseGroup, setChooseGroup] = useState({ val: "", label: "" });
|
||||
const [showReport, setShowReport] = useState(false);
|
||||
@@ -122,24 +124,33 @@ export default function Report() {
|
||||
}, [showReport]);
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Laporan Divisi",
|
||||
headerTitleAlign: "center",
|
||||
header: () => (
|
||||
<AppHeader title="Laporan Divisi"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
<View style={[Styles.p15, Styles.mb50]}>
|
||||
<SelectForm
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
label="Lembaga Desa"
|
||||
placeholder="Pilih Lembaga Desa"
|
||||
value={chooseGroup.label}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import ModalSelect from "@/components/modalSelect";
|
||||
@@ -10,6 +10,7 @@ import { apiEditProfile, apiGetProfile } from "@/lib/api";
|
||||
import { setEntities } from "@/lib/entitiesSlice";
|
||||
import { validateName } from "@/lib/fun_validateName";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { useHeaderHeight } from "@react-navigation/elements";
|
||||
import * as ImagePicker from "expo-image-picker";
|
||||
@@ -43,9 +44,11 @@ type Props = {
|
||||
export default function EditProfile() {
|
||||
const headerHeight = useHeaderHeight()
|
||||
const dispatch = useDispatch()
|
||||
const { colors } = useTheme();
|
||||
const entities = useSelector((state: any) => state.entities)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const [errorImg, setErrorImg] = useState(false)
|
||||
// ... keeping state same ...
|
||||
const [selectedImage, setSelectedImage] = useState<string | undefined | { uri: string }>(undefined);
|
||||
const [choosePosition, setChoosePosition] = useState({ val: entities.idPosition, label: entities.position });
|
||||
const [chooseGender, setChooseGender] = useState({ val: entities.gender, label: entities.gender == "F" ? 'Perempuan' : 'Laki-laki' });
|
||||
@@ -213,27 +216,43 @@ export default function EditProfile() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Edit Profile",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading ? true : false}
|
||||
category="update"
|
||||
onPress={() => {
|
||||
handleEdit()
|
||||
}}
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Profile"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading ? true : false}
|
||||
category="update"
|
||||
onPress={() => {
|
||||
handleEdit()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disableBtn || loading ? true : false}
|
||||
// category="update"
|
||||
// onPress={() => {
|
||||
// handleEdit()
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
}}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
|
||||
@@ -1,51 +1,49 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import { ButtonFiturMenu } from "@/components/buttonFiturMenu";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { AntDesign, Entypo, Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign, Entypo, Feather, Ionicons, MaterialCommunityIcons, MaterialIcons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
import { SafeAreaView, View } from "react-native";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
export default function Feature() {
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Fitur',
|
||||
headerTitleAlign: 'center'
|
||||
headerTitleAlign: 'center',
|
||||
header: () => (
|
||||
<AppHeader title="Fitur" showBack={true} onPressLeft={() => router.back()} />
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={[Styles.p15]}>
|
||||
<View style={[Styles.rowSpaceBetween, Styles.mb15]}>
|
||||
<ButtonFiturMenu icon={<MaterialIcons name="group" size={35} color="black" />} text="Divisi" onPress={() => { router.push('/division?active=true') }} />
|
||||
<ButtonFiturMenu icon={<AntDesign name="areachart" size={35} color="black" />} text="Kegiatan" onPress={() => { router.push('/project?status=0') }} />
|
||||
<ButtonFiturMenu icon={<MaterialIcons name="campaign" size={35} color="black" />} text="Pengumuman" onPress={() => { router.push('/announcement') }} />
|
||||
<ButtonFiturMenu icon={<Ionicons name="chatbubbles-sharp" size={35} color="black" />} text="Diskusi" onPress={() => { router.push('/discussion?active=true') }} />
|
||||
<ButtonFiturMenu icon={<Feather name="users" size={30} color={colors.icon} />} text="Divisi" onPress={() => { router.push('/division?active=true') }} />
|
||||
<ButtonFiturMenu icon={<Feather name="bar-chart" size={30} color={colors.icon} />} text="Kegiatan" onPress={() => { router.push('/project?status=0') }} />
|
||||
<ButtonFiturMenu icon={<Ionicons name="megaphone-outline" size={30} color={colors.icon} />} text="Pengumuman" onPress={() => { router.push('/announcement') }} />
|
||||
<ButtonFiturMenu icon={<Ionicons name="chatbubbles-outline" size={30} color={colors.icon} />} text="Diskusi" onPress={() => { router.push('/discussion?active=true') }} />
|
||||
</View>
|
||||
<View style={[Styles.rowSpaceBetween, Styles.mb15, (entityUser.role == 'cosupadmin' ? Styles.w70 : entityUser.role == 'supadmin' || entityUser.role == 'developer' ? Styles.w100 : Styles.w40)]}>
|
||||
<ButtonFiturMenu icon={<MaterialIcons name="groups" size={35} color="black" />} text="Anggota" onPress={() => { router.push('/member') }} />
|
||||
<ButtonFiturMenu icon={<MaterialCommunityIcons name="account-tie" size={35} color="black" />} text="Jabatan" onPress={() => { router.push('/position') }} />
|
||||
<ButtonFiturMenu icon={<MaterialCommunityIcons name="account-group-outline" size={30} color={colors.icon} />} text="Anggota" onPress={() => { router.push('/member') }} />
|
||||
<ButtonFiturMenu icon={<MaterialCommunityIcons name="account-tie-outline" size={30} color={colors.icon} />} text="Jabatan" onPress={() => { router.push('/position') }} />
|
||||
{
|
||||
entityUser.role == "cosupadmin" && <ButtonFiturMenu icon={<Entypo name="image-inverted" size={35} color="black" />} text="Banner" onPress={() => { router.push('/banner') }} />
|
||||
entityUser.role == "cosupadmin" && <ButtonFiturMenu icon={<Ionicons name="images-outline" size={30} color={colors.icon} />} text="Banner" onPress={() => { router.push('/banner') }} />
|
||||
}
|
||||
{
|
||||
(entityUser.role == "supadmin" || entityUser.role == "developer") &&
|
||||
<>
|
||||
<ButtonFiturMenu icon={<AntDesign name="tags" size={35} color="black" />} text="Lembaga Desa" onPress={() => { router.push('/group') }} />
|
||||
{/* <ButtonFiturMenu icon={<Ionicons name="color-palette-sharp" size={35} color="black" />} text="Tema" onPress={() => { }} /> */}
|
||||
<ButtonFiturMenu icon={<Entypo name="image-inverted" size={35} color="black" />} text="Banner" onPress={() => { router.push('/banner') }} />
|
||||
<ButtonFiturMenu icon={<Ionicons name="bookmarks-outline" size={30} color={colors.icon} />} text="Lembaga Desa" onPress={() => { router.push('/group') }} />
|
||||
{/* <ButtonFiturMenu icon={<Ionicons name="color-palette-sharp" size={30} color={colors.icon} />} text="Tema" onPress={() => { }} /> */}
|
||||
<ButtonFiturMenu icon={<Ionicons name="images-outline" size={30} color={colors.icon} />} text="Banner" onPress={() => { router.push('/banner') }} />
|
||||
</>
|
||||
}
|
||||
</View>
|
||||
{/* {
|
||||
(entityUser.role == "supadmin" || entityUser.role == "developer") &&
|
||||
<View style={[Styles.rowSpaceBetween, Styles.mb15]}>
|
||||
<ButtonFiturMenu icon={<Entypo name="image-inverted" size={35} color="black" />} text="Banner" onPress={() => { router.push('/banner') }} />
|
||||
</View>
|
||||
} */}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi";
|
||||
import ModalConfirmation from "@/components/ModalConfirmation";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import { ButtonForm } from "@/components/buttonForm";
|
||||
import ButtonTab from "@/components/buttonTab";
|
||||
@@ -8,12 +8,13 @@ import InputSearch from "@/components/inputSearch";
|
||||
import MenuItemRow from "@/components/menuItemRow";
|
||||
import SkeletonTwoItem from "@/components/skeletonTwoItem";
|
||||
import Text from "@/components/Text";
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import WrapTab from "@/components/wrapTab";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiDeleteGroup, apiEditGroup, apiGetGroup } from "@/lib/api";
|
||||
import { setUpdateGroup } from "@/lib/groupSlice";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { AntDesign, Feather, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign, Feather, Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { useEffect, useState } from "react";
|
||||
import { RefreshControl, View, VirtualizedList } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
@@ -27,12 +28,14 @@ type Props = {
|
||||
|
||||
export default function Index() {
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const [isModal, setModal] = useState(false)
|
||||
const [isVisibleEdit, setVisibleEdit] = useState(false)
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
const [search, setSearch] = useState('')
|
||||
const arrSkeleton = Array.from({ length: 5 }, (_, index) => index)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
const [status, setStatus] = useState<'true' | 'false'>('true')
|
||||
const [loadingSubmit, setLoadingSubmit] = useState(false)
|
||||
const [idChoose, setIdChoose] = useState('')
|
||||
@@ -127,24 +130,24 @@ export default function Index() {
|
||||
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<View style={[Styles.mb10]}>
|
||||
<View style={[Styles.wrapBtnTab]}>
|
||||
<WrapTab>
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="true"
|
||||
onPress={() => { setStatus("true") }}
|
||||
label="Aktif"
|
||||
icon={<Feather name="check-circle" color={status == "true" ? 'white' : 'black'} size={20} />}
|
||||
icon={<Feather name="check-circle" color={status == "true" ? 'white' : colors.dimmed} size={20} />}
|
||||
n={2} />
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="false"
|
||||
onPress={() => { setStatus("false") }}
|
||||
label="Tidak Aktif"
|
||||
icon={<AntDesign name="closecircleo" color={status == "false" ? 'white' : 'black'} size={20} />}
|
||||
icon={<AntDesign name="closecircleo" color={status == "false" ? 'white' : colors.dimmed} size={20} />}
|
||||
n={2} />
|
||||
</View>
|
||||
</WrapTab>
|
||||
<InputSearch onChange={setSearch} />
|
||||
</View>
|
||||
<View style={[{ flex: 2 }, Styles.mt05]}>
|
||||
@@ -173,8 +176,8 @@ export default function Index() {
|
||||
}}
|
||||
borderType="all"
|
||||
icon={
|
||||
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
|
||||
<MaterialCommunityIcons name="office-building-outline" size={25} color={'#384288'} />
|
||||
<View style={[Styles.iconContent]}>
|
||||
<Ionicons name="bookmark-outline" size={25} color={'black'} />
|
||||
</View>
|
||||
}
|
||||
title={item.name}
|
||||
@@ -187,30 +190,29 @@ export default function Index() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada data</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada data</Text>
|
||||
}
|
||||
</View>
|
||||
|
||||
<DrawerBottom animation="slide" isVisible={isModal} setVisible={() => setModal(false)} title={titleChoose}>
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="toggle-switch-off-outline" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="toggle-switch-off-outline" color={colors.text} size={25} />}
|
||||
title={activeChoose ? "Non Aktifkan" : "Aktifkan"}
|
||||
onPress={() => {
|
||||
setModal(false)
|
||||
AlertKonfirmasi({
|
||||
title: 'Konfirmasi',
|
||||
desc: activeChoose ? 'Apakah anda yakin ingin menonaktifkan data?' : 'Apakah anda yakin ingin mengaktifkan data?',
|
||||
onPress: () => { handleDelete() }
|
||||
})
|
||||
setTimeout(() => {
|
||||
setShowDeleteModal(true)
|
||||
}, 600)
|
||||
}}
|
||||
/>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color={colors.text} size={25} />}
|
||||
title="Edit"
|
||||
onPress={() => {
|
||||
setModal(false)
|
||||
@@ -232,6 +234,7 @@ export default function Index() {
|
||||
label="Lembaga Desa"
|
||||
value={titleChoose}
|
||||
error={error.title}
|
||||
bg={"transparent"}
|
||||
errorText="Lembaga Desa tidak boleh kosong & minimal 3 karakter"
|
||||
onChange={(val) => { validationForm(val, 'title') }} />
|
||||
</View>
|
||||
@@ -240,6 +243,19 @@ export default function Index() {
|
||||
</View>
|
||||
</View>
|
||||
</DrawerBottom>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showDeleteModal}
|
||||
title="Konfirmasi"
|
||||
message={activeChoose ? 'Apakah anda yakin ingin menonaktifkan data?' : 'Apakah anda yakin ingin mengaktifkan data?'}
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false)
|
||||
handleDelete()
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
confirmText={activeChoose ? "Nonaktifkan" : "Aktifkan"}
|
||||
cancelText="Batal"
|
||||
/>
|
||||
</View >
|
||||
|
||||
)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import CaraouselHome from "@/components/home/carouselHome";
|
||||
import CaraouselHome2 from "@/components/home/carouselHome2";
|
||||
import ChartDokumenHome from "@/components/home/chartDokumenHome";
|
||||
import ChartProgresHome from "@/components/home/chartProgresHome";
|
||||
import DisccussionHome from "@/components/home/discussionHome";
|
||||
import DivisionHome from "@/components/home/divisionHome";
|
||||
import EventHome from "@/components/home/eventHome";
|
||||
import FiturHome from "@/components/home/fiturHome";
|
||||
import { HeaderRightHome } from "@/components/home/headerRightHome";
|
||||
import ProjectHome from "@/components/home/projectHome";
|
||||
import Text from "@/components/Text";
|
||||
@@ -12,9 +11,11 @@ import Styles from "@/constants/Styles";
|
||||
import { apiGetProfile } from "@/lib/api";
|
||||
import { setEntities } from "@/lib/entitiesSlice";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { Stack } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Platform, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import { Dimensions, Platform, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
@@ -23,6 +24,7 @@ export default function Home() {
|
||||
const entities = useSelector((state: any) => state.entities)
|
||||
const dispatch = useDispatch()
|
||||
const { token, decryptToken, signOut } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const insets = useSafeAreaInsets()
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
|
||||
@@ -47,13 +49,13 @@ export default function Home() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
title: 'Home',
|
||||
headerTitle: entities.village,
|
||||
header: () => (
|
||||
<View style={[Styles.rowItemsCenter, Styles.ph20, Platform.OS === 'ios' ? Styles.pb07 : Styles.pb13, { backgroundColor: '#19345E', paddingTop: Platform.OS === 'ios' ? insets.top : 10 }]}>
|
||||
<View style={[Styles.rowItemsCenter, Styles.ph20, Platform.OS === 'ios' ? Styles.pb07 : Styles.pb13, { backgroundColor: colors.header, paddingTop: Platform.OS === 'ios' ? insets.top : 10 }]}>
|
||||
<Text style={Styles.textHeaderHome}>{entities.village}</Text>
|
||||
<HeaderRightHome />
|
||||
</View>
|
||||
@@ -65,19 +67,34 @@ export default function Home() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={{ backgroundColor: colors.background }}
|
||||
>
|
||||
<CaraouselHome refreshing={refreshing}/>
|
||||
<View style={[Styles.ph15, Styles.mb100]}>
|
||||
<FiturHome />
|
||||
<ProjectHome refreshing={refreshing}/>
|
||||
<DivisionHome refreshing={refreshing}/>
|
||||
<ChartProgresHome refreshing={refreshing}/>
|
||||
<ChartDokumenHome refreshing={refreshing}/>
|
||||
<EventHome refreshing={refreshing}/>
|
||||
<DisccussionHome refreshing={refreshing}/>
|
||||
<LinearGradient
|
||||
colors={[colors.header, colors.header, colors.header, colors.header, colors.homeGradient]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
width: Dimensions.get('window').width * 1.5,
|
||||
height: Dimensions.get('window').width * 1.5,
|
||||
borderRadius: Dimensions.get('window').width * 0.5,
|
||||
top: -Dimensions.get('window').width * 1, // Positioned to show the bottom part of the circle as an arc
|
||||
left: -Dimensions.get('window').width * 0.25,
|
||||
zIndex: -1,
|
||||
}}
|
||||
/>
|
||||
{/* <CaraouselHome refreshing={refreshing} /> */}
|
||||
<View style={[Styles.ph15]}>
|
||||
<CaraouselHome2 refreshing={refreshing} />
|
||||
{/* <FiturHome /> */}
|
||||
<ProjectHome refreshing={refreshing} />
|
||||
<DivisionHome refreshing={refreshing} />
|
||||
<ChartProgresHome refreshing={refreshing} />
|
||||
<ChartDokumenHome refreshing={refreshing} />
|
||||
<EventHome refreshing={refreshing} />
|
||||
<DisccussionHome refreshing={refreshing} />
|
||||
</View>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import ItemDetailMember from "@/components/itemDetailMember";
|
||||
import LabelStatus from "@/components/labelStatus";
|
||||
@@ -10,8 +10,10 @@ import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import { valueRoleUser } from "@/constants/RoleUser";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetProfile } from "@/lib/api";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import ImageViewing from 'react-native-image-viewing';
|
||||
import Toast from "react-native-toast-message";
|
||||
@@ -33,6 +35,7 @@ type Props = {
|
||||
|
||||
export default function MemberDetail() {
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const { colors } = useTheme();
|
||||
const [data, setData] = useState<Props>()
|
||||
const [errorImg, setErrorImg] = useState(false)
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
@@ -74,26 +77,36 @@ export default function MemberDetail() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Anggota',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (entityUser.role != "user") && isEdit ? <HeaderRightMemberDetail active={data?.isActive} id={id} /> : <></>,
|
||||
headerShadowVisible: false
|
||||
header: () => (
|
||||
<AppHeader title="Anggota"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
(entityUser.role != "user") && isEdit ? <HeaderRightMemberDetail active={data?.isActive} id={id} /> : <></>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<View style={[Styles.wrapHeadViewMember]}>
|
||||
<LinearGradient
|
||||
colors={[colors.header, colors.homeGradient]}
|
||||
style={[Styles.wrapHeadViewMember]}
|
||||
>
|
||||
{
|
||||
loading ?
|
||||
<>
|
||||
@@ -111,10 +124,10 @@ export default function MemberDetail() {
|
||||
</>
|
||||
|
||||
}
|
||||
</View>
|
||||
</LinearGradient>
|
||||
<View style={[Styles.p15]}>
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>Informasi</Text>
|
||||
<Text style={[Styles.textDefaultSemiBold, { color: colors.text }]}>Informasi</Text>
|
||||
<LabelStatus
|
||||
size="small"
|
||||
category={data?.isActive ? 'success' : 'error'}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import ModalSelect from "@/components/modalSelect";
|
||||
@@ -10,6 +10,7 @@ import { apiCreateUser } from "@/lib/api";
|
||||
import { validateName } from "@/lib/fun_validateName";
|
||||
import { setUpdateMember } from "@/lib/memberSlice";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import * as ImagePicker from "expo-image-picker";
|
||||
@@ -32,6 +33,7 @@ export default function CreateMember() {
|
||||
const dispatch = useDispatch()
|
||||
const update = useSelector((state: any) => state.memberUpdate)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const [valSelect, setValSelect] = useState<"group" | "position" | "role" | "gender">("group");
|
||||
const [chooseGroup, setChooseGroup] = useState({ val: "", label: "" });
|
||||
const [choosePosition, setChoosePosition] = useState({ val: "", label: "" });
|
||||
@@ -206,29 +208,28 @@ export default function CreateMember() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
headerTitle: "Tambah Anggota",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading}
|
||||
category="create"
|
||||
onPress={() => { handleCreate() }}
|
||||
header: () => (
|
||||
<AppHeader title="Anggota"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading}
|
||||
category="create"
|
||||
onPress={() => { handleCreate() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={headerHeight}
|
||||
>
|
||||
@@ -258,6 +259,7 @@ export default function CreateMember() {
|
||||
placeholder="Pilih Lembaga Desa"
|
||||
value={chooseGroup.label}
|
||||
required
|
||||
bg={colors.card}
|
||||
onPress={() => {
|
||||
setValChoose(chooseGroup.val);
|
||||
setValSelect("group");
|
||||
@@ -272,6 +274,7 @@ export default function CreateMember() {
|
||||
placeholder="Pilih Jabatan"
|
||||
value={choosePosition.label}
|
||||
required
|
||||
bg={colors.card}
|
||||
onPress={() => {
|
||||
setValChoose(choosePosition.val);
|
||||
setValSelect("position");
|
||||
@@ -285,6 +288,7 @@ export default function CreateMember() {
|
||||
placeholder="Pilih Role"
|
||||
value={chooseRole.label}
|
||||
required
|
||||
bg={colors.card}
|
||||
onPress={() => {
|
||||
setValChoose(chooseRole.val);
|
||||
setValSelect("role");
|
||||
@@ -298,6 +302,7 @@ export default function CreateMember() {
|
||||
type="numeric"
|
||||
placeholder="NIK"
|
||||
required
|
||||
bg={colors.card}
|
||||
error={error.nik}
|
||||
errorText="NIK Harus 16 Karakter"
|
||||
onChange={val => {
|
||||
@@ -309,6 +314,7 @@ export default function CreateMember() {
|
||||
type="default"
|
||||
placeholder="Nama"
|
||||
required
|
||||
bg={colors.card}
|
||||
error={error.name}
|
||||
errorText="Nama harus 3–50 karakter (huruf, angka, spasi, dan simbol ringan (. , ' _ -))"
|
||||
onChange={val => {
|
||||
@@ -320,6 +326,7 @@ export default function CreateMember() {
|
||||
type="default"
|
||||
placeholder="Email"
|
||||
required
|
||||
bg={colors.card}
|
||||
error={error.email}
|
||||
errorText="Email tidak valid"
|
||||
onChange={val => {
|
||||
@@ -331,7 +338,8 @@ export default function CreateMember() {
|
||||
type="numeric"
|
||||
placeholder="8XX-XXX-XXX"
|
||||
required
|
||||
itemLeft={<Text style={[Platform.OS === 'ios' && Styles.mt02]}>+62</Text>}
|
||||
bg={colors.card}
|
||||
itemLeft={<Text style={[Platform.OS === 'ios' && Styles.mt02, { color: colors.text }]}>+62</Text>}
|
||||
error={error.phone}
|
||||
errorText="Nomor Telepon tidak valid"
|
||||
onChange={val => {
|
||||
@@ -343,6 +351,7 @@ export default function CreateMember() {
|
||||
placeholder="Pilih Jenis Kelamin"
|
||||
value={chooseGender.label}
|
||||
required
|
||||
bg={colors.card}
|
||||
onPress={() => {
|
||||
setValChoose(chooseGender.val);
|
||||
setValSelect("gender");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import ModalSelect from "@/components/modalSelect";
|
||||
@@ -10,6 +10,7 @@ import { apiEditUser, apiGetProfile } from "@/lib/api";
|
||||
import { validateName } from "@/lib/fun_validateName";
|
||||
import { setUpdateMember } from "@/lib/memberSlice";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import * as ImagePicker from "expo-image-picker";
|
||||
@@ -47,6 +48,7 @@ export default function EditMember() {
|
||||
const update = useSelector((state: any) => state.memberUpdate)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const { colors } = useTheme();
|
||||
const [errorImg, setErrorImg] = useState(false)
|
||||
const [selectedImage, setSelectedImage] = useState<string | undefined | { uri: string }>(undefined);
|
||||
const [choosePosition, setChoosePosition] = useState({ val: "", label: "" });
|
||||
@@ -236,32 +238,32 @@ export default function EditMember() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
headerTitle: "Edit Anggota",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading}
|
||||
category="update"
|
||||
onPress={() => {
|
||||
handleEdit()
|
||||
}}
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Anggota"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading}
|
||||
category="update"
|
||||
onPress={() => {
|
||||
handleEdit()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
<KeyboardAvoidingView
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={headerHeight}
|
||||
>
|
||||
@@ -309,6 +311,7 @@ export default function EditMember() {
|
||||
placeholder="Pilih Jabatan"
|
||||
value={choosePosition.label}
|
||||
required
|
||||
bg={colors.card}
|
||||
onPress={() => {
|
||||
setValChoose(choosePosition.val);
|
||||
setValSelect("position");
|
||||
@@ -322,6 +325,7 @@ export default function EditMember() {
|
||||
placeholder="Pilih Role"
|
||||
value={chooseRole.label}
|
||||
required
|
||||
bg={colors.card}
|
||||
onPress={() => {
|
||||
setValChoose(chooseRole.val);
|
||||
setValSelect("role");
|
||||
@@ -336,6 +340,7 @@ export default function EditMember() {
|
||||
placeholder="NIK"
|
||||
required
|
||||
value={data?.nik}
|
||||
bg={colors.card}
|
||||
error={error.nik}
|
||||
errorText="NIK Harus 16 Karakter"
|
||||
onChange={val => {
|
||||
@@ -348,6 +353,7 @@ export default function EditMember() {
|
||||
placeholder="Nama"
|
||||
required
|
||||
value={data?.name}
|
||||
bg={colors.card}
|
||||
error={error.name}
|
||||
errorText="Nama harus 3–50 karakter (huruf, angka, spasi, dan simbol ringan (. , ' _ -))"
|
||||
onChange={val => {
|
||||
@@ -360,6 +366,7 @@ export default function EditMember() {
|
||||
placeholder="Email"
|
||||
required
|
||||
value={data?.email}
|
||||
bg={colors.card}
|
||||
error={error.email}
|
||||
errorText="Email tidak valid"
|
||||
onChange={val => {
|
||||
@@ -371,8 +378,9 @@ export default function EditMember() {
|
||||
type="numeric"
|
||||
placeholder="8XX-XXX-XXX"
|
||||
required
|
||||
itemLeft={<Text style={[Platform.OS === 'ios' && Styles.mt02]}>+62</Text>}
|
||||
itemLeft={<Text style={[Platform.OS === 'ios' && Styles.mt02, { color: colors.text }]}>+62</Text>}
|
||||
value={data?.phone}
|
||||
bg={colors.card}
|
||||
error={error.phone}
|
||||
errorText="Nomor Telepon tidak valid"
|
||||
onChange={val => {
|
||||
@@ -384,6 +392,7 @@ export default function EditMember() {
|
||||
placeholder="Pilih Jenis Kelamin"
|
||||
value={chooseGender.label}
|
||||
required
|
||||
bg={colors.card}
|
||||
onPress={() => {
|
||||
setValChoose(chooseGender.val);
|
||||
setValSelect("gender");
|
||||
|
||||
@@ -2,12 +2,15 @@ import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonTab from "@/components/buttonTab";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import InputSearch from "@/components/inputSearch";
|
||||
import LabelStatus from "@/components/labelStatus";
|
||||
import SkeletonTwoItem from "@/components/skeletonTwoItem";
|
||||
import Text from "@/components/Text";
|
||||
import WrapTab from "@/components/wrapTab";
|
||||
import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetUser } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign, Feather } from "@expo/vector-icons";
|
||||
import { router, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -32,6 +35,7 @@ export default function Index() {
|
||||
const { active, group } = useLocalSearchParams<{ active?: string, group?: string }>()
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const { colors } = useTheme();
|
||||
const [search, setSearch] = useState('')
|
||||
const [nameGroup, setNameGroup] = useState('')
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
@@ -103,29 +107,30 @@ export default function Index() {
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<View>
|
||||
<View style={[Styles.wrapBtnTab]}>
|
||||
<WrapTab>
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="true"
|
||||
onPress={() => { setStatus("true") }}
|
||||
label="Aktif"
|
||||
icon={<Feather name="check-circle" color={status == "false" ? 'black' : 'white'} size={20} />}
|
||||
icon={<Feather name="check-circle" color={status == "false" ? colors.dimmed : 'white'} size={20} />}
|
||||
n={2} />
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="false"
|
||||
onPress={() => { setStatus("false") }}
|
||||
label="Tidak Aktif"
|
||||
icon={<AntDesign name="closecircleo" color={status == "false" ? 'white' : 'black'} size={20} />}
|
||||
icon={<AntDesign name="closecircleo" color={status == "false" ? 'white' : colors.dimmed} size={20} />}
|
||||
n={2} />
|
||||
</View>
|
||||
</WrapTab>
|
||||
<InputSearch onChange={setSearch} />
|
||||
{
|
||||
(entityUser.role == "supadmin" || entityUser.role == "developer") &&
|
||||
<View style={[Styles.mv05]}>
|
||||
<Text>Filter : {nameGroup}</Text>
|
||||
<View style={[Styles.mv05, Styles.rowOnly]}>
|
||||
<Text>Filter :</Text>
|
||||
<LabelStatus size="small" category="secondary" text={nameGroup} style={[Styles.mh05]} />
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
@@ -166,11 +171,12 @@ export default function Index() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada data</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada data</Text>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import BorderBottomItemVertical from "@/components/borderBottomItemVertical";
|
||||
import SkeletonTwoItem from "@/components/skeletonTwoItem";
|
||||
import Text from "@/components/Text";
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
@@ -7,6 +8,7 @@ import { apiGetNotification, apiReadOneNotification } from "@/lib/api";
|
||||
import { setUpdateNotification } from "@/lib/notificationSlice";
|
||||
import { pushToPage } from "@/lib/pushToPage";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { useEffect, useState } from "react";
|
||||
import { RefreshControl, SafeAreaView, View, VirtualizedList } from "react-native";
|
||||
@@ -24,6 +26,7 @@ type Props = {
|
||||
|
||||
export default function Notification() {
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
const [page, setPage] = useState(1)
|
||||
@@ -97,7 +100,7 @@ export default function Notification() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<View style={[Styles.p15]}>
|
||||
{
|
||||
loading ?
|
||||
@@ -114,21 +117,21 @@ export default function Notification() {
|
||||
getItem={getItem}
|
||||
renderItem={({ item, index }: { item: Props, index: number }) => {
|
||||
return (
|
||||
<BorderBottomItem
|
||||
<BorderBottomItemVertical
|
||||
borderType="bottom"
|
||||
icon={
|
||||
<View style={[Styles.iconContent, item.isRead ? ColorsStatus.secondary : ColorsStatus.primary]}>
|
||||
<Feather name="bell" size={25} color="white" />
|
||||
<View style={[Styles.iconContent, item.isRead && ColorsStatus.secondary]}>
|
||||
<Feather name="bell" size={25} color="black" />
|
||||
</View>
|
||||
}
|
||||
title={item.title}
|
||||
rightTopInfo={item.createdAt}
|
||||
desc={item.desc}
|
||||
textColor={item.isRead ? 'gray' : 'black'}
|
||||
textColor={item.isRead ? 'gray' : colors.text}
|
||||
onPress={() => {
|
||||
handleReadNotification(item.id, item.category, item.idContent)
|
||||
|
||||
}}
|
||||
bgColor={'transparent'}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
@@ -140,11 +143,12 @@ export default function Notification() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada data</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada data</Text>
|
||||
}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi";
|
||||
import ModalConfirmation from "@/components/ModalConfirmation";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import { ButtonForm } from "@/components/buttonForm";
|
||||
import ButtonTab from "@/components/buttonTab";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import InputSearch from "@/components/inputSearch";
|
||||
import LabelStatus from "@/components/labelStatus";
|
||||
import MenuItemRow from "@/components/menuItemRow";
|
||||
import SkeletonTwoItem from "@/components/skeletonTwoItem";
|
||||
import Text from "@/components/Text";
|
||||
import WrapTab from "@/components/wrapTab";
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiDeletePosition, apiEditPosition, apiGetPosition } from "@/lib/api";
|
||||
import { setUpdatePosition } from "@/lib/positionSlice";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign, Feather, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -32,6 +35,7 @@ export default function Index() {
|
||||
const arrSkeleton = Array.from({ length: 5 }, (_, index) => index)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme()
|
||||
const [status, setStatus] = useState<'true' | 'false'>('true')
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const { active, group } = useLocalSearchParams<{ active?: string, group?: string }>()
|
||||
@@ -46,6 +50,7 @@ export default function Index() {
|
||||
name: false,
|
||||
});
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const update = useSelector((state: any) => state.positionUpdate)
|
||||
@@ -145,29 +150,30 @@ export default function Index() {
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<View>
|
||||
<View style={[Styles.wrapBtnTab]}>
|
||||
<WrapTab>
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="true"
|
||||
onPress={() => { setStatus("true") }}
|
||||
label="Aktif"
|
||||
icon={<Feather name="check-circle" color={status == "true" ? 'white' : 'black'} size={20} />}
|
||||
icon={<Feather name="check-circle" color={status == "true" ? 'white' : colors.dimmed} size={20} />}
|
||||
n={2} />
|
||||
<ButtonTab
|
||||
active={status == "false" ? "false" : "true"}
|
||||
value="false"
|
||||
onPress={() => { setStatus("false") }}
|
||||
label="Tidak Aktif"
|
||||
icon={<AntDesign name="closecircleo" color={status == "false" ? 'white' : 'black'} size={20} />}
|
||||
icon={<AntDesign name="closecircleo" color={status == "false" ? 'white' : colors.dimmed} size={20} />}
|
||||
n={2} />
|
||||
</View>
|
||||
</WrapTab>
|
||||
<InputSearch onChange={setSearch} />
|
||||
{
|
||||
(entityUser.role == "supadmin" || entityUser.role == "developer") &&
|
||||
<View style={[Styles.mv05]}>
|
||||
<Text>Filter : {nameGroup}</Text>
|
||||
<View style={[Styles.mv05, Styles.rowOnly]}>
|
||||
<Text>Filter :</Text>
|
||||
<LabelStatus size="small" category="secondary" text={nameGroup} style={[Styles.mh05]} />
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
@@ -195,8 +201,8 @@ export default function Index() {
|
||||
}}
|
||||
borderType="all"
|
||||
icon={
|
||||
<View style={[Styles.iconContent, ColorsStatus.lightGreen]}>
|
||||
<MaterialCommunityIcons name="account-tie" size={25} color={'#384288'} />
|
||||
<View style={[Styles.iconContent]}>
|
||||
<MaterialCommunityIcons name="account-tie-outline" size={25} color={'black'} />
|
||||
</View>
|
||||
}
|
||||
title={item.name}
|
||||
@@ -210,29 +216,28 @@ export default function Index() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada data</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada data</Text>
|
||||
}
|
||||
</View>
|
||||
<DrawerBottom animation="slide" isVisible={isModal} setVisible={() => setModal(false)} title={chooseData.name}>
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="toggle-switch-off-outline" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="toggle-switch-off-outline" color={colors.text} size={25} />}
|
||||
title={chooseData.active ? 'Non Aktifkan' : "Aktifkan"}
|
||||
onPress={() => {
|
||||
setModal(false)
|
||||
AlertKonfirmasi({
|
||||
title: 'Konfirmasi',
|
||||
desc: chooseData.active ? 'Apakah anda yakin ingin menonaktifkan data?' : 'Apakah anda yakin ingin mengaktifkan data?',
|
||||
onPress: () => { handleDelete() }
|
||||
})
|
||||
setTimeout(() => {
|
||||
setShowDeleteModal(true)
|
||||
}, 600)
|
||||
}}
|
||||
/>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color={colors.text} size={25} />}
|
||||
title="Edit"
|
||||
onPress={() => {
|
||||
setModal(false)
|
||||
@@ -253,6 +258,7 @@ export default function Index() {
|
||||
placeholder="Nama Jabatan"
|
||||
required
|
||||
label="Jabatan"
|
||||
bg={"transparent"}
|
||||
value={chooseData.name}
|
||||
onChange={(val) => { validationForm(val) }}
|
||||
error={error.name}
|
||||
@@ -264,6 +270,19 @@ export default function Index() {
|
||||
</View>
|
||||
</View>
|
||||
</DrawerBottom>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showDeleteModal}
|
||||
title="Konfirmasi"
|
||||
message={chooseData.active ? 'Apakah anda yakin ingin menonaktifkan data?' : 'Apakah anda yakin ingin mengaktifkan data?'}
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false)
|
||||
handleDelete()
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
confirmText={chooseData.active ? "Nonaktifkan" : "Aktifkan"}
|
||||
cancelText="Batal"
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -1,49 +1,85 @@
|
||||
import AlertKonfirmasi from "@/components/alertKonfirmasi";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import { ButtonHeader } from "@/components/buttonHeader";
|
||||
import ItemDetailMember from "@/components/itemDetailMember";
|
||||
import Text from "@/components/Text";
|
||||
import { assetUserImage } from "@/constants/AssetsError";
|
||||
import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetProfile } from "@/lib/api";
|
||||
import { setEntities } from "@/lib/entitiesSlice";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Feather } from "@expo/vector-icons";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { router, Stack } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { Image, Pressable, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import { Image, Pressable, RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import ImageViewing from 'react-native-image-viewing';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
export default function Profile() {
|
||||
const { signOut } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const entities = useSelector((state: any) => state.entities)
|
||||
const [error, setError] = useState(false)
|
||||
const [preview, setPreview] = useState(false)
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const dispatch = useDispatch()
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
|
||||
async function handleUserLogin() {
|
||||
const hasil = await decryptToken(String(token?.current))
|
||||
apiGetProfile({ id: hasil })
|
||||
.then((data) => dispatch(setEntities(data.data)))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
});
|
||||
}
|
||||
|
||||
const handleRefresh = async () => {
|
||||
setRefreshing(true)
|
||||
handleUserLogin()
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
setRefreshing(false)
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Profile',
|
||||
headerTitleAlign: 'center',
|
||||
headerShadowVisible: false,
|
||||
headerRight: () => <ButtonHeader
|
||||
item={<AntDesign name="logout" size={20} color="white" />}
|
||||
onPress={() => {
|
||||
AlertKonfirmasi({
|
||||
title: 'Keluar',
|
||||
desc: 'Apakah anda yakin ingin keluar?',
|
||||
onPress: () => { signOut() }
|
||||
})
|
||||
}}
|
||||
/>
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Profile"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonHeader
|
||||
item={<Feather name="settings" size={20} color="white" />}
|
||||
onPress={() => {
|
||||
router.push('/setting')
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView style={[Styles.h100]}>
|
||||
<ScrollView
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
<View style={{ flexDirection: 'column' }}>
|
||||
<View style={[Styles.wrapHeadViewMember]}>
|
||||
<LinearGradient
|
||||
colors={[colors.header, colors.homeGradient]}
|
||||
style={[Styles.wrapHeadViewMember]}
|
||||
>
|
||||
<Pressable onPress={() => setPreview(true)}>
|
||||
<Image
|
||||
source={error ? require("../../assets/images/user.jpg") : { uri: `${ConstEnv.url_storage}/files/${entities.img}` }}
|
||||
@@ -53,14 +89,12 @@ export default function Profile() {
|
||||
</Pressable>
|
||||
<Text style={[Styles.textSubtitle, Styles.cWhite, Styles.mt10]}>{entities.name}</Text>
|
||||
<Text style={[Styles.textMediumNormal, Styles.cWhite]}>{entities.role}</Text>
|
||||
</View>
|
||||
</LinearGradient>
|
||||
<View style={[Styles.p15]}>
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
<Text style={[Styles.textDefaultSemiBold]}>Informasi</Text>
|
||||
{
|
||||
entities.idUserRole != "developer" && <Text onPress={() => { router.push('/edit-profile') }} style={[Styles.textLink]}>Edit</Text>
|
||||
}
|
||||
<Text style={[Styles.textDefaultSemiBold, { color: colors.text }]}>Informasi</Text>
|
||||
</View>
|
||||
{/* Note: ItemDetailMember might need updates to support dynamic colors if it uses default text colors */}
|
||||
<ItemDetailMember category="nik" value={entities.nik} />
|
||||
<ItemDetailMember category="group" value={entities.group} />
|
||||
<ItemDetailMember category="position" value={entities.position} />
|
||||
@@ -70,6 +104,7 @@ export default function Profile() {
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
<ImageViewing
|
||||
images={[{ uri: error ? assetUserImage.uri : `${ConstEnv.url_storage}/files/${entities.img}` }]}
|
||||
imageIndex={0}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/AppHeader"
|
||||
import BorderBottomItem from "@/components/borderBottomItem"
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader"
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader"
|
||||
import ButtonSelect from "@/components/buttonSelect"
|
||||
import DrawerBottom from "@/components/drawerBottom"
|
||||
@@ -8,6 +8,7 @@ import Styles from "@/constants/Styles"
|
||||
import { apiAddFileProject, apiCheckFileProject } from "@/lib/api"
|
||||
import { setUpdateProject } from "@/lib/projectUpdate"
|
||||
import { useAuthSession } from "@/providers/AuthProvider"
|
||||
import { useTheme } from "@/providers/ThemeProvider"
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons"
|
||||
import * as DocumentPicker from "expo-document-picker"
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router"
|
||||
@@ -17,6 +18,7 @@ import Toast from "react-native-toast-message"
|
||||
import { useDispatch, useSelector } from "react-redux"
|
||||
|
||||
export default function ProjectAddFile() {
|
||||
const { colors } = useTheme();
|
||||
const { id } = useLocalSearchParams<{ id: string }>()
|
||||
const [fileForm, setFileForm] = useState<any[]>([])
|
||||
const [listFile, setListFile] = useState<any[]>([])
|
||||
@@ -127,32 +129,46 @@ export default function ProjectAddFile() {
|
||||
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Tambah File',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <ButtonSaveHeader
|
||||
disable={fileForm.length == 0 || loading ? true : false}
|
||||
category="create"
|
||||
onPress={() => { handleAddFile() }} />
|
||||
// headerRight: () => <ButtonSaveHeader
|
||||
// disable={fileForm.length == 0 || loading ? true : false}
|
||||
// category="create"
|
||||
// onPress={() => { handleAddFile() }} />
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah File"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={fileForm.length == 0 || loading ? true : false}
|
||||
category="create"
|
||||
onPress={() => { handleAddFile() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
<ScrollView style={{ backgroundColor: colors.background }}>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<ButtonSelect value="Upload File" onPress={pickDocumentAsync} />
|
||||
{
|
||||
listFile.length > 0 && (
|
||||
<View style={[Styles.mb15]}>
|
||||
<Text style={[Styles.textDefaultSemiBold, Styles.mv05]}>File</Text>
|
||||
<View style={[Styles.wrapPaper]}>
|
||||
<View style={[Styles.wrapPaper, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
{
|
||||
listFile.map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType="all"
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item}
|
||||
titleWeight="normal"
|
||||
onPress={() => { setIndexDelFile(index); setModal(true) }}
|
||||
@@ -174,7 +190,7 @@ export default function ProjectAddFile() {
|
||||
<DrawerBottom animation="slide" isVisible={isModal} setVisible={setModal} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => { deleteFile(indexDelFile) }}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import ImageWithLabel from "@/components/imageWithLabel";
|
||||
@@ -9,6 +9,7 @@ import Styles from "@/constants/Styles";
|
||||
import { apiAddMemberProject, apiGetProjectOne, apiGetUser } from "@/lib/api";
|
||||
import { setUpdateProject } from "@/lib/projectUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -23,6 +24,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function AddMemberProject() {
|
||||
const { colors } = useTheme();
|
||||
const dispatch = useDispatch()
|
||||
const update = useSelector((state: any) => state.projectUpdate)
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
@@ -97,21 +99,37 @@ export default function AddMemberProject() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Tambah Anggota Kegiatan',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="update"
|
||||
// disable={selectMember.length == 0 || loading ? true : false}
|
||||
// onPress={() => {
|
||||
// handleAddMember()
|
||||
// }}
|
||||
// />
|
||||
// )
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Anggota Kegiatan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="update"
|
||||
disable={selectMember.length == 0 || loading ? true : false}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<InputSearch onChange={(val) => handleSearch(val)} value={search} />
|
||||
{
|
||||
selectMember.length > 0
|
||||
@@ -132,11 +150,11 @@ export default function AddMemberProject() {
|
||||
</View>
|
||||
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
|
||||
<Text style={[Styles.textDefault, Styles.pv05, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada member yang dipilih</Text>
|
||||
}
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
{
|
||||
data.length > 0 ?
|
||||
@@ -157,12 +175,12 @@ export default function AddMemberProject() {
|
||||
<View style={[Styles.ml10]}>
|
||||
<Text style={[Styles.textDefault]} numberOfLines={1}>{item.name}</Text>
|
||||
{
|
||||
found && <Text style={[Styles.textInformation, Styles.cGray]}>sudah menjadi anggota</Text>
|
||||
found && <Text style={[Styles.textInformation, { color: colors.dimmed }]}>sudah menjadi anggota</Text>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={'black'} />
|
||||
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={colors.text} />
|
||||
}
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import ModalAddDetailTugasProject from "@/components/project/modalAddDetailTugasProject";
|
||||
import Text from "@/components/Text";
|
||||
@@ -9,6 +10,7 @@ import { formatDateOnly } from "@/lib/fun_formatDateOnly";
|
||||
import { getDatesInRange } from "@/lib/fun_getDatesInRange";
|
||||
import { setUpdateProject } from "@/lib/projectUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import 'intl';
|
||||
@@ -18,7 +20,6 @@ import { useEffect, useState } from "react";
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Pressable,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
View
|
||||
@@ -30,6 +31,7 @@ import DateTimePicker, {
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function ProjectAddTask() {
|
||||
const { colors } = useTheme();
|
||||
const headerHeight = useHeaderHeight();
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const dispatch = useDispatch()
|
||||
@@ -133,34 +135,48 @@ export default function ProjectAddTask() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah Tugas",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disable || loading}
|
||||
category="create"
|
||||
onPress={() => { handleCreate() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disable || loading}
|
||||
// category="create"
|
||||
// onPress={() => { handleCreate() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Tugas"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disable || loading}
|
||||
category="create"
|
||||
onPress={() => { handleCreate() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={headerHeight}
|
||||
>
|
||||
<ScrollView>
|
||||
<ScrollView style={{ backgroundColor: colors.background }}>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
<DateTimePicker
|
||||
mode="range"
|
||||
startDate={range.startDate}
|
||||
@@ -170,13 +186,13 @@ export default function ProjectAddTask() {
|
||||
selected: Styles.selectedDate,
|
||||
selected_label: Styles.cWhite,
|
||||
range_fill: Styles.selectRangeDate,
|
||||
month_label: Styles.cBlack,
|
||||
month_selector_label: Styles.cBlack,
|
||||
year_label: Styles.cBlack,
|
||||
year_selector_label: Styles.cBlack,
|
||||
day_label: Styles.cBlack,
|
||||
time_label: Styles.cBlack,
|
||||
weekday_label: Styles.cBlack,
|
||||
month_label: { color: colors.text },
|
||||
month_selector_label: { color: colors.text },
|
||||
year_label: { color: colors.text },
|
||||
year_selector_label: { color: colors.text },
|
||||
day_label: { color: colors.text },
|
||||
time_label: { color: colors.text },
|
||||
weekday_label: { color: colors.text },
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -184,38 +200,39 @@ export default function ProjectAddTask() {
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
<View style={[{ width: "48%" }]}>
|
||||
<Text style={[Styles.mb05]}>
|
||||
Tanggal Mulai <Text style={Styles.cError}>*</Text>
|
||||
Tanggal Mulai <Text style={{ color: colors.error }}>*</Text>
|
||||
</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: "center" }}>{from}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={[{ width: "48%" }]}>
|
||||
<Text style={[Styles.mb05]}>
|
||||
Tanggal Berakhir <Text style={Styles.cError}>*</Text>
|
||||
Tanggal Berakhir <Text style={{ color: colors.error }}>*</Text>
|
||||
</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: "center" }}>{to}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, Styles.cError, Styles.mt05]}>Tanggal tidak boleh kosong</Text>
|
||||
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, { color: colors.error }, Styles.mt05]}>Tanggal tidak boleh kosong</Text>
|
||||
}
|
||||
<Pressable
|
||||
{/* <Pressable
|
||||
style={[Styles.btnTab, Styles.btnLainnya, dsbButton && Styles.btnDisabled]}
|
||||
disabled={dsbButton}
|
||||
onPress={() => { setModalDetail(true) }}
|
||||
>
|
||||
<Text style={[dsbButton ? Styles.cGray : Styles.cWhite]}>Detail</Text>
|
||||
</Pressable>
|
||||
</Pressable> */}
|
||||
<ButtonSelect value="Detail" onPress={() => { setModalDetail(true) }} />
|
||||
</View>
|
||||
<InputForm
|
||||
label="Judul Tugas"
|
||||
type="default"
|
||||
placeholder="Judul Tugas"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={title}
|
||||
error={error.title}
|
||||
errorText="Judul tidak boleh kosong"
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiCancelProject } from "@/lib/api";
|
||||
import { setUpdateProject } from "@/lib/projectUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { SafeAreaView, ScrollView, View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function ProjectCancel() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const dispatch = useDispatch();
|
||||
@@ -65,32 +67,48 @@ export default function ProjectCancel() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Pembatalan Kegiatan",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disable || loading}
|
||||
category="cancel"
|
||||
onPress={() => {
|
||||
handleCancel();
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disable || loading}
|
||||
// category="cancel"
|
||||
// onPress={() => {
|
||||
// handleCancel();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Pembatalan Kegiatan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disable || loading}
|
||||
category="cancel"
|
||||
onPress={() => {
|
||||
handleCancel();
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
<InputForm
|
||||
@@ -98,7 +116,7 @@ export default function ProjectCancel() {
|
||||
type="default"
|
||||
placeholder="Alasan Pembatalan"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
error={error}
|
||||
errorText="Alasan pembatalan harus diisi"
|
||||
onChange={(val) => onValidation(val)}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiEditProject, apiGetProjectOne } from "@/lib/api";
|
||||
import { setUpdateProject } from "@/lib/projectUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SafeAreaView, ScrollView, View } from "react-native";
|
||||
@@ -12,6 +13,7 @@ import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function EditProject() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const dispatch = useDispatch()
|
||||
@@ -43,7 +45,7 @@ export default function EditProject() {
|
||||
setJudul(val)
|
||||
if (val == "" || val == "null") {
|
||||
setError(true)
|
||||
}else{
|
||||
} else {
|
||||
setError(false)
|
||||
}
|
||||
}
|
||||
@@ -86,35 +88,49 @@ export default function EditProject() {
|
||||
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Edit Judul Kegiatan",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disable || loading}
|
||||
category="update"
|
||||
onPress={() => { handleUpdate() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disable || loading}
|
||||
// category="update"
|
||||
// onPress={() => { handleUpdate() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Judul Kegiatan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disable || loading}
|
||||
category="update"
|
||||
onPress={() => { handleUpdate() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView>
|
||||
<ScrollView style={{ backgroundColor: colors.background }}>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<InputForm
|
||||
label="Judul Kegiatan"
|
||||
type="default"
|
||||
placeholder="Judul Kegiatan"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={judul}
|
||||
onChange={(val) => { onValidation(val) }}
|
||||
error={error}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import HeaderRightProjectDetail from "@/components/project/headerProjectDetail";
|
||||
import SectionFile from "@/components/project/sectionFile";
|
||||
import SectionLink from "@/components/project/sectionLink";
|
||||
@@ -10,8 +10,9 @@ import SectionProgress from "@/components/sectionProgress";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetProjectOne } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
@@ -32,6 +33,7 @@ type Props = {
|
||||
|
||||
export default function DetailProject() {
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const [data, setData] = useState<Props>()
|
||||
const [progress, setProgress] = useState(0)
|
||||
@@ -91,20 +93,32 @@ export default function DetailProject() {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: loading ? 'Loading... ' : data?.title,
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (entityUser.role == "user" || entityUser.role == "coadmin") && !isMember ? null : <HeaderRightProjectDetail id={id} status={data?.status} />,
|
||||
// headerRight: () => (entityUser.role == "user" || entityUser.role == "coadmin") && !isMember ? null : <HeaderRightProjectDetail id={id} status={data?.status} />,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title={loading ? 'Loading...' : data && data?.title || ''}
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
(entityUser.role == "user" || entityUser.role == "coadmin") && !isMember ? null : <HeaderRightProjectDetail id={id} status={data?.status} />
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
style={{ backgroundColor: colors.background }}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetProjectOne, apiReportProject } from "@/lib/api";
|
||||
import { setUpdateProject } from "@/lib/projectUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { SafeAreaView, ScrollView, View } from "react-native";
|
||||
@@ -12,6 +13,7 @@ import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function ReportProject() {
|
||||
const { colors } = useTheme();
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const dispatch = useDispatch()
|
||||
@@ -86,30 +88,44 @@ export default function ReportProject() {
|
||||
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Laporan Kegiatan",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disable || loading}
|
||||
category="update"
|
||||
onPress={() => { handleUpdate() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disable || loading}
|
||||
// category="update"
|
||||
// onPress={() => { handleUpdate() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Laporan Kegiatan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disable || loading}
|
||||
category="update"
|
||||
onPress={() => { handleUpdate() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
<InputForm
|
||||
@@ -117,7 +133,7 @@ export default function ReportProject() {
|
||||
type="default"
|
||||
placeholder="Laporan Kegiatan"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={laporan}
|
||||
onChange={(val) => { onValidation(val) }}
|
||||
error={error}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
@@ -18,6 +18,7 @@ import { setMemberChoose } from "@/lib/memberChoose";
|
||||
import { setUpdateProject } from "@/lib/projectUpdate";
|
||||
import { setTaskCreate } from "@/lib/taskCreate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import * as DocumentPicker from "expo-document-picker";
|
||||
import { router, Stack } from "expo-router";
|
||||
@@ -31,6 +32,7 @@ import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function CreateProject() {
|
||||
const { colors } = useTheme();
|
||||
const [loading, setLoading] = useState(false)
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const [chooseGroup, setChooseGroup] = useState({ val: "", label: "" });
|
||||
@@ -190,32 +192,47 @@ export default function CreateProject() {
|
||||
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
handleBack();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// handleBack();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah Kegiatan",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading}
|
||||
category="create"
|
||||
onPress={() => {
|
||||
handleCreate()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disableBtn || loading}
|
||||
// category="create"
|
||||
// onPress={() => {
|
||||
// handleCreate()
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader title="Tambah Kegiatan"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loading}
|
||||
category="create"
|
||||
onPress={() => {
|
||||
handleCreate()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={[Styles.h100]}
|
||||
style={[Styles.h100, { backgroundColor: colors.background }]}
|
||||
>
|
||||
<View style={[Styles.p15]}>
|
||||
{
|
||||
@@ -227,6 +244,7 @@ export default function CreateProject() {
|
||||
placeholder="Pilih Lembaga Desa"
|
||||
value={chooseGroup.label}
|
||||
required
|
||||
bg={colors.card}
|
||||
onPress={() => {
|
||||
setValChoose(chooseGroup.val);
|
||||
setValSelect("group");
|
||||
@@ -242,6 +260,7 @@ export default function CreateProject() {
|
||||
type="default"
|
||||
placeholder="Nama Kegiatan"
|
||||
required
|
||||
bg={colors.card}
|
||||
value={dataForm.title}
|
||||
error={error.title}
|
||||
errorText="Nama kegiatan tidak boleh kosong"
|
||||
@@ -279,13 +298,13 @@ export default function CreateProject() {
|
||||
fileForm.length > 0 && (
|
||||
<View style={[Styles.mb15]}>
|
||||
<Text style={[Styles.textDefaultSemiBold, Styles.mv05]}>File</Text>
|
||||
<View style={[Styles.wrapPaper]}>
|
||||
<View style={[Styles.wrapPaper, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
{
|
||||
fileForm.map((item, index) => (
|
||||
<BorderBottomItem
|
||||
key={index}
|
||||
borderType="all"
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color="black" />}
|
||||
icon={<MaterialCommunityIcons name="file-outline" size={25} color={colors.text} />}
|
||||
title={item.name}
|
||||
titleWeight="normal"
|
||||
onPress={() => { setIndexDelFile(index); setModal(true) }}
|
||||
@@ -303,7 +322,7 @@ export default function CreateProject() {
|
||||
<Text>Total {entitiesMember.length} Anggota</Text>
|
||||
</View>
|
||||
|
||||
<View style={[Styles.borderAll, Styles.round10, Styles.p10]}>
|
||||
<View style={[Styles.borderAll, Styles.round05, Styles.p10, { borderColor: colors.icon + '20', backgroundColor: colors.card }]}>
|
||||
{entitiesMember.map(
|
||||
(item: { img: any; name: any }, index: any) => {
|
||||
return (
|
||||
@@ -329,7 +348,7 @@ export default function CreateProject() {
|
||||
<DrawerBottom animation="slide" isVisible={isModal} setVisible={setModal} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => { deleteFile(indexDelFile) }}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import ImageWithLabel from "@/components/imageWithLabel";
|
||||
@@ -9,10 +9,11 @@ import Styles from "@/constants/Styles";
|
||||
import { apiGetUser } from "@/lib/api";
|
||||
import { setMemberChoose } from "@/lib/memberChoose";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Pressable, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Pressable, ScrollView, View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
@@ -23,6 +24,7 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function AddMemberCreateProject() {
|
||||
const { colors } = useTheme();
|
||||
const dispatch = useDispatch()
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
@@ -74,21 +76,36 @@ export default function AddMemberCreateProject() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Pilih Anggota',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={selectMember.length > 0 ? false : true}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// category="create"
|
||||
// disable={selectMember.length > 0 ? false : true}
|
||||
// onPress={() => {
|
||||
// handleAddMember()
|
||||
// }}
|
||||
// />
|
||||
// )
|
||||
header: () => (
|
||||
<AppHeader title="Pilih Anggota"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
category="create"
|
||||
disable={selectMember.length > 0 ? false : true}
|
||||
onPress={() => {
|
||||
handleAddMember()
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<InputSearch onChange={(val) => setSearch(val)} value={search} />
|
||||
|
||||
{
|
||||
@@ -110,10 +127,11 @@ export default function AddMemberCreateProject() {
|
||||
</View>
|
||||
|
||||
:
|
||||
<Text style={[Styles.textDefault, Styles.cGray, Styles.pv05, { textAlign: 'center' }]}>Tidak ada member yang dipilih</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }, Styles.pv05]}>Tidak ada member yang dipilih</Text>
|
||||
}
|
||||
<ScrollView
|
||||
showsVerticalScrollIndicator={false}
|
||||
style={{ backgroundColor: colors.background }}
|
||||
>
|
||||
|
||||
{
|
||||
@@ -122,7 +140,7 @@ export default function AddMemberCreateProject() {
|
||||
return (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[Styles.itemSelectModal]}
|
||||
style={[Styles.itemSelectModal, { borderColor: colors.icon + '20' }]}
|
||||
onPress={() => {
|
||||
onChoose(item.id, item.name, item.img)
|
||||
}}
|
||||
@@ -134,7 +152,7 @@ export default function AddMemberCreateProject() {
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={'black'} />
|
||||
selectMember.some((i: any) => i.idUser == item.id) && <AntDesign name="check" size={20} color={colors.text} />
|
||||
}
|
||||
</Pressable>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import ModalAddDetailTugasProject from "@/components/project/modalAddDetailTugasProject";
|
||||
import Text from "@/components/Text";
|
||||
@@ -7,16 +8,16 @@ import Styles from "@/constants/Styles";
|
||||
import { formatDateOnly } from "@/lib/fun_formatDateOnly";
|
||||
import { getDatesInRange } from "@/lib/fun_getDatesInRange";
|
||||
import { setTaskCreate } from "@/lib/taskCreate";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import { router, Stack } from "expo-router";
|
||||
import 'intl';
|
||||
import 'intl/locale-data/jsonp/id';
|
||||
import moment from "moment";
|
||||
import { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import {
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
Pressable,
|
||||
SafeAreaView,
|
||||
ScrollView,
|
||||
View
|
||||
@@ -27,6 +28,7 @@ import DateTimePicker, {
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function CreateProjectAddTask() {
|
||||
const { colors } = useTheme();
|
||||
const headerHeight = useHeaderHeight();
|
||||
const dispatch = useDispatch()
|
||||
const [disable, setDisable] = useState(true);
|
||||
@@ -119,34 +121,48 @@ export default function CreateProjectAddTask() {
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => (
|
||||
<ButtonBackHeader
|
||||
onPress={() => {
|
||||
router.back();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
// headerLeft: () => (
|
||||
// <ButtonBackHeader
|
||||
// onPress={() => {
|
||||
// router.back();
|
||||
// }}
|
||||
// />
|
||||
// ),
|
||||
headerTitle: "Tambah Tugas",
|
||||
headerTitleAlign: "center",
|
||||
headerRight: () => (
|
||||
<ButtonSaveHeader
|
||||
disable={disable}
|
||||
category="create"
|
||||
onPress={() => { handleCreate() }}
|
||||
// headerRight: () => (
|
||||
// <ButtonSaveHeader
|
||||
// disable={disable}
|
||||
// category="create"
|
||||
// onPress={() => { handleCreate() }}
|
||||
// />
|
||||
// ),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Tambah Tugas"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disable}
|
||||
category="create"
|
||||
onPress={() => { handleCreate() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={headerHeight}
|
||||
>
|
||||
<ScrollView>
|
||||
<ScrollView style={{ backgroundColor: colors.background }}>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
<DateTimePicker
|
||||
mode="range"
|
||||
startDate={range.startDate}
|
||||
@@ -156,13 +172,13 @@ export default function CreateProjectAddTask() {
|
||||
selected: Styles.selectedDate,
|
||||
selected_label: Styles.cWhite,
|
||||
range_fill: Styles.selectRangeDate,
|
||||
month_label: Styles.cBlack,
|
||||
month_selector_label: Styles.cBlack,
|
||||
year_label: Styles.cBlack,
|
||||
year_selector_label: Styles.cBlack,
|
||||
day_label: Styles.cBlack,
|
||||
time_label: Styles.cBlack,
|
||||
weekday_label: Styles.cBlack,
|
||||
month_label: { color: colors.text },
|
||||
month_selector_label: { color: colors.text },
|
||||
year_label: { color: colors.text },
|
||||
year_selector_label: { color: colors.text },
|
||||
day_label: { color: colors.text },
|
||||
time_label: { color: colors.text },
|
||||
weekday_label: { color: colors.text },
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
@@ -170,38 +186,39 @@ export default function CreateProjectAddTask() {
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
<View style={[{ width: "48%" }]}>
|
||||
<Text style={[Styles.mb05]}>
|
||||
Tanggal Mulai <Text style={Styles.cError}>*</Text>
|
||||
Tanggal Mulai <Text style={{ color: colors.error }}>*</Text>
|
||||
</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: "center" }}>{from}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={[{ width: "48%" }]}>
|
||||
<Text style={[Styles.mb05]}>
|
||||
Tanggal Berakhir <Text style={Styles.cError}>*</Text>
|
||||
Tanggal Berakhir <Text style={{ color: colors.error }}>*</Text>
|
||||
</Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: "center" }}>{to}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, Styles.cError, Styles.mt05]}>Tanggal tidak boleh kosong</Text>
|
||||
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, Styles.mt05, { color: colors.error }]}>Tanggal tidak boleh kosong</Text>
|
||||
}
|
||||
<Pressable
|
||||
{/* <Pressable
|
||||
style={[Styles.btnTab, Styles.btnLainnya, dsbButton && Styles.btnDisabled]}
|
||||
disabled={dsbButton}
|
||||
onPress={() => { setModalDetail(true) }}
|
||||
>
|
||||
<Text style={[dsbButton ? Styles.cGray : Styles.cWhite]}>Detail</Text>
|
||||
</Pressable>
|
||||
</Pressable> */}
|
||||
<ButtonSelect value="Detail" onPress={() => { setModalDetail(true) }} />
|
||||
</View>
|
||||
<InputForm
|
||||
label="Judul Tugas"
|
||||
type="default"
|
||||
placeholder="Judul Tugas"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={title}
|
||||
error={error.title}
|
||||
errorText="Judul tidak boleh kosong"
|
||||
|
||||
@@ -7,10 +7,12 @@ import ProgressBar from "@/components/progressBar";
|
||||
import Skeleton from "@/components/skeleton";
|
||||
import SkeletonTwoItem from "@/components/skeletonTwoItem";
|
||||
import Text from "@/components/Text";
|
||||
import WrapTab from "@/components/wrapTab";
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetProject } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import {
|
||||
AntDesign,
|
||||
Ionicons,
|
||||
@@ -32,16 +34,20 @@ type Props = {
|
||||
};
|
||||
|
||||
export default function ListProject() {
|
||||
const { status, group, cat } = useLocalSearchParams<{
|
||||
const { status, group, cat, year } = useLocalSearchParams<{
|
||||
status?: string;
|
||||
group?: string;
|
||||
cat?: string;
|
||||
year?: string;
|
||||
}>();
|
||||
const [statusFix, setStatusFix] = useState<'0' | '1' | '2' | '3'>('0')
|
||||
const { token, decryptToken } = useAuthSession();
|
||||
const { colors } = useTheme();
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
const [search, setSearch] = useState("")
|
||||
const [nameGroup, setNameGroup] = useState("")
|
||||
// ... state same ...
|
||||
const [isYear, setYear] = useState("")
|
||||
const [data, setData] = useState<Props[]>([])
|
||||
const [isList, setList] = useState(false)
|
||||
const update = useSelector((state: any) => state.projectUpdate)
|
||||
@@ -63,11 +69,13 @@ export default function ListProject() {
|
||||
search: search,
|
||||
group: String(group),
|
||||
kategori: String(cat),
|
||||
page: thisPage
|
||||
page: thisPage,
|
||||
year: String(year)
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setNameGroup(response.filter.name);
|
||||
setYear(response.tahun)
|
||||
if (thisPage == 1) {
|
||||
setData(response.data);
|
||||
} else if (thisPage > 1 && response.data.length > 0) {
|
||||
@@ -91,7 +99,7 @@ export default function ListProject() {
|
||||
|
||||
useEffect(() => {
|
||||
handleLoad(true, 1);
|
||||
}, [statusFix, search, group, cat]);
|
||||
}, [statusFix, search, group, cat, year]);
|
||||
|
||||
const loadMoreData = () => {
|
||||
if (waiting) return
|
||||
@@ -118,66 +126,69 @@ export default function ListProject() {
|
||||
})
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1 }]}>
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<View>
|
||||
<ScrollView horizontal style={[Styles.mb10]} showsHorizontalScrollIndicator={false}>
|
||||
<ButtonTab
|
||||
active={statusFix}
|
||||
value="0"
|
||||
onPress={() => { setStatusFix("0") }}
|
||||
label="Segera"
|
||||
icon={
|
||||
<MaterialCommunityIcons
|
||||
name="clock-alert-outline"
|
||||
color={statusFix == "0" ? "white" : "black"}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
n={4}
|
||||
/>
|
||||
<ButtonTab
|
||||
active={statusFix}
|
||||
value="1"
|
||||
onPress={() => { setStatusFix("1") }}
|
||||
label="Dikerjakan"
|
||||
icon={
|
||||
<MaterialCommunityIcons
|
||||
name="progress-check"
|
||||
color={statusFix == "1" ? "white" : "black"}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
n={4}
|
||||
/>
|
||||
<ButtonTab
|
||||
active={statusFix}
|
||||
value="2"
|
||||
onPress={() => { setStatusFix("2") }}
|
||||
label="Selesai"
|
||||
icon={
|
||||
<Ionicons
|
||||
name="checkmark-done-circle-outline"
|
||||
color={statusFix == "2" ? "white" : "black"}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
n={4}
|
||||
/>
|
||||
<ButtonTab
|
||||
active={statusFix}
|
||||
value="3"
|
||||
onPress={() => { setStatusFix("3") }}
|
||||
label="Batal"
|
||||
icon={
|
||||
<AntDesign
|
||||
name="closecircleo"
|
||||
color={statusFix == "3" ? "white" : "black"}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
n={4}
|
||||
/>
|
||||
</ScrollView>
|
||||
<WrapTab>
|
||||
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={[Styles.round20]}>
|
||||
<ButtonTab
|
||||
active={statusFix}
|
||||
value="0"
|
||||
onPress={() => { setStatusFix("0") }}
|
||||
label="Segera"
|
||||
icon={
|
||||
<MaterialCommunityIcons
|
||||
name="clock-alert-outline"
|
||||
color={statusFix == "0" ? "white" : colors.dimmed}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
n={4}
|
||||
/>
|
||||
<ButtonTab
|
||||
active={statusFix}
|
||||
value="1"
|
||||
onPress={() => { setStatusFix("1") }}
|
||||
label="Dikerjakan"
|
||||
icon={
|
||||
<MaterialCommunityIcons
|
||||
name="progress-check"
|
||||
color={statusFix == "1" ? "white" : colors.dimmed}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
n={4}
|
||||
/>
|
||||
<ButtonTab
|
||||
active={statusFix}
|
||||
value="2"
|
||||
onPress={() => { setStatusFix("2") }}
|
||||
label="Selesai"
|
||||
icon={
|
||||
<Ionicons
|
||||
name="checkmark-done-circle-outline"
|
||||
color={statusFix == "2" ? "white" : colors.dimmed}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
n={4}
|
||||
/>
|
||||
<ButtonTab
|
||||
active={statusFix}
|
||||
value="3"
|
||||
onPress={() => { setStatusFix("3") }}
|
||||
label="Batal"
|
||||
icon={
|
||||
<AntDesign
|
||||
name="closecircleo"
|
||||
color={statusFix == "3" ? "white" : colors.dimmed}
|
||||
size={20}
|
||||
/>
|
||||
}
|
||||
n={4}
|
||||
/>
|
||||
</ScrollView>
|
||||
</WrapTab>
|
||||
|
||||
<View style={[Styles.rowSpaceBetween, { alignItems: 'center' }]}>
|
||||
<InputSearch width={68} onChange={setSearch} />
|
||||
<Pressable
|
||||
@@ -187,24 +198,32 @@ export default function ListProject() {
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={isList ? "format-list-bulleted" : "view-grid"}
|
||||
color={"black"}
|
||||
color={colors.text}
|
||||
size={30}
|
||||
/>
|
||||
</Pressable>
|
||||
</View>
|
||||
<View style={[Styles.mv05]}>
|
||||
{
|
||||
entityUser.role != 'cosupadmin' && entityUser.role != 'admin' &&
|
||||
<Text>Filter :
|
||||
// entityUser.role != 'cosupadmin' && entityUser.role != 'admin' &&
|
||||
<View style={[Styles.rowOnly]}>
|
||||
<Text style={[Styles.mr05]}>Filter :</Text>
|
||||
{
|
||||
(entityUser.role == "supadmin" || entityUser.role == "developer") && nameGroup
|
||||
(entityUser.role == "supadmin" || entityUser.role == "developer") &&
|
||||
<LabelStatus size="small" category="secondary" text={nameGroup} style={{ marginRight: 5 }} />
|
||||
}
|
||||
{
|
||||
(entityUser.role == 'user' || entityUser.role == 'coadmin')
|
||||
? (cat == 'null' || cat == 'undefined' || cat == undefined || cat == '' || cat == 'data-saya') ? 'Kegiatan Saya' : 'Semua Kegiatan'
|
||||
? (cat == 'null' || cat == 'undefined' || cat == undefined || cat == '' || cat == 'data-saya') ? <LabelStatus size="small" category="secondary" text="Kegiatan Saya" style={{ marginRight: 5 }} /> : <LabelStatus size="small" category="secondary" text="Semua Kegiatan" style={{ marginRight: 5 }} />
|
||||
: ''
|
||||
}
|
||||
</Text>
|
||||
<LabelStatus size="small" category="secondary" text={isYear} style={{ marginRight: 5 }} />
|
||||
{/* {
|
||||
(entityUser.role == 'user' || entityUser.role == 'coadmin')
|
||||
? (cat == 'null' || cat == 'undefined' || cat == undefined || cat == '' || cat == 'data-saya') ? <LabelStatus size="small" category="primary" text="Kegiatan Saya" /> : <LabelStatus size="small" category="primary" text="Semua Kegiatan" />
|
||||
: ''
|
||||
} */}
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
@@ -234,12 +253,13 @@ export default function ListProject() {
|
||||
key={index}
|
||||
onPress={() => { router.push(`/project/${item.id}`); }}
|
||||
borderType="bottom"
|
||||
bgColor="transparent"
|
||||
icon={
|
||||
<View style={[Styles.iconContent, ColorsStatus.lightGreen]} >
|
||||
<View style={[Styles.iconContent]} >
|
||||
<AntDesign
|
||||
name="areachart"
|
||||
size={25}
|
||||
color={"#384288"}
|
||||
color={"black"}
|
||||
/>
|
||||
</View>
|
||||
}
|
||||
@@ -255,6 +275,7 @@ export default function ListProject() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -301,17 +322,17 @@ export default function ListProject() {
|
||||
>
|
||||
<ProgressBar value={item.progress} category="list" />
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
<Text style={[Styles.textDefault, Styles.cGray]}>
|
||||
<Text style={[Styles.textDefault, { color: colors.dimmed }]}>
|
||||
{item.createdAt}
|
||||
</Text>
|
||||
<LabelStatus
|
||||
size="default"
|
||||
category={
|
||||
item.status === 0 ? 'primary' :
|
||||
item.status === 0 ? 'secondary' :
|
||||
item.status === 1 ? 'warning' :
|
||||
item.status === 2 ? 'success' :
|
||||
item.status === 3 ? 'error' :
|
||||
'primary'
|
||||
'secondary'
|
||||
}
|
||||
text={
|
||||
item.status === 0 ? 'SEGERA' :
|
||||
@@ -333,6 +354,7 @@ export default function ListProject() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
@@ -349,7 +371,7 @@ export default function ListProject() {
|
||||
>
|
||||
<ProgressBar value={item.progress} category="list" />
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
<Text style={[Styles.textDefault, Styles.cGray]}>
|
||||
<Text style={[Styles.textDefault, { color: colors.dimmed }]}>
|
||||
{item.createdAt}
|
||||
</Text>
|
||||
<LabelStatus
|
||||
@@ -377,7 +399,7 @@ export default function ListProject() {
|
||||
)
|
||||
:
|
||||
<View style={[Styles.mt15]}>
|
||||
<Text style={[Styles.textDefault, Styles.cGray, { textAlign: 'center' }]}>Tidak ada kegiatan</Text>
|
||||
<Text style={[Styles.textDefault, { textAlign: 'center', color: colors.dimmed }]}>Tidak ada kegiatan</Text>
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import ButtonSaveHeader from "@/components/buttonSaveHeader";
|
||||
import ButtonSelect from "@/components/buttonSelect";
|
||||
import { InputForm } from "@/components/inputForm";
|
||||
import ModalAddDetailTugasProject from "@/components/project/modalAddDetailTugasProject";
|
||||
import Text from "@/components/Text";
|
||||
@@ -9,18 +10,20 @@ import { formatDateOnly } from "@/lib/fun_formatDateOnly";
|
||||
import { getDatesInRange } from "@/lib/fun_getDatesInRange";
|
||||
import { setUpdateProject } from "@/lib/projectUpdate";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import { router, Stack, useLocalSearchParams } from "expo-router";
|
||||
import 'intl';
|
||||
import 'intl/locale-data/jsonp/id';
|
||||
import moment from "moment";
|
||||
import { useEffect, useState } from "react";
|
||||
import { KeyboardAvoidingView, Platform, Pressable, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { KeyboardAvoidingView, Platform, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
import DateTimePicker, { DateType } from "react-native-ui-datepicker";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export default function UpdateProjectTask() {
|
||||
const { colors } = useTheme();
|
||||
const headerHeight = useHeaderHeight();
|
||||
const dispatch = useDispatch()
|
||||
const update = useSelector((state: any) => state.projectUpdate)
|
||||
@@ -169,26 +172,40 @@ export default function UpdateProjectTask() {
|
||||
}, [range])
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
// headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Edit Tanggal dan Tugas',
|
||||
headerTitleAlign: 'center',
|
||||
headerRight: () => <ButtonSaveHeader
|
||||
disable={disableBtn || loadingSubmit}
|
||||
category="update"
|
||||
onPress={() => { handleEdit() }}
|
||||
/>
|
||||
// headerRight: () => <ButtonSaveHeader
|
||||
// disable={disableBtn || loadingSubmit}
|
||||
// category="update"
|
||||
// onPress={() => { handleEdit() }}
|
||||
// />
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Edit Tanggal dan Tugas"
|
||||
showBack={true}
|
||||
onPressLeft={() => router.back()}
|
||||
right={
|
||||
<ButtonSaveHeader
|
||||
disable={disableBtn || loadingSubmit}
|
||||
category="update"
|
||||
onPress={() => { handleEdit() }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
||||
keyboardVerticalOffset={headerHeight}
|
||||
>
|
||||
<ScrollView>
|
||||
<ScrollView style={{ backgroundColor: colors.background }}>
|
||||
<View style={[Styles.p15, Styles.mb100]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<View style={[Styles.wrapPaper, Styles.p10, { backgroundColor: colors.card, borderColor: colors.background }]}>
|
||||
{
|
||||
!loading
|
||||
&&
|
||||
@@ -203,13 +220,13 @@ export default function UpdateProjectTask() {
|
||||
selected: Styles.selectedDate,
|
||||
selected_label: Styles.cWhite,
|
||||
range_fill: Styles.selectRangeDate,
|
||||
month_label: Styles.cBlack,
|
||||
month_selector_label: Styles.cBlack,
|
||||
year_label: Styles.cBlack,
|
||||
year_selector_label: Styles.cBlack,
|
||||
day_label: Styles.cBlack,
|
||||
time_label: Styles.cBlack,
|
||||
weekday_label: Styles.cBlack,
|
||||
month_label: { color: colors.text },
|
||||
month_selector_label: { color: colors.text },
|
||||
year_label: { color: colors.text },
|
||||
year_selector_label: { color: colors.text },
|
||||
day_label: { color: colors.text },
|
||||
time_label: { color: colors.text },
|
||||
weekday_label: { color: colors.text },
|
||||
}}
|
||||
/>
|
||||
}
|
||||
@@ -218,35 +235,36 @@ export default function UpdateProjectTask() {
|
||||
<View style={[Styles.mv10]}>
|
||||
<View style={[Styles.rowSpaceBetween]}>
|
||||
<View style={[{ width: '48%' }]}>
|
||||
<Text style={[Styles.mb05]}>Tanggal Mulai <Text style={Styles.cError}>*</Text></Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<Text style={[Styles.mb05]}>Tanggal Mulai <Text style={{ color: colors.error }}>*</Text></Text>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: 'center' }}>{from}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={[{ width: '48%' }]}>
|
||||
<Text style={[Styles.mb05]}>Tanggal Berakhir <Text style={Styles.cError}>*</Text></Text>
|
||||
<View style={[Styles.wrapPaper, Styles.p10]}>
|
||||
<Text style={[Styles.mb05]}>Tanggal Berakhir <Text style={{ color: colors.error }}>*</Text></Text>
|
||||
<View style={[Styles.wrapPaper, Styles.noShadow, Styles.borderAll, Styles.p10, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
|
||||
<Text style={{ textAlign: 'center' }}>{to}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, Styles.cError, Styles.mt05]}>Tanggal tidak boleh kosong</Text>
|
||||
(error.endDate || error.startDate) && <Text style={[Styles.textInformation, { color: colors.error }, Styles.mt05]}>Tanggal tidak boleh kosong</Text>
|
||||
}
|
||||
<Pressable
|
||||
{/* <Pressable
|
||||
style={[Styles.btnTab, Styles.btnLainnya, dsbButton && Styles.btnDisabled]}
|
||||
disabled={dsbButton}
|
||||
onPress={() => { setModalDetail(true) }}
|
||||
>
|
||||
<Text style={[dsbButton ? Styles.cGray : Styles.cWhite]}>Detail</Text>
|
||||
</Pressable>
|
||||
</Pressable> */}
|
||||
<ButtonSelect value="Detail" onPress={() => { setModalDetail(true) }} />
|
||||
</View>
|
||||
<InputForm
|
||||
label="Judul Tugas"
|
||||
type="default"
|
||||
placeholder="Judul Tugas"
|
||||
required
|
||||
bg="white"
|
||||
bg={colors.card}
|
||||
value={title}
|
||||
error={error.title}
|
||||
errorText="Judul tidak boleh kosong"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/AppHeader";
|
||||
import BorderBottomItem from "@/components/borderBottomItem";
|
||||
import ButtonBackHeader from "@/components/buttonBackHeader";
|
||||
import ImageUser from "@/components/imageNew";
|
||||
import InputSearch from "@/components/inputSearch";
|
||||
import Text from '@/components/Text';
|
||||
@@ -8,12 +8,14 @@ import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiGetSearch } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign, MaterialIcons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
import React, { useState } from "react";
|
||||
import { RefreshControl, SafeAreaView, ScrollView, View } from "react-native";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
// ... types ...
|
||||
type PropsUser = {
|
||||
id: string
|
||||
name: string
|
||||
@@ -43,6 +45,7 @@ export default function Search() {
|
||||
const [dataProject, setDataProject] = useState<PropProject[]>([])
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const [search, setSearch] = useState('')
|
||||
const { colors } = useTheme();
|
||||
|
||||
async function handleSearch(cari: string) {
|
||||
try {
|
||||
@@ -79,12 +82,14 @@ export default function Search() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SafeAreaView>
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
headerLeft: () => <ButtonBackHeader onPress={() => { router.back() }} />,
|
||||
headerTitle: 'Pencarian',
|
||||
headerTitleAlign: 'center'
|
||||
headerTitleAlign: 'center',
|
||||
header: () => (
|
||||
<AppHeader title="Pencarian" showBack={true} onPressLeft={() => router.back()} />
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<View style={[Styles.p15]}>
|
||||
@@ -98,6 +103,7 @@ export default function Search() {
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={handleRefresh}
|
||||
tintColor={colors.icon}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@@ -175,7 +181,7 @@ export default function Search() {
|
||||
</ScrollView>
|
||||
:
|
||||
<View style={[Styles.contentItemCenter, Styles.mt10]}>
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>Tidak ada data</Text>
|
||||
<Text style={[Styles.textInformation, { color: colors.icon }]}>Tidak ada data</Text>
|
||||
</View>
|
||||
}
|
||||
|
||||
|
||||
186
app/(application)/setting/index.tsx
Normal file
186
app/(application)/setting/index.tsx
Normal file
@@ -0,0 +1,186 @@
|
||||
import ModalConfirmation from "@/components/ModalConfirmation";
|
||||
import Text from "@/components/Text";
|
||||
import ButtonSetting from "@/components/buttonSetting";
|
||||
import DrawerBottom from "@/components/drawerBottom";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiRegisteredToken, apiUnregisteredToken } from "@/lib/api";
|
||||
import { checkPermission, getToken, openSettings, requestPermission } from "@/lib/useNotification";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { Feather, Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { AppState, AppStateStatus, Pressable, View } from "react-native";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
export default function ListSetting() {
|
||||
const { theme, setTheme, colors } = useTheme()
|
||||
const { signOut } = useAuthSession()
|
||||
const [isNotificationEnabled, setIsNotificationEnabled] = useState<boolean | null>(null);
|
||||
const entities = useSelector((state: any) => state.entities)
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [modalConfig, setModalConfig] = useState({
|
||||
title: '',
|
||||
message: '',
|
||||
confirmText: 'Buka Pengaturan',
|
||||
onConfirm: () => { }
|
||||
});
|
||||
|
||||
const [showLogoutModal, setShowLogoutModal] = useState(false)
|
||||
const [showThemeModal, setShowThemeModal] = useState(false)
|
||||
|
||||
const registerToken = async () => {
|
||||
try {
|
||||
const token = await getToken();
|
||||
if (token) {
|
||||
await apiRegisteredToken({ user: entities.id, token });
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Error registering token:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const unregisterToken = async () => {
|
||||
try {
|
||||
const token = await getToken();
|
||||
if (token) {
|
||||
await apiUnregisteredToken({ user: entities.id, token });
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Error unregistering token:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const checkNotif = useCallback(async () => {
|
||||
const status = await checkPermission();
|
||||
setIsNotificationEnabled((prev) => {
|
||||
if (prev === false && status === true) {
|
||||
registerToken();
|
||||
} else if (prev === true && status === false) {
|
||||
unregisterToken();
|
||||
}
|
||||
return !!status;
|
||||
});
|
||||
}, [entities.id]);
|
||||
|
||||
useEffect(() => {
|
||||
checkNotif();
|
||||
|
||||
const subscription = AppState.addEventListener('change', (nextAppState: AppStateStatus) => {
|
||||
if (nextAppState === 'active') {
|
||||
checkNotif();
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
subscription.remove();
|
||||
};
|
||||
}, [checkNotif]);
|
||||
|
||||
const handleToggleNotif = async () => {
|
||||
if (isNotificationEnabled) {
|
||||
setModalConfig({
|
||||
title: "Matikan Notifikasi?",
|
||||
message: "Anda akan diarahkan ke pengaturan sistem untuk mematikan notifikasi.",
|
||||
confirmText: "Buka Pengaturan",
|
||||
onConfirm: () => {
|
||||
setModalVisible(false);
|
||||
openSettings();
|
||||
}
|
||||
});
|
||||
setModalVisible(true);
|
||||
} else {
|
||||
const granted = await requestPermission();
|
||||
if (granted) {
|
||||
setIsNotificationEnabled(true);
|
||||
registerToken();
|
||||
} else {
|
||||
setModalConfig({
|
||||
title: "Aktifkan Notifikasi?",
|
||||
message: "Izin notifikasi tidak diberikan. Buka pengaturan sistem untuk mengaktifkannya?",
|
||||
confirmText: "Buka Pengaturan",
|
||||
onConfirm: () => {
|
||||
setModalVisible(false);
|
||||
openSettings();
|
||||
}
|
||||
});
|
||||
setModalVisible(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const ThemeOption = ({ label, value, icon }: { label: string, value: 'light' | 'dark' | 'system', icon: string }) => (
|
||||
<Pressable
|
||||
style={[Styles.itemSelectModal, { borderColor: colors.icon + '20' }]}
|
||||
onPress={() => {
|
||||
setTheme(value);
|
||||
}}
|
||||
>
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<Ionicons name={icon as any} size={20} color={colors.text} style={{ marginRight: 10 }} />
|
||||
<Text style={{ color: colors.text }}>{label}</Text>
|
||||
</View>
|
||||
{theme === value && <Ionicons name="checkmark" size={20} color={colors.text} />}
|
||||
</Pressable>
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={[Styles.p15, { flex: 1, backgroundColor: colors.background }]}>
|
||||
<View style={[Styles.wrapPaper, { backgroundColor: colors.card, borderColor: colors.icon + '20' }, Styles.p0, Styles.round05]}>
|
||||
{
|
||||
entities.idUserRole != "developer" &&
|
||||
<ButtonSetting
|
||||
title="Edit Profile"
|
||||
icon={<Feather name="user" size={20} color={colors.text} />}
|
||||
onPress={() => { router.push('/edit-profile') }}
|
||||
/>
|
||||
}
|
||||
<ButtonSetting
|
||||
title="Tema Aplikasi"
|
||||
icon={<Ionicons name="color-palette-outline" size={20} color={colors.text} />}
|
||||
onPress={() => setShowThemeModal(true)}
|
||||
value={theme === 'light' ? 'Terang' : theme === 'dark' ? 'Gelap' : 'Sistem'}
|
||||
/>
|
||||
<ButtonSetting
|
||||
title="Notifikasi"
|
||||
icon={<Feather name="bell" size={20} color={colors.text} />}
|
||||
onPress={handleToggleNotif}
|
||||
value={isNotificationEnabled === null ? 'Memuat...' : isNotificationEnabled ? 'Aktif' : 'Nonaktif'}
|
||||
/>
|
||||
<ButtonSetting
|
||||
title="Keluar"
|
||||
icon={<Feather name="log-out" size={20} color={colors.text} />}
|
||||
onPress={() => setShowLogoutModal(true)}
|
||||
borderBottom={false}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={modalVisible}
|
||||
title={modalConfig.title}
|
||||
message={modalConfig.message}
|
||||
confirmText={modalConfig.confirmText}
|
||||
onConfirm={modalConfig.onConfirm}
|
||||
onCancel={() => setModalVisible(false)}
|
||||
/>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showLogoutModal}
|
||||
title="Keluar"
|
||||
message="Apakah anda yakin ingin keluar?"
|
||||
onConfirm={() => {
|
||||
setShowLogoutModal(false)
|
||||
signOut()
|
||||
}}
|
||||
onCancel={() => setShowLogoutModal(false)}
|
||||
confirmText="Keluar"
|
||||
cancelText="Batal"
|
||||
/>
|
||||
<DrawerBottom animation="slide" isVisible={showThemeModal} setVisible={setShowThemeModal} title="Tema Aplikasi">
|
||||
<ThemeOption label="Terang" value="light" icon="sunny-outline" />
|
||||
<ThemeOption label="Gelap" value="dark" icon="moon-outline" />
|
||||
<ThemeOption label="Sistem" value="system" icon="phone-portrait-outline" />
|
||||
</DrawerBottom>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import AuthProvider from '@/providers/AuthProvider';
|
||||
import ThemeProvider from '@/providers/ThemeProvider';
|
||||
import { useFonts } from 'expo-font';
|
||||
import { Stack } from 'expo-router';
|
||||
import * as SplashScreen from 'expo-splash-screen';
|
||||
@@ -29,14 +30,16 @@ export default function RootLayout() {
|
||||
return (
|
||||
<GestureHandlerRootView style={{ flex: 1 }}>
|
||||
<NotifierWrapper>
|
||||
<AuthProvider>
|
||||
<Stack>
|
||||
<Stack.Screen name="index" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="verification" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="(application)" options={{ headerShown: false }} />
|
||||
</Stack>
|
||||
<StatusBar style="auto" />
|
||||
</AuthProvider>
|
||||
<ThemeProvider>
|
||||
<AuthProvider>
|
||||
<Stack>
|
||||
<Stack.Screen name="index" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="verification" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="(application)" options={{ headerShown: false }} />
|
||||
</Stack>
|
||||
<StatusBar style="auto" />
|
||||
</AuthProvider>
|
||||
</ThemeProvider>
|
||||
</NotifierWrapper>
|
||||
</GestureHandlerRootView>
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@ import Text from '@/components/Text';
|
||||
import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import CryptoES from "crypto-es";
|
||||
import React, { useState } from "react";
|
||||
import { Image, View } from "react-native";
|
||||
@@ -15,16 +16,23 @@ export default function Index() {
|
||||
value,
|
||||
setValue,
|
||||
});
|
||||
const { colors } = useTheme();
|
||||
|
||||
const { signIn } = useAuthSession();
|
||||
const login = (): void => {
|
||||
const random: string = 'contohLoginMobileDarmasaba';
|
||||
var mytexttoEncryption = "contohLoginMobileDarmasaba"
|
||||
const encrypted = CryptoES.AES.encrypt(mytexttoEncryption, ConstEnv.pass_encrypt).toString();
|
||||
signIn(encrypted);
|
||||
// WARNING: This is a hardcoded bypass for development purposes.
|
||||
// It should be removed or secured before production release.
|
||||
if (__DEV__) {
|
||||
const random: string = 'contohLoginMobileDarmasaba';
|
||||
var mytexttoEncryption = "contohLoginMobileDarmasaba"
|
||||
const encrypted = CryptoES.AES.encrypt(mytexttoEncryption, ConstEnv.pass_encrypt).toString();
|
||||
signIn(encrypted);
|
||||
} else {
|
||||
console.warn("Bypass login disabled in production.");
|
||||
}
|
||||
}
|
||||
return (
|
||||
<View style={Styles.wrapLogin} >
|
||||
<View style={[Styles.wrapLogin, { backgroundColor: colors.background }]} >
|
||||
<View style={{ alignItems: "center", marginVertical: 50 }}>
|
||||
<Image
|
||||
source={require("../assets/images/logo.png")}
|
||||
@@ -50,7 +58,7 @@ export default function Index() {
|
||||
renderCell={({ index, symbol, isFocused }) => (
|
||||
<Text
|
||||
key={index}
|
||||
style={[Styles.verificationCell, isFocused && Styles.verificationFocusCell]}
|
||||
style={[Styles.verificationCell, isFocused && Styles.verificationFocusCell, { borderColor: isFocused ? colors.tint : colors.icon, color: colors.text }]}
|
||||
onLayout={getCellOnLayoutHandler(index)}>
|
||||
{symbol || (isFocused ? <Cursor /> : null)}
|
||||
</Text>
|
||||
@@ -61,7 +69,7 @@ export default function Index() {
|
||||
// onPress={() => { router.push("/home") }}
|
||||
onPress={login}
|
||||
/>
|
||||
<Text style={[Styles.textInformation, Styles.mt05, Styles.cDefault, { textAlign: 'center' }]}>
|
||||
<Text style={[Styles.textInformation, Styles.mt05, Styles.cDefault, { textAlign: 'center', color: colors.tint }]}>
|
||||
Tidak Menerima kode verifikasi? Kirim Ulang
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
BIN
assets/images/bgproject-dark.png
Normal file
BIN
assets/images/bgproject-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/images/bgproject-light.png
Normal file
BIN
assets/images/bgproject-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/images/cogniti-dark.png
Normal file
BIN
assets/images/cogniti-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/images/cogniti-light.png
Normal file
BIN
assets/images/cogniti-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
BIN
assets/images/logo-dark.png
Normal file
BIN
assets/images/logo-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
39
components/AppHeader.tsx
Normal file
39
components/AppHeader.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import Styles from '@/constants/Styles';
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { useRouter } from 'expo-router';
|
||||
import { Platform, Text, View } from 'react-native';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import ButtonBackHeader from './buttonBackHeader';
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
right?: React.ReactNode;
|
||||
showBack?: boolean;
|
||||
onPressLeft?: () => void
|
||||
left?: React.ReactNode
|
||||
};
|
||||
|
||||
export default function AppHeader({ title, right, showBack = true, onPressLeft, left }: Props) {
|
||||
const insets = useSafeAreaInsets();
|
||||
const router = useRouter();
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<View style={[Styles.headerContainer, Platform.OS === 'ios' ? Styles.pb05 : Styles.pb13, { backgroundColor: colors.header, paddingTop: Platform.OS === 'ios' ? insets.top : 10 }]}>
|
||||
<View style={Styles.headerApp}>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
{showBack ? (
|
||||
<ButtonBackHeader onPress={onPressLeft} />
|
||||
) :
|
||||
left ? left :
|
||||
(
|
||||
<View style={Styles.headerSide} />
|
||||
)}
|
||||
|
||||
<Text style={[Styles.headerTitle, Styles.ml05]}>{title}</Text>
|
||||
</View>
|
||||
<View style={Styles.headerSide}>{right}</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
82
components/ModalConfirmation.tsx
Normal file
82
components/ModalConfirmation.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import Styles from '@/constants/Styles';
|
||||
import { useTheme } from '@/providers/ThemeProvider';
|
||||
import React from 'react';
|
||||
import { Modal, TouchableOpacity, View } from 'react-native';
|
||||
import Text from './Text';
|
||||
|
||||
interface ModalConfirmationProps {
|
||||
visible: boolean;
|
||||
title: string;
|
||||
message: string;
|
||||
onConfirm: () => void;
|
||||
onCancel: () => void;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
isDestructive?: boolean;
|
||||
}
|
||||
|
||||
const ModalConfirmation: React.FC<ModalConfirmationProps> = ({
|
||||
visible,
|
||||
title,
|
||||
message,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
confirmText = 'Ya',
|
||||
cancelText = 'Batal',
|
||||
isDestructive = false,
|
||||
}) => {
|
||||
const { colors } = useTheme();
|
||||
|
||||
return (
|
||||
<Modal
|
||||
transparent
|
||||
visible={visible}
|
||||
animationType="fade"
|
||||
onRequestClose={onCancel}
|
||||
>
|
||||
<View style={Styles.modalOverlay}>
|
||||
<View style={[Styles.modalConfirmContainer, { backgroundColor: colors.modalBackground }]}>
|
||||
<View style={Styles.modalConfirmContent}>
|
||||
<Text style={Styles.modalConfirmTitle}>{title}</Text>
|
||||
<Text style={[Styles.modalConfirmMessage, { color: colors.text }]}>
|
||||
{message}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={[Styles.modalConfirmDivider, { backgroundColor: colors.icon + '20' }]} />
|
||||
|
||||
<View style={Styles.modalConfirmFooter}>
|
||||
<TouchableOpacity
|
||||
style={Styles.modalConfirmButton}
|
||||
onPress={onCancel}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={[Styles.modalConfirmButtonText, { color: colors.text }]}>
|
||||
{cancelText}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={[Styles.modalConfirmVerticalDivider, { backgroundColor: colors.icon + '20' }]} />
|
||||
|
||||
<TouchableOpacity
|
||||
style={Styles.modalConfirmButton}
|
||||
onPress={onConfirm}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
Styles.modalConfirmButtonText,
|
||||
{ color: isDestructive ? colors.error : colors.text }
|
||||
]}
|
||||
>
|
||||
{confirmText}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalConfirmation;
|
||||
@@ -1,23 +1,19 @@
|
||||
import { useTheme } from '@/providers/ThemeProvider';
|
||||
import React from 'react';
|
||||
import { Text as RNText, TextProps as RNTextProps, StyleSheet } from 'react-native';
|
||||
import { StyleSheet, Text as RNText, TextProps as RNTextProps } from 'react-native';
|
||||
|
||||
type TextProps = RNTextProps & {
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const Text: React.FC<TextProps> = ({ style, ...props }) => {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<RNText
|
||||
style={[styles.defaultText, style]}
|
||||
<RNText
|
||||
style={[{ color: colors.text }, style]}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
defaultText: {
|
||||
color: 'black',
|
||||
},
|
||||
});
|
||||
|
||||
export default Text;
|
||||
|
||||
@@ -2,13 +2,14 @@ import Styles from "@/constants/Styles"
|
||||
import { setUpdateAnnouncement } from "@/lib/announcementUpdate"
|
||||
import { apiDeleteAnnouncement } from "@/lib/api"
|
||||
import { useAuthSession } from "@/providers/AuthProvider"
|
||||
import { useTheme } from "@/providers/ThemeProvider"
|
||||
import { Ionicons, MaterialCommunityIcons } from "@expo/vector-icons"
|
||||
import { router } from "expo-router"
|
||||
import { useState } from "react"
|
||||
import { View } from "react-native"
|
||||
import Toast from "react-native-toast-message"
|
||||
import { useDispatch, useSelector } from "react-redux"
|
||||
import AlertKonfirmasi from "../alertKonfirmasi"
|
||||
import ModalConfirmation from "../ModalConfirmation"
|
||||
import ButtonMenuHeader from "../buttonMenuHeader"
|
||||
import DrawerBottom from "../drawerBottom"
|
||||
import MenuItemRow from "../menuItemRow"
|
||||
@@ -18,8 +19,10 @@ type Props = {
|
||||
}
|
||||
export default function HeaderRightAnnouncementDetail({ id }: Props) {
|
||||
const { token, decryptToken } = useAuthSession()
|
||||
const { colors } = useTheme();
|
||||
const [isVisible, setVisible] = useState(false)
|
||||
const update = useSelector((state: any) => state.announcementUpdate)
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
const dispatch = useDispatch()
|
||||
|
||||
|
||||
@@ -46,7 +49,7 @@ export default function HeaderRightAnnouncementDetail({ id }: Props) {
|
||||
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={setVisible} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color="black" size={25} />}
|
||||
icon={<MaterialCommunityIcons name="pencil-outline" color={colors.text} size={25} />}
|
||||
title="Edit"
|
||||
onPress={() => {
|
||||
setVisible(false)
|
||||
@@ -54,21 +57,30 @@ export default function HeaderRightAnnouncementDetail({ id }: Props) {
|
||||
}}
|
||||
/>
|
||||
<MenuItemRow
|
||||
icon={<Ionicons name="trash" color="black" size={25} />}
|
||||
icon={<Ionicons name="trash-outline" color={colors.text} size={25} />}
|
||||
title="Hapus"
|
||||
onPress={() => {
|
||||
setVisible(false)
|
||||
AlertKonfirmasi({
|
||||
title: 'Konfirmasi',
|
||||
desc: 'Apakah anda yakin ingin menghapus pengumuman ini?',
|
||||
onPress: () => {
|
||||
handleDelete()
|
||||
}
|
||||
})
|
||||
setTimeout(() => {
|
||||
setShowDeleteModal(true)
|
||||
}, 600)
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</DrawerBottom>
|
||||
|
||||
<ModalConfirmation
|
||||
visible={showDeleteModal}
|
||||
title="Konfirmasi"
|
||||
message="Apakah anda yakin ingin menghapus pengumuman ini?"
|
||||
onConfirm={() => {
|
||||
setShowDeleteModal(false)
|
||||
handleDelete()
|
||||
}}
|
||||
onCancel={() => setShowDeleteModal(false)}
|
||||
confirmText="Hapus"
|
||||
cancelText="Batal"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import Styles from "@/constants/Styles"
|
||||
import { useTheme } from "@/providers/ThemeProvider"
|
||||
import { AntDesign } from "@expo/vector-icons"
|
||||
import { router } from "expo-router"
|
||||
import { useState } from "react"
|
||||
@@ -9,6 +10,7 @@ import DrawerBottom from "../drawerBottom"
|
||||
import MenuItemRow from "../menuItemRow"
|
||||
|
||||
export default function HeaderRightAnnouncementList() {
|
||||
const { colors } = useTheme();
|
||||
const [isVisible, setVisible] = useState(false)
|
||||
const entityUser = useSelector((state: any) => state.user)
|
||||
|
||||
@@ -21,7 +23,7 @@ export default function HeaderRightAnnouncementList() {
|
||||
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={setVisible} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<AntDesign name="pluscircle" color="black" size={25} />}
|
||||
icon={<AntDesign name="pluscircleo" color={colors.text} size={25} />}
|
||||
title="Tambah Pengumuman"
|
||||
onPress={() => {
|
||||
setVisible(false)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import Styles from "@/constants/Styles"
|
||||
import { apiCheckPhoneLogin, apiSendOtp } from "@/lib/api"
|
||||
import { useAuthSession } from "@/providers/AuthProvider"
|
||||
import { useTheme } from "@/providers/ThemeProvider"
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage"
|
||||
import { StatusBar } from "expo-status-bar"
|
||||
import { useState } from "react"
|
||||
@@ -21,6 +22,7 @@ export default function ViewLogin({ onValidate }: Props) {
|
||||
const [disableLogin, setDisableLogin] = useState(true)
|
||||
const [phone, setPhone] = useState('')
|
||||
const { signIn, encryptToken } = useAuthSession();
|
||||
const { colors, theme } = useTheme();
|
||||
|
||||
const handleCheckPhone = async () => {
|
||||
try {
|
||||
@@ -49,13 +51,12 @@ export default function ViewLogin({ onValidate }: Props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<StatusBar style={Platform.OS === 'ios' ? 'dark' : 'light'} translucent={false} backgroundColor="black" />
|
||||
<ToastCustom />
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<StatusBar style={theme === 'dark' ? 'light' : 'dark'} translucent={false} backgroundColor={colors.background} />
|
||||
<View style={[Styles.p20, Styles.h100]}>
|
||||
<View style={{ alignItems: "center", marginTop: 70, marginBottom: 50 }}>
|
||||
<Image
|
||||
source={require("../../assets/images/logo.png")}
|
||||
source={theme === 'dark' ? require("../../assets/images/logo-dark.png") : require("../../assets/images/logo.png")}
|
||||
style={[{ width: 300, height: 150 }]}
|
||||
width={270}
|
||||
height={110}
|
||||
@@ -69,18 +70,28 @@ export default function ViewLogin({ onValidate }: Props) {
|
||||
}}
|
||||
type="numeric"
|
||||
placeholder="XXX-XXX-XXXX"
|
||||
round
|
||||
itemLeft={<Text style={[Platform.OS === 'ios' && Styles.mt02]}>+62</Text>}
|
||||
info="Kami akan mengirim kode verifikasi melalui WhatsApp, guna mengonfirmasikan nomor Anda." />
|
||||
<ButtonForm
|
||||
text="MASUK"
|
||||
onPress={() => { handleCheckPhone() }}
|
||||
disabled={disableLogin}
|
||||
/>
|
||||
<View style={[{ width: '50%', alignSelf: 'center' }]}>
|
||||
<ButtonForm
|
||||
text="MASUK"
|
||||
onPress={() => { handleCheckPhone() }}
|
||||
disabled={disableLogin}
|
||||
/>
|
||||
</View>
|
||||
<View style={{ flex: 1 }} />
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<Image
|
||||
source={theme === 'dark' ? require("../../assets/images/cogniti-dark.png") : require("../../assets/images/cogniti-light.png")}
|
||||
style={{ width: 86, height: 27 }}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
{
|
||||
loadingLogin && <ModalLoading isVisible={true} setVisible={setLoadingLogin} />
|
||||
}
|
||||
<ToastCustom position="bottom" />
|
||||
</SafeAreaView>
|
||||
|
||||
)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import Styles from "@/constants/Styles";
|
||||
import { apiSendOtp } from "@/lib/api";
|
||||
import { useAuthSession } from "@/providers/AuthProvider";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import { useState } from "react";
|
||||
import { Image, Platform, View } from "react-native";
|
||||
import { Image, SafeAreaView, View } from "react-native";
|
||||
import { OtpInput } from "react-native-otp-entry";
|
||||
import Toast from 'react-native-toast-message';
|
||||
import { ButtonForm } from "../buttonForm";
|
||||
@@ -20,6 +21,7 @@ export default function ViewVerification({ phone, otp }: Props) {
|
||||
const [value, setValue] = useState('');
|
||||
const [otpFix, setOtpFix] = useState(otp)
|
||||
const { signIn, encryptToken } = useAuthSession();
|
||||
const { colors, theme } = useTheme();
|
||||
|
||||
const login = async () => {
|
||||
const valueUser = await AsyncStorage.getItem('user');
|
||||
@@ -56,13 +58,12 @@ export default function ViewVerification({ phone, otp }: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<StatusBar style={Platform.OS === 'ios' ? 'dark' : 'light'} translucent={false} backgroundColor="black" />
|
||||
<ToastCustom />
|
||||
<View style={Styles.wrapLogin} >
|
||||
<SafeAreaView style={{ flex: 1, backgroundColor: colors.background }}>
|
||||
<StatusBar style={theme === 'dark' ? 'light' : 'dark'} translucent={false} backgroundColor={colors.background} />
|
||||
<View style={[Styles.p20, Styles.h100]} >
|
||||
<View style={{ alignItems: "center", marginTop: 70, marginBottom: 50 }}>
|
||||
<Image
|
||||
source={require("../../assets/images/logo.png")}
|
||||
source={theme === 'dark' ? require("../../assets/images/logo-dark.png") : require("../../assets/images/logo.png")}
|
||||
style={[{ width: 300, height: 150 }]}
|
||||
width={270}
|
||||
height={110}
|
||||
@@ -77,25 +78,36 @@ export default function ViewVerification({ phone, otp }: Props) {
|
||||
<OtpInput
|
||||
numberOfDigits={4}
|
||||
onTextChange={(text) => setValue(text)}
|
||||
focusColor={'#19345E'}
|
||||
focusColor={colors.tint}
|
||||
type="numeric"
|
||||
theme={{
|
||||
containerStyle: {
|
||||
width: '80%',
|
||||
alignSelf: 'center'
|
||||
},
|
||||
pinCodeContainerStyle: Styles.verificationCell,
|
||||
pinCodeTextStyle: { color: 'black' }
|
||||
pinCodeContainerStyle: { ...Styles.verificationCell, borderColor: colors.icon },
|
||||
pinCodeTextStyle: { color: colors.text }
|
||||
}}
|
||||
/>
|
||||
<ButtonForm
|
||||
text="SUBMIT"
|
||||
onPress={() => { onCheckOtp() }}
|
||||
/>
|
||||
<Text style={[Styles.textInformation, Styles.mt05, Styles.cDefault, { textAlign: 'center' }]}>
|
||||
Tidak Menerima kode verifikasi? <Text onPress={() => { resendOtp() }}>Kirim Ulang</Text>
|
||||
<View style={[{ width: '50%', alignSelf: 'center' }]}>
|
||||
<ButtonForm
|
||||
text="SUBMIT"
|
||||
onPress={() => { onCheckOtp() }}
|
||||
/>
|
||||
</View>
|
||||
<Text style={[Styles.textInformation, Styles.mt05, { textAlign: 'center', color: colors.dimmed }]}>
|
||||
Tidak Menerima kode verifikasi? <Text onPress={() => { resendOtp() }} style={[{ color: colors.tint }]}>Kirim Ulang</Text>
|
||||
</Text>
|
||||
<View style={{ flex: 1 }} />
|
||||
<View style={[{ alignItems: 'center' }]}>
|
||||
<Image
|
||||
source={theme === 'dark' ? require("../../assets/images/cogniti-dark.png") : require("../../assets/images/cogniti-light.png")}
|
||||
style={{ width: 86, height: 27 }}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
<ToastCustom position="bottom" />
|
||||
</SafeAreaView>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +1,23 @@
|
||||
import { useState } from "react";
|
||||
import ButtonMenuHeader from "../buttonMenuHeader";
|
||||
import DrawerBottom from "../drawerBottom";
|
||||
import { View } from "react-native";
|
||||
import Styles from "@/constants/Styles";
|
||||
import MenuItemRow from "../menuItemRow";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { AntDesign } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import { useState } from "react";
|
||||
import { View } from "react-native";
|
||||
import ButtonMenuHeader from "../buttonMenuHeader";
|
||||
import DrawerBottom from "../drawerBottom";
|
||||
import MenuItemRow from "../menuItemRow";
|
||||
|
||||
export default function HeaderRightBannerList() {
|
||||
const [isVisible, setVisible] = useState(false)
|
||||
const { colors } = useTheme()
|
||||
return (
|
||||
<>
|
||||
<ButtonMenuHeader onPress={() => { setVisible(true) }} />
|
||||
<DrawerBottom animation="slide" isVisible={isVisible} setVisible={() => setVisible(false)} title="Menu">
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
<MenuItemRow
|
||||
icon={<AntDesign name="pluscircle" color="black" size={25} />}
|
||||
icon={<AntDesign name="pluscircleo" color={colors.text} size={25} />}
|
||||
title="Tambah Banner"
|
||||
onPress={() => {
|
||||
setVisible(false)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import React, { useState } from "react";
|
||||
import { Dimensions, Pressable, View } from "react-native";
|
||||
import Text from "./Text";
|
||||
@@ -16,18 +17,16 @@ type Props = {
|
||||
leftBottomInfo?: React.ReactNode | string
|
||||
rightBottomInfo?: React.ReactNode | string
|
||||
titleWeight?: 'normal' | 'bold'
|
||||
bgColor?: 'white' | 'transparent'
|
||||
width?: number
|
||||
bgColor?: string
|
||||
descEllipsize?: boolean
|
||||
textColor?: string,
|
||||
colorPress?: boolean
|
||||
titleShowAll?: boolean
|
||||
}
|
||||
|
||||
export default function BorderBottomItem({ title, subtitle, icon, desc, onPress, onLongPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, width, descEllipsize, textColor, colorPress, titleShowAll }: Props) {
|
||||
const lebarDim = Dimensions.get("window").width;
|
||||
const lebar = width ? lebarDim * width / 100 : 'auto';
|
||||
const textColorFix = textColor ? textColor : 'black';
|
||||
export default function BorderBottomItem({ title, subtitle, icon, desc, onPress, onLongPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, descEllipsize, textColor, colorPress, titleShowAll }: Props) {
|
||||
const { colors } = useTheme();
|
||||
const textColorFix = textColor ? textColor : colors.text;
|
||||
const [isTap, setIsTap] = useState(false);
|
||||
|
||||
|
||||
@@ -37,31 +36,31 @@ export default function BorderBottomItem({ title, subtitle, icon, desc, onPress,
|
||||
onPressOut={() => setIsTap(false)}
|
||||
style={({ pressed }) => [
|
||||
borderType == 'bottom'
|
||||
? Styles.wrapItemBorderBottom
|
||||
? [Styles.wrapItemBorderBottom, { borderBottomColor: colors.icon + '20' }]
|
||||
: borderType == 'all'
|
||||
? Styles.wrapItemBorderAll
|
||||
? [Styles.wrapItemBorderAll, { borderColor: colors.icon + '20' }]
|
||||
: Styles.wrapItemBorderNone,
|
||||
bgColor && bgColor == 'white' && ColorsStatus.white,
|
||||
bgColor == "transparent" ? { backgroundColor: 'transparent' } : { backgroundColor: colors.card },
|
||||
// efek warna saat ditekan (sementara)
|
||||
isTap && colorPress && ColorsStatus.pressedGray,
|
||||
isTap && colorPress && { backgroundColor: colors.icon + '20' },
|
||||
]}
|
||||
>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
{icon}
|
||||
<View style={[Styles.rowSpaceBetween, width ? { width: lebar } : { width: '88%' }]}>
|
||||
<View style={[Styles.ml10, rightTopInfo ? { width: '70%' } : { width: '90%' }]}>
|
||||
<View style={[Styles.rowSpaceBetween, Styles.flex1]}>
|
||||
<View style={[Styles.ml10, Styles.flex1, Styles.mr10]}>
|
||||
<Text style={[titleWeight == 'normal' ? Styles.textDefault : Styles.textDefaultSemiBold, { color: textColorFix }]} numberOfLines={titleShowAll ? 0 : 1} ellipsizeMode='tail'>{title}</Text>
|
||||
{
|
||||
subtitle &&
|
||||
typeof subtitle == "string"
|
||||
? <Text style={[Styles.textMediumNormal, { lineHeight: 15, color: textColorFix }]}>{subtitle}</Text>
|
||||
? <Text style={[Styles.textMediumNormal, { lineHeight: 15, color: colors.dimmed }]}>{subtitle}</Text>
|
||||
: <View style={{ alignItems: 'flex-start' }}>
|
||||
{subtitle}
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
{
|
||||
rightTopInfo && <Text style={[Styles.textInformation, Styles.mt05, { color: textColorFix }]}>{rightTopInfo}</Text>
|
||||
rightTopInfo && <Text style={[Styles.textInformation, Styles.mt05, { color: colors.dimmed }]}>{rightTopInfo}</Text>
|
||||
}
|
||||
</View>
|
||||
|
||||
@@ -73,13 +72,13 @@ export default function BorderBottomItem({ title, subtitle, icon, desc, onPress,
|
||||
<View style={[rightBottomInfo && !leftBottomInfo ? Styles.rowSpaceBetweenReverse : Styles.rowSpaceBetween, Styles.mt05]}>
|
||||
{
|
||||
typeof leftBottomInfo == 'string' ?
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>{leftBottomInfo}</Text>
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }]}>{leftBottomInfo}</Text>
|
||||
:
|
||||
leftBottomInfo
|
||||
}
|
||||
{
|
||||
typeof rightBottomInfo == 'string' ?
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>{rightBottomInfo}</Text>
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }]}>{rightBottomInfo}</Text>
|
||||
:
|
||||
rightBottomInfo
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import { ConstEnv } from "@/constants/ConstEnv";
|
||||
import { isImageFile } from "@/constants/FileExtensions";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import { startActivityAsync } from 'expo-intent-launcher';
|
||||
import * as Sharing from 'expo-sharing';
|
||||
import React, { useState } from "react";
|
||||
import { Alert, Dimensions, Platform, Pressable, View } from "react-native";
|
||||
import { Dimensions, Platform, Pressable, View } from "react-native";
|
||||
import { ScrollView } from "react-native-gesture-handler";
|
||||
import ImageViewing from "react-native-image-viewing";
|
||||
import * as mime from 'react-native-mime-types';
|
||||
import Toast from "react-native-toast-message";
|
||||
import Text from "./Text";
|
||||
|
||||
|
||||
@@ -33,26 +37,48 @@ type Props = {
|
||||
dataFile: { id: string; idStorage: string; name: string; extension: string }[]
|
||||
}
|
||||
|
||||
type PropsFile = {
|
||||
id: string;
|
||||
idStorage: string;
|
||||
name: string;
|
||||
extension: string
|
||||
}
|
||||
|
||||
|
||||
export default function BorderBottomItem2({ title, subtitle, icon, desc, onPress, onLongPress, rightTopInfo, borderType, leftBottomInfo, rightBottomInfo, titleWeight, bgColor, width, descEllipsize, textColor, colorPress, titleShowAll, dataFile }: Props) {
|
||||
const { colors } = useTheme();
|
||||
const lebarDim = Dimensions.get("window").width;
|
||||
const lebar = width ? lebarDim * width / 100 : 'auto';
|
||||
const textColorFix = textColor ? textColor : 'black';
|
||||
const textColorFix = textColor ? textColor : colors.text;
|
||||
const [isTap, setIsTap] = useState(false);
|
||||
const [loadingOpen, setLoadingOpen] = useState(false)
|
||||
const [chooseFile, setChooseFile] = useState<PropsFile>()
|
||||
const [preview, setPreview] = useState(false)
|
||||
|
||||
function handleChooseFile(item: PropsFile) {
|
||||
setChooseFile(item)
|
||||
setPreview(true)
|
||||
}
|
||||
|
||||
const openFile = (item: { idStorage: string; name: string; extension: string }) => {
|
||||
if (Platform.OS == 'android') setLoadingOpen(true)
|
||||
let remoteUrl = ConstEnv.url_storage + '/files/' + item.idStorage;
|
||||
const fileName = item.name + '.' + item.extension;
|
||||
let localPath = `${FileSystem.documentDirectory}/${fileName}`;
|
||||
const mimeType = mime.lookup(fileName)
|
||||
const openFile = async (item: PropsFile) => {
|
||||
try {
|
||||
setLoadingOpen(true);
|
||||
const remoteUrl = ConstEnv.url_storage + '/files/' + item.idStorage;
|
||||
const fileName = item.name + '.' + item.extension;
|
||||
const localPath = `${FileSystem.documentDirectory}/${fileName}`;
|
||||
const mimeType = mime.lookup(fileName);
|
||||
|
||||
// Download the file
|
||||
const downloadResult = await FileSystem.downloadAsync(remoteUrl, localPath);
|
||||
|
||||
if (downloadResult.status !== 200) {
|
||||
throw new Error(`Download failed with status ${downloadResult.status}`);
|
||||
}
|
||||
|
||||
const contentURL = await FileSystem.getContentUriAsync(downloadResult.uri);
|
||||
|
||||
FileSystem.downloadAsync(remoteUrl, localPath).then(async ({ uri }) => {
|
||||
const contentURL = await FileSystem.getContentUriAsync(uri);
|
||||
setLoadingOpen(false)
|
||||
try {
|
||||
if (Platform.OS == 'android') {
|
||||
if (Platform.OS === 'android') {
|
||||
await startActivityAsync(
|
||||
'android.intent.action.VIEW',
|
||||
{
|
||||
@@ -61,89 +87,148 @@ export default function BorderBottomItem2({ title, subtitle, icon, desc, onPress
|
||||
type: mimeType as string,
|
||||
}
|
||||
);
|
||||
} else if (Platform.OS == 'ios') {
|
||||
Sharing.shareAsync(localPath);
|
||||
} else if (Platform.OS === 'ios') {
|
||||
await Sharing.shareAsync(localPath);
|
||||
}
|
||||
} catch (error) {
|
||||
Alert.alert('INFO', 'Gagal membuka file, tidak ada aplikasi yang dapat membuka file ini');
|
||||
} finally {
|
||||
if (Platform.OS == 'android') setLoadingOpen(false)
|
||||
} catch (openError) {
|
||||
console.error('Error opening file:', openError);
|
||||
Toast.show({
|
||||
type: 'error',
|
||||
text1: 'Tidak ada aplikasi yang dapat membuka file ini'
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error downloading or opening file:', error);
|
||||
Toast.show({
|
||||
type: 'error',
|
||||
text1: 'Gagal membuka file',
|
||||
text2: 'Silakan coba lagi nanti'
|
||||
});
|
||||
} finally {
|
||||
setLoadingOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Pressable onLongPress={onLongPress} onPress={onPress}
|
||||
onPressIn={() => setIsTap(true)}
|
||||
onPressOut={() => setIsTap(false)}
|
||||
style={({ pressed }) => [
|
||||
borderType == 'bottom'
|
||||
? Styles.wrapItemBorderBottom
|
||||
: borderType == 'all'
|
||||
? Styles.wrapItemBorderAll
|
||||
: Styles.wrapItemBorderNone,
|
||||
bgColor && bgColor == 'white' && ColorsStatus.white,
|
||||
// efek warna saat ditekan (sementara)
|
||||
isTap && colorPress && ColorsStatus.pressedGray,
|
||||
]}
|
||||
>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
{icon}
|
||||
<View style={[Styles.rowSpaceBetween, width ? { width: lebar } : { width: '88%' }]}>
|
||||
<View style={[Styles.ml10, rightTopInfo ? { width: '70%' } : { width: '90%' }]}>
|
||||
<Text style={[titleWeight == 'normal' ? Styles.textDefault : Styles.textDefaultSemiBold, { color: textColorFix }]} numberOfLines={titleShowAll ? 0 : 1} ellipsizeMode='tail'>{title}</Text>
|
||||
<>
|
||||
<Pressable onLongPress={onLongPress} onPress={onPress}
|
||||
onPressIn={() => setIsTap(true)}
|
||||
onPressOut={() => setIsTap(false)}
|
||||
style={({ pressed }) => [
|
||||
borderType == 'bottom'
|
||||
? [Styles.wrapItemBorderBottom, { borderBottomColor: colors.icon + '20' }]
|
||||
: borderType == 'all'
|
||||
? [Styles.wrapItemBorderAll, { borderColor: colors.icon + '20' }]
|
||||
: Styles.wrapItemBorderNone,
|
||||
bgColor && bgColor == 'white' && { backgroundColor: colors.card },
|
||||
// efek warna saat ditekan (sementara)
|
||||
isTap && colorPress && ColorsStatus.pressedGray,
|
||||
]}
|
||||
>
|
||||
<View style={[Styles.rowItemsCenter]}>
|
||||
{icon}
|
||||
<View style={[Styles.rowSpaceBetween, width ? { width: lebar } : { width: '88%' }]}>
|
||||
<View style={[Styles.ml10, rightTopInfo ? { width: '70%' } : { width: '90%' }]}>
|
||||
<Text style={[titleWeight == 'normal' ? Styles.textDefault : Styles.textDefaultSemiBold, { color: textColorFix }]} numberOfLines={titleShowAll ? 0 : 1} ellipsizeMode='tail'>{title}</Text>
|
||||
{
|
||||
subtitle &&
|
||||
typeof subtitle == "string"
|
||||
? <Text style={[Styles.textMediumNormal, { lineHeight: 15, color: textColorFix }]}>{subtitle}</Text>
|
||||
: <View style={{ alignItems: 'flex-start' }}>
|
||||
{subtitle}
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
{
|
||||
subtitle &&
|
||||
typeof subtitle == "string"
|
||||
? <Text style={[Styles.textMediumNormal, { lineHeight: 15, color: textColorFix }]}>{subtitle}</Text>
|
||||
: <View style={{ alignItems: 'flex-start' }}>
|
||||
{subtitle}
|
||||
</View>
|
||||
rightTopInfo && <Text style={[Styles.textInformation, Styles.mt05, { color: textColorFix }]}>{rightTopInfo}</Text>
|
||||
}
|
||||
</View>
|
||||
{
|
||||
rightTopInfo && <Text style={[Styles.textInformation, Styles.mt05, { color: textColorFix }]}>{rightTopInfo}</Text>
|
||||
}
|
||||
</View>
|
||||
|
||||
</View>
|
||||
{desc && <Text style={[Styles.textDefault, Styles.mt05, { textAlign: 'left', color: textColorFix }]} numberOfLines={descEllipsize == false ? 0 : 2} ellipsizeMode='tail'>{desc}</Text>}
|
||||
{
|
||||
dataFile.length > 0 && (
|
||||
<ScrollView horizontal style={[Styles.mv05]} showsHorizontalScrollIndicator={false}>
|
||||
{dataFile.map((item, index) => (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[Styles.rowItemsCenter, Styles.borderAll, Styles.round10, Styles.ph05, Styles.pv03, Styles.mr05]}
|
||||
onPress={() => { openFile({ idStorage: item.idStorage, name: item.name, extension: item.extension }) }}
|
||||
>
|
||||
<Ionicons name="document-text" size={18} color="grey" style={Styles.mr05} />
|
||||
<Text style={[Styles.textInformation, Styles.cGray, Styles.mb05]}>{item.name}.{item.extension}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
{
|
||||
(leftBottomInfo || rightBottomInfo) &&
|
||||
(
|
||||
<View style={[rightBottomInfo && !leftBottomInfo ? Styles.rowSpaceBetweenReverse : Styles.rowSpaceBetween, Styles.mt05]}>
|
||||
{
|
||||
typeof leftBottomInfo == 'string' ?
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>{leftBottomInfo}</Text>
|
||||
:
|
||||
leftBottomInfo
|
||||
}
|
||||
{
|
||||
typeof rightBottomInfo == 'string' ?
|
||||
<Text style={[Styles.textInformation, Styles.cGray]}>{rightBottomInfo}</Text>
|
||||
:
|
||||
rightBottomInfo
|
||||
}
|
||||
</View>
|
||||
{desc && <Text style={[Styles.textDefault, Styles.mt05, { textAlign: 'left', color: textColorFix }]} numberOfLines={descEllipsize == false ? 0 : 2} ellipsizeMode='tail'>{desc}</Text>}
|
||||
{
|
||||
dataFile.length > 0 && (
|
||||
<ScrollView horizontal style={[Styles.mv05]} showsHorizontalScrollIndicator={false}>
|
||||
{dataFile.map((item, index) => (
|
||||
<Pressable
|
||||
key={index}
|
||||
style={[Styles.rowItemsCenter, Styles.borderAll, Styles.round05, Styles.ph05, Styles.pv03, Styles.mr05, { borderColor: colors.icon }]}
|
||||
onPress={() => {
|
||||
isImageFile(item.extension) ?
|
||||
handleChooseFile(item)
|
||||
: openFile(item)
|
||||
}}
|
||||
>
|
||||
<MaterialCommunityIcons
|
||||
name={isImageFile(item.extension) ? "file-image-outline" : "file-document-outline"}
|
||||
size={18}
|
||||
color={colors.icon} />
|
||||
<Text style={[Styles.textInformation]}>{item.name}.{item.extension}</Text>
|
||||
</Pressable>
|
||||
))}
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
{
|
||||
(leftBottomInfo || rightBottomInfo) &&
|
||||
(
|
||||
<View style={[rightBottomInfo && !leftBottomInfo ? Styles.rowSpaceBetweenReverse : Styles.rowSpaceBetween, Styles.mt05]}>
|
||||
{
|
||||
typeof leftBottomInfo == 'string' ?
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }]}>{leftBottomInfo}</Text>
|
||||
:
|
||||
leftBottomInfo
|
||||
}
|
||||
{
|
||||
typeof rightBottomInfo == 'string' ?
|
||||
<Text style={[Styles.textInformation, { color: colors.dimmed }]}>{rightBottomInfo}</Text>
|
||||
:
|
||||
rightBottomInfo
|
||||
}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
</Pressable>
|
||||
|
||||
<ImageViewing
|
||||
images={[{ uri: `${ConstEnv.url_storage}/files/${chooseFile?.idStorage}` }]}
|
||||
imageIndex={0}
|
||||
visible={preview}
|
||||
onRequestClose={() => setPreview(false)}
|
||||
doubleTapToZoomEnabled
|
||||
HeaderComponent={({ imageIndex }) => (
|
||||
<View style={[Styles.headerModalViewImg]}>
|
||||
{/* CLOSE */}
|
||||
<Pressable
|
||||
onPress={() => setPreview(false)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Close image viewer"
|
||||
>
|
||||
<Text style={{ color: 'white', fontSize: 26 }}>✕</Text>
|
||||
</Pressable>
|
||||
|
||||
{/* MENU */}
|
||||
<Pressable
|
||||
onPress={() => chooseFile && openFile(chooseFile)}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Download or share image"
|
||||
disabled={loadingOpen}
|
||||
>
|
||||
<Text style={{ color: loadingOpen ? 'gray' : 'white', fontSize: 26 }}>⋯</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
</Pressable>
|
||||
)}
|
||||
FooterComponent={({ imageIndex }) => (
|
||||
<View style={{
|
||||
paddingBottom: 20,
|
||||
paddingHorizontal: 16,
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Text style={{ color: 'white', fontSize: 16 }}>{chooseFile?.name}.{chooseFile?.extension}</Text>
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
53
components/borderBottomItemVertical.tsx
Normal file
53
components/borderBottomItemVertical.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import Styles from "@/constants/Styles";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import React, { useState } from "react";
|
||||
import { Pressable, View } from "react-native";
|
||||
import Text from "./Text";
|
||||
|
||||
type Props = {
|
||||
title?: string
|
||||
icon: React.ReactNode
|
||||
desc?: string
|
||||
rightTopInfo?: string
|
||||
onPress?: () => void
|
||||
onLongPress?: () => void
|
||||
borderType: 'all' | 'bottom' | 'none'
|
||||
titleWeight?: 'normal' | 'bold'
|
||||
bgColor?: string
|
||||
descEllipsize?: boolean
|
||||
textColor?: string,
|
||||
titleShowAll?: boolean
|
||||
}
|
||||
|
||||
export default function BorderBottomItemVertical({ title, icon, desc, onPress, onLongPress, rightTopInfo, borderType, titleWeight, bgColor, descEllipsize, textColor, titleShowAll }: Props) {
|
||||
const { colors } = useTheme();
|
||||
const textColorFix = textColor ? textColor : colors.text;
|
||||
|
||||
return (
|
||||
<Pressable onLongPress={onLongPress} onPress={onPress}
|
||||
style={({ pressed }) => [
|
||||
borderType == 'bottom'
|
||||
? [Styles.wrapItemBorderBottom, { borderBottomColor: colors.icon + '20' }]
|
||||
: borderType == 'all'
|
||||
? [Styles.wrapItemBorderAll, { borderColor: colors.icon + '20' }]
|
||||
: Styles.wrapItemBorderNone,
|
||||
bgColor == "transparent" ? { backgroundColor: 'transparent' } : { backgroundColor: colors.card },
|
||||
]}
|
||||
>
|
||||
<View style={Styles.rowItemsCenter}>
|
||||
{icon}
|
||||
<View style={[Styles.ml10, Styles.flex1]}>
|
||||
<View style={Styles.rowSpaceBetween}>
|
||||
<View style={[Styles.flex1, Styles.mr10]}>
|
||||
<Text style={[titleWeight == 'normal' ? Styles.textDefault : Styles.textDefaultSemiBold, { color: textColorFix }]} numberOfLines={titleShowAll ? 0 : 1} ellipsizeMode='tail'>{title}</Text>
|
||||
</View>
|
||||
{
|
||||
rightTopInfo && <Text style={[Styles.textInformation, Styles.mt05, { color: textColorFix }]}>{rightTopInfo}</Text>
|
||||
}
|
||||
</View>
|
||||
{desc && <Text style={[Styles.textDefault, { textAlign: 'left', color: textColorFix }]} numberOfLines={descEllipsize == false ? 0 : 2} ellipsizeMode='tail'>{desc}</Text>}
|
||||
</View>
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
@@ -9,7 +9,7 @@ export default function ButtonBackHeader({ onPress }: Props) {
|
||||
return (
|
||||
<>
|
||||
<ButtonHeader
|
||||
item={<Feather name="chevron-left" size={20} color="white" />}
|
||||
item={<Feather name="chevron-left" size={25} color="white" />}
|
||||
onPress={() => { onPress && onPress() }}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Styles from "@/constants/Styles";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import React from "react";
|
||||
import { TouchableWithoutFeedback, View } from "react-native";
|
||||
import Text from "./Text";
|
||||
@@ -10,13 +11,14 @@ type Props = {
|
||||
};
|
||||
|
||||
export function ButtonFiturMenu({ onPress, icon, text }: Props) {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={onPress}>
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
<View style={[Styles.btnFiturMenu]}>
|
||||
<View style={[Styles.btnFiturMenu, { backgroundColor: colors.card, borderColor: colors.icon + '20', shadowColor: colors.text }]}>
|
||||
{icon}
|
||||
</View>
|
||||
<Text style={[Styles.mt05]}>{text}</Text>
|
||||
<Text style={[Styles.mt05, { color: colors.text }]}>{text}</Text>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ColorsStatus } from "@/constants/ColorsStatus";
|
||||
import Styles from "@/constants/Styles";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { TouchableOpacity } from "react-native";
|
||||
import Text from './Text';
|
||||
|
||||
@@ -10,8 +10,9 @@ type Props = {
|
||||
};
|
||||
|
||||
export function ButtonForm({ text, onPress, disabled }: Props) {
|
||||
const { colors } = useTheme();
|
||||
return (
|
||||
<TouchableOpacity style={[Styles.btnRound, { marginTop: 30,}, disabled && ColorsStatus.gray]} onPress={onPress} disabled={disabled}>
|
||||
<TouchableOpacity style={[Styles.btnRound, Styles.round05, Styles.mt30, { backgroundColor: colors.primary }, disabled && { backgroundColor: colors.tabIconDefault }]} onPress={onPress} disabled={disabled}>
|
||||
<Text style={[Styles.textDefaultSemiBold, Styles.cWhite]}>{text}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user