Compare commits

..

10 Commits

Author SHA1 Message Date
17e6208aae feature
deskripsi:
- new component select
- fix styles login dan verifikasi
2025-07-03 17:43:52 +08:00
f5cf9e1549 fix component
- buttom radius valuenya constans
- perbaikan bottom pada auth file
2025-07-03 17:05:53 +08:00
7b58e3315f feature
deskripsi:
- tampilan edit profile
- resourcing stack
- fix text input & buttom
2025-07-03 16:35:39 +08:00
101c9053d8 feature
deskripsi:
- new component stack
2025-07-03 14:54:05 +08:00
4a92385d6d fix nama komponen profile 2025-07-03 14:24:10 +08:00
7e39133c2f feature components
deskripsi:
- BaseBox, TextCustom
- Perbaikan home & profile
2025-07-03 11:29:51 +08:00
e2744f0344 fix styles dan feature component text 2025-07-02 14:55:12 +08:00
9667065bb3 fix folder component 2025-07-02 12:20:42 +08:00
23ae416f42 fix component drawer 2025-07-02 11:56:04 +08:00
8fb37db0db feature
deksripsi:
- merapikan folder profile
- Issu: Drawer
2025-07-02 11:43:22 +08:00
48 changed files with 1196 additions and 315 deletions

View File

@@ -1,5 +1,5 @@
import { AccentColor, MainColor } from "@/constants/color-palet";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { Ionicons } from "@expo/vector-icons";
import { router, Stack } from "expo-router";
@@ -8,8 +8,8 @@ export default function ApplicationLayout() {
<>
<Stack
screenOptions={{
headerStyle: Styles.headerStyle,
headerTitleStyle: Styles.headerTitleStyle,
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
headerTitleAlign: "center",
contentStyle: {
borderBottomColor: AccentColor.blue,
@@ -89,35 +89,20 @@ export default function ApplicationLayout() {
/>
{/* Profile */}
{/* <Stack.Screen
name="profile/index"
<Stack.Screen
name="profile"
options={{
title: "Profile",
headerLeft: () => (
<Ionicons
name="arrow-back"
size={20}
color={MainColor.yellow}
onPress={() => router.back()}
/>
),
headerShown: false,
}}
/> */}
/>
{/* <Stack.Screen
name="profile/[id]"
{/* Portofolio */}
<Stack.Screen
name="portofolio"
options={{
title: "Profile",
headerLeft: () => (
<Ionicons
name="arrow-back"
size={20}
color={MainColor.yellow}
onPress={() => router.back()}
/>
),
headerShown: false,
}}
/> */}
/>
{/* Event */}
<Stack.Screen

View File

@@ -1,6 +1,6 @@
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { AccentColor, MainColor } from "@/constants/color-palet";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { router } from "expo-router";
import { Text, TouchableHighlight, View } from "react-native";
@@ -17,7 +17,7 @@ export default function Event() {
borderWidth: 1,
}}
>
<Text style={Styles.textLabel}>Event</Text>
<Text style={GStyles.textLabel}>Event</Text>
</View>
</TouchableHighlight>
</ViewWrapper>

View File

@@ -1,11 +1,11 @@
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { Text } from "react-native";
export default function Kontribusi() {
return (
<ViewWrapper>
<Text style={Styles.textLabel}>Kontribusi</Text>
<Text style={GStyles.textLabel}>Kontribusi</Text>
</ViewWrapper>
);
}

View File

@@ -1,11 +1,11 @@
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { Text } from "react-native";
export default function Riwayat() {
return (
<ViewWrapper>
<Text style={Styles.textLabel}>Riwayat</Text>
<Text style={GStyles.textLabel}>Riwayat</Text>
</ViewWrapper>
);
}

View File

@@ -1,11 +1,11 @@
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { Text } from "react-native";
export default function Status() {
return (
<ViewWrapper>
<Text style={Styles.textLabel}>Status</Text>
<Text style={GStyles.textLabel}>Status</Text>
</ViewWrapper>
);
}

View File

@@ -1,5 +1,5 @@
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { useLocalSearchParams } from "expo-router";
import { Text } from "react-native";
@@ -8,7 +8,7 @@ export default function DetailEvent() {
console.log("id event >", id);
return (
<ViewWrapper>
<Text style={Styles.textLabel}>Detail Event {id}</Text>
<Text style={GStyles.textLabel}>Detail Event {id}</Text>
</ViewWrapper>
);
}

View File

@@ -1,9 +0,0 @@
import { Text, View } from "react-native";
export default function Portofolio() {
return (
<View>
<Text>Portofolio</Text>
</View>
);
}

View File

@@ -0,0 +1,32 @@
import BackButton from "@/components/Button/BackButton";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { GStyles } from "@/styles/global-styles";
import { Stack, useLocalSearchParams } from "expo-router";
import { Text } from "react-native";
export default function Portofolio() {
const { id } = useLocalSearchParams();
return (
<ViewWrapper>
{/* Header */}
<Stack.Screen
options={{
title: "Portofolio",
headerLeft: () => <BackButton />,
// headerRight: () => (
// <TouchableOpacity onPress={openDrawer}>
// <Ionicons
// name="ellipsis-vertical"
// size={20}
// color={MainColor.yellow}
// />
// </TouchableOpacity>
// ),
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
}}
/>
<Text style={GStyles.textLabel}>Portofolio {id}</Text>
</ViewWrapper>
);
}

View File

@@ -0,0 +1,26 @@
import BackButton from "@/components/Button/BackButton";
import { GStyles } from "@/styles/global-styles";
import { Stack } from "expo-router";
export default function PortofolioLayout() {
return (
<>
<Stack
screenOptions={{
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
headerTitleAlign: "center",
headerBackButtonDisplayMode: "minimal",
headerLeft: () => <BackButton />,
}}
>
{/* <Stack.Screen name="[id]/index" options={{ title: "Portofolio" }} /> */}
<Stack.Screen
name="[id]/create"
options={{ title: "Tambah Portofolio" }}
/>
</Stack>
</>
);
}

View File

@@ -0,0 +1,105 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
ButtonCustom,
SelectCustom,
StackCustom,
TextCustom,
TextInputCustom,
ViewWrapper,
} from "@/components";
import { MainColor } from "@/constants/color-palet";
import { router, useLocalSearchParams } from "expo-router";
import { useState } from "react";
import { StyleSheet, Text } from "react-native";
export default function ProfileEdit() {
const { id } = useLocalSearchParams();
const [nama, setNama] = useState("Bagas Banuna");
const [email, setEmail] = useState("bagasbanuna@gmail.com");
const [alamat, setAlamat] = useState("Bandar Lampung");
const [selectedValue, setSelectedValue] = useState<string | number>("");
const options = [
{ label: "React", value: "react" },
{ label: "Vue", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
{ label: "Next.js", value: "nextjs" },
{ label: "Nuxt.js", value: "nuxtjs" },
{ label: "Remix", value: "remix" },
{ label: "Sapper", value: "sapper" },
{ label: "SvelteKit", value: "sveltekit" },
];
return (
<ViewWrapper
bottomBarComponent={
<ButtonCustom
disabled={!nama || !email || !alamat || !selectedValue}
onPress={() => {
console.log("data >>", nama, email, alamat, selectedValue);
router.back();
}}
>
Simpan
</ButtonCustom>
}
>
<StackCustom gap={"xs"}>
<SelectCustom
label="Framework"
placeholder="Pilih framework favoritmu"
data={options}
value={selectedValue}
onChange={setSelectedValue}
/>
{/* {selectedValue && (
<Text style={styles.result}>Terpilih: {selectedValue}</Text>
)} */}
<TextInputCustom
label="Nama"
placeholder="Nama"
value={nama}
onChangeText={(text) => {
setNama(text);
}}
required
/>
<TextInputCustom
label="Email"
placeholder="Email"
value={email}
onChangeText={(text) => {
setEmail(text);
}}
required
/>
<TextInputCustom
label="Alamat"
placeholder="Alamat"
value={alamat}
onChangeText={(text) => {
setAlamat(text);
}}
required
/>
</StackCustom>
</ViewWrapper>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
padding: 20,
},
result: {
marginTop: 20,
fontSize: 16,
fontWeight: "bold",
},
});

View File

@@ -1,16 +1,17 @@
import { IMenuDrawerItem } from "@/components/_Interface/types";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import AlertCustom from "@/components/Alert/AlertCustom";
import BackButton from "@/components/Button/BackButton";
import DrawerCustom from "@/components/Drawer/DrawerCustom";
import { MainColor } from "@/constants/color-palet";
import { DRAWER_HEIGHT } from "@/constants/constans-value";
import Profile_MenuDrawerSection from "@/screens/Profile/menuDrawerSection";
import { Styles } from "@/styles/global-styles";
import Profile_MenuDrawerSection from "@/screens/Profile/MenuDrawerSection";
import ProfilSection from "@/screens/Profile/ProfilSection";
import { GStyles } from "@/styles/global-styles";
import { Ionicons } from "@expo/vector-icons";
import { router, Stack, useLocalSearchParams } from "expo-router";
import React, { useRef, useState } from "react";
import { Animated, Text, TouchableOpacity } from "react-native";
import { Animated, InteractionManager, TouchableOpacity } from "react-native";
export default function Profile() {
const { id } = useLocalSearchParams();
@@ -21,22 +22,22 @@ export default function Profile() {
{
icon: "create",
label: "Edit profile",
path: "/(application)/profile/edit",
path: `/(application)/profile/${id}/edit`,
},
{
icon: "camera",
label: "Ubah foto profile",
path: `/(application)/profile/update-photo/${id}`,
path: `/(application)/profile/${id}/update-photo`,
},
{
icon: "image",
label: "Ubah latar belakang",
path: `/(application)/profile/update-background/${id}`,
path: `/(application)/profile/${id}/update-background`,
},
{
icon: "add-circle",
label: "Tambah portofolio",
path: `/(application)/portofolio/create/${id}`,
path: `/(application)/portofolio/${id}/create`,
},
// {
// icon: "settings",
@@ -64,7 +65,9 @@ export default function Profile() {
duration: 300,
useNativeDriver: true,
}).start(() => {
setIsDrawerOpen(false); // baru ganti state setelah animasi selesai
InteractionManager.runAfterInteractions(() => {
setIsDrawerOpen(false); // baru ganti state setelah animasi selesai
});
});
};
@@ -81,14 +84,7 @@ export default function Profile() {
<Stack.Screen
options={{
title: "Profile",
headerLeft: () => (
<Ionicons
name="arrow-back"
size={20}
color={MainColor.yellow}
onPress={() => router.back()}
/>
),
headerLeft: () => <BackButton />,
headerRight: () => (
<TouchableOpacity onPress={openDrawer}>
<Ionicons
@@ -98,9 +94,12 @@ export default function Profile() {
/>
</TouchableOpacity>
),
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
}}
/>
<Text style={Styles.textLabel}>Profile {id}</Text>
<ProfilSection />
</ViewWrapper>
{/* Drawer Komponen Eksternal */}
@@ -113,6 +112,7 @@ export default function Profile() {
<Profile_MenuDrawerSection
drawerItems={drawerItems}
setShowLogoutAlert={setShowLogoutAlert}
setIsDrawerOpen={setIsDrawerOpen}
/>
</DrawerCustom>

View File

@@ -0,0 +1,30 @@
import { BackButton } from "@/components";
import { GStyles } from "@/styles/global-styles";
import { Stack } from "expo-router";
export default function ProfileLayout() {
return (
<>
<Stack
screenOptions={{
headerStyle: GStyles.headerStyle,
headerTitleStyle: GStyles.headerTitleStyle,
headerTitleAlign: "center",
headerBackButtonDisplayMode: "minimal",
headerLeft: () => <BackButton />,
}}
>
{/* <Stack.Screen name="[id]/index" options={{ headerShown: false }} /> */}
<Stack.Screen name="[id]/edit" options={{ title: "Edit Profile" }} />
<Stack.Screen
name="[id]/update-photo"
options={{ title: "Update Foto" }}
/>
<Stack.Screen
name="[id]/update-background"
options={{ title: "Update Latar Belakang" }}
/>
</Stack>
</>
);
}

View File

@@ -1,9 +0,0 @@
import { Text, View } from "react-native";
export default function ProfileEdit() {
return (
<View>
<Text>Profile Edit</Text>
</View>
)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,55 @@
import { MainColor } from "@/constants/color-palet";
import { Image, ImageSourcePropType, StyleSheet } from "react-native";
type Size = "base" | "sm" | "md" | "lg";
interface AvatarCustomProps {
source?: ImageSourcePropType;
size?: Size;
}
const sizeMap = {
base: 40,
sm: 60,
md: 80,
lg: 100,
};
export default function AvatarCustom({
source = require("@/assets/images/dummy/dummy-avatar.png"),
size = "base",
}: AvatarCustomProps) {
const dimension = sizeMap[size];
return (
<Image
source={source}
style={[
styles.overlappingAvatar,
{
width: dimension,
height: dimension,
borderRadius: dimension / 2,
},
]}
resizeMode="cover"
/>
);
}
const styles = StyleSheet.create({
container: {
alignItems: "center",
justifyContent: "center",
},
overlappingAvatar: {
borderWidth: 2,
borderColor: "#fff",
backgroundColor: MainColor.white,
// shadowColor: "#000",
// shadowOffset: { width: 0, height: 2 },
// shadowOpacity: 0.2,
shadowRadius: 3,
elevation: 3,
},
});

View File

@@ -0,0 +1,59 @@
import { AccentColor } from "@/constants/color-palet";
import { StyleProp, TouchableHighlight, View, ViewStyle } from "react-native";
interface BaseBoxProps {
children: React.ReactNode;
style?: StyleProp<ViewStyle>;
onPress?: () => void;
marginBottom?: number;
padding?: number;
paddingInline?: number;
}
export default function BaseBox({
children,
style,
onPress,
marginBottom = 16,
padding = 12,
}: BaseBoxProps) {
return (
<>
{onPress ? (
<TouchableHighlight
onPress={onPress}
style={[
{
backgroundColor: AccentColor.darkblue,
borderColor: AccentColor.blue,
borderWidth: 1,
borderRadius: 10,
marginBottom,
padding,
},
style,
]}
// activeOpacity={0.7}
>
<View>{children}</View>
</TouchableHighlight>
) : (
<View
style={[
{
backgroundColor: AccentColor.darkblue,
borderColor: AccentColor.blue,
borderWidth: 1,
borderRadius: 10,
marginBottom,
padding,
},
style,
]}
>
{children}
</View>
)}
</>
);
}

View File

@@ -0,0 +1,16 @@
import { Ionicons } from "@expo/vector-icons";
import { MainColor } from "@/constants/color-palet";
import { router } from "expo-router";
const BackButton = () => {
return (
<Ionicons
name="arrow-back"
size={20}
color={MainColor.yellow}
onPress={() => router.back()}
/>
);
};
export default BackButton;

View File

@@ -2,44 +2,44 @@
import React from "react";
import { Text, TouchableOpacity } from "react-native";
import buttonStyles from "./buttonStyles";
import buttonStyles from "./buttonCustomStyles";
import { radiusMap } from "@/constants/radius-value";
import { MainColor } from "@/constants/color-palet";
// Definisi props dengan TypeScript
// Import radiusMap
// Definisi type untuk radius
type RadiusType = keyof typeof radiusMap | number;
interface ButtonProps {
onPress: () => void;
children?: React.ReactNode;
onPress?: () => void;
title?: string;
backgroundColor?: string;
textColor?: string;
radius?: number;
radius?: RadiusType; // ← bisa string enum atau number
disabled?: boolean;
iconLeft?: React.ReactNode;
}
/**
* Props untuk ButtonCustom
* @param onPress: () => void
* @param title?: string
* @param backgroundColor?: string
* @param textColor?: string
* @param radius?: number
* @param disabled?: boolean
* @param iconLeft?: React.ReactNode
* @example iconLeft={<Icon name="arrow-right" size={20} color={MainColor.black}/>
*/
const ButtonCustom: React.FC<ButtonProps> = ({
children,
onPress,
title = "Button",
backgroundColor = "#007AFF",
textColor = "#FFFFFF",
radius = 8,
backgroundColor = MainColor.yellow,
textColor = MainColor.black,
radius = "full", // default md
disabled = false,
iconLeft,
}) => {
const borderRadius =
typeof radius === "number" ? radius : radiusMap[radius ?? "md"]; // fallback ke 'md'
const styles = buttonStyles({
backgroundColor,
textColor,
borderRadius: radius,
borderRadius,
});
return (
@@ -51,7 +51,7 @@ const ButtonCustom: React.FC<ButtonProps> = ({
>
{/* Render icon jika tersedia */}
{iconLeft && iconLeft}
<Text style={styles.buttonText}>{title}</Text>
<Text style={styles.buttonText}>{children || title}</Text>
</TouchableOpacity>
);
};

View File

@@ -1,9 +1,10 @@
import React, { useRef } from "react";
import {
Animated,
PanResponder,
StyleSheet,
View
Animated,
PanResponder,
StyleSheet,
View,
InteractionManager,
} from "react-native";
import { AccentColor, MainColor } from "@/constants/color-palet";
@@ -18,6 +19,12 @@ interface DrawerCustomProps {
// openLogoutAlert: () => void;
}
/**
*
* @param drawerAnim
* @example const drawerAnim = useRef(new Animated.Value(DRAWER_HEIGHT)).current; // mulai di luar bawah layar
*/
export default function DrawerCustom({
children,
height,
@@ -39,7 +46,9 @@ DrawerCustomProps) {
},
onPanResponderRelease: (_, gestureState) => {
if (gestureState.dy > 200) {
closeDrawer();
InteractionManager.runAfterInteractions(() => {
closeDrawer();
});
} else {
Animated.spring(drawerAnim, {
toValue: 0,
@@ -58,7 +67,11 @@ DrawerCustomProps) {
<View
style={styles.overlay}
pointerEvents="auto"
onTouchStart={closeDrawer}
onTouchStart={() => {
InteractionManager.runAfterInteractions(() => {
closeDrawer();
});
}}
/>
{/* Custom Bottom Drawer */}

View File

@@ -0,0 +1,113 @@
import React, { createContext, useContext, useMemo } from "react";
import { StyleSheet, useWindowDimensions, View, ViewStyle } from "react-native";
// Tipe untuk span
type SpanValue = {
base?: number;
md?: number;
lg?: number;
};
// Props untuk Grid.Col
interface ColProps {
children: React.ReactNode;
span?: number | SpanValue;
style?: ViewStyle;
}
// Props untuk Grid
interface GridProps {
children: React.ReactNode;
gap?: number;
columns?: number;
containerStyle?: ViewStyle;
}
// Context untuk menyimpan konfigurasi grid
type GridContextType = {
gap: number;
columns: number;
};
const GridContext = createContext<GridContextType>({
gap: 0,
columns: 12,
});
const useGrid = () => useContext(GridContext);
// Helper untuk menentukan span berdasarkan lebar layar
const getSpan = (
spanProp: number | SpanValue | undefined,
width: number
): number => {
if (typeof spanProp === "number") return spanProp;
const span = spanProp || { base: 12 };
if (width >= 992 && span.lg) return span.lg;
if (width >= 768 && span.md) return span.md;
return span.base ?? 12;
};
// Grid Component
const GridComponent: React.FC<GridProps> = ({
children,
gap = 6,
columns = 12,
containerStyle,
}) => {
const contextValue = useMemo(() => ({ gap, columns }), [gap, columns]);
return (
<GridContext.Provider value={contextValue}>
<View
style={[
styles.container,
{ marginHorizontal: -gap / 2 },
containerStyle,
]}
>
{React.Children.map(children, (child) =>
React.isValidElement(child)
? React.cloneElement(child as React.ReactElement<ColProps>, {})
: child
)}
</View>
</GridContext.Provider>
);
};
// Grid.Col Component
const Col: React.FC<ColProps> = ({ children, span, style }) => {
const { gap, columns } = useGrid();
const { width } = useWindowDimensions();
const colSpan = getSpan(span, width);
const margin = gap / 2;
const styles = StyleSheet.create({
col: {
flexBasis: `${(100 / columns) * colSpan}%`,
paddingVertical: margin,
// marginBottom: gap,
marginBlock: gap,
},
});
return <View style={[styles.col, style]}>{children}</View>;
};
// Export bersama-sama
const Grid = Object.assign(GridComponent, { Col });
export default Grid;
// Styles
const styles = StyleSheet.create({
container: {
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "flex-start",
},
});

View File

@@ -0,0 +1,123 @@
// components/Select.tsx
import { MainColor } from "@/constants/color-palet";
import { TEXT_SIZE_MEDIUM } from "@/constants/constans-value";
import React, { useState } from "react";
import {
View,
Text,
Pressable,
Modal,
FlatList,
StyleSheet,
TouchableOpacity,
} from "react-native";
type SelectItem = {
label: string;
value: string | number;
};
type SelectProps = {
label?: string;
placeholder?: string;
data: SelectItem[];
value?: string | number | null;
onChange: (value: string | number) => void;
};
const SelectCustom: React.FC<SelectProps> = ({
label,
placeholder = "Pilih opsi",
data,
value,
onChange,
}) => {
const [modalVisible, setModalVisible] = useState(false);
const selectedItem = data.find((item) => item.value === value);
return (
<View style={styles.container}>
{label && <Text style={styles.label}>{label}</Text>}
<Pressable style={styles.input} onPress={() => setModalVisible(true)}>
<Text style={selectedItem ? styles.text : styles.placeholder}>
{selectedItem?.label || placeholder}
</Text>
</Pressable>
<Modal visible={modalVisible} transparent animationType="fade">
<TouchableOpacity
style={styles.modalOverlay}
activeOpacity={1}
onPressOut={() => setModalVisible(false)}
>
<View style={styles.modalContent}>
<FlatList
data={data}
keyExtractor={(item) => String(item.value)}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.option}
onPress={() => {
onChange(item.value);
setModalVisible(false);
}}
>
<Text>{item.label}</Text>
</TouchableOpacity>
)}
/>
</View>
</TouchableOpacity>
</Modal>
</View>
);
};
export default SelectCustom;
const styles = StyleSheet.create({
container: {
marginBottom: 16,
},
label: {
fontSize: TEXT_SIZE_MEDIUM,
marginBottom: 4,
color: MainColor.white,
fontWeight: "500",
},
input: {
borderWidth: 1,
borderColor: "#ccc",
padding: 12,
borderRadius: 8,
minHeight: 48,
justifyContent: "center",
backgroundColor: MainColor.white,
},
text: {
fontSize: TEXT_SIZE_MEDIUM,
},
placeholder: {
fontSize: TEXT_SIZE_MEDIUM,
color: MainColor.placeholder,
},
modalOverlay: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.3)",
justifyContent: "center",
alignItems: "center",
},
modalContent: {
width: "80%",
maxHeight: 300,
backgroundColor: "white",
borderRadius: 8,
overflow: "hidden",
},
option: {
padding: 16,
borderBottomWidth: 1,
borderBottomColor: "#eee",
},
});

View File

@@ -0,0 +1,66 @@
// components/Stack.tsx
import React from "react";
import { View, ViewStyle, StyleSheet } from "react-native";
import { AlignType, GapSizeType, JustifyType } from "@/components/Stack/stack-types";
interface StackProps {
children: React.ReactNode;
align?: AlignType;
justify?: JustifyType;
gap?: GapSizeType;
direction?: "row" | "column";
style?: ViewStyle | ViewStyle[];
}
const StackCustom: React.FC<StackProps> = ({
children,
align = "stretch",
justify = "flex-start",
gap = "md",
direction = "column",
style,
}) => {
const spacing = convertToSpacing(gap);
return (
<View
style={[
// styles.stack,
{
flexDirection: direction,
alignItems: align,
justifyContent: justify,
gap: spacing,
},
style,
]}
>
{children}
</View>
);
};
// Fungsi untuk mengubah nilai gap ke dalam ukuran pixel
const convertToSpacing = (value: GapSizeType): number => {
if (typeof value === "number") return value;
const sizes: Record<string, number> = {
xs: 4,
sm: 8,
md: 16,
lg: 24,
xl: 32,
};
return sizes[value] || 16; // default md
};
const styles = StyleSheet.create({
stack: {
flex: 1,
},
});
export default StackCustom;

View File

@@ -0,0 +1,19 @@
// types/stack.types.ts
export type AlignType =
| "stretch"
| "flex-start"
| "center"
| "flex-end"
| "baseline";
export type JustifyType =
| "flex-start"
| "center"
| "flex-end"
| "space-between"
| "space-around"
| "space-evenly";
export type GapSizeType = "xs" | "sm" | "md" | "lg" | "xl" | number;
// | string;

View File

@@ -0,0 +1,104 @@
import { MainColor } from "@/constants/color-palet";
import {
TEXT_SIZE_LARGE,
TEXT_SIZE_MEDIUM,
TEXT_SIZE_SMALL,
} from "@/constants/constans-value";
import React from "react";
import { Text as RNText, StyleProp, StyleSheet, TextStyle } from "react-native";
// Tambahkan type TextAlignProps agar lebih type-safe
type TextAlign = "left" | "center" | "right";
interface TextCustomProps {
children: string | React.ReactNode;
style?: StyleProp<TextStyle>;
bold?: boolean;
semiBold?: boolean;
size?: "default" | "large" | "small";
color?: "default" | "yellow" | "red";
align?: TextAlign; // Prop untuk alignment
truncate?: boolean | number;
}
const TextCustom: React.FC<TextCustomProps> = ({
children,
style,
bold = false,
semiBold = false,
size = "default",
color = "default",
align = "left", // Default alignment
truncate = false,
}) => {
const getStyle = () => {
let selectedStyles = [];
// Base style
selectedStyles.push(styles.default);
// Font weight
if (bold) selectedStyles.push(styles.bold);
else if (semiBold) selectedStyles.push(styles.semiBold);
// Size
if (size === "large") selectedStyles.push(styles.large);
else if (size === "small") selectedStyles.push(styles.small);
// Color
if (color === "yellow") selectedStyles.push(styles.yellow);
else if (color === "red") selectedStyles.push(styles.red);
// Alignment
if (align) {
selectedStyles.push({ textAlign: align });
}
// Override with passed style
if (style) selectedStyles.push(style);
return selectedStyles;
};
return (
<RNText
numberOfLines={
typeof truncate === "number" ? truncate : truncate ? 1 : undefined
}
ellipsizeMode="tail"
style={getStyle()}
>
{children}
</RNText>
);
};
export default TextCustom;
export const styles = StyleSheet.create({
default: {
fontSize: TEXT_SIZE_MEDIUM,
color: MainColor.white,
fontFamily: "Poppins-Regular",
},
bold: {
fontFamily: "Poppins-Bold",
fontWeight: "700",
},
semiBold: {
fontFamily: "Poppins-SemiBold",
fontWeight: "500",
},
large: {
fontSize: TEXT_SIZE_LARGE,
},
small: {
fontSize: TEXT_SIZE_SMALL,
},
yellow: {
color: MainColor.yellow,
},
red: {
color: MainColor.red,
},
});

View File

@@ -0,0 +1,3 @@
import { TextInputCustom } from "./TextInputCustom";
export { TextInputCustom };

View File

@@ -34,7 +34,7 @@ export const textInputStyles = StyleSheet.create({
alignItems: "center",
borderWidth: 1,
borderColor: AccentColor.white,
backgroundColor: MainColor.login,
backgroundColor: MainColor.white,
paddingHorizontal: 12,
height: 50,
},

View File

@@ -1,5 +1,5 @@
import { MainColor } from "@/constants/color-palet";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { ImageBackground, ScrollView, View } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
@@ -7,12 +7,14 @@ interface ViewWrapperProps {
children: React.ReactNode;
withBackground?: boolean;
tabBarComponent?: React.ReactNode;
bottomBarComponent?: React.ReactNode;
}
const ViewWrapper = ({
children,
withBackground = false,
tabBarComponent,
bottomBarComponent,
}: ViewWrapperProps) => {
const assetBackground = require("../../assets/images/main-background.png");
@@ -34,18 +36,25 @@ const ViewWrapper = ({
<ImageBackground
source={assetBackground}
resizeMode="cover"
style={Styles.imageBackground}
style={GStyles.imageBackground}
>
<View style={Styles.containerWithBackground}>{children}</View>
<View style={GStyles.containerWithBackground}>{children}</View>
</ImageBackground>
) : (
<View style={Styles.container}>{children}</View>
<View style={GStyles.container}>{children}</View>
)}
</ScrollView>
{tabBarComponent}
{tabBarComponent ? tabBarComponent : null}
{bottomBarComponent ? (
<View style={GStyles.bottomBar}>
<View style={GStyles.bottomBarContainer}>
{bottomBarComponent}
</View>
</View>
) : null}
</SafeAreaView>
</>
// <SafeAreaProvider>
// <SafeAreaView
// edges={[

52
components/index.ts Normal file
View File

@@ -0,0 +1,52 @@
// Alert
import AlertCustom from "./Alert/AlertCustom";
// Button
import BackButton from "./Button/BackButton";
import ButtonCustom from "./Button/ButtonCustom";
// Drawer
import DrawerCustom from "./Drawer/DrawerCustom";
import MenuDrawerDynamicGrid from "./Drawer/MenuDrawerDynamicGird";
// ShareComponent
import ViewWrapper from "./_ShareComponent/ViewWrapper";
import Spacing from "./_ShareComponent/Spacing";
// Text
import TextCustom from "./Text/TextCustom";
// TextInput
import { TextInputCustom } from "./TextInput/TextInputCustom";
// Grid
import Grid from "./Grid/GridCustom";
// Box
import BaseBox from "./Box/BaseBox";
// Avatar
import AvatarCustom from "./Avatar/AvatarCustom"
// Stack
import StackCustom from "./Stack/StackCustom";
// Select
import SelectCustom from "./Select/SelectCustom";
export {
AlertCustom,
// Button
BackButton,
ButtonCustom,
// Drawer
DrawerCustom,
MenuDrawerDynamicGrid,
// ShareComponent
Spacing,
ViewWrapper,
// Text
TextCustom,
// TextInput
TextInputCustom,
// Grid
Grid,
// Box
BaseBox,
// Avatar
AvatarCustom,
// Stack
StackCustom,
// Select
SelectCustom,
};

View File

@@ -7,7 +7,8 @@ export const MainColor = {
red: "#FF4B4C",
orange: "#FF7043",
green: "#4CAF4F",
login: "#EDEBEBFF",
text_input: "#EDEBEBFF",
placeholder: "#999",
disabled: "#606360",
};

View File

@@ -5,6 +5,7 @@ export {
ICON_SIZE_SMALL,
ICON_SIZE_MEDIUM,
DRAWER_HEIGHT,
RADIUS_BUTTON,
};
const TEXT_SIZE_SMALL = 12;
@@ -13,3 +14,4 @@ const TEXT_SIZE_LARGE = 16;
const ICON_SIZE_SMALL = 20;
const ICON_SIZE_MEDIUM = 24;
const DRAWER_HEIGHT = 500; // tinggi drawer5
const RADIUS_BUTTON = 50

10
constants/radius-value.ts Normal file
View File

@@ -0,0 +1,10 @@
// constants/radius.ts
export const radiusMap = {
xs: 2,
sm: 4,
md: 6,
lg: 8,
xl: 12,
full: 100,
} as const;

View File

@@ -1,13 +1,12 @@
import ButtonCustom from "@/components/Button/ButtonCustom";
import Spacing from "@/components/_ShareComponent/Spacing";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import { MainColor } from "@/constants/color-palet";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { router } from "expo-router";
import { useState } from "react";
import { Text, View } from "react-native";
import PhoneInput, { ICountry } from "react-native-international-phone-number";
import ButtonCustom from "@/components/Button/ButtonCustom";
import Spacing from "@/components/_ShareComponent/Spacing";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
export default function LoginView() {
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
@@ -24,18 +23,28 @@ export default function LoginView() {
function handleLogin() {
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
const fixNumber = callingCode + inputValue;
console.log(fixNumber);
router.push("/verification");
// console.log("fixNumber", fixNumber);
const randomAlfabet = Math.random().toString(36).substring(2, 8);
const randomNumber = Math.floor(Math.random() * 1000000);
const id = randomAlfabet + randomNumber + fixNumber;
console.log("user id :", id);
router.navigate("/verification");
// router.navigate(`/(application)/profile/${id}`);
// router.navigate("/(application)/home");
// router.navigate(`/(application)/profile/${id}/edit`);
}
return (
<ViewWrapper withBackground>
<View style={Styles.authContainer}>
<View style={GStyles.authContainer}>
<View>
<View style={Styles.authContainerTitle}>
<Text style={Styles.authSubTitle}>WELCOME TO</Text>
<View style={GStyles.authContainerTitle}>
<Text style={GStyles.authSubTitle}>WELCOME TO</Text>
<Spacing height={5} />
<Text style={Styles.authTitle}>HIPMI BADUNG APPS</Text>
<Text style={GStyles.authTitle}>HIPMI BADUNG APPS</Text>
<Spacing height={5} />
</View>
<Spacing height={50} />
@@ -65,13 +74,7 @@ export default function LoginView() {
<Spacing height={20} />
<ButtonCustom
title="Login"
backgroundColor={MainColor.yellow}
textColor={MainColor.black}
radius={10}
onPress={handleLogin}
/>
<ButtonCustom onPress={handleLogin}>Login</ButtonCustom>
</View>
</ViewWrapper>
);

View File

@@ -3,19 +3,25 @@ import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import ButtonCustom from "@/components/Button/ButtonCustom";
import { TextInputCustom } from "@/components/TextInput/TextInputCustom";
import { MainColor } from "@/constants/color-palet";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import { router } from "expo-router";
import { Text, View } from "react-native";
import { useState } from "react";
export default function RegisterView() {
const [username, setUsername] = useState("Bagas Banuna");
const handleRegister = () => {
console.log("Success register", username);
router.push("/(application)/home");
};
return (
<>
<ViewWrapper withBackground>
<View style={Styles.authContainer}>
<View style={GStyles.authContainer}>
<View>
<View style={Styles.authContainerTitle}>
<Text style={Styles.authTitle}>REGISTRASI</Text>
<View style={GStyles.authContainerTitle}>
<Text style={GStyles.authTitle}>REGISTRASI</Text>
<Spacing />
<MaterialCommunityIcons
name="account"
@@ -24,30 +30,24 @@ export default function RegisterView() {
/>
<Spacing />
<Text style={Styles.textLabel}>
<Text style={GStyles.textLabel}>
Anda akan terdaftar dengan nomor
</Text>
<Text style={Styles.textLabel}>+6282xxxxxxxxx</Text>
<Text style={GStyles.textLabel}>+6282xxxxxxxxx</Text>
<Spacing />
</View>
<TextInputCustom placeholder="Masukkan username" />
<ButtonCustom
title="Daftar"
backgroundColor={MainColor.yellow}
textColor={MainColor.black}
radius={10}
onPress={() => (
console.log("Success register"),
router.push("/(application)/home")
)}
<TextInputCustom
placeholder="Masukkan username"
value={username}
onChangeText={(text) => setUsername(text)}
/>
<ButtonCustom onPress={handleRegister}>Daftar</ButtonCustom>
{/* <Spacing />
<ButtonCustom
title="Coba"
backgroundColor={MainColor.yellow}
textColor={MainColor.black}
radius={10}
onPress={() => {
console.log("Home clicked");
router.push("/(application)/coba");

View File

@@ -2,22 +2,26 @@ import Spacing from "@/components/_ShareComponent/Spacing";
import ViewWrapper from "@/components/_ShareComponent/ViewWrapper";
import ButtonCustom from "@/components/Button/ButtonCustom";
import { MainColor } from "@/constants/color-palet";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { router } from "expo-router";
import { Text, View } from "react-native";
import { OtpInput } from "react-native-otp-entry";
export default function VerificationView() {
const handleVerification = () => {
console.log("Verification clicked");
router.push("/register");
};
return (
<>
<ViewWrapper withBackground>
<View style={Styles.authContainer}>
<View style={GStyles.authContainer}>
<View>
<View style={Styles.authContainerTitle}>
<Text style={Styles.authTitle}>Verifikasi KOde OTP</Text>
<View style={GStyles.authContainerTitle}>
<Text style={GStyles.authTitle}>Verifikasi KOde OTP</Text>
<Spacing height={30} />
<Text style={Styles.textLabel}>Masukan 4 digit kode otp</Text>
<Text style={Styles.textLabel}>
<Text style={GStyles.textLabel}>Masukan 4 digit kode otp</Text>
<Text style={GStyles.textLabel}>
Yang di kirim ke +6282xxxxxxxxx
</Text>
<Spacing height={30} />
@@ -25,7 +29,7 @@ export default function VerificationView() {
numberOfDigits={4}
theme={{
pinCodeContainerStyle: {
backgroundColor: MainColor.login,
backgroundColor: MainColor.text_input,
borderRadius: 10,
borderWidth: 1,
borderColor: MainColor.yellow,
@@ -39,21 +43,21 @@ export default function VerificationView() {
}}
/>
<Spacing height={30} />
<Text style={Styles.textLabel}>
<Text style={GStyles.textLabel}>
Tidak menerima kode ?{" "}
<Text style={Styles.textLabel}>Kirim Ulang</Text>
<Text style={GStyles.textLabel}>Kirim Ulang</Text>
</Text>
</View>
<Spacing height={30} />
</View>
<ButtonCustom
title="Verifikasi"
backgroundColor={MainColor.yellow}
textColor={MainColor.black}
radius={10}
onPress={() => router.push("/register")}
/>
onPress={() => handleVerification()}
>
Verifikasi
</ButtonCustom>
</View>
</ViewWrapper>
</>

View File

@@ -1,120 +0,0 @@
import Spacing from "@/components/_ShareComponent/Spacing";
import { Styles } from "@/styles/global-styles";
import { Ionicons } from "@expo/vector-icons";
import { Image } from "expo-image";
import { router } from "expo-router";
import { ScrollView, Text, TouchableOpacity, View } from "react-native";
import Icon from "react-native-vector-icons/FontAwesome";
import DynamicTruncatedText from "@/components/_ShareComponent/TruncatedText";
import { stylesHome } from "./homeViewStyle";
export default function HomeView() {
return (
<>
<ScrollView contentContainerStyle={{ flexGrow: 1 }}>
<View style={Styles.homeContainer}>
<Spacing height={20} />
<View
style={{
alignItems: "center",
justifyContent: "center",
backgroundColor: "#fff",
borderRadius: 10,
}}
>
<Image
source={require("@/assets/images/constants/home-hipmi.png")}
// placeholder={{ blurhash: "" }}
contentFit="cover"
transition={1000}
style={{
width: "100%",
height: 120,
borderRadius: 10,
}}
/>
</View>
<Spacing height={10} />
{/* Grid Section */}
<View style={stylesHome.gridContainer}>
<TouchableOpacity
style={stylesHome.gridItem}
onPress={() => router.push("/(application)/event")}
>
<Ionicons name="analytics" size={48} color="white" />
<Text style={stylesHome.gridLabel}>Event</Text>
</TouchableOpacity>
<TouchableOpacity style={stylesHome.gridItem}>
<Ionicons name="share" size={48} color="white" />
<Text style={stylesHome.gridLabel}>Collaboration</Text>
</TouchableOpacity>
<TouchableOpacity style={stylesHome.gridItem}>
<Ionicons name="cube" size={48} color="white" />
<Text style={stylesHome.gridLabel}>Voting</Text>
</TouchableOpacity>
<TouchableOpacity style={stylesHome.gridItem}>
<Ionicons name="heart" size={48} color="white" />
<Text style={stylesHome.gridLabel}>Crowdfunding</Text>
</TouchableOpacity>
</View>
<Spacing height={10} />
{/* Job Vacancy Section */}
<View style={stylesHome.jobVacancyContainer}>
<View style={stylesHome.jobVacancyHeader}>
<Icon name="briefcase" size={24} color="white" />
<Text style={stylesHome.jobVacancyTitle}>Job Vacancy</Text>
</View>
<View style={stylesHome.vacancyList}>
{/* Vacancy Item 1 */}
<View style={stylesHome.vacancyItem}>
{/* <Icon name="user" size={20} color="#FFD700" /> */}
<View style={stylesHome.vacancyDetails}>
<DynamicTruncatedText
text="Bagas_banuna"
fontSize={14}
fontFamily="System"
style={stylesHome.vacancyName}
/>
<Spacing height={5} />
<DynamicTruncatedText
text="Dicari perawat kucing"
fontSize={12}
fontFamily="System"
style={stylesHome.vacancyDescription}
/>
</View>
</View>
{/* Vacancy Item 2 */}
<View style={stylesHome.vacancyItem}>
{/* <Icon name="user" size={20} color="#FFD700" /> */}
<View style={stylesHome.vacancyDetails}>
<DynamicTruncatedText
text="fibramarcell"
fontSize={14}
fontFamily="System"
style={stylesHome.vacancyName}
/>
<Spacing height={5} />
<DynamicTruncatedText
text="Di Butuhkan Seorang..."
fontSize={12}
fontFamily="System"
style={stylesHome.vacancyDescription}
/>
</View>
</View>
</View>
</View>
<Spacing height={20} />
</View>
</ScrollView>
</>
);
}

View File

@@ -1,6 +1,7 @@
import { TextCustom } from "@/components";
import Spacing from "@/components/_ShareComponent/Spacing";
import DynamicTruncatedText from "@/components/_ShareComponent/TruncatedText";
import { Text, View } from "react-native";
import React from "react";
import { View } from "react-native";
import Icon from "react-native-vector-icons/FontAwesome";
import { stylesHome } from "./homeViewStyle";
@@ -10,7 +11,10 @@ export default function Home_BottomFeatureSection() {
<View style={stylesHome.jobVacancyContainer}>
<View style={stylesHome.jobVacancyHeader}>
<Icon name="briefcase" size={24} color="white" />
<Text style={stylesHome.jobVacancyTitle}>Job Vacancy</Text>
<Spacing width={10}/>
<TextCustom bold size="large">
Job Vacancy
</TextCustom>
</View>
<View style={stylesHome.vacancyList}>
@@ -18,19 +22,13 @@ export default function Home_BottomFeatureSection() {
<View style={stylesHome.vacancyItem}>
{/* <Icon name="user" size={20} color="#FFD700" /> */}
<View style={stylesHome.vacancyDetails}>
<DynamicTruncatedText
text="Bagas_banuna"
fontSize={14}
fontFamily="System"
style={stylesHome.vacancyName}
/>
<TextCustom bold color="yellow" truncate size="large">
Bagas_banuna
</TextCustom>
<Spacing height={5} />
<DynamicTruncatedText
text="Dicari perawat kucing"
fontSize={12}
fontFamily="System"
style={stylesHome.vacancyDescription}
/>
<TextCustom truncate={2}>
Dicari perawat kucing dan perawat anjing
</TextCustom>
</View>
</View>
@@ -38,19 +36,13 @@ export default function Home_BottomFeatureSection() {
<View style={stylesHome.vacancyItem}>
{/* <Icon name="user" size={20} color="#FFD700" /> */}
<View style={stylesHome.vacancyDetails}>
<DynamicTruncatedText
text="fibramarcell"
fontSize={14}
fontFamily="System"
style={stylesHome.vacancyName}
/>
<TextCustom bold color="yellow" truncate size="large">
fibramarcell
</TextCustom>
<Spacing height={5} />
<DynamicTruncatedText
text="Di Butuhkan Seorang..."
fontSize={12}
fontFamily="System"
style={stylesHome.vacancyDescription}
/>
<TextCustom truncate={2}>
Di Butuhkan Seorang Programer dan Designer
</TextCustom>
</View>
</View>
</View>

View File

@@ -1,5 +1,5 @@
import { ICustomTab, ITabs } from "@/components/_Interface/types";
import { Styles } from "@/styles/global-styles";
import { GStyles } from "@/styles/global-styles";
import { Ionicons } from "@expo/vector-icons";
import { router } from "expo-router";
import React from "react";
@@ -8,12 +8,12 @@ import { Text, TouchableOpacity, View } from "react-native";
const CustomTab = ({ icon, label, isActive, onPress }: ICustomTab) => (
<TouchableOpacity
style={[Styles.tabItem, isActive && Styles.activeTab]}
style={[GStyles.tabItem, isActive && GStyles.activeTab]}
onPress={onPress}
activeOpacity={0.7}
>
<View
style={[Styles.iconContainer, isActive && Styles.activeIconContainer]}
style={[GStyles.iconContainer, isActive && GStyles.activeIconContainer]}
>
<Ionicons
name={icon as any}
@@ -21,7 +21,7 @@ const CustomTab = ({ icon, label, isActive, onPress }: ICustomTab) => (
color={isActive ? "#fff" : "#666"}
/>
</View>
<Text style={[Styles.tabLabel, isActive && Styles.activeTabLabel]}>
<Text style={[GStyles.tabLabel, isActive && GStyles.activeTabLabel]}>
{label}
</Text>
</TouchableOpacity>
@@ -30,8 +30,8 @@ const CustomTab = ({ icon, label, isActive, onPress }: ICustomTab) => (
export default function TabSection({ tabs }: { tabs: ITabs[] }) {
return (
<>
<View style={Styles.tabBar}>
<View style={Styles.tabContainer}>
<View style={GStyles.tabBar}>
<View style={GStyles.tabContainer}>
{tabs.map((e) => (
<CustomTab
key={e.id}

View File

@@ -33,7 +33,7 @@ export const tabsHome: ITabs[] = [
icon: "person-outline",
activeIcon: "person",
label: "Profile",
path: "/profile/coba-id",
path: "/profile/id-percoban-123456",
isActive: true,
disabled: false,
},

View File

@@ -0,0 +1,51 @@
import { AvatarCustom } from "@/components";
import { AccentColor } from "@/constants/color-palet";
import { View, ImageBackground, StyleSheet } from "react-native";
const AvatarAndBackground = () => {
return (
<View style={styles.container}>
<ImageBackground
source={require("@/assets/images/logo-hipmi.png")}
style={styles.backgroundImage}
resizeMode="contain"
/>
{/* Background Image */}
{/* Avatar yang sedikit keluar */}
<View style={styles.avatarOverlap}>
<AvatarCustom
source={require("@/assets/images/react-logo.png")}
size="lg"
/>
</View>
</View>
);
};
export default AvatarAndBackground;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
},
backgroundImage: {
width: "100%",
height: 150, // Tinggi background sesuai kebutuhan
justifyContent: "center",
alignItems: "center",
borderRadius: 6,
overflow: "hidden",
borderWidth: 1,
borderColor: AccentColor.blue,
backgroundColor: "white",
},
avatarOverlap: {
position: "absolute", // Meletakkan avatar di atas background
top: 90, // Posisi avatar sedikit keluar dari background
left: "50%", // Sentralisasi horizontal
transform: [{ translateX: -50 }], // Menggeser ke kiri 50% lebarnya
},
});

View File

@@ -5,17 +5,21 @@ import { router } from "expo-router";
export default function Profile_MenuDrawerSection({
drawerItems,
setShowLogoutAlert,
setIsDrawerOpen,
}: {
drawerItems: IMenuDrawerItem[];
setShowLogoutAlert: (value: boolean) => void;
setIsDrawerOpen: (value: boolean) => void;
}) {
const handlePress = (item: IMenuDrawerItem) => {
if (item.label === "Keluar") {
// console.log("Logout clicked");
// console.log("Logout clicked");
setShowLogoutAlert(true);
} else {
console.log("PATH >> ", item.path);
router.push(item.path as any);
}
setIsDrawerOpen(false);
};
return (

View File

@@ -0,0 +1,121 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { BaseBox, Grid, Spacing, TextCustom } from "@/components";
import { MainColor } from "@/constants/color-palet";
import { ICON_SIZE_SMALL } from "@/constants/constans-value";
import { FontAwesome5, Ionicons } from "@expo/vector-icons";
import { useLocalSearchParams } from "expo-router";
import { View } from "react-native";
import AvatarAndBackground from "./AvatarAndBackground";
export default function ProfilSection() {
const { id } = useLocalSearchParams();
const listData = [
{
icon: (
<Ionicons name="call-outline" size={ICON_SIZE_SMALL} color="white" />
),
label: "+6282340374412",
},
{
icon: (
<Ionicons name="mail-outline" size={ICON_SIZE_SMALL} color="white" />
),
label: "bagasbanuna@gmail.com",
},
{
icon: (
<Ionicons
name="location-outline"
size={ICON_SIZE_SMALL}
color="white"
/>
),
label: "Jalan Raya Sesetan No. 123, Bandung, Indonesia",
},
{
icon: (
<FontAwesome5 name="transgender" size={ICON_SIZE_SMALL} color="white" />
),
label: "Laki-laki",
},
];
return (
<>
<BaseBox>
<AvatarAndBackground />
<Spacing height={50} />
<View style={{ alignItems: "center" }}>
<TextCustom bold size="large" align="center">
Nama User
</TextCustom>
<Spacing height={5} />
<TextCustom size="small">@Username</TextCustom>
</View>
<Spacing height={30} />
{listData.map((item, index) => (
<Grid key={index}>
<Grid.Col
span={2}
style={{
alignItems: "center",
justifyContent: "center",
}}
>
{item.icon}
</Grid.Col>
<Grid.Col span={10}>
<TextCustom bold>{item.label}</TextCustom>
</Grid.Col>
</Grid>
))}
</BaseBox>
<BaseBox>
<View>
<TextCustom bold size="large" align="center">
Portofolio
</TextCustom>
<Spacing />
{Array.from({ length: 2 }).map((_, index) => (
<BaseBox
key={index}
style={{ backgroundColor: MainColor.darkblue }}
onPress={() => console.log("pressed")}
>
<Grid>
<Grid.Col
span={10}
style={{ justifyContent: "center", backgroundColor: "" }}
>
<TextCustom bold size="large" truncate={1}>
Nama usaha portofolio
</TextCustom>
<TextCustom size="small" color="yellow">
#id-porofolio12345
</TextCustom>
</Grid.Col>
<Grid.Col
span={2}
style={{ alignItems: "flex-end", justifyContent: "center" }}
>
<Ionicons
name="caret-forward"
size={ICON_SIZE_SMALL}
color="white"
/>
</Grid.Col>
</Grid>
</BaseBox>
))}
</View>
</BaseBox>
</>
);
}

View File

@@ -1,18 +1,19 @@
import { TEXT_SIZE_MEDIUM } from "@/constants/constans-value";
import { Dimensions, StyleSheet } from "react-native";
import { AccentColor, MainColor } from "../constants/color-palet";
const { width } = Dimensions.get("window");
export const Styles = StyleSheet.create({
export const GStyles = StyleSheet.create({
container: {
flex: 1,
paddingInline: 25,
paddingInline: 20,
paddingBlock: 10,
backgroundColor: MainColor.darkblue,
},
containerWithBackground: {
flex: 1,
paddingInline: 25,
paddingInline: 20,
paddingBlock: 10,
},
imageBackground: {
@@ -42,7 +43,7 @@ export const Styles = StyleSheet.create({
// TEXT & LABEL
textLabel: {
fontSize: 14,
fontSize: TEXT_SIZE_MEDIUM,
color: MainColor.white,
fontWeight: "normal",
},
@@ -134,4 +135,24 @@ export const Styles = StyleSheet.create({
backgroundColor: "#007AFF",
},
// =============== TAB =============== //
// =============== BOTTOM BAR =============== //
bottomBar: {
backgroundColor: MainColor.darkblue,
borderTopColor: AccentColor.blue,
borderTopWidth: 1,
// shadowColor: AccentColor.blue,
// shadowOffset: {
// width: 0,
// height: -1,
// },
// shadowOpacity: 0.9,
// shadowRadius: 5,
// elevation: 5,
},
bottomBarContainer: {
paddingHorizontal: 15,
paddingVertical: 10,
},
// =============== BOTTOM BAR =============== //
});