diff --git a/app/(application)/profile/[id]/create.tsx b/app/(application)/profile/[id]/create.tsx
new file mode 100644
index 0000000..a519137
--- /dev/null
+++ b/app/(application)/profile/[id]/create.tsx
@@ -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 (
+
+ Simpan
+
+ }
+ >
+
+
+
+
+
+ console.log("pressed")} />
+
+
+
+
+
+
+
+
+ console.log("pressed")} />
+
+
+
+ setData({ ...data, name: text })}
+ />
+ setData({ ...data, email: text })}
+ />
+ setData({ ...data, address: text })}
+ />
+ setData({ ...(data as any), gender: value })}
+ />
+
+
+
+ );
+}
diff --git a/app/(application)/profile/[id]/index.tsx b/app/(application)/profile/[id]/index.tsx
index 945f18f..1a0295f 100644
--- a/app/(application)/profile/[id]/index.tsx
+++ b/app/(application)/profile/[id]/index.tsx
@@ -45,6 +45,11 @@ export default function Profile() {
// path: `/(application)/profile/dashboard-admin`,
// },
{ icon: "log-out", label: "Keluar", color: "red", path: "" },
+ {
+ icon: "create-outline",
+ label: "Create profile",
+ path: `/(application)/profile/${id}/create`,
+ },
];
// Animasi menggunakan translateY (lebih kompatibel)
diff --git a/app/(application)/profile/_layout.tsx b/app/(application)/profile/_layout.tsx
index eda4d6d..be16cde 100644
--- a/app/(application)/profile/_layout.tsx
+++ b/app/(application)/profile/_layout.tsx
@@ -24,6 +24,10 @@ export default function ProfileLayout() {
name="[id]/update-background"
options={{ title: "Update Latar Belakang" }}
/>
+
>
);
diff --git a/components/Alert/AlertCustom.tsx b/components/Alert/AlertCustom.tsx
index d6732e4..4d3b7dd 100644
--- a/components/Alert/AlertCustom.tsx
+++ b/components/Alert/AlertCustom.tsx
@@ -110,7 +110,7 @@ const styles = StyleSheet.create({
alertButton: {
flex: 1,
padding: 10,
- borderRadius: 5,
+ borderRadius: 50,
marginHorizontal: 5,
alignItems: "center",
},
diff --git a/components/Box/InformationBox.tsx b/components/Box/InformationBox.tsx
new file mode 100644
index 0000000..4cd996a
--- /dev/null
+++ b/components/Box/InformationBox.tsx
@@ -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 (
+ <>
+
+
+
+
+
+
+
+ {text
+ ? text
+ : "Lorem ipsum dolor sit amet consectetur adipisicing elit."}
+
+
+
+
+ >
+ );
+}
diff --git a/components/Avatar/AvatarCustom.tsx b/components/Image/AvatarCustom.tsx
similarity index 87%
rename from components/Avatar/AvatarCustom.tsx
rename to components/Image/AvatarCustom.tsx
index d86c7e9..17d8b67 100644
--- a/components/Avatar/AvatarCustom.tsx
+++ b/components/Image/AvatarCustom.tsx
@@ -1,7 +1,8 @@
import { MainColor } from "@/constants/color-palet";
+import DUMMY_IMAGE from "@/constants/dummy-image-value";
import { Image, ImageSourcePropType, StyleSheet } from "react-native";
-type Size = "base" | "sm" | "md" | "lg";
+type Size = "base" | "sm" | "md" | "lg" | "xl";
interface AvatarCustomProps {
source?: ImageSourcePropType;
@@ -13,10 +14,11 @@ const sizeMap = {
sm: 60,
md: 80,
lg: 100,
+ xl: 120,
};
export default function AvatarCustom({
- source = require("@/assets/images/dummy/dummy-avatar.png"),
+ source = DUMMY_IMAGE.avatar,
size = "base",
}: AvatarCustomProps) {
const dimension = sizeMap[size];
diff --git a/components/Image/LandscapeFrameUploaded.tsx b/components/Image/LandscapeFrameUploaded.tsx
new file mode 100644
index 0000000..02439de
--- /dev/null
+++ b/components/Image/LandscapeFrameUploaded.tsx
@@ -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 (
+
+
+
+ );
+}
diff --git a/components/Select/SelectCustom.tsx b/components/Select/SelectCustom.tsx
index 8ca8a33..557a304 100644
--- a/components/Select/SelectCustom.tsx
+++ b/components/Select/SelectCustom.tsx
@@ -22,6 +22,7 @@ type SelectProps = {
placeholder?: string;
data: SelectItem[];
value?: string | number | null;
+ required?: boolean; // <-- new prop
onChange: (value: string | number) => void;
};
@@ -30,16 +31,27 @@ const SelectCustom: React.FC = ({
placeholder = "Pilih opsi",
data,
value,
+ required = false, // <-- default false
onChange,
}) => {
const [modalVisible, setModalVisible] = useState(false);
const selectedItem = data.find((item) => item.value === value);
+ const hasError = required && value === null; // <-- check if empty and required
+
return (
- {label && {label}}
- setModalVisible(true)}>
+ {label && (
+
+ {label}
+ {required && *}
+
+ )}
+ setModalVisible(true)}
+ >
{selectedItem?.label || placeholder}
@@ -70,6 +82,11 @@ const SelectCustom: React.FC = ({
+
+ {/* Optional Error Message */}
+ {hasError && (
+ Harap pilih salah satu
+ )}
);
};
@@ -86,6 +103,10 @@ const styles = StyleSheet.create({
color: MainColor.white,
fontWeight: "500",
},
+ requiredIndicator: {
+ color: "red",
+ fontWeight: "bold",
+ },
input: {
borderWidth: 1,
borderColor: "#ccc",
@@ -95,6 +116,9 @@ const styles = StyleSheet.create({
justifyContent: "center",
backgroundColor: MainColor.white,
},
+ inputError: {
+ borderColor: "red",
+ },
text: {
fontSize: TEXT_SIZE_MEDIUM,
},
@@ -120,4 +144,9 @@ const styles = StyleSheet.create({
borderBottomWidth: 1,
borderBottomColor: "#eee",
},
+ errorMessage: {
+ marginTop: 4,
+ fontSize: 12,
+ color: "red",
+ },
});
diff --git a/components/TextInput/TextInputCustom.tsx b/components/TextInput/TextInputCustom.tsx
index 0ad9e4e..0be5c29 100644
--- a/components/TextInput/TextInputCustom.tsx
+++ b/components/TextInput/TextInputCustom.tsx
@@ -1,5 +1,3 @@
-// components/TextInputCustom.tsx
-
import Ionicons from "@expo/vector-icons/Ionicons";
import React, { useState } from "react";
import {
@@ -32,15 +30,18 @@ export const TextInputCustom = ({
iconRight,
label,
required = false,
- error = "",
+ error: externalError = "",
secureTextEntry = false,
fontColor = "#000",
disabled = false,
borderRadius = 8,
style,
+ keyboardType,
+ onChangeText,
...rest
}: Props) => {
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
+ const [internalError, setInternalError] = useState("");
// Helper untuk render ikon
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 (
{label && (
@@ -65,7 +83,7 @@ export const TextInputCustom = ({
textInputStyles.inputContainer,
disabled && textInputStyles.disabled,
{ borderRadius },
- error ? textInputStyles.errorBorder : null,
+ externalError || internalError ? textInputStyles.errorBorder : null,
style,
]}
>
@@ -76,6 +94,8 @@ export const TextInputCustom = ({
style={[textInputStyles.input, { color: fontColor }]}
editable={!disabled}
secureTextEntry={secureTextEntry && !isPasswordVisible}
+ keyboardType={keyboardType}
+ onChangeText={handleTextChange}
{...rest}
/>
{secureTextEntry && (
@@ -94,7 +114,12 @@ export const TextInputCustom = ({
{renderIcon(iconRight)}
)}
- {error ? {error} : null}
+ {/* Prioritaskan error eksternal */}
+ {externalError || internalError ? (
+
+ {externalError || internalError}
+
+ ) : null}
);
};
diff --git a/components/_ShareComponent/ViewWrapper.tsx b/components/_ShareComponent/ViewWrapper.tsx
index a85dd27..c054f72 100644
--- a/components/_ShareComponent/ViewWrapper.tsx
+++ b/components/_ShareComponent/ViewWrapper.tsx
@@ -47,42 +47,11 @@ const ViewWrapper = ({
{tabBarComponent ? tabBarComponent : null}
{bottomBarComponent ? (
-
- {bottomBarComponent}
-
+ {bottomBarComponent}
) : null}
>
-
- //
- //
- //
- // {withBackground ? (
- //
- // {children}
- //
- // ) : (
- // {children}
- // )}
- //
- // {tabBarComponent}
- //
- //
);
};
diff --git a/components/index.ts b/components/index.ts
index 8fb4ec2..c4d1325 100644
--- a/components/index.ts
+++ b/components/index.ts
@@ -18,7 +18,7 @@ import Grid from "./Grid/GridCustom";
// Box
import BaseBox from "./Box/BaseBox";
// Avatar
-import AvatarCustom from "./Avatar/AvatarCustom"
+import AvatarCustom from "./Image/AvatarCustom"
// Stack
import StackCustom from "./Stack/StackCustom";
// Select
diff --git a/screens/Authentication/LoginView.tsx b/screens/Authentication/LoginView.tsx
index 74601c5..9fcaf5f 100644
--- a/screens/Authentication/LoginView.tsx
+++ b/screens/Authentication/LoginView.tsx
@@ -28,10 +28,10 @@ export default function LoginView() {
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);
+ console.log("login user id :", id);
- // router.navigate("/verification");
- router.navigate(`/(application)/profile/${id}`);
+ router.navigate("/verification");
+ // router.navigate(`/(application)/profile/${id}`);
// router.navigate("/(application)/home");
// router.navigate(`/(application)/profile/${id}/edit`);
diff --git a/screens/Profile/AvatarAndBackground.tsx b/screens/Profile/AvatarAndBackground.tsx
index b48fe04..0673df8 100644
--- a/screens/Profile/AvatarAndBackground.tsx
+++ b/screens/Profile/AvatarAndBackground.tsx
@@ -1,12 +1,13 @@
import { AvatarCustom } from "@/components";
import { AccentColor } from "@/constants/color-palet";
+import DUMMY_IMAGE from "@/constants/dummy-image-value";
import { View, ImageBackground, StyleSheet } from "react-native";
const AvatarAndBackground = () => {
return (
@@ -15,7 +16,7 @@ const AvatarAndBackground = () => {
{/* Avatar yang sedikit keluar */}