Compare commits
39 Commits
tabs/26-ma
...
staging
| Author | SHA1 | Date | |
|---|---|---|---|
| 17396cea68 | |||
| fa4c9005de | |||
| c86a3f0ffa | |||
| c2e6ce7b06 | |||
| b1a5be5105 | |||
| 9e902cec6e | |||
| 088c46de3e | |||
| 52d8e91eb2 | |||
| 064cf18cba | |||
| 435c5834e9 | |||
| 155cfae331 | |||
| 415243ec6d | |||
| 2f2b5a460f | |||
| b5b4e0816c | |||
| 32eee66a41 | |||
| 3ad8fba6d2 | |||
| a7022e5afd | |||
| 0cacb5f7de | |||
| 03f53d7b48 | |||
| c4859b7e82 | |||
| e4a66f3bc3 | |||
| 6d352dd091 | |||
| 0f62243410 | |||
| 4f32d522a4 | |||
| 1357dff2e3 | |||
| 0ba77e5160 | |||
| 186c667016 | |||
| 16f4bf0e62 | |||
| d66dab1a6f | |||
| e9b6c54c6e | |||
| f201f61ce1 | |||
| e407598c7b | |||
| c8e2119e99 | |||
| a287d5545f | |||
| 12034ebbfb | |||
| 359daf4e4b | |||
| 4f0cd3df02 | |||
| a614cfaac9 | |||
| 1a7ad58505 |
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git add *)"
|
||||
]
|
||||
}
|
||||
}
|
||||
2
QWEN.md
2
QWEN.md
@@ -387,7 +387,7 @@ apiConfig.interceptors.request.use(async (config) => {
|
||||
|
||||
### Deep Linking
|
||||
- Scheme: `hipmimobile://`
|
||||
- HTTPS: `cld-dkr-hipmi-stg.wibudev.com`
|
||||
- HTTPS: `cld-dkr-staging-hipmi.wibudev.com`
|
||||
- Configured for both platforms
|
||||
|
||||
### Camera
|
||||
|
||||
@@ -1,369 +0,0 @@
|
||||
# Fix Phone Number Input - iOS 16+ Compatibility
|
||||
|
||||
## 📋 Ringkasan Task
|
||||
Memperbaiki masalah phone number input pada `screens/Authentication/LoginView.tsx` yang tidak berfungsi dengan baik pada iOS versi 16 ke atas.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Tujuan
|
||||
1. Fix keyboard overlay issues pada iOS 16+
|
||||
2. Perbaiki layout measurement dan safe area
|
||||
3. Pastikan input phone number responsive di semua device
|
||||
4. Maintain UX yang konsisten dengan Android
|
||||
|
||||
---
|
||||
|
||||
## 📁 File yang Terlibat
|
||||
|
||||
### File Utama
|
||||
- **Target**: `screens/Authentication/LoginView.tsx`
|
||||
|
||||
### File Pendukung
|
||||
- `components/TextInput/TextInputCustom.tsx` - Alternatif custom input
|
||||
- `styles/global-styles.ts` - Styling adjustments
|
||||
- `package.json` - Update dependencies (jika perlu)
|
||||
|
||||
### Dependencies Terkait
|
||||
- `react-native-international-phone-number`: ^0.9.3
|
||||
- `react-native-keyboard-controller`: ^1.18.6
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Analisis Masalah
|
||||
|
||||
### Issues pada iOS 16+
|
||||
|
||||
#### 1. **Keyboard Overlay Problem**
|
||||
**Symptom**: Input field tertutup keyboard saat aktif
|
||||
**Cause**:
|
||||
- Safe area insets tidak terhitung dengan benar
|
||||
- Keyboard animation tidak sinkron dengan layout
|
||||
- `react-native-keyboard-controller` tidak terintegrasi dengan baik
|
||||
|
||||
#### 2. **Layout Measurement Issues**
|
||||
**Symptom**: Input field berubah ukuran secara tidak terduga
|
||||
**Cause**:
|
||||
- Dynamic Type settings mempengaruhi layout
|
||||
- Font scaling pada iOS 16+ berbeda
|
||||
- Container tidak memiliki fixed height
|
||||
|
||||
#### 3. **Focus Behavior**
|
||||
**Symptom**: Input tidak auto-scroll saat di-focus
|
||||
**Cause**:
|
||||
- ScrollView/KeyboardAvoidingView configuration salah
|
||||
- Keyboard dismissing behavior tidak konsisten
|
||||
|
||||
#### 4. **Visual Glitches**
|
||||
**Symptom**: Country flag dropdown tidak muncul atau terpotong
|
||||
**Cause**:
|
||||
- Z-index issues pada iOS
|
||||
- Modal/Popover rendering problems
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Solusi yang Direkomendasikan
|
||||
|
||||
### Option 1: **KeyboardAvoidingView Enhancement** (RECOMMENDED)
|
||||
**Effort**: Medium
|
||||
**Impact**: High
|
||||
|
||||
```typescript
|
||||
import { KeyboardAvoidingView, Platform } from "react-native";
|
||||
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
keyboardVerticalOffset={Platform.OS === "ios" ? 90 : 50}
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
<ViewWrapper>
|
||||
{/* Content */}
|
||||
</ViewWrapper>
|
||||
</KeyboardAvoidingView>
|
||||
```
|
||||
|
||||
**Keuntungan**:
|
||||
- Native solution dari React Native
|
||||
- Tidak perlu tambahan library
|
||||
- Stabil untuk iOS 16+
|
||||
|
||||
**Kekurangan**:
|
||||
- Perlu tuning offset untuk setiap device
|
||||
- Tidak se-smooth keyboard controller
|
||||
|
||||
### Option 2: **React Native Keyboard Controller** (BETTER UX)
|
||||
**Effort**: Medium-High
|
||||
**Impact**: High
|
||||
|
||||
```typescript
|
||||
import { KeyboardProvider } from "react-native-keyboard-controller";
|
||||
|
||||
// Wrap di root app (sudah ada di _layout.tsx)
|
||||
<KeyboardProvider>
|
||||
<App />
|
||||
</KeyboardProvider>
|
||||
|
||||
// Di LoginView
|
||||
import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
|
||||
|
||||
<KeyboardAwareScrollView
|
||||
bottomOffset={20}
|
||||
keyboardOffset={10}
|
||||
>
|
||||
{/* Content */}
|
||||
</KeyboardAwareScrollView>
|
||||
```
|
||||
|
||||
**Keuntungan**:
|
||||
- Smooth keyboard animations
|
||||
- Better control over keyboard behavior
|
||||
- Cross-platform consistency
|
||||
|
||||
**Kekurangan**:
|
||||
- Perlu verify konfigurasi yang sudah ada
|
||||
- Mungkin perlu update library
|
||||
|
||||
### Option 3: **Custom Phone Input** (FALLBACK)
|
||||
**Effort**: High
|
||||
**Impact**: Medium
|
||||
|
||||
Membuat custom phone input dengan:
|
||||
- TextInputCustom component
|
||||
- Country code picker modal
|
||||
- Validation logic
|
||||
|
||||
**Keuntungan**:
|
||||
- Full control atas behavior
|
||||
- Tidak depend on third-party issues
|
||||
|
||||
**Kekurangan**:
|
||||
- Development time lebih lama
|
||||
- Perlu testing ekstensif
|
||||
- Maintain code sendiri
|
||||
|
||||
---
|
||||
|
||||
## 📝 Breakdown Task
|
||||
|
||||
### Task 1: Research & Setup ✅
|
||||
- [x] Identifikasi masalah pada iOS 16+
|
||||
- [x] Cek dokumentasi library
|
||||
- [x] Review existing implementation
|
||||
- [ ] Test di iOS simulator (iOS 16+)
|
||||
- [ ] Test di device fisik (jika ada)
|
||||
|
||||
### Task 2: Implement KeyboardAvoidingView Fix
|
||||
- [ ] Wrap ViewWrapper dengan KeyboardAvoidingView
|
||||
- [ ] Set behavior berdasarkan Platform
|
||||
- [ ] Adjust keyboardVerticalOffset
|
||||
- [ ] Test di berbagai ukuran layar
|
||||
- [ ] Test landscape mode (jika applicable)
|
||||
|
||||
### Task 3: Adjust Layout & Styling
|
||||
- [ ] Fix container height/width
|
||||
- [ ] Adjust safe area insets
|
||||
- [ ] Test dengan Dynamic Type settings
|
||||
- [ ] Ensure consistent padding/margin
|
||||
|
||||
### Task 4: Test Focus Behavior
|
||||
- [ ] Auto-scroll saat focus
|
||||
- [ ] Keyboard dismiss saat tap outside
|
||||
- [ ] Next/previous field navigation (jika ada)
|
||||
- [ ] Input validation on blur
|
||||
|
||||
### Task 5: Country Picker Fix
|
||||
- [ ] Verify dropdown z-index
|
||||
- [ ] Test modal presentation
|
||||
- [ ] Ensure flag icons visible
|
||||
- [ ] Test search functionality
|
||||
|
||||
### Task 6: Testing & QA
|
||||
- [ ] Test iOS 16, 17, 18
|
||||
- [ ] Test Android (regression)
|
||||
- [ ] Test dengan berbagai device sizes
|
||||
- [ ] Test accessibility (VoiceOver)
|
||||
- [ ] Performance test (no lag)
|
||||
|
||||
### Task 7: Documentation
|
||||
- [ ] Update code comments
|
||||
- [ ] Document iOS-specific workarounds
|
||||
- [ ] Add troubleshooting notes
|
||||
|
||||
---
|
||||
|
||||
## 💻 Implementation Guidelines
|
||||
|
||||
### Recommended Implementation (Option 1 + 2 Hybrid)
|
||||
|
||||
```typescript
|
||||
import { KeyboardAvoidingView, Platform } from "react-native";
|
||||
import { KeyboardProvider } from "react-native-keyboard-controller";
|
||||
|
||||
// Di LoginView.tsx
|
||||
export default function LoginView() {
|
||||
return (
|
||||
<ViewWrapper
|
||||
withBackground
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
|
||||
}
|
||||
>
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
keyboardVerticalOffset={Platform.OS === "ios" ? 100 : 50}
|
||||
style={{ flex: 1 }}
|
||||
>
|
||||
<View style={[GStyles.authContainer, { paddingBottom: 40 }]}>
|
||||
{/* Title Section */}
|
||||
<View style={GStyles.authContainerTitle}>
|
||||
<Text style={GStyles.authSubTitle}>WELCOME TO</Text>
|
||||
<Spacing height={5} />
|
||||
<Text style={GStyles.authTitle}>HIPMI BADUNG APPS</Text>
|
||||
<Spacing height={5} />
|
||||
</View>
|
||||
|
||||
<Spacing height={50} />
|
||||
|
||||
{/* Phone Input - Wrap dengan View untuk stability */}
|
||||
<View style={{ marginBottom: 20 }}>
|
||||
<PhoneInput
|
||||
value={inputValue}
|
||||
onChangePhoneNumber={handleInputValue}
|
||||
selectedCountry={selectedCountry}
|
||||
onChangeSelectedCountry={handleSelectedCountry}
|
||||
defaultCountry="ID"
|
||||
placeholder="Masukkan nomor"
|
||||
// Add iOS-specific props
|
||||
textInputProps={{
|
||||
keyboardType: "phone-pad",
|
||||
autoComplete: "tel",
|
||||
importantForAutofill: "yes",
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Spacing />
|
||||
|
||||
{/* Login Button */}
|
||||
<ButtonCustom
|
||||
onPress={handleLogin}
|
||||
disabled={loadingTerm}
|
||||
isLoading={loading || loadingTerm}
|
||||
>
|
||||
Login
|
||||
</ButtonCustom>
|
||||
|
||||
<Spacing height={50} />
|
||||
|
||||
{/* Terms Text */}
|
||||
<Text style={{ ...GStyles.textLabel, textAlign: "center", fontSize: 12 }}>
|
||||
Dengan menggunakan aplikasi ini, Anda telah menyetujui{" "}
|
||||
<Text
|
||||
style={{
|
||||
color: MainColor.yellow,
|
||||
textDecorationLine: "underline",
|
||||
}}
|
||||
onPress={() => {
|
||||
const toUrl = `${url}/terms-of-service.html`;
|
||||
openBrowser(toUrl);
|
||||
}}
|
||||
>
|
||||
Syarat & Ketentuan
|
||||
</Text>{" "}
|
||||
dan seluruh kebijakan privasi yang berlaku.
|
||||
</Text>
|
||||
|
||||
{/* Version Info */}
|
||||
<Text
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 35,
|
||||
right: 50,
|
||||
fontSize: 10,
|
||||
fontWeight: "thin",
|
||||
fontStyle: "italic",
|
||||
color: MainColor.white_gray,
|
||||
}}
|
||||
>
|
||||
{version} | powered by muku.id
|
||||
</Text>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
</ViewWrapper>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Styling Adjustments
|
||||
|
||||
```typescript
|
||||
// styles/global-styles.ts
|
||||
export const GStyles = StyleSheet.create({
|
||||
authContainer: {
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: 24,
|
||||
// Add padding bottom untuk keyboard space
|
||||
paddingBottom: Platform.OS === "ios" ? 40 : 20,
|
||||
},
|
||||
// ... existing styles
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Acceptance Criteria
|
||||
|
||||
### Functional
|
||||
- [ ] Input field tidak tertutup keyboard saat focus
|
||||
- [ ] Country picker dropdown berfungsi dengan baik
|
||||
- [ ] Auto-scroll bekerja smooth saat focus
|
||||
- [ ] Keyboard dismiss saat tap outside
|
||||
- [ ] Input validation berjalan normal
|
||||
|
||||
### Visual
|
||||
- [ ] Layout tidak berubah saat keyboard muncul
|
||||
- [ ] No visual glitches atau flickering
|
||||
- [ ] Country flag icons visible
|
||||
- [ ] Consistent spacing dan padding
|
||||
|
||||
### Compatibility
|
||||
- [ ] iOS 16, 17, 18 - Tested ✅
|
||||
- [ ] Android - No regression ✅
|
||||
- [ ] iPad - Responsive ✅
|
||||
- [ ] Landscape mode - Usable ✅
|
||||
|
||||
### Performance
|
||||
- [ ] No lag saat typing
|
||||
- [ ] Smooth keyboard animations
|
||||
- [ ] No memory leaks
|
||||
- [ ] Fast input response
|
||||
|
||||
---
|
||||
|
||||
## 🔗 References
|
||||
- [React Native KeyboardAvoidingView Docs](https://reactnative.dev/docs/keyboardavoidingview)
|
||||
- [react-native-keyboard-controller](https://github.com/kirillzyusko/react-native-keyboard-controller)
|
||||
- [react-native-international-phone-number Issues](https://github.com/bluesky01/react-native-international-phone-number/issues)
|
||||
- [iOS 16+ Keyboard Changes](https://developer.apple.com/documentation/uikit/uikit)
|
||||
|
||||
---
|
||||
|
||||
## 📊 Estimated Effort
|
||||
- **Complexity**: Medium
|
||||
- **Time Estimate**: 2-4 jam
|
||||
- **Risk Level**: Medium (perlu testing ekstensif di device)
|
||||
|
||||
---
|
||||
|
||||
## 📝 Notes
|
||||
- **Priority**: High (login adalah critical path)
|
||||
- **Testing**: Wajib test di device fisik jika memungkinkan
|
||||
- **Fallback**: Jika Option 1 & 2 gagal, siap untuk implement Option 3 (custom input)
|
||||
- **Monitoring**: Add analytics untuk track input completion rate
|
||||
|
||||
---
|
||||
|
||||
**Created**: 2026-03-25
|
||||
**Status**: Pending
|
||||
**Priority**: High
|
||||
**Related Issue**: iOS 16+ keyboard compatibility
|
||||
@@ -34,7 +34,7 @@ export default {
|
||||
associatedDomains: [
|
||||
"applinks:cld-dkr-hipmi-stg.wibudev.com",
|
||||
],
|
||||
buildNumber: "7",
|
||||
buildNumber: "4",
|
||||
},
|
||||
|
||||
android: {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { BackButton } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import PdfViewer from "@/components/_ShareComponent/PdfViewer";
|
||||
import API_STRORAGE from "@/constants/base-url-api-strorage";
|
||||
import { Stack, useLocalSearchParams } from "expo-router";
|
||||
@@ -8,12 +7,13 @@ import { SafeAreaView } from "react-native-safe-area-context";
|
||||
export default function FileScreen() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const url = API_STRORAGE.GET({ fileId: id as string });
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => <AppHeader title="File" left={<BackButton />} />,
|
||||
title: "File",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<SafeAreaView style={{ flex: 1 }} edges={["bottom"]}>
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
import { BackButton } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import { IconDot } from "@/components/_Icon/IconComponent";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import { HeaderStyles } from "@/styles/header-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router, Stack } from "expo-router";
|
||||
|
||||
export default function UserLayout() {
|
||||
return (
|
||||
<>
|
||||
<Stack>
|
||||
<Stack screenOptions={HeaderStyles}>
|
||||
<Stack.Screen
|
||||
name="delete-account"
|
||||
options={{
|
||||
header: () => <AppHeader title="Hapus Akun" />,
|
||||
title: "Hapus Akun",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
@@ -46,7 +47,8 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="user-search/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Pencarian Pengguna" />,
|
||||
title: "Pencarian Pengguna",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -69,18 +71,10 @@ export default function UserLayout() {
|
||||
|
||||
{/* ========== Event Section ========= */}
|
||||
|
||||
{/* <Stack.Screen
|
||||
name="event/(tabs)"
|
||||
options={{
|
||||
header: () => <AppHeader title="Event" left={<BackButton path="/home" />} />,
|
||||
}}
|
||||
/> */}
|
||||
|
||||
<Stack.Screen
|
||||
name="event/(tabs)"
|
||||
options={{
|
||||
title: "Event",
|
||||
header: () => <AppHeader title="Event" left={<BackButton path="/home" />} />,
|
||||
// NOTE: DIPINDAH DI FILE /Event/(Tabs)/_layout.tsx
|
||||
// headerLeft: () => (
|
||||
// <LeftButtonCustom path="/(application)/(user)/home" />
|
||||
@@ -91,28 +85,32 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="event/create"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Event" />,
|
||||
title: "Tambah Event",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="event/detail/[id]"
|
||||
options={{
|
||||
header: () => <AppHeader title="Event Detail" left={<LeftButtonCustom />} />,
|
||||
title: "Event Detail",
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="event/[id]/edit"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Event" />,
|
||||
title: "Edit Event",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="event/[id]/list-of-participants"
|
||||
options={{
|
||||
header: () => <AppHeader title="Daftar peserta" />,
|
||||
title: "Daftar peserta",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
{/* ========== End Event Section ========= */}
|
||||
@@ -121,19 +119,22 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="collaboration/(tabs)"
|
||||
options={{
|
||||
header: () => <AppHeader title="Collaboration" left={<BackButton path="/home" />} />,
|
||||
title: "Collaboration",
|
||||
headerLeft: () => <BackButton path="/home" />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="collaboration/create"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Proyek" />,
|
||||
title: "Tambah Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/list-of-participants"
|
||||
options={{
|
||||
header: () => <AppHeader title="Daftar Partisipan" />,
|
||||
title: "Daftar Partisipan",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
{/* <Stack.Screen
|
||||
@@ -146,19 +147,22 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/edit"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Proyek" />,
|
||||
title: "Edit Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/create-pacticipants"
|
||||
options={{
|
||||
header: () => <AppHeader title="Ajukan Partisipasi" />,
|
||||
title: "Ajukan Partisipasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="collaboration/[id]/select-of-participants"
|
||||
options={{
|
||||
header: () => <AppHeader title="Pilih Partisipan" />,
|
||||
title: "Pilih Partisipan",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -168,25 +172,29 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="voting/create"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Voting" />,
|
||||
title: "Tambah Voting",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="voting/(tabs)"
|
||||
options={{
|
||||
header: () => <AppHeader title="Voting" left={<BackButton path="/home" />} />,
|
||||
title: "Voting",
|
||||
headerLeft: () => <BackButton path="/home" />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="voting/[id]/edit"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Voting" />,
|
||||
title: "Edit Voting",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="voting/[id]/list-of-contributor"
|
||||
options={{
|
||||
header: () => <AppHeader title="Daftar Kontributor" />,
|
||||
title: "Daftar Kontributor",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -196,7 +204,8 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="crowdfunding/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Crowdfunding" left={<BackButton path="/home" />} />,
|
||||
title: "Crowdfunding",
|
||||
headerLeft: () => <BackButton path="/home" />,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -206,95 +215,103 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="investment/(tabs)"
|
||||
options={{
|
||||
header: () => <AppHeader title="Investasi" left={<BackButton path="/crowdfunding" />} />,
|
||||
title: "Investasi",
|
||||
headerLeft: () => <BackButton path="/crowdfunding" />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/create"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Investasi" />,
|
||||
title: "Tambah Investasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Detail Investasi" />,
|
||||
title: "Detail Investasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/edit"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Investasi" />,
|
||||
title: "Edit Investasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/edit-prospectus"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Prospektus" />,
|
||||
title: "Edit Prospektus",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(document)/list-of-document"
|
||||
options={{
|
||||
header: () => <AppHeader title="Daftar Dokumen" />,
|
||||
title: "Daftar Dokumen",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(document)/add-document"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Dokumen" />,
|
||||
title: "Tambah Dokumen",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(document)/edit-document"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Dokumen" />,
|
||||
title: "Edit Dokumen",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(news)/add-news"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Berita" />,
|
||||
title: "Tambah Berita",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/investor"
|
||||
options={{
|
||||
header: () => <AppHeader title="Investor" />,
|
||||
title: "Investor",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(transaction-flow)/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Pembelian Saham" />,
|
||||
title: "Pembelian Saham",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(transaction-flow)/select-bank"
|
||||
options={{
|
||||
header: () => <AppHeader title="Pilih Bank" />,
|
||||
title: "Pilih Bank",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(transaction-flow)/invoice"
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Invoice"
|
||||
left={
|
||||
<Ionicons
|
||||
name="close"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() =>
|
||||
router.navigate(`/investment/(tabs)/transaction`)
|
||||
}
|
||||
/>
|
||||
title: "Invoice",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="close"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() =>
|
||||
router.navigate(`/investment/(tabs)/transaction`)
|
||||
}
|
||||
/>
|
||||
),
|
||||
@@ -303,18 +320,14 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(transaction-flow)/process"
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Proses"
|
||||
left={
|
||||
<Ionicons
|
||||
name="close"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() =>
|
||||
router.navigate(`/investment/(tabs)/transaction`)
|
||||
}
|
||||
/>
|
||||
title: "Proses",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="close"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() =>
|
||||
router.navigate(`/investment/(tabs)/transaction`)
|
||||
}
|
||||
/>
|
||||
),
|
||||
@@ -323,20 +336,23 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(transaction-flow)/success"
|
||||
options={{
|
||||
header: () => <AppHeader title="Transaksi Berhasil" />,
|
||||
title: "Transaksi Berhasil",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(transaction-flow)/failed"
|
||||
options={{
|
||||
header: () => <AppHeader title="Transaksi Gagal" />,
|
||||
title: "Transaksi Gagal",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="investment/[id]/(my-holding)/[id]"
|
||||
options={{
|
||||
header: () => <AppHeader title="Detail Saham Saya" />,
|
||||
title: "Detail Saham Saya",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
{/* ========== End Investment Section ========= */}
|
||||
@@ -345,111 +361,122 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="donation/(tabs)"
|
||||
options={{
|
||||
header: () => <AppHeader title="Donasi" left={<BackButton path="/crowdfunding" />} />,
|
||||
title: "Donasi",
|
||||
headerLeft: () => <BackButton path="/crowdfunding" />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="donation/create"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Donasi" />,
|
||||
title: "Tambah Donasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/create-story"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Donasi" />,
|
||||
title: "Tambah Donasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="donation/[id]/edit"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Donasi" />,
|
||||
title: "Edit Donasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/edit-story"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Donasi" />,
|
||||
title: "Edit Donasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/edit-rekening"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Rekening" />,
|
||||
title: "Edit Rekening",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/detail-story"
|
||||
options={{
|
||||
header: () => <AppHeader title="Cerita Penggalang" />,
|
||||
title: "Cerita Penggalang",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/infromation-fundrising"
|
||||
options={{
|
||||
header: () => <AppHeader title="Informasi Penggalang Dana" />,
|
||||
title: "Informasi Penggalang Dana",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/list-of-donatur"
|
||||
options={{
|
||||
header: () => <AppHeader title="Daftar Donatur" />,
|
||||
title: "Daftar Donatur",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/fund-disbursement"
|
||||
options={{
|
||||
header: () => <AppHeader title="Pencairan Dana" />,
|
||||
title: "Pencairan Dana",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="donation/[id]/(news)/recap-of-news"
|
||||
options={{
|
||||
header: () => <AppHeader title="Rekap Kabar" />,
|
||||
title: "Rekap Kabar",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/(news)/add-news"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Berita" />,
|
||||
title: "Tambah Berita",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/(news)/[news]/edit-news"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Berita" />,
|
||||
title: "Edit Berita",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="donation/[id]/(transaction-flow)/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Donasi" />,
|
||||
title: "Donasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/(transaction-flow)/select-bank"
|
||||
options={{
|
||||
header: () => <AppHeader title="Pilih Bank" />,
|
||||
title: "Pilih Bank",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/(transaction-flow)/[invoiceId]/invoice"
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Invoice"
|
||||
left={
|
||||
<Ionicons
|
||||
name="close"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.navigate(`/donation/(tabs)/my-donation`)}
|
||||
/>
|
||||
}
|
||||
title: "Invoice",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="close"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.navigate(`/donation/(tabs)/my-donation`)}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
@@ -457,17 +484,13 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="donation/[id]/(transaction-flow)/[invoiceId]/process"
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Proses"
|
||||
left={
|
||||
<Ionicons
|
||||
name="close"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.navigate(`/donation/(tabs)/my-donation`)}
|
||||
/>
|
||||
}
|
||||
title: "Proses",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="close"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.navigate(`/donation/(tabs)/my-donation`)}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
@@ -475,51 +498,55 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="donation/[id]/(transaction-flow)/[invoiceId]/success"
|
||||
options={{
|
||||
header: () => <AppHeader title="Donasi Berhasil" />,
|
||||
title: "Donasi Berhasil",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="donation/[id]/(transaction-flow)/[invoiceId]/failed"
|
||||
options={{
|
||||
header: () => <AppHeader title="Donasi Gagal" />,
|
||||
title: "Donasi Gagal",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ========== End Donation Section ========= */}
|
||||
|
||||
{/* ========== Job Section ========= */}
|
||||
|
||||
|
||||
<Stack.Screen
|
||||
name="job/create"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Job" />,
|
||||
title: "Tambah Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/(tabs)"
|
||||
options={{
|
||||
title: "Job Vacancy",
|
||||
// headerLeft: () => <BackButton path="/home" />,
|
||||
// NOTE: headerLeft di pindahkan ke Tabs Layout
|
||||
header: () => <AppHeader title="Job Vacancy" left={<BackButton path="/home" />} />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/[id]/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Detail Job" />,
|
||||
title: "Detail Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/[id]/edit"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Job" />,
|
||||
title: "Edit Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="job/[id]/archive"
|
||||
options={{
|
||||
header: () => <AppHeader title="Arsip Job" />,
|
||||
title: "Arsip Job",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -529,67 +556,78 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="forum/create"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Diskusi" />,
|
||||
title: "Tambah Diskusi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/edit"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Diskusi" />,
|
||||
title: "Edit Diskusi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/forumku"
|
||||
options={{
|
||||
header: () => <AppHeader title="Forumku" left={<BackButton icon={"close"} />} />,
|
||||
title: "Forumku",
|
||||
headerLeft: () => <BackButton icon={"close"} />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Detail" />,
|
||||
title: "Detail",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/report-commentar"
|
||||
options={{
|
||||
header: () => <AppHeader title="Laporkan Komentar" />,
|
||||
title: "Laporkan Komentar",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/other-report-commentar"
|
||||
options={{
|
||||
header: () => <AppHeader title="Laporkan Komentar" />,
|
||||
title: "Laporkan Komentar",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/report-posting"
|
||||
options={{
|
||||
header: () => <AppHeader title="Laporkan Diskusi" />,
|
||||
title: "Laporkan Diskusi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/other-report-posting"
|
||||
options={{
|
||||
header: () => <AppHeader title="Laporkan Diskusi" />,
|
||||
title: "Laporkan Diskusi",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/terms"
|
||||
options={{
|
||||
header: () => <AppHeader title="Syarat & Ketentuan Forum" />,
|
||||
title: "Syarat & Ketentuan Forum",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/preview-report-posting"
|
||||
options={{
|
||||
header: () => <AppHeader title="Laporan Postingan" />,
|
||||
title: "Laporan Postingan",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="forum/[id]/preview-report-comment"
|
||||
options={{
|
||||
header: () => <AppHeader title="Laporan Komentar" />,
|
||||
title: "Laporan Komentar",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -597,25 +635,29 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="maps/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Maps" />,
|
||||
title: "Maps",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="maps/create"
|
||||
options={{
|
||||
header: () => <AppHeader title="Tambah Maps" />,
|
||||
title: "Tambah Maps",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="maps/[id]/edit"
|
||||
options={{
|
||||
header: () => <AppHeader title="Edit Maps" />,
|
||||
title: "Edit Maps",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="maps/[id]/custom-pin"
|
||||
options={{
|
||||
header: () => <AppHeader title="Custom Pin Maps" />,
|
||||
title: "Custom Pin Maps",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -623,7 +665,8 @@ export default function UserLayout() {
|
||||
<Stack.Screen
|
||||
name="marketplace/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Market Place" />,
|
||||
title: "Market Place",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { apiCollaborationGroup } from "@/service/api-client/api-collaboration";
|
||||
import { Stack, useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import { useState, useCallback } from "react";
|
||||
@@ -41,7 +40,8 @@ export default function CollaborationRoomInfo() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => <AppHeader title="Info" left={<BackButton />} />,
|
||||
title: `Info`,
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { BackButton } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
|
||||
import ChatScreen from "@/screens/Collaboration/GroupChatSection";
|
||||
@@ -13,18 +12,14 @@ export default function CollaborationRoomChat() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title={`Proyek ${detail}`}
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<Feather
|
||||
name="info"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.push(`/collaboration/${id}/${detail}/info`)}
|
||||
/>
|
||||
}
|
||||
title: `Proyek ${detail}`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<Feather
|
||||
name="info"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.push(`/collaboration/${id}/${detail}/info`)}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import { apiCollaborationGetOne } from "@/service/api-client/api-collaboration";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
@@ -39,14 +38,10 @@ export default function CollaborationDetailParticipant() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail Proyek"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<DotButton onPress={() => setOpenDrawerParticipant(true)} />
|
||||
}
|
||||
/>
|
||||
title: "Detail Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton onPress={() => setOpenDrawerParticipant(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
Spacing,
|
||||
ViewWrapper
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import {
|
||||
@@ -67,13 +66,9 @@ export default function CollaborationDetailProjectMain() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Proyek Saya"
|
||||
left={<BackButton />}
|
||||
right={<DotButton onPress={() => setOpenDrawer(true)} />}
|
||||
/>
|
||||
),
|
||||
title: "Proyek Saya",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import Collaboration_BoxDetailSection from "@/screens/Collaboration/BoxDetailSection";
|
||||
import {
|
||||
@@ -75,14 +74,10 @@ export default function CollaborationDetail() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail Proyek"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<DotButton onPress={() => setOpenDrawerMenu(true)} />
|
||||
}
|
||||
/>
|
||||
title: "Detail Proyek",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton onPress={() => setOpenDrawerMenu(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import { IconTrash } from "@/components/_Icon/IconTrash";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
@@ -58,17 +57,12 @@ export default function DonationNews() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail Kabar"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
user?.id === data?.authorId && (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: "Detail Kabar",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
user?.id === data?.authorId && (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
NewWrapper,
|
||||
Spacing,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconEdit, IconNews } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||
@@ -98,19 +97,14 @@ export default function DonasiDetailStatus() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title={`Detail ${_.startCase(status as string)}`}
|
||||
left={<BackButton />}
|
||||
right={
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: `Detail ${_.startCase(status as string)}`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
<NewWrapper
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconNews } from "@/components/_Icon";
|
||||
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
@@ -91,17 +90,12 @@ export default function DonasiDetailBeranda() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail Donasi"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
user?.id === data?.Author?.id ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: `Detail Donasi`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
user?.id === data?.Author?.id ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
<NewWrapper footerComponent={buttonSection}>
|
||||
|
||||
@@ -4,34 +4,36 @@ import {
|
||||
IconHome,
|
||||
IconStatus,
|
||||
} from "@/components/_Icon";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { router, Tabs, useLocalSearchParams } from "expo-router";
|
||||
import { router, Tabs, useLocalSearchParams, useNavigation } from "expo-router";
|
||||
import { useLayoutEffect } from "react";
|
||||
|
||||
export default function EventTabsLayout() {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const { from, category } = useLocalSearchParams<{
|
||||
from?: string;
|
||||
category?: string;
|
||||
}>();
|
||||
|
||||
console.log("from", from);
|
||||
console.log("category", category);
|
||||
|
||||
// Atur header secara dinamis
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerLeft: () => (
|
||||
<BackButtonFromNotification
|
||||
from={from as string}
|
||||
category={category as string}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}, [from, router, navigation]);
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Event"
|
||||
left={
|
||||
<BackButtonFromNotification
|
||||
from={from as string}
|
||||
category={category as string}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import Event_ButtonStatusSection from "@/screens/Event/ButtonStatusSection";
|
||||
@@ -82,17 +81,12 @@ export default function EventDetailStatus() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title={`Detail ${status === "publish" ? "" : status}`}
|
||||
left={<LeftButtonCustom />}
|
||||
right={
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: `Detail ${status === "publish" ? "" : status}`,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import {
|
||||
@@ -266,17 +265,13 @@ export default function UserEventConfirmation() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Konfirmasi Event"
|
||||
left={
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.navigate("/")}
|
||||
/>
|
||||
}
|
||||
title: "Konfirmasi Event",
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="arrow-back"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => router.navigate("/")}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
|
||||
@@ -50,13 +49,9 @@ export default function EventDetailContribution() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail kontribusi"
|
||||
left={<LeftButtonCustom />}
|
||||
right={<DotButton onPress={() => setOpenDrawer(true)} />}
|
||||
/>
|
||||
),
|
||||
title: `Detail kontribusi`,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
ViewWrapper,
|
||||
Spacing,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import Event_BoxDetailPublishSection from "@/screens/Event/BoxDetailPublishSection";
|
||||
@@ -45,13 +44,9 @@ export default function EventDetailHistory() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail riwayat"
|
||||
left={<LeftButtonCustom />}
|
||||
right={<DotButton onPress={() => setOpenDrawer(true)} />}
|
||||
/>
|
||||
),
|
||||
title: `Detail riwayat`,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
@@ -157,13 +156,9 @@ export default function EventDetailPublish() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Event Publish"
|
||||
left={<BackButton onPress={() => router.back()} />}
|
||||
right={<DotButton onPress={() => setOpenDrawer(true)} />}
|
||||
/>
|
||||
),
|
||||
title: `Event Publish`,
|
||||
headerLeft: () => <BackButton onPress={() => router.back()} />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { BasicWrapper, NewWrapper, StackCustom, ViewWrapper } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { BasicWrapper, StackCustom, ViewWrapper } from "@/components";
|
||||
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
@@ -118,37 +117,29 @@ export default function Application() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="HIPMI"
|
||||
showBack={false}
|
||||
left={
|
||||
data ? (
|
||||
<Ionicons
|
||||
name="search"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => {
|
||||
router.push("/user-search");
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<CustomSkeleton height={30} width={30} radius={100} />
|
||||
)
|
||||
}
|
||||
right={
|
||||
data ? (
|
||||
<HeaderBell />
|
||||
) : (
|
||||
<CustomSkeleton height={30} width={30} radius={100} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: `HIPMI`,
|
||||
headerLeft: () =>
|
||||
data ? (
|
||||
<Ionicons
|
||||
name="search"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
onPress={() => {
|
||||
router.push("/user-search");
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<CustomSkeleton height={30} width={30} radius={100} />
|
||||
),
|
||||
headerRight: () =>
|
||||
data ? (
|
||||
<HeaderBell />
|
||||
) : (
|
||||
<CustomSkeleton height={30} width={30} radius={100} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
<NewWrapper
|
||||
<ViewWrapper
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
@@ -166,19 +157,18 @@ export default function Application() {
|
||||
})}
|
||||
/>
|
||||
) : (
|
||||
null
|
||||
// <View style={GStyles.tabBar}>
|
||||
// <View style={[GStyles.tabContainer, { paddingTop: 10 }]}>
|
||||
// {Array.from({ length: 4 }).map((e, index) => (
|
||||
// <CustomSkeleton
|
||||
// key={index}
|
||||
// height={40}
|
||||
// width={40}
|
||||
// radius={100}
|
||||
// />
|
||||
// ))}
|
||||
// </View>
|
||||
// </View>
|
||||
<View style={GStyles.tabBar}>
|
||||
<View style={[GStyles.tabContainer, { paddingTop: 10 }]}>
|
||||
{Array.from({ length: 4 }).map((e, index) => (
|
||||
<CustomSkeleton
|
||||
key={index}
|
||||
height={40}
|
||||
width={40}
|
||||
radius={100}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
>
|
||||
@@ -202,10 +192,10 @@ export default function Application() {
|
||||
{data ? (
|
||||
<Home_BottomFeatureSection listData={listData} />
|
||||
) : (
|
||||
<CustomSkeleton height={150} />
|
||||
<CustomSkeleton height={200} />
|
||||
)}
|
||||
</StackCustom>
|
||||
</NewWrapper>
|
||||
</ViewWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconDocument, IconEdit, IconNews } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
@@ -31,13 +30,13 @@ export default function InvestmentDetailHolding() {
|
||||
const [openDrawerDraft, setOpenDrawerDraft] = useState(false);
|
||||
const [openDrawerPublish, setOpenDrawerPublish] = useState(false);
|
||||
const [data, setData] = useState<any>(null);
|
||||
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadData();
|
||||
}, [id, status])
|
||||
);
|
||||
|
||||
|
||||
const onLoadData = async () => {
|
||||
try {
|
||||
const response = await apiInvestmentGetInvoice({
|
||||
@@ -45,7 +44,7 @@ export default function InvestmentDetailHolding() {
|
||||
authorId: user?.id,
|
||||
category: "invoice",
|
||||
});
|
||||
|
||||
|
||||
console.log("[DATA]", JSON.stringify(response.data, null, 2));
|
||||
setData(response.data);
|
||||
} catch (error) {
|
||||
@@ -77,19 +76,14 @@ export default function InvestmentDetailHolding() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title={`Detail ${_.startCase(status as string)}`}
|
||||
left={<BackButton />}
|
||||
right={
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: `Detail ${_.startCase(status as string)}`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconTrash } from "@/components/_Icon/IconTrash";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import {
|
||||
@@ -57,17 +56,12 @@ export default function InvestmentNews() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail Berita"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
user?.id === data?.authorId && (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: "Detail Berita",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
user?.id === data?.authorId && (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconDocument, IconEdit, IconNews } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
@@ -107,19 +106,14 @@ export default function InvestmentDetailStatus() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title={`Detail ${_.startCase(status as string)}`}
|
||||
left={<BackButton />}
|
||||
right={
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: `Detail ${_.startCase(status as string)}`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
MenuDrawerDynamicGrid,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconDocument, IconEdit, IconNews } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
@@ -106,19 +105,14 @@ export default function InvestmentDetail() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title={`Detail ${_.startCase(status as string)}`}
|
||||
left={<BackButton />}
|
||||
right={
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: `Detail ${_.startCase(status as string)}`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { BackButton } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconHome, IconStatus } from "@/components/_Icon";
|
||||
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
@@ -8,30 +7,31 @@ import { Ionicons } from "@expo/vector-icons";
|
||||
import {
|
||||
router,
|
||||
Tabs,
|
||||
useLocalSearchParams
|
||||
useLocalSearchParams,
|
||||
useNavigation
|
||||
} from "expo-router";
|
||||
import { useLayoutEffect } from "react";
|
||||
|
||||
export default function JobTabsLayout() {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const { from, category } = useLocalSearchParams<{
|
||||
from?: string;
|
||||
category?: string;
|
||||
}>();
|
||||
|
||||
// Atur header secara dinamis
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerLeft: () => (
|
||||
<BackButtonFromNotification from={from as string} category={category as string} />
|
||||
),
|
||||
});
|
||||
}, [from, router, navigation]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Job Vacancy"
|
||||
left={
|
||||
<BackButtonFromNotification from={from as string} category={category as string} />
|
||||
}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import ReportBox from "@/components/Box/ReportBox";
|
||||
@@ -59,17 +58,12 @@ export default function JobDetailStatus() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: `Detail`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawer(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
|
||||
@@ -1,5 +1,365 @@
|
||||
import { ScreenPortofolioCreate } from "@/screens/Portofolio/ScreenPortofolioCreate";
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import {
|
||||
ActionIcon,
|
||||
AvatarComp,
|
||||
BaseBox,
|
||||
ButtonCenteredOnly,
|
||||
CenterCustom,
|
||||
Grid,
|
||||
InformationBox,
|
||||
NewWrapper,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import Portofolio_ButtonCreate from "@/screens/Portofolio/ButtonCreatePortofolio";
|
||||
import {
|
||||
apiMasterBidangBisnis,
|
||||
apiMasterSubBidangBisnis,
|
||||
} from "@/service/api-client/api-master";
|
||||
import {
|
||||
IMasterBidangBisnis,
|
||||
IMasterSubBidangBisnis,
|
||||
} from "@/types/Type-Master";
|
||||
import pickImage from "@/utils/pickImage";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Image } from "expo-image";
|
||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Text, TouchableOpacity, View } from "react-native";
|
||||
import PhoneInput, { ICountry } from "react-native-international-phone-number";
|
||||
import { Avatar } from "react-native-paper";
|
||||
|
||||
export default function PortofolioCreate() {
|
||||
return <ScreenPortofolioCreate />;
|
||||
const { id } = useLocalSearchParams();
|
||||
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
|
||||
const [inputValue, setInputValue] = useState<string>("");
|
||||
const [data, setData] = useState({
|
||||
namaBisnis: "",
|
||||
masterBidangBisnisId: "",
|
||||
alamatKantor: "",
|
||||
tlpn: "",
|
||||
deskripsi: "",
|
||||
});
|
||||
const [imageUri, setImageUri] = useState<string | null>(null);
|
||||
|
||||
const [bidangBisnis, setBidangBisnis] = useState<IMasterBidangBisnis[]>([]);
|
||||
const [subBidangBisnis, setSubBidangBisnis] = useState<
|
||||
IMasterSubBidangBisnis[]
|
||||
>([]);
|
||||
|
||||
const [selectedSubBidang, setSelectedSubBidang] = useState<string[]>([]);
|
||||
const [listSubBidangSelected, setListSubBidangSelected] = useState([
|
||||
{
|
||||
id: "",
|
||||
},
|
||||
]);
|
||||
|
||||
const [dataMedsos, setDataMedsos] = useState({
|
||||
facebook: "",
|
||||
twitter: "",
|
||||
instagram: "",
|
||||
youtube: "",
|
||||
tiktok: "",
|
||||
});
|
||||
|
||||
const [isLoadingCreate, setIsLoadingCreate] = useState(false);
|
||||
|
||||
function handleInputValue(phoneNumber: string) {
|
||||
setInputValue(phoneNumber);
|
||||
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
|
||||
let fixNumber = inputValue.replace(/\s+/g, "").replace(/^0+/, "");
|
||||
const realNumber = callingCode + fixNumber;
|
||||
setData({ ...data, tlpn: realNumber });
|
||||
}
|
||||
|
||||
function handleSelectedCountry(country: ICountry) {
|
||||
setSelectedCountry(country);
|
||||
}
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadMaster();
|
||||
onLoadMasterSubBidangBisnis();
|
||||
}, [])
|
||||
);
|
||||
|
||||
const onLoadMaster = async () => {
|
||||
try {
|
||||
const response = await apiMasterBidangBisnis();
|
||||
setBidangBisnis(response.data);
|
||||
} catch (error) {
|
||||
setBidangBisnis([]);
|
||||
console.log("Error onLoadMasterBidangBisnis", error);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadMasterSubBidangBisnis = async () => {
|
||||
try {
|
||||
const response = await apiMasterSubBidangBisnis({});
|
||||
setSubBidangBisnis(response.data);
|
||||
} catch (error) {
|
||||
setSubBidangBisnis([]);
|
||||
console.log("Error onLoadMasterBidangBisnis", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerSelectedSubBidang = ({ id }: { id: string }) => {
|
||||
const selectedList = subBidangBisnis?.filter(
|
||||
(item) => (item?.masterBidangBisnisId as any) === id
|
||||
);
|
||||
setSelectedSubBidang(selectedList as any[]);
|
||||
};
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
footerComponent={
|
||||
<Portofolio_ButtonCreate
|
||||
id={id as string}
|
||||
data={data}
|
||||
dataMedsos={dataMedsos}
|
||||
imageUri={imageUri}
|
||||
subBidangSelected={listSubBidangSelected}
|
||||
isLoadingCreate={isLoadingCreate}
|
||||
setIsLoadingCreate={setIsLoadingCreate}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{/* <TextCustom>Portofolio Create {id}</TextCustom> */}
|
||||
<StackCustom gap={"xs"}>
|
||||
<InformationBox text="Lengkapi data bisnis anda." />
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Nama Bisnis"
|
||||
placeholder="Masukkan nama bisnis"
|
||||
onChangeText={(value: any) => setData({ ...data, namaBisnis: value })}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
label="Bidang Usaha"
|
||||
required
|
||||
data={bidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.masterBidangBisnisId}
|
||||
onChange={(value) => {
|
||||
const isSameBidang = data.masterBidangBisnisId === value;
|
||||
|
||||
if (!isSameBidang) {
|
||||
setListSubBidangSelected([{ id: "" }]);
|
||||
}
|
||||
|
||||
setData({ ...(data as any), masterBidangBisnisId: value });
|
||||
handlerSelectedSubBidang({ id: value as string });
|
||||
}}
|
||||
/>
|
||||
|
||||
{listSubBidangSelected.map((item, index) => (
|
||||
<SelectCustom
|
||||
key={index}
|
||||
disabled={data.masterBidangBisnisId === ""}
|
||||
label="Sub Bidang Usaha"
|
||||
required
|
||||
data={_.map(selectedSubBidang as any)
|
||||
.filter((option: any) => {
|
||||
const selectedValues = listSubBidangSelected.map((s) => s.id);
|
||||
return (
|
||||
option.id === item.id || // biarkan tetap muncul kalau ini valuenya sendiri
|
||||
!selectedValues.includes(option.id)
|
||||
);
|
||||
})
|
||||
.map((e: any) => ({
|
||||
value: e.id,
|
||||
label: e.name,
|
||||
}))}
|
||||
value={item.id || null}
|
||||
onChange={(value) => {
|
||||
const list = _.clone(listSubBidangSelected);
|
||||
list[index].id = value as any;
|
||||
setListSubBidangSelected(list);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
<CenterCustom>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
|
||||
<ActionIcon
|
||||
disabled={
|
||||
selectedSubBidang.length === listSubBidangSelected.length
|
||||
}
|
||||
onPress={() => {
|
||||
setListSubBidangSelected([
|
||||
...listSubBidangSelected,
|
||||
{ id: "" },
|
||||
]);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="add-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
<ActionIcon
|
||||
disabled={listSubBidangSelected.length <= 1}
|
||||
onPress={() => {
|
||||
const list = _.clone(listSubBidangSelected);
|
||||
list.pop();
|
||||
setListSubBidangSelected(list);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="remove-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
</View>
|
||||
</CenterCustom>
|
||||
<Spacing />
|
||||
|
||||
{/* <SelectCustom
|
||||
label="Bidang Usaha"
|
||||
required
|
||||
data={bidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={null}
|
||||
onChange={(value) => {
|
||||
setData({ ...(data as any), masterBidangBisnisId: value });
|
||||
handlerSelectedSubBidang({ id: value as string });
|
||||
}}
|
||||
/> */}
|
||||
|
||||
{/* <ButtonCenteredOnly
|
||||
onPress={() => {
|
||||
setListSubBidangSelected([...listSubBidangSelected, { id: "" }]);
|
||||
}}
|
||||
>
|
||||
Tambah Pilihan
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing /> */}
|
||||
|
||||
{/* <TextCustom>{JSON.stringify(bidangBisnis, null, 2)}</TextCustom> */}
|
||||
|
||||
<View>
|
||||
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
|
||||
Nomor Telepon
|
||||
</TextCustom>
|
||||
<Text style={{ color: "red" }}> *</Text>
|
||||
</View>
|
||||
<Spacing height={5} />
|
||||
<PhoneInput
|
||||
value={inputValue}
|
||||
onChangePhoneNumber={handleInputValue}
|
||||
selectedCountry={selectedCountry}
|
||||
onChangeSelectedCountry={handleSelectedCountry}
|
||||
defaultCountry="ID"
|
||||
placeholder="xxx-xxx-xxx"
|
||||
/>
|
||||
</View>
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Alamat Bisnis"
|
||||
placeholder="Masukkan alamat bisnis"
|
||||
onChangeText={(value: any) =>
|
||||
setData({ ...data, alamatKantor: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Bisnis"
|
||||
placeholder="Masukkan deskripsi bisnis"
|
||||
value={data.deskripsi}
|
||||
onChangeText={(value: any) => setData({ ...data, deskripsi: value })}
|
||||
autosize
|
||||
minRows={2}
|
||||
maxRows={5}
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
<Spacing />
|
||||
|
||||
{/* Logo */}
|
||||
<InformationBox text="Upload logo bisnis anda untuk di tampilaka pada portofolio." />
|
||||
|
||||
<CenterCustom>
|
||||
<Avatar.Image
|
||||
source={imageUri ? { uri: imageUri } : DUMMY_IMAGE.dummy_image}
|
||||
size={200}
|
||||
/>
|
||||
</CenterCustom>
|
||||
<Spacing />
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => {
|
||||
pickImage({
|
||||
setImageUri,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
<Spacing height={40} />
|
||||
|
||||
{/* Social Media */}
|
||||
<InformationBox text="Isi hanya pada sosial media yang anda miliki." />
|
||||
<TextInputCustom
|
||||
label="Tiktok"
|
||||
placeholder="Masukkan username tiktok"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, tiktok: value })
|
||||
}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Facebook"
|
||||
placeholder="Masukkan username facebook"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, facebook: value })
|
||||
}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Instagram"
|
||||
placeholder="Masukkan username instagram"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, instagram: value })
|
||||
}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Twitter"
|
||||
placeholder="Masukkan username twitter"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, twitter: value })
|
||||
}
|
||||
/>
|
||||
<TextInputCustom
|
||||
label="Youtube"
|
||||
placeholder="Masukkan username youtube"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, youtube: value })
|
||||
}
|
||||
/>
|
||||
{/* <Spacing /> */}
|
||||
</StackCustom>
|
||||
</NewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
ButtonCustom,
|
||||
CenterCustom,
|
||||
NewWrapper,
|
||||
PhoneInputCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
@@ -16,7 +15,6 @@ import {
|
||||
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||
import { DEFAULT_COUNTRY, type CountryData, COUNTRIES } from "@/constants/countries";
|
||||
import {
|
||||
apiMasterBidangBisnis,
|
||||
apiMasterSubBidangBisnis,
|
||||
@@ -34,6 +32,7 @@ import { router, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
import PhoneInput, { ICountry } from "react-native-international-phone-number";
|
||||
import { ActivityIndicator } from "react-native-paper";
|
||||
import Toast from "react-native-toast-message";
|
||||
|
||||
@@ -60,8 +59,8 @@ export default function PortofolioEdit() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [data, setData] = useState<any>({});
|
||||
const [phoneNumber, setPhoneNumber] = useState<string>("");
|
||||
const [selectedCountry, setSelectedCountry] = useState<CountryData>(DEFAULT_COUNTRY);
|
||||
|
||||
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
|
||||
const [bidangBisnis, setBidangBisnis] = useState<
|
||||
IMasterBidangBisnis[] | null
|
||||
>(null);
|
||||
@@ -73,42 +72,12 @@ export default function PortofolioEdit() {
|
||||
IListSubBidangSelected[]
|
||||
>([]);
|
||||
|
||||
function handlePhoneChange(phone: string) {
|
||||
setPhoneNumber(phone);
|
||||
|
||||
// Format phone number for API
|
||||
const callingCode = selectedCountry.callingCode;
|
||||
let fixNumber = phone.replace(/\s+/g, "").replace(/^0+/, "");
|
||||
|
||||
// Remove country code if already present
|
||||
if (fixNumber.startsWith(callingCode)) {
|
||||
fixNumber = fixNumber.substring(callingCode.length);
|
||||
}
|
||||
|
||||
// Remove leading zero
|
||||
fixNumber = fixNumber.replace(/^0+/, "");
|
||||
|
||||
const realNumber = callingCode + fixNumber;
|
||||
setData({ ...data, tlpn: realNumber });
|
||||
function handleInputValue(phoneNumber: string) {
|
||||
setData({ ...data, tlpn: phoneNumber });
|
||||
}
|
||||
|
||||
function handleCountryChange(country: CountryData) {
|
||||
function handleSelectedCountry(country: ICountry) {
|
||||
setSelectedCountry(country);
|
||||
|
||||
// Re-format with new country code
|
||||
const callingCode = country.callingCode;
|
||||
let fixNumber = phoneNumber.replace(/\s+/g, "").replace(/^0+/, "");
|
||||
|
||||
// Remove country code if already present
|
||||
if (fixNumber.startsWith(callingCode)) {
|
||||
fixNumber = fixNumber.substring(callingCode.length);
|
||||
}
|
||||
|
||||
// Remove leading zero
|
||||
fixNumber = fixNumber.replace(/^0+/, "");
|
||||
|
||||
const realNumber = callingCode + fixNumber;
|
||||
setData({ ...data, tlpn: realNumber });
|
||||
}
|
||||
|
||||
const onLoadMasterBidang = async () => {
|
||||
@@ -153,27 +122,8 @@ export default function PortofolioEdit() {
|
||||
const response = await apiGetOnePortofolio({ id: id });
|
||||
|
||||
if (response.success) {
|
||||
// Extract phone number without country code for display
|
||||
const fullNumber = response.data.tlpn;
|
||||
let displayNumber = fullNumber;
|
||||
let detectedCountry = DEFAULT_COUNTRY;
|
||||
|
||||
// Try to detect country from calling code
|
||||
for (const country of COUNTRIES) {
|
||||
if (fullNumber.startsWith(country.callingCode)) {
|
||||
detectedCountry = country;
|
||||
displayNumber = fullNumber.substring(country.callingCode.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
setSelectedCountry(detectedCountry);
|
||||
|
||||
// Remove leading zero if present
|
||||
displayNumber = displayNumber.replace(/^0+/, "");
|
||||
|
||||
setPhoneNumber(displayNumber);
|
||||
setData({ ...response.data, tlpn: displayNumber });
|
||||
const fixNumber = response.data.tlpn.replace("62", "");
|
||||
setData({ ...response.data, tlpn: fixNumber });
|
||||
|
||||
// Cek apakah ada sub bidang bisnis yang terpilih
|
||||
const prevSubBidang = response.data.Portofolio_BidangDanSubBidangBisnis;
|
||||
@@ -294,11 +244,15 @@ export default function PortofolioEdit() {
|
||||
}
|
||||
|
||||
const handleSubmitUpdate = async () => {
|
||||
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
|
||||
let fixNumber = data.tlpn.replace(/\s+/g, "").replace(/^0+/, "");
|
||||
const realNumber = callingCode + fixNumber;
|
||||
|
||||
const newData: IFormData = {
|
||||
id_Portofolio: data.id_Portofolio,
|
||||
namaBisnis: data.namaBisnis,
|
||||
alamatKantor: data.alamatKantor,
|
||||
tlpn: data.tlpn, // Already formatted by PhoneInputCustom
|
||||
tlpn: realNumber,
|
||||
deskripsi: data.deskripsi,
|
||||
masterBidangBisnisId: data.masterBidangBisnisId,
|
||||
subBidang: listSubBidangSelected,
|
||||
@@ -481,11 +435,12 @@ export default function PortofolioEdit() {
|
||||
<Text style={{ color: "red" }}> *</Text>
|
||||
</View>
|
||||
<Spacing height={5} />
|
||||
<PhoneInputCustom
|
||||
value={phoneNumber}
|
||||
onChangePhoneNumber={handlePhoneChange}
|
||||
<PhoneInput
|
||||
value={data.tlpn}
|
||||
onChangePhoneNumber={handleInputValue}
|
||||
selectedCountry={selectedCountry}
|
||||
onChangeCountry={handleCountryChange}
|
||||
onChangeSelectedCountry={handleSelectedCountry}
|
||||
defaultCountry="ID"
|
||||
placeholder="xxx-xxx-xxx"
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import GridTwoView from "@/components/_ShareComponent/GridTwoView";
|
||||
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||
@@ -73,23 +72,20 @@ export default function Portofolio() {
|
||||
{/* Header */}
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Portofolio"
|
||||
left={<LeftButtonCustom />}
|
||||
right={
|
||||
data?.Profile?.id !== profileId ? null : (
|
||||
<TouchableOpacity onPress={openDrawer}>
|
||||
<Ionicons
|
||||
name="ellipsis-vertical"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: "Portofolio",
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () =>
|
||||
data?.Profile?.id !== profileId ? null : (
|
||||
<TouchableOpacity onPress={openDrawer}>
|
||||
<Ionicons
|
||||
name="ellipsis-vertical"
|
||||
size={20}
|
||||
color={MainColor.yellow}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
headerStyle: GStyles.headerStyle,
|
||||
headerTitleStyle: GStyles.headerTitleStyle,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import { HeaderStyles } from "@/styles/header-styles";
|
||||
import { Stack } from "expo-router";
|
||||
|
||||
export default function PortofolioLayout() {
|
||||
@@ -7,9 +7,8 @@ export default function PortofolioLayout() {
|
||||
<>
|
||||
<Stack
|
||||
screenOptions={{
|
||||
header: () => (
|
||||
<AppHeader title="Portofolio" left={<LeftButtonCustom />} />
|
||||
),
|
||||
...HeaderStyles,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
}}
|
||||
>
|
||||
{/* <Stack.Screen name="[id]/index" options={{ title: "Portofolio" }} /> */}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { NewWrapper, StackCustom } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||
import LeftButtonCustom from "@/components/Button/BackButton";
|
||||
import DrawerCustom from "@/components/Drawer/DrawerCustom";
|
||||
@@ -102,20 +101,18 @@ export default function Profile() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Profile"
|
||||
left={<LeftButtonCustom />}
|
||||
right={
|
||||
<ButtonnDot
|
||||
id={id as string}
|
||||
openDrawer={openDrawer}
|
||||
isUserCheck={isUserCheck()}
|
||||
logout={logout}
|
||||
/>
|
||||
}
|
||||
title: `Profile`,
|
||||
headerLeft: () => <LeftButtonCustom />,
|
||||
headerRight: () => (
|
||||
<ButtonnDot
|
||||
id={id as string}
|
||||
openDrawer={openDrawer}
|
||||
isUserCheck={isUserCheck()}
|
||||
logout={logout}
|
||||
/>
|
||||
),
|
||||
headerStyle: GStyles.headerStyle,
|
||||
headerTitleStyle: GStyles.headerTitleStyle,
|
||||
}}
|
||||
/>
|
||||
{/* Main View */}
|
||||
|
||||
@@ -1,39 +1,47 @@
|
||||
import { BackButton } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Stack } from "expo-router";
|
||||
|
||||
export default function ProfileLayout() {
|
||||
return (
|
||||
<>
|
||||
<Stack>
|
||||
<Stack
|
||||
screenOptions={{
|
||||
headerStyle: GStyles.headerStyle,
|
||||
headerTitleStyle: GStyles.headerTitleStyle,
|
||||
headerTitleAlign: "center",
|
||||
headerBackButtonDisplayMode: "minimal",
|
||||
}}
|
||||
>
|
||||
{/* <Stack.Screen name="[id]/index" options={{ headerShown: false }} /> */}
|
||||
<Stack.Screen
|
||||
name="[id]/edit"
|
||||
options={{ header: () => <AppHeader title="Edit Profile" /> }}
|
||||
options={{ title: "Edit Profile", headerLeft: () => <BackButton /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="[id]/update-photo"
|
||||
options={{ header: () => <AppHeader title="Update Foto" /> }}
|
||||
options={{ title: "Update Foto", headerLeft: () => <BackButton /> }}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="[id]/update-background"
|
||||
options={{
|
||||
header: () => <AppHeader title="Update Latar Belakang" />,
|
||||
title: "Update Latar Belakang",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="create"
|
||||
options={{ headerBackVisible: false }}
|
||||
options={{ title: "Buat Profile", headerBackVisible: false }}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="[id]/blocked-list"
|
||||
options={{ header: () => <AppHeader title="Daftar Blokir" /> }}
|
||||
options={{ title: "Daftar Blokir", headerLeft: () => <BackButton /> }}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="[id]/detail-blocked"
|
||||
options={{ header: () => <AppHeader title="Detail Blokir" /> }}
|
||||
options={{ title: "Detail Blokir", headerLeft: () => <BackButton /> }}
|
||||
/>
|
||||
</Stack>
|
||||
</>
|
||||
|
||||
@@ -4,34 +4,36 @@ import {
|
||||
IconHome,
|
||||
IconStatus,
|
||||
} from "@/components/_Icon";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import BackButtonFromNotification from "@/components/Button/BackButtonFromNotification";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { router, Tabs, useLocalSearchParams } from "expo-router";
|
||||
import { Tabs, useLocalSearchParams, useNavigation, router } from "expo-router";
|
||||
import { useLayoutEffect } from "react";
|
||||
|
||||
export default function VotingTabsLayout() {
|
||||
const navigation = useNavigation();
|
||||
|
||||
const { from, category } = useLocalSearchParams<{
|
||||
from?: string;
|
||||
category?: string;
|
||||
}>();
|
||||
|
||||
console.log("from", from);
|
||||
console.log("category", category);
|
||||
|
||||
// Atur header secara dinamis
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerLeft: () => (
|
||||
<BackButtonFromNotification
|
||||
from={from as string}
|
||||
category={category as string}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}, [from, router, navigation]);
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Voting"
|
||||
left={
|
||||
<BackButtonFromNotification
|
||||
from={from as string}
|
||||
category={category as string}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
TextCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconArchive, IconContribution, IconEdit } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import ReportBox from "@/components/Box/ReportBox";
|
||||
@@ -104,19 +103,14 @@ export default function VotingDetailStatus() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
),
|
||||
title: `Detail`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () =>
|
||||
status === "draft" ? (
|
||||
<DotButton onPress={() => setOpenDrawerDraft(true)} />
|
||||
) : status === "publish" ? (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
) : null,
|
||||
}}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconContribution } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
@@ -82,14 +81,10 @@ export default function VotingDetailContribution() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail Kontribusi"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
}
|
||||
/>
|
||||
title: "Detail Kontribusi",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Spacing,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconContribution } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
@@ -83,14 +82,10 @@ export default function VotingDetailHistory() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Riwayat Voting"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
}
|
||||
/>
|
||||
title: "Riwayat Voting",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
StackCustom,
|
||||
ViewWrapper,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconArchive, IconContribution } from "@/components/_Icon";
|
||||
import { IMenuDrawerItem } from "@/components/_Interface/types";
|
||||
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||
@@ -143,14 +142,10 @@ export default function VotingDetail() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Detail Voting"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
}
|
||||
/>
|
||||
title: `Detail Voting`,
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton onPress={() => setOpenDrawerPublish(true)} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { BackButton } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import BackgroundNotificationHandler from "@/components/Notification/BackgroundNotificationHandler";
|
||||
import NotificationInitializer from "@/components/Notification/NotificationInitializer";
|
||||
import { NotificationProvider } from "@/hooks/use-notification-store";
|
||||
import { HeaderStyles } from "@/styles/header-styles";
|
||||
import { Stack } from "expo-router";
|
||||
|
||||
export default function ApplicationLayout() {
|
||||
@@ -20,7 +20,7 @@ export default function ApplicationLayout() {
|
||||
function ApplicationStack() {
|
||||
return (
|
||||
<>
|
||||
<Stack>
|
||||
<Stack screenOptions={HeaderStyles}>
|
||||
<Stack.Screen name="(user)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="admin" options={{ headerShown: false }} />
|
||||
|
||||
@@ -28,7 +28,8 @@ function ApplicationStack() {
|
||||
<Stack.Screen
|
||||
name="(image)/take-picture/[id]/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Ambil Gambar" />,
|
||||
title: "Ambil Gambar",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -36,7 +37,8 @@ function ApplicationStack() {
|
||||
<Stack.Screen
|
||||
name="(image)/preview-image/[id]/index"
|
||||
options={{
|
||||
header: () => <AppHeader title="Preview Gambar" />,
|
||||
title: "Preview Gambar",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import DrawerAdmin from "@/components/Drawer/DrawerAdmin";
|
||||
import NavbarMenu from "@/components/Drawer/NavbarMenu";
|
||||
import NavbarMenu_V2 from "@/components/Drawer/NavbarMenu_V2";
|
||||
@@ -36,28 +35,12 @@ import { useState } from "react";
|
||||
export default function AdminLayout() {
|
||||
const [openDrawerNavbar, setOpenDrawerNavbar] = useState(false);
|
||||
const [openDrawerUser, setOpenDrawerUser] = useState(false);
|
||||
// const [user, setUser] = useState(null);
|
||||
|
||||
const { logout, user } = useAuth();
|
||||
|
||||
console.log("[USER LAYOUT]", JSON.stringify(user, null, 2));
|
||||
|
||||
const headerLeft = () => (
|
||||
<Ionicons
|
||||
name="menu"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.white}
|
||||
onPress={() => setOpenDrawerNavbar(true)}
|
||||
/>
|
||||
);
|
||||
|
||||
const headerRight = () => (
|
||||
<FontAwesome6
|
||||
name="circle-user"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={MainColor.white}
|
||||
onPress={() => setOpenDrawerUser(true)}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack
|
||||
@@ -69,33 +52,20 @@ export default function AdminLayout() {
|
||||
contentStyle: {
|
||||
borderBottomColor: AccentColor.blue,
|
||||
},
|
||||
|
||||
// headerLeft: () => (
|
||||
// <Ionicons
|
||||
// name="menu"
|
||||
// size={ICON_SIZE_XLARGE}
|
||||
// color={MainColor.white}
|
||||
// onPress={() => setOpenDrawerNavbar(true)}
|
||||
// />
|
||||
// ),
|
||||
// headerRight: () => (
|
||||
// <FontAwesome6
|
||||
// name="circle-user"
|
||||
// size={ICON_SIZE_MEDIUM}
|
||||
// color={MainColor.white}
|
||||
// onPress={() => setOpenDrawerUser(true)}
|
||||
// />
|
||||
// ),
|
||||
|
||||
|
||||
|
||||
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="HIPMI DASHBOARD"
|
||||
showBack={false}
|
||||
left={headerLeft()}
|
||||
right={headerRight()}
|
||||
headerLeft: () => (
|
||||
<Ionicons
|
||||
name="menu"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.white}
|
||||
onPress={() => setOpenDrawerNavbar(true)}
|
||||
/>
|
||||
),
|
||||
headerRight: () => (
|
||||
<FontAwesome6
|
||||
name="circle-user"
|
||||
size={ICON_SIZE_MEDIUM}
|
||||
color={MainColor.white}
|
||||
onPress={() => setOpenDrawerUser(true)}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { BackButton, StackCustom, TextCustom, ViewWrapper } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { router, Stack } from "expo-router";
|
||||
|
||||
export default function NotFoundScreen() {
|
||||
@@ -16,7 +15,7 @@ export default function NotFoundScreen() {
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{ header: () => <AppHeader title="" left={<BackButton onPress={() => handleBack()} />} /> }}
|
||||
options={{ headerShown: true, title: "", headerLeft: () => <BackButton onPress={() => handleBack()} /> }}
|
||||
/>
|
||||
<ViewWrapper>
|
||||
<StackCustom
|
||||
|
||||
3
bun.lock
3
bun.lock
@@ -41,7 +41,6 @@
|
||||
"expo-symbols": "~1.0.7",
|
||||
"expo-system-ui": "~6.0.7",
|
||||
"expo-web-browser": "~15.0.9",
|
||||
"libphonenumber-js": "^1.12.40",
|
||||
"lodash": "^4.17.21",
|
||||
"moti": "^0.30.0",
|
||||
"react": "19.1.0",
|
||||
@@ -1773,8 +1772,6 @@
|
||||
|
||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||
|
||||
"libphonenumber-js": ["libphonenumber-js@1.12.40", "", {}, "sha512-HKGs7GowShNls3Zh+7DTr6wYpPk5jC78l508yQQY3e8ZgJChM3A9JZghmMJZuK+5bogSfuTafpjksGSR3aMIEg=="],
|
||||
|
||||
"lighthouse-logger": ["lighthouse-logger@1.4.2", "", { "dependencies": { "debug": "^2.6.9", "marky": "^1.2.2" } }, "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g=="],
|
||||
|
||||
"lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="],
|
||||
|
||||
@@ -12,15 +12,14 @@ export default function BackButtonFromNotification({
|
||||
return (
|
||||
<>
|
||||
<BackButton
|
||||
|
||||
onPress={() => {
|
||||
if (from === "notifications") {
|
||||
router.push(`/notifications?category=${category}`);
|
||||
router.replace(`/notifications?category=${category}`);
|
||||
} else {
|
||||
if (from) {
|
||||
router.back();
|
||||
router.replace(`/${from}` as any);
|
||||
} else {
|
||||
router.back();
|
||||
router.navigate("/home");
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import {
|
||||
DEFAULT_COUNTRY,
|
||||
searchCountries,
|
||||
type CountryData,
|
||||
} from "@/constants/countries";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Modal,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from "react-native";
|
||||
|
||||
interface PhoneInputProps {
|
||||
value: string;
|
||||
onChangePhoneNumber: (phone: string) => void;
|
||||
selectedCountry?: CountryData;
|
||||
onChangeCountry: (country: CountryData) => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export default function PhoneInputCustom({
|
||||
value,
|
||||
onChangePhoneNumber,
|
||||
selectedCountry = DEFAULT_COUNTRY,
|
||||
onChangeCountry,
|
||||
placeholder = "Masukkan nomor",
|
||||
disabled = false,
|
||||
}: PhoneInputProps) {
|
||||
const [countryPickerVisible, setCountryPickerVisible] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const filteredCountries = searchCountries(searchQuery);
|
||||
|
||||
const handleSelectCountry = (country: CountryData) => {
|
||||
onChangeCountry(country);
|
||||
setCountryPickerVisible(false);
|
||||
setSearchQuery("");
|
||||
};
|
||||
|
||||
const handlePhoneChange = (text: string) => {
|
||||
// Only allow numbers and spaces
|
||||
const cleaned = text.replace(/[^\d\s]/g, "");
|
||||
onChangePhoneNumber(cleaned);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Phone Input Field */}
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity
|
||||
style={styles.countryPickerButton}
|
||||
onPress={() => setCountryPickerVisible(true)}
|
||||
disabled={disabled}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<Text style={styles.countryCodeText}>+{selectedCountry.callingCode}</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<View style={styles.divider} />
|
||||
|
||||
<TextInput
|
||||
style={[styles.phoneInput, disabled && styles.disabledInput]}
|
||||
placeholder={placeholder}
|
||||
placeholderTextColor={MainColor.placeholder}
|
||||
value={value}
|
||||
onChangeText={handlePhoneChange}
|
||||
keyboardType="phone-pad"
|
||||
autoComplete="tel"
|
||||
importantForAutofill="yes"
|
||||
editable={!disabled}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* Country Picker Modal */}
|
||||
<Modal
|
||||
visible={countryPickerVisible}
|
||||
transparent
|
||||
animationType="slide"
|
||||
onRequestClose={() => setCountryPickerVisible(false)}
|
||||
>
|
||||
<View style={styles.modalOverlay}>
|
||||
<View style={styles.modalContent}>
|
||||
<View style={styles.modalHeader}>
|
||||
<Text style={styles.modalTitle}>Pilih Negara</Text>
|
||||
<TouchableOpacity onPress={() => setCountryPickerVisible(false)}>
|
||||
<Text style={styles.modalClose}>✕</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<TextInput
|
||||
style={styles.searchInput}
|
||||
placeholder="Cari negara atau kode..."
|
||||
placeholderTextColor={MainColor.placeholder}
|
||||
value={searchQuery}
|
||||
onChangeText={setSearchQuery}
|
||||
autoFocus
|
||||
/>
|
||||
|
||||
<ScrollView style={styles.countryList}>
|
||||
{filteredCountries.map((country) => (
|
||||
<TouchableOpacity
|
||||
key={country.code}
|
||||
style={[
|
||||
styles.countryItem,
|
||||
selectedCountry.code === country.code &&
|
||||
styles.countryItemSelected,
|
||||
]}
|
||||
onPress={() => handleSelectCountry(country)}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.countryInfo}>
|
||||
<Text style={styles.countryName}>{country.name}</Text>
|
||||
<Text style={styles.countryCode}>+{country.callingCode}</Text>
|
||||
</View>
|
||||
{selectedCountry.code === country.code && (
|
||||
<Text style={styles.checkmark}>✓</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
</View>
|
||||
</View>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
// Container
|
||||
container: {
|
||||
flexDirection: "row",
|
||||
backgroundColor: MainColor.white,
|
||||
borderRadius: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: MainColor.white_gray,
|
||||
marginBottom: 16,
|
||||
overflow: "hidden",
|
||||
},
|
||||
// Country Picker Button
|
||||
countryPickerButton: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 14,
|
||||
backgroundColor: MainColor.text_input,
|
||||
borderRightWidth: 1,
|
||||
borderRightColor: MainColor.white_gray,
|
||||
},
|
||||
countryCodeText: {
|
||||
fontSize: 16,
|
||||
color: MainColor.black,
|
||||
fontWeight: "600",
|
||||
},
|
||||
dropdownIcon: {
|
||||
fontSize: 18,
|
||||
color: MainColor.placeholder,
|
||||
marginLeft: 4,
|
||||
},
|
||||
// Divider
|
||||
divider: {
|
||||
width: 1,
|
||||
backgroundColor: MainColor.white_gray,
|
||||
},
|
||||
// Phone Input
|
||||
phoneInput: {
|
||||
flex: 1,
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 12,
|
||||
fontSize: 16,
|
||||
color: MainColor.black,
|
||||
},
|
||||
disabledInput: {
|
||||
backgroundColor: MainColor.text_input,
|
||||
color: MainColor.placeholder,
|
||||
},
|
||||
// Modal
|
||||
modalOverlay: {
|
||||
flex: 1,
|
||||
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
||||
justifyContent: "flex-end",
|
||||
},
|
||||
modalContent: {
|
||||
backgroundColor: MainColor.white,
|
||||
borderTopLeftRadius: 20,
|
||||
borderTopRightRadius: 20,
|
||||
maxHeight: "80%",
|
||||
paddingBottom: 34,
|
||||
},
|
||||
modalHeader: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: 20,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: MainColor.white_gray,
|
||||
},
|
||||
modalTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: "bold",
|
||||
color: MainColor.black,
|
||||
},
|
||||
modalClose: {
|
||||
fontSize: 24,
|
||||
color: MainColor.placeholder,
|
||||
padding: 5,
|
||||
},
|
||||
// Search Input
|
||||
searchInput: {
|
||||
backgroundColor: MainColor.text_input,
|
||||
margin: 16,
|
||||
padding: 12,
|
||||
borderRadius: 8,
|
||||
fontSize: 16,
|
||||
color: MainColor.black,
|
||||
},
|
||||
// Country List
|
||||
countryList: {
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
countryItem: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 12,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: MainColor.white_gray,
|
||||
},
|
||||
countryItemSelected: {
|
||||
backgroundColor: MainColor.soft_darkblue + "15",
|
||||
},
|
||||
countryInfo: {
|
||||
flex: 1,
|
||||
},
|
||||
countryName: {
|
||||
fontSize: 16,
|
||||
color: MainColor.black,
|
||||
fontWeight: "500",
|
||||
},
|
||||
countryCode: {
|
||||
fontSize: 14,
|
||||
color: MainColor.placeholder,
|
||||
marginTop: 2,
|
||||
},
|
||||
checkmark: {
|
||||
fontSize: 20,
|
||||
color: MainColor.green,
|
||||
fontWeight: "bold",
|
||||
},
|
||||
});
|
||||
@@ -1,114 +0,0 @@
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { Platform, StyleSheet, Text, View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { BackButton } from "..";
|
||||
|
||||
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();
|
||||
|
||||
// iOS 16+ detection (Dynamic Island) - insets.top > 47 indicates Dynamic Island
|
||||
const isIOS26Plus =
|
||||
Platform.OS === "ios" && insets.top > 47;
|
||||
|
||||
// Dynamic padding berdasarkan platform dan iOS version
|
||||
const paddingTop =
|
||||
Platform.OS === "ios"
|
||||
? isIOS26Plus
|
||||
? insets.top - 10
|
||||
: insets.top
|
||||
: 10;
|
||||
|
||||
const paddingBottom = Platform.OS === "ios" ? 8 : 13;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
{
|
||||
backgroundColor: MainColor.darkblue,
|
||||
paddingTop,
|
||||
paddingBottom,
|
||||
},
|
||||
]}
|
||||
pointerEvents="box-none"
|
||||
>
|
||||
{/* Header Container dengan absolute positioning untuk title center */}
|
||||
<View style={styles.headerApp} pointerEvents="box-none">
|
||||
{/* Left Section - Absolute Left */}
|
||||
<View style={styles.headerLeft}>
|
||||
{showBack ? (
|
||||
<BackButton onPress={onPressLeft} />
|
||||
) : left ? (
|
||||
left
|
||||
) : (
|
||||
<View style={styles.placeholder} />
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* Title - Absolute Center */}
|
||||
<View style={styles.headerCenter}>
|
||||
<Text
|
||||
style={styles.headerTitle}
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
{title ? title.charAt(0).toUpperCase() + title.slice(1) : ""}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Right Section - Absolute Right */}
|
||||
<View style={styles.headerRight}>
|
||||
{right}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
headerApp: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: 16,
|
||||
height: 44, // Fixed height untuk consistency
|
||||
},
|
||||
headerLeft: {
|
||||
position: "absolute",
|
||||
left: 16,
|
||||
zIndex: 1,
|
||||
},
|
||||
headerCenter: {
|
||||
flex: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
headerRight: {
|
||||
position: "absolute",
|
||||
right: 16,
|
||||
zIndex: 1,
|
||||
},
|
||||
placeholder: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
},
|
||||
headerTitle: {
|
||||
color: MainColor.yellow,
|
||||
fontSize: 18,
|
||||
fontWeight: "600",
|
||||
textAlign: "center",
|
||||
},
|
||||
});
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
SafeAreaView,
|
||||
} from "react-native-safe-area-context";
|
||||
import type { ScrollViewProps, FlatListProps } from "react-native";
|
||||
import Spacing from "./Spacing";
|
||||
|
||||
// --- ✅ Tambahkan refreshControl ke BaseProps ---
|
||||
interface BaseProps {
|
||||
@@ -84,7 +83,7 @@ const NewWrapper = (props: NewWrapperProps) => {
|
||||
return <View style={[GStyles.container, style]}>{content}</View>;
|
||||
};
|
||||
|
||||
// 🔹 Mode Dinamis (FlatList)
|
||||
// 🔹 Mode Dinamis
|
||||
if ("listData" in props) {
|
||||
const listProps = props as ListModeProps;
|
||||
|
||||
@@ -96,7 +95,7 @@ const NewWrapper = (props: NewWrapperProps) => {
|
||||
{headerComponent && (
|
||||
<View style={GStyles.stickyHeader}>{headerComponent}</View>
|
||||
)}
|
||||
<View style={[GStyles.container, style, { flex: 1 }]}>
|
||||
<View style={[GStyles.container, style]}>
|
||||
<FlatList
|
||||
data={listProps.listData}
|
||||
renderItem={listProps.renderItem}
|
||||
@@ -108,36 +107,30 @@ const NewWrapper = (props: NewWrapperProps) => {
|
||||
return `fallback-${index}-${JSON.stringify(item)}`;
|
||||
}
|
||||
|
||||
// Gabungkan ID dengan indeks untuk mencegah duplikasi
|
||||
return `${String(item.id)}-${index}`;
|
||||
})
|
||||
}
|
||||
refreshControl={refreshControl}
|
||||
|
||||
refreshControl={refreshControl} // ✅ dari BaseProps
|
||||
onEndReached={listProps.onEndReached}
|
||||
onEndReachedThreshold={0.5}
|
||||
ListHeaderComponent={listProps.ListHeaderComponent}
|
||||
ListFooterComponent={listProps.ListFooterComponent}
|
||||
ListEmptyComponent={listProps.ListEmptyComponent}
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
paddingBottom: footerComponent && !hideFooter ? OS_HEIGHT : 0
|
||||
}}
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
/>
|
||||
</View>
|
||||
|
||||
{/* Footer dengan position absolute untuk stay di bawah */}
|
||||
{footerComponent && !hideFooter && (
|
||||
<View style={styles.footerContainer}>
|
||||
<SafeAreaView
|
||||
edges={Platform.OS === "ios" ? edgesFooter : ["bottom"]}
|
||||
style={{ backgroundColor: MainColor.darkblue }}
|
||||
>
|
||||
{footerComponent}
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{!footerComponent && !hideFooter && (
|
||||
{footerComponent ? (
|
||||
<SafeAreaView
|
||||
edges={Platform.OS === "ios" ? edgesFooter : ["bottom"]}
|
||||
style={{ backgroundColor: MainColor.darkblue, height: OS_HEIGHT }}
|
||||
>
|
||||
{footerComponent}
|
||||
</SafeAreaView>
|
||||
) : hideFooter ? null : (
|
||||
<SafeAreaView
|
||||
edges={["bottom"]}
|
||||
style={{ backgroundColor: MainColor.darkblue }}
|
||||
@@ -151,7 +144,7 @@ const NewWrapper = (props: NewWrapperProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
// 🔹 Mode Statis (ScrollView)
|
||||
// 🔹 Mode Statis
|
||||
const staticProps = props as StaticModeProps;
|
||||
|
||||
return (
|
||||
@@ -163,34 +156,24 @@ const NewWrapper = (props: NewWrapperProps) => {
|
||||
<View style={GStyles.stickyHeader}>{headerComponent}</View>
|
||||
)}
|
||||
|
||||
<View style={{ flex: 0 }} collapsable={false}>
|
||||
<ScrollView
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
paddingBottom: footerComponent && !hideFooter ? OS_HEIGHT : 0
|
||||
}}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
refreshControl={refreshControl}
|
||||
<ScrollView
|
||||
contentContainerStyle={{ flexGrow: 1 }}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
refreshControl={refreshControl} // ✅ sekarang valid
|
||||
>
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
||||
{renderContainer(staticProps.children)}
|
||||
</TouchableWithoutFeedback>
|
||||
</ScrollView>
|
||||
|
||||
{footerComponent ? (
|
||||
<SafeAreaView
|
||||
edges={Platform.OS === "ios" ? edgesFooter : ["bottom"]}
|
||||
style={{ backgroundColor: MainColor.darkblue, height: OS_HEIGHT }}
|
||||
>
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
||||
{renderContainer(staticProps.children)}
|
||||
</TouchableWithoutFeedback>
|
||||
</ScrollView>
|
||||
</View>
|
||||
|
||||
{/* Footer dengan position absolute untuk stay di bawah */}
|
||||
{footerComponent && !hideFooter && (
|
||||
<View style={styles.footerContainer}>
|
||||
<SafeAreaView
|
||||
edges={Platform.OS === "ios" ? edgesFooter : ["bottom"]}
|
||||
style={{ backgroundColor: MainColor.darkblue }}
|
||||
>
|
||||
{footerComponent}
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{!footerComponent && !hideFooter && (
|
||||
{footerComponent}
|
||||
</SafeAreaView>
|
||||
) : hideFooter ? null : (
|
||||
<SafeAreaView
|
||||
edges={["bottom"]}
|
||||
style={{ backgroundColor: MainColor.darkblue }}
|
||||
@@ -204,15 +187,4 @@ const NewWrapper = (props: NewWrapperProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
// Styles untuk footer dengan position absolute
|
||||
const styles = {
|
||||
footerContainer: {
|
||||
position: "absolute" as const,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
backgroundColor: MainColor.darkblue,
|
||||
},
|
||||
};
|
||||
|
||||
export default NewWrapper;
|
||||
|
||||
@@ -49,8 +49,6 @@ import MapCustom from "./Map/MapCustom";
|
||||
import CenterCustom from "./Center/CenterCustom";
|
||||
// Clickable
|
||||
import ClickableCustom from "./Clickable/ClickableCustom";
|
||||
// PhoneInput
|
||||
import PhoneInputCustom from "./PhoneInput/PhoneInputCustom";
|
||||
// Scroll
|
||||
import ScrollableCustom from "./Scroll/ScrollCustom";
|
||||
// ShareComponent
|
||||
@@ -97,8 +95,6 @@ export {
|
||||
CheckboxGroup,
|
||||
// Clickable
|
||||
ClickableCustom,
|
||||
// PhoneInput
|
||||
PhoneInputCustom,
|
||||
// Container
|
||||
CircleContainer,
|
||||
// Divider
|
||||
|
||||
@@ -24,7 +24,7 @@ export {
|
||||
|
||||
// OS Height
|
||||
const OS_ANDROID_HEIGHT = 115
|
||||
const OS_IOS_HEIGHT = 80
|
||||
const OS_IOS_HEIGHT = 90
|
||||
const OS_HEIGHT = Platform.OS === "ios" ? OS_IOS_HEIGHT : OS_ANDROID_HEIGHT
|
||||
|
||||
// Text Size
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
import { type CountryCode } from "libphonenumber-js";
|
||||
|
||||
/**
|
||||
* Country data for phone number input
|
||||
* Contains only country name and calling code (NO flags for maximum compatibility)
|
||||
*/
|
||||
export interface CountryData {
|
||||
code: CountryCode;
|
||||
name: string;
|
||||
callingCode: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of supported countries for phone number input
|
||||
*
|
||||
* @description
|
||||
* This list includes major countries across different regions.
|
||||
* Countries are ordered by likelihood of use (Indonesia first as default).
|
||||
*
|
||||
* @note
|
||||
* NO emoji flags used - only text-based country name and calling code
|
||||
* This ensures maximum compatibility across all platforms and iOS versions
|
||||
*/
|
||||
export const COUNTRIES: CountryData[] = [
|
||||
// Asia Pacific (Primary markets)
|
||||
{ code: "ID", name: "Indonesia", callingCode: "62" },
|
||||
{ code: "SG", name: "Singapore", callingCode: "65" },
|
||||
{ code: "MY", name: "Malaysia", callingCode: "60" },
|
||||
{ code: "AU", name: "Australia", callingCode: "61" },
|
||||
|
||||
// Asia (Other)
|
||||
{ code: "CN", name: "China", callingCode: "86" },
|
||||
{ code: "JP", name: "Japan", callingCode: "81" },
|
||||
{ code: "KR", name: "South Korea", callingCode: "82" },
|
||||
{ code: "IN", name: "India", callingCode: "91" },
|
||||
|
||||
// Middle East
|
||||
{ code: "AE", name: "United Arab Emirates", callingCode: "971" },
|
||||
{ code: "SA", name: "Saudi Arabia", callingCode: "966" },
|
||||
|
||||
// Europe
|
||||
{ code: "GB", name: "United Kingdom", callingCode: "44" },
|
||||
{ code: "DE", name: "Germany", callingCode: "49" },
|
||||
{ code: "FR", name: "France", callingCode: "33" },
|
||||
{ code: "NL", name: "Netherlands", callingCode: "31" },
|
||||
|
||||
// Americas
|
||||
{ code: "US", name: "United States", callingCode: "1" },
|
||||
];
|
||||
|
||||
/**
|
||||
* Default country for phone number input
|
||||
* Used when no country is selected (Indonesia by default)
|
||||
*/
|
||||
export const DEFAULT_COUNTRY: CountryData = COUNTRIES[0];
|
||||
|
||||
/**
|
||||
* Get country by calling code
|
||||
* @param callingCode - The calling code to search for (e.g., "62", "1")
|
||||
* @returns The matching country data or undefined if not found
|
||||
*/
|
||||
export function getCountryByCallingCode(callingCode: string): CountryData | undefined {
|
||||
return COUNTRIES.find((country) => country.callingCode === callingCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get country by country code (ISO 3166-1 alpha-2)
|
||||
* @param code - The country code to search for (e.g., "ID", "US")
|
||||
* @returns The matching country data or undefined if not found
|
||||
*/
|
||||
export function getCountryByCode(code: CountryCode): CountryData | undefined {
|
||||
return COUNTRIES.find((country) => country.code === code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search countries by name or calling code
|
||||
* @param query - The search query (case-insensitive)
|
||||
* @returns Array of matching countries
|
||||
*/
|
||||
export function searchCountries(query: string): CountryData[] {
|
||||
const normalizedQuery = query.toLowerCase().trim();
|
||||
|
||||
return COUNTRIES.filter(
|
||||
(country) =>
|
||||
country.name.toLowerCase().includes(normalizedQuery) ||
|
||||
country.code.toLowerCase().includes(normalizedQuery) ||
|
||||
country.callingCode.includes(normalizedQuery)
|
||||
);
|
||||
}
|
||||
@@ -55,10 +55,10 @@ Component yang digunakan: components/_ShareComponent/NewWrapper.tsx
|
||||
|
||||
<!-- START Prompt Admin Refactoring -->
|
||||
<!-- Pindah kode ke Screen Component -->
|
||||
File source: app/(application)/(user)/portofolio/[id]/create.tsx
|
||||
Folder tujuan: screens/Portofolio
|
||||
Nama file utama: ScreenPortofolioCreate.tsx
|
||||
Nama function utama: Admin_ScreenPortofolioCreate
|
||||
File source: app/(application)/admin/event/[id]/[status]/index.tsx
|
||||
Folder tujuan: screens/Admin/Event
|
||||
Nama file utama: ScreenEventDetail.tsx
|
||||
Nama function utama: Admin_ScreenEventDetail
|
||||
File komponen wrapper: components/_ShareComponent/NewWrapper.tsx
|
||||
|
||||
Buat file baru pada "Folder tujuan" dengan nama "Nama file utama" dan ubah nama function menjadi "Nama function utama" kemudian clean code, import dan panggil function tersebut pada file "File source"
|
||||
@@ -120,9 +120,4 @@ Buatkan file baru pada "Folder tujuan" dengan nama "Nama file utama" dan ubah na
|
||||
|
||||
<!-- END Create Box -->
|
||||
|
||||
<!-- Random Prompt -->
|
||||
Diskusi pada file screens/Authentication/LoginView.tsx , tentang penggunaan phone number input. Karena tidak berfungsi dengan baik pada versi ios 26 keatas
|
||||
|
||||
<!-- END Random Prompt -->
|
||||
|
||||
<!-- END Use Prompt Now -->
|
||||
|
||||
@@ -189,20 +189,6 @@
|
||||
0D62979D96BF4B99AB9FBE7C /* Remove signature files (Xcode workaround) */,
|
||||
49B80EF12BE8476C86534CEA /* Remove signature files (Xcode workaround) */,
|
||||
6218417B3C954EFF9B5F4853 /* Remove signature files (Xcode workaround) */,
|
||||
7B5AE3770142492D84AEAAB3 /* Remove signature files (Xcode workaround) */,
|
||||
0F1E3753571D42AB932C4F72 /* Remove signature files (Xcode workaround) */,
|
||||
B020BDE42E304FBD99BD2279 /* Remove signature files (Xcode workaround) */,
|
||||
6ECA5F81B0BC4C70A91BE265 /* Remove signature files (Xcode workaround) */,
|
||||
44265583B67C48F2A24BA93E /* Remove signature files (Xcode workaround) */,
|
||||
D5CA1D54CFF74AB4B8B5B583 /* Remove signature files (Xcode workaround) */,
|
||||
97C01196E2194AF5A13C7773 /* Remove signature files (Xcode workaround) */,
|
||||
EB19F4C53C8B434CBAD50897 /* Remove signature files (Xcode workaround) */,
|
||||
95ABFC1FE48F4F2ABAF407D8 /* Remove signature files (Xcode workaround) */,
|
||||
A8B22025FD1A438684F239A7 /* Remove signature files (Xcode workaround) */,
|
||||
1041145746B64EBB994D41F1 /* Remove signature files (Xcode workaround) */,
|
||||
A6E85C04CEFE4EB1BEC42A8F /* Remove signature files (Xcode workaround) */,
|
||||
90185735FF1241C998928089 /* Remove signature files (Xcode workaround) */,
|
||||
7FC9CE1604F2440EBA9E61D6 /* Remove signature files (Xcode workaround) */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -1117,244 +1103,6 @@
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
7B5AE3770142492D84AEAAB3 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
0F1E3753571D42AB932C4F72 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
B020BDE42E304FBD99BD2279 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
6ECA5F81B0BC4C70A91BE265 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
44265583B67C48F2A24BA93E /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
D5CA1D54CFF74AB4B8B5B583 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
97C01196E2194AF5A13C7773 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
EB19F4C53C8B434CBAD50897 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
95ABFC1FE48F4F2ABAF407D8 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
A8B22025FD1A438684F239A7 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
1041145746B64EBB994D41F1 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
A6E85C04CEFE4EB1BEC42A8F /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
90185735FF1241C998928089 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
7FC9CE1604F2440EBA9E61D6 /* Remove signature files (Xcode workaround) */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Remove signature files (Xcode workaround)";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "
|
||||
echo \"Remove signature files (Xcode workaround)\";
|
||||
rm -rf \"$CONFIGURATION_BUILD_DIR/MapLibre.xcframework-ios.signature\";
|
||||
";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>7</string>
|
||||
<string>4</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
"expo-symbols": "~1.0.7",
|
||||
"expo-system-ui": "~6.0.7",
|
||||
"expo-web-browser": "~15.0.9",
|
||||
"libphonenumber-js": "^1.12.40",
|
||||
"lodash": "^4.17.21",
|
||||
"moti": "^0.30.0",
|
||||
"react": "19.1.0",
|
||||
|
||||
@@ -128,13 +128,12 @@ export function Admin_ScreenEventDetail() {
|
||||
);
|
||||
}
|
||||
|
||||
return <Spacing height={100} />;
|
||||
return null;
|
||||
}, [status, id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<NewWrapper
|
||||
hideFooter
|
||||
headerComponent={headerComponent}
|
||||
// footerComponent={
|
||||
// <View style={{ paddingInline: 8 }}>
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import { IconDot } from "@/components/_Icon/IconComponent";
|
||||
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||
@@ -122,16 +121,12 @@ export default function Admin_ScreenNotification() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Admin Notifikasi"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<IconDot
|
||||
color={MainColor.yellow}
|
||||
onPress={() => setOpenDrawer(true)}
|
||||
/>
|
||||
}
|
||||
title: "Admin Notifikasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<IconDot
|
||||
color={MainColor.yellow}
|
||||
onPress={() => setOpenDrawer(true)}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
} from "@/components";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import { IconDot } from "@/components/_Icon/IconComponent";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_SMALL, PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
@@ -170,16 +169,12 @@ export default function Admin_ScreenNotification2() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
// title: "Admin Notifikasi",
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Admin Notifikasi"
|
||||
right={
|
||||
<IconDot
|
||||
color={MainColor.yellow}
|
||||
onPress={() => setOpenDrawer(true)}
|
||||
/>
|
||||
}
|
||||
title: "Admin Notifikasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<IconDot
|
||||
color={MainColor.yellow}
|
||||
onPress={() => setOpenDrawer(true)}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { NewWrapper, PhoneInputCustom, ViewWrapper } from "@/components";
|
||||
import { NewWrapper } from "@/components";
|
||||
import ButtonCustom from "@/components/Button/ButtonCustom";
|
||||
import ModalReactNative from "@/components/Modal/ModalReactNative";
|
||||
import Spacing from "@/components/_ShareComponent/Spacing";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { DEFAULT_COUNTRY, type CountryData } from "@/constants/countries";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiVersion, BASE_URL } from "@/service/api-config";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
@@ -11,16 +10,16 @@ import { openBrowser } from "@/utils/openBrower";
|
||||
import versionBadge from "@/utils/viersionBadge";
|
||||
import { Redirect } from "expo-router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { KeyboardAvoidingView, Platform, RefreshControl, Text, View } from "react-native";
|
||||
import { parsePhoneNumber } from "libphonenumber-js";
|
||||
import { RefreshControl, Text, View } from "react-native";
|
||||
import PhoneInput, { ICountry } from "react-native-international-phone-number";
|
||||
import Toast from "react-native-toast-message";
|
||||
import EULASection from "./EULASection";
|
||||
|
||||
export default function LoginView() {
|
||||
const url = BASE_URL;
|
||||
const [version, setVersion] = useState<string>("");
|
||||
const [selectedCountry, setSelectedCountry] = useState<CountryData>(DEFAULT_COUNTRY);
|
||||
const [phoneNumber, setPhoneNumber] = useState<string>("");
|
||||
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
|
||||
const [inputValue, setInputValue] = useState<string>("");
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [refreshing, setRefreshing] = useState<boolean>(false);
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
@@ -44,40 +43,38 @@ export default function LoginView() {
|
||||
async function handleRefresh() {
|
||||
setRefreshing(true);
|
||||
await onLoadVersion();
|
||||
setPhoneNumber("");
|
||||
setSelectedCountry(DEFAULT_COUNTRY);
|
||||
setInputValue("");
|
||||
setLoading(false);
|
||||
setRefreshing(false);
|
||||
}
|
||||
|
||||
function handleInputValue(phoneNumber: string) {
|
||||
setInputValue(phoneNumber);
|
||||
}
|
||||
|
||||
function handleSelectedCountry(country: ICountry) {
|
||||
setSelectedCountry(country);
|
||||
}
|
||||
|
||||
async function validateData() {
|
||||
if (phoneNumber.length === 0) {
|
||||
if (inputValue.length === 0) {
|
||||
return Toast.show({
|
||||
type: "error",
|
||||
text1: "Masukan nomor anda",
|
||||
});
|
||||
}
|
||||
|
||||
if (phoneNumber.length < 9) {
|
||||
if (selectedCountry === null) {
|
||||
return Toast.show({
|
||||
type: "error",
|
||||
text1: "Nomor tidak valid",
|
||||
text1: "Pilih negara",
|
||||
});
|
||||
}
|
||||
|
||||
// Validate with libphonenumber-js
|
||||
try {
|
||||
const parsedNumber = parsePhoneNumber(phoneNumber, selectedCountry.code);
|
||||
if (!parsedNumber || !parsedNumber.isValid()) {
|
||||
return Toast.show({
|
||||
type: "error",
|
||||
text1: "Nomor tidak valid",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
if (inputValue.length < 9) {
|
||||
return Toast.show({
|
||||
type: "error",
|
||||
text1: "Format nomor tidak valid",
|
||||
text1: "Nomor tidak valid",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,17 +85,8 @@ export default function LoginView() {
|
||||
const isValid = await validateData();
|
||||
if (!isValid) return;
|
||||
|
||||
// Format phone number with country code
|
||||
const callingCode = selectedCountry.callingCode;
|
||||
let fixNumber = phoneNumber.replace(/\s+/g, "").replace(/^0+/, "");
|
||||
|
||||
// Remove country code if already present
|
||||
if (fixNumber.startsWith(callingCode)) {
|
||||
fixNumber = fixNumber.substring(callingCode.length);
|
||||
}
|
||||
|
||||
// Remove leading zero
|
||||
fixNumber = fixNumber.replace(/^0+/, "");
|
||||
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
|
||||
let fixNumber = inputValue.replace(/\s+/g, "").replace(/^0+/, "");
|
||||
|
||||
const realNumber = callingCode + fixNumber;
|
||||
|
||||
@@ -140,82 +128,75 @@ export default function LoginView() {
|
||||
}
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
behavior={Platform.OS === "ios" ? "padding" : "height"}
|
||||
keyboardVerticalOffset={Platform.OS === "ios" ? 100 : 50}
|
||||
style={{ flex: 1 }}
|
||||
<NewWrapper
|
||||
withBackground
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
|
||||
}
|
||||
>
|
||||
<ViewWrapper
|
||||
withBackground
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
|
||||
}
|
||||
>
|
||||
<View style={[GStyles.authContainer, { paddingBottom: 40 }]}>
|
||||
<View>
|
||||
<View style={GStyles.authContainerTitle}>
|
||||
<Text style={GStyles.authSubTitle}>WELCOME TO</Text>
|
||||
<Spacing height={5} />
|
||||
<Text style={GStyles.authTitle}>HIPMI BADUNG APPS</Text>
|
||||
<Spacing height={5} />
|
||||
</View>
|
||||
<Spacing height={50} />
|
||||
<Text
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 35,
|
||||
right: 50,
|
||||
fontSize: 10,
|
||||
fontWeight: "thin",
|
||||
fontStyle: "italic",
|
||||
color: MainColor.white_gray,
|
||||
}}
|
||||
>
|
||||
{version} | powered by muku.id
|
||||
</Text>
|
||||
<View style={GStyles.authContainer}>
|
||||
<View>
|
||||
<View style={GStyles.authContainerTitle}>
|
||||
<Text style={GStyles.authSubTitle}>WELCOME TO</Text>
|
||||
<Spacing height={5} />
|
||||
<Text style={GStyles.authTitle}>HIPMI BADUNG APPS</Text>
|
||||
<Spacing height={5} />
|
||||
</View>
|
||||
|
||||
<Spacing height={20} />
|
||||
|
||||
<PhoneInputCustom
|
||||
value={phoneNumber}
|
||||
onChangePhoneNumber={setPhoneNumber}
|
||||
selectedCountry={selectedCountry}
|
||||
onChangeCountry={setSelectedCountry}
|
||||
placeholder="Masukkan nomor"
|
||||
/>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<ButtonCustom
|
||||
onPress={handleLogin}
|
||||
disabled={loadingTerm}
|
||||
isLoading={loading || loadingTerm}
|
||||
>
|
||||
Login
|
||||
</ButtonCustom>
|
||||
<Spacing height={50} />
|
||||
|
||||
<Text
|
||||
style={{ ...GStyles.textLabel, textAlign: "center", fontSize: 12 }}
|
||||
style={{
|
||||
position: "absolute",
|
||||
bottom: 35,
|
||||
right: 50,
|
||||
fontSize: 10,
|
||||
fontWeight: "thin",
|
||||
fontStyle: "italic",
|
||||
color: MainColor.white_gray,
|
||||
}}
|
||||
>
|
||||
Dengan menggunakan aplikasi ini, Anda telah menyetujui{" "}
|
||||
<Text
|
||||
style={{
|
||||
color: MainColor.yellow,
|
||||
textDecorationLine: "underline",
|
||||
}}
|
||||
onPress={() => {
|
||||
const toUrl = `${url}/terms-of-service.html`;
|
||||
openBrowser(toUrl);
|
||||
}}
|
||||
>
|
||||
Syarat & Ketentuan
|
||||
</Text>{" "}
|
||||
dan seluruh kebijakan privasi yang berlaku.
|
||||
{version} | powered by muku.id
|
||||
</Text>
|
||||
</View>
|
||||
</ViewWrapper>
|
||||
|
||||
<PhoneInput
|
||||
value={inputValue}
|
||||
onChangePhoneNumber={handleInputValue}
|
||||
selectedCountry={selectedCountry}
|
||||
onChangeSelectedCountry={handleSelectedCountry}
|
||||
defaultCountry="ID"
|
||||
placeholder="Masukkan nomor"
|
||||
/>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<ButtonCustom
|
||||
onPress={handleLogin}
|
||||
disabled={loadingTerm}
|
||||
isLoading={loading || loadingTerm}
|
||||
>
|
||||
Login
|
||||
</ButtonCustom>
|
||||
<Spacing height={50} />
|
||||
|
||||
<Text
|
||||
style={{ ...GStyles.textLabel, textAlign: "center", fontSize: 12 }}
|
||||
>
|
||||
Dengan menggunakan aplikasi ini, Anda telah menyetujui{" "}
|
||||
<Text
|
||||
style={{
|
||||
color: MainColor.yellow,
|
||||
textDecorationLine: "underline",
|
||||
}}
|
||||
onPress={() => {
|
||||
const toUrl = `${url}/terms-of-service.html`;
|
||||
openBrowser(toUrl);
|
||||
}}
|
||||
>
|
||||
Syarat & Ketentuan
|
||||
</Text>{" "}
|
||||
dan seluruh kebijakan privasi yang berlaku.
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<ModalReactNative isVisible={modalVisible}>
|
||||
<EULASection
|
||||
@@ -224,6 +205,6 @@ export default function LoginView() {
|
||||
setLoadingTerm={setLoadingTerm}
|
||||
/>
|
||||
</ModalReactNative>
|
||||
</KeyboardAvoidingView>
|
||||
</NewWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { BackButton, DrawerCustom, MenuDrawerDynamicGrid } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
@@ -53,12 +52,8 @@ export default function Donation_ScreenListOfNews({
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Daftar Kabar"
|
||||
left={<BackButton />}
|
||||
/>
|
||||
),
|
||||
title: "Daftar Kabar",
|
||||
headerLeft: () => <BackButton />,
|
||||
}}
|
||||
/>
|
||||
<NewWrapper
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { PAGINATION_DEFAULT_TAKE } from "@/constants/constans-value";
|
||||
@@ -62,13 +61,9 @@ export default function Donation_ScreenRecapOfNews({
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Rekap Kabar"
|
||||
left={<BackButton />}
|
||||
right={<DotButton onPress={() => setOpenDrawer(true)} />}
|
||||
/>
|
||||
),
|
||||
title: "Rekap Kabar",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<NewWrapper
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
LoaderCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiForumGetAll } from "@/service/api-client/api-forum";
|
||||
import { apiUser } from "@/service/api-client/api-user";
|
||||
@@ -55,17 +54,13 @@ export default function Forum_ViewBeranda() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Forum"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<AvatarComp
|
||||
fileId={dataUser?.Profile?.imageId}
|
||||
size="base"
|
||||
href={`/forum/${user?.id}/forumku`}
|
||||
/>
|
||||
}
|
||||
title: "Forum",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<AvatarComp
|
||||
fileId={dataUser?.Profile?.imageId}
|
||||
size="base"
|
||||
href={`/forum/${user?.id}/forumku`}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
StackCustom,
|
||||
TextCustom, // ← gunakan NewWrapper yang sudah diperbaiki
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import SkeletonCustom from "@/components/_ShareComponent/SkeletonCustom";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
@@ -156,17 +155,13 @@ export default function Forum_ViewBeranda2() {
|
||||
{/* 🔹 Header Navigation */}
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Forum"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<AvatarComp
|
||||
fileId={dataUser?.Profile?.imageId}
|
||||
size="base"
|
||||
href={`/forum/${user?.id}/forumku`}
|
||||
/>
|
||||
}
|
||||
title: "Forum",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<AvatarComp
|
||||
fileId={dataUser?.Profile?.imageId}
|
||||
size="base"
|
||||
href={`/forum/${user?.id}/forumku`}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
FloatingButton,
|
||||
SearchInput,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
@@ -18,7 +17,7 @@ import _ from "lodash";
|
||||
import { useEffect, useState } from "react";
|
||||
import { RefreshControl, TouchableOpacity, View } from "react-native";
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
const PAGE_SIZE = 5;
|
||||
|
||||
export default function Forum_ViewBeranda3() {
|
||||
const { user } = useAuth();
|
||||
@@ -85,22 +84,18 @@ export default function Forum_ViewBeranda3() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Forum"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<TouchableOpacity
|
||||
onPress={() => router.navigate(`/forum/${user?.id}/forumku`)}
|
||||
>
|
||||
<AvatarComp
|
||||
fileId={dataUser?.Profile?.imageId}
|
||||
size="base"
|
||||
href={`/forum/${user?.id}/forumku`}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
}
|
||||
/>
|
||||
title: "Forum",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<TouchableOpacity
|
||||
onPress={() => router.navigate(`/forum/${user?.id}/forumku`)}
|
||||
>
|
||||
<AvatarComp
|
||||
fileId={dataUser?.Profile?.imageId}
|
||||
size="base"
|
||||
href={`/forum/${user?.id}/forumku`}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -94,7 +94,7 @@ export const stylesHome = StyleSheet.create({
|
||||
jobVacancyHeader: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginBottom: 20,
|
||||
marginBottom: 16,
|
||||
},
|
||||
jobVacancyTitle: {
|
||||
fontSize: 18,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { router } from "expo-router";
|
||||
import React from "react";
|
||||
import { Text, TouchableOpacity, View } from "react-native";
|
||||
|
||||
|
||||
const CustomTab = ({ icon, label, isActive, onPress }: ICustomTab) => (
|
||||
<TouchableOpacity
|
||||
style={[GStyles.tabItem, isActive && GStyles.activeTab]}
|
||||
@@ -16,7 +17,7 @@ const CustomTab = ({ icon, label, isActive, onPress }: ICustomTab) => (
|
||||
>
|
||||
<Ionicons
|
||||
name={icon as any}
|
||||
size={18}
|
||||
size={20}
|
||||
color={isActive ? "#fff" : "#666"}
|
||||
/>
|
||||
</View>
|
||||
@@ -29,8 +30,8 @@ const CustomTab = ({ icon, label, isActive, onPress }: ICustomTab) => (
|
||||
export default function TabSection({ tabs }: { tabs: ITabs[] }) {
|
||||
return (
|
||||
<>
|
||||
<View style={GStyles.tabBar} pointerEvents="box-none">
|
||||
<View style={GStyles.tabContainer} pointerEvents="box-none">
|
||||
<View style={GStyles.tabBar}>
|
||||
<View style={GStyles.tabContainer}>
|
||||
{tabs.map((e) => (
|
||||
<CustomTab
|
||||
key={e.id}
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
DrawerCustom,
|
||||
MenuDrawerDynamicGrid
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconEdit } from "@/components/_Icon";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
@@ -126,18 +125,14 @@ export default function Investment_ScreenRecapOfDocument() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Rekap Dokumen"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<DotButton
|
||||
onPress={() => {
|
||||
setOpenDrawer(true);
|
||||
setOpenDrawerBox(false);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
title: "Rekap Dokumen",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<DotButton
|
||||
onPress={() => {
|
||||
setOpenDrawer(true);
|
||||
setOpenDrawerBox(false);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
MenuDrawerDynamicGrid,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
@@ -65,13 +64,9 @@ export default function Investment_ScreenListOfNews({
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Daftar Berita"
|
||||
left={<BackButton />}
|
||||
// headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
/>
|
||||
),
|
||||
title: "Daftar Berita",
|
||||
headerLeft: () => <BackButton />,
|
||||
// headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Spacing,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconPlus } from "@/components/_Icon";
|
||||
import NewWrapper from "@/components/_ShareComponent/NewWrapper";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
@@ -67,13 +66,9 @@ export default function Investment_ScreenRecapOfNews({
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Rekap Berita"
|
||||
left={<BackButton />}
|
||||
right={<DotButton onPress={() => setOpenDrawer(true)} />}
|
||||
/>
|
||||
),
|
||||
title: "Rekap Berita",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => <DotButton onPress={() => setOpenDrawer(true)} />,
|
||||
}}
|
||||
/>
|
||||
<NewWrapper
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconDot } from "@/components/_Icon/IconComponent";
|
||||
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||
import NoDataText from "@/components/_ShareComponent/NoDataText";
|
||||
@@ -218,16 +217,12 @@ export default function ScreenNotification_V1() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Notifikasi"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<IconDot
|
||||
color={MainColor.yellow}
|
||||
onPress={() => setOpenDrawer(true)}
|
||||
/>
|
||||
}
|
||||
title: "Notifikasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<IconDot
|
||||
color={MainColor.yellow}
|
||||
onPress={() => setOpenDrawer(true)}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
StackCustom,
|
||||
TextCustom,
|
||||
} from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import { IconDot } from "@/components/_Icon/IconComponent";
|
||||
import { AccentColor, MainColor } from "@/constants/color-palet";
|
||||
import {
|
||||
@@ -183,16 +182,12 @@ export default function ScreenNotification_V2() {
|
||||
<>
|
||||
<Stack.Screen
|
||||
options={{
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Notifikasi"
|
||||
left={<BackButton />}
|
||||
right={
|
||||
<IconDot
|
||||
color={MainColor.yellow}
|
||||
onPress={() => setOpenDrawer(true)}
|
||||
/>
|
||||
}
|
||||
title: "Notifikasi",
|
||||
headerLeft: () => <BackButton />,
|
||||
headerRight: () => (
|
||||
<IconDot
|
||||
color={MainColor.yellow}
|
||||
onPress={() => setOpenDrawer(true)}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -1,367 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import {
|
||||
ActionIcon,
|
||||
ButtonCenteredOnly,
|
||||
CenterCustom,
|
||||
InformationBox,
|
||||
NewWrapper,
|
||||
PhoneInputCustom,
|
||||
SelectCustom,
|
||||
Spacing,
|
||||
StackCustom,
|
||||
TextAreaCustom,
|
||||
TextCustom,
|
||||
TextInputCustom,
|
||||
} from "@/components";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||
import { DEFAULT_COUNTRY, type CountryData } from "@/constants/countries";
|
||||
import Portofolio_ButtonCreate from "@/screens/Portofolio/ButtonCreatePortofolio";
|
||||
import {
|
||||
apiMasterBidangBisnis,
|
||||
apiMasterSubBidangBisnis,
|
||||
} from "@/service/api-client/api-master";
|
||||
import {
|
||||
IMasterBidangBisnis,
|
||||
IMasterSubBidangBisnis,
|
||||
} from "@/types/Type-Master";
|
||||
import pickImage from "@/utils/pickImage";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useFocusEffect, useLocalSearchParams } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useState } from "react";
|
||||
import { Text, View } from "react-native";
|
||||
import { Avatar } from "react-native-paper";
|
||||
|
||||
export function ScreenPortofolioCreate() {
|
||||
const { id } = useLocalSearchParams();
|
||||
const [selectedCountry, setSelectedCountry] = useState<CountryData>(DEFAULT_COUNTRY);
|
||||
const [phoneNumber, setPhoneNumber] = useState<string>("");
|
||||
const [data, setData] = useState({
|
||||
namaBisnis: "",
|
||||
masterBidangBisnisId: "",
|
||||
alamatKantor: "",
|
||||
tlpn: "",
|
||||
deskripsi: "",
|
||||
});
|
||||
const [imageUri, setImageUri] = useState<string | null>(null);
|
||||
|
||||
const [bidangBisnis, setBidangBisnis] = useState<IMasterBidangBisnis[]>([]);
|
||||
const [subBidangBisnis, setSubBidangBisnis] = useState<
|
||||
IMasterSubBidangBisnis[]
|
||||
>([]);
|
||||
|
||||
const [selectedSubBidang, setSelectedSubBidang] = useState<string[]>([]);
|
||||
const [listSubBidangSelected, setListSubBidangSelected] = useState([
|
||||
{
|
||||
id: "",
|
||||
},
|
||||
]);
|
||||
|
||||
const [dataMedsos, setDataMedsos] = useState({
|
||||
facebook: "",
|
||||
twitter: "",
|
||||
instagram: "",
|
||||
youtube: "",
|
||||
tiktok: "",
|
||||
});
|
||||
|
||||
const [isLoadingCreate, setIsLoadingCreate] = useState(false);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
onLoadMaster();
|
||||
onLoadMasterSubBidangBisnis();
|
||||
}, []),
|
||||
);
|
||||
|
||||
const onLoadMaster = async () => {
|
||||
try {
|
||||
const response = await apiMasterBidangBisnis();
|
||||
setBidangBisnis(response.data);
|
||||
} catch (error) {
|
||||
setBidangBisnis([]);
|
||||
console.log("Error onLoadMasterBidangBisnis", error);
|
||||
}
|
||||
};
|
||||
|
||||
const onLoadMasterSubBidangBisnis = async () => {
|
||||
try {
|
||||
const response = await apiMasterSubBidangBisnis({});
|
||||
setSubBidangBisnis(response.data);
|
||||
} catch (error) {
|
||||
setSubBidangBisnis([]);
|
||||
console.log("Error onLoadMasterSubBidangBisnis", error);
|
||||
}
|
||||
};
|
||||
|
||||
const handlerSelectedSubBidang = ({ id }: { id: string }) => {
|
||||
const selectedList = subBidangBisnis?.filter(
|
||||
(item) => (item?.masterBidangBisnisId as any) === id,
|
||||
);
|
||||
setSelectedSubBidang(selectedList as any[]);
|
||||
};
|
||||
|
||||
const handlePhoneChange = (phone: string) => {
|
||||
setPhoneNumber(phone);
|
||||
|
||||
// Format phone number for API
|
||||
const callingCode = selectedCountry.callingCode;
|
||||
let fixNumber = phone.replace(/\s+/g, "").replace(/^0+/, "");
|
||||
|
||||
// Remove country code if already present
|
||||
if (fixNumber.startsWith(callingCode)) {
|
||||
fixNumber = fixNumber.substring(callingCode.length);
|
||||
}
|
||||
|
||||
// Remove leading zero
|
||||
fixNumber = fixNumber.replace(/^0+/, "");
|
||||
|
||||
const realNumber = callingCode + fixNumber;
|
||||
setData({ ...data, tlpn: realNumber });
|
||||
};
|
||||
|
||||
const handleCountryChange = (country: CountryData) => {
|
||||
setSelectedCountry(country);
|
||||
|
||||
// Re-format with new country code
|
||||
const callingCode = country.callingCode;
|
||||
let fixNumber = phoneNumber.replace(/\s+/g, "").replace(/^0+/, "");
|
||||
|
||||
// Remove country code if already present
|
||||
if (fixNumber.startsWith(callingCode)) {
|
||||
fixNumber = fixNumber.substring(callingCode.length);
|
||||
}
|
||||
|
||||
// Remove leading zero
|
||||
fixNumber = fixNumber.replace(/^0+/, "");
|
||||
|
||||
const realNumber = callingCode + fixNumber;
|
||||
setData({ ...data, tlpn: realNumber });
|
||||
};
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
footerComponent={
|
||||
<Portofolio_ButtonCreate
|
||||
id={id as string}
|
||||
data={data}
|
||||
dataMedsos={dataMedsos}
|
||||
imageUri={imageUri}
|
||||
subBidangSelected={listSubBidangSelected}
|
||||
isLoadingCreate={isLoadingCreate}
|
||||
setIsLoadingCreate={setIsLoadingCreate}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<StackCustom gap="xs">
|
||||
<InformationBox text="Lengkapi data bisnis anda." />
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Nama Bisnis"
|
||||
placeholder="Masukkan nama bisnis"
|
||||
onChangeText={(value: any) => setData({ ...data, namaBisnis: value })}
|
||||
/>
|
||||
|
||||
<SelectCustom
|
||||
label="Bidang Usaha"
|
||||
required
|
||||
data={bidangBisnis.map((item) => ({
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
value={data.masterBidangBisnisId}
|
||||
onChange={(value) => {
|
||||
const isSameBidang = data.masterBidangBisnisId === value;
|
||||
|
||||
if (!isSameBidang) {
|
||||
setListSubBidangSelected([{ id: "" }]);
|
||||
}
|
||||
|
||||
setData({ ...(data as any), masterBidangBisnisId: value });
|
||||
handlerSelectedSubBidang({ id: value as string });
|
||||
}}
|
||||
/>
|
||||
|
||||
{listSubBidangSelected.map((item, index) => (
|
||||
<SelectCustom
|
||||
key={index}
|
||||
disabled={data.masterBidangBisnisId === ""}
|
||||
label="Sub Bidang Usaha"
|
||||
required
|
||||
data={_.map(selectedSubBidang as any)
|
||||
.filter((option: any) => {
|
||||
const selectedValues = listSubBidangSelected.map((s) => s.id);
|
||||
return (
|
||||
option.id === item.id || !selectedValues.includes(option.id)
|
||||
);
|
||||
})
|
||||
.map((e: any) => ({
|
||||
value: e.id,
|
||||
label: e.name,
|
||||
}))}
|
||||
value={item.id || null}
|
||||
onChange={(value) => {
|
||||
const list = _.clone(listSubBidangSelected);
|
||||
list[index].id = value as any;
|
||||
setListSubBidangSelected(list);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
<CenterCustom>
|
||||
<View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
|
||||
<ActionIcon
|
||||
disabled={
|
||||
selectedSubBidang.length === listSubBidangSelected.length
|
||||
}
|
||||
onPress={() => {
|
||||
setListSubBidangSelected([
|
||||
...listSubBidangSelected,
|
||||
{ id: "" },
|
||||
]);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="add-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
<ActionIcon
|
||||
disabled={listSubBidangSelected.length <= 1}
|
||||
onPress={() => {
|
||||
const list = _.clone(listSubBidangSelected);
|
||||
list.pop();
|
||||
setListSubBidangSelected(list);
|
||||
}}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="remove-circle-outline"
|
||||
size={ICON_SIZE_XLARGE}
|
||||
color={MainColor.black}
|
||||
/>
|
||||
}
|
||||
size="xl"
|
||||
/>
|
||||
</View>
|
||||
</CenterCustom>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<View>
|
||||
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||
<TextCustom semiBold style={{ color: MainColor.white_gray }}>
|
||||
Nomor Telepon
|
||||
</TextCustom>
|
||||
<Text style={{ color: "red" }}> *</Text>
|
||||
</View>
|
||||
<Spacing height={5} />
|
||||
<PhoneInputCustom
|
||||
value={phoneNumber}
|
||||
onChangePhoneNumber={handlePhoneChange}
|
||||
selectedCountry={selectedCountry}
|
||||
onChangeCountry={handleCountryChange}
|
||||
placeholder="xxx-xxx-xxx"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<TextInputCustom
|
||||
required
|
||||
label="Alamat Bisnis"
|
||||
placeholder="Masukkan alamat bisnis"
|
||||
onChangeText={(value: any) =>
|
||||
setData({ ...data, alamatKantor: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextAreaCustom
|
||||
label="Deskripsi Bisnis"
|
||||
placeholder="Masukkan deskripsi bisnis"
|
||||
value={data.deskripsi}
|
||||
onChangeText={(value: any) => setData({ ...data, deskripsi: value })}
|
||||
autosize
|
||||
minRows={2}
|
||||
maxRows={5}
|
||||
required
|
||||
showCount
|
||||
maxLength={1000}
|
||||
/>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<InformationBox text="Upload logo bisnis anda untuk di tampilaka pada portofolio." />
|
||||
|
||||
<CenterCustom>
|
||||
<Avatar.Image
|
||||
source={imageUri ? { uri: imageUri } : DUMMY_IMAGE.dummy_image}
|
||||
size={200}
|
||||
/>
|
||||
</CenterCustom>
|
||||
|
||||
<Spacing />
|
||||
|
||||
<ButtonCenteredOnly
|
||||
icon="upload"
|
||||
onPress={() => {
|
||||
pickImage({
|
||||
setImageUri,
|
||||
});
|
||||
}}
|
||||
>
|
||||
Upload
|
||||
</ButtonCenteredOnly>
|
||||
|
||||
<Spacing height={40} />
|
||||
|
||||
<InformationBox text="Isi hanya pada sosial media yang anda miliki." />
|
||||
|
||||
<TextInputCustom
|
||||
label="Tiktok"
|
||||
placeholder="Masukkan username tiktok"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, tiktok: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
label="Facebook"
|
||||
placeholder="Masukkan username facebook"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, facebook: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
label="Instagram"
|
||||
placeholder="Masukkan username instagram"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, instagram: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
label="Twitter"
|
||||
placeholder="Masukkan username twitter"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, twitter: value })
|
||||
}
|
||||
/>
|
||||
|
||||
<TextInputCustom
|
||||
label="Youtube"
|
||||
placeholder="Masukkan username youtube"
|
||||
onChangeText={(value: any) =>
|
||||
setDataMedsos({ ...dataMedsos, youtube: value })
|
||||
}
|
||||
/>
|
||||
</StackCustom>
|
||||
</NewWrapper>
|
||||
);
|
||||
}
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
ICON_SIZE_SMALL,
|
||||
PAGINATION_DEFAULT_TAKE,
|
||||
} from "@/constants/constans-value";
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
import { usePagination } from "@/hooks/use-pagination";
|
||||
import { apiAllUser } from "@/service/api-client/api-user";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
@@ -20,89 +19,21 @@ import { router, useFocusEffect } from "expo-router";
|
||||
import _ from "lodash";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
|
||||
const PAGE_SIZE = PAGINATION_DEFAULT_TAKE;
|
||||
|
||||
/**
|
||||
* Render header dengan search input
|
||||
*/
|
||||
const renderHeader = (search: string, setSearch: (text: string) => void) => (
|
||||
<TextInputCustom
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
placeholder="Cari Pengguna"
|
||||
borderRadius={50}
|
||||
containerStyle={{ marginBottom: 0 }}
|
||||
/>
|
||||
);
|
||||
|
||||
/**
|
||||
* Render item user
|
||||
*/
|
||||
const renderItem = ({ item }: { item: any }) => (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: MainColor.soft_darkblue,
|
||||
borderRadius: 8,
|
||||
padding: 12,
|
||||
marginBottom: 10,
|
||||
elevation: 2,
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 2,
|
||||
}}
|
||||
>
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
router.push(`/profile/${item?.Profile?.id}`);
|
||||
}}
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarComp fileId={item?.Profile?.imageId} size="base" />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={9}>
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom size="large">{item?.username}</TextCustom>
|
||||
<TextCustom size="small">+{item?.nomor}</TextCustom>
|
||||
{item?.Profile?.businessField && (
|
||||
<TextCustom size="small">
|
||||
{item?.Profile?.businessField}
|
||||
</TextCustom>
|
||||
)}
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={1}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</ClickableCustom>
|
||||
</View>
|
||||
);
|
||||
import { createPaginationComponents } from "@/helpers/paginationHelpers";
|
||||
|
||||
export default function UserSearchMainView_V2() {
|
||||
const isInitialMount = useRef(true);
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
const pagination = usePagination({
|
||||
const {
|
||||
listData,
|
||||
loading,
|
||||
refreshing,
|
||||
hasMore,
|
||||
onRefresh,
|
||||
loadMore,
|
||||
isInitialLoad,
|
||||
} = usePagination({
|
||||
fetchFunction: async (page, searchQuery) => {
|
||||
const response = await apiAllUser({
|
||||
page: String(page),
|
||||
@@ -110,7 +41,7 @@ export default function UserSearchMainView_V2() {
|
||||
});
|
||||
return response;
|
||||
},
|
||||
pageSize: PAGE_SIZE,
|
||||
pageSize: PAGINATION_DEFAULT_TAKE,
|
||||
searchQuery: search,
|
||||
});
|
||||
|
||||
@@ -118,42 +49,119 @@ export default function UserSearchMainView_V2() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
if (isInitialMount.current) {
|
||||
// Skip saat pertama kali mount
|
||||
isInitialMount.current = false;
|
||||
return;
|
||||
}
|
||||
pagination.onRefresh();
|
||||
}, [pagination.onRefresh]),
|
||||
// Hanya refresh saat kembali dari screen lain
|
||||
onRefresh();
|
||||
}, [onRefresh]),
|
||||
);
|
||||
|
||||
const renderHeader = () => (
|
||||
<>
|
||||
<TextInputCustom
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
iconLeft={
|
||||
<Ionicons
|
||||
name="search"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
}
|
||||
placeholder="Cari Pengguna"
|
||||
borderRadius={50}
|
||||
containerStyle={{ marginBottom: 0 }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
const renderItem = ({ item }: { item: any }) => (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: MainColor.soft_darkblue,
|
||||
borderRadius: 8,
|
||||
padding: 12,
|
||||
marginBottom: 10,
|
||||
elevation: 2,
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 1 },
|
||||
shadowOpacity: 0.2,
|
||||
shadowRadius: 2,
|
||||
// height: 100
|
||||
}}
|
||||
>
|
||||
<ClickableCustom
|
||||
onPress={() => {
|
||||
console.log("Ke Profile");
|
||||
router.push(`/profile/${item?.Profile?.id}`);
|
||||
}}
|
||||
>
|
||||
<Grid>
|
||||
<Grid.Col span={2}>
|
||||
<AvatarComp fileId={item?.Profile?.imageId} size="base" />
|
||||
</Grid.Col>
|
||||
<Grid.Col span={9}>
|
||||
<StackCustom gap={"sm"}>
|
||||
<TextCustom size="large">{item?.username}</TextCustom>
|
||||
<TextCustom size="small">+{item?.nomor}</TextCustom>
|
||||
{item?.Profile?.businessField && (
|
||||
<TextCustom size="small">
|
||||
{item?.Profile?.businessField}
|
||||
</TextCustom>
|
||||
)}
|
||||
</StackCustom>
|
||||
</Grid.Col>
|
||||
<Grid.Col
|
||||
span={1}
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
alignItems: "flex-end",
|
||||
}}
|
||||
>
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={ICON_SIZE_SMALL}
|
||||
color={MainColor.placeholder}
|
||||
/>
|
||||
</Grid.Col>
|
||||
</Grid>
|
||||
</ClickableCustom>
|
||||
</View>
|
||||
);
|
||||
|
||||
const { ListEmptyComponent, ListFooterComponent } =
|
||||
createPaginationComponents({
|
||||
loading: pagination.loading,
|
||||
refreshing: pagination.refreshing,
|
||||
listData: pagination.listData,
|
||||
loading,
|
||||
refreshing,
|
||||
listData,
|
||||
searchQuery: search,
|
||||
emptyMessage: "Tidak ada pengguna ditemukan",
|
||||
emptySearchMessage: "Tidak ada hasil pencarian",
|
||||
skeletonCount: PAGINATION_DEFAULT_TAKE,
|
||||
skeletonHeight: 100,
|
||||
loadingFooterText: "Memuat lebih banyak pengguna...",
|
||||
isInitialLoad: pagination.isInitialLoad,
|
||||
isInitialLoad,
|
||||
});
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
headerComponent={renderHeader(search, setSearch)}
|
||||
listData={pagination.listData}
|
||||
renderItem={renderItem}
|
||||
onEndReached={pagination.loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
progressBackgroundColor={MainColor.yellow}
|
||||
refreshing={pagination.refreshing}
|
||||
onRefresh={pagination.onRefresh}
|
||||
/>
|
||||
}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
/>
|
||||
<>
|
||||
<NewWrapper
|
||||
headerComponent={renderHeader()}
|
||||
listData={listData}
|
||||
renderItem={renderItem}
|
||||
onEndReached={loadMore}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
progressBackgroundColor={MainColor.yellow}
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
/>
|
||||
}
|
||||
ListFooterComponent={ListFooterComponent}
|
||||
ListEmptyComponent={ListEmptyComponent}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ export async function apiUser(id: string) {
|
||||
}
|
||||
|
||||
export async function apiAllUser({
|
||||
page = "1",
|
||||
page,
|
||||
search,
|
||||
}: {
|
||||
page?: string;
|
||||
search?: string;
|
||||
}) {
|
||||
const pageQuery = `?page=${page}`;
|
||||
const pageQuery = page ? `?page=${page}` : "";
|
||||
const searchQuery = search ? `&search=${search}` : "";
|
||||
|
||||
try {
|
||||
|
||||
@@ -159,7 +159,7 @@ export const GStyles = StyleSheet.create({
|
||||
transform: [{ scale: 1.05 }],
|
||||
},
|
||||
iconContainer: {
|
||||
padding: 5,
|
||||
padding: 8,
|
||||
borderRadius: 20,
|
||||
// marginBottom: 4,
|
||||
},
|
||||
@@ -207,7 +207,7 @@ export const GStyles = StyleSheet.create({
|
||||
elevation: 8, // untuk Android
|
||||
},
|
||||
bottomBarContainer: {
|
||||
paddingHorizontal: 25,
|
||||
paddingHorizontal: 15,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
// =============== BOTTOM BAR =============== //
|
||||
|
||||
@@ -11,7 +11,7 @@ export const TabsStyles: BottomTabNavigationOptions = {
|
||||
tabBarStyle: Platform.select({
|
||||
ios: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 12,
|
||||
paddingTop: 5,
|
||||
height: OS_IOS_HEIGHT,
|
||||
},
|
||||
android: {
|
||||
|
||||
Reference in New Issue
Block a user