Deskripsi:

Fix: event beranda dan status > tampilan
Feature: create event dan halaman detail status
Fix: Basebox: Hide safearea kalau ada tabs
Fix: TextCustom: Tambah size xlarge
Fix: ScrollCustom penambhan props value

# No Issue
This commit is contained in:
2025-07-18 16:30:56 +08:00
parent b8b1efc71e
commit f9e96aa077
12 changed files with 346 additions and 70 deletions

View File

@@ -1,30 +1,151 @@
import {
AvatarCustom,
BaseBox,
Grid,
StackCustom,
TextCustom,
} from "@/components";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper"; import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import FloatingButton from "@/components/Button/FloatingButton"; import FloatingButton from "@/components/Button/FloatingButton";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { GStyles } from "@/styles/global-styles";
import { router } from "expo-router"; import { router } from "expo-router";
import { Text, TouchableHighlight, View } from "react-native";
export default function Event() { export default function Event() {
const index = "test-id-event";
return ( return (
<ViewWrapper <ViewWrapper
hideFooter
floatingButton={ floatingButton={
<FloatingButton onPress={() => router.push("/event/create")} /> <FloatingButton onPress={() => router.push("/event/create")} />
} }
> >
<TouchableHighlight onPress={() => router.push("/event/detail/1")}> {/* {Array.from({ length: 10 }).map((_, index) => (
<View <BaseBox key={index}>
style={{ <StackCustom gap={"xs"}>
padding: 20, <Grid>
backgroundColor: MainColor.darkblue, <Grid.Col span={2}>
borderRadius: 10, <AvatarCustom href={`/profile/${index}`} />
borderColor: AccentColor.blue, </Grid.Col>
borderWidth: 1, <Grid.Col span={10} style={{ justifyContent: "center" }}>
}} <TextCustom bold>Username</TextCustom>
> </Grid.Col>
<Text style={GStyles.textLabel}>Event</Text> </Grid>
</View> <TextCustom truncate bold>
</TouchableHighlight> Lorem ipsum dolor sit
</TextCustom>
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro sed
doloremque tempora soluta. Dolorem ex quidem ipsum tempora, ipsa,
obcaecati quia suscipit numquam, voluptates commodi porro impedit
natus quos doloremque!
</TextCustom>
</StackCustom>
</BaseBox>
))} */}
<BaseBox>
<StackCustom gap={"xs"}>
<Grid>
<Grid.Col span={2}>
<AvatarCustom href={`/profile/${index}`} />
</Grid.Col>
<Grid.Col span={10} style={{ justifyContent: "center" }}>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
</Grid>
<TextCustom truncate bold>
Lorem ipsum dolor sit
</TextCustom>
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro sed
doloremque tempora soluta. Dolorem ex quidem ipsum tempora, ipsa,
obcaecati quia suscipit numquam, voluptates commodi porro impedit
natus quos doloremque!
</TextCustom>
</StackCustom>
</BaseBox>
<BaseBox>
<StackCustom gap={"xs"}>
<Grid>
<Grid.Col span={2}>
<AvatarCustom href={`/profile/${index}`} />
</Grid.Col>
<Grid.Col span={10} style={{ justifyContent: "center" }}>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
</Grid>
<TextCustom truncate bold>
Lorem ipsum dolor sit
</TextCustom>
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro sed
doloremque tempora soluta. Dolorem ex quidem ipsum tempora, ipsa,
obcaecati quia suscipit numquam, voluptates commodi porro impedit
natus quos doloremque!
</TextCustom>
</StackCustom>
</BaseBox>
<BaseBox>
<StackCustom gap={"xs"}>
<Grid>
<Grid.Col span={2}>
<AvatarCustom href={`/profile/${index}`} />
</Grid.Col>
<Grid.Col span={10} style={{ justifyContent: "center" }}>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
</Grid>
<TextCustom truncate bold>
Lorem ipsum dolor sit
</TextCustom>
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro sed
doloremque tempora soluta. Dolorem ex quidem ipsum tempora, ipsa,
obcaecati quia suscipit numquam, voluptates commodi porro impedit
natus quos doloremque!
</TextCustom>
</StackCustom>
</BaseBox>
<BaseBox>
<StackCustom gap={"xs"}>
<Grid>
<Grid.Col span={2}>
<AvatarCustom href={`/profile/${index}`} />
</Grid.Col>
<Grid.Col span={10} style={{ justifyContent: "center" }}>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
</Grid>
<TextCustom truncate bold>
Lorem ipsum dolor sit
</TextCustom>
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro sed
doloremque tempora soluta. Dolorem ex quidem ipsum tempora, ipsa,
obcaecati quia suscipit numquam, voluptates commodi porro impedit
natus quos doloremque!
</TextCustom>
</StackCustom>
</BaseBox>
<BaseBox>
<StackCustom gap={"xs"}>
<Grid>
<Grid.Col span={2}>
<AvatarCustom href={`/profile/${index}`} />
</Grid.Col>
<Grid.Col span={10} style={{ justifyContent: "center" }}>
<TextCustom bold>Username</TextCustom>
</Grid.Col>
</Grid>
<TextCustom truncate bold>
Lorem ipsum dolor sit
</TextCustom>
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Porro sed
doloremque tempora soluta. Dolorem ex quidem ipsum tempora, ipsa,
obcaecati quia suscipit numquam, voluptates commodi porro impedit
natus quos doloremque!
</TextCustom>
</StackCustom>
</BaseBox>
</ViewWrapper> </ViewWrapper>
); );
} }

View File

@@ -1,11 +1,63 @@
import {
BaseBox,
Grid,
ScrollableCustom,
StackCustom,
TextCustom,
} from "@/components";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper"; import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { GStyles } from "@/styles/global-styles"; import { masterStatus } from "@/lib/dummy-data/_master/status";
import { Text } from "react-native"; import { useState } from "react";
export default function Status() { export default function Status() {
const id = "test-id-event";
const [activeCategory, setActiveCategory] = useState<string | null>(
"publish"
);
const handlePress = (item: any) => {
setActiveCategory(item.value);
// tambahkan logika lain seperti filter dsb.
};
const scrollComponent = (
<ScrollableCustom
data={masterStatus.map((e, i) => ({
id: i,
label: e.label,
value: e.value,
}))}
onButtonPress={handlePress}
activeId={activeCategory as any}
/>
);
return ( return (
<ViewWrapper> <ViewWrapper headerComponent={scrollComponent}>
<Text style={GStyles.textLabel}>Status</Text> <BaseBox href={`/event/${id}/${activeCategory}/detail-event`}>
<StackCustom gap={"xs"}>
<Grid>
<Grid.Col span={8}>
<TextCustom truncate bold>
Lorem ipsum,{" "}
<TextCustom color="green">{activeCategory}</TextCustom> dolor
sit amet consectetur adipisicing elit.
</TextCustom>
</Grid.Col>
<Grid.Col span={4} style={{ alignItems: "flex-end" }}>
<TextCustom>{new Date().toLocaleDateString()}</TextCustom>
</Grid.Col>
</Grid>
<TextCustom truncate={2}>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur
eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora,
atque. Aperiam minima asperiores dicta perferendis quis adipisci,
dolore optio porro!
</TextCustom>
</StackCustom>
</BaseBox>
</ViewWrapper> </ViewWrapper>
); );
} }

View File

@@ -0,0 +1,69 @@
import {
BaseBox,
Grid,
Spacing,
StackCustom,
TextCustom,
ViewWrapper,
} from "@/components";
import LeftButtonCustom from "@/components/Button/BackButton";
import { Stack, useLocalSearchParams } from "expo-router";
export default function EventDetail() {
const { id, detail } = useLocalSearchParams();
const listData = [
{
title: "Lokasi",
value:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
},
{
title: "Tipe Acara",
value: "Workshop",
},
{
title: "Tanggal Mulai",
value: "Senin, 18 Juli 2025, 10:00 WIB",
},
{
title: "Tanggal Berakhir",
value: "Selasa, 19 Juli 2025, 12:00 WIB",
},
{
title: "Deskripsi",
value:
"Lorem ipsum dolor sit amet consectetur adipisicing elit. Consectetur eveniet ab eum ducimus tempore a quia deserunt quisquam. Tempora, atque. Aperiam minima asperiores dicta perferendis quis adipisci, dolore optio porro!",
},
];
return (
<>
<Stack.Screen
options={{
title: `Detail ${detail}`,
headerLeft: () => <LeftButtonCustom />,
}}
/>
<ViewWrapper>
<BaseBox>
<StackCustom>
<TextCustom bold align="center" size="xlarge">
Judul event {id}
</TextCustom>
{listData.map((item, index) => (
<Grid key={index}>
<Grid.Col span={4}>
<TextCustom bold>{item.title}</TextCustom>
</Grid.Col>
<Grid.Col span={8}>
<TextCustom>{item.value}</TextCustom>
</Grid.Col>
</Grid>
))}
</StackCustom>
</BaseBox>
</ViewWrapper>
</>
);
}

View File

@@ -41,14 +41,14 @@ export default function EventCreate() {
}; };
const buttonSubmit = ( const buttonSubmit = (
<BoxButtonOnFooter>
<ButtonCustom title="Simpan" onPress={handlerSubmit} /> <ButtonCustom title="Simpan" onPress={handlerSubmit} />
</BoxButtonOnFooter> // <BoxButtonOnFooter>
// </BoxButtonOnFooter>
); );
return ( return (
<> <>
<ViewWrapper footerComponent={buttonSubmit}> <ViewWrapper>
<StackCustom gap={"xs"}> <StackCustom gap={"xs"}>
<TextInputCustom <TextInputCustom
placeholder="Masukkan nama event" placeholder="Masukkan nama event"
@@ -93,7 +93,10 @@ export default function EventCreate() {
showCount showCount
maxLength={100} maxLength={100}
/> />
{buttonSubmit}
</StackCustom> </StackCustom>
</ViewWrapper> </ViewWrapper>
</> </>
); );

View File

@@ -11,18 +11,18 @@ import { useState } from "react";
import { View } from "react-native"; import { View } from "react-native";
const categories = [ const categories = [
{ id: 1, label: "Semua" }, { value: "all", label: "Semua" },
{ id: 2, label: "Event" }, { value: "event", label: "Event" },
{ id: 3, label: "Job" }, { value: "job", label: "Job" },
{ id: 4, label: "Voting" }, { value: "voting", label: "Voting" },
{ id: 5, label: "Donasi" }, { value: "donasi", label: "Donasi" },
{ id: 6, label: "Investasi" }, { value: "investasi", label: "Investasi" },
{ id: 7, label: "Forum" }, { value: "forum", label: "Forum" },
{ id: 8, label: "Collaboration" }, { value: "collaboration", label: "Collaboration" },
]; ];
const selectedCategory = (id: number) => { const selectedCategory = (value: string) => {
const category = categories.find((c) => c.id === id); const category = categories.find((c) => c.value === value);
return category?.label; return category?.label;
}; };
@@ -31,7 +31,7 @@ const BoxNotification = ({
activeCategory, activeCategory,
}: { }: {
index: number; index: number;
activeCategory: number | null; activeCategory: string | null;
}) => { }) => {
return ( return (
<> <>
@@ -39,13 +39,13 @@ const BoxNotification = ({
onPress={() => onPress={() =>
console.log( console.log(
"Notification >", "Notification >",
selectedCategory(activeCategory as number) selectedCategory(activeCategory as string)
) )
} }
> >
<StackCustom> <StackCustom>
<TextCustom bold> <TextCustom bold>
# {selectedCategory(activeCategory as number)} # {selectedCategory(activeCategory as string)}
</TextCustom> </TextCustom>
<View <View
@@ -81,38 +81,31 @@ const BoxNotification = ({
}; };
export default function Notifications() { export default function Notifications() {
const [activeCategory, setActiveCategory] = useState<number | null>(1); const [activeCategory, setActiveCategory] = useState<string | null>("all");
const handlePress = (item: any) => { const handlePress = (item: any) => {
setActiveCategory(item.id); setActiveCategory(item.value);
// tambahkan logika lain seperti filter dsb. // tambahkan logika lain seperti filter dsb.
}; };
return ( return (
<ViewWrapper <ViewWrapper
headerComponent={ headerComponent={
<ScrollableCustom <ScrollableCustom
data={categories} data={categories.map((e, i) => ({
id: i,
label: e.label,
value: e.value,
}))}
onButtonPress={handlePress} onButtonPress={handlePress}
activeId={activeCategory as any} activeId={activeCategory as string}
/> />
} }
> >
{Array.from({ length: 20 }).map((e, i) => ( {Array.from({ length: 20 }).map((e, i) => (
<View key={i}> <View key={i}>
<BoxNotification index={i} activeCategory={activeCategory} /> <BoxNotification index={i} activeCategory={activeCategory as any} />
</View> </View>
))} ))}
{/* Konten utama di sini */}
{/* <View style={{ flex: 1 }}>
<Text style={{ color: "white" }}>
{activeCategory
? `Kategori Aktif: ${
categories.find((c) => c.id === activeCategory)?.label
}`
: "Pilih kategori"}
</Text>
</View> */}
</ViewWrapper> </ViewWrapper>
); );
} }

View File

@@ -1,10 +1,22 @@
import { AccentColor } from "@/constants/color-palet"; import { AccentColor } from "@/constants/color-palet";
import { PADDING_EXTRA_SMALL, PADDING_MEDIUM, PADDING_SMALL } from "@/constants/constans-value"; import {
import { StyleProp, TouchableHighlight, View, ViewStyle } from "react-native"; PADDING_EXTRA_SMALL,
PADDING_MEDIUM,
PADDING_SMALL,
} from "@/constants/constans-value";
import { Href, router } from "expo-router";
import {
StyleProp,
TouchableHighlight,
TouchableOpacity,
View,
ViewStyle,
} from "react-native";
interface BaseBoxProps { interface BaseBoxProps {
children: React.ReactNode; children: React.ReactNode;
style?: StyleProp<ViewStyle>; style?: StyleProp<ViewStyle>;
href?: Href;
onPress?: () => void; onPress?: () => void;
marginBottom?: number; marginBottom?: number;
padding?: number; padding?: number;
@@ -15,16 +27,19 @@ interface BaseBoxProps {
export default function BaseBox({ export default function BaseBox({
children, children,
style, style,
href,
onPress, onPress,
marginBottom = PADDING_MEDIUM, marginBottom = PADDING_MEDIUM,
paddingBlock = PADDING_EXTRA_SMALL, paddingBlock = PADDING_MEDIUM,
paddingInline = PADDING_SMALL, paddingInline = PADDING_SMALL,
}: BaseBoxProps) { }: BaseBoxProps) {
return ( return (
<> <>
{onPress ? ( {onPress || href ? (
<TouchableHighlight <TouchableOpacity
onPress={onPress} activeOpacity={0.7}
onPress={href ? () => router.navigate(href) : onPress}
style={[ style={[
{ {
backgroundColor: AccentColor.darkblue, backgroundColor: AccentColor.darkblue,
@@ -37,10 +52,9 @@ export default function BaseBox({
}, },
style, style,
]} ]}
// activeOpacity={0.7}
> >
<View>{children}</View> <View>{children}</View>
</TouchableHighlight> </TouchableOpacity>
) : ( ) : (
<View <View
style={[ style={[

View File

@@ -6,6 +6,7 @@ import ButtonCustom from "../Button/ButtonCustom";
interface ButtonData { interface ButtonData {
id: string | number; id: string | number;
label: string; label: string;
value: string;
} }
interface ScrollableCustomProps { interface ScrollableCustomProps {
@@ -27,7 +28,7 @@ const ScrollableCustom = ({
style={styles.scrollView} style={styles.scrollView}
> >
{data.map((item) => { {data.map((item) => {
const isActive = activeId === item.id; const isActive = activeId === item.value;
return ( return (
<ButtonCustom <ButtonCustom

View File

@@ -3,6 +3,7 @@ import {
TEXT_SIZE_LARGE, TEXT_SIZE_LARGE,
TEXT_SIZE_MEDIUM, TEXT_SIZE_MEDIUM,
TEXT_SIZE_SMALL, TEXT_SIZE_SMALL,
TEXT_SIZE_XLARGE,
} from "@/constants/constans-value"; } from "@/constants/constans-value";
import React from "react"; import React from "react";
import { import {
@@ -21,7 +22,7 @@ interface TextCustomProps {
style?: StyleProp<TextStyle>; style?: StyleProp<TextStyle>;
bold?: boolean; bold?: boolean;
semiBold?: boolean; semiBold?: boolean;
size?: "default" | "large" | "small"; size?: "default" | "large" | "small" | "xlarge";
color?: "default" | "yellow" | "red" | "gray" | "green" | "black" color?: "default" | "yellow" | "red" | "gray" | "green" | "black"
align?: TextAlign; // Prop untuk alignment align?: TextAlign; // Prop untuk alignment
truncate?: boolean | number; truncate?: boolean | number;
@@ -51,6 +52,7 @@ const TextCustom: React.FC<TextCustomProps> = ({
// Size // Size
if (size === "large") selectedStyles.push(styles.large); if (size === "large") selectedStyles.push(styles.large);
else if (size === "xlarge") selectedStyles.push(styles.xlarge);
else if (size === "small") selectedStyles.push(styles.small); else if (size === "small") selectedStyles.push(styles.small);
// Color // Color
@@ -113,11 +115,14 @@ export const styles = StyleSheet.create({
fontFamily: "Poppins-SemiBold", fontFamily: "Poppins-SemiBold",
fontWeight: "500", fontWeight: "500",
}, },
small: {
fontSize: TEXT_SIZE_SMALL,
},
large: { large: {
fontSize: TEXT_SIZE_LARGE, fontSize: TEXT_SIZE_LARGE,
}, },
small: { xlarge: {
fontSize: TEXT_SIZE_SMALL, fontSize: TEXT_SIZE_XLARGE,
}, },
yellow: { yellow: {
color: MainColor.yellow, color: MainColor.yellow,

View File

@@ -20,15 +20,23 @@ interface ViewWrapperProps {
headerComponent?: React.ReactNode; headerComponent?: React.ReactNode;
footerComponent?: React.ReactNode; footerComponent?: React.ReactNode;
floatingButton?: React.ReactNode; floatingButton?: React.ReactNode;
hideFooter?: boolean;
style?: StyleProp<ViewStyle>; style?: StyleProp<ViewStyle>;
} }
/**
*
* @param hideFooter
* @returns meneyembunyikan footer ketika menggunakan tabs (misal: bottom tab)
*/
const ViewWrapper = ({ const ViewWrapper = ({
children, children,
withBackground = false, withBackground = false,
headerComponent, headerComponent,
footerComponent, footerComponent,
floatingButton, floatingButton,
hideFooter = false,
style, style,
}: ViewWrapperProps) => { }: ViewWrapperProps) => {
const assetBackground = require("../../assets/images/main-background.png"); const assetBackground = require("../../assets/images/main-background.png");
@@ -78,10 +86,12 @@ const ViewWrapper = ({
{footerComponent} {footerComponent}
</SafeAreaView> </SafeAreaView>
) : ( ) : (
<SafeAreaView hideFooter ? null : (
edges={["bottom"]} <SafeAreaView
style={{ backgroundColor: MainColor.darkblue }} edges={["bottom"]}
/> style={{ backgroundColor: MainColor.darkblue }}
/>
)
)} )}
{/* Floating Component (misal: FAB) */} {/* Floating Component (misal: FAB) */}

View File

@@ -7,6 +7,7 @@ export {
TEXT_SIZE_SMALL, TEXT_SIZE_SMALL,
TEXT_SIZE_MEDIUM, TEXT_SIZE_MEDIUM,
TEXT_SIZE_LARGE, TEXT_SIZE_LARGE,
TEXT_SIZE_XLARGE,
ICON_SIZE_SMALL, ICON_SIZE_SMALL,
ICON_SIZE_MEDIUM, ICON_SIZE_MEDIUM,
DRAWER_HEIGHT, DRAWER_HEIGHT,
@@ -27,6 +28,7 @@ const OS_HEIGHT = Platform.OS === "ios" ? OS_IOS_HEIGHT : OS_ANDROID_HEIGHT
const TEXT_SIZE_SMALL = 12; const TEXT_SIZE_SMALL = 12;
const TEXT_SIZE_MEDIUM = 14; const TEXT_SIZE_MEDIUM = 14;
const TEXT_SIZE_LARGE = 16; const TEXT_SIZE_LARGE = 16;
const TEXT_SIZE_XLARGE = 18;
// Icon Size // Icon Size
const ICON_SIZE_BUTTON = 18 const ICON_SIZE_BUTTON = 18

View File

@@ -0,0 +1,6 @@
export const masterStatus = [
{ value: "publish", label: "Publish" },
{ value: "review", label: "Review" },
{ value: "draft", label: "Draft" },
{ value: "reject", label: "Reject" },
];

View File

@@ -32,11 +32,11 @@ export default function LoginView() {
// router.navigate("/verification"); // router.navigate("/verification");
// router.navigate(`/(application)/(user)/profile/${id}`); // router.navigate(`/(application)/(user)/profile/${id}`);
// router.navigate("/(application)/(user)/home"); router.navigate("/(application)/(user)/home");
// router.navigate(`/(application)/profile/${id}/edit`); // router.navigate(`/(application)/profile/${id}/edit`);
// router.navigate(`/(application)/(user)/portofolio/${id}`) // router.navigate(`/(application)/(user)/portofolio/${id}`)
// router.navigate(`/(application)/(image)/preview-image/${id}`); // router.navigate(`/(application)/(image)/preview-image/${id}`);
router.replace("/(application)/(user)/event/(tabs)"); // router.replace("/(application)/(user)/event/(tabs)");
} }
return ( return (