upd: redesign aplikasi

Deskripsi:
- update home, profile dll
- blm selesai

NO Issues
This commit is contained in:
2026-02-10 17:32:56 +08:00
parent d3802ca26c
commit 064a8ccaad
29 changed files with 368 additions and 132 deletions

View File

@@ -1,4 +1,5 @@
import Styles from '@/constants/Styles';
import { useTheme } from "@/providers/ThemeProvider";
import { useRouter } from 'expo-router';
import { Platform, Text, View } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -15,20 +16,22 @@ type Props = {
export default function AppHeader({ title, right, showBack = true, onPressLeft, left }: Props) {
const insets = useSafeAreaInsets();
const router = useRouter();
const { colors } = useTheme();
return (
<View style={[Styles.headerContainer, Platform.OS === 'ios' ? Styles.pb05 : Styles.pb13, { paddingTop: Platform.OS === 'ios' ? insets.top : 10 }]}>
<View style={[Styles.headerContainer, Platform.OS === 'ios' ? Styles.pb05 : Styles.pb13, { backgroundColor: colors.header, paddingTop: Platform.OS === 'ios' ? insets.top : 10 }]}>
<View style={Styles.headerApp}>
{showBack ? (
<ButtonBackHeader onPress={onPressLeft} />
) :
left ? left :
(
<View style={Styles.headerSide} />
)}
<Text style={Styles.headerTitle}>{title}</Text>
<View style={[Styles.rowItemsCenter]}>
{showBack ? (
<ButtonBackHeader onPress={onPressLeft} />
) :
left ? left :
(
<View style={Styles.headerSide} />
)}
<Text style={[Styles.headerTitle, Styles.ml05]}>{title}</Text>
</View>
<View style={Styles.headerSide}>{right}</View>
</View>
</View>

View File

@@ -9,7 +9,7 @@ export default function ButtonBackHeader({ onPress }: Props) {
return (
<>
<ButtonHeader
item={<Feather name="chevron-left" size={20} color="white" />}
item={<Feather name="chevron-left" size={25} color="white" />}
onPress={() => { onPress && onPress() }}
/>
</>

View File

@@ -18,7 +18,7 @@ export function ButtonFiturMenu({ onPress, icon, text }: Props) {
<View style={[Styles.btnFiturMenu, { backgroundColor: colors.card, borderColor: colors.icon + '20', shadowColor: colors.text }]}>
{icon}
</View>
<Text style={[Styles.mt05]}>{text}</Text>
<Text style={[Styles.mt05, { color: colors.text }]}>{text}</Text>
</View>
</TouchableWithoutFeedback>
)

View File

@@ -1,7 +1,6 @@
import { ColorsStatus } from "@/constants/ColorsStatus"
import Styles from "@/constants/Styles"
import Styles from "@/constants/Styles";
import { useTheme } from "@/providers/ThemeProvider";
import { TouchableOpacity } from "react-native"
import { TouchableOpacity } from "react-native";
import Text from "./Text";
type Props = {
@@ -17,9 +16,9 @@ type Props = {
export default function ButtonTab({ active, value, onPress, label, n, icon }: Props) {
const { colors } = useTheme();
return (
<TouchableOpacity style={[Styles.btnTab, (active == value) && ColorsStatus.orange, { width: n == 2 ? '50%' : 'auto' }]} onPress={() => { onPress() }}>
<TouchableOpacity style={[Styles.btnTab, (active == value) && { backgroundColor: colors.tabActive }, { width: n == 2 ? '50%' : 'auto' }]} onPress={() => { onPress() }}>
{icon}
<Text numberOfLines={1} style={[Styles.textMediumSemiBold, Styles.ml10, { color: active == value ? 'white' : colors.text }]}>{label}</Text>
<Text numberOfLines={1} style={[Styles.textMediumSemiBold, Styles.ml10, { color: active == value ? 'white' : colors.dimmed }]}>{label}</Text>
</TouchableOpacity>
)
}

View File

@@ -14,7 +14,7 @@ type Props = {
export default function DiscussionItem({ title, user, date, onPress }: Props) {
const { colors } = useTheme();
return (
<Pressable style={[Styles.wrapItemDiscussion, { backgroundColor: colors.card, borderColor: colors.background }]} onPress={onPress}>
<Pressable style={[Styles.wrapItemDiscussion, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]} onPress={onPress}>
<View style={[Styles.rowItemsCenter, Styles.mb10]}>
<Ionicons name="chatbox-ellipses-outline" size={22} color={colors.text} style={Styles.mr10} />
<View style={[{ flex: 1 }]}>
@@ -23,12 +23,12 @@ export default function DiscussionItem({ title, user, date, onPress }: Props) {
</View>
<View style={[Styles.rowSpaceBetween]}>
<View style={[Styles.rowItemsCenter, { flex: 1 }]}>
<Feather name="user" size={18} color={colors.text} style={Styles.mr05} />
<Text style={[Styles.textInformation]} numberOfLines={1} ellipsizeMode="tail">{user}</Text>
<Feather name="user" size={18} color={colors.dimmed} style={Styles.mr05} />
<Text style={[Styles.textInformation, { color: colors.dimmed }]} numberOfLines={1} ellipsizeMode="tail">{user}</Text>
</View>
<View style={[Styles.rowItemsCenter, { flex: 1, justifyContent: 'flex-end' }]}>
<Feather name="clock" size={18} color={colors.text} style={Styles.mr05} />
<Text style={[Styles.textInformation]}>{date}</Text>
<Feather name="clock" size={18} color={colors.dimmed} style={Styles.mr05} />
<Text style={[Styles.textInformation, { color: colors.dimmed }]}>{date}</Text>
</View>
</View>
</Pressable>

View File

@@ -0,0 +1,143 @@
import { ConstEnv } from "@/constants/ConstEnv";
import Styles from "@/constants/Styles";
import { apiGetBanner, apiGetProfile } from "@/lib/api";
import { setEntities } from "@/lib/bannerSlice";
import { setEntityUser } from "@/lib/userSlice";
import { useAuthSession } from "@/providers/AuthProvider";
import { useTheme } from "@/providers/ThemeProvider";
import { AntDesign, Feather, FontAwesome5, Ionicons, MaterialCommunityIcons, MaterialIcons, } from "@expo/vector-icons";
import { router } from "expo-router";
import React, { useEffect } from "react";
import { Dimensions, Image, View } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import Carousel, { ICarouselInstance } from "react-native-reanimated-carousel";
import { useDispatch, useSelector } from "react-redux";
import { ButtonFiturMenu } from "../buttonFiturMenu";
import Text from "../Text";
export default function CaraouselHome2({ refreshing }: { refreshing: boolean }) {
const { decryptToken, token } = useAuthSession()
const { colors } = useTheme();
const ref = React.useRef<ICarouselInstance>(null);
const width = Dimensions.get("window").width;
const progress = useSharedValue<number>(0);
const dispatch = useDispatch()
const entities = useSelector((state: any) => state.banner)
const entityUser = useSelector((state: any) => state.user)
async function handleBannerView() {
const hasil = await decryptToken(String(token?.current))
apiGetBanner({ user: hasil }).then((data) => {
if (data.data.length > 0) {
dispatch(setEntities(data.data))
} else {
dispatch(setEntities([]))
}
})
}
async function handleUser() {
const hasil = await decryptToken(String(token?.current))
const response = await apiGetProfile({ id: hasil })
dispatch(setEntityUser({ role: response.data.idUserRole, admin: false }))
}
useEffect(() => {
if (refreshing)
handleBannerView()
}, [refreshing]);
useEffect(() => {
handleBannerView()
}, [dispatch]);
useEffect(() => {
handleUser()
}, []);
return (
<View
style={[
Styles.mv15,
Styles.p15,
Styles.round05,
{ backgroundColor: colors.card },
Styles.wrapHomeCarousel
]}
>
{/* WRAPPER CAROUSEL */}
<View
style={{
justifyContent: 'center', // ⬅️ vertical center
alignItems: 'center',
}}
>
{entities.length > 0 ? (
<Carousel
ref={ref}
width={width - 30} // ⬅️ biar ga nabrak padding hijau
height={width / 2.6}
data={entities}
loop
autoPlay
autoPlayInterval={5000}
onProgressChange={progress}
renderItem={({ index }) => (
<Image
source={{ uri: `${ConstEnv.url_storage}/files/${entities[index].image}` }}
style={[
Styles.caraoselContent,
Styles.round05,
{
backgroundColor: colors.primary
},
]}
resizeMode="cover"
/>
)}
/>
) : (
<View
style={[
Styles.caraoselContent,
{
height: width / 2.5,
backgroundColor: colors.primary,
justifyContent: 'center',
alignItems: 'center',
},
]}
>
<Text style={[Styles.textDefault, Styles.cWhite]}>BANNER</Text>
</View>
)}
</View>
{/* FITUR */}
<View style={[Styles.mt15]}>
<View style={Styles.rowSpaceBetween}>
<ButtonFiturMenu
icon={<Feather name="users" size={30} color={colors.icon} />}
text="Divisi"
onPress={() => router.push('/division?active=true')}
/>
<ButtonFiturMenu
icon={<Feather name="bar-chart" size={30} color={colors.icon} />}
text="Kegiatan"
onPress={() => router.push('/project?status=0')}
/>
<ButtonFiturMenu
icon={<Ionicons name="megaphone-outline" size={30} color={colors.icon} />}
text="Pengumuman"
onPress={() => router.push('/announcement')}
/>
<ButtonFiturMenu
icon={<Ionicons name="grid-outline" size={30} color={colors.icon} />}
text="Semua"
onPress={() => router.push('/feature')}
/>
</View>
</View>
</View>
)
}

View File

@@ -58,7 +58,7 @@ export default function ChartProgresHome({ refreshing }: { refreshing: boolean }
data={data}
showText
showValuesAsTooltipText
textColor={colors.text}
textColor={'black'}
radius={120}
textSize={15}
focusOnPress={false}

View File

@@ -7,6 +7,7 @@ import { View } from "react-native";
import DiscussionItem from "../discussionItem";
import Skeleton from "../skeleton";
import Text from "../Text";
import { useTheme } from "@/providers/ThemeProvider";
type Props = {
id: string
@@ -21,6 +22,7 @@ export default function DisccussionHome({ refreshing }: { refreshing: boolean })
const { decryptToken, token } = useAuthSession()
const [data, setData] = useState<Props[]>([])
const [loading, setLoading] = useState(true)
const { colors } = useTheme();
async function handleData(loading: boolean) {
@@ -48,7 +50,7 @@ export default function DisccussionHome({ refreshing }: { refreshing: boolean })
return (
<View style={[Styles.mb15]}>
<Text style={[Styles.textDefaultSemiBold, Styles.mv10]}>Diskusi</Text>
<View style={[Styles.wrapPaper]}>
<View style={[Styles.wrapPaper, { backgroundColor: colors.card, borderColor: colors.icon + '20' }, Styles.p0]}>
{
loading ?
<>

View File

@@ -13,10 +13,10 @@ export default function FiturHome() {
<View style={[Styles.mb15]}>
<Text style={[Styles.textDefaultSemiBold, Styles.mv10]}>Fitur</Text>
<View style={[Styles.rowSpaceBetween]}>
<ButtonFiturMenu icon={<MaterialIcons name="group" size={35} color="black" />} text="Divisi" onPress={() => { router.push('/division?active=true') }} />
<ButtonFiturMenu icon={<AntDesign name="areachart" size={35} color="black" />} text="Kegiatan" onPress={() => { router.push('/project?status=0') }} />
<ButtonFiturMenu icon={<MaterialIcons name="campaign" size={35} color="black" />} text="Pengumuman" onPress={() => { router.push('/announcement') }} />
<ButtonFiturMenu icon={<MaterialCommunityIcons name="view-grid" size={35} color="black" />} text="Semua" onPress={() => { router.push('/feature') }} />
<ButtonFiturMenu icon={<MaterialIcons name="group" size={35} color={colors.icon} />} text="Divisi" onPress={() => { router.push('/division?active=true') }} />
<ButtonFiturMenu icon={<AntDesign name="areachart" size={35} color={colors.icon} />} text="Kegiatan" onPress={() => { router.push('/project?status=0') }} />
<ButtonFiturMenu icon={<MaterialIcons name="campaign" size={35} color={colors.icon} />} text="Pengumuman" onPress={() => { router.push('/announcement') }} />
<ButtonFiturMenu icon={<MaterialCommunityIcons name="view-grid" size={35} color={colors.icon} />} text="Semua" onPress={() => { router.push('/feature') }} />
</View>
</View>
)

View File

@@ -29,7 +29,7 @@ export function HeaderRightHome() {
return (
<View style={[Styles.rowSpaceBetween, { width: 140 }]}>
<View style={[Styles.rowSpaceBetween, { width: 90 }]}>
<ButtonHeader item={<Feather name="search" size={18} color="white" />} onPress={() => { router.push('/search') }} />
<ButtonHeader valueTop={notification > 0 ? true : false} item={<Feather name="bell" size={18} color="white" />} onPress={() => { router.push('/notification') }} />
<ButtonHeader item={<Feather name="user" size={18} color="white" />} onPress={() => { router.push('/profile') }} />

View File

@@ -10,6 +10,7 @@ import PaperGridContent from "../paperGridContent";
import ProgressBar from "../progressBar";
import Skeleton from "../skeleton";
import Text from "../Text";
import { useTheme } from "@/providers/ThemeProvider";
type Props = {
id: string
@@ -26,6 +27,7 @@ export default function ProjectHome({ refreshing }: { refreshing: boolean }) {
const width = Dimensions.get("window").width;
const [data, setData] = useState<Props[]>([])
const [loading, setLoading] = useState(true)
const { colors } = useTheme();
async function handleData(loading: boolean) {
try {
@@ -72,7 +74,7 @@ export default function ProjectHome({ refreshing }: { refreshing: boolean }) {
<PaperGridContent titleTail={1} content="carousel" onPress={() => { router.push(`/project/${data[index].id}`) }} title={data[index].title} headerColor="primary">
<ProgressBar value={data[index].progress} category="carousel" />
<View style={[Styles.rowSpaceBetween]}>
<Text style={[Styles.textDefault, Styles.cGray]}>{data[index].createdAt}</Text>
<Text style={[Styles.textDefault, { color: colors.dimmed }]}>{data[index].createdAt}</Text>
<LabelStatus
size="default"
category={

View File

@@ -1,4 +1,4 @@
import { ColorsStatus } from "@/constants/ColorsStatus";
import { useTheme } from "@/providers/ThemeProvider";
import Styles from "@/constants/Styles";
import { View, StyleProp, ViewStyle } from "react-native";
import Text from "./Text";
@@ -10,10 +10,20 @@ type Props = {
style?: StyleProp<ViewStyle>
}
export default function LabelStatus({ category, text, size, style }: Props) {
const { colors } = useTheme();
const backgroundColor = {
primary: colors.primary,
success: colors.success,
warning: colors.warning,
error: colors.error,
secondary: colors.dimmed,
}[category] || 'gray'; // Removed ColorsStatus[category]?.backgroundColor as ColorsStatus is removed
return (
<View style={[
size == "small" ? Styles.labelStatusSmall : Styles.labelStatus,
ColorsStatus[category],
{ backgroundColor },
Styles.round10,
Styles.contentItemCenter,
style

View File

@@ -18,8 +18,8 @@ export default function PaperGridContent({ content, children, title, headerColor
const { colors } = useTheme();
return (
<Pressable onPress={onPress}>
<View style={[content == 'carousel' ? Styles.wrapGridCaraousel : Styles.wrapGridContent, headerColor == 'warning' ? ColorsStatus.warning : { backgroundColor: colors.primary }]}>
<View style={[Styles.headerPaperGrid]}>
<View style={[content == 'carousel' ? Styles.wrapGridCaraousel : Styles.wrapGridContent]}>
<View style={[Styles.headerPaperGrid, headerColor == 'warning' ? ColorsStatus.warning : { backgroundColor: colors.primary }]}>
<Text numberOfLines={titleTail ? titleTail : undefined} style={[Styles.textSubtitle, headerColor == 'warning' ? Styles.cDefault : Styles.cWhite, { textAlign: 'center' }]}>{title}</Text>
</View>
<View style={[

16
components/wrapTab.tsx Normal file
View File

@@ -0,0 +1,16 @@
import Styles from "@/constants/Styles";
import { useTheme } from "@/providers/ThemeProvider";
import { View } from "react-native";
type Props = {
children: React.ReactNode
}
export default function WrapTab({ children }: Props) {
const { colors } = useTheme()
return (
<View style={[Styles.wrapBtnTab, { backgroundColor: colors.card, borderColor: colors.icon + '20' }]}>
{children}
</View>
)
}