feature & fix
deksripsi: feature: - Information Box - Create profile fix: component: Alet, Avatar, Select # No Issue
This commit is contained in:
97
app/(application)/profile/[id]/create.tsx
Normal file
97
app/(application)/profile/[id]/create.tsx
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
import {
|
||||||
|
AvatarCustom,
|
||||||
|
ButtonCustom,
|
||||||
|
Spacing,
|
||||||
|
StackCustom,
|
||||||
|
SelectCustom,
|
||||||
|
TextInputCustom,
|
||||||
|
ViewWrapper,
|
||||||
|
} from "@/components";
|
||||||
|
import InformationBox from "@/components/Box/InformationBox";
|
||||||
|
import ButtonUpload from "@/components/Button/ButtonUpload";
|
||||||
|
import LandscapeFrameUploaded from "@/components/Image/LandscapeFrameUploaded";
|
||||||
|
import { View } from "react-native";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
|
||||||
|
export default function CreateProfile() {
|
||||||
|
const [data, setData] = useState({
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
address: "",
|
||||||
|
gender: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const handlerSave = () => {
|
||||||
|
console.log("data create profile >>", data);
|
||||||
|
// router.back();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ViewWrapper
|
||||||
|
bottomBarComponent={
|
||||||
|
<ButtonCustom
|
||||||
|
onPress={handlerSave}
|
||||||
|
disabled={!data.name || !data.email || !data.address || !data.gender}
|
||||||
|
>
|
||||||
|
Simpan
|
||||||
|
</ButtonCustom>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<StackCustom>
|
||||||
|
<InformationBox text="Upload foto profile anda." />
|
||||||
|
<View style={{ alignItems: "center" }}>
|
||||||
|
<AvatarCustom size="xl" />
|
||||||
|
<Spacing />
|
||||||
|
<ButtonUpload onPress={() => console.log("pressed")} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Spacing />
|
||||||
|
|
||||||
|
<View>
|
||||||
|
<InformationBox text="Upload foto latar belakang anda." />
|
||||||
|
<LandscapeFrameUploaded />
|
||||||
|
<Spacing />
|
||||||
|
<ButtonUpload onPress={() => console.log("pressed")} />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Spacing />
|
||||||
|
<TextInputCustom
|
||||||
|
required
|
||||||
|
label="Nama"
|
||||||
|
placeholder="Masukkan nama"
|
||||||
|
value={data.name}
|
||||||
|
onChangeText={(text) => setData({ ...data, name: text })}
|
||||||
|
/>
|
||||||
|
<TextInputCustom
|
||||||
|
keyboardType="email-address"
|
||||||
|
required
|
||||||
|
label="Email"
|
||||||
|
placeholder="Masukkan email"
|
||||||
|
value={data.email}
|
||||||
|
onChangeText={(text) => setData({ ...data, email: text })}
|
||||||
|
/>
|
||||||
|
<TextInputCustom
|
||||||
|
required
|
||||||
|
label="Alamat"
|
||||||
|
placeholder="Masukkan alamat"
|
||||||
|
value={data.address}
|
||||||
|
onChangeText={(text) => setData({ ...data, address: text })}
|
||||||
|
/>
|
||||||
|
<SelectCustom
|
||||||
|
label="Jenis Kelamin"
|
||||||
|
placeholder="Pilih jenis kelamin"
|
||||||
|
data={[
|
||||||
|
{ label: "Laki-laki", value: "laki-laki" },
|
||||||
|
{ label: "Perempuan", value: "perempuan" },
|
||||||
|
]}
|
||||||
|
value={data.gender}
|
||||||
|
required
|
||||||
|
onChange={(value) => setData({ ...(data as any), gender: value })}
|
||||||
|
/>
|
||||||
|
<Spacing height={100} />
|
||||||
|
</StackCustom>
|
||||||
|
</ViewWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -45,6 +45,11 @@ export default function Profile() {
|
|||||||
// path: `/(application)/profile/dashboard-admin`,
|
// path: `/(application)/profile/dashboard-admin`,
|
||||||
// },
|
// },
|
||||||
{ icon: "log-out", label: "Keluar", color: "red", path: "" },
|
{ icon: "log-out", label: "Keluar", color: "red", path: "" },
|
||||||
|
{
|
||||||
|
icon: "create-outline",
|
||||||
|
label: "Create profile",
|
||||||
|
path: `/(application)/profile/${id}/create`,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Animasi menggunakan translateY (lebih kompatibel)
|
// Animasi menggunakan translateY (lebih kompatibel)
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ export default function ProfileLayout() {
|
|||||||
name="[id]/update-background"
|
name="[id]/update-background"
|
||||||
options={{ title: "Update Latar Belakang" }}
|
options={{ title: "Update Latar Belakang" }}
|
||||||
/>
|
/>
|
||||||
|
<Stack.Screen
|
||||||
|
name="[id]/create"
|
||||||
|
options={{ title: "Buat Profile" }}
|
||||||
|
/>
|
||||||
</Stack>
|
</Stack>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ const styles = StyleSheet.create({
|
|||||||
alertButton: {
|
alertButton: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
borderRadius: 5,
|
borderRadius: 50,
|
||||||
marginHorizontal: 5,
|
marginHorizontal: 5,
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
},
|
},
|
||||||
|
|||||||
33
components/Box/InformationBox.tsx
Normal file
33
components/Box/InformationBox.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
import Grid from "../Grid/GridCustom";
|
||||||
|
import TextCustom from "../Text/TextCustom";
|
||||||
|
import BaseBox from "./BaseBox";
|
||||||
|
|
||||||
|
export default function InformationBox({ text }: { text: string }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BaseBox>
|
||||||
|
<Grid>
|
||||||
|
<Grid.Col
|
||||||
|
span={2}
|
||||||
|
style={{ alignItems: "center", justifyContent: "center" }}
|
||||||
|
>
|
||||||
|
<Ionicons
|
||||||
|
name="information-circle-outline"
|
||||||
|
size={24}
|
||||||
|
color={MainColor.white}
|
||||||
|
/>
|
||||||
|
</Grid.Col>
|
||||||
|
<Grid.Col span={10} style={{ justifyContent: "center" }}>
|
||||||
|
<TextCustom>
|
||||||
|
{text
|
||||||
|
? text
|
||||||
|
: "Lorem ipsum dolor sit amet consectetur adipisicing elit."}
|
||||||
|
</TextCustom>
|
||||||
|
</Grid.Col>
|
||||||
|
</Grid>
|
||||||
|
</BaseBox>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
|
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||||
import { Image, ImageSourcePropType, StyleSheet } from "react-native";
|
import { Image, ImageSourcePropType, StyleSheet } from "react-native";
|
||||||
|
|
||||||
type Size = "base" | "sm" | "md" | "lg";
|
type Size = "base" | "sm" | "md" | "lg" | "xl";
|
||||||
|
|
||||||
interface AvatarCustomProps {
|
interface AvatarCustomProps {
|
||||||
source?: ImageSourcePropType;
|
source?: ImageSourcePropType;
|
||||||
@@ -13,10 +14,11 @@ const sizeMap = {
|
|||||||
sm: 60,
|
sm: 60,
|
||||||
md: 80,
|
md: 80,
|
||||||
lg: 100,
|
lg: 100,
|
||||||
|
xl: 120,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function AvatarCustom({
|
export default function AvatarCustom({
|
||||||
source = require("@/assets/images/dummy/dummy-avatar.png"),
|
source = DUMMY_IMAGE.avatar,
|
||||||
size = "base",
|
size = "base",
|
||||||
}: AvatarCustomProps) {
|
}: AvatarCustomProps) {
|
||||||
const dimension = sizeMap[size];
|
const dimension = sizeMap[size];
|
||||||
20
components/Image/LandscapeFrameUploaded.tsx
Normal file
20
components/Image/LandscapeFrameUploaded.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||||
|
import { Image } from "react-native";
|
||||||
|
import BaseBox from "../Box/BaseBox";
|
||||||
|
|
||||||
|
export default function LandscapeFrameUploaded() {
|
||||||
|
return (
|
||||||
|
<BaseBox
|
||||||
|
style={{
|
||||||
|
height: 250,
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
source={DUMMY_IMAGE.background}
|
||||||
|
resizeMode="cover"
|
||||||
|
style={{ width: "100%", height: "100%", borderRadius: 10 }}
|
||||||
|
/>
|
||||||
|
</BaseBox>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ type SelectProps = {
|
|||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
data: SelectItem[];
|
data: SelectItem[];
|
||||||
value?: string | number | null;
|
value?: string | number | null;
|
||||||
|
required?: boolean; // <-- new prop
|
||||||
onChange: (value: string | number) => void;
|
onChange: (value: string | number) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,16 +31,27 @@ const SelectCustom: React.FC<SelectProps> = ({
|
|||||||
placeholder = "Pilih opsi",
|
placeholder = "Pilih opsi",
|
||||||
data,
|
data,
|
||||||
value,
|
value,
|
||||||
|
required = false, // <-- default false
|
||||||
onChange,
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
|
|
||||||
const selectedItem = data.find((item) => item.value === value);
|
const selectedItem = data.find((item) => item.value === value);
|
||||||
|
|
||||||
|
const hasError = required && value === null; // <-- check if empty and required
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{label && <Text style={styles.label}>{label}</Text>}
|
{label && (
|
||||||
<Pressable style={styles.input} onPress={() => setModalVisible(true)}>
|
<Text style={styles.label}>
|
||||||
|
{label}
|
||||||
|
{required && <Text style={styles.requiredIndicator}> *</Text>}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
<Pressable
|
||||||
|
style={[styles.input, hasError ? styles.inputError : null]} // <-- add error style
|
||||||
|
onPress={() => setModalVisible(true)}
|
||||||
|
>
|
||||||
<Text style={selectedItem ? styles.text : styles.placeholder}>
|
<Text style={selectedItem ? styles.text : styles.placeholder}>
|
||||||
{selectedItem?.label || placeholder}
|
{selectedItem?.label || placeholder}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -70,6 +82,11 @@ const SelectCustom: React.FC<SelectProps> = ({
|
|||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
{/* Optional Error Message */}
|
||||||
|
{hasError && (
|
||||||
|
<Text style={styles.errorMessage}>Harap pilih salah satu</Text>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -86,6 +103,10 @@ const styles = StyleSheet.create({
|
|||||||
color: MainColor.white,
|
color: MainColor.white,
|
||||||
fontWeight: "500",
|
fontWeight: "500",
|
||||||
},
|
},
|
||||||
|
requiredIndicator: {
|
||||||
|
color: "red",
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
input: {
|
input: {
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: "#ccc",
|
borderColor: "#ccc",
|
||||||
@@ -95,6 +116,9 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
backgroundColor: MainColor.white,
|
backgroundColor: MainColor.white,
|
||||||
},
|
},
|
||||||
|
inputError: {
|
||||||
|
borderColor: "red",
|
||||||
|
},
|
||||||
text: {
|
text: {
|
||||||
fontSize: TEXT_SIZE_MEDIUM,
|
fontSize: TEXT_SIZE_MEDIUM,
|
||||||
},
|
},
|
||||||
@@ -120,4 +144,9 @@ const styles = StyleSheet.create({
|
|||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderBottomColor: "#eee",
|
borderBottomColor: "#eee",
|
||||||
},
|
},
|
||||||
|
errorMessage: {
|
||||||
|
marginTop: 4,
|
||||||
|
fontSize: 12,
|
||||||
|
color: "red",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// components/TextInputCustom.tsx
|
|
||||||
|
|
||||||
import Ionicons from "@expo/vector-icons/Ionicons";
|
import Ionicons from "@expo/vector-icons/Ionicons";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import {
|
import {
|
||||||
@@ -32,15 +30,18 @@ export const TextInputCustom = ({
|
|||||||
iconRight,
|
iconRight,
|
||||||
label,
|
label,
|
||||||
required = false,
|
required = false,
|
||||||
error = "",
|
error: externalError = "",
|
||||||
secureTextEntry = false,
|
secureTextEntry = false,
|
||||||
fontColor = "#000",
|
fontColor = "#000",
|
||||||
disabled = false,
|
disabled = false,
|
||||||
borderRadius = 8,
|
borderRadius = 8,
|
||||||
style,
|
style,
|
||||||
|
keyboardType,
|
||||||
|
onChangeText,
|
||||||
...rest
|
...rest
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
||||||
|
const [internalError, setInternalError] = useState("");
|
||||||
|
|
||||||
// Helper untuk render ikon
|
// Helper untuk render ikon
|
||||||
const renderIcon = (icon: IconType) => {
|
const renderIcon = (icon: IconType) => {
|
||||||
@@ -52,6 +53,23 @@ export const TextInputCustom = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Validasi email jika keyboardType = email-address
|
||||||
|
const handleTextChange = (text: string) => {
|
||||||
|
if (keyboardType === "email-address") {
|
||||||
|
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(text);
|
||||||
|
if (!isValid) {
|
||||||
|
setInternalError("Masukkan email yang valid");
|
||||||
|
} else {
|
||||||
|
setInternalError("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panggil onChangeText eksternal jika ada
|
||||||
|
if (onChangeText) {
|
||||||
|
onChangeText(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={textInputStyles.container}>
|
<View style={textInputStyles.container}>
|
||||||
{label && (
|
{label && (
|
||||||
@@ -65,7 +83,7 @@ export const TextInputCustom = ({
|
|||||||
textInputStyles.inputContainer,
|
textInputStyles.inputContainer,
|
||||||
disabled && textInputStyles.disabled,
|
disabled && textInputStyles.disabled,
|
||||||
{ borderRadius },
|
{ borderRadius },
|
||||||
error ? textInputStyles.errorBorder : null,
|
externalError || internalError ? textInputStyles.errorBorder : null,
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@@ -76,6 +94,8 @@ export const TextInputCustom = ({
|
|||||||
style={[textInputStyles.input, { color: fontColor }]}
|
style={[textInputStyles.input, { color: fontColor }]}
|
||||||
editable={!disabled}
|
editable={!disabled}
|
||||||
secureTextEntry={secureTextEntry && !isPasswordVisible}
|
secureTextEntry={secureTextEntry && !isPasswordVisible}
|
||||||
|
keyboardType={keyboardType}
|
||||||
|
onChangeText={handleTextChange}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
{secureTextEntry && (
|
{secureTextEntry && (
|
||||||
@@ -94,7 +114,12 @@ export const TextInputCustom = ({
|
|||||||
<View style={textInputStyles.icon}>{renderIcon(iconRight)}</View>
|
<View style={textInputStyles.icon}>{renderIcon(iconRight)}</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
{error ? <Text style={textInputStyles.errorMessage}>{error}</Text> : null}
|
{/* Prioritaskan error eksternal */}
|
||||||
|
{externalError || internalError ? (
|
||||||
|
<Text style={textInputStyles.errorMessage}>
|
||||||
|
{externalError || internalError}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,42 +47,11 @@ const ViewWrapper = ({
|
|||||||
{tabBarComponent ? tabBarComponent : null}
|
{tabBarComponent ? tabBarComponent : null}
|
||||||
{bottomBarComponent ? (
|
{bottomBarComponent ? (
|
||||||
<View style={GStyles.bottomBar}>
|
<View style={GStyles.bottomBar}>
|
||||||
<View style={GStyles.bottomBarContainer}>
|
<View style={GStyles.bottomBarContainer}>{bottomBarComponent}</View>
|
||||||
{bottomBarComponent}
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
// <SafeAreaProvider>
|
|
||||||
// <SafeAreaView
|
|
||||||
// edges={[
|
|
||||||
// "bottom",
|
|
||||||
// // "top",
|
|
||||||
// ]}
|
|
||||||
// style={{
|
|
||||||
// flex: 1,
|
|
||||||
// // paddingTop: StatusBar.currentHeight,
|
|
||||||
// backgroundColor: MainColor.darkblue,
|
|
||||||
// }}
|
|
||||||
// >
|
|
||||||
// <ScrollView contentContainerStyle={{ flexGrow: 1 }}>
|
|
||||||
// {withBackground ? (
|
|
||||||
// <ImageBackground
|
|
||||||
// source={assetBackground}
|
|
||||||
// resizeMode="cover"
|
|
||||||
// style={Styles.imageBackground}
|
|
||||||
// >
|
|
||||||
// <View style={Styles.containerWithBackground}>{children}</View>
|
|
||||||
// </ImageBackground>
|
|
||||||
// ) : (
|
|
||||||
// <View style={Styles.container}>{children}</View>
|
|
||||||
// )}
|
|
||||||
// </ScrollView>
|
|
||||||
// {tabBarComponent}
|
|
||||||
// </SafeAreaView>
|
|
||||||
// </SafeAreaProvider>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import Grid from "./Grid/GridCustom";
|
|||||||
// Box
|
// Box
|
||||||
import BaseBox from "./Box/BaseBox";
|
import BaseBox from "./Box/BaseBox";
|
||||||
// Avatar
|
// Avatar
|
||||||
import AvatarCustom from "./Avatar/AvatarCustom"
|
import AvatarCustom from "./Image/AvatarCustom"
|
||||||
// Stack
|
// Stack
|
||||||
import StackCustom from "./Stack/StackCustom";
|
import StackCustom from "./Stack/StackCustom";
|
||||||
// Select
|
// Select
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ export default function LoginView() {
|
|||||||
const randomAlfabet = Math.random().toString(36).substring(2, 8);
|
const randomAlfabet = Math.random().toString(36).substring(2, 8);
|
||||||
const randomNumber = Math.floor(Math.random() * 1000000);
|
const randomNumber = Math.floor(Math.random() * 1000000);
|
||||||
const id = randomAlfabet + randomNumber + fixNumber;
|
const id = randomAlfabet + randomNumber + fixNumber;
|
||||||
console.log("user id :", id);
|
console.log("login user id :", id);
|
||||||
|
|
||||||
// router.navigate("/verification");
|
router.navigate("/verification");
|
||||||
router.navigate(`/(application)/profile/${id}`);
|
// router.navigate(`/(application)/profile/${id}`);
|
||||||
// router.navigate("/(application)/home");
|
// router.navigate("/(application)/home");
|
||||||
// router.navigate(`/(application)/profile/${id}/edit`);
|
// router.navigate(`/(application)/profile/${id}/edit`);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { AvatarCustom } from "@/components";
|
import { AvatarCustom } from "@/components";
|
||||||
import { AccentColor } from "@/constants/color-palet";
|
import { AccentColor } from "@/constants/color-palet";
|
||||||
|
import DUMMY_IMAGE from "@/constants/dummy-image-value";
|
||||||
import { View, ImageBackground, StyleSheet } from "react-native";
|
import { View, ImageBackground, StyleSheet } from "react-native";
|
||||||
|
|
||||||
const AvatarAndBackground = () => {
|
const AvatarAndBackground = () => {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ImageBackground
|
<ImageBackground
|
||||||
source={require("@/assets/images/logo-hipmi.png")}
|
source={DUMMY_IMAGE.background}
|
||||||
style={styles.backgroundImage}
|
style={styles.backgroundImage}
|
||||||
resizeMode="contain"
|
resizeMode="contain"
|
||||||
/>
|
/>
|
||||||
@@ -15,7 +16,7 @@ const AvatarAndBackground = () => {
|
|||||||
{/* Avatar yang sedikit keluar */}
|
{/* Avatar yang sedikit keluar */}
|
||||||
<View style={styles.avatarOverlap}>
|
<View style={styles.avatarOverlap}>
|
||||||
<AvatarCustom
|
<AvatarCustom
|
||||||
source={require("@/assets/images/react-logo.png")}
|
source={DUMMY_IMAGE.avatar}
|
||||||
size="lg"
|
size="lg"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
Reference in New Issue
Block a user