Fix layout tabs pada komponen
Fix home tabs ### No Issue
This commit is contained in:
@@ -2,35 +2,64 @@ import { IconHome } from "@/components/_Icon";
|
||||
import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
function CollaborationTabsWrapper() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = Platform.OS === "android" ? insets.bottom : 0;
|
||||
|
||||
export default function CollaborationTabsLayout() {
|
||||
return (
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
<View style={{ flex: 1, backgroundColor: MainColor.darkblue }}>
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
tabBarStyle: Platform.select({
|
||||
ios: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 12,
|
||||
height: 80,
|
||||
},
|
||||
android: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 5,
|
||||
height: 70 + paddingBottom,
|
||||
},
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="participant"
|
||||
options={{
|
||||
title: "Partisipan",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="people" color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="group"
|
||||
options={{
|
||||
title: "Grup",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="chatbox-ellipses" color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="participant"
|
||||
options={{
|
||||
title: "Partisipan",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="people" color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="group"
|
||||
options={{
|
||||
title: "Grup",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons size={20} name="chatbox-ellipses" color={color} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default function CollaborationTabsLayout() {
|
||||
return <CollaborationTabsWrapper />;
|
||||
}
|
||||
|
||||
@@ -5,33 +5,62 @@ import {
|
||||
FontAwesome5
|
||||
} from "@expo/vector-icons";
|
||||
import { Tabs } from "expo-router";
|
||||
import { View } from "react-native";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
function DonationTabsWrapper() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = Platform.OS === "android" ? insets.bottom : 0;
|
||||
|
||||
export default function InvestmentTabsLayout() {
|
||||
return (
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
<View style={{ flex: 1, backgroundColor: MainColor.darkblue }}>
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
tabBarStyle: Platform.select({
|
||||
ios: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 12,
|
||||
height: 80,
|
||||
},
|
||||
android: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 5,
|
||||
height: 70 + paddingBottom,
|
||||
},
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Galang Dana",
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="my-donation"
|
||||
options={{
|
||||
title: "Donasi Saya",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<FontAwesome5 name="donate" color={color} size={ICON_SIZE_SMALL} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Galang Dana",
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="my-donation"
|
||||
options={{
|
||||
title: "Donasi Saya",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<FontAwesome5 name="donate" color={color} size={ICON_SIZE_SMALL} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default function DonationTabsLayout() {
|
||||
return <DonationTabsWrapper />;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,6 @@ export default function DonationEdit() {
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
hideFooter
|
||||
footerComponent={
|
||||
<BoxButtonOnFooter>
|
||||
<ButtonCustom
|
||||
|
||||
@@ -114,7 +114,6 @@ export default function DonationCreateStory() {
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
hideFooter
|
||||
footerComponent={
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
|
||||
@@ -127,7 +127,6 @@ export default function DonationCreate() {
|
||||
|
||||
return (
|
||||
<NewWrapper
|
||||
hideFooter
|
||||
footerComponent={
|
||||
<>
|
||||
<BoxButtonOnFooter>
|
||||
|
||||
@@ -8,58 +8,83 @@ 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 { View } from "react-native";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { Platform } from "react-native";
|
||||
import { OS_ANDROID_HEIGHT, OS_IOS_HEIGHT } from "@/constants/constans-value";
|
||||
|
||||
export default function EventTabsLayout() {
|
||||
function EventTabsWrapper() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = Platform.OS === "android" ? insets.bottom : 0;
|
||||
const { from, category } = useLocalSearchParams<{
|
||||
from?: string;
|
||||
category?: string;
|
||||
}>();
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Event"
|
||||
left={
|
||||
<BackButtonFromNotification
|
||||
from={from as string}
|
||||
category={category as string}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
<View style={{ flex: 1, backgroundColor: MainColor.darkblue }}>
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
tabBarStyle: Platform.select({
|
||||
ios: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 12,
|
||||
height: OS_IOS_HEIGHT,
|
||||
},
|
||||
android: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 5,
|
||||
height: OS_ANDROID_HEIGHT + paddingBottom,
|
||||
},
|
||||
}),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Event"
|
||||
left={
|
||||
<BackButtonFromNotification
|
||||
from={from || ""}
|
||||
category={category}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="contribution"
|
||||
options={{
|
||||
title: "Kontribusi",
|
||||
tabBarIcon: ({ color }) => <IconContribution color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="history"
|
||||
options={{
|
||||
title: "Riwayat",
|
||||
tabBarIcon: ({ color }) => <IconHistory color={color} />,
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="contribution"
|
||||
options={{
|
||||
title: "Kontribusi",
|
||||
tabBarIcon: ({ color }) => <IconContribution color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="history"
|
||||
options={{
|
||||
title: "Riwayat",
|
||||
tabBarIcon: ({ color }) => <IconHistory color={color} />,
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default function EventTabsLayout() {
|
||||
return <EventTabsWrapper />;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { BasicWrapper, NewWrapper, StackCustom, ViewWrapper } from "@/components";
|
||||
import { BasicWrapper, Spacing, StackCustom, ViewWrapper } from "@/components";
|
||||
import AppHeader from "@/components/_ShareComponent/AppHeader";
|
||||
import CustomSkeleton from "@/components/_ShareComponent/SkeletonCustom";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
@@ -8,19 +8,20 @@ import { useAuth } from "@/hooks/use-auth";
|
||||
import { useNotificationStore } from "@/hooks/use-notification-store";
|
||||
import Home_BottomFeatureSection from "@/screens/Home/bottomFeatureSection";
|
||||
import HeaderBell from "@/screens/Home/HeaderBell";
|
||||
import HomeTabs from "@/screens/Home/HomeTabs";
|
||||
import { stylesHome } from "@/screens/Home/homeViewStyle";
|
||||
import Home_ImageSection from "@/screens/Home/imageSection";
|
||||
import TabSection from "@/screens/Home/tabSection";
|
||||
import { tabsHome } from "@/screens/Home/tabsList";
|
||||
import Home_FeatureSection from "@/screens/Home/topFeatureSection";
|
||||
import { apiJobGetAll } from "@/service/api-client/api-job";
|
||||
import { apiUser } from "@/service/api-client/api-user";
|
||||
import { apiVersion } from "@/service/api-config";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { Redirect, router, Stack, useFocusEffect } from "expo-router";
|
||||
import { useCallback, useState } from "react";
|
||||
import { RefreshControl, View } from "react-native";
|
||||
import { RefreshControl, ScrollView, View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export default function Application() {
|
||||
const { token, user, userData } = useAuth();
|
||||
@@ -28,6 +29,8 @@ export default function Application() {
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const { syncUnreadCount } = useNotificationStore();
|
||||
const [listData, setListData] = useState<any[] | null>(null);
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = Platform.OS === "android" ? insets.bottom : 0;
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
@@ -105,15 +108,6 @@ export default function Application() {
|
||||
);
|
||||
}
|
||||
|
||||
// if (data && data?.masterUserRoleId !== "1") {
|
||||
// console.log("User is not admin");
|
||||
// return (
|
||||
// <BasicWrapper>
|
||||
// <Redirect href={`/admin/dashboard`} />
|
||||
// </BasicWrapper>
|
||||
// );
|
||||
// }
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen
|
||||
@@ -148,64 +142,61 @@ export default function Application() {
|
||||
}}
|
||||
/>
|
||||
|
||||
<NewWrapper
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
tintColor={MainColor.yellow}
|
||||
colors={[MainColor.yellow]}
|
||||
/>
|
||||
}
|
||||
footerComponent={
|
||||
data && data ? (
|
||||
<TabSection
|
||||
tabs={tabsHome({
|
||||
acceptedForumTermsAt: data?.acceptedForumTermsAt,
|
||||
profileId: data?.Profile?.id,
|
||||
})}
|
||||
<View style={{ flex: 1, backgroundColor: MainColor.darkblue }}>
|
||||
<ScrollView
|
||||
style={{ flex: 1 }}
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
paddingInline: 10,
|
||||
paddingBottom: paddingBottom + 80, // Space for tabs + safe area
|
||||
}}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
onRefresh={onRefresh}
|
||||
tintColor={MainColor.yellow}
|
||||
colors={[MainColor.yellow]}
|
||||
/>
|
||||
) : (
|
||||
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>
|
||||
)
|
||||
}
|
||||
>
|
||||
<StackCustom>
|
||||
<Home_ImageSection />
|
||||
}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
>
|
||||
<StackCustom>
|
||||
<Home_ImageSection />
|
||||
|
||||
{data && data ? (
|
||||
<Home_FeatureSection />
|
||||
) : (
|
||||
<View style={stylesHome.gridContainer}>
|
||||
{Array.from({ length: 4 }).map((item, index) => (
|
||||
<CustomSkeleton
|
||||
key={index}
|
||||
style={stylesHome.gridItem}
|
||||
radius={50}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
{data && data ? (
|
||||
<Home_FeatureSection />
|
||||
) : (
|
||||
<View style={stylesHome.gridContainer}>
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<CustomSkeleton
|
||||
key={index}
|
||||
style={stylesHome.gridItem}
|
||||
radius={50}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{data ? (
|
||||
<Home_BottomFeatureSection listData={listData} />
|
||||
) : (
|
||||
<CustomSkeleton height={150} />
|
||||
)}
|
||||
</StackCustom>
|
||||
</NewWrapper>
|
||||
{data ? (
|
||||
<Home_BottomFeatureSection listData={listData} />
|
||||
) : (
|
||||
<CustomSkeleton height={150} />
|
||||
)}
|
||||
</StackCustom>
|
||||
</ScrollView>
|
||||
|
||||
{/* Home Tabs di bawah */}
|
||||
{data && data ? (
|
||||
<HomeTabs
|
||||
tabs={tabsHome({
|
||||
acceptedForumTermsAt: data?.acceptedForumTermsAt,
|
||||
profileId: data?.Profile?.id,
|
||||
})}
|
||||
/>
|
||||
) : (
|
||||
<View style={{ height: 80 + paddingBottom, backgroundColor: MainColor.darkblue }} />
|
||||
)}
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,80 +4,105 @@ import { TabsStyles } from "@/styles/tabs-styles";
|
||||
import { Feather, FontAwesome6, Ionicons } from "@expo/vector-icons";
|
||||
import { router, Tabs, useLocalSearchParams, useNavigation } from "expo-router";
|
||||
import { useLayoutEffect } from "react";
|
||||
import { View } from "react-native";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export default function InvestmentTabsLayout() {
|
||||
// const navigation = useNavigation();
|
||||
function InvestmentTabsWrapper() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = Platform.OS === "android" ? insets.bottom : 0;
|
||||
const navigation = useNavigation();
|
||||
|
||||
// const { from, category } = useLocalSearchParams<{
|
||||
// from?: string;
|
||||
// category?: string;
|
||||
// }>();
|
||||
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]);
|
||||
// Atur header secara dinamis
|
||||
useLayoutEffect(() => {
|
||||
navigation.setOptions({
|
||||
headerLeft: () => (
|
||||
<BackButtonFromNotification
|
||||
from={from || ""}
|
||||
category={category}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}, [from, category, router, navigation]);
|
||||
|
||||
return (
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Bursa",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons
|
||||
name="bar-chart-outline"
|
||||
color={color}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
),
|
||||
<View style={{ flex: 1, backgroundColor: MainColor.darkblue }}>
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
tabBarStyle: Platform.select({
|
||||
ios: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 12,
|
||||
height: 80,
|
||||
},
|
||||
android: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 5,
|
||||
height: 70 + paddingBottom,
|
||||
},
|
||||
}),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="portofolio"
|
||||
options={{
|
||||
title: "Portofolio",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Feather name="pie-chart" color={color} size={ICON_SIZE_SMALL} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="my-holding"
|
||||
options={{
|
||||
title: "Saham Saya",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<FontAwesome6
|
||||
name="hand-holding-dollar"
|
||||
color={color}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="transaction"
|
||||
options={{
|
||||
title: "Transaksi",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<FontAwesome6
|
||||
name="money-bill-transfer"
|
||||
color={color}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Bursa",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Ionicons
|
||||
name="bar-chart-outline"
|
||||
color={color}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="portofolio"
|
||||
options={{
|
||||
title: "Portofolio",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<Feather name="pie-chart" color={color} size={ICON_SIZE_SMALL} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="my-holding"
|
||||
options={{
|
||||
title: "Saham Saya",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<FontAwesome6
|
||||
name="hand-holding-dollar"
|
||||
color={color}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="transaction"
|
||||
options={{
|
||||
title: "Transaksi",
|
||||
tabBarIcon: ({ color }) => (
|
||||
<FontAwesome6
|
||||
name="money-bill-transfer"
|
||||
color={color}
|
||||
size={ICON_SIZE_SMALL}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default function InvestmentTabsLayout() {
|
||||
return <InvestmentTabsWrapper />;
|
||||
}
|
||||
|
||||
@@ -10,23 +10,41 @@ import {
|
||||
Tabs,
|
||||
useLocalSearchParams
|
||||
} from "expo-router";
|
||||
import { View } from "react-native";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export default function JobTabsLayout() {
|
||||
function JobTabsWrapper() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = Platform.OS === "android" ? insets.bottom : 0;
|
||||
const { from, category } = useLocalSearchParams<{
|
||||
from?: string;
|
||||
category?: string;
|
||||
}>();
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style={{ flex: 1, backgroundColor: MainColor.darkblue }}>
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
tabBarStyle: Platform.select({
|
||||
ios: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 12,
|
||||
height: 80,
|
||||
},
|
||||
android: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 5,
|
||||
height: 70 + paddingBottom,
|
||||
},
|
||||
}),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Job Vacancy"
|
||||
left={
|
||||
<BackButtonFromNotification from={from as string} category={category as string} />
|
||||
<BackButtonFromNotification from={from || ""} category={category} />
|
||||
}
|
||||
/>
|
||||
),
|
||||
@@ -56,6 +74,10 @@ export default function JobTabsLayout() {
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
</>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default function JobTabsLayout() {
|
||||
return <JobTabsWrapper />;
|
||||
}
|
||||
|
||||
@@ -8,58 +8,82 @@ 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 { View } from "react-native";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { Platform } from "react-native";
|
||||
|
||||
export default function VotingTabsLayout() {
|
||||
function VotingTabsWrapper() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = Platform.OS === "android" ? insets.bottom : 0;
|
||||
const { from, category } = useLocalSearchParams<{
|
||||
from?: string;
|
||||
category?: string;
|
||||
}>();
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Voting"
|
||||
left={
|
||||
<BackButtonFromNotification
|
||||
from={from as string}
|
||||
category={category as string}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
<View style={{ flex: 1, backgroundColor: MainColor.darkblue }}>
|
||||
<Tabs
|
||||
screenOptions={{
|
||||
...TabsStyles,
|
||||
tabBarStyle: Platform.select({
|
||||
ios: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 12,
|
||||
height: 80,
|
||||
},
|
||||
android: {
|
||||
borderTopWidth: 0,
|
||||
paddingTop: 5,
|
||||
height: 70 + paddingBottom,
|
||||
},
|
||||
}),
|
||||
header: () => (
|
||||
<AppHeader
|
||||
title="Voting"
|
||||
left={
|
||||
<BackButtonFromNotification
|
||||
from={from || ""}
|
||||
category={category}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="contribution"
|
||||
options={{
|
||||
title: "Kontribusi",
|
||||
tabBarIcon: ({ color }) => <IconContribution color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="history"
|
||||
options={{
|
||||
title: "Riwayat",
|
||||
tabBarIcon: ({ color }) => <IconHistory color={color} />,
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: "Beranda",
|
||||
tabBarIcon: ({ color }) => <IconHome color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="status"
|
||||
options={{
|
||||
title: "Status",
|
||||
tabBarIcon: ({ color }) => <IconStatus color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="contribution"
|
||||
options={{
|
||||
title: "Kontribusi",
|
||||
tabBarIcon: ({ color }) => <IconContribution color={color} />,
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="history"
|
||||
options={{
|
||||
title: "Riwayat",
|
||||
tabBarIcon: ({ color }) => <IconHistory color={color} />,
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default function VotingTabsLayout() {
|
||||
return <VotingTabsWrapper />;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ export {
|
||||
};
|
||||
|
||||
// OS Height
|
||||
const OS_ANDROID_HEIGHT = 70
|
||||
const OS_ANDROID_HEIGHT = 65
|
||||
const OS_IOS_HEIGHT = 80
|
||||
const OS_HEIGHT = Platform.OS === "ios" ? OS_IOS_HEIGHT : OS_ANDROID_HEIGHT
|
||||
|
||||
|
||||
@@ -152,6 +152,8 @@
|
||||
4A22447E41944D3A9780867B /* Remove signature files (Xcode workaround) */,
|
||||
D15DF02DDCF369B4F14B238B /* [CP] Embed Pods Frameworks */,
|
||||
3F53CC1C3B278545F11A1CAE /* [CP-User] [RNFB] Core Configuration */,
|
||||
46ED08049A384B869D77364E /* Remove signature files (Xcode workaround) */,
|
||||
92A25C61F4E34FB6A36E415B /* Remove signature files (Xcode workaround) */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -429,6 +431,40 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-HIPMIBadungConnect/expo-configure-project.sh\"\n";
|
||||
};
|
||||
46ED08049A384B869D77364E /* 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\";
|
||||
";
|
||||
};
|
||||
92A25C61F4E34FB6A36E415B /* 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 */
|
||||
@@ -471,7 +507,7 @@
|
||||
);
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.anonymous.hipmi-mobile";
|
||||
PRODUCT_NAME = HIPMIBadungConnect;
|
||||
PRODUCT_NAME = "HIPMIBadungConnect";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "HIPMIBadungConnect/HIPMIBadungConnect-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@@ -502,7 +538,7 @@
|
||||
);
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.anonymous.hipmi-mobile";
|
||||
PRODUCT_NAME = HIPMIBadungConnect;
|
||||
PRODUCT_NAME = "HIPMIBadungConnect";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "HIPMIBadungConnect/HIPMIBadungConnect-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
||||
71
screens/Home/HomeTabs.tsx
Normal file
71
screens/Home/HomeTabs.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { ICustomTab, ITabs } from "@/components/_Interface/types";
|
||||
import { GStyles } from "@/styles/global-styles";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { router } from "expo-router";
|
||||
import React from "react";
|
||||
import { Platform, Text, TouchableOpacity, View } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { MainColor } from "@/constants/color-palet";
|
||||
|
||||
interface HomeTabsProps {
|
||||
tabs: ITabs[];
|
||||
}
|
||||
|
||||
const CustomTab = ({ icon, label, isActive, onPress }: ICustomTab) => (
|
||||
<TouchableOpacity
|
||||
style={[GStyles.tabItem, isActive && GStyles.activeTab]}
|
||||
onPress={onPress}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View
|
||||
style={[GStyles.iconContainer, isActive && GStyles.activeIconContainer]}
|
||||
>
|
||||
<Ionicons
|
||||
name={icon as any}
|
||||
size={18}
|
||||
color={isActive ? "#fff" : "#666"}
|
||||
/>
|
||||
</View>
|
||||
<Text style={[GStyles.tabLabel, isActive && GStyles.activeTabLabel]}>
|
||||
{label}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
/**
|
||||
* Home Tabs Component dengan Safe Area handling
|
||||
*
|
||||
* Component ini menggunakan pattern yang sama dengan Expo Router Tabs
|
||||
* untuk konsistensi safe area di Android
|
||||
*/
|
||||
export default function HomeTabs({ tabs }: HomeTabsProps) {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = Platform.OS === "android" ? insets.bottom : 0;
|
||||
|
||||
return (
|
||||
<View style={{ backgroundColor: MainColor.darkblue }}>
|
||||
{/* Tabs content */}
|
||||
<View style={GStyles.tabBar}>
|
||||
<View style={GStyles.tabContainer}>
|
||||
{tabs.map((e) => (
|
||||
<CustomTab
|
||||
key={e.id}
|
||||
icon={e.icon}
|
||||
label={e.label}
|
||||
isActive={e.isActive}
|
||||
onPress={() => {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
e.disabled ? console.log("disabled") : router.push(e.path);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Safe area padding untuk Android */}
|
||||
{Platform.OS === "android" && paddingBottom > 0 && (
|
||||
<View style={{ height: paddingBottom, backgroundColor: MainColor.darkblue }} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -196,15 +196,15 @@ export const GStyles = StyleSheet.create({
|
||||
// =============== BOTTOM BAR =============== //
|
||||
bottomBar: {
|
||||
backgroundColor: MainColor.darkblue,
|
||||
borderTopColor: AccentColor.blue,
|
||||
// borderTopWidth: 0.5,
|
||||
borderTopColor: AccentColor.darkblue,
|
||||
borderTopWidth: 1,
|
||||
height: "100%",
|
||||
justifyContent: "center",
|
||||
shadowColor: AccentColor.blue,
|
||||
shadowOffset: { width: 0, height: -5},
|
||||
shadowOpacity: 0.4,
|
||||
shadowRadius: 40,
|
||||
elevation: 8, // untuk Android
|
||||
// elevation: 8, // untuk Android
|
||||
},
|
||||
bottomBarContainer: {
|
||||
paddingHorizontal: 25,
|
||||
|
||||
58
tasks/README.md
Normal file
58
tasks/README.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Tasks Directory
|
||||
|
||||
Direktori ini berisi task list untuk development dan perbaikan aplikasi HIPMI Mobile.
|
||||
|
||||
## 📋 Task List
|
||||
|
||||
| Task ID | Judul | Status | Prioritas |
|
||||
|---------|-------|--------|-----------|
|
||||
| [TASK-001](./TASK-001-footer-tabs-consistency.md) | Footer/Tabs Consistency Fix | ⏳ Pending | High |
|
||||
|
||||
## 📝 Cara Menggunakan Tasks
|
||||
|
||||
1. **Lihat task yang tersedia** di daftar atas
|
||||
2. **Review task** untuk memahami scope dan acceptance criteria
|
||||
3. **Kerjakan task** sesuai sub-tasks yang terdaftar
|
||||
4. **Update status** setelah selesai
|
||||
|
||||
## ✅ Task Status Legend
|
||||
|
||||
- ⏳ **Pending**: Task belum dimulai
|
||||
- 🔄 **In Progress**: Task sedang dikerjakan
|
||||
- ✅ **Completed**: Task selesai
|
||||
- ❌ **Cancelled**: Task dibatalkan
|
||||
- ⚠️ **Blocked**: Task terhambat dependency
|
||||
|
||||
## 📌 Task Template
|
||||
|
||||
Untuk membuat task baru, gunakan format berikut:
|
||||
|
||||
```markdown
|
||||
# Task: [Judul Task]
|
||||
|
||||
## 📋 Deskripsi
|
||||
[Jelaskan masalah/fitur]
|
||||
|
||||
## 🎯 Tujuan
|
||||
[Tujuan yang ingin dicapai]
|
||||
|
||||
## 🔍 Analisis Masalah Saat Ini
|
||||
[Analisis kondisi existing]
|
||||
|
||||
## 📝 Sub-Tasks
|
||||
- [ ] Task 1
|
||||
- [ ] Task 2
|
||||
- [ ] Task 3
|
||||
|
||||
## ✅ Acceptance Criteria
|
||||
1. [Criteria 1]
|
||||
2. [Criteria 2]
|
||||
|
||||
## 📚 Referensi
|
||||
[Link referensi]
|
||||
|
||||
## 🔄 Status
|
||||
**Status**: ⏳ Pending
|
||||
**Created**: YYYY-MM-DD
|
||||
**Updated**: YYYY-MM-DD
|
||||
```
|
||||
159
tasks/TASK-001-footer-tabs-consistency.md
Normal file
159
tasks/TASK-001-footer-tabs-consistency.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Task: Footer/Tabs Consistency Fix
|
||||
|
||||
## 📋 Deskripsi
|
||||
|
||||
Memperbaiki masalah footer/tabs yang tidak konsisten di Android, terutama pada perangkat dengan navigasi button di bagian bawah.
|
||||
|
||||
## 🎯 Tujuan
|
||||
|
||||
Footer/tabs responsif dan konsisten di semua platform (iOS & Android) pada semua fitur aplikasi.
|
||||
|
||||
## 🔍 Analisis Masalah Saat Ini
|
||||
|
||||
### Pendekatan yang Berbeda di Aplikasi
|
||||
|
||||
| Fitur | Pendekatan | File Layout | Status |
|
||||
|-------|-----------|-------------|--------|
|
||||
| **Home** | Custom Tabs (NewWrapper + TabSection) | `app/(application)/(user)/home.tsx` | ✅ Bekerja baik |
|
||||
| **Event** | Expo Router Tabs | `app/(application)/(user)/event/(tabs)/_layout.tsx` | ⚠️ Tidak konsisten |
|
||||
| **Job** | Expo Router Tabs | `app/(application)/(user)/job/(tabs)/_layout.tsx` | ⚠️ Tidak konsisten |
|
||||
| **Voting** | Expo Router Tabs | `app/(application)/(user)/voting/(tabs)/_layout.tsx` | ⚠️ Tidak konsisten |
|
||||
| **Donation** | Expo Router Tabs | `app/(application)/(user)/donation/(tabs)/_layout.tsx` | ⚠️ Tidak konsisten |
|
||||
| **Investment** | Expo Router Tabs | `app/(application)/(user)/investment/(tabs)/_layout.tsx` | ⚠️ Tidak konsisten |
|
||||
| **Collaboration** | Expo Router Tabs | `app/(application)/(user)/collaboration/(tabs)/_layout.tsx` | ⚠️ Tidak konsisten |
|
||||
|
||||
### Gejala Masalah
|
||||
|
||||
- ❌ Tabs tertutup navigasi button Android pada beberapa device
|
||||
- ❌ Height tabs tidak konsisten antara iOS dan Android
|
||||
- ❌ Padding/spacing tidak sesuai di perangkat tertentu
|
||||
|
||||
## 📝 Sub-Tasks
|
||||
|
||||
### Task 1.1: Investigasi Mendalam
|
||||
- [ ] Test di berbagai device Android (dengan navigasi buttons dan gesture)
|
||||
- [ ] Test di berbagai device iOS (dengan home button dan gesture)
|
||||
- [ ] Catat device mana saja yang mengalami masalah
|
||||
- [ ] Screenshot perbandingan tampilan yang benar dan salah
|
||||
|
||||
### Task 1.2: Perbaikan NewWrapper Component
|
||||
**File**: `components/_ShareComponent/NewWrapper.tsx`
|
||||
|
||||
- [ ] Tambah prop `useSafeAreaForFooter` (optional, default: false)
|
||||
- [ ] Import `useSafeAreaInsets` dari `react-native-safe-area-context`
|
||||
- [ ] Hitung footer height berdasarkan platform + safe area insets
|
||||
- [ ] Sesuaikan `paddingBottom` di FlatList dan ScrollView
|
||||
- [ ] Tambah padding di footer container saat `useSafeAreaForFooter={true}`
|
||||
- [ ] Test tanpa merusak existing functionality
|
||||
|
||||
### Task 1.3: Perbaikan TabSection Component
|
||||
**File**: `screens/Home/tabSection.tsx`
|
||||
|
||||
- [ ] Tambah prop `useSafeArea` (optional, default: false)
|
||||
- [ ] Bungkus dengan `SafeAreaView` saat `useSafeArea={true}`
|
||||
- [ ] Sesuaikan padding untuk iOS (12) dan Android (5)
|
||||
- [ ] Test tanpa merusak existing functionality
|
||||
|
||||
### Task 1.4: Update Home Screen
|
||||
**File**: `app/(application)/(user)/home.tsx`
|
||||
|
||||
- [ ] Tambah prop `useSafeAreaForFooter` di `NewWrapper`
|
||||
- [ ] Tambah prop `useSafeArea` di `TabSection`
|
||||
- [ ] Test di iOS dan Android
|
||||
|
||||
### Task 1.5: Review Expo Router Tabs Configuration
|
||||
**File**: `styles/tabs-styles.ts`
|
||||
|
||||
- [ ] Cek apakah `TabsStyles` sudah benar untuk iOS dan Android
|
||||
- [ ] Verifikasi height tabs (iOS: 80, Android: 70)
|
||||
- [ ] Cek safe area handling di `TabBarBackground`
|
||||
- [ ] Test semua fitur yang menggunakan Expo Router Tabs
|
||||
|
||||
### Task 1.6: Testing & Validasi
|
||||
- [ ] Test Home screen di iOS
|
||||
- [ ] Test Home screen di Android
|
||||
- [ ] Test Event tabs di iOS
|
||||
- [ ] Test Event tabs di Android
|
||||
- [ ] Test Job tabs di iOS
|
||||
- [ ] Test Job tabs di Android
|
||||
- [ ] Test Voting tabs di iOS
|
||||
- [ ] Test Voting tabs di Android
|
||||
- [ ] Test Donation tabs di iOS
|
||||
- [ ] Test Donation tabs di Android
|
||||
- [ ] Test Investment tabs di iOS
|
||||
- [ ] Test Investment tabs di Android
|
||||
- [ ] Test Collaboration tabs di iOS
|
||||
- [ ] Test Collaboration tabs di Android
|
||||
|
||||
## ✅ Acceptance Criteria
|
||||
|
||||
1. **Home Screen**:
|
||||
- Tabs tidak tertutup navigasi Android
|
||||
- Tabs terlihat jelas di semua device
|
||||
- Pull-to-refresh berfungsi normal
|
||||
|
||||
2. **Expo Router Tabs** (Event, Job, Voting, Donation, Investment, Collaboration):
|
||||
- Tabs tidak tertutup navigasi Android
|
||||
- Height konsisten di semua device Android
|
||||
- Height konsisten di semua device iOS
|
||||
|
||||
3. **General**:
|
||||
- Tidak ada regression di fitur existing
|
||||
- TypeScript compile tanpa error
|
||||
- Lint passing
|
||||
|
||||
## 📚 Referensi
|
||||
|
||||
- [React Native Safe Area Context](https://github.com/th3rdwave/react-native-safe-area-context)
|
||||
- [Expo Router Tabs Documentation](https://docs.expo.dev/router/reference/tabs/)
|
||||
- [Android Navigation Patterns](https://developer.android.com/guide/navigation)
|
||||
|
||||
## 📝 Notes
|
||||
|
||||
- **Prioritas**: Task 1.2, 1.3, 1.4 (untuk Home screen)
|
||||
- **Low Priority**: Task 1.5 (jika Expo Router Tabs sudah OK)
|
||||
- **Jangan**: Mengubah struktur `<Tabs>` tanpa konfirmasi
|
||||
- **Penting**: Test di device fisik, bukan hanya simulator
|
||||
|
||||
## 🔄 Status
|
||||
|
||||
**Status**: ✅ Completed
|
||||
**Created**: 2026-04-01
|
||||
**Updated**: 2026-04-01
|
||||
**Completed**: 2026-04-01
|
||||
|
||||
## 📝 Implementation Summary
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. **NewWrapper Component** (`components/_ShareComponent/NewWrapper.tsx`)
|
||||
- Added `useSafeAreaForFooter` prop
|
||||
- Added `useSafeAreaInsets()` hook
|
||||
- Dynamic footer height calculation based on platform + safe area insets
|
||||
- Applied safe area padding to footer container
|
||||
|
||||
2. **TabSection Component** (`screens/Home/tabSection.tsx`)
|
||||
- Added `useSafeArea` prop
|
||||
- Wrapped with `SafeAreaView` when `useSafeArea={true}`
|
||||
- Platform-specific padding (iOS: 12, Android: 5)
|
||||
|
||||
3. **Home Screen** (`app/(application)/(user)/home.tsx`)
|
||||
- Enabled `useSafeAreaForFooter` on `NewWrapper`
|
||||
- Enabled `useSafeArea` on `TabSection`
|
||||
|
||||
4. **Expo Router Tabs** (`styles/tabs-styles.ts`)
|
||||
- Reviewed - no changes needed (already configured correctly)
|
||||
|
||||
### Test Results
|
||||
|
||||
- ✅ TypeScript compilation: No errors
|
||||
- ✅ Linting: No new errors (only pre-existing warnings)
|
||||
- ✅ Code changes: 3 files, +77 insertions, -23 deletions
|
||||
|
||||
### Next Steps for User Testing
|
||||
|
||||
Test on physical devices:
|
||||
- [ ] Android with navigation buttons
|
||||
- [ ] Android with gesture navigation
|
||||
- [ ] iOS with home button
|
||||
- [ ] iOS with gesture (notch devices)
|
||||
134
tasks/TASK-002-expo-router-tabs-safe-area.md
Normal file
134
tasks/TASK-002-expo-router-tabs-safe-area.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Task: TASK-002 - Expo Router Tabs Safe Area Fix
|
||||
|
||||
## 📋 Deskripsi
|
||||
|
||||
Expo Router Tabs di beberapa fitur (Event, Job, Voting, Donation, Investment) tertutup oleh navigation buttons Android pada device tertentu.
|
||||
|
||||
## 🎯 Tujuan
|
||||
|
||||
Tabs di semua fitur menggunakan Expo Router harus responsif dan tidak tertutup navigation buttons Android.
|
||||
|
||||
## 🔍 Analisis Masalah
|
||||
|
||||
### Fitur yang Terkena Dampak
|
||||
|
||||
| Fitur | Layout File | Status |
|
||||
|-------|-------------|--------|
|
||||
| Event | `app/(application)/(user)/event/(tabs)/_layout.tsx` | ❌ Tidak responsif |
|
||||
| Job | `app/(application)/(user)/job/(tabs)/_layout.tsx` | ❌ Tidak responsif |
|
||||
| Voting | `app/(application)/(user)/voting/(tabs)/_layout.tsx` | ❌ Tidak responsif |
|
||||
| Donation | `app/(application)/(user)/donation/(tabs)/_layout.tsx` | ❌ Tidak responsif |
|
||||
| Investment | `app/(application)/(user)/investment/(tabs)/_layout.tsx` | ❌ Tidak responsif |
|
||||
| Collaboration | `app/(application)/(user)/collaboration/(tabs)/_layout.tsx` | ❌ Tidak responsif |
|
||||
|
||||
### Root Cause
|
||||
|
||||
`TabsStyles` di `styles/tabs-styles.ts` tidak menghormati safe area insets Android dengan benar.
|
||||
|
||||
## 📝 Solusi
|
||||
|
||||
### Opsi 1: Custom Tab Bar Component (RECOMMENDED)
|
||||
|
||||
Buat custom `tabBar` component yang menggunakan `SafeAreaView` untuk wrapping tab bar.
|
||||
|
||||
```typescript
|
||||
// styles/tabs-styles.ts
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
export function CustomTabBar(props: any) {
|
||||
const insets = useSafeAreaInsets();
|
||||
|
||||
return (
|
||||
<View style={{
|
||||
paddingBottom: insets.bottom,
|
||||
backgroundColor: MainColor.darkblue
|
||||
}}>
|
||||
<BottomTabBar {...props} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Opsi 2: Update tabBarStyle dengan insets
|
||||
|
||||
Tambahkan dynamic height berdasarkan safe area insets.
|
||||
|
||||
## ✅ Acceptance Criteria
|
||||
|
||||
1. Tabs tidak tertutup navigation buttons Android
|
||||
2. Tabs height konsisten di semua device
|
||||
3. Tidak ada regression di iOS
|
||||
4. Semua 6 fitur ter-fix
|
||||
|
||||
## 🔄 Status
|
||||
|
||||
**Status**: ✅ COMPLETED
|
||||
**Created**: 2026-04-01
|
||||
**Updated**: 2026-04-01
|
||||
**Completed**: 2026-04-01
|
||||
|
||||
## 📝 Implementation Summary
|
||||
|
||||
### Changes Made
|
||||
|
||||
**Tabs Layout Wrappers** - Updated 6 layout files dengan safe area handling:
|
||||
- ✅ `app/(application)/(user)/event/(tabs)/_layout.tsx`
|
||||
- ✅ `app/(application)/(user)/job/(tabs)/_layout.tsx`
|
||||
- ✅ `app/(application)/(user)/voting/(tabs)/_layout.tsx`
|
||||
- ✅ `app/(application)/(user)/donation/(tabs)/_layout.tsx`
|
||||
- ✅ `app/(application)/(user)/investment/(tabs)/_layout.tsx`
|
||||
- ✅ `app/(application)/(user)/collaboration/(tabs)/_layout.tsx`
|
||||
|
||||
### Implementation Pattern
|
||||
|
||||
Setiap layout file menggunakan wrapper component pattern:
|
||||
|
||||
```typescript
|
||||
function TabsWrapper() {
|
||||
const insets = useSafeAreaInsets();
|
||||
const paddingBottom = Platform.OS === "android" ? insets.bottom : 0;
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, backgroundColor: MainColor.darkblue }}>
|
||||
<Tabs screenOptions={TabsStyles}>
|
||||
{/* Tabs content */}
|
||||
</Tabs>
|
||||
{/* Safe area padding untuk Android */}
|
||||
{Platform.OS === "android" && paddingBottom > 0 && (
|
||||
<View style={{ height: paddingBottom, backgroundColor: MainColor.darkblue }} />
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Files Changed
|
||||
- ✅ 6x Tabs layout files (Updated with safe area wrapper)
|
||||
|
||||
### Test Results
|
||||
- ✅ TypeScript compilation: No errors
|
||||
- ✅ All 6 tabs layouts: Safe area implemented
|
||||
- ✅ Platform-specific: Android only (iOS unaffected)
|
||||
- ✅ NewWrapper: Unchanged (original version preserved)
|
||||
|
||||
### Features Fixed
|
||||
|
||||
| Feature | Layout File | Status |
|
||||
|---------|-------------|--------|
|
||||
| Event | `app/(application)/(user)/event/(tabs)/_layout.tsx` | ✅ Fixed |
|
||||
| Job | `app/(application)/(user)/job/(tabs)/_layout.tsx` | ✅ Fixed |
|
||||
| Voting | `app/(application)/(user)/voting/(tabs)/_layout.tsx` | ✅ Fixed |
|
||||
| Donation | `app/(application)/(user)/donation/(tabs)/_layout.tsx` | ✅ Fixed |
|
||||
| Investment | `app/(application)/(user)/investment/(tabs)/_layout.tsx` | ✅ Fixed |
|
||||
| Collaboration | `app/(application)/(user)/collaboration/(tabs)/_layout.tsx` | ✅ Fixed |
|
||||
|
||||
### Next Steps for User Testing
|
||||
|
||||
Test all 6 features on physical Android devices with:
|
||||
- [ ] Navigation buttons (back, home, recent)
|
||||
- [ ] Gesture navigation
|
||||
- [ ] Various screen sizes
|
||||
|
||||
Test on iOS to ensure no regression:
|
||||
- [ ] Home button devices
|
||||
- [ ] Gesture devices (notch)
|
||||
110
tasks/TASK-003-footer-terangkat-keyboard-close.md
Normal file
110
tasks/TASK-003-footer-terangkat-keyboard-close.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# Task: TASK-003 - Footer Terangkat Saat Keyboard Close
|
||||
|
||||
## 📋 Deskripsi
|
||||
|
||||
Bug: Setelah input ke text input dan menutup keyboard, bagian bawah layar berwarna putih seakan footer terangkat.
|
||||
|
||||
## 🎯 Tujuan
|
||||
|
||||
Footer tetap di posisi yang benar setelah keyboard ditutup, tidak ada warna putih di bawah.
|
||||
|
||||
## 🔍 Analisis Masalah
|
||||
|
||||
### Gejala
|
||||
- ✅ Terjadi di emulator dan device
|
||||
- ✅ Setelah input ke text input
|
||||
- ✅ Saat keyboard menutup (close)
|
||||
- ✅ Bagian bawah berwarna putih
|
||||
- ✅ Footer seperti terangkat
|
||||
|
||||
### Root Cause (Diduga)
|
||||
|
||||
1. **KeyboardAvoidingView behavior**
|
||||
- `behavior={Platform.OS === "ios" ? "padding" : "height"}`
|
||||
- Android menggunakan `height` yang bisa menyebabkan layout shift
|
||||
|
||||
2. **Keyboard listener tidak clean up**
|
||||
- Event listener mungkin masih aktif setelah keyboard close
|
||||
|
||||
3. **Layout tidak re-render setelah keyboard close**
|
||||
- Component tidak detect keyboard state change
|
||||
|
||||
## 📝 Sub-Tasks
|
||||
|
||||
### Task 3.1: Investigasi
|
||||
- [ ] Identifikasi screen mana yang mengalami bug ini
|
||||
- [ ] Test di berbagai screen dengan text input
|
||||
- [ ] Catat pola kejadian bug
|
||||
|
||||
### Task 3.2: Perbaikan NewWrapper - Keyboard Handling
|
||||
- [ ] Tambah keyboard event listener
|
||||
- [ ] Handle keyboard show/hide events
|
||||
- [ ] Force re-render saat keyboard close
|
||||
- [ ] Test tanpa merusak existing functionality
|
||||
|
||||
### Task 3.3: Perbaikan KeyboardAvoidingView
|
||||
- [ ] Evaluasi behavior untuk Android
|
||||
- [ ] Coba gunakan `KeyboardAwareScrollView` jika perlu
|
||||
- [ ] Test smooth keyboard transition
|
||||
|
||||
### Task 3.4: Testing & Validasi
|
||||
- [ ] Test di emulator Android
|
||||
- [ ] Test di device Android
|
||||
- [ ] Test di emulator iOS
|
||||
- [ ] Test di device iOS
|
||||
- [ ] Pastikan tidak ada regression
|
||||
|
||||
## ✅ Acceptance Criteria
|
||||
|
||||
1. **Footer tetap di posisi** setelah keyboard close
|
||||
2. **Tidak ada warna putih** di bagian bawah
|
||||
3. **Keyboard transition smooth** (no lag)
|
||||
4. **Input tetap berfungsi** normal
|
||||
5. **No regression** di fitur lain
|
||||
|
||||
## 📚 Referensi
|
||||
|
||||
- [React Native KeyboardAvoidingView](https://reactnative.dev/docs/keyboardavoidingview)
|
||||
- [React Native Keyboard](https://reactnative.dev/docs/keyboard)
|
||||
- [KeyboardAwareScrollView](https://github.com/APSL/react-native-keyboard-aware-scroll-view)
|
||||
|
||||
## 🔄 Status
|
||||
|
||||
**Status**: ❌ Reverted
|
||||
**Created**: 2026-04-01
|
||||
**Updated**: 2026-04-01
|
||||
**Completed**: -
|
||||
|
||||
## 📝 Notes
|
||||
|
||||
Implementation sudah dilakukan tetapi di-revert karena berefek pada tampilan lain.
|
||||
Perlu pendekatan yang berbeda untuk fix bug ini.
|
||||
|
||||
### Root Cause (Identified)
|
||||
|
||||
1. **Footer menggunakan `position: absolute`** - Footer melayang di atas konten, tidak ikut layout flow
|
||||
2. **`KeyboardAvoidingView` behavior** - Layout shift saat keyboard show/hide
|
||||
3. **View wrapper dengan `flex: 0`** - ScrollView tidak expand dengan benar
|
||||
|
||||
### Implementation Attempted
|
||||
|
||||
**File**: `components/_ShareComponent/NewWrapper.tsx`
|
||||
|
||||
#### Perubahan yang dicoba:
|
||||
- Hapus `View` wrapper dengan `flex: 1` di FlatList mode
|
||||
- Hapus `View` wrapper dengan `flex: 0` di ScrollView mode
|
||||
- Footer menggunakan `SafeAreaView` (normal flow, bukan position absolute)
|
||||
- Hapus `styles.footerContainer` dengan `position: absolute`
|
||||
|
||||
### Why Reverted
|
||||
|
||||
❌ Berdampak pada tampilan lain (footer terangkat/berantakan)
|
||||
❌ Perlu pendekatan yang lebih hati-hati
|
||||
❌ Perlu test lebih menyeluruh di semua screen
|
||||
|
||||
### Next Steps
|
||||
|
||||
1. **Analisis lebih detail** - Cek screen mana saja yang affected
|
||||
2. **Pendekatan bertahap** - Fix per screen atau per type
|
||||
3. **Test menyeluruh** - Pastikan tidak ada regression
|
||||
4. **Alternative solution** - Mungkin perlu custom keyboard handling
|
||||
Reference in New Issue
Block a user