Refactor pagination implementation dan perbaikan UI

- Add default page parameter di apiAllUser
- Refactor MainView_V2.tsx dengan separate render functions
- Update pagination pageSize menjadi 10 di Forum
- Fix iOS height constant dan tab styling
- Rename Admin_ScreenPortofolioCreate ke ScreenPortofolioCreate
- Add TASKS documentation folder

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-03-25 16:22:54 +08:00
parent f68deab8c0
commit b6cd308b0b
15 changed files with 511 additions and 151 deletions

View File

@@ -0,0 +1,337 @@
# Crowdfunding Page Redesign - Modern UI Enhancement
## 📋 Ringkasan Task
Redesign halaman Crowdfunding (`app/(application)/(user)/crowdfunding/index.tsx`) untuk menciptakan tampilan yang lebih modern, menarik, dan user-friendly dengan visual hierarchy yang lebih baik.
---
## 🎯 Tujuan
1. Meningkatkan visual appeal dengan desain card yang modern
2. Memperbaiki spacing dan layout untuk readability yang lebih baik
3. Menambahkan elemen visual (icon, color accent) untuk navigasi yang lebih intuitif
4. Menciptakan konsistensi dengan design system aplikasi HIPMI
---
## 📁 File yang Terlibat
### File Utama
- **Target**: `app/(application)/(user)/crowdfunding/index.tsx`
### Komponen yang Mungkin Digunakan
- `ViewWrapper` - Wrapper utama
- `StackCustom` - Layout vertikal
- `Grid` & `Grid.Col` - Layout horizontal
- `TextCustom` - Typography
- `BaseBox` / `AdminBasicBox` - Card container
- `ClickableCustom` - Interactive element
- `Feather` / `Ionicons` - Icons
### Constants
- `MainColor` - Color palette
- `ICON_SIZE_SMALL`, `ICON_SIZE_BASE` - Icon sizing
### Assets
- `@/assets/images/constants/crowd-hipmi.png` - Header image
---
## 🔍 Analisis Kondisi Saat Ini
### Kelemahan Design Sekarang
1. **Header Image**
- Plain image tanpa overlay atau title
- Tidak ada visual hierarchy
2. **Card List**
- BaseBox terlalu simple, kurang depth
- Tidak ada shadow atau elevation
- Border radius mungkin kurang smooth
- Spacing antar elemen kurang konsisten
3. **Typography**
- Judul dan deskripsi kurang kontras
- Tidak ada visual emphasis yang kuat
4. **Navigation**
- Chevron icon terlalu plain
- Tidak ada visual feedback saat hover/press
5. **Color Usage**
- Kurang color accent untuk membedakan sections
- Monoton dengan warna yang ada
---
## 🎨 Rencana Desain
### 1. Header Section Enhancement
```
┌─────────────────────────────────────┐
│ [Hero Image dengan Overlay] │
│ ┌─────────────────────────────┐ │
│ │ Crowdfunding │ │
│ │ Platform Investasi & Donasi │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
```
**Implementasi:**
- Image dengan overlay gradient untuk text readability
- Title "Crowdfunding" dengan subtitle
- Rounded corners dengan overflow hidden
- Height: ~180-200px
### 2. Card Design Modern
```
┌─────────────────────────────────────┐
│ [Icon] Investasi → │
│ Deskripsi singkat... │
│ (2-3 baris max) │
└─────────────────────────────────────┘
```
**Implementasi:**
- Card dengan:
- Background gradient atau solid color dengan tint
- Shadow/elevation untuk depth
- Border radius: 12-16px
- Padding: 16-20px
- Icon di kiri (Investasi & Donasi)
- Chevron di kanan dengan style yang lebih modern
### 3. Icon Integration
- **Investasi**: Icon grafik/trending (contoh: `trending-up`, `pie-chart`)
- **Donasi**: Icon hati/tangan (contoh: `heart`, `hand-heart`)
- Size: 40-48px untuk icon card
- Background icon: Circle dengan color accent
### 4. Color Scheme
```
Investasi:
- Primary: Blue/Teal gradient
- Accent: Soft blue background
- Icon: White on blue
Donasi:
- Primary: Orange/Red gradient
- Accent: Soft orange background
- Icon: White on orange
```
### 5. Typography Hierarchy
```
Header Title: bold, x-large (20-22px)
Header Subtitle: regular, small (14-15px), gray
Card Title: bold, large (17-18px)
Card Description: regular, base (14px), gray
```
### 6. Spacing & Layout
```
Container padding: 16px
Card margin bottom: 12-16px
Card internal padding: 16px
Gap between elements: 8-12px
```
---
## 📝 Breakdown Task
### Task 1: Persiapan & Research
- [ ] Review komponen yang tersedia di `components/`
- [ ] Cek color palette di `constants/color-palet.ts`
- [ ] Identifikasi icon yang tersedia (Feather, Ionicons)
- [ ] Screenshot design sekarang untuk perbandingan
### Task 2: Setup Structure
- [ ] Buat constant untuk list menu (pindahkan dari component body)
- [ ] Tambahkan icon mapping untuk setiap menu item
- [ ] Setup color scheme untuk setiap card
### Task 3: Header Redesign
- [ ] Buat container dengan overflow hidden
- [ ] Tambahkan image dengan overlay gradient
- [ ] Tambahkan title "Crowdfunding" dengan text white
- [ ] Tambahkan subtitle (opsional)
- [ ] Test di berbagai ukuran layar
### Task 4: Card Component
- [ ] Buat custom card component atau modify BaseBox
- [ ] Tambahkan shadow/elevation
- [ ] Tambahkan border radius yang smooth
- [ ] Setup gradient background (opsional)
- [ ] Tambahkan visual feedback saat press
### Task 5: Icon Integration
- [ ] Pilih icon yang sesuai untuk setiap menu
- [ ] Buat icon container dengan background color
- [ ] Setup icon size dan positioning
- [ ] Test visibility di berbagai device
### Task 6: Typography & Content
- [ ] Apply text hierarchy (title, subtitle, desc)
- [ ] Truncate description jika terlalu panjang (max 2-3 baris)
- [ ] Ensure text contrast yang baik
- [ ] Test dengan text panjang
### Task 7: Polish & Refinement
- [ ] Adjust spacing dan padding
- [ ] Test di light/dark mode (jika applicable)
- [ ] Test di berbagai ukuran layar (responsive)
- [ ] Add smooth transitions/animations
### Task 8: Testing
- [ ] Test navigation ke setiap halaman
- [ ] Test di iOS simulator
- [ ] Test di Android emulator
- [ ] Test di device fisik (jika memungkinkan)
- [ ] Check performance (no lag saat scroll)
---
## 💻 Implementation Guidelines
### Code Structure Example
```typescript
// Constants
const CROWDFUNDING_MENU = [
{
title: "Investasi",
desc: "Buat investasi dan jual beli saham lebih mudah dengan pengguna lain.",
path: "investment/(tabs)",
icon: "trending-up",
color: MainColor.blue,
gradient: ["#667eea", "#764ba2"],
},
// ...
];
// Component
export default function Crowdfunding() {
const renderHeader = () => (
<View style={styles.headerContainer}>
<Image source={...} style={styles.headerImage} />
<View style={styles.headerOverlay}>
<TextCustom bold size="x-large">Crowdfunding</TextCustom>
<TextCustom>Platform Investasi & Donasi</TextCustom>
</View>
</View>
);
const renderCard = (item: CrowdfundingMenuItem) => (
<ClickableCustom onPress={...} style={styles.card}>
<View style={[styles.iconContainer, { backgroundColor: item.color }]}>
<Feather name={item.icon} size={24} color={MainColor.white} />
</View>
<Grid.Col span={8}>
<TextCustom bold size="large">{item.title}</TextCustom>
<TextCustom numberOfLines={2}>{item.desc}</TextCustom>
</Grid.Col>
<Feather name="chevron-right" size={ICON_SIZE_SMALL} />
</ClickableCustom>
);
return (
<ViewWrapper>
<StackCustom>
{renderHeader()}
{CROWDFUNDING_MENU.map(renderCard)}
</StackCustom>
</ViewWrapper>
);
}
```
### Style Guidelines
```typescript
const styles = StyleSheet.create({
headerContainer: {
position: 'relative',
borderRadius: 16,
overflow: 'hidden',
marginBottom: 20,
},
headerOverlay: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
padding: 16,
backgroundColor: 'rgba(0,0,0,0.6)',
},
card: {
backgroundColor: MainColor.white,
borderRadius: 16,
padding: 16,
marginBottom: 12,
elevation: 4,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
iconContainer: {
width: 48,
height: 48,
borderRadius: 24,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
},
});
```
---
## ✅ Acceptance Criteria
### Visual
- [ ] Header dengan overlay text yang readable
- [ ] Card dengan shadow/elevation yang jelas
- [ ] Icon untuk setiap menu item
- [ ] Color accent yang berbeda untuk Investasi & Donasi
- [ ] Typography hierarchy yang jelas
### Functional
- [ ] Navigation ke halaman tujuan berfungsi
- [ ] Responsive di berbagai ukuran layar
- [ ] No console errors atau warnings
- [ ] Smooth scroll (60fps)
### Code Quality
- [ ] Code terorganisir dengan baik
- [ ] Components terpisah untuk reusability
- [ ] Constants untuk data statis
- [ ] Comments untuk logic yang kompleks
- [ ] TypeScript types yang proper
---
## 📊 Estimated Effort
- **Complexity**: Low-Medium
- **Time Estimate**: 2-3 jam
- **Risk Level**: Low (tidak ada breaking changes)
---
## 🔗 References
- Design Reference: `screens/Forum/ViewBeranda3.tsx` (untuk pagination pattern)
- Color Palette: `constants/color-palet.ts`
- Icons: Feather Icons, Ionicons
- Components: `components/_ShareComponent/`
---
## 📝 Notes
- Pastikan backward compatibility (tidak breaking existing features)
- Test di device dengan screen size berbeda
- Consider accessibility (text contrast, touch target size)
- Jika ada time, tambahkan micro-interactions (scale on press, dll)
---
**Created**: 2026-03-25
**Status**: Pending
**Priority**: Medium

View File

@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable react-hooks/exhaustive-deps */
import { BasicWrapper, StackCustom, ViewWrapper } from "@/components";
import { BasicWrapper, NewWrapper, StackCustom, ViewWrapper } from "@/components";
import AppHeader from "@/components/_ShareComponent/AppHeader";
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
import { MainColor } from "@/constants/color-palet";
@@ -148,7 +148,7 @@ export default function Application() {
}}
/>
<ViewWrapper
<NewWrapper
refreshControl={
<RefreshControl
refreshing={refreshing}
@@ -166,18 +166,19 @@ export default function Application() {
})}
/>
) : (
<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>
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>
)
}
>
@@ -201,10 +202,10 @@ export default function Application() {
{data ? (
<Home_BottomFeatureSection listData={listData} />
) : (
<CustomSkeleton height={200} />
<CustomSkeleton height={150} />
)}
</StackCustom>
</ViewWrapper>
</NewWrapper>
</>
);
}

View File

@@ -1,5 +1,5 @@
import { Admin_ScreenPortofolioCreate } from "@/screens/Portofolio/ScreenPortofolioCreate";
import { ScreenPortofolioCreate } from "@/screens/Portofolio/ScreenPortofolioCreate";
export default function PortofolioCreate() {
return <Admin_ScreenPortofolioCreate />;
return <ScreenPortofolioCreate />;
}

View File

@@ -19,6 +19,7 @@ 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 {
@@ -111,7 +112,6 @@ const NewWrapper = (props: NewWrapperProps) => {
return `${String(item.id)}-${index}`;
})
}
refreshControl={refreshControl} // ✅ dari BaseProps
onEndReached={listProps.onEndReached}
onEndReachedThreshold={0.5}
@@ -156,15 +156,27 @@ const NewWrapper = (props: NewWrapperProps) => {
<View style={GStyles.stickyHeader}>{headerComponent}</View>
)}
<ScrollView
contentContainerStyle={{ flexGrow: 1 }}
<View style={{ flex: 0 }} collapsable={false}>
<ScrollView
contentContainerStyle={{ flexGrow: 1 }}
keyboardShouldPersistTaps="handled"
refreshControl={refreshControl} // ✅ sekarang valid
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
{renderContainer(staticProps.children)}
</TouchableWithoutFeedback>
</ScrollView>
</View>
{/* <ScrollView
contentContainerStyle={{ flexGrow: 0 }}
keyboardShouldPersistTaps="handled"
refreshControl={refreshControl} // ✅ sekarang valid
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
{renderContainer(staticProps.children)}
</TouchableWithoutFeedback>
</ScrollView>
</ScrollView> */}
{footerComponent ? (
<SafeAreaView

View File

@@ -24,7 +24,7 @@ export {
// OS Height
const OS_ANDROID_HEIGHT = 115
const OS_IOS_HEIGHT = 90
const OS_IOS_HEIGHT = 80
const OS_HEIGHT = Platform.OS === "ios" ? OS_IOS_HEIGHT : OS_ANDROID_HEIGHT
// Text Size

View File

@@ -197,6 +197,7 @@
D5CA1D54CFF74AB4B8B5B583 /* Remove signature files (Xcode workaround) */,
97C01196E2194AF5A13C7773 /* Remove signature files (Xcode workaround) */,
EB19F4C53C8B434CBAD50897 /* Remove signature files (Xcode workaround) */,
95ABFC1FE48F4F2ABAF407D8 /* Remove signature files (Xcode workaround) */,
);
buildRules = (
);
@@ -1247,6 +1248,23 @@
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\";
";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */

View File

@@ -1,4 +1,4 @@
import { NewWrapper } from "@/components";
import { NewWrapper, ViewWrapper } from "@/components";
import ButtonCustom from "@/components/Button/ButtonCustom";
import ModalReactNative from "@/components/Modal/ModalReactNative";
import Spacing from "@/components/_ShareComponent/Spacing";
@@ -128,7 +128,7 @@ export default function LoginView() {
}
return (
<NewWrapper
<ViewWrapper
withBackground
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
@@ -205,6 +205,6 @@ export default function LoginView() {
setLoadingTerm={setLoadingTerm}
/>
</ModalReactNative>
</NewWrapper>
</ViewWrapper>
);
}

View File

@@ -18,7 +18,7 @@ import _ from "lodash";
import { useEffect, useState } from "react";
import { RefreshControl, TouchableOpacity, View } from "react-native";
const PAGE_SIZE = 5;
const PAGE_SIZE = 10;
export default function Forum_ViewBeranda3() {
const { user } = useAuth();

View File

@@ -94,7 +94,7 @@ export const stylesHome = StyleSheet.create({
jobVacancyHeader: {
flexDirection: "row",
alignItems: "center",
marginBottom: 16,
marginBottom: 20,
},
jobVacancyTitle: {
fontSize: 18,

View File

@@ -5,7 +5,6 @@ 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]}
@@ -17,7 +16,7 @@ const CustomTab = ({ icon, label, isActive, onPress }: ICustomTab) => (
>
<Ionicons
name={icon as any}
size={20}
size={18}
color={isActive ? "#fff" : "#666"}
/>
</View>
@@ -30,8 +29,8 @@ const CustomTab = ({ icon, label, isActive, onPress }: ICustomTab) => (
export default function TabSection({ tabs }: { tabs: ITabs[] }) {
return (
<>
<View style={GStyles.tabBar}>
<View style={GStyles.tabContainer}>
<View style={GStyles.tabBar} pointerEvents="box-none">
<View style={GStyles.tabContainer} pointerEvents="box-none">
{tabs.map((e) => (
<CustomTab
key={e.id}

View File

@@ -33,7 +33,7 @@ import { Text, View } from "react-native";
import PhoneInput, { ICountry } from "react-native-international-phone-number";
import { Avatar } from "react-native-paper";
export function Admin_ScreenPortofolioCreate() {
export function ScreenPortofolioCreate() {
const { id } = useLocalSearchParams();
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
const [inputValue, setInputValue] = useState<string>("");
@@ -72,7 +72,7 @@ export function Admin_ScreenPortofolioCreate() {
useCallback(() => {
onLoadMaster();
onLoadMasterSubBidangBisnis();
}, [])
}, []),
);
const onLoadMaster = async () => {
@@ -97,7 +97,7 @@ export function Admin_ScreenPortofolioCreate() {
const handlerSelectedSubBidang = ({ id }: { id: string }) => {
const selectedList = subBidangBisnis?.filter(
(item) => (item?.masterBidangBisnisId as any) === id
(item) => (item?.masterBidangBisnisId as any) === id,
);
setSelectedSubBidang(selectedList as any[]);
};
@@ -168,8 +168,7 @@ export function Admin_ScreenPortofolioCreate() {
.filter((option: any) => {
const selectedValues = listSubBidangSelected.map((s) => s.id);
return (
option.id === item.id ||
!selectedValues.includes(option.id)
option.id === item.id || !selectedValues.includes(option.id)
);
})
.map((e: any) => ({
@@ -188,7 +187,9 @@ export function Admin_ScreenPortofolioCreate() {
<CenterCustom>
<View style={{ flexDirection: "row", alignItems: "center", gap: 10 }}>
<ActionIcon
disabled={selectedSubBidang.length === listSubBidangSelected.length}
disabled={
selectedSubBidang.length === listSubBidangSelected.length
}
onPress={() => {
setListSubBidangSelected([
...listSubBidangSelected,

View File

@@ -12,6 +12,7 @@ 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";
@@ -19,21 +20,89 @@ import { router, useFocusEffect } from "expo-router";
import _ from "lodash";
import { useCallback, useRef, useState } from "react";
import { RefreshControl, View } from "react-native";
import { createPaginationComponents } from "@/helpers/paginationHelpers";
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>
);
export default function UserSearchMainView_V2() {
const isInitialMount = useRef(true);
const [search, setSearch] = useState("");
const {
listData,
loading,
refreshing,
hasMore,
onRefresh,
loadMore,
isInitialLoad,
} = usePagination({
const pagination = usePagination({
fetchFunction: async (page, searchQuery) => {
const response = await apiAllUser({
page: String(page),
@@ -41,7 +110,7 @@ export default function UserSearchMainView_V2() {
});
return response;
},
pageSize: PAGINATION_DEFAULT_TAKE,
pageSize: PAGE_SIZE,
searchQuery: search,
});
@@ -49,119 +118,42 @@ export default function UserSearchMainView_V2() {
useFocusEffect(
useCallback(() => {
if (isInitialMount.current) {
// Skip saat pertama kali mount
isInitialMount.current = false;
return;
}
// 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>
pagination.onRefresh();
}, [pagination.onRefresh]),
);
const { ListEmptyComponent, ListFooterComponent } =
createPaginationComponents({
loading,
refreshing,
listData,
loading: pagination.loading,
refreshing: pagination.refreshing,
listData: pagination.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,
isInitialLoad: pagination.isInitialLoad,
});
return (
<>
<NewWrapper
headerComponent={renderHeader()}
listData={listData}
renderItem={renderItem}
onEndReached={loadMore}
refreshControl={
<RefreshControl
progressBackgroundColor={MainColor.yellow}
refreshing={refreshing}
onRefresh={onRefresh}
/>
}
ListFooterComponent={ListFooterComponent}
ListEmptyComponent={ListEmptyComponent}
/>
</>
<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}
/>
);
}

View File

@@ -6,13 +6,13 @@ export async function apiUser(id: string) {
}
export async function apiAllUser({
page,
page = "1",
search,
}: {
page?: string;
search?: string;
}) {
const pageQuery = page ? `?page=${page}` : "";
const pageQuery = `?page=${page}`;
const searchQuery = search ? `&search=${search}` : "";
try {

View File

@@ -159,7 +159,7 @@ export const GStyles = StyleSheet.create({
transform: [{ scale: 1.05 }],
},
iconContainer: {
padding: 8,
padding: 5,
borderRadius: 20,
// marginBottom: 4,
},

View File

@@ -11,7 +11,7 @@ export const TabsStyles: BottomTabNavigationOptions = {
tabBarStyle: Platform.select({
ios: {
borderTopWidth: 0,
paddingTop: 5,
paddingTop: 12,
height: OS_IOS_HEIGHT,
},
android: {